Files
roboco/docs/api/websockets.md
T
Renn F 3441e37120 [sweep] strip Fxxx audit-ID tokens + trim bloated comments/docstrings + add behavior-change docs
Post-audit sweep over the 135 audit-fix commits since 19a474d3:

1. Stripped every # Fxxx: audit-ID token from comments AND every Fxxx token
   from docstring openings across 211 blocks / ~626 lines. The CEO flagged
   these twice: audit-issue IDs in code confuse future devs/agents. The
   descriptive text is preserved; only the Fxxx token is removed (and bloated
   narrative blocks trimmed to 1-3 lines keeping the one non-obvious invariant).
2. Trimmed bloated comments/docstrings to the concise standard (1-3 lines).
3. Added missing behavior-change docs for the audit-fix batch: prompts/roles
   (documenter, pr_reviewer, qa), user-facing docs (api auth, websockets,
   agent-gateway, megatask, merge-model, task-lifecycle, grok, resilience,
   conventions, panel, security, troubleshooting), and the RAG corpus (cell-pm,
   main-pm, pr-reviewer, qa roles; conventions; messaging-tools; escalation;
   megatask; task-claiming workflows).

Comment/docstring/prose ONLY — zero code-line edits (verified: the diff
contains no def/class/return/if/for/await/assignment/call lines). Gates green:
ruff format + ruff check clean, mypy clean on roboco/. The only pytest failures
are the pre-existing sync_branch tracing-decision gap (B1, 250be5c2) — not
sweep-caused and tracked separately.
2026-06-29 01:25:40 +02:00

4.4 KiB

WebSockets

RoboCo pushes live updates over WebSocket endpoints under /ws, served by the orchestrator (roboco/api/websocket.py) and routed through nginx alongside the REST API. A single in-process ConnectionManager holds per-resource connection sets and broadcasts events to them. The panel consumes all of these through its useWebSocket("/<endpoint>", …) hook — you rarely connect to them directly, but they're the same streams an integrator can subscribe to.

The endpoints

There are four per-resource streams plus one operator-wide stream:

Endpoint Stream Auth
/ws/channels/{channel_id} Live messages in a team channel agent_id query param, validated against the DB + channel access; CEO panel token required in secure mode
/ws/agents/{agent_id} An agent's output and lifecycle events viewer_id/agent_id query param, validated against the DB; CEO panel token required in secure mode
/ws/sessions/{session_id} Messages in a communication session agent_id query param, validated; CEO panel token required in secure mode
/ws/notifications/{agent_id} An agent's notifications agent_id query param, validated; CEO panel token required in secure mode
/ws/system Operator/system-wide stream — no per-agent keying Unauthenticated, read-only (operator-only by design; not token-gated)

All sockets support a ping/pong keepalive: send {"type": "ping"} and you'll get a pong back.

!!! info "Secure mode now covers the per-agent streams" When ROBOCO_AGENT_AUTH_REQUIRED=true, the four per-resource sockets require the CEO panel token (the signed X-Agent-Token nginx injects for the panel) on top of their agent_id/viewer_id DB validation — an agent on the Docker network can no longer subscribe to another agent's stream unauthenticated. /ws/system is intentionally left operator-only and read-only. A forged token is rejected even in dev mode. See Authentication.

How events reach the sockets

Server-side events are published to an in-process StreamEventBus. The bridge in roboco/api/websocket_bridge.py subscribes to it and registers a _handle_* forwarder per event type, mapping each EventType to the right socket broadcast.

flowchart LR
    S[Service / orchestrator] -->|publish EventType| B[StreamEventBus]
    B --> WB[websocket_bridge _handle_*]
    WB --> R[/ws/channels, /ws/agents, /ws/sessions, /ws/notifications/]
    WB --> SYS[/ws/system/]
    R --> P[Panel useWebSocket hook]
    SYS --> P

To add a new live event you define an EventType, publish it to the bus, add a _handle_* forwarder in websocket_bridge, and consume it on the panel via the same hook — you never stand up a parallel endpoint.

Event types

Event Arrives on What it carries
RATE_LIMIT_HIT /ws/system A provider just hit a rate limit / overload and was parked. Drives the panel's amber rate-limit banner.
RATE_LIMIT_LIFTED /ws/system A parked provider recovered; queued work resumes. Clears the banner.
USAGE_SNAPSHOT /ws/system A fresh token-usage/cost snapshot. Drives the live "Token Usage & Cost" dashboard.
NOTIFICATION_SENT / NOTIFICATION_ACKED /ws/notifications/{agent_id} A notification was sent to or acknowledged by an agent.
SESSION_CREATED / SESSION_CLOSED / SESSION_TIMEOUT /ws/sessions/{session_id} Communication-session lifecycle.
AGENT_SPAWNED / AGENT_STOPPED / AGENT_WAITING / AGENT_RESUMED / AGENT_ERROR /ws/agents/{agent_id} Agent runtime lifecycle transitions.

Each forwarded message is a JSON object with a type field (the event-type name above) merged with the event's data.

REST fallbacks

The two operator dashboards that ride /ws/system fall back to HTTP polling when the socket is down, so the panel keeps working without the stream:

Live event HTTP fallback
RATE_LIMIT_HIT / RATE_LIMIT_LIFTED GET /api/system/rate-limits
USAGE_SNAPSHOT GET /api/usage/summary?period=24h|7d|30d

See Cost & usage and Health & metrics for what the panel does with these.

Next

  • REST API — the /api/* route map and the error envelope.
  • Authentication — the WebSocket-auth caveat in full.