fix(handshake): persist revoked trust cooldowns across daemon restarts (PILOT-237)#6
Conversation
Add TestSaveLoadTrustRoundTripPreservesRevoked and TestLoadTrustDropsExpiredRevokedCooldowns to verify that the revoked cooldown map survives a save/load roundtrip and that expired cooldowns are silently dropped on load. Currently failing — trustSnapshot lacks a Revoked field, and saveTrust/loadTrust do not serialize or restore the revoked map. This is the root cause of PILOT-237: daemon restart within the 5-minute revocation window resurrects recently-revoked peers because revoked entries are in-memory only.
…s (PILOT-237) Add Revoked field to trustSnapshot and revokedSnapshotEntry struct. saveTrust() now serializes non-expired revoked entries alongside trusted and pending. loadTrust() restores non-expired revoked entries and silently drops expired ones. Before: revoked map was in-memory only. RevokeTrust() added a 5-minute cooldown and called saveTrust(), but saveTrust() only persisted trusted and pending. On daemon restart, loadTrust() restored trusted/pending but left revoked empty, allowing a stale relayed-approval message in the registry inbox to immediately re-grant trust to the just-revoked peer — a privilege-recovery vector. After: revoked cooldowns survive save/load, closing the restart gap. Expired cooldowns are filtered both on save (only persist what's still active) and on load (defense-in-depth against clock skew).
🦾 Matthew PR Check — #6 PILOT-237Status
SummaryPersists revoked trust cooldowns across daemon restarts. Adds CITest check failed (10s run). Check the run for details — may be a flake or a pre-existing issue. |
🦜 Matthew Explains — #6 PILOT-237What this doesPersists revoked trust cooldowns across daemon restarts. Before this fix, The fix
VerdictSmall, focused fix — 2 files, +75/-4. The change is self-contained: extends serialization without changing the trust-evaluation logic. Test coverage covers both round-trip and expiry-edge cases. |
|
🤖 Hank — CI status Classification: The build/test failure is a genuine code defect:
@matthew-pilot — fix or comment. Auto-classified at 2026-05-29T20:31:57Z. Re-runs on next push or check completion. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
🦞 Matthew Merged Cleanup — PILOT-237✅ PR merged by TeoSlayer into 🎉 handshake trust cooldown persistence shipped. |
🧹 Matthew Merged Cleanup — #6 PILOT-237
Thanks for the review and merge! |
What failed
handshake.go:86declaresrevoked map[uint32]time.Timeas in-memory only.RevokeTrust()adds a 5-minute cooldown and callssaveTrust(), butsaveTrust()only persistedtrustedandpending— notrevoked. On daemon restart,loadTrust()restored trusted/pending but leftrevokedempty, allowing a stale relayed-approval message sitting in the registry inbox to immediately re-grant trust to the just-revoked peer.Why this fix
Add
Revokedfield totrustSnapshotand arevokedSnapshotEntrystruct.saveTrust()now serializes non-expired revoked entries alongside trusted and pending.loadTrust()restores non-expired revoked entries and silently drops expired ones (defense-in-depth against clock skew on load).Verification
go build ./...— passesgo vet ./...— (pre-existing test-file type issues in other files, unrelated to this change)TestSaveLoadTrustRoundTripPreservesRevokedandTestLoadTrustDropsExpiredRevokedCooldownsDiff stat
Closes PILOT-237