WebSocket v3 (Unified Ghostty Transport)
Single WebSocket. Multiplexed sessions. Binary framing. Same protocol for web + iOS.Endpoint
GET /ws(WebSocket upgrade)- Auth:
- normal:
?token=... --no-auth: token optional
- normal:
Framing (binary)
web/src/shared/ws-v3.ts.
Message types (v3)
IDs:WsV3MessageType in web/src/shared/ws-v3.ts.
Client → Server:
SUBSCRIBEpayload =encodeWsV3SubscribePayload({ flags, snapshotMinIntervalMs, snapshotMaxIntervalMs })sessionIdmay be empty ("") to subscribe to globalEVENTframes (no per-session STDOUT/snapshots).
UNSUBSCRIBEpayload emptyINPUT_TEXTpayload = UTF-8 text bytes (includes escape sequences when needed)INPUT_KEYpayload = UTF-8 key name (SpecialKey)RESIZEpayload =u32 colsLE+u32 rowsLEKILLpayload = UTF-8 signal (defaultSIGTERM)RESET_SIZEpayload emptyPINGpayload optional
WELCOMEpayload = JSON{ ok: true, version: 3 }STDOUTpayload = UTF-8 bytes from PTY (asciinema “o” frames’ data)SNAPSHOT_VTpayload = VT snapshot bytes (see next section)EVENTpayload = JSON- per-session:
exit,git-status-update, … - global (
sessionId == ""):connected,test-notification, …
- per-session:
ERRORpayload = JSON{ message: string }PONGpayload optional
Subscribe flags
WsV3SubscribeFlags in web/src/shared/ws-v3.ts:
Stdout(bit 0)Snapshots(bit 1)Events(bit 2)
Snapshot payload (SNAPSHOT_VT)
Payload is the existing VT snapshot v1 byte format (magic VT, version 1).
- Used for:
- session list previews/thumbnails (server-rendered)
- optional “hard resync” for interactive clients
- Encoder:
TerminalManager(server-side Ghostty emulation)
Implementation map
- Server hub:
web/src/server/services/ws-v3-hub.ts - Stdout source:
web/src/server/services/cast-output-hub.ts(tails cast + pruning vialastClearOffset) - Git events:
web/src/server/services/git-status-hub.ts - Web client transport:
web/src/client/services/terminal-socket-client.ts - iOS transport:
ios/VibeTunnel/Services/BufferWebSocketClient.swift
HQ mode
HQ uses the same/ws v3 frames.
- HQ keeps one upstream WS per remote.
- Downstream subscriptions aggregate flags per session and fan out frames to clients.
Removed legacy transports
/buffers(v20xBFframing)/ws/input
/api/sessions/:id/text(plain-text rendering of the current terminal buffer)