2c. Config plane & realtime (under the hood)#
Heartbeat config de-duplication. The connector heartbeat response carries a full config block
(favicon, DB/redis/S3 creds, filter patterns, resource packs, language, network feeds) — previously
re-sent to every backend every ~3s. The panel now hashes the config per server and omits it when
byte-identical (the connector keeps its last config when the config key is absent — fully
backward-compatible, no connector change). Effect: on a quiet network the heavy block (and the DB
passwords in it) flow only on change / on (re)register, not every beat. A restarted connector
force-gets it via /register (which resets the per-server hash). Efficiency is visible at
GET /api/metrics → heartbeatDiff (sent, skipped, skipRate). Files: lib/heartbeat-diff.ts.
Event-driven realtime (live bus). Views that can't be cheaply timer-diffed update the instant a
write happens instead of polling. A tiny in-process bus (lib/live-bus.ts) bumps a topic version on
each write; the SSE endpoint GET /api/live?topics=… pushes {topic:version} on change; the client
useLive(topics, refetch) hook triggers an immediate refetch. Topics:
bans/logs/activity— Moderation + Activity pages (poll dropped to a 15–30 s safety net).state— bumped insidemutate()whenever the orchestration (groups/tasks) changes, so the Servers and Containers pages refetch instantly on any create/scale/delete/maintenance — across operators and tabs (the point of the RBAC work: operator A scales, operator B sees it live). Only a real groups/tasks change bumps it (an orchestration signature is diffed), not unrelated writes. Live instance status (booting→running) still comes from the poll +GET /api/stream.
This is separate from GET /api/stream (the connector's live server/player state, diffed every 500 ms).
Cross-node (HA) note: the bus is in-process, so a bump on one panel node reaches browsers on that node
instantly; other nodes catch up via the poll safety net (a Redis-backed bus is the future upgrade).