ops(nvb-watch): no per-bridge liveness check — 3 bridges dead for 3 days while daily digests read healthy #131
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
dark/nether-voicebridge#131
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
nvb-watchdetected the 2026-08-11 bridge outage once, then reported the instance healthy for three days while three bridges were dead. Detection is event-based; there is no bridge-level liveness state check.Timeline (NVB Ops room)
The single alert fired correctly, at 01:30 on a Monday night, and was missed. Nothing repeated it.
Why the monitor went quiet
nvb-watch.shscans a 15-minute journal cursor window (journalctl --after-cursor). Once the five ERROR lines scrolled past, every later run sawerr=0. A dead bridge is silent, so silence reads as health.💀only checkssystemctl is-active. The process was alive; only 3 of its 9 bridges weren't. Same masking already documented for the reboot boot-race — this is the second time it has hidden a real outage.err=0 warn=0 svc=activelooked reassuring every single day of the outage.Fix
Interim (no release needed): latch on
channel unregistered from puppet pool— the exact death signature — intostate/bridges.down; alert once, then re-alert daily while the latch is non-empty; clear with a ✅ when that bridge registers again.nvb-ops.confalready carriesNVB_CONFIG, so the configured roster is available for comparison.Robust: have the supervisor write a machine-readable roster to
<data_dir>/bridges.json(sibling of the existingaudit.jsonl) on every state transition plus a periodic refresh:nvb-watchthen alerts when any bridge is notalive, or whenupdatedgoes stale (>5 min — dead-man's switch for the writer), and addsbridges=N/Mto the daily digest so that a healthy digest actually proves the bridges are alive — precisely what failed here.Also:
nvb-watch.sh,nvb-backup.shand a sanitisednvb-ops.conf.examplecurrently exist only on the prod host, unreviewed and unbackuped. Bring them into the repo underops/and pointdocs/operations-runbook.mdat them.Related gap found while fixing this: nothing watches
ci.ymleither.The hermetic CI gate has been red on master since the 0.3.1 release commit
ad53ccdon 2026-07-26 (run #289) — 19 days — because that commit orphanedgenerate_config_with_pool_sizeandclippy --all-targets -D warningsfails on dead code. Nobody noticed, for the same structural reason as the bridge outage: the nvb-watch Actions guardrail (section 3b) polls onlye2e.ymlscheduled runs.Fixed the dead code in #132, but the monitoring hole stands. Worth extending the guardrail to alert when the newest
ci.ymlrun on master is failing — cheap, same API call, same state-latch pattern.Shipped in v0.3.2 (https://nether.codes/dark/nether-voicebridge/releases/tag/v0.3.2), deployed to prod 2026-08-14 18:16 UTC.
Prod verification: all 9 bridges report
joined Matrix roomafter the restart,/var/lib/nether-voicebridge/bridges.jsonlists 9/9 alive with itsupdatedstamp advancing every 60 s, and the ops digest now carriesbridges=9/9. The only journal ERROR since the restart is a pre-existing stale federated invite fromqwertyad.one, unrelated to this work.Live fault-injection results (run on the build host against the test fixture, prod untouched):
Matrix sync error — retrying in 5 s, zerobridge failed, bridge recovered on its own;401 Unauthorizedstill classified fatal → automatic restarts at 5 / 10 / 20 / 40 / 80 s, roster showingrestarting,consecutive_failures=5.