Loading…
Reference in a new issue
No description provided.
Delete branch "fix/130-sync-auth-misclassification"
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?
Closes #130, closes #131.
The bug
A transient
/synctransport error was classified as a fatal auth failure, because the classifier substring-matched the stringified error — which embeds the request URL, and/synccarries?since=<token>:On 2026-08-11 the homeserver's sync stream sat at position
5401xxxx, so every bridge's token (54013070,54014591,54015213,54015517) contained401. Five bridges died in ten minutes. Three — the staticconfig.tomlones — stayed dead for three days, because a fatal verdict had no restart path; the tenant bridges came back only when an unrelated link approval triggered a reconcile.What changed
auth_error— a pureis_fatal_auth(status, errcode)plus amatrix_sdk::Erroradapter. A transport error (no HTTP response at all) is never fatal;401/M_UNKNOWN_TOKEN/M_MISSING_TOKENare, andM_FORBIDDENonly alongside a 403. Replaces the substring checks in both sync loops.AppserviceHttpErrornow carries a typed status so the join path stops matching on"403"too.BridgeOutcome; a failed one (error or panic) is respawned afterbridge_restart_base_ms * 2^n, capped bybridge_restart_max_ms(new[timing]keys, 5 s → 5 min), streak reset after stable uptime. Operator stops, reconcile replacements and shutdown never auto-restart. This also closes the reboot boot-race where a startup 502 killed every bridge permanently.<data_dir>/bridges.json— a roster naming every bridgealive/restarting/down, refreshed every 60 s as a dead-man's switch.ops/—nvb-watch.sh,nvb-backup.shand a conf example brought into the repo (they lived only on the prod host, unreviewed), with a bridge-liveness section that re-alerts daily while a bridge is down and putsbridges=N/Min the digest.ops/test-nvb-watch.shdrives the real watcher against stubbedjournalctl/systemctl/curl.generate_config_with_pool_sizelost its last non-test caller inad53ccd, soclippy --all-targets -D warningshas failed on master since the 0.3.1 release (run #289, 19 days). Gated behindcfg(test).Verification
cargo test --workspace --locked— 359 tests, 0 failures;clippy --all-targets -D warningsclean. CI #311 green.since=54015213classifies as transient, and the four real tokens are asserted to still trip the old substring predicate while the typed one lets them through.ops/test-nvb-watch.sh: 13/13. It caught two real bugs in the new section before it shipped —grep -vexiting 1 when it filters out every line (latch would have stuck forever), and the dying bridge's own ERROR line clearing its own latch (fixed by comparing log positions).Matrix sync error — retrying in 5 sand zerobridge failed, with the roster stayingaliveand itsupdatedstamp advancing. A corrupted appservice token produced a real401 Unauthorized→ still fatal → then restarts at 5 s → 10 s → 20 s → 40 s → 80 s, with the roster showingrestarting/consecutive_failures=5.Note on scope of the live test: the injected transport error carried token
55219851, which contains neither401nor403, so that run exercised the retry path rather than the misclassification itself — the token case is covered by the unit tests.