Milestone 6: hard-refuse flip, scoped to INVALID only -- closes Milestone 6

Before flipping, found CAPSULE_SIG_MISSING (no signature at all) is the
normal state everywhere except this machine -- CI and any other
checkout have no access to the offline key, by design. Refusing on
MISSING the same as INVALID would brick boot everywhere but here.
Decided (on request): enforce ONLY on CAPSULE_SIG_INVALID (a signature
that IS present but doesn't verify -- unambiguous tampering/corruption
evidence). MISSING/NO_ROOT_KEY stay WARN-only permanently.

All three capsule_birth.c call sites now return CAPSULE_RUN_ERR_INVALID
on CAPSULE_SIG_INVALID, after logging the same WARN as before.

Verified on all three architectures, both directions, per the original
rollout commitment: positive case (real signed capsules) reboots clean
with zero warnings on amd64/aarch64/riscv64. Negative case (same
one-byte signature corruption used for the WARN-only proof, on Mama's
own init.4th) now genuinely refuses identically on all three:
"capsule sig: init.4th: INVALID" then "Init: Mama birth FAILED". The
feared "no ok> at all" blast radius didn't materialize -- kernel_main.c
already had graceful error handling for a failed Mama birth (log and
continue, pre-existing code); the kernel reaches a degraded ok> rather
than crashing, on all three architectures. Final acceptance pass (real
signed capsules, tampering reverted) clean on all three.

Milestone 6 is now fully closed except magic-number content-type
detection (shared with Milestone 4, separate scope, not started).
Documented in FABRIC-3.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd
This commit is contained in:
Robert Allan James
2026-08-26 22:08:20 -04:00
co-authored by Claude Sonnet 5
parent c640f99211
commit 030a3e6dc0
11 changed files with 63302 additions and 13 deletions
+26
View File
@@ -823,6 +823,32 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
magic-number content-type detection (Section U item 14, shared with Milestone 4, not
started) and the hard-refuse flip (deliberately deferred).**
**Hard-refuse flip, done 2026-08-26 -- and it landed narrower than the original plan
assumed.** Before flipping, found that `CAPSULE_SIG_MISSING` (no signature at all) is the
*normal* state on every machine except this one -- CI and any other checkout have no access
to the offline key, by design. Refusing on `MISSING` the same as `INVALID` would brick boot
everywhere but here, not catch anything real. **Decided (on request): enforce ONLY on
`CAPSULE_SIG_INVALID`** (a signature that IS present but doesn't verify -- unambiguous
tampering/corruption evidence, safe to refuse on regardless of who's building).
`MISSING`/`NO_ROOT_KEY` stay WARN-only permanently, not as a transitional state.
All three `capsule_birth.c` call sites now `return CAPSULE_RUN_ERR_INVALID` on
`CAPSULE_SIG_INVALID`, after logging the same WARN as before. **Verified on all three
architectures, both directions, exactly per the original rollout commitment:** the positive
case (real signed capsules) reboots clean with zero warnings on amd64/aarch64/riscv64,
identical to before this flip. The negative case (the same one-byte signature corruption
technique used for the WARN-only proof, on Mama's own `init.4th`) now genuinely refuses --
`capsule_birth_mama()` returns the error -- on all three architectures identically:
`capsule sig: init.4th: INVALID` followed by `Init: Mama birth FAILED`. **The feared "no
ok> at all" blast radius didn't materialize**: `kernel_main.c` already had graceful
error handling for a failed Mama birth (log and continue, pre-existing code, not written for
this feature) -- the kernel reaches a degraded `ok>` rather than crashing or hanging, on all
three architectures. Final acceptance pass (real, correctly-signed capsules, all tampering
reverted) clean on all three, zero warnings, conservation intact, real disk untouched.
**Milestone 6 is now fully closed** except magic-number content-type detection (shared with
Milestone 4, genuinely separate scope, not started).
### From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust tiers
- [ ] Create the `capsules/contrib/` directory (mechanically trivial, matches existing
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-27T01:40:17Z -->
<!-- Generated by mkcapsule --manifest 2026-08-27T02:05:00Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
+11 -7
View File
@@ -5,13 +5,17 @@
* signature/behavior stays untouched; this is a new, additive check
* called alongside it, not folded into it.
*
* Currently WARN-only, not enforced -- see FABRIC-3.md's Milestone 6
* rollout decision. A bug here has a larger blast radius than most of
* this project's other checks: a false refusal on Mama's own capsule
* would mean no `ok>` at all, on any architecture. Land warn-only,
* prove correct against both a valid and a deliberately-corrupted
* capsule on all three architectures, then flip to hard-refuse
* separately.
* Enforced ONLY on CAPSULE_SIG_INVALID (2026-08-26, after landing
* WARN-only and proving correct on all three architectures against both
* a valid and a deliberately-corrupted capsule -- see FABRIC-3.md's
* Milestone 6 writeup). CAPSULE_SIG_MISSING and CAPSULE_SIG_NO_ROOT_KEY
* stay WARN-only, deliberately: MISSING is the normal state on every
* machine without access to the offline signing key (CI, any other
* checkout) -- refusing on it would brick boot everywhere but the one
* machine that minted the key, not catch anything real. Only INVALID
* (a signature that IS present but does not verify) is unambiguous
* tampering/corruption evidence, safe to refuse on regardless of who's
* building.
*/
#ifndef STARKERNEL_CAPSULE_SIG_H
#define STARKERNEL_CAPSULE_SIG_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+16 -5
View File
@@ -416,9 +416,13 @@ CapsuleRunResult capsule_birth_mama(
CapsuleValidateResult vr = capsule_validate(mama_cap, arena, dir->arena_size, 1);
if (vr != CAPSULE_VALID) return CAPSULE_RUN_ERR_INVALID;
/* Milestone 6 (Phase 8): WARN-only signature check, not yet enforced --
* see capsule_sig.h's own doc comment for why (blast radius: a false
* refusal here means Mama herself never births, no ok> on any arch). */
/* Milestone 6 (Phase 8): signature check. Enforced ONLY on INVALID (a
* signature that IS present but does not verify -- unambiguous
* tampering/corruption evidence). MISSING and NO_ROOT_KEY stay
* WARN-only: MISSING is the normal state on every machine without
* access to the offline signing key (CI, any other checkout) --
* refusing on it would brick boot everywhere but the one machine
* that minted this key, not catch anything real. See capsule_sig.h. */
{
int idx = (int)(mama_cap - descs);
CapsuleSigResult sr = capsule_verify_signature(
@@ -426,6 +430,7 @@ CapsuleRunResult capsule_birth_mama(
if (sr != CAPSULE_SIG_OK) {
log_message(LOG_WARN, "capsule sig: %s: %s",
names[idx].name, capsule_sig_result_str(sr));
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
}
}
@@ -488,7 +493,9 @@ CapsuleRunResult capsule_birth_baby(
CapsuleValidateResult vr = capsule_validate(cap, arena, dir->arena_size, 1);
if (vr != CAPSULE_VALID) return CAPSULE_RUN_ERR_INVALID;
/* Milestone 6 (Phase 8): WARN-only, see capsule_sig.h. */
/* Milestone 6 (Phase 8): enforced only on INVALID -- see the fuller
* comment in capsule_birth_mama() above for why MISSING/NO_ROOT_KEY
* stay WARN-only. */
{
int idx = (int)(cap - descs);
CapsuleSigResult sr = capsule_verify_signature(
@@ -496,6 +503,7 @@ CapsuleRunResult capsule_birth_baby(
if (sr != CAPSULE_SIG_OK) {
log_message(LOG_WARN, "capsule sig: %s: %s",
names[idx].name, capsule_sig_result_str(sr));
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
}
}
@@ -634,7 +642,9 @@ CapsuleRunResult capsule_run_experiment(
CapsuleValidateResult vr = capsule_validate(cap, arena, dir->arena_size, 1);
if (vr != CAPSULE_VALID) return CAPSULE_RUN_ERR_INVALID;
/* Milestone 6 (Phase 8): WARN-only, see capsule_sig.h. */
/* Milestone 6 (Phase 8): enforced only on INVALID -- see the fuller
* comment in capsule_birth_mama() above for why MISSING/NO_ROOT_KEY
* stay WARN-only. */
{
int idx = (int)(cap - descs);
CapsuleSigResult sr = capsule_verify_signature(
@@ -642,6 +652,7 @@ CapsuleRunResult capsule_run_experiment(
if (sr != CAPSULE_SIG_OK) {
log_message(LOG_WARN, "capsule sig: %s: %s",
names[idx].name, capsule_sig_result_str(sr));
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
}
}