Studio Notebook

Claude Code Atlas

Remote, Bridge, And Connected Modes

Learn how Claude Code operates when work crosses process or machine boundaries.

Why this matters

Remote means Claude Code is reaching across the network to talk to a session somewhere else. Bridge means the local REPL stays in your terminal while a remote client watches the same work. The point of this page is not the tool names; it is the boundary each mode crosses and who owns the conversation.

One product, three boundary-crossing modes

Remote, bridge, and local REPL at a glance

The same product can stay local, move the session remote, or mirror a local REPL to a remote client.

Local REPL Bridge session Remote session

Local REPL mode is the ordinary case: you type in one terminal, and the loop stays there. Remote-session mode (--remote / CCR elsewhere) moves that loop onto a separate session. Bridge mode keeps the local REPL running but mirrors the work to a remote client that can observe and drive it.

This root page is about crossing boundaries, not just running a tool. The reading map comes next, then the exact source slices that later pages depend on.

How this part breaks down

  1. remote-session-manager-and-ccr-lifecycle Start with the remote-session path: how Claude Code connects to a CCR session, receives SDK messages over WebSocket, and sends permission answers back across that boundary.
  2. remote-and-bridge-data-structures Read this appendix early if the connection objects feel abstract. It introduces RemoteSessionConfig, RemoteSessionCallbacks, BridgeConfig, WorkSecret, and the bridge state labels before later pages depend on them.
  3. repl-bridge-lifecycle-and-session-creation Follow the always-on bridge path: how the REPL bridge registers an environment, creates or reconnects sessions, and keeps ingress state alive across polling and reconnects.
  4. bridge-message-flow-and-permission-roundtrip Study the message bridge itself: which inbound messages are eligible, how the bridge turns them into local SDK events, and how permission requests make a roundtrip between local UI and remote client.

The remote session contract

RemoteSessionConfig is the remote boundary in its simplest form: identify the session, get a token, know the org, and note when the client is only viewing.

export type RemoteSessionConfig = {
  sessionId: string
  getAccessToken: () => string
  orgUuid: string
  /** True if session was created with an initial prompt that's being processed */
  hasInitialPrompt?: boolean
  /**
   * When true, this client is a pure viewer. Ctrl+C/Escape do NOT send
   * interrupt to the remote agent; 60s reconnect timeout is disabled;
   * session title is never updated. Used by `claude assistant`.
   */
  viewerOnly?: boolean
}

The bridge handle and state

ReplBridgeHandle is the local handle the REPL uses to write work across the bridge. BridgeState is the small status set the UI needs to know whether the bridge is ready, connected, reconnecting, or failed.

export type ReplBridgeHandle = {
  bridgeSessionId: string
  environmentId: string
  sessionIngressUrl: string
  writeMessages(messages: Message[]): void
  writeSdkMessages(messages: SDKMessage[]): void
  sendControlRequest(request: SDKControlRequest): void
  sendControlResponse(response: SDKControlResponse): void
  sendControlCancelRequest(requestId: string): void
  sendResult(): void
  teardown(): Promise<void>
}

export type BridgeState = 'ready' | 'connected' | 'reconnecting' | 'failed'

The bridge config and secret payload

BridgeConfig is the bridge-registration shape. WorkSecret is the server-issued payload that carries the ingress token and API base URL the bridge needs to keep polling.

export type BridgeConfig = {
  dir: string
  machineName: string
  branch: string
  gitRepoUrl: string | null
  maxSessions: number
  spawnMode: SpawnMode
  verbose: boolean
  sandbox: boolean
  /** Client-generated UUID identifying this bridge instance. */
  bridgeId: string
  /**
   * Sent as metadata.worker_type so web clients can filter by origin.
   * Backend treats this as opaque — any string, not just BridgeWorkerType.
   */
  workerType: string
  /** Client-generated UUID for idempotent environment registration. */
  environmentId: string
  /**
   * Backend-issued environment_id to reuse on re-register. When set, the
   * backend treats registration as a reconnect to the existing environment
   * instead of creating a new one. Used by `claude remote-control
   * --session-id` resume. Must be a backend-format ID — client UUIDs are
   * rejected with 400.
   */
  reuseEnvironmentId?: string
  /** API base URL the bridge is connected to (used for polling). */
  apiBaseUrl: string
  /** Session ingress base URL for WebSocket connections (may differ from apiBaseUrl locally). */
  sessionIngressUrl: string
  /** Debug file path passed via --debug-file. */
  debugFile?: string
  /** Per-session timeout in milliseconds. Sessions exceeding this are killed. */
  sessionTimeoutMs?: number
}

WorkSecret carries the session ingress token, API base URL, source list, and the server-driven CCR v2 selector.

export type WorkSecret = {
  version: number
  session_ingress_token: string
  api_base_url: string
  sources: Array<{
    type: string
    git_info?: { type: string; repo: string; ref?: string; token?: string }
  }>
  auth: Array<{ type: string; token: string }>
  claude_code_args?: Record<string, string> | null
  mcp_config?: unknown | null
  environment_variables?: Record<string, string> | null
  /**
   * Server-driven CCR v2 selector. Set by prepare_work_secret() when the
   * session was created via the v2 compat layer (ccr_v2_compat_enabled).
   * Same field the BYOC runner reads at environment-runner/sessionExecutor.ts.
   */
  use_code_sessions?: boolean
}