- 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.
66 lines
2.7 KiB
Markdown
66 lines
2.7 KiB
Markdown
# 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.
|