Bridge & Remote
Claude Code supports remote execution through two mechanisms: the bridge system for distributed agent execution, and direct connect for peer-to-peer session access.
Bridge Mode
Bridge mode allows Claude Code to run as a worker that polls for and executes tasks from a remote server. This enables distributed execution patterns where work is dispatched from a central coordinator.
Architecture
┌─────────────┐ poll/claim ┌─────────────────┐
│ Bridge API │ ◄───────────────── │ Bridge Worker │
│ (Server) │ ────────────────► │ (bridgeMain.ts) │
│ │ work items │ │
│ │ ◄───────────────── │ Session Spawner │
│ │ results │ │
└─────────────┘ └─────────────────┘Bridge Configuration
Authentication and URL resolution are centralized in bridgeConfig.ts:
// Access token: dev override first, then OAuth keychain
function getBridgeAccessToken(): string | undefined
// Base URL: dev override first, then production OAuth config
function getBridgeBaseUrl(): stringundefined if not logged in.Bridge Main Loop
The bridgeMain.ts module implements the worker lifecycle:
Initialization
Validate the bridge ID, set up logging, create the API client, and initialize the token refresh scheduler.
Worker Registration
Register the worker with the bridge API using a work secret. The work secret encodes the session ID and is used to build SDK URLs.
Poll Loop
Poll for available work items with exponential backoff. Connection failures use separate backoff parameters from general errors.
Session Execution
When work is claimed, spawn a Claude Code session via the SessionSpawner. Sessions can run in an isolated Git worktree.
Result Reporting
Report session results (success, failure, timeout) back to the bridge API. Handle retries for transient failures.
Backoff Configuration
The bridge uses separate backoff strategies for connection vs. general errors:
type BackoffConfig = {
connInitialMs: number // 2,000ms
connCapMs: number // 120,000ms (2 minutes)
connGiveUpMs: number // 600,000ms (10 minutes)
generalInitialMs: number // 500ms
generalCapMs: number // 30,000ms
generalGiveUpMs: number // 600,000ms (10 minutes)
shutdownGraceMs?: number // SIGTERM to SIGKILL grace period
stopWorkBaseDelayMs?: number
}Session Spawning
The createSessionSpawner() factory creates a SessionSpawner that can:
- Create and remove Git worktrees for isolated execution
- Spawn sessions with configurable timeouts (default:
DEFAULT_SESSION_TIMEOUT_MS) - Track session handles for cleanup on shutdown
type SessionSpawnOpts = { ... }
type SessionHandle = { ... }
type SpawnMode = 'worktree' | 'cwd'JWT and Security
Bridge mode uses JWT tokens for authentication. The createTokenRefreshScheduler() handles automatic token renewal before expiration. Expired tokens trigger the isExpiredErrorType check and are treated as fatal errors requiring re-authentication.
The bridge validates its ID via validateBridgeId() and supports trusted device tokens via getTrustedDeviceToken() for additional security.
Remote Session Manager
The RemoteSessionManager manages WebSocket connections to remote Claude Code sessions:
type RemoteSessionConfig = {
sessionId: string
getAccessToken: () => string
orgUuid: string
hasInitialPrompt?: boolean
viewerOnly?: boolean
}Remote Session Callbacks
type RemoteSessionCallbacks = {
onMessage: (message: SDKMessage) => void
onPermissionRequest: (
request: SDKControlPermissionRequest,
requestId: string,
) => void
onPermissionCancelled?: (requestId: string, toolUseId?: string) => void
onConnected?: () => void
onDisconnected?: () => void
}The manager handles:
- SDK message routing between the local UI and remote agent
- Permission request forwarding and response relay
- Connection lifecycle (connect, reconnect, disconnect)
- Event delivery to the remote session via
sendEventToRemoteSession()
Permission Responses
Remote permission responses use a simplified type:
type RemotePermissionResponse =
| { behavior: 'allow'; updatedInput: Record<string, unknown> }
| { behavior: 'deny'; message: string }Direct Connect
Direct connect enables peer-to-peer session access over WebSocket without the bridge server intermediary:
type DirectConnectConfig = {
serverUrl: string
sessionId: string
wsUrl: string
authToken?: string
}DirectConnectSessionManager
The DirectConnectSessionManager class manages a single WebSocket connection:
class DirectConnectSessionManager {
constructor(config: DirectConnectConfig, callbacks: DirectConnectCallbacks)
connect(): void
sendMessage(content: RemoteMessageContent): void
sendPermissionResponse(requestId: string, response: RemotePermissionResponse): void
disconnect(): void
}The WebSocket receives newline-delimited JSON messages. Each message is validated as an StdoutMessage type before being dispatched to callbacks. Messages are distinguished between SDK messages (forwarded to onMessage) and control messages (permission requests forwarded to onPermissionRequest).
SDK Message Adapter
The remote system uses a type guard to distinguish SDK messages from control messages:
function isSDKMessage(message: SDKMessage | SDKControlRequest | SDKControlResponse): message is SDKMessageControl messages include control_request, control_response, and control_cancel_request types for managing permission flows across the remote boundary.