On this page
Declared repair
Declared repair is the one Stado capability for recovering a service whose runtime, authorization, storage, or delivered version has wedged. It reads the ordered repair array on the service in stado-rs/data/catalog/repair-catalog.json; the command never invents a repair from the service name. Each row declares the step name, one-sentence incident/restoration summary, whether the step mutates, and the proof the implementation reads afterwards.
Stado owns this repair catalog separately from the product catalog, catalog/products.yml, that stado product reads. Internal resources such as stado-control-plane have repair policies without becoming installable products. Changing the product catalog does not remove their repair declarations.
Before answering, Stado compares those declaration rows with the typed REPAIR_STEPS: &'static [RepairStep] table in stado-rs/src/cli/repair/steps.rs. A declaration without code and code without a declaration are refusals. Applying more than one step follows catalog order and stops at the first refusal or failed proof.
Invocations
stado repair list [--service <NAME>] [--json]lists every service and its ordered repair rows, or only the named service. It reads only the compiled repair catalog and changes nothing.stado repair show <SERVICE> <STEP> [--json]shows the declared summary, mutation mode, proof, and declaration path for one step. It changes nothing.stado repair <SERVICE> [--step <STEP>] [--target <TARGET>] [--apply] [--json]resolves the service and optional step from the catalog. Without--apply, it reports every selected step and, when--targetis present, includes a read-only host inventory observation; without a target it explicitly reports that no live target was selected. With--apply,--targetis required, the implementations run in catalog order, and every step report carries the proof value read by that implementation.
The shipped declaration assigns these steps:
stado:host,object-api,release-store,link,object-verifier,release-verifier,service-verifier,storage-root.stado-control-plane:release-state,agent-skarbiec.skarbiec:audit-lock,crypto,acquisition-state.brama:declarations.weles,oko, andtranscript-lakeexplicitly declare an empty repair array.
The declarations step makes again, for the host --target names, the lasting state Brama's service installation recipe declares through Stado: its brama maintain schedule pinned to that host (catalog/products.yml, after_install). Only stado product install --surface service runs that recipe; a gateway placed by a release update has none of it, so no maintenance pass runs and no grant is renewed. The schedule is created under the id the installation derives ({install_id:maintain} for brama's service on that host), so where it already stands nothing changes. Steps that run the installation's own program or hand it a release archive are reported ran: false with the reason. The report lists every step as {"argv", "ran"}; stado schedule show <id> is the proof.
$ stado repair brama --step declarations --target <gateway host> --apply
The crypto step runs the recover-skarbiec-crypto payload on the target. It admits a recovery on one of three grounds it reads from the host itself, never assumed: Skarbiec's readiness reports a GPG failure or a keybox lock; readiness names the process holding the keyring's lock (gpg's own waiting for lock (held by <pid>), or Skarbiec's <lock> is held by pid <pid> on <host> (<what it runs>), taken <when>) or does not answer within the probe's own limit, the shape of a lock gpg waits on; or one of the account's GnuPG daemons holds more than SKARBIEC_GPG_DAEMON_MEMORY_LIMIT_MB (1024 MiB unset) of physical memory — resident, compressed and swapped pages together, the reading ps cannot give: a keyboxd can grow to many GiB over days while ps reports a few hundred MiB and readiness answers ok. The repair itself is Skarbiec's own: the payload runs skarbiec recover-daemons on the host, which replaces the account's keyboxd, gpg-agent and scdaemon through gpgconf and releases a keyring lock whose holder can no longer let go — a dead or reused pid, a lock written under another host name, a GnuPG program that held it past gpg's own wait — the same repair the vault runs for itself after a failed read, and its own lines (which daemon it signalled, what each held, which lock it released) stay in the step's output. Readiness is then read again; skarbiec cryptographic daemons recovered is the proof, and a vault that stays unready is reported with its own answer. When no ground holds it refuses with refusing recovery: Skarbiec did not report a GPG failure, a keybox lock or a held keyring lock, and no GnuPG daemon of this account stands over the <N> MiB ceiling; readiness answered (curl status <status>): <answer>; a host without ~/.stado/bin/skarbiec is named before the repair fails on it. The same payload runs by itself on the vault host: its object API, when a vault answer to one of its bearer reads names a held keyring lock, runs it once per holder in the background and reports the outcome in /api/state.json under vault.keyring_lock_repair (Machine Interface); on a host that declares no vault the log names the command that runs it on the vault host. The same two commands are approved for stado host exec <vault host> -- skarbiec doctor (the vault's own diagnosis, nothing decrypted) and -- skarbiec recover-daemons, so a supervisor runs the diagnosis and the repair through the product without a shell on the host.
Declaration and implementation refusals
The exact capability-owned refusal sentences are:
<service> declares no repair; add it to stado-rs/data/catalog/repair-catalog.json.<service> declares no repair step <step>; add it to stado-rs/data/catalog/repair-catalog.json.<service> declares no repair steps; add them to stado-rs/data/catalog/repair-catalog.json.<service> declares mutating repair steps but no target was selected; pass --target <TARGET>.<service> repair step <step> declares no implementation; add it to stado-rs/src/cli/repair/steps.rs.<service> implements repair step <step> but declares no repair; add it to stado-rs/data/catalog/repair-catalog.json.<service> declares repair step <step> more than once; keep one row in stado-rs/data/catalog/repair-catalog.json.<service> repair step <step> has more than one implementation; keep one entry in stado-rs/src/cli/repair/steps.rs.<target> storage-root repair ended <state>; <resident-worker-detail>.<target> storage-root repair produced no durable completion proof within 360 seconds; inspect transaction <transaction>.<target> did not complete host repair; inspect the reported blockers and retry the declared stado repair stado --step host --target <target> --apply.The
hoststep's report names an outcome for every LaunchAgent the target declares. When the pass on the host reaches its last line and names none for a declared unit, that unit is a blocker withfinding: "unreported"and the reasonthe recovery pass reached its last line and reported no outcome for <label>, so whether it runs is unknown, and the step's status isblocked, notok. A pass that stopped before the units states its own failure and lists no unit.<target> did not complete release-state repair; inspect the reported host drift and failed deliveries.<target> has no target declaration; add it to the fleet registry.<target> target declaration disappeared during repair; retry after the registry is stable.<target> declares no desired <binary> version; add it to the target's version declaration.
Argument-shape refusals are complete sentences too:
repair list takes no positional service; pass --service <NAME>.repair show takes SERVICE and STEP as positionals, not --service.repair show requires SERVICE and STEP.repair SERVICE accepts --step, --target, --apply, and --json.repair <operation> accepts only its documented declaration filters and --json.
Each implementation can also return its existing incident-specific sentence: missing target declarations and SSH routes, mismatched verifier declarations, absent credential items, an unsupported Skarbiec diagnosis, an unhealthy post-repair endpoint, or a failed durable storage worker are all surfaced rather than converted to a bare code.
The resident storage worker the durable transaction starts after a declared step begins is an implementation detail; it is not shown by stado host --help.
Stado Desktop
Every host inspector has a Declared repair section. It loads the same compiled catalog through stado repair list --json, groups the ordered steps under stado, stado-control-plane, and skarbiec, and displays each step's incident/restoration summary and proof. Dry run executes the service report against the selected host without applying a step. Apply declared steps… requires an explicit destructive confirmation, then runs the service's declared steps in order. The last dry-run or applied report remains visible per host and service, including each step's status, live observation or proof report, and proof declaration. The link repair, storage reconciliation command display, and machine-enrollment host proof all call the matching declared stado repair step; none retains a removed host verb.
Source: this website