On this page

Native builds

A build compiles one committed source on every platform its .wisent-release.json declares and keeps the result under its own id. A release consumes a build that passed. The two are separate commands with separate durable records: stado build compiles and reports, stado release submit signs, publishes, delivers and promotes, and it refuses a build that is still waiting or has failed.

A build never signs an artifact, changes release_control, or promotes a release. Its identity is the product, the version the commit declares, the digest of the committed tree and the digest of the manifest — no channel — so one build serves a candidate release and the stable release that follows it, and submitting the same commit again resumes the same build instead of compiling it twice. When Stado itself is delivered, the installer records the installed version beside the managed binary. Each managed queue agent finishes its active jobs, detects that its loaded version differs, and exits through its declared KeepAlive or Restart=on-failure policy; the supervisor then starts the installed release without unloading the unit or interrupting a job. Before executing an artifact, every delivery verifies both the newest submitted source in the product catalog and the exact published platform coordinate in its still-delivering run. This fences an older queued delivery without assuming the product has a separate release-control rollout policy. The Stado delivery job starts its worker from the digest-pinned candidate archive and that worker uses itself for release local install; a broken older installed worker therefore cannot prevent the release that repairs it. A non-Stado archive contains that product rather than another Stado binary, so its delivery runs the installed Stado worker against the digest-pinned archive.

Repeating stado release submit resumes the same release run. An initial platform submission keeps its original stable job identity and output URI. Before replacing a failed platform attempt, the coordinator reads the original job: queued, running, completed, and uploaded jobs keep that identity and their existing output. A publication or storage-read failure does not start another build. A failed job also keeps its identity when its durable build receipt says passed: the bootstrap's upload can fail after compilation and qualification succeed, while the native finalizer subsequently preserves the output. If canonical output is missing, resumption reads the retained receipt from the recorded builder through Stado. It verifies the receipt's run, job, product, version, platform, builder, source and manifest bindings, then asks that builder's installed Stado to upload its retained command log, archive and receipt to both the attempt and canonical output locations. The receipt is uploaded last; publication checks the storage write receipts, reads the canonical receipt back, and verifies the actual archive's SHA-256 before using it. A mismatch returns release job <JOB> returned a receipt that does not match this run: <FIELD> is "<RECEIPT>", not "<EXPECTED>"; …, naming every field that disagrees, an absent artifact with the archive's digest, and a status other than passed with the failure the receipt states. A missing archive returns release job omitted archive. Neither replaces a passed build. The original job keeps its recorded terminal state and cause; recovery does not rewrite that history.

The worker also checks for its own exact completed output before executing a replayed request. It reuses a passed receipt only when the immutable request, including its inputs and secret references, matches and the retained archive still has the recorded size and SHA-256. That replay returns the existing build instead of running the compiler again.

The stored platform request fixes its builder and input archives. Resumption checks that request against the release's source and manifest, preserves its exact bytes, and uses the already-staged inputs instead of fetching them again. An existing queue plan retains its recorded consumer and does not require a new live-builder selection. If no queue plan exists yet, the saved builder must still pass normal admission; another machine is not silently selected. An altered request returns immutable queue object differs: PATH without overwriting the saved request or creating a replacement build.

stado release status stado --json shows the recorded release runs, including each platform's builder, job ID, state, and failure. A recorded platform failure can describe a publisher read error rather than a failed worker; resumption checks the job and its build receipt before deciding whether another attempt is needed. A published platform can retain a failed job whose passing build receipt and archive were recovered; the qualification receipt records the successful build and the job record preserves the failed upload.

Every saved run is also filed under its product (runs/release-pipeline-by-product/<product>/<run id>), so a question about one product's runs, such as stado release status tama or a delivery's check for the newest published run, reads only that product's run objects. Runs saved before the index existed are filed by the next stado release newest, which already reads every run once; until then a product question reads every run object and says so on stderr ([release runs] runs are not indexed by product yet …).

When a required delivery fails, the submit command names the delivery and its queue job, followed by that job's recorded failure and last command output. The same detail is retained in the run's delivery record. Use stado machine status JOB_ID and stado machine logs JOB_ID to inspect that exact job; no new build or delivery is needed to retrieve its failure.

A cancelled unfinished build, or a failed build without a passed receipt, receives a retry identity derived from the release run, platform, and prior terminal job ID, with its own output URI. A cancelled replay is different when its retained passing receipt was completed before that replay started: resumption can publish the earlier completed build without undoing the later cancellation. A passing receipt from the cancelled attempt itself does not override cancellation. A crash before or after the replacement platform record is saved reuses the same attempt; only another recorded terminal job failure can chain to another retry. An unreadable job or receipt returns the storage or decoding error. A missing job returns release job JOB_ID was not found in recorded states; refusing a replacement without terminal failure; neither condition creates a replacement build.

Local queue jobs execute from the agent owner's ~/.stado/work/jobs/wc-<job-id> tree, not a temporary directory. Admission resolves the physical home, then creates or opens .stado, work, and jobs one component at a time as owner-only directories with O_DIRECTORY|O_NOFOLLOW; component symlinks are refused. The serialized old-agent bridge repeats that refusal and changes permissions or creates the next child only from a held cwd whose physical path it just verified. A release submission remains able to repair an older agent: its deterministic bootstrap moves that agent's already-materialized job into the persistent root before the worker starts and preserves a compatibility symlink for the old agent's log and artifact upload. It also pins TMPDIR, TMP, and TEMP beneath that persistent tree so a pre-migration worker cannot put its release scratch back in OS temp. After a successful worker, the bridge uses the checked storage writer to upload and read back the command log and archive at both canonical status and exact attempt URIs, then publishes the attempt's receipt.json last as the completion marker. Each writer receipt must match the quiescent local source's SHA-256 and byte count. Its JSON and lifecycle-watch responses use random 0600 files under the validated work tmp directory, are removed after parsing, and leave only their proof lines in the already-open command log. A failed write or read-back turns the workload into a failure instead of allowing a successful terminal record. For a worker that already failed, the bridge still proves the command log at both destinations and, when an owned regular receipt.json exists, publishes that failed receipt to canonical then attempt storage. Evidence errors are appended to the safe log without replacing the worker's original nonzero exit code; the legacy link remains for the old agent's canonical finalization. The bootstrap prints both physical workdir and effective temporary root before invoking the worker, so an operator can prove the retry left /tmp from the canonical job log. Queue-workdir reclamation runs only through the janitor under its exclusive admission lock; every pass reserves a bounded scan share for terminal old-agent links independently of canonical workdir enumeration. Each agent tick observes all local workload exits and releases their cleanup holds before reading remote policy or finalizing any slot. A slow upload or queue read for one job cannot retain the holds of siblings that already exited; live workloads remain protected. If the canonical tree is ever absent or replaced while a job is live, the agent emits workdir_missing with its exact expected path at heartbeat and finalization, then retains that marker and the real workload exit code in the terminal job error. An existing empty command log remains the distinct wrote no output case.

stado release redeliver PRODUCT RUN_ID DELIVERY --retry-token TOKEN is the operator recovery path for one delivery from the exact newest completed run. It does not publish a new candidate or move a channel. The retry token identifies a fixed per-run CAS transaction and stable queue submission, so repeating the same command resumes after interruption. A different token is refused while a transaction is active. Success replaces that delivery's job, output, state, and receipt evidence before restoring the run's prior state; failure preserves the previous passed delivery while recording the terminal failure and restoring the run state.

stado host config set TARGET KEY VALUE migrates an older deployment profile through the installed stado config migrate before applying the field. The migration preserves the exact prior file beside the profile and refuses newer schemas. No separate migration command is needed; --reload-service SERVICE activates the change through the declared service policy.

Owner-local builds covers owner-local source selection and qualification, and canonical Swift sources and editor indexing.

Owner-local Apple signing credentials

stado product reads the fleet's signing material through Skarbiec by default, from the one vault item that plays the role macos-development-signing (it carries the tag stado:role:macos-development-signing), so no private PEM text is copied into a command and no item is named. Another role is named explicitly:

WISENT_CODESIGN_ROLE=<role> \
WISENT_CODESIGN_ISSUERS_FILE=/path/to/apple-issuers.pem \
stado product install transcript-lake --surface cli --json

The item in the role supplies certificate and private_key. The optional issuer file contains public PEM certificates, not another private key. Overlapping issuer certificates are imported once, even when their PEM wrapping differs. Stado checks the key against the certificate, uses an owner-only, short-lived keychain under ~/.stado/signing, takes only its own entry off the search list, and removes its private files when the operation ends; one scope signs on a host at a time (signing).

An existing publisher can instead supply both WISENT_CODESIGN_CERTIFICATE_PEM and WISENT_CODESIGN_PRIVATE_KEY_PEM. Selecting a role and nonempty PEM input together is refused. No item in the role, two items in it, or an absent field reports the role and the actual Skarbiec read error; an unreadable issuer file reports its path and filesystem error. Invalid certificates and mismatched keys fail before installed bytes are replaced. An explicitly selected credential never falls back to another signing identity.

A product's catalog installation recipe may instead require signing_keychain, a local path on the machine running the installation. Skarbiec's CLI, service and desktop recipes declare $HOME/Library/Keychains/login.keychain-db; they do not read their own unavailable vault for signing. On a Mac without that file, stado product install skarbiec --surface cli --json refuses naming the missing keychain before exporting or building. An explicit role, PEM pair or different keychain does not override the recipe. A successful source installation records signing.source, signing.keychain and observed signing.signatures per native installed path in the JSON receipt; stado product status skarbiec --surface cli --json reads the same record. An exact published release is verified and placed without local signing.

This owner-local input does not widen a queue worker's Skarbiec grant or move publication credentials into the control plane. Queued releases still use their declared secret references and grants. Use stado product signing inspect PATH --json to read the resulting identifier, Apple team, authority and designated requirement. stado product signing report and Desktop Settings show the same installed signature diagnostics on the selected host; neither resets privacy permissions.

Request failure diagnostics

A storage request failure includes the HTTP client's complete cause chain, not just error sending request. Read the failed operation and URL together with the underlying cause: for example, Connection refused (os error 61) means the connection was refused, not that compilation failed. The operating system's error numbers differ between platforms. A TLS failure, or a connection the operating system abandoned, reports the cause the HTTP client supplied rather than being relabelled as a connection refusal.

The same message reaches the CLI, a release run's saved failure when that record can be written, and the native Desktop command API's stderr. Stado Desktop's Releases screen displays the recorded failure without shortening its cause chain. If recording the failure also fails, the command keeps the original error followed by failed to persist release failure and the second error; the stored run may therefore not contain the latest failure.

This change adds no request, retry, rebuild, or field to stored records. Existing failure classification uses the richer message. It cannot reconstruct causes discarded by an older binary. Inspect the retained command output from that run before attributing an older generic message to a specific network fault.

Release publication authority

Which publisher may publish a release and how its writes are authorized: Release publication and the active executable.

Wait for the stable release endpoint

Reading the stable release endpoint once a release is published: Release publication and the active executable.

Resolve the executable that is actually active

Finding the executable a host actually runs: Release publication and the active executable.

Private Cargo build inputs

Private Cargo sources a build needs and how they are supplied: Private Cargo build inputs.

Build one product

Building one product and the products its post-build tests run: Building, reading and releasing builds.

Read a build

Reading a build's state, its platforms and its failure: Building, reading and releasing builds.

Build the whole workspace

Building every product of the workspace at once: Building, reading and releasing builds.

Release a build

Releasing a finished build: Building, reading and releasing builds.

What the forge's continuous integration last did

Reading what the forge's continuous integration last did for a product: Building, reading and releasing builds.

The delivery routine

The routine that delivers every built release: The delivery routine and the compiler cache.

The fleet's daily build budget

The fleet's daily build budget and a recorded user exception: Building, reading and releasing builds.

Failure boundaries

Platform state Meaning
no job for a platform no builder could take it yet, or the build was refused before queueing
building the job is queued or a matching worker claimed it
failed the quality gates, the build command, packaging, signing or the receipt itself failed; the reason is on the platform line
passed the job wrote a passing receipt naming this build and its archive

A successful process exit without a passing receipt and an archive is a failed platform. A host merely declaring a platform is insufficient: a live worker must publish capacity for that platform. The artifacts and command log of every job stay under status/<job-id>/output/ and download with stado results <job-id> ./build-results.

A platform whose secret_env asks for a role no item the stado consumer can see plays is refused before anything is queued or charged: a builder resolves a job's secrets as that consumer, so no host could ever claim the job. A secret_env entry is ENV: "role#field"; the role is what the secret is for, and the vault decides which item plays it (Skarbiec and Stado). The refusal names each environment variable and why its role cannot be selected:

Error: <product>'s darwin-arm64 build asks for roles the stado consumer cannot select, so no builder could ever claim it: MACOS_CERT_P12: no item visible here carries stado:role:<role>; store the secret for role <role> with `stado credentials item put --host <vault owner> --role <role>`. Store the secret with `stado credentials item put --host <vault owner> --role <ROLE>` and grant it to stado before building.

The check reads ids and tags only, never values, through the same listing stado credentials ls prints. A builder's declared agent.skarbiec.secret_fields is not proof: the declaration can name a role no item plays. A job already queued for a role its builder's grant cannot resolve is shown by stado host gates <builder> as a declined: line.

The quality gate names whose revision refused

How a refusing quality gate names the revision it read: Building, reading and releasing builds.

Disk pressure on build hosts

stado space report TARGET --json includes Linux inventory under the managed home, /home, /mnt, /var, and /opt. stado host reclaim TARGET --apply --reason TEXT also recognizes the former ~/.stado/work/platform-matrix-cargo-target cache. It refuses linked, unrecognizable, younger-than-one-hour, or live-held trees; unrelated untagged directories remain outside that cleanup. host build-caches reports no-cache-tags, root-protected, or scan-failed rather than making those cases look like a successful empty scan.

The compiler cache

How builds share compiled output through the compiler cache: The delivery routine and the compiler cache.

Desktop

Stado Desktop's Releases screen carries the same operations as the CLI: build a source, build every product, read and list builds, and release a passed build.

Source: this website