Skip to content

Design Spec: Web Dashboard (web serve)

Document Control

Field Value
Status Implemented
Command surface canarchy web serve
Primary area CLI, presentation

Goal

Add a presentation tier that streams the existing JSONL event envelope to a small browser-based dashboard. The CLI remains the contract; the web layer is a view, like the TUI.

User-Facing Motivation

Operators triaging a capture want a live, glanceable view — frame stream, bus status, decoded signals, J1939 activity, UDS transactions — without leaving the structured-output model or installing a heavy GUI.

Requirements

ID Type Requirement
REQ-WEB-01 Ubiquitous The system shall provide a canarchy web serve --file <capture> [--dbc <ref>] [--bind <host:port>] [--rate <x>] [--loop] command that starts an HTTP + WebSocket server streaming the capture as canonical envelope events.
REQ-WEB-02 Ubiquitous The server shall be read-only: it shall expose no active-transmit endpoints, and every non-GET HTTP request shall be rejected with status 405 and a structured WEB_READ_ONLY error body.
REQ-WEB-03 Event-driven When a WebSocket client connects to /ws, the system shall stream the capture's events in timestamp order, paced by inter-event timestamp deltas divided by --rate (gaps capped at 1 s; --rate 0 disables pacing), and shall mark end-of-stream with an alert event carrying code STREAM_COMPLETE followed by a close frame, unless --loop restarts the stream.
REQ-WEB-04 Ubiquitous The streamed events shall be the canonical envelope objects: frame per frame; j1939_pgn per 29-bit frame annotated with the bundled PGN label/name and source-address name; decoded_message where a --dbc database decodes the frame; and uds_transaction for transactions reassembled from the capture — each carrying event_type, payload, source, and timestamp.
REQ-WEB-05 Ubiquitous GET / shall serve the bundled dependency-light single-file SPA (canarchy/resources/web/index.html, vanilla JS, no frontend framework) rendering the live frame stream, bus status, decoded signals, J1939 PGN/source-address activity, and recent UDS transactions. GET /api/status shall return server status JSON including read_only, event_count, clients, and source metadata.
REQ-WEB-06 Unwanted behaviour If the bind address is malformed or the port cannot be bound, the system shall return a structured error with code WEB_BIND_INVALID / WEB_BIND_FAILED and exit code 1; if the capture file is missing or invalid, the existing transport error codes apply with exit code 2.
REQ-WEB-07 Ubiquitous The WebSocket implementation shall be a minimal RFC 6455 server (handshake accept key, unmasked server→client text frames with 7/16/64-bit lengths, masked client-frame reads, ping→pong, close handling) with no third-party dependency.
REQ-WEB-08 Ubiquitous On startup the CLI shall emit a canonical envelope reporting the dashboard url, read_only: true, source metadata, and event_count, then serve until interrupted; web serve shall not be exposed as an MCP tool (long-running front end, like shell/tui).

| REQ-WEB-09 | Event-driven | When startup output is written to a pipe, the CLI shall flush the complete startup result before entering the serving loop in JSON, JSONL, text, and table modes, so launchers can read the resolved URL without enabling unbuffered Python output. |

Command Surface

canarchy web serve --file <capture> [--dbc <path|provider:ref>] \
    [--bind <host:port>] [--rate <multiplier>] [--loop] \
    [--offset <n>] [--max-frames <n>] [--seconds <s>] \
    [--json|--jsonl|--text|--table]

Default bind: 127.0.0.1:8474. Port 0 selects an ephemeral port (the startup envelope reports the resolved URL).

Responsibilities And Boundaries

In scope:

  • file-backed capture streaming over WebSocket with timestamp pacing
  • DBC-decoded signal events, J1939 PGN/source-address annotation, UDS transaction reassembly — all reusing the existing engine helpers
  • a bundled, dependency-light SPA served from package resources
  • read-only HTTP surface with structured errors

Out of scope (v1):

  • live interface streaming (capture files are the source; live follows later)
  • any active-transmit endpoint, regardless of flags
  • authentication/TLS — bind defaults to loopback; operators exposing the dashboard beyond localhost own the transport security

Architecture

  • src/canarchy/web.py — build_dashboard_events (engine → envelope events), minimal RFC 6455 helpers, and WebDashboardServer (ThreadingHTTPServer subclass; /, /api/status, /ws).
  • src/canarchy/resources/web/index.html — single-file vanilla-JS SPA.
  • emit_web_serve in cli.py — argument handling, structured startup envelope, serve loop.

Error Codes

Code Trigger Exit code
WEB_BIND_INVALID --bind not in <host>:<port> form or port out of range 1
WEB_BIND_FAILED Port already in use / cannot bind 1
WEB_READ_ONLY Non-GET HTTP request (HTTP 405 body, not a CLI exit) —
CAPTURE_SOURCE_* Existing transport errors for missing/invalid captures 2