ADR 0004: vault rollback resistance — evaluated, deferred (#71) - #119
Merged
Conversation
Spike deliverable for #71: design note answering all five spike questions, the keystone primitive #72/#108 build on, an honest residual, and a small unwired reference implementation behind an off-by-default feature. Method: empirical dep-cost measurement (cargo tree on macOS, diffed vs the workspace lock) + a fan-out research/adversarial-verification pass whose skeptics landed 16 attacks that reshaped the conclusion. Q1 (anchor crate): recommend `keyring` (pinned, per-OS native, default-features off) on macOS/Windows, file-only on Linux; measured cost is +1 crate on macOS, +4 (incl. the libdbus C lib) on Linux where the D-Bus Secret Service is also absent on the headless boxes signerd runs on. NEW DEP — approval-gated, NOT added here. Q2 (correctness): binding the epoch into core_bytes() so both AEAD tags cover it is the only construction that makes the file's epoch un-editable-without-passphrase — but it is a FORMAT_VERSION->2 change that breaks the frozen KAT, so it is specified and deferred, not landed. Check strictly after AEAD verify (no oracle to an unauthenticated caller); key on a re-seal-stable identity, not the per-seal random vault_id. Q3/Q4 (restore + residual): fail-open-with-confirm on the Control capability for testnet alpha; honest that delete-anchor->bootstrap bypasses the gate and that file-only Linux gives ~zero adversarial resistance. THREAT-MODEL residual row #7 added. Q5 (generalization): one StateAnchor over a namespaced record (vault/policy/cap), not a global counter; atomic+fsync+single-writer; the real consumers are #72 (policy version+MAC, fields that must be added first) and #108 (cap reserve-before-sign, with the intra-generation gap and clock-rollback corrected). The standalone vault detector is vacuous today (nothing bumps the vault epoch), so build order is: primitive -> policy/cap -> vault (v2). Prototype: signerd `state_anchor.rs` behind off-by-default `state-anchor`, unwired, zero new deps, 7 tests. DoD green (fmt, `just check` default+tray, `cargo test --workspace`); featured clippy+tests run separately. Refs #71 #72 #108. Relates ADR 0003 (#105).
Codex cross-model adjudication + the adversarial review agree the rollback anchor is mispriced for a testnet alpha whose threat model already concedes same-uid (incl. filesystem write). Vault rollback's entire worst case is reverting a passphrase/KDF rotation; the seed is constant and balances are on-chain; and the mechanism doesn't hold anyway (nothing advances the vault counter; sidecar is replayable; the anchor is same-uid-deletable/rewritable; only a TPM NV counter holds, and macOS has no equivalent). So: defer #71 with blockers B1-B3, build NO cross-trust-domain anchor, and decouple #72/#108 into independent local fixes (policy MAC + fail-closed; durable cap + reserve-before-sign) that never needed it. The "one shared mechanism for three issues" framing was the error; this supersedes ADR 0003 items #4-#6's sequencing. - Rewrite ADR 0004 as a plain Deferred record (no invented vocabulary). - Remove the unwired StateAnchor prototype, its feature flag, and lib wiring. - Reword THREAT-MODEL residual #7 to "accepted residual, deferred". Refs #71 #72 #108. Supersedes ADR 0003 (#105) keystone sequencing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Outcome of the #71 spike. After an empirical dependency-cost check, a fan-out adversarial review (16 attacks landed), and an independent Codex cross-model adjudication — all pointing the same way — the conclusion is to not build this, and to record why so it isn't re-proposed.
This PR is now doc-only:
docs/adr/0004-rollback-resistant-state-anchor.md(a plain Deferred record) + one rewordedTHREAT-MODEL.mdresidual row. The earlier unwired prototype, its feature flag, and the lib wiring have been removed.Why defer
vault.binback only reverts a passphrase/KDF rotation — and a same-uid attacker during an unlocked session already reads the live seed from RAM.What changes for the siblings
The "one shared mechanism for three issues" framing was the error. #72 and #108 do not depend on #71 and never needed an anchor:
policy.json+ fail-closed + an inert version field (local integrity).This supersedes the shared-foundation sequencing in ADR 0003 (items #4–#6); that ADR's findings/ranking stand.
Refs #71 #72 #108— closes none of them. #71 stays open as a parked issue carrying blockers B1–B3; #72/#108 proceed decoupled (their comments/bodies updated).DoD
Doc-only + a code removal;
cargo fmt --checkandcargo clippy -p deckard-signerd -- -D warningsgreen;Cargo.lockunchanged.