Gateway identify-storm circuit breaker — protect puppet tokens from Discord's 1000-connections/day auto-reset #129
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#129
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?
Motivation — 2026-08-08 incident (mautrix-discord, same class of failure)
On 2026-08-07 21:38 UTC, Discord abruptly closed the mautrix-discord text bridge's established gateway connection with
4002: Error while decoding payload. The client library (discordgo) then looped for ~5 hours: reconnect → resume rejected (Op9) → fresh IDENTIFY → READY → dead again ~15 s later. ~1,333 IDENTIFYs crossed Discord's 1000-connections/day protection and Discord automatically reset the bot's token (email to owner). That bridge's Discord side was dead until manual re-login.Nothing in nether-voicebridge caused this — but the same failure class applies here:
ShardQueuerretries identifies forever with only a ~5 s floor. A server-side rejection loop (Discord change, glitch, or client connection-state corruption) would burn ~1000 identifies within a day → puppet bot token auto-reset → that puppet is dead until an operator mints a new token and edits the config.serenitytargets (0 serenity lines in 14 days of journal).Mitigations already deployed (ops-side)
RUST_LOGnow appendsserenity=infoso gateway lifecycle lines ("Attempting to reconnect", "Received session invalidation", "Failed to resume", …) reach the journal (pending service restart).Asks (code-side)
serenity=infoto the built-inEnvFilterdefault incrates/bridge/src/main.rsso every deployment gets gateway visibility without env config.Design note: parking must go through the puppet-pool allocator (per-(bot,guild) unit, #46) so anchors fail over rather than leaving a channel uncaptured.
Implementation on branch
feat/129-identify-circuit-breaker(ff36121), awaiting review/merge. All three asks:serenity=infoadded to the built-in EnvFilter default — every deployment gets gateway lifecycle visibility without env config.ready= one completed IDENTIFY (a successful RESUME fires noready), so per-bot ready-counting counts exactly what Discord's 1000/day budget counts — which also answers ask 3 (we count IDENTIFYs, not just session deaths). 10 readies in 10 min → the bot is parked: allocations fail over through the per-(bot,guild) allocator (anchors included — reuses the join-timeout failover path), guild footprint cleared, command loop retired, gateway shut down via ShardManager. Loud ERROR (nvb-watch forwards it).run()rebuilds the client; the freshreadyre-seeds guilds and rebalances. Worst case a still-broken bot spends ~11 identifies per ~70 min (≈200/day) — safely inside budget while still self-healing.Hermetic tests cover the window math and the park→skip→re-arm allocator lifecycle (367 workspace tests green). Live storm injection isn't reasonably testable — the ops-side tripwire (🌪 in nvb-watch) plus the new serenity logs are the observability net.
Merged to master (
40de972) and released in v0.3.3. serenity=info default logging, per-bot IDENTIFY counting (10 in 10 min parks the bot via the allocator with failover + gateway shutdown), 1h cooldown re-arm. Closing.