docs(10): correct D-03's gate signal — key_exists is true on every booted node

D-03 named NodeIdentity::key_exists as one of the two gate signals. Server::new
(server.rs:63-72) calls load_or_create on both branches, and load_or_create
(identity.rs:48-51) generates and writes a random temporary node key when none
exists — so key_exists is true on any node that has booted once, onboarded or
not. A gate keyed on it would refuse seed.generate on a fresh node and brick
onboarding fleet-wide.

The flaw came from the audit's own suggested remediation (§214-221) and was
repeated in the planning brief; the planner caught it against the code.

D-03's intent (two signals, OR-ed, fail safe on drift) is unchanged. Corrected
signal set: is_setup() / is_onboarding_complete() / seed_exists(), pinned by a
test rather than a comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-01 07:07:06 -04:00
co-authored by Claude Opus 5
parent 04c4d3460c
commit 2781bd5a6c
@@ -40,11 +40,26 @@ derivation paths, word counts, or the at-rest encryption envelope.
from `UNAUTHENTICATED_METHODS` — so this path does not need to be built, and it is not itself
a bypass.
- **D-03:** The gate **refuses if *either* signal says onboarded**the on-disk key file
(`NodeIdentity::key_exists`, `identity.rs:117`) **or** the onboarding flag
(`AuthManager::is_onboarding_complete`, `auth.rs:182`). Fails safe when the two disagree,
which is a real state: `auth.rs:196-207` already carries auto-heal logic for exactly that
drift. Do not pick one signal and trust it alone.
- **D-03:** The gate **refuses if *either* signal says onboarded**never one signal trusted
alone. Fails safe when signals disagree, which is a real state: `auth.rs:196-207` already
carries auto-heal logic for exactly that drift.
- **D-03a (mechanism correction, 2026-08-01 — intent of D-03 unchanged):** D-03 originally named
`NodeIdentity::key_exists` (`identity.rs:117`) as one of the two signals. **That signal is
unusable and would have bricked onboarding on every fresh node.** `Server::new`
(`server.rs:63-72`) calls `NodeIdentity::load_or_create` on *both* branches of its
fresh-vs-existing check, and `load_or_create` (`identity.rs:48-51`) generates and writes a
random temporary node key when none exists. So `key_exists` is `true` on every node that has
booted even once, onboarded or not — a gate keyed on it refuses `seed.generate` on a node that
has never been onboarded.
This flaw was inherited from the audit's own suggested remediation
(`ENTROPY-SEED-AUDIT-2026-07-31.md:214-221`) and repeated in the planning brief; the planner
caught it against the code. The corrected signal set is `is_setup()` /
`is_onboarding_complete()` / `seed_exists()` — all false on a fresh node and during the seed
steps, all true afterwards. `key_exists` and `fips_key_exists` are rejected with recorded
verdicts, and the correction is pinned by a test
(`allows_on_fresh_temp_dir_even_though_node_key_exists`), not a comment.
- **D-04:** 10-01 covers **every method in `UNAUTHENTICATED_METHODS` that can mutate identity
or credentials**, behind the same gate and one shared test suite — not just the two endpoints