orchestrator: Add the LockdownLatch terminal capability trait The Effect::LatchLockdown executor has no capability seam. Add LockdownLatch: a one-way latch into the platform safe state, a sticky bit rather than an acquire/release lock. Ok only when the safe state is in force; a failed latch is a hard fault the caller must treat as terminal. Trait only; the platform driver composition follows separately. Assisted-by: Claude:claude-fable-5 Signed-off-by: Christina Quast <christina.quast@9elements.com>
diff --git a/services/orchestrator/capabilities/BUILD.bazel b/services/orchestrator/capabilities/BUILD.bazel index 859f7a9..c1c4d8e 100644 --- a/services/orchestrator/capabilities/BUILD.bazel +++ b/services/orchestrator/capabilities/BUILD.bazel
@@ -10,6 +10,7 @@ "src/boot_watch.rs", "src/evidence.rs", "src/lib.rs", + "src/lockdown_latch.rs", "src/svn_floor.rs", ], edition = "2024",
diff --git a/services/orchestrator/capabilities/src/lib.rs b/services/orchestrator/capabilities/src/lib.rs index 0a721ea..cf71cde 100644 --- a/services/orchestrator/capabilities/src/lib.rs +++ b/services/orchestrator/capabilities/src/lib.rs
@@ -19,6 +19,9 @@ //! `BootWatch` is the seam the orchestrator polls: one device's boot walk, //! erased of every device-specific type, answering with a `WalkVerdict`. //! +//! `LockdownLatch` is the terminal capability: latch the platform into its safe +//! state, one-way, at the top of the escalation ladder. +//! //! This crate is a dependency-free leaf: it holds the capability contracts, //! and everything depends downward on it. Concrete adapters bind a capability //! to a signal source and live in their own crates, so naming a capability @@ -32,9 +35,11 @@ mod boot_control; mod boot_watch; mod evidence; +mod lockdown_latch; mod svn_floor; pub use boot_control::BootControl; pub use boot_watch::{BootWatch, FailureCause, WalkVerdict}; pub use evidence::{BootStatus, EvidenceReader}; +pub use lockdown_latch::LockdownLatch; pub use svn_floor::{Svn, SvnFloor};
diff --git a/services/orchestrator/capabilities/src/lockdown_latch.rs b/services/orchestrator/capabilities/src/lockdown_latch.rs new file mode 100644 index 0000000..f57686d --- /dev/null +++ b/services/orchestrator/capabilities/src/lockdown_latch.rs
@@ -0,0 +1,88 @@ +// Licensed under the Apache-2.0 license +// SPDX-License-Identifier: Apache-2.0 + +//! The [`LockdownLatch`] terminal safe-state capability contract. + +/// Latch capability: put the platform into its terminal safe state. +/// +/// What the safe state is (gating every managed device, tripping a fuse, +/// parking straps) is board wiring and never leaks through this seam. +/// +/// The latch is one-way and idempotent: nothing short of a platform reset +/// unlatches it, and latching an already-latched platform succeeds. `Ok` +/// means the safe state is in force, not merely requested. A failed latch +/// is a hard fault: `Err` means the safe state is not in force and the +/// caller must not continue as if it were. How the platform escalates from +/// there is board policy, outside this contract. +pub trait LockdownLatch { + /// The error type of this platform's latch mechanism. + /// + /// Bounded by [`core::error::Error`] so the caller gets `Display` and a + /// `source()` cause chain. Error categories are implementation-defined. + type Error: core::error::Error; + + /// Latches the platform into the safe state. + fn latch(&mut self) -> Result<(), Self::Error>; +} + +#[cfg(test)] +mod tests { + use super::*; + + // Implements LockdownLatch with no HAL dependency: the contract must be + // satisfiable from any stack (mock, IPC proxy, simulator). A HAL-bound + // `Error` type would stop this compiling. + struct MockLatch { + latched: bool, + fail: bool, + } + + #[derive(Debug, PartialEq, Eq)] + struct MockFault; + + impl core::fmt::Display for MockFault { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("mock latch fault") + } + } + + impl core::error::Error for MockFault {} + + impl LockdownLatch for MockLatch { + type Error = MockFault; + + fn latch(&mut self) -> Result<(), MockFault> { + if self.fail { + return Err(MockFault); + } + self.latched = true; + Ok(()) + } + } + + #[test] + fn contract_is_implementable_without_the_hal() { + let mut dev = MockLatch { + latched: false, + fail: false, + }; + + dev.latch().expect("latch failed"); + dev.latch().expect("repeated latch failed"); // idempotent + + assert!(dev.latched); + } + + #[test] + fn errors_surface_through_the_generic_seam() { + let mut dev = MockLatch { + latched: false, + fail: true, + }; + + let err = dev.latch().expect_err("expected the latch fault"); + + // Display comes from the core::error::Error bound, not a Debug dump. + assert_eq!(err.to_string(), "mock latch fault"); + } +}