1a3c481f4c6160e26f3643926046d7523dba430b
Closes the remaining insider resource-exhaustion vector the Codex Tier C
audit flagged: the active-roster cap bounds the live peer map, but a member
could join (<=32), drop the link without a signed Leave, let the grace timer
expire, and repeat with a fresh identity. Each abandoned identity grew two
unbounded structures and kept doing periodic work forever:
- known_peers[topic] (the retained rejoin/recovery dial table) was only
pruned on a signed PeerLeft, so grace-evicted ghosts accumulated.
- the recovery coordinator's active set + entries map had no identity cap
and no terminal retry budget — backoff saturated at 60s and re-dialed a
never-returning peer indefinitely.
Two non-breaking, dependency-free bounds (no wire/protocol change):
- MAX_RETAINED_PEERS=64 per topic via pure admit_retained() — refreshing a
tracked peer always succeeds, a brand-new identity is rejected when full.
Set above MAX_ACTIVE_PEERS=32 so legitimate rooms never hit it.
- RECOVERY_TERMINAL_ATTEMPTS=12 (~7 min) via pure recovery_is_terminal():
the coordinator gives up, frees the active slot, and signals a new
terminal channel; a small drain task forgets the retained address (so the
table self-drains), scrubs seen-connected state, and emits
PeerConnectionFailed.
Giving up never blocks a legitimate reconnect: a peer returning after a long
outage still rejoins on its own via a gossip announce — terminal eviction only
stops us from dialing a peer that is not coming back, which was a latent leak
even absent an attacker.
+2 pure-seam unit tests (admit_retained, recovery_is_terminal); 418 lib tests
green, clippy --all-targets clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>