Commit Graph
2 Commits
Author SHA1 Message Date
EdnunpandClaude Fable 5 26a6fbfdf6 Pre-release hardening 1-3: nonce prefix widen + status-text dedup + stale comments
From the four-agent pre-release review (all read-only), the three items that warranted
fixing before ship:

1. NONCE PREFIX WIDENED (the one security-relevant finding). The interim counter-nonce
   used a 32-bit random prefix + 64-bit counter; since a fresh instance restarts the
   counter at 0, two instances that drew the same prefix would reuse nonces under the
   same long-lived key (catastrophic for AES-GCM), and 32 bits collides at only ~2^16
   instances. Now 48-bit random prefix + 48-bit counter: birthday bound ~2^24 instances
   while 2^48 packets/session stays far beyond any real session - strictly safer than
   both the interim scheme AND the original per-packet 96-bit-random nonce. Wire
   unchanged (receiver reads the nonce off the packet). Comment corrected.

2. STATUS-TEXT DE-DUPLICATED. The CheckedListBox spoken-status builder existed in two
   copies (CheckedListAccessibility + MainForm) with a comment falsely claiming they were
   "exact" - and they had already drifted (the remembered-apps empty-state line was in the
   MainForm copy only). Folded to ONE builder (CheckedListAccessibility.ApplyStatus +
   EmptyTextFor); MainForm delegates. NVDA wording is now identical in the main window and
   every dialog by construction. No user-visible change today; removes the silent-desync trap.

3. STALE COMMENTS. RemPacket still documented the pre-5.6 plaintext control wire format
   (2 bytes / 14 on the wire); it is now sealed (50 on the wire, the 2 bytes are inner
   plaintext). Fixed that and the MainForm.Peers.cs comment that misplaced CaptureSpecBuilder
   in Core (it is App by necessity). Docs only.

Plus review-flagged test top-ups: NonceSequence layout + two-instance-distinct-prefix
assert; ControlReceiveGuard future-dated rejection + 300-command burst (no false replay
across the prune threshold); log-rotation INTEGRITY (every line survives exactly once)
+ a 4-thread concurrency test that actually fails if writeGate is removed; ReleaseSigning
now honestly SKIPs (not caveat-PASSes) when the publisher key is absent.

Gate 69/69.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 09:57:25 +01:00
EdnunpandClaude Fable 5 6f91e0ed5e Peer extraction stage 3: peer cluster into MainForm.Peers.cs + heartbeat windows pinned
The god-object shrink, completed as pure code motion: the peer state (known/manual/
remembered/selected/named peers, endpoint anti-thrash) and the whole Peers section
(discovery reconciliation, endpoint follow, arming refresh, manual add, named-peers
dialog, remembered delete) move VERBATIM into MainForm.Peers.cs as a partial-class
split - the same pattern Andre's SensorReadout form uses. Same class, same members,
compiler-proven identical; MainForm.cs drops ~430 lines and the peer logic finally
has its own front door. The shared LOGIC under it (PeerArming, CaptureSpecBuilder,
PeerAddress) landed in stages 1-2.

Plus the last flagged test gap: the heartbeat Healthy->Stale->Unreachable windows are
now pinned against a CONTROLLED clock via a seam over the real derivation
(SnapshotHealthLocked, made static - it was already pure): 1s = Healthy, 3s = Stale,
6s = Unreachable, never-answered = pending first, Unreachable only after sustained
pinging. These are the numbers every peer's armed/pruned state hangs off in both the
app and the service.

Gate 61/61. This completes the review plan's Phase 4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 23:02:11 +01:00