No description
This repository has been archived on 2026-05-07. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
  • Go 81.7%
  • Vue 9.1%
  • TypeScript 6.6%
  • Makefile 1.6%
  • HTML 0.4%
  • Other 0.6%
Find a file
2026-05-07 15:33:45 +02:00
daemon Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
dashboard Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
server Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
.gitignore Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
Makefile Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
README.md Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00
SPEC.md Initial project scaffolding with daemon, dashboard, and server 2026-05-07 15:33:45 +02:00

CCRM — Claude Code Remote Control

Remote monitoring and control of Claude Code sessions. Manages Claude Code as subprocesses via the Claude Agent SDK's JSON protocol, with the ability to attach interactively via tmux. Monitor activity via a web dashboard and send prompts remotely.

Architecture

┌─ Developer Machine ──────────────────────────────────────────────┐
│                                                                  │
│  ccrmd (daemon)                                                  │
│    ├─ Spawns claude as subprocess (JSON lines on stdin/stdout)   │
│    ├─ Routes subprocess messages (system, assistant, result)     │
│    ├─ Auto-allows tool permissions (PoC)                         │
│    ├─ Accepts CLI commands via Unix control socket               │
│    └─ Connects to server via WebSocket                           │
│         │                                                        │
│  On "ccrm attach":                                               │
│    ├─ Stops subprocess, opens tmux with claude --resume <id>     │
│    └─ On detach/exit: restarts subprocess with --resume          │
│         │                                                        │
└─────────┼────────────────────────────────────────────────────────┘
          │ WebSocket
          ▼
┌─ Server ────────────────────────────────────────────────────────┐
│  ccrm-server                                                     │
│    ├─ REST API + WebSocket hub                                   │
│    ├─ SQLite (sessions, events)                                  │
│    └─ Vue 3 dashboard (embedded static files)                    │
└──────────────────────────────────────────────────────────────────┘

Dual-mode sessions

Each session operates in one of two modes, switchable at runtime:

SUBPROCESS MODE (default)                    INTERACTIVE MODE (attach)
┌─────────────────────────┐                  ┌──────────────────────────┐
│ daemon process          │                  │ tmux session             │
│   └─ claude subprocess  │   ccrm attach    │   └─ claude --resume <id>│
│      stdin ← JSON msgs  │ ──────────────→  │      (full TUI)          │
│      stdout → JSON msgs │                  │      user types directly │
│      (daemon controls)  │  ccrm detach /   │                          │
│                         │ ←──────────────  │                          │
│                         │  session exits   │                          │
└─────────────────────────┘                  └──────────────────────────┘
  • Subprocess mode — default. The daemon controls Claude, sends prompts via stdin, reads structured JSON responses from stdout. No tmux involved.
  • Interactive mode — activated by ccrm attach. The daemon stops the subprocess, opens a tmux session running claude --resume <session_id>, and the user interacts directly with the TUI. When the user detaches or exits, the daemon automatically resumes subprocess mode.

Components

Component Description
daemon/ Go daemon (ccrmd) + CLI (ccrm) for session management
server/ Go web server with REST API, WebSocket hub, and SQLite
dashboard/ Vue 3 + TypeScript + Tailwind web UI

Prerequisites

  • Go 1.21+
  • Bun
  • tmux (only needed for ccrm attach)
  • Claude Code CLI

Quick Start

1. Build

make all

This produces three binaries in bin/:

  • ccrm — CLI for session management
  • ccrmd — Background daemon
  • ccrm-server — Web server

2. Configure the daemon

Create ~/.config/ccrm/config.yaml:

server:
  url: "ws://localhost:8080/ws/daemon"
  api_key: "change-me-daemon-key"

daemon:
  socket_path: "/tmp/ccrm.sock"
  tmux_prefix: "ccrm-"
  log_level: "info"

claude:
  binary: "claude"

3. Configure and start the server

# Default config — listens on :8080, SQLite at ./ccrm.db
./bin/ccrm-server

# Or with a config file:
./bin/ccrm-server --config server.yaml

Server config (server.yaml):

server:
  listen: ":8080"
database:
  path: "ccrm.db"
auth:
  daemon_api_key: "change-me-daemon-key"
  dashboard_token: "change-me-dashboard-token"

4. Start the daemon

./bin/ccrm daemon start

5. Start a session

cd /path/to/your/project
ccrm start --name my-project

The daemon spawns Claude as a subprocess. You can now send prompts or attach interactively.

6. Send a prompt

ccrm send my-project "list the files in this directory"

7. Attach interactively

ccrm attach my-project

This switches the session to interactive mode — you get the full Claude TUI. Detach with Ctrl-b d and the daemon automatically resumes subprocess control.

8. Open the dashboard

Navigate to http://localhost:8080 and enter your dashboard token.

CLI Reference

ccrm start [--name <n>] [--claude-args "..."]   Start a new Claude Code session
ccrm list                                        List all sessions (name, mode, status)
ccrm attach <name>                               Switch to interactive mode and attach
ccrm stop <name>                                 Gracefully stop a session
ccrm kill <name>                                 Force-kill a session
ccrm send <name> "prompt"                        Send a prompt to a session

ccrm daemon start                                Start the daemon in the background
ccrm daemon stop                                 Stop the daemon
ccrm daemon status                               Check daemon status

All session commands communicate with the daemon via its Unix control socket. The daemon must be running.

REST API

All API endpoints require the dashboard token via Authorization: Bearer <token> header.

Method Endpoint Description
GET /api/sessions List sessions (?active=true to filter)
GET /api/sessions/:id Get session details
GET /api/sessions/:id/events List session events (?limit=N&offset=N)
POST /api/sessions/:id/prompt Send a prompt ({"prompt": "..."})

WebSocket

Endpoint Auth Description
/ws/daemon X-API-Key header Daemon connection
/ws/dashboard ?token= query param Dashboard live updates

Development

# Run each component in a separate terminal:
make dev-daemon       # go run ./cmd/ccrmd
make dev-server       # go run ./cmd/ccrm-server
make dev-dashboard    # vite dev server on :5173 (proxies /api and /ws to :8080)

Makefile Targets

Target Description
make all Build daemon, server, and dashboard
make daemon Build ccrm and ccrmd binaries
make server Build ccrm-server (includes dashboard)
make dashboard Install dashboard dependencies
make dashboard-build Build dashboard for production
make dev-daemon Run daemon in dev mode
make dev-server Run server in dev mode
make dev-dashboard Run dashboard Vite dev server
make clean Remove build artifacts

Session Lifecycle

ccrm start → [starting] → system message → [idle]
                                              │
                           prompt sent ──→ [active] → result → [idle] → drain queue
                                              │
                        tool_use_permission → [waiting_permission] → auto-allow → [active]
                                              │
                             ccrm stop/kill → [stopped]

Attach/detach cycle:
  [subprocess/idle] → ccrm attach → [interactive/active] → detach → [subprocess/idle]

Control Socket Protocol

The daemon listens on a Unix socket (default /tmp/ccrm.sock) for JSON commands from the CLI.

Command Fields Response
session.start name, path, claude_args Session JSON
session.stop session_name OK
session.kill session_name OK
session.attach session_name tmux_session name
sessions.list Session array
prompt.send session_name, prompt prompt_status
daemon.stop OK (then shuts down)

Project Structure

ccrm/
├── daemon/                     Go daemon + CLI
│   ├── cmd/
│   │   ├── ccrm/main.go       CLI entry point
│   │   └── ccrmd/main.go      Daemon entry point
│   └── internal/
│       ├── claude/             Subprocess protocol + process lifecycle
│       ├── config/             YAML config loading
│       ├── relay/              WebSocket client + protocol types
│       ├── session/            Session state machine + manager
│       └── tmux/               tmux operations (for attach mode)
├── server/                     Go web server
│   ├── cmd/
│   │   └── ccrm-server/main.go
│   └── internal/
│       ├── api/                REST endpoints + auth
│       ├── config/             Server config
│       ├── db/                 SQLite + migrations
│       └── ws/                 WebSocket hub
├── dashboard/                  Vue 3 frontend
│   └── src/
│       ├── views/              Login, Dashboard, SessionDetail
│       ├── components/         SessionCard, EventStream, PromptInput
│       ├── composables/        useWebSocket
│       ├── stores/             Pinia stores (auth, sessions)
│       └── types/              TypeScript types
└── Makefile

License

MIT