Expand description
WebSocket → NATS bridge.
GET /ws/events upgrades the connection, subscribes to
cellos.events.> (or the optional ?subject= override), and forwards
every NATS message as a JSON envelope:
{ "seq": 12345, "event": { /* CloudEvent */ } }seq is the cursor described in ADR-0015. When the underlying
subscription is a JetStream consumer it is the JetStream stream
sequence; when it is a core-NATS subscription (today’s MVP path) it
is a per-connection monotonic counter. Either way the contract on
the wire is the same: seq is monotonic, and the snapshot
endpoint’s cursor is comparable to it on the same broker.
§ADR-0015 §D3 — ?since=<seq> resume
Clients open /ws/events?since=<cursor> to resume after a
reconnect. With a JetStream consumer this maps directly to
DeliverPolicy::ByStartSequence { OptStartSeq: since + 1 }. With
the core-NATS bridge the parameter is accepted but cannot replay
history — the bridge only delivers messages published after the
subscription was created. The contract is preserved (every frame
still carries seq) so clients work transparently against either
bridge; migration to JetStream is tracked as a follow-up. Omitting
since keeps today’s “new messages only” behavior for ad-hoc
subscribers (cellctl --follow, debug tools).
§ADR-0015 §D6 — heartbeat
A WebSocket can sit idle on a quiet stream without either side
knowing the underlying socket has died. The server sends a Ping
frame every 25 seconds when idle; axum’s WebSocket type handles the
pong roundtrip transparently.