Skip to content

orchestrator: add Recovery capability trait - #463

Open
chrysh wants to merge 2 commits into
OpenPRoT:mainfrom
9elements:add-recovery
Open

chrysh wants to merge 2 commits into
OpenPRoT:mainfrom
9elements:add-recovery

Conversation

@chrysh

@chrysh chrysh commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

The Recovery capability trait: rewrite one managed device's active image from
its board-configured recovery source. This is the seam
Effect::RecoverComponent needs, which currently falls through to
Err(EffectError) in the platform driver.

restore returns Result<RestoreOutcome, Self::Error>:

  • Ok(Restored) means the mechanism completed, not that the image is good.
    Judging the restored image stays with the verifier on the re-walk, so a
    restore can never forge a verdict.
  • Ok(SourceExhausted) means no untried source remains. The orchestrator
    gates the component per its failure policy immediately, without waiting for
    the retry cap. This is a clean, known condition, not a fault.
  • Err is an actuation fault only (source unreachable, write failed). The
    orchestrator treats these fail-closed.

restore takes the attempt count straight from the effect, so an implementor
holding several sources can pick a different one each try (slot A on 0, slot B
on 1, golden on 2). A count kept by the device would drift, because it never
sees which attempt succeeded.

Trait only: no driver executor, no BoardCapabilities change, no board wiring.
Those come in a follow-up.

The source-exhaustion question from #382 is resolved: exhaustion travels on the
Ok side as RestoreOutcome::SourceExhausted, so the platform driver reports
Event::RecoveryUnavailable and the component is gated per policy instead of
the whole platform locking. Matches the verdict-vs-error contract in #473.

Related: #48.

@rusty1968

Copy link
Copy Markdown
Collaborator

The questions are answered by #473

@rusty1968

Copy link
Copy Markdown
Collaborator

Recovery::restore() returns a single opaque Result<(), Self::Error>, and the doc note above it says exhaustion is "an actuation error like any other." That conflicts with the contract #473 just added on Platform::execute (services/orchestrator/sm/src/lib.rs): a driver must distinguish "no recovery source left" (→ Event::RecoveryUnavailable, gated per FailurePolicy) from a genuine actuation fault (→ EffectError, unconditional lockdown). As written, whoever implements the RecoverComponent executor has no way to tell those apart from Self::Error alone.

Suggest extending the contract so an implementor can signal exhaustion distinctly, e.g.:

enum RestoreOutcome {
    Restored,
    SourcesExhausted,
}

fn restore(&mut self, attempt: u8) -> Result<RestoreOutcome, Self::Error>;

so Self::Error stays reserved for real actuation faults, and SourcesExhausted maps directly to Event::RecoveryUnavailable.

@rusty1968
rusty1968 self-requested a review September 18, 2026 22:31

@rusty1968 rusty1968 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

See comments.

One managed device's restore mechanism: rewrite the active image from
the board-configured recovery source. Ok means the mechanism completed,
not that the image is good - judging the restored image stays with the
verifier on the re-walk, so a restore can never forge a verdict. Errors
are actuation faults only and are treated fail-closed.

restore takes the attempt count from Effect::RecoverComponent so an
implementor holding several sources can pick a different one each try.
A count kept by the device would drift, because it never sees which
attempt succeeded.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Christina Quast <christina.quast@9elements.com>
Source exhaustion is a verdict, not a fault: it travels on the Ok side
as RestoreOutcome::SourceExhausted so the platform driver can report
Event::RecoveryUnavailable and the component is gated per policy,
instead of the whole platform locking. Matches the verdict-vs-error
contract in OpenPRoT#473.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Christina Quast <christina.quast@9elements.com>
@chrysh
chrysh marked this pull request as ready for review September 21, 2026 12:35
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.

2 participants