orchestrator/sm: commit SVN floor on proven boot, not activation Add BootConfirmed event + CommitSvnFloor effect; Ready emits the commit only on a proven-healthy boot, decoupling the anti-rollback floor advance from ActivateUpdate. Conforms to CSA resiliency: the floor advances after a higher-SVN image successfully boots and is deemed stable.
diff --git a/services/orchestrator/sm/src/lib.rs b/services/orchestrator/sm/src/lib.rs index bb9fc22..d2265cf 100644 --- a/services/orchestrator/sm/src/lib.rs +++ b/services/orchestrator/sm/src/lib.rs
@@ -486,6 +486,14 @@ State::Ready => match event { Event::UpdateRequest => Outcome::Transition(State::Updating), + // Proven-boot checkpoint: the image authenticated at + // `ActivateUpdate`, but the SVN floor only advances now, once + // the shell reports it healthy. Handled in place — confirming a + // running image is not a state change. + Event::BootConfirmed(id) => { + ctx.emit(Effect::CommitSvnFloor(*id)); + Outcome::Handled + } _ => Outcome::Super, },
diff --git a/services/orchestrator/sm/src/model.rs b/services/orchestrator/sm/src/model.rs index 0bf3d86..d13891c 100644 --- a/services/orchestrator/sm/src/model.rs +++ b/services/orchestrator/sm/src/model.rs
@@ -195,6 +195,11 @@ UpdateVerified, /// The staged update failed authentication. UpdateRejected, + /// The activated image proved itself healthy at runtime (supervised + /// health / attestation pass — not mere boot). Gates the anti-rollback + /// commit: only now is it safe to advance the SVN floor past this image, + /// because it has demonstrated it runs, not merely that it authenticated. + BootConfirmed(ComponentId), /// This component was found corrupt at runtime. CorruptionDetected(ComponentId), /// This component's golden image has been restored. @@ -234,6 +239,13 @@ StageUpdate, ActivateUpdate, DiscardStaged, + /// Advance the anti-rollback (SVN) floor past `id`'s now-confirmed image. + /// Deliberately decoupled from [`ActivateUpdate`]: activation happens on + /// authentication (`UpdateVerified`), but the floor may only move once the + /// image has proven healthy ([`Event::BootConfirmed`]). Committing the + /// floor earlier would burn anti-rollback on an image that authenticated + /// but has not yet demonstrated it can boot and run. + CommitSvnFloor(ComponentId), RestoreGoldenImage(ComponentId), /// Report that a component has been isolated (held in reset and removed /// from the trust chain) so management software is aware the platform is
diff --git a/services/orchestrator/sm/src/tests.rs b/services/orchestrator/sm/src/tests.rs index 5055b0a..f41979a 100644 --- a/services/orchestrator/sm/src/tests.rs +++ b/services/orchestrator/sm/src/tests.rs
@@ -1025,6 +1025,42 @@ assert!(!effects.contains(&Effect::RestoreGoldenImage(C0))); } +/// The anti-rollback floor is committed only on a proven-healthy boot, never +/// at activation. `UpdateVerified` activates the image (authentication) but +/// must NOT emit `CommitSvnFloor`; a later `BootConfirmed` (the runtime health +/// proof) is what advances the floor. This pins the decoupling so activation +/// can never silently commit the floor early. +#[test] +fn svn_floor_commits_on_boot_confirmed_not_on_activation() { + // Activation alone: no floor commit yet. + let (activated, activated_state) = drive( + passive_required(&[C0]), + &[ + BOOT, + Event::VerificationPassed(C0), + Event::UpdateRequest, + Event::UpdateVerified, + ], + ); + assert_eq!(activated_state, State::Ready); + assert!(activated.contains(&Effect::ActivateUpdate)); + assert!(!activated.contains(&Effect::CommitSvnFloor(C0))); + + // Proven-healthy boot: the floor advances now, without leaving Ready. + let (confirmed, confirmed_state) = drive( + passive_required(&[C0]), + &[ + BOOT, + Event::VerificationPassed(C0), + Event::UpdateRequest, + Event::UpdateVerified, + Event::BootConfirmed(C0), + ], + ); + assert_eq!(confirmed_state, State::Ready); + assert!(confirmed.contains(&Effect::CommitSvnFloor(C0))); +} + /// Locked is a terminal state: no effects are produced in response to any /// event after the machine latches. #[test]