InkdownInkdown
Start writing

Claude-Code

62 filesยท4 subfolders

Shared Workspace

Claude-Code
codex

07-bridge-remote

Shared from "Claude-Code" on Inkdown

Bridge & Remote Control

How Claude Code sessions can be controlled from the web (claude.ai/code) and mobile app.


What Is the Bridge?

The bridge system turns a locally-running Claude Code session into a remotely accessible experience. You can:

  1. Start a session on your laptop
  2. Open claude.ai/code in your browser
  3. Watch and interact with the same session in real-time
  4. Do the same from the Claude mobile app

Two Operational Modes

1. Standalone Bridge (claude remote-control)

Runs as a persistent server that:

0000_start_here_index_and_recommended_reading_order.md
0100_project_overview_tech_stack_runtime_modes_and_folder_map.md
0200_startup_flow_entry_points_and_cold_start_sequence.md
0300_codebase_modules_layers_state_models_and_schemas.md
0400_system_architecture_and_design_rationale.md
0500_interactive_repl_request_flow_end_to_end.md
0600_headless_sdk_and_print_mode_request_flow_end_to_end.md
0700_mcp_integration_connection_and_tool_call_flow.md
0800_external_services_sdks_storage_and_local_dependencies.md
0900_environment_variables_settings_feature_flags_and_failure_modes.md
1000_non_obvious_patterns_gotchas_and_debugging_traps.md
1100_full_codebase_file_inventory_grouped_by_directory.md
kimi
00-overview.md
01-entrypoints.md
02-state-management.md
03-query-system.md
04-tools-system.md
05-tasks-system.md
06-ui-components.md
07-bridge-remote.md
08-services.md
09-skills-plugins.md
10-commands.md
11-testing-architecture.md
12-permission-system.md
13-build-system.md
14-ink-internals.md
15-git-internals.md
16-context-compaction.md
17-vim-mode.md
18-mailbox-notifications.md
19-session-persistence.md
20-hooks-system.md
21-error-recovery.md
README.md
qwen
00-overview.md
01-entry-points.md
02-query-engine.md
03-tools-and-tasks.md
04-commands-and-skills.md
05-state-management.md
06-ink-rendering.md
07-bridge-remote.md
08-mcp-services.md
09-services-overview.md
10-multi-agent.md
11-system-prompt-constants.md
12-tool-interface.md
13-memory-system.md
14-buddy-companion.md
15-keybindings.md
16-stop-hooks.md
17-vim-mode.md
18-upstreamproxy.md
19-cost-tracking-history.md
20-contexts-styles-onboarding.md
21-hooks.md
22-screens.md
tweets-explain
claude-code-memory-analysis.md
compact
memory-system
agentic-architecture
  • 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
2. REPL Bridge (/remote-control)

Bridges an already-running REPL session:

  • Creates a bridge environment
  • Flushes session history to the server
  • Maintains a live WebSocket/SSE connection
  • Supports crash recovery (resume with --continue)

Architecture

Plain text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Mobile App  โ”‚     โ”‚  claude.ai   โ”‚     โ”‚   Server     โ”‚
โ”‚              โ”‚     โ”‚  /code       โ”‚     โ”‚              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚                   โ”‚                    โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                    Work Items (Redis stream)
                           โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚          Bridge Instance               โ”‚
       โ”‚                                        โ”‚
       โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
       โ”‚  โ”‚ Poll     โ”‚โ”€โ”€โ”€โ–ถโ”‚ Session Spawner  โ”‚  โ”‚
       โ”‚  โ”‚ Loop     โ”‚    โ”‚ (child process)  โ”‚  โ”‚
       โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
       โ”‚                           โ”‚            โ”‚
       โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
       โ”‚  โ”‚    Transport (WS or SSE)         โ”‚  โ”‚
       โ”‚  โ”‚                                  โ”‚  โ”‚
       โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚  โ”‚
       โ”‚  โ”‚  โ”‚ Messages โ”‚  โ”‚ Control      โ”‚  โ”‚  โ”‚
       โ”‚  โ”‚  โ”‚ (user)   โ”‚  โ”‚ (set_model,  โ”‚  โ”‚  โ”‚
       โ”‚  โ”‚  โ”‚          โ”‚  โ”‚  interrupt)  โ”‚  โ”‚  โ”‚
       โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚  โ”‚
       โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                           โ”‚
                    NDJSON (stream-json)
                           โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚        Claude Code Child Process       โ”‚
       โ”‚        (--print --sdk-url ...)         โ”‚
       โ”‚                                        โ”‚
       โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
       โ”‚  โ”‚  Query Engine โ†’ Tools โ†’ API      โ”‚  โ”‚
       โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Core Concepts

Environment

An "environment" is the bridge's identity on the server:

TypeScript
// Registration
POST /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:

TypeScript
type WorkItem = {
  id: string
  type: 'work'
  data: { type: 'session' | 'healthcheck', id: string }
  secret: string  // base64url-encoded WorkSecret
}
Work Secret

Decoded from the work item:

TypeScript
type WorkSecret = {
  session_ingress_token: string  // JWT for auth
  api_base_url: string
  use_code_sessions: boolean     // v2 vs v1 transport
  sources: string[]
  auth: { type: string, token: string }
  environment_variables: Record<string, string>
  mcp_config: McpConfig
}

Transport Versions

v1 โ€” Session Ingress (WebSocket)
Plain text
Read:  WebSocket (HybridTransport)
Write: HTTP POST to Session-Ingress endpoint
Auth:  OAuth tokens
v2 โ€” CCR (Server-Sent Events)
Plain text
Read:  SSE (SSETransport)
Write: HTTP POST to CCR /worker/* endpoints
Auth:  JWT from work secret (validates session_id claim)

The server decides per-session via secret.use_code_sessions.


Session Spawning

When a work item arrives, the bridge spawns a child process:

Bash
claude --print \
  --sdk-url <url> \
  --session-id <id> \
  --input-format stream-json \
  --output-format stream-json \
  --replay-user-messages

Environment variables set on the child:

  • CLAUDE_CODE_SESSION_ACCESS_TOKEN โ€” JWT for session auth
  • CLAUDE_CODE_USE_CCR_V2 โ€” selects v2 transport
  • CLAUDE_CODE_WORKER_EPOCH โ€” worker epoch for v2
Session Handle

The spawner returns a SessionHandle:

TypeScript
interface SessionHandle {
  done: Promise<'completed' | 'failed' | 'interrupted'>
  kill(): void
  forceKill(): void
  writeStdin(message: string): void
  updateAccessToken(token: string): void
  activities: ActivityRingBuffer
  currentActivity: Activity | null
}
Activity Tracking

The spawner parses the child's NDJSON stdout to extract:

  • Tool activities: "Editing src/foo.ts", "Running tests..."
  • Permission requests
  • First user message (for auto-derived session title)

Messaging Protocol

SDK Messages (User โ†’ Session)

Only user type messages are forwarded to the REPL. Other types (tool_result, progress, etc.) are internal and filtered out.

Control Requests (Server โ†’ Session)

Server-initiated requests with a ~10-14s timeout:

SubtypePurpose
initializeSession lifecycle handshake
set_modelChange the AI model
set_max_thinking_tokensAdjust thinking budget
set_permission_modeChange permission mode
interruptInterrupt current turn
Control Responses (Session โ†’ Server)

Permission decisions forwarded from the server.


Echo Deduplication

Two bounded UUID sets prevent message echoes:

TypeScript
recentPostedUUIDs: BoundedUUIDSet   // Our sent messages (capacity 2000)
recentInboundUUIDs: BoundedUUIDSet  // Inbound prompts already forwarded

Flush Gate

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 = await initBridgeCore({
  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:

JSON
{
  "environmentId": "env_abc123",
  "sessionId": "session_xyz789",
  "sessionUrl": "https://claude.ai/code/session_xyz789"
}

On restart with --continue:

  1. Read pointer file
  2. Re-register environment with reuseEnvironmentId
  3. Reconnect session (same session ID, URL stays valid)
Reconnection Strategy

Strategy 1 โ€” Reconnect in place:

  • Re-register with same environment ID
  • Reconnect session
  • Session ID stays the same

Strategy 2 โ€” Fresh session fallback:

  • If environment is truly gone (TTL expired)
  • Archive old session
  • Create new session on fresh environment

Both strategies have a reentrancy guard (reconnectPromise) and cap at 3 attempts.

State Machine
Plain text
'ready' โ†’ 'connected' โ†’ 'reconnecting' โ†’ 'connected'
                              โ†“
                           'failed'

Multi-Session Architecture

Spawn Modes
ModeBehavior
single-sessionOne session, bridge exits when done
same-dirPersistent server, all sessions share directory
worktreePersistent 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

Key Files Reference

FilePurpose
src/bridge/bridgeMain.tsStandalone bridge server
src/bridge/replBridge.tsREPL bridge (existing session)
src/bridge/bridgeConfig.tsAuth/URL resolution
src/bridge/bridgeMessaging.tsMessage parsing and routing
src/bridge/createSession.tsSession API client
src/bridge/sessionRunner.tsSession spawner
src/bridge/types.tsShared type definitions
src/bridge/flushGate.tsFlush gate mechanism
src/bridge/capacityWake.tsCapacity wake signals
src/bridge/workSecret.tsWork secret decoding
src/bridge/jwtUtils.tsJWT utilities
src/bridge/trustedDevice.tsTrusted device management
src/bridge/pollConfig.tsPoll configuration
src/bridge/replBridgeTransport.tsREPL bridge transport wiring
src/bridge/replBridgeHandle.tsREPL bridge handle API
src/remote/Remote session management
src/server/Direct connect server