Skip to content

orchestrator: add Recovery capability trait - #463

Merged
chrysh merged 2 commits into
OpenPRoT:mainfrom
9elements:add-recovery
Sep 22, 2026
Merged

chrysh merged 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.

@chrysh
chrysh marked this pull request as ready for review September 21, 2026 12:35
@chrysh
chrysh force-pushed the add-recovery branch 4 times, most recently from b27418e to 921de6c Compare September 21, 2026 20:26
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. The verifier judges the restored image on
the re-walk, so a restore must not check it here. 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>
Return RestoreOutcome instead of a bare Ok, so an implementor can report
source exhaustion without it looking like an actuation fault.
SourceExhausted travels on the Ok side, the platform driver reports
Event::RecoveryUnavailable, and the component is gated per policy instead
of the whole platform locking. Matches the error contract in OpenPRoT#473.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Christina Quast <christina.quast@9elements.com>
@chrysh
chrysh merged commit 299c757 into OpenPRoT:main Sep 22, 2026
5 checks passed
@chrysh
chrysh deleted the add-recovery branch September 22, 2026 07:46
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