WebSocket API¶
Live terminal streaming over WebSocket at /ws/{session_id}.
Connection¶
WebSocket connections require authentication via session cookie (same-origin).
Message Format¶
All messages are JSON except for PNG frame data which is sent as binary.
Server → Client Messages¶
viewer_assigned¶
Sent immediately after connection with the server-assigned viewer tag.
Fields:
| Field | Description |
|---|---|
viewer_tag |
Server-assigned viewer identifier for log correlation. |
role_assigned¶
Sent immediately after connection with this viewer's driver/passenger role.
Fields:
| Field | Description |
|---|---|
role |
Assigned role: driver or passenger. |
driver_tag |
Current driver viewer tag, if any. |
driver_info |
Driver connection metadata when this viewer is a passenger. |
status¶
Sent with PNG-mode frames and as periodic status in xterm mode.
Fields:
| Field | Description |
|---|---|
frame |
Capture counter for sync. |
program |
Detected program name. |
state |
Current state (Idle, Working, Blocked, Error). |
detail |
State detail string. |
scroll |
Scroll state object. |
cols |
Terminal width from tmux. |
rows |
Terminal height from tmux. |
cwd |
Current working directory reported by the daemon, when known. |
terminal¶
Sent to xterm-mode viewers with terminal text and cursor metadata.
Fields:
| Field | Description |
|---|---|
text |
Captured terminal text including ANSI sequences. |
cursor_x |
Cursor column, zero-indexed. |
cursor_y |
Cursor row, zero-indexed. |
cursor_visible |
Whether the cursor should be visible. |
frame |
Capture counter for sync. |
program |
Detected program name. |
state |
Current state. |
cols |
Terminal width from tmux. |
rows |
Terminal height from tmux. |
terminal_diff¶
Sent to diff-capable xterm viewers: only the rows that changed since the viewer's last frame (issue #82). Applied without clearing the screen. Full 'terminal' frames are interleaved on connect, resize, large changes, and a periodic re-sync.
Fields:
| Field | Description |
|---|---|
seq |
Daemon capture counter for this frame (logging/debug). |
changes |
Array of {line, content}: 0-indexed row + its full new content (ANSI preserved). |
cursor_x |
Cursor column, zero-indexed. |
cursor_y |
Cursor row, zero-indexed. |
cursor_visible |
Whether the cursor should be visible. |
frame |
Capture counter for sync. |
program |
Detected program name. |
state |
Current state. |
cols |
Terminal width from tmux. |
rows |
Terminal height from tmux. |
scroll_up¶
Sent to xterm viewers immediately before a frame when tmux reports that lines left the top of the pane. The client writes that many line feeds first, which moves the rows off its screen and into xterm's own scrollback — a real scroll rather than a repaint.
Fields:
| Field | Description |
|---|---|
lines |
Rows that scrolled off since this viewer's last frame, capped at the screen height. |
scrollback_seed¶
Sent once when a viewer enters xterm mode, carrying the pane history that predates its connection. The client writes it as ordinary output before the first frame arrives, so the session opens with a scrollback instead of an empty one.
Fields:
| Field | Description |
|---|---|
text |
Captured history including the current screen, ANSI preserved. |
total_lines |
Number of lines captured. |
pane_height |
Visible pane height at capture time. |
cols |
Terminal width from tmux. |
scroll_buffer¶
Metadata returned with a captured scrollback buffer.
Fields:
| Field | Description |
|---|---|
total_lines |
Number of scrollback lines captured. |
pane_height |
Visible pane height. |
history_size |
Total tmux history size. |
cols |
Terminal width from tmux. |
retina |
Whether the generated scrollback image used retina scaling. |
tmux_session_ended¶
Sent when the underlying tmux session no longer exists.
Fields:
| Field | Description |
|---|---|
reason |
Why the session ended. |
server_restarting¶
Broadcast before the daemon restarts so clients can reconnect.
Fields:
| Field | Description |
|---|---|
reason |
Restart reason. |
input_rejected¶
Sent when a passenger attempts driver-only input.
Fields:
| Field | Description |
|---|---|
reason |
Rejection reason, such as passenger_mode. |
control_requested¶
Sent to the driver when a passenger requests control.
Fields:
| Field | Description |
|---|---|
requester_tag |
Viewer tag requesting control. |
timeout_seconds |
Seconds before the request auto-resolves. |
message |
Optional requester-supplied message. |
requester_info |
Requester connection metadata, when available. |
control_request_sent¶
Sent to a passenger after its control request is accepted for delivery.
Fields:
| Field | Description |
|---|---|
timeout_seconds |
Seconds before the request auto-resolves. |
control_request_error¶
Sent when a passenger control request cannot be created.
Fields:
| Field | Description |
|---|---|
error |
Failure reason. |
control_request_result¶
Sent to a requester when a control request is accepted, denied, or times out.
Fields:
| Field | Description |
|---|---|
granted |
Whether control was granted. |
reason |
Result reason, such as accepted, denied, or timeout. |
control_request_cancelled¶
Sent when a pending control request is cancelled.
Fields:
| Field | Description |
|---|---|
requester_tag |
Viewer tag whose request was cancelled, when known. |
control_cancel_error¶
Sent when cancelling a control request fails.
Fields:
| Field | Description |
|---|---|
error |
Failure reason. |
control_response_error¶
Sent when a driver response to a control request cannot be applied.
Fields:
| Field | Description |
|---|---|
error |
Failure reason. |
role_changed¶
Sent when a viewer's driver/passenger role changes.
Fields:
| Field | Description |
|---|---|
role |
New role: driver or passenger. |
driver_tag |
Current driver viewer tag. |
driver_info |
Driver connection metadata, when available. |
reason |
Reason for the role change, when available. |
relinquish_failed¶
Sent when the driver cannot relinquish control.
Fields:
| Field | Description |
|---|---|
reason |
Failure reason. |
inbox_new¶
Sent when a new inbox item is shared to the session.
Fields:
| Field | Description |
|---|---|
item |
Inbox item payload. |
ack¶
Acknowledgment of a queued client input message.
Fields:
| Field | Description |
|---|---|
msgId |
Client message id being acknowledged. |
error¶
Sent when a WebSocket input or stream error occurs.
Fields:
| Field | Description |
|---|---|
message |
Error description. |
version_mismatch¶
Sent when the client version does not match the server-required asset version.
Fields:
| Field | Description |
|---|---|
client_version |
Client's reported version. |
server_version |
Server's required version. |
Binary Frames¶
PNG image data is sent as binary WebSocket frames after each status message.
Client → Server Messages¶
ping¶
Client keepalive used to keep upstream WebSocket traffic active.
Fields:
| Field | Description |
|---|---|
t |
Client timestamp in milliseconds. |
quality¶
Update viewer quality and streaming-mode settings.
Fields:
| Field | Description |
|---|---|
fps |
Frame rate. PNG mode is clamped to 0.1-10; xterm mode to 0.1-30. |
retina |
Enable 2x PNG rendering. |
palette |
Enable 8-bit color quantization for PNG frames. |
mode |
Streaming mode: png or xterm. |
diff |
xterm line-diff capability: when true, the server may send terminal_diff frames. |
client_version |
Client asset version for sync checking. |
frame_ack¶
Acknowledge first frame receipt so the server can enable deduplication.
visibility¶
Page Visibility signal (issue #82). Hidden tabs stop counting toward the session capture rate (drops to ~1 Hz); foregrounding resumes high fps and forces a full frame before diffs resume.
Fields:
| Field | Description |
|---|---|
visible |
Whether the viewer's tab is foregrounded. |
resize¶
Request a tmux pane resize; driver role required.
Fields:
| Field | Description |
|---|---|
cols |
Requested terminal columns. |
rows |
Requested terminal rows. |
request_control¶
Passenger request to become the driver.
Fields:
| Field | Description |
|---|---|
message |
Optional message shown to the current driver. |
relinquish_control¶
Driver request to give control to the next passenger.
control_response¶
Driver response to a pending control request.
Fields:
| Field | Description |
|---|---|
grant |
true to grant control, false to deny. |
cancel_control_request¶
Passenger request to cancel its pending control request.
keys¶
Send literal text to the terminal; driver role required.
Fields:
| Field | Description |
|---|---|
text |
Text to send. |
enter¶
Send Enter; driver role required.
ctrl¶
Send a Ctrl+key combination; driver role required.
Fields:
| Field | Description |
|---|---|
key |
Single character, such as c for Ctrl+C. |
special¶
Send a named special key; driver role required.
Fields:
| Field | Description |
|---|---|
key |
Named key such as Escape, Tab, Backspace, Up, Down, Left, or Right. |
scroll¶
Control tmux copy-mode scrolling; driver role required.
Fields:
| Field | Description |
|---|---|
direction |
up, down, or exit. |
batch¶
Send multiple input actions in sequence; driver role required.
Fields:
| Field | Description |
|---|---|
actions |
Array of keys, enter, ctrl, special, and scroll actions. |
Example Session¶
const ws = new WebSocket('wss://localhost:8081/ws/coding-0');
ws.onopen = () => {
// Set quality preferences
ws.send(JSON.stringify({
type: 'quality',
fps: 2.0,
retina: true,
client_version: window.CLIENT_VERSION
}));
};
ws.onmessage = (event) => {
if (typeof event.data === 'string') {
const msg = JSON.parse(event.data);
if (msg.type === 'status') {
console.log(`State: ${msg.state}`);
}
} else {
// Binary PNG frame
const blob = new Blob([event.data], { type: 'image/png' });
img.src = URL.createObjectURL(blob);
}
};
// Send keystrokes
ws.send(JSON.stringify({ type: 'keys', text: 'hello' }));
ws.send(JSON.stringify({ type: 'enter' }));
ws.send(JSON.stringify({ type: 'ctrl', key: 'c' }));