Skip to content

orchestrator: Add the platform-driver skeleton behind the Platform seam - #418

Open
chrysh wants to merge 4 commits into
OpenPRoT:mainfrom
9elements:add-shell-skeleton
Open

orchestrator: Add the platform-driver skeleton behind the Platform seam#418
chrysh wants to merge 4 commits into
OpenPRoT:mainfrom
9elements:add-shell-skeleton

Conversation

@chrysh

@chrysh chrysh commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

TL;DR: Adds services/orchestrator/driver, the effect-executing layer around the orchestrator state machine — the platform driver (#357's name for it; the last commit renames it from the working name "shell"). Only a skeleton: the actual implementation lands step by step.

  • board.rs (trait-only commit): ImageSource — board-supplied access to a component's active firmware image (interposed flash, PLDM transfer, test double); Verifier — judges an image, where an error means the check could not run (a bad image is Verdict::Rejected, so an actuation fault cannot forge a verdict); BoardTypes/Board — one marker type and one bundle struct per board, so a new seam adds an associated type and a field, never another parameter.
  • driver.rs: PlatformDriver, one executor method per SM effect, each documenting its obligation from the platform-boundary contract (orchestrator-model.md §6). read_firmware/verify_firmware are implemented; the rest return NotImplemented and the SM fail-closes on them.
  • Executor-produced events queue in the driver and feed back via PlatformDriver::take_event. orchestrator: Let Platform::execute return a follow-up event #419 proposes letting Platform::execute return the event instead, which deletes this queue.

The platform driver is board-agnostic: mock vs hardware is invisible from inside. Tests drive the real SM against a RAM-backed board.

@chrysh
chrysh force-pushed the add-shell-skeleton branch 3 times, most recently from 294a48f to 2af78c4 Compare August 15, 2026 07:36
@chrysh
chrysh marked this pull request as ready for review August 15, 2026 07:52
chrysh added 2 commits August 17, 2026 14:53
ImageSource is board-supplied access to a component's active image
(interposed flash, PLDM transfer, test double); Verifier judges it,
and its error means the check could not run — a bad image is
Verdict::Rejected, never an error. BoardTypes and Board bundle one
board's types and wired instances; a new seam adds an associated type
and a field, not another parameter.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
One executor method per SM effect, each documenting its obligation
from the platform-boundary contract (orchestrator-model.md §6);
unimplemented executors fail closed. read_firmware opens the image
source; verify_firmware has the verifier judge it and queues the
verdict, which the driver loop feeds back via take_event.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
@leongross

Copy link
Copy Markdown
Member

In #357 we discussed not naming a component shell due to the ambiguity of this name.

PR OpenPRoT#357 already settled this word: 'shell' reads as bash, and the
docs say platform driver. The crate follows: PlatformDriver in
driver.rs, DriverError, openprot_orchestrator_driver. The outer pump
is now the event loop, freeing 'driver' for the struct.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
@chrysh chrysh changed the title orchestrator: Add the shell skeleton behind the Platform seam orchestrator: Add the platform-driver skeleton behind the Platform seam Aug 17, 2026
Comment thread services/orchestrator/shell/src/shell.rs Outdated
Comment thread services/orchestrator/driver/src/driver.rs
@chrysh

chrysh commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

In #357 we discussed not naming a component shell due to the ambiguity of this name.

True, I renamed it to PlatformDriver

The executor stages the image (opens the source, marks it staged);
it does not read it. Review on OpenPRoT#418 flagged the mismatch. The SM
effect stays ReadFirmware — only the driver method is renamed.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
Claude-Session: https://claude.ai/code/session_018vLnXX7edp1R5YxQA3FM3h
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verdict {
/// Reported as `Event::VerificationPassed`.
Authentic,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if we cryptographically authenticated an image, I would name it Authenticated, since this is common naming in cryptographic contexts.

Comment on lines +100 to +102
if self.staged != Some(id) {
return Err(ShellError::NoImage);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NoImage seems confusing, since this code location should inform the API consumer that the image is not staged and hence cannot be read, not that there is no image in the system for the Id, right?

Maybe use NotStaged or sth similar.

.board
.images
.get_mut(id.get() as usize)
.ok_or(ShellError::UnknownComponent)?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the id validity check happen before we check if it is staged? Also, if the ID does not exist for the board images, it could never have been staged, right?

Comment on lines +19 to +25
fn valid_image() -> std::vec::Vec<u8> {
let mut image = std::vec![0u8; IMAGE_LEN];
image[..4].copy_from_slice(&IMAGE_MAGIC);
image[4..IMAGE_LEN - 1].fill(0xAB);
image[IMAGE_LEN - 1] = image[..IMAGE_LEN - 1].iter().fold(0, |acc, b| acc ^ b);
image
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we at some point verify this exact image format? Then it would make sense to create an abstraction struct for this and serialze/deserialze it in a generic way and implement verification methods for this.
I agree it is overkill for a unit test, but when we have the image format at some point, we should remove this; it looks messy, and raw byte generation is prone to later image change regressions.

Comment on lines +122 to +124
pub fn release_reset(&mut self, _id: ComponentId) -> Result<(), DriverError> {
Err(DriverError::NotImplemented)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of stubbing these functions, why not provide them by traits and extend the generic inherent implementation block over these traits? Something like:

type BootControl: orchestrator_capabilities::BootControl;

For the board, incorporate

pub boot_controls: [B::BootControl; N]

And then have a generic implementation

pub fn assert_reset(&mut self, id: ComponentId) -> Result<(), DriverError> {
      self.board
          .boot_controls
          .get_mut(id.get() as usize)
          .ok_or(DriverError::UnknownComponent)?
          .hold_in_reset()
          .map_err(|_| DriverError::BootControlFault)
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants