- BLAKE3 keyed proof-of-work on object storage and auth challenges, with frozen vectors cross-checked against an independent Python reference implementing the single-block hash it needs. - Sparse Merkle trie over object IDs: order-independent roots, inclusion and absence proofs (internal/smt). - Signed checkpoint chain per relay: transport key amendment to INV-1, /v1/checkpoint/* and inclusion/absence proof endpoints, restart-safe epoch continuity (internal/checkpoint). - Head gossip with TOFU pinning and equivocation detection; light node (cmd/lightnode) that stores no history: quorum of pinned relays, every served object proven against the agreed root, LRU disk cache. - WebSocket streaming on relay and light node (coder/websocket): scoped channels mirroring REST, raw envelopes verified client-side; light node marks streamed objects unproven until checkpoint coverage. - Protocol v1 additions: DelegationClaim tag 0x07 with deterministic chain resolution in verify.Graph, KeyRotationRequest/Confirm tags 0x08/0x09 with hash-bound two-sided consent and Policy.RotationMaxAge; spec sections, frozen vectors appended byte-identically, Python reference extended. - Optional permissioned BFT finality over gossip (internal/bft): prevote/precommit with quorum certificates verifiable offline. - Quick wins: Policy.TrustedIssuers, per-type stored metrics, batch fetch, lexicographic lists with stable cursor pagination. - Security review of the network layer (docs/SECURITY-REVIEW.md) with findings F-01..F-09; hub send/close race and unstable pagination fixed under review. 12 packages green, vet/gofmt clean, protocol fuzzing stable.
2.7 KiB
Key rotation
How an identity changes its key without losing the trust built around the old one — and why two objects are needed to do it honestly.
Wire objects: KeyRotationRequest (tag 0x08) and
KeyRotationConfirm (tag 0x09), PROTOCOL.md §8.8–8.9.
1. Why two objects
A rotation needs evidence that both keys agreed:
- The successor signs a request: "I succeed predecessor." Alone it is worthless — anyone can name any predecessor, including a victim's.
- The predecessor signs a confirm bound to that exact request by content
ID (
rotation_hash = SHA-256(request TCE), the INV-4 pattern). Alone it is unintelligible.
Together they are cryptographic evidence of mutual consent. A stolen predecessor key can still rotate silently — cryptography cannot detect theft — but it can never point the identity at a key whose owner never consented, and the freshness bound below caps how long a silent thief keeps working.
2. Wire rules
- The request lives at most 60 seconds, the same format bound as approval requests. Rotation intent is short-lived; long-horizon freshness is policy, not wire.
- The confirm must sit inside the request window with the usual ±120 s skew allowance, exactly like an approval response.
- One relay stores at most one confirm per request; competing confirms make a link unusable rather than letting a verifier pick a winner.
3. Resolution
With Policy.TrustedIssuers set and a claim's issuer not a root, the
verifier walks confirmed successions backwards:
trusted root (old) ◀──confirm── new ◀──confirm── newer ──claim──▶ subject
- Every link must fully verify: both signatures plus the hash binding,
checked against stored bytes via
VerifyKeyRotationConfirm. - Hops are bounded by
Policy.MaxDepth(default for rotations: 4). - Freshness: with
Policy.RotationMaxAge > 0, a link's confirm must be no older than that many seconds at the evaluation instant. Zero disables the check — history stays valid forever, which suits long-lived roots whose rotation ceremony was audited once. - The accepted result reports the path in
Result.RotationChain(issuer → … → root) as audit evidence.
Rotation composes independently of delegation: an issuer may reach a trusted root either by being granted voice or by being the root's newer key. If both paths exist, delegation wins as the more specific statement; both chains are reported when applicable.
4. What rotation does not mean
- It does not transfer revocations: claims signed by the old key keep their own lifecycle; only the authority to be believed going forward moves.
- It does not heal compromise retroactively: anything signed before the confirm stands as signed.