Registers itself as an "environment" with the server
Polls for work items
Spawns child Claude Code processes for each session
Supports up to 32 concurrent sessions
Plain text
claude remote-control # Single session mode
claude remote-control --capacity 8 # Multi-session, same directory
claude remote-control --worktree # Multi-session, isolated worktrees
An "environment" is the bridge's identity on the server:
TypeScript
// RegistrationPOST /v1/environments
โ { environment_id, environment_secret }
// The environment has:// - maxSessions: how many concurrent sessions// - spawnMode: 'single-session' | 'same-dir' | 'worktree'// - TTL: how long it lives without heartbeat
Session
A Claude Code conversation, bound to an environment:
TypeScript
POST /v1/sessions
โ { session_id, session_ingress_token }
Work Item
A dispatchable unit of work queued to an environment's Redis stream:
Gates message writes during initial history flush to prevent ordering races:
Plain text
History flush in progress
โ
โผ
New message arrives
โ
โผ
Queued (not sent yet)
โ
โผ
Flush completes โ drain gate โ send queued messages
Keep-Alive
A silent keep_alive frame is sent every 120s (configurable via GrowthBook) to prevent upstream proxies from garbage-collecting idle sessions.
REPL Bridge Details
Initialization
TypeScript
const handle = awaitinitBridgeCore({
createSession, // How to create a session
wireTransport, // How to wire up transport
onInboundMessage, // Handle incoming messages
onPermissionResponse, // Handle permission responses
onControlRequest, // Handle control requests
onStateChange, // State change notifications
perpetual, // Crash recovery mode
crashRecoveryPointer // Path to pointer file
})
Crash Recovery
In perpetual mode, the bridge writes bridge-pointer.json:
Persistent server, each session gets isolated worktree
Capacity Management
Plain text
while (true) {
if (activeSessions.size >= capacity) {
// Heartbeat-only mode (no new work polling)
await heartbeatActiveWork()
await waitForCapacityWake() // Signal when session completes
} else {
const work = await pollForWork()
if (work) spawnSession(work)
}
}
Token Refresh
Proactive scheduler fires ~5 minutes before JWT expires:
v1: Delivers fresh OAuth token to child via stdin
v2: Calls reconnectSession to trigger server-side re-dispatch
Graceful Shutdown
Plain text
SIGTERM received
โ
โผ
Stop polling for new work
โ
โผ
Send SIGTERM to all child sessions
โ
โผ
Wait 30 seconds (grace period)
โ
โผ
SIGKILL stragglers
โ
โผ
Archive sessions, deregister environment
โ
โผ
Exit