ADR-0018 - Live calls dashboard: SSE vs WebSocket
- Status: Accepted (2026-05-21)
- Deciders: Massimo Bagnoli, Claude
- Implementation tasks: TASK-192, TASK-195
- Supersedes: nessuno
- Superseded by: nessuno
Context
The NOC /live-calls dashboard needs a real-time stream of call.* events.
The dashboard observes current call state; it does not need to send control
messages back to the server.
Three transport options were considered:
- Server-Sent Events.
- WebSocket.
- REST polling every 1-5 seconds.
Decision
Use Server-Sent Events.
The backend exposes /api/v1/live/calls as a standard EventSource endpoint
using sse-starlette. kam-cdr-bridge also publishes NATS events on
call.events.<phase> as ephemeral pub/sub, separate from the durable
cdr.raw JetStream path.
Redis keeps an active_calls sorted set used for initial snapshot population
and reconnect resync.
Access is scoped by role:
- Admin, operator and NOC users can observe all live calls.
- Regular users are limited to their own
company_id.
Rationale
- The stream is unidirectional from server to browser, which matches SSE.
- SSE works over HTTP/1.1 through Caddy without WebSocket upgrade handling.
- Browser
EventSourceprovides built-in reconnect behavior. - SSE avoids WebSocket lifecycle concerns such as ping/pong and idle detection.
- Polling adds request overhead and has 1-5 second latency instead of sub-second event delivery.
Consequences
Positive
- The frontend can subscribe with the native browser EventSource API.
- Caddy proxy configuration stays simpler than a WebSocket path.
- Reconnect behavior is predictable and supported by browser defaults.
Negative
- SSE is server-to-client only; future bidirectional control actions would need a separate API or a new transport decision.
- A large number of concurrent clients can create backpressure on the backend and NATS subscription path.
Limits
The pilot target is 100+ concurrent SSE clients. GA scale-out and backpressure handling are deferred to a future task if production demand exceeds that target.
Alternatives considered
WebSocket
Rejected. Bidirectional transport is overkill for an observational NOC dashboard and adds connection lifecycle and Caddy upgrade complexity.
REST polling
Rejected. It increases traffic and CPU with worse latency than event streaming.
References
- ADR-0016: CDR pipeline implementation and bridge event source.
- TASK-192: backend SSE endpoint.
- TASK-195: frontend EventSource subscriber.