---
title: "Server, Bridge, Remote — Claude Code Internals — Vlad Arbatov"
description: "Server mode, bridge protocol, CCR, WebSocket, cron, state management"
image: "https://arbatov.dev/share-claude.png"
---

# Server, Bridge, and Remote Systems

## Overview

Three execution systems beyond the default REPL:

1. **Server Mode** — Long-lived daemon polling for work via Environments API
2. **Bridge System** — Always-on reverse proxy connecting REPL to claude.ai
3. **Remote/Assistant Mode** — Viewer-only sessions connected to remote CCR via WebSocket

---

## Server Mode

### API Surface (bridge/bridgeApi.ts:68-451)

| Method | Endpoint | Purpose |
|--------|----------|---------|
| `registerBridgeEnvironment` | `POST /v1/environments/bridge` | Register as worker |
| `pollForWork` | `GET /v1/environments/{id}/work/poll` | Block for work (~10s) |
| `acknowledgeWork` | `POST .../work/{id}/ack` | Acknowledge receipt |
| `stopWork` | `POST .../work/{id}/stop` | Stop a work item |
| `heartbeatWork` | `POST .../work/{id}/heartbeat` | Extend lease |
| `sendPermissionResponseEvent` | `POST /v1/sessions/{id}/events` | Permission decisions |
| `archiveSession` | `POST /v1/sessions/{id}/archive` | Mark session done |
| `reconnectSession` | `POST .../bridge/reconnect` | Resume stale session |
| `deregisterEnvironment` | `DELETE .../bridge/{id}` | Cleanup on shutdown |

### Work Secret Structure (bridge/types.ts)

Base64url-encoded JSON containing:
- `session_ingress_token` — auth for session-ingress
- `api_base_url` — where to POST messages
- `sources` — git info for workspace
- `auth` — auth tokens
- `claude_code_args` — CLI arg overrides
- `mcp_config` — MCP server configuration
- `environment_variables` — env var injection

### Session Spawning (bridge/sessionRunner.ts:248-548)

Bridge spawns child CLI with:
```bash
claude --print --sdk-url <url> --session-id <id> \
       --input-format stream-json --output-format stream-json \
       --replay-user-messages
```

**Output**: NDJSON (one JSON object per line), parsed as SDK messages or control requests.

**Activity Tracking**: Ring buffer of last 10 activities from tool_use blocks:
- `Read → "Reading {file_path}"`
- `Bash → "Running {command}"`
- `Glob/Grep → "Searching {pattern}"`

### Permission Flow

Child emits control_request → Bridge forwards to claude.ai → User responds → Bridge sends control_response back to child.

---

## Bridge System

### Env-less Bridge (CCR v2) (bridge/remoteBridgeCore.ts)

1. **Create Session** → `POST /v1/code/sessions`
2. **Get Worker JWT** → `POST /v1/code/sessions/{id}/bridge` (OAuth)
3. **Create SSE Transport** → `GET /v1/code/sessions/{id}/messages`
4. **Token Refresh** → proactive re-call `/bridge` before expiry

Key difference from env-based: No `/v1/environments` registration, no polling loop, direct OAuth→JWT exchange.

### Bridge Configuration (bridge/types.ts:81-115)

```typescript
type BridgeConfig = {
  dir: string
  machineName: string
  branch: string
  maxSessions: number
  spawnMode: 'single-session' | 'worktree' | 'same-dir'
  workerType: 'claude_code' | 'claude_code_assistant'
  environmentId: string
  apiBaseUrl: string
  sessionIngressUrl: string
  sessionTimeoutMs?: number
}
```

### API Headers

```
Authorization: Bearer {token}
Content-Type: application/json
anthropic-version: 2023-06-01
anthropic-beta: environments-2025-11-01
x-environment-runner-version: {version}
```

---

## Remote/Assistant Mode

### RemoteSessionManager (remote/RemoteSessionManager.ts:95-324)

- `connect()` — open WebSocket to CCR session
- `sendMessage(content)` — POST message to session
- `respondToPermissionRequest(requestId, result)` — answer permission prompt
- `cancelSession()` — send interrupt signal
- `reconnect()` — force-reconnect WebSocket

### WebSocket Connection (remote/SessionsWebSocket.ts:82-250)

**URL**: `wss://api.anthropic.com/v1/sessions/ws/{sessionId}/subscribe?organization_uuid={uuid}`

**Reconnection**:
- Max 5 retries, 2s base delay, exponential backoff
- Close code 4003 (unauthorized) → permanent close
- Close code 4001 (session not found) → 3 retries
- Ping interval: 30s keep-alive

---

## Remote Execution & Scheduling

### RemoteTriggerTool (tools/RemoteTriggerTool/)

CRUD for remote triggers:
- `list`, `get {id}`, `create {body}`, `update {id} {body}`, `run {id}`
- Gated by `tengu_surreal_dali` GrowthBook flag

### Cron Scheduling (tools/ScheduleCronTool/)

```typescript
{
  cron: string,        // 5-field: "M H DoM Mon DoW"
  prompt: string,      // Prompt to enqueue
  recurring: boolean,  // Default true
  durable: boolean     // Persist to disk (default false)
}
```

- Max 50 jobs per session
- One-shot: auto-delete after firing
- Recurring: auto-expire after 90 days
- Storage: `~/.claude/scheduled_tasks.json` (durable) or AppState (session-only)

---

## State Management

### AppStateStore (state/AppStateStore.ts:89-452)

Central Redux-like store with immutable + mutable sections:

**Key immutable fields:**
```typescript
settings, verbose, mainLoopModel, statusLineText,
expandedView, kairosEnabled, thinkingEnabled,
remoteSessionUrl, remoteConnectionStatus,

// Bridge state
replBridgeEnabled, replBridgeConnected,
replBridgeSessionActive, replBridgeReconnecting,
replBridgeConnectUrl, replBridgeSessionUrl,

// MCP
mcp: { clients, tools, commands, resources }
```

**Key mutable fields:**
```typescript
tasks: { [taskId]: TaskState },
agentNameRegistry: Map<string, AgentId>,
sessionHooks: Map<...>,
fileHistory: FileHistoryState,
todos: { [agentId]: TodoList }
```

### Persistence Locations

| Data | Location |
|------|----------|
| Settings | `~/.claude/config.json` |
| Session history | `~/.claude/sessions/{sessionId}.json` |
| Scheduled tasks | `~/.claude/scheduled_tasks.json` |
| Server sessions | `~/.claude/server-sessions.json` |

---

## SDK Message Types (entrypoints/agentSdkTypes.ts)

```typescript
type SDKMessage =
  | SDKUserMessage      // role: 'user'
  | SDKAssistantMessage // role: 'assistant'
  | SDKResultMessage    // subtype: 'success' | 'error' | 'timeout'
  | SDKSystemMessage    // subtype: 'post_turn_summary' | ...
```

### Control Protocol

**Permission Request:**
```json
{
  "type": "control_request",
  "request_id": "uuid",
  "request": {
    "subtype": "can_use_tool",
    "tool_name": "Write",
    "input": {"file_path": "..."},
    "tool_use_id": "..."
  }
}
```

**Permission Response:**
```json
{
  "type": "control_response",
  "response": {
    "subtype": "success",
    "request_id": "uuid",
    "response": {
      "behavior": "allow" | "deny",
      "updatedInput": {...}
    }
  }
}
```

---

## Transport Protocols

### CCR v1 (HybridTransport)

- **Write**: `POST /v1/sessions/{id}/events`
- **Read**: WebSocket or SSE fallback
- **Auth**: Bearer token

### CCR v2 (SSE + CCRClient)

- **Credentials**: `POST /v1/code/sessions/{id}/bridge` → worker_jwt
- **Write**: `POST {api_base_url}/v1/code/sessions/{id}/messages`
- **Read**: `GET {api_base_url}/v1/code/sessions/{id}/messages` (SSE)
- **Auth**: Bearer {worker_jwt}
- **Refresh**: Re-call `/bridge` before expiry

---

## Supporting Systems

### Voice Mode (voice/voiceModeEnabled.ts)

- Requires `VOICE_MODE` feature gate + GrowthBook flag
- OAuth only (not API key/Bedrock/Vertex)
- Uses `/voice_stream` endpoint

### Buddy/Companion (buddy/companion.ts)

- Procedurally-generated character (species, hat, stats, rarity)
- Seeded by username hash (stable across sessions)

### Upstream Proxy (upstreamproxy/upstreamproxy.ts)

CCR container setup:
1. Read session token from `/run/ccr/session_token`
2. Download proxy CA cert
3. Start local CONNECT→WebSocket relay
4. Set `HTTPS_PROXY` + `SSL_CERT_FILE`

No-proxy list: loopback, RFC1918, IMDS, anthropic.com, GitHub, npm, PyPI

### Native TypeScript (native-ts/)

- `file-index` — fuzzy file search (nucleo-style scoring)
- `yoga-layout` — layout engine
- `color-diff` — color distance

### Vim Mode (vim/)

Vi/Vim keybindings for REPL input.

---

## Key Flows

### Remote Control Startup

1. Register bridge → environment_id
2. Poll for work (blocking) → session_id
3. Acknowledge work
4. Spawn child with `--sdk-url` + `--session-id`
5. Monitor NDJSON output
6. Forward control requests to claude.ai
7. On exit: stop work + archive session
8. On daemon exit: deregister environment

### Always-On Bridge (/remote-control)

1. Create session → cse_...
2. Get JWT → POST /bridge
3. Create SSE transport
4. Schedule token refresh
5. REPL sends/receives via SSE + POST
6. Permission prompts routed via control_request/response
7. On 401: rebuild transport with fresh JWT
