Skip to content

Reference

WebSocket reference

  • v0 · latest
  • 11 min read
  • Updated Sep 29, 2026
  • Generated from source

Realtime protocol v1 used by the dashboard for live feeds, the screencast and takeover input. Generated from @browserhive/contracts/ws. Available when --admin is on.

  • URL: ws://<host>:<port>/api/v1/ws (same port as everything else).
  • Subprotocol: Sec-WebSocket-Protocol: browserhive.v1.
  • Authentication: the dashboard session cookie or Authorization: Bearer <token>, checked at upgrade. A failed check completes the upgrade and closes immediately with 4401, so browsers see the code.
  • The first server frame is a reply with payload type: "hello", carrying epoch and the current cursor. epoch changes on every server start; a cursor from another epoch cannot be replayed.

Every server text frame is JSON:

{ v: 1, kind: "event" | "reply" | "error" | "stream", seq: number, ts: number, topic?: string, corr?: string, payload: unknown }
kind Meaning
event ordered, replayable feed event on a topic (has topic and seq)
reply answer to a client command (echoes corr); also the first hello frame
error command failure or protocol violation; the socket stays open
stream screencast control message on screencast:<id>; latest-wins, never replayed

Client frames are JSON objects discriminated on type (see client commands); any command may carry corr (1–64 characters), which is echoed on its reply or error.

Limit Value
maxInboundFrameBytes 16KiB
maxProtocolViolations 5
feedBufferCount 10000
feedBufferBytes 8MiB
feedBufferMs 5m
heartbeatMs 20s
staleMs 1m
tickMs 30s
overloadBytes 4MiB
overloadGraceMs 10s
screencastDropBytes 1MiB
Code Name Meaning
4401 UNAUTHORIZED not authenticated or the session expired; log in again
4403 PASSWORD_CHANGE_REQUIRED the operator must change the password first
4400 PROTOCOL_ERROR too many malformed frames
4406 BAD_SUBPROTOCOL missing or unknown Sec-WebSocket-Protocol (expected browserhive.v1)
1013 OVERLOADED the client did not read fast enough (backpressure limit exceeded)
1001 GOING_AWAY server shutting down, or the socket was silent too long
type Fields Scope
ping — any authenticated caller
subscribe topic: string, cursor?: integer any authenticated caller
unsubscribe topic: string any authenticated caller
screencast.start session_id: string, max_width?: integer, max_height?: integer, quality?: integer sessions:read
screencast.stop session_id: string sessions:read
screencast.set_size session_id: string, max_width: integer, max_height: integer sessions:read
input session_id: string, input: object sessions:takeover
session.set_viewport session_id: string, width: integer, height: integer sessions:write
logs.tail level?: one of error, warn, info, debug, trace, module?: string logs:read

input is accepted only while an attention request is open for the session and is re-checked on every message (INPUT_NOT_PERMITTED otherwise). session.set_viewport is an observability control and is not attention-gated.

Field names follow the Chrome DevTools Protocol (camelCase). modifiers is a bitmask: Alt=1, Ctrl=2, Meta=4, Shift=8.

Field Type Required Constraints
type "mouse" yes —
action one of mouseMoved, mousePressed, mouseReleased, mouseWheel yes —
x number yes ≥ 0; ≤ 100000
y number yes ≥ 0; ≤ 100000
button one of none, left, middle, right no —
clickCount integer no ≥ 0; ≤ 8
deltaX number no ≥ -10000; ≤ 10000
deltaY number no ≥ -10000; ≤ 10000
modifiers integer no ≥ 0; ≤ 15
Field Type Required Constraints
type "key" yes —
action one of keyDown, keyUp, char, rawKeyDown yes —
key string no max length 32
code string no max length 64
text string no max length 16
windowsVirtualKeyCode integer no ≥ 0; ≤ 255
modifiers integer no ≥ 0; ≤ 15
Field Type Required Constraints
type "touch" yes —
action one of touchStart, touchEnd, touchMove, touchCancel yes —
points object[] yes at most 10 items; each item: keys x, y, radiusX, radiusY, force, id
modifiers integer no ≥ 0; ≤ 15
payload type Fields
hello protocol: "browserhive.v1", protocol_version: 1, epoch: string, cursor: integer, server_version: string, now: integer
subscribed topic: string, from: integer, to: integer, complete: boolean
unsubscribed topic: string, ok: boolean
pong ts: integer
screencast.started topic: string, ordinal: integer
ok result?: any
resync_required topic?: string, reason: one of cursor_expired, epoch_changed, buffer_overflow

Subscribe with { "type": "subscribe", "topic": "<topic>", "cursor"?: <last seq> }. The reply subscribed { from, to, complete } says whether the replay was complete; complete: false (or resync_required) means reload from REST.

Topic Scope Events
sessions sessions:read session.opened, session.updated, session.closed, session.removed
attention attention:read attention.created, attention.resolved
vault.confirm vault:read vault.confirm.created, vault.confirm.resolved
vault.config vault:read vault.binding.changed, vault.policy.changed, vault.lock_state
vault.access vault:read vault.access
pages sessions:read page.visited
blocklist blocklist:read blocklist.hit, blocklist.reloaded
system system:read system.degraded, system.recovered, system.tick, system.capacity, retention.completed
logs logs:read log.record
notifications notifications:read notification.created, notification.updated
channels channels:read channel.changed, channel.removed, delivery.updated, action.recorded
session:<id> sessions:read session.opened, session.updated, session.closed, session.removed, session.warning, tool.called, page.visited, screenshot.captured, vault.access, blocklist.hit, attention.created, attention.resolved, vault.confirm.created, vault.confirm.resolved
screencast:<id> sessions:read stream messages meta, started, stopped, failed and binary frames

Payloads of kind: "event" frames, discriminated on type. DTO fields (session, row, request, …) are the same shapes the REST API returns.

Field Type Required Constraints
session object yes keys session_id, slug, owner, tenant_id, channel, engine, headless, incognito, persistence_mode, current_url, created_at, last_activity_at, closed_at, closed_reason, archived_at, lease_expires_at, lease_paused_at, lease_remaining_ms, state, live, disable_evaluate, vault_enabled, stealth, fingerprint, humanize, stealth_recorded, identity, browser, proxy_label, counts, has_live_viewers, harness, client

Field Type Required Constraints
session object yes keys session_id, slug, owner, tenant_id, channel, engine, headless, incognito, persistence_mode, current_url, created_at, last_activity_at, closed_at, closed_reason, archived_at, lease_expires_at, lease_paused_at, lease_remaining_ms, state, live, disable_evaluate, vault_enabled, stealth, fingerprint, humanize, stealth_recorded, identity, browser, proxy_label, counts, has_live_viewers, harness, client

Field Type Required Constraints
session_id string yes pattern ^([a-z][a-z0-9-]{1,31})-([0-9a-z]{8})$
closed_at integer yes ≥ 0
reason one of user, operator, lease_expired, crash, shutdown, interrupted, launch_failed yes —

Field Type Required Constraints
session_id string yes pattern ^([a-z][a-z0-9-]{1,31})-([0-9a-z]{8})$
action one of archived, unarchived, deleted yes —
at integer yes ≥ 0

Field Type Required Constraints
session_id string yes pattern ^([a-z][a-z0-9-]{1,31})-([0-9a-z]{8})$
code string yes —
message string yes —
details any no —

Field Type Required Constraints
row object yes keys event_id, session_id, tool, tab_id, ok, error_code, error_message, duration_ms, result_size_bytes, ts, trace_id, has_screenshot, harness, args_json, result_text
has_detail boolean yes —

Field Type Required Constraints
row object yes keys event_id, session_id, tab_id, url, title, domain, category, ts

Field Type Required Constraints
row object yes keys event_id, session_id, tool, kind, content_type, width, height, size_bytes, ts, url

Field Type Required Constraints
row object yes keys event_id, session_id, session_slug, tool_event_id, entry_name, handle, result, reason, evaluate_enabled, page_url, origin_check, principal_id, details, ts

Field Type Required Constraints
row object yes keys event_id, session_id, session_slug, tool_event_id, url, domain, pattern, source, tool, ts

Field Type Required Constraints
request object yes keys request_id, kind, session_id, session_slug, owner, reason, mode, options, status, message, resolved_by, resolution_reason, created_at, resolved_at, deadline_at, waited_ms, page_url, tool, event_id, entry_name

Field Type Required Constraints
request object yes keys request_id, kind, session_id, session_slug, owner, reason, mode, options, status, message, resolved_by, resolution_reason, created_at, resolved_at, deadline_at, waited_ms, page_url, tool, event_id, entry_name

Field Type Required Constraints
request object yes keys request_id, kind, session_id, session_slug, owner, reason, mode, options, status, message, resolved_by, resolution_reason, created_at, resolved_at, deadline_at, waited_ms, page_url, tool, event_id, entry_name

Field Type Required Constraints
request object yes keys request_id, kind, session_id, session_slug, owner, reason, mode, options, status, message, resolved_by, resolution_reason, created_at, resolved_at, deadline_at, waited_ms, page_url, tool, event_id, entry_name

Field Type Required Constraints
handle string yes pattern ^[a-z0-9][a-z0-9._-]{0,127}$
action one of created, updated, removed yes —

Field Type Required Constraints
group_id string or null yes —

Field Type Required Constraints
unlocked boolean yes —

Field Type Required Constraints
patterns integer yes ≥ 0
skipped integer yes ≥ 0
loaded_at integer yes ≥ 0

Field Type Required Constraints
event object yes keys event_id, code, severity, message, details, first_seen_at, last_seen_at, count, resolved_at

Field Type Required Constraints
event object yes keys event_id, code, severity, message, details, first_seen_at, last_seen_at, count, resolved_at

Field Type Required Constraints
now integer yes ≥ 0

Field Type Required Constraints
live integer yes ≥ 0
max integer or null yes —

Field Type Required Constraints
at integer yes ≥ 0
pruned_rows integer yes ≥ 0
result one of ok, partial, failed yes —
severity one of info, warn, error no —

Field Type Required Constraints
notification object yes keys notification_id, principal_id, type, title, body, session_id, session_slug, target, source_event_id, created_at, updated_at, count, read_at, dismissed_at, kind, category, severity, state, revision, thread

Field Type Required Constraints
notification object yes keys notification_id, principal_id, type, title, body, session_id, session_slug, target, source_event_id, created_at, updated_at, count, read_at, dismissed_at, kind, category, severity, state, revision, thread

Field Type Required Constraints
channel object yes keys channel_id, name, kind, mode, source, status, target, target_hint, secret_refs, secrets, rules, capabilities, ready, problem, failure_count, last_error, last_ok_at, last_failure_at, created_at, updated_at, stats, connection, reports

Field Type Required Constraints
channel_id string yes —

Field Type Required Constraints
delivery object yes keys seq, channel_id, channel_name, channel_kind, notification_id, notification_kind, notification_title, revision, op, status, reason, attempts, next_attempt_at, last_error, duration_ms, message_ref, created_at, updated_at, report

Field Type Required Constraints
action object yes keys seq, at, channel_id, channel_name, channel_kind, notification_id, notification_title, action_id, action_label, op, actor, actor_name, outcome, detail

Field Type Required Constraints
record object yes keys seq, ts, level, msg, module, trace_id, span_id, request_id, session_id, principal, transport, err; additional keys allowed

Start with screencast.start { session_id, max_width?, max_height?, quality? }; the reply screencast.started carries the ordinal that tags this screencast’s binary frames. Frames are latest-wins and dropped under backpressure; each viewer may request its own size with screencast.set_size.

type Fields
meta session_id: string, ordinal: integer, device_width: integer, device_height: integer, page_scale: number, offset_top: number
started session_id: string, ordinal: integer
stopped session_id: string, reason: one of stopped, session_closed, session_crashed, connection_closed
failed session_id: string, code: string, message?: string

Each binary frame is a 16-byte big-endian header followed by the JPEG bytes.

Offset Field Encoding
0 magic 4 ASCII bytes BHSC
4 ordinal u32
8 seq u32 (monotonic per screencast; a lower seq after a higher one is dropped)
12 width u16 (JPEG width)
14 height u16 (JPEG height)