Skip to content

Protocol v2

The web mirror speaks protocol v2 envelopes. Every frame in either direction is a JSON object with a type and a seq (server → client only).

Open a WebSocket to /ws on the mirror’s host. Optionally append ?t=<token> for auth, and ?resume=<id>&seq=<n> to replay buffered output from a previous session.

ws://<host>:3000/ws?t=<token>&resume=<id>&seq=<n>

The server’s first frame describes the session:

{
"type": "hello",
"sessionId": "f4a2…",
"seq": 0,
"version": 2,
"settings": { "theme": "obsidian", "paneGap": 4, … },
"snapshot": {
"workspaces": [ … ],
"panels": [ … ],
"sidebar": { … }
}
}

The client stores sessionId for resume, sets up xterm with the snapshot, and starts processing subsequent frames.

typeWhenPayload
helloFirst frame after upgrade.session id, settings, snapshot
surfaceStdoutPTY output.surfaceId, bytes (base64)
surfaceMetadataA surface’s metadata changed.surfaceId, full SurfaceMetadata
panelCreate / panelUpdate / panelClearSideband panel lifecycle.panel options or id
sidebarUpdateStatus pill / progress / log change.partial sidebar state
notificationCreate / notificationDismissNotifications.notification record / id
settingsSnapshotTheme / font / status-bar settings broadcast (M11, 0.2.85).theme preset, ANSI palette, font, density, status-bar key order, notification-overlay flags, autoContinueEngine — sensitive fields (auth token, telegram bot token, allowed ids) are dropped by pickWebSettings server-side
htKeysSeenht set-status discovery list (M11, 0.2.85).array of registered keys
plansSnapshotPlan panel content (M17, 0.3.0).per-workspace plan-step list
autoContinueAuditAuto-continue audit feed (M17, 0.3.0).recent audit entries; hidden client-side when autoContinueEngine is off
pongReply to a client ping.server time

Each carries a seq — a per-session sequence number incremented on every frame.

typePurposePayload
surfaceStdinTyping into a terminal.surfaceId, bytes (base64), capped at 64 KiB
surfaceResizeRequestxterm reports new dims.surfaceId, cols (10–500), rows (4–500)
surfaceFocusUI focus follows.surfaceId
panelInteractClick / drag / resize on an interactive panel.panel id, event
selectWorkspaceCwdPin a CWD from the workspace card chip row (M13, 0.2.87).workspaceId, cwd. v1 stores in localStorage; bun-side hook is null-safe so wiring can land later without a protocol bump
pingLiveness check.nonce
cancelCancel a streaming method (e.g. metadata follow).id

Frames are bounded at 256 KiB per envelope and rate-limited at 256/sec per connection.

To resume after a disconnect, reconnect with ?resume=<sessionId>&seq=<lastSeqYouSaw>:

  • The server checks its ring buffer (2 MB per session) for everything since seq.
  • If found: the server replays missed frames in order, then resumes live streaming.
  • If missing or expired: the server emits a fresh hello and the client re-snapshots.

PTY output is coalesced at 16 ms granularity. Many small writes within a single frame interval are flushed as one surfaceStdout envelope. Keeps frame rate at ≤ 60 Hz without losing perceptual responsiveness.

For resume scenarios where the ring buffer is too small (e.g. minutes of disconnect), the server uses @xterm/headless + SerializeAddon to compute a single-frame “current state” snapshot of the terminal — colors, cursor position, alt-screen — and ships that instead of streaming the full historical byte stream.

  • src/bun/web/server.ts — envelope dispatch.
  • src/bun/web/connection.ts — SessionBuffer (ring buffer, seq, backpressure).
  • src/web-client/transport.ts — client-side envelope handling.
  • src/web-client/protocol-dispatcher.ts — server-message → store-action dispatch.