Status boards show state that background workers mutate — sync runs, curation runs, backups, agent runs. The screen never mutates what it shows, so cache invalidation tied to user actions can't refresh it, and per-screen polling timers don't scale. One platform push channel replaces them all.
/events/stream — one SSE per tab
The channel rides the cache Redis pub/sub — a lost nudge costs one delayed refresh, never data. Publishing is best-effort: a broken bus must not fail the worker's own transaction.
GET /api/v1/events/streamOne multiplexed stream per tab carries every push concern; the notification bell rides it too, so the platform holds a single persistent connection. The dispatcher keeps publishing bell nudges exactly as before — only the transport is shared.
| Event | Payload | Meaning |
|---|---|---|
hello |
{boards: […]} |
opens the stream: the boards this caller is subscribed to. The client invalidates them all — a reconnect catches up on everything missed. |
board |
{board} |
the named board changed; the client invalidates its mapped query keys. |
unread |
{count} |
the bell counter, refetched server-side on the bell nudge — unchanged behavior, new transport. |
ping |
{} |
heartbeat every 25 s of silence — a real event frame, not an SSE comment, so the client watchdog sees liveness. |
Access is decided at subscription time: when the stream opens, the caller's role picks the channel set — admin boards for admins, an agents channel per owner for everyone. No per-message filtering, no per-user fan-out for admin boards.
| Board | Fed by | Audience |
|---|---|---|
harvester |
Harvester sync-run journal + page-level progress counters | admins |
knowledge |
Knowledge Store curation runs · re-embed progress · backups | admins |
agents |
Agent Engine run journal, steps included | the agent's owner — own channel; admins — the all-agents channel |
What publishes. Every committed run transition (queued → running → terminal, cancel) and every committed mid-run progress write — a sync page boundary, a re-embed batch, an agent step. Publish follows the commit, so a nudge never announces state a refetch can't see yet.
What doesn't. Pure heartbeat touches — liveness bookkeeping is not a state change and must not wake every open screen.
Coalescing is the subscriber's job. Workers publish freely — a Redis publish is near-free; each stream connection coalesces per board with a minimum emit gap of 1.5 s, trailing-edge (a burst emits one frame now and one when the gap expires). A hot worker loop can never make a client refetch faster than the gap, and the last state always lands.
The shell mounts a single stream consumer holding a declarative registry
board → query-key prefixes. A board frame invalidates
that board's prefixes; only queries actually mounted refetch. Screens carry no
live-update code at all — no timers, no intervals: a screen is live because its
keys are in the registry, not because it polls.
hello on reopen invalidates all boards — the gap
is caught up wholesale.
staleTime 0 —
every screen visit refetches even with the stream down, plus the standard
refetch on window focus and network reconnect.
The built-in embedder's weight loading is the single live status that lives outside the platform's database — inside the embeddings runtime, which the backend only proxies. There is no transition of ours to publish from, so the embedder card keeps a narrow poll only while weights load. Everything else on every board is push. A second status of this kind would justify a server-side watcher; one does not.