# Trusted Build Inputs

This inventory covers the build inputs currently trusted by the capOS boot
image, generated bindings, host tooling, and verification paths. It started as
the Security Verification Track S.10.0 inventory, records the Security
Verification Track S.10.2 generated-code drift check, and now also records the
Security Verification Track S.10.3 dependency policy plus the shared no_std
generated-code patch helper. The consolidated long-horizon supply-chain risk
view -- floating Rust nightly, repo-pinned `qemu-system-x86_64` /
`xorriso` digests (CI now apt-installs `qemu-system-x86=1:8.2.2+ds-0ubuntu1.16`,
`xorriso=1:1.5.6-1.1ubuntu3`, and `ovmf=2024.02-2ubuntu0.8` so package identity
is captured; the OVMF firmware blob is now repo-pinned by SHA-256
(`OVMF_CODE_SHA256`, landed at commit `f1c8c8fb`, merged at `ca5a1fea`) and
the `ovmf-verify` Makefile gate fails the build on drift, but
download-and-verify of the `qemu-system-x86_64` / `xorriso` tool blobs
remains a future step), PR-blocking CI environment provenance comparison, and
the remaining immutable-runner-image / repo-managed tool-digest gap -- is
tracked as R13 in
`docs/design-risks-register.md`; the gap text below stays consistent with that
entry.

## Summary

| Input | Current source | Pinning status | Drift-review status |
| --- | --- | --- | --- |
| Limine bootloader binaries | `Makefile:5-10`, `Makefile:34-49` | Git commit and selected binary SHA-256 values are pinned. | `make limine-verify` fails if the checked-out commit or copied bootloader artifacts drift. |
| Rust toolchain | `rust-toolchain.toml:1-4`, `.github/workflows/ci.yml` | Date-pinned `nightly-2026-04-20` channel with target triples and the `rust-src` component required by custom-target `-Zbuild-std` userspace builds. The CI `host-baseline`, `dma-assurance-models`, and `qemu-smoke` jobs explicitly request the same dated nightly. The Kani job remains pinned separately to `nightly-2025-11-21` paired with the Kani-compatible bundle installed by `cargo kani setup`. | The dated channel resolves to `rustc 1.97.0-nightly (e22c616e4 2026-04-19)` (the 2026-04-20 manifest carries the previous day's rustc commit). Bumps are review-visible as `rust-toolchain.toml` and workflow diffs; the advance procedure is recorded in the Rust Toolchain section below. |
| Workspace cargo dependencies | `Cargo.toml`, crate `Cargo.toml` files, `Cargo.lock` | Lockfile pins exact crate versions and checksums for the root workspace. Manifest requirements remain semver ranges. | `make dependency-policy-check` runs `cargo deny check` plus `cargo audit` against the root workspace and lockfile in CI. |
| Standalone cargo dependencies (covered by `make dependency-policy-check`) | `init/Cargo.lock`, `demos/Cargo.lock`, `demos/wasi-hello-rust/Cargo.lock`, `demos/wasi-cli-args/Cargo.lock`, `demos/wasi-env/Cargo.lock`, `demos/wasi-fs/Cargo.lock`, `demos/wasi-random/Cargo.lock`, `demos/wasi-preview1-refusals/Cargo.lock`, `demos/wasi-stdio-fd/Cargo.lock`, `tools/adventure-content-gen/Cargo.lock`, `tools/paperclips-content-gen/Cargo.lock`, `tools/mkmanifest/Cargo.lock`, `tools/remote-session-client/Cargo.lock`, `tools/ringtap-viewer/Cargo.lock`, `capos-rt/Cargo.lock`, `capos-service/Cargo.lock`, `shell/Cargo.lock`, `libcapos/Cargo.lock`, `libcapos-posix/Cargo.lock`, `capos-wasm/Cargo.lock`, `fuzz/Cargo.lock` | Each standalone workspace has its own lockfile. The Makefile `DEPENDENCY_POLICY_MANIFESTS` / `DEPENDENCY_POLICY_LOCKFILES` lists drive the gate. | `make dependency-policy-check` runs the shared deny/audit baseline against every standalone manifest and lockfile listed above (root workspace `Cargo.lock` plus the 21 standalone lockfiles in this row). Cross-workspace version drift remains review-visible and intentional where lockfiles differ. |
| Standalone cargo dependencies (not yet under policy gates) | `tools/remote-session-client/src-tauri/Cargo.lock`, `vendor/wasmi-no_std/wasmi-1.0.9/Cargo.lock` | Two checked-in lockfiles fall outside `DEPENDENCY_POLICY_LOCKFILES`. `tools/remote-session-client/src-tauri/Cargo.lock` is the Tauri scaffold lockfile; `make remote-session-tauri` only exposes deterministic `policy` and `check` modes and reviewed `dev` mode -- distributable `package` and desktop `automation` modes are blocked. `vendor/wasmi-no_std/wasmi-1.0.9/Cargo.lock` is part of the vendored upstream snapshot covered separately by the wasmi `=1.0.9` path-dependency pin in `capos-wasm/Cargo.toml`. | Both lockfiles are review-visible through ordinary diffs but are not run through `cargo deny check` / `cargo audit` today. Promoting either into `DEPENDENCY_POLICY_LOCKFILES` is gated on the matching authority decision (Tauri scaffold scope decision; wasmi refresh procedure in `vendor/wasmi-no_std/VENDORED_FROM.md`). |
| Cap'n Proto compiler | `Makefile:12-80`, `tools/capnp-build/src/lib.rs`, `capos-config/build.rs`, `tools/check-generated-capnp.sh`, `tools/mkmanifest/src/lib.rs`, `tools/mkmanifest/src/main.rs` | Official `capnproto-c++-1.2.0.tar.gz` source tarball URL, version, and SHA-256 are pinned in `Makefile`; `make capnp-ensure` builds `$(CAPOS_TOOLS_ROOT)/capnp/1.2.0/bin/capnp` under the per-user tool cache so linked worktrees reuse it. The build rule patches the distributed CLI version placeholder to the pinned version before compiling. | The shared build helper defaults to the pinned path and rejects `CAPOS_CAPNP` when it points elsewhere. Make targets export the pinned path and CI persists it through `$GITHUB_ENV`. `make generated-code-check` verifies both the exact compiler path and `Cap'n Proto version 1.2.0` before regenerating bindings through Cargo. `mkmanifest cue-to-capnp` also rejects missing or non-canonical `CAPOS_CAPNP`, checks `Cap'n Proto version 1.2.0`, and delegates schema-aware JSON-to-binary conversion to that pinned compiler. |
| Cap'n Proto Rust runtime/codegen crates | `capos-config/Cargo.toml`, `kernel/Cargo.toml`, `tools/capnp-build/Cargo.toml`, `Cargo.lock` | Cargo manifests use exact `capnp = "=0.25.4"` and `capnpc = "=0.25.3"` requirements where declared; lockfiles pin exact crate versions and checksums. | Security Verification Track S.10.3 now requires dependency-class and no_std review before these changes are accepted. |
| Kani verifier toolchain | `.github/workflows/ci.yml`, `Makefile`, `tools/run-kani-proofs.sh`, `tools/cloudbuild-kani.yaml`, `.gcloudignore` | GitHub CI pins `kani-verifier 0.67.0`; `cargo kani setup` installs the matching Kani bundle plus `nightly-2025-11-21-x86_64-unknown-linux-gnu` into the user-local Kani/rustup paths. Local `make kani-lib` and `make kani-dma-authority` expect a compatible `cargo-kani` install. The high-memory `make kani-lib-full` path uses Google Cloud Build image digest `rust@sha256:adab7941580c74513aa3347f2d2a1f975498280743d29ec62978ba12e3540d3a` on `E2_HIGHCPU_32`, installs rustup from `https://sh.rustup.rs`, sources `/usr/local/cargo/env`, initializes minimal git metadata for build tooling that expects a repository, then pins `nightly-2025-11-21` plus `cargo-kani 0.67.0`. | The CI `kani-proofs` job installs `kani-verifier 0.67.0`, runs `cargo kani setup`, and executes the bounded `make kani-lib` harness list plus the DMA-authority `make kani-dma-authority` harness group. The Cloud Build config installs the same Kani version and runs `make kani-lib-full`; it depends on explicit source staging and logs in maintainer-private GCS buckets configured in `tools/cloudbuild-kani.yaml`, `.gcloudignore` secret exclusions, and account/project IAM for Cloud Build submission and the selected runtime service account. Version, image, worker, bucket, IAM, rustup bootstrap, synthetic git metadata, or setup-path changes are review-visible in the workflow, Cloud Build config, runner script, and this inventory. |
| Alloy Analyzer (DMA assurance model checker) | `Makefile` `ALLOY_VERSION`/`ALLOY_TARBALL_URL`/`ALLOY_TARBALL_SHA256`, `tools/run-dma-alloy-model.sh`, `models/dma/dma_authority.als` | Self-contained linux/amd64 Alloy Analyzer `6.2.0` app image (bundled Temurin JRE + native SAT solvers) pinned by SHA-256; `make alloy-ensure` downloads and verifies it into `$(CAPOS_TOOLS_ROOT)/alloy/6.2.0/` (the jar is not vendored). This slice owns the Alloy pin shared with the scheduler lease model track. | `make model-dma-alloy` verifies the tarball SHA-256, checks the launcher reports version `6.2.0`, and runs the relational authority-graph checks/witnesses headless at scope `for 4`, failing on any counterexample or analyzer error. GitHub CI runs it in the `dma-assurance-models` job. |
| TLC model checker (DMA assurance lifecycle model) | `Makefile` `TLA_TOOLS_VERSION`/`TLA_TOOLS_JAR_URL`/`TLA_TOOLS_JAR_SHA256`/`TLA_JRE_URL`/`TLA_JRE_SHA256`, `tools/run-dma-tla-model.sh`, `models/dma/dma_authority.tla` | `tla2tools.jar` `1.7.4` (TLC 2.19) pinned by SHA-256 plus a SHA-256-pinned Temurin JRE `17.0.19+10` (the bare jar needs a JVM, unlike the self-contained Alloy app image); `make tla-ensure` downloads and verifies both into `$(CAPOS_TOOLS_ROOT)/tla/` (neither is vendored). This slice owns the TLC pin shared by the scheduler/IRQ TLA+ model tracks. | `make model-dma-tla` re-verifies the jar SHA-256 and the pinned JRE version, then runs TLC over the bounded `.cfg` (2 devices / 2 domains / 2 pages / 2 iovas, generations 0..1), failing closed on any invariant violation, deadlock, or analyzer error (exit code *and* the "No error" marker are both asserted). GitHub CI runs it in the `dma-assurance-models` job. |
| Generated capnp bindings | `capos-config/src/lib.rs:10-12`, `tools/generated/capos_capnp.rs`, `tools/check-generated-capnp.sh` | Generated into Cargo `OUT_DIR`; the expected patched output is checked in under `tools/generated/`. | `make generated-code-check` regenerates the canonical `capos-config` output and fails if that output differs from the checked-in baseline or if kernel-generated output reappears. |
| no_std patching of generated bindings | `tools/capnp-build/src/lib.rs`, `capos-config/build.rs`, `tools/check-generated-capnp.sh` | One shared build-support crate asserts the patch anchor and injects the no_std imports after generation. `capos-config/build.rs` calls that helper as the single schema binding owner. | `make generated-code-check` verifies the patched output contains the expected no_std imports and matches the checked-in baseline. |
| Generated adventure content | `demos/adventure-content/content/prototype.cue`, `tools/adventure-content-gen/`, `demos/adventure-content/src/generated.rs`, `tools/check-generated-adventure-content.sh` | Prototype mission content is authored in checked-in CUE and generated by a standalone locked Cargo host tool into a checked-in no_std Rust content blob. The checker requires the pinned CUE path under `$(CAPOS_TOOLS_ROOT)` and `cue version v0.16.0`. | `make generated-code-check` runs `generated-adventure-content-check`, which exports the CUE source as JSON, runs `tools/adventure-content-gen` with `cargo run --locked`, formats the generated output, and fails on drift from the checked-in baseline. |
| Generated Paperclips content | `demos/paperclips-content/content/paperclips.cue`, `schema/paperclips-content.capnp`, `tools/paperclips-content-gen/`, `demos/paperclips-content/src/generated.rs`, `tools/check-generated-paperclips-content.sh` | Paperclips game content is authored in checked-in CUE, schema-validated through the typed `PaperclipsContent` Cap'n Proto root, and generated by a standalone locked Cargo host tool into checked-in typed Cap'n Proto bytes embedded by a no_std Rust wrapper. The checker requires the pinned CUE path under `$(CAPOS_TOOLS_ROOT)`, `cue version v0.16.0`, and the pinned Cap'n Proto compiler path/version used for schema-aware conversion. | `make generated-code-check` runs `generated-paperclips-content-check`, which exports the CUE source as JSON, converts it through `mkmanifest cue-to-capnp` against `schema/paperclips-content.capnp`, runs `tools/paperclips-content-gen` with `cargo run --locked`, formats the generated output, and fails on drift from the checked-in generated content. |
| Userspace custom target | `targets/x86_64-unknown-capos.json`, `.cargo/config.toml`, `Makefile`, `system*.cue` | Source-controlled target specification plus Cargo aliases, Makefile build wrappers, and manifest paths for booted `init`, `demos`, `shell`, and `capos-rt` runtime builds. The target JSON uses Rust nightly custom-target support and builds `core,alloc` from `rust-src`. | `make init-capos-build demos-capos-build shell-capos-build capos-rt-capos-build` verifies the userspace crates against `target_os = "capos"`; QEMU smokes embed `target/x86_64-unknown-capos/release` userspace artifacts. |
| Userspace runtime surface check | `tools/check-userspace-runtime-surface.sh` | Source-controlled script that treats `capos-rt` as the only owner of `_start`, panic, allocator, raw syscall, and entry-point macro definitions. | Run directly when runtime or userspace entry code changes; it is not a QEMU transcript assertion and does not live inline in `Makefile`. |
| Linker script build scripts | `kernel/build.rs`, `init/build.rs`, `demos/*/build.rs`, `capos-rt/build.rs`, `capos-wasm/build.rs` | Source-controlled scripts and linker scripts. `capos-rt/build.rs` emits the runtime linker script for both the legacy `target_os = "none"` userspace build path and the booted custom `target_os = "capos"` path. `capos-wasm/build.rs` mirrors the same pattern for the wasm-host bin (Phase W.2 onward) and uses `cargo:rustc-link-arg-bins` so the linker script applies only to the bin and not the lib. | Build rerun boundaries are explicit; generated link args are not independently audited. |
| CUE manifest compiler | `Makefile` `CUE_TARBALL_URL`/`CUE_TARBALL_SHA256`, `tools/mkmanifest/src/main.rs`, `tools/mkmanifest/src/lib.rs`, `.github/workflows/ci.yml` | `make cue-ensure` downloads the official `cue_v0.16.0_linux_amd64.tar.gz` release binary, verifies its SHA-256, extracts `cue` into `$(CAPOS_TOOLS_ROOT)/cue/0.16.0/bin/cue`, and checks the reported version -- the same download-and-verify pattern used for Typst and uv. `CAPOS_TOOLS_ROOT` defaults to `$HOME/.capos-tools` (per-user shared cache); operators may override it explicitly. This replaces the prior `go install cuelang.org/go/cmd/cue`, which compiled from source under a floating Go toolchain rather than verifying a pinned binary by hash. | Make exports `CAPOS_CUE` and `CAPOS_TOOLS_ROOT` to `tools/mkmanifest`, and CI records that exact path through `$GITHUB_ENV` before both the host-baseline `cargo test-mkmanifest` gate and QEMU smoke. `mkmanifest::expected_cue_path` derives the same per-user path, rejects missing or non-canonical `CAPOS_CUE`, and checks `cue version v0.16.0` before export. The same path and version checks now gate both boot-manifest compilation and `mkmanifest cue-to-capnp` data-message conversion. |
| Default boot manifest defaults package | `cue/defaults/defaults.cue`, `cue.mod/module.cue`, `system.cue`, `tools/mkmanifest/src/lib.rs` | `cue/defaults/defaults.cue` declares `package defaults` and exports `#DefaultSystem`, the shared scaffold for the default boot manifest. `cue.mod/module.cue` pins `module: "capos.local"` with language `v0.16.0`. `system.cue` imports the defaults via `capos.local/cue/defaults`, declares `package capos`, and `mkmanifest --package capos system.cue manifest.bin` exports the unified package. | `make` invokes `mkmanifest` with `--package capos` only when `MANIFEST_SOURCE` is `system.cue`; focused-proof `system-*.cue` manifests stay in single-file mode. The defaults package is a manifest-rule prerequisite, so edits trigger rebuilds. |
| Operator overlay surface | `system.local.cue.example`, `system.local.cue` (gitignored), `.gitignore` | The repo-root overlay file is `system.local.cue` (`package capos`); `system.local.cue.example` is the committed worked-example template. CUE's package mode unifies it with `system.cue` automatically. | Operators copy the example, edit, and rebuild — `system.local.cue` is a `wildcard`-resolved manifest-rule prerequisite. The overlay is gitignored explicitly to avoid accidental commits of host-specific keys or principals. |
| Host-user manifest tag | `Makefile`, `system.cue` `_user @tag(user)` / `_displayName @tag(displayName)`, `tools/mkmanifest/src/lib.rs` `cue_export_args` / `cue_tags_from_env_values`, `target/.cue-tags.<manifest>` | `make run` sets `CAPOS_CUE_USER=$(USER)`. `mkmanifest` reads that structured account variable, derives `displayName` from the same account's first GECOS/comment field in `/etc/passwd` when `CAPOS_CUE_DISPLAY_NAME` is unset, and falls back to the account name when the passwd comment is unavailable. It also reads generic `CAPOS_CUE_TAGS` (and `--tag key=value` CLI repeats) and forwards each entry to `cue export --inject`; structured `CAPOS_CUE_USER` / `CAPOS_CUE_DISPLAY_NAME` override duplicate generic keys. The `target/.cue-tags.<manifest-bin>` sentinel records the active tag state via a `FORCE`-prereq rule that touches the file only when content differs, so a tag change invalidates the cached `manifest.bin`; the recipe reads exported environment values at shell runtime rather than splicing tag text into shell syntax. | The injected `user` value reaches the manifest via `system.cue`'s `_user: string \| *"operator" @tag(user)` and surfaces as the default local operator seed account `name`; `displayName` reaches the seed account display name. Untagged `system.cue` keeps the `operator` account-name/display-name defaults, while focused demo and smoke manifests pin their own `demo` fixtures. |
| mdBook documentation tools | `Makefile`, `book.toml` | GitHub release assets for mdBook `v0.5.0` and `mdbook-mermaid` `v0.17.0` are pinned by version and SHA-256 under `$(CAPOS_TOOLS_ROOT)`, which defaults to `$HOME/.capos-tools`. `mdbook-mermaid` supplies the pinned `mermaid.min.js` browser bundle used by both mdBook HTML rendering and docs-PDF Mermaid rasterization. | `make docs` and `make cloudflare-pages-build` verify the tarball checksums and executable versions, refresh the Mermaid assets, and build `target/docs-site`. |
| Typst typesetter (paper and docs PDF builds) | `Makefile` `TYPST_VERSION`, `papers/schema-as-abi/main.typ`, `docs/manual.typ` | GitHub release asset for Typst `v0.14.2` is pinned by version and SHA-256 under `$(CAPOS_TOOLS_ROOT)/typst/0.14.2`, mirroring the mdBook pinning pattern. `typst-ensure` verifies the tarball checksum and the binary's reported version before paper and docs-PDF targets invoke it. Bundled `New Computer Modern` font keeps builds reproducible across hosts. | `make paper` rebuilds `target/papers/schema-as-abi/main.pdf` using the pinned Typst binary; `make cloudflare-pages-build` additionally publishes the PDF as `target/docs-site/papers/schema-as-abi.pdf`. `make docs` also uses Typst to compile the generated system manual PDF from `docs/manual.typ` plus per-page converted Markdown body content. Generated PDFs are not checked in; source `main.typ`, `references.bib`, `docs/manual.typ`, and documentation inputs are checked in. |
| Documentation PDF converter | `Makefile` `UV_VERSION` / `MD2TYPST_VERSION`, `.node-version`, `package.json`, `package-lock.json`, `tools/md2typst-constraints.txt`, `tools/docs-bundle.js`, `tools/build-typst-manual.js`, `tools/mermaid-puppeteer-config.json`, `docs/manual.typ`, `docs/manual-overrides/*.typ` | GitHub release asset for uv `0.11.8` (`uv-x86_64-unknown-linux-gnu.tar.gz`) is pinned by version and SHA-256 under `$(CAPOS_TOOLS_ROOT)/uv/0.11.8`. `uv-ensure` verifies the tarball checksum and the binary's reported version before PDF generation. `uv tool run --constraints tools/md2typst-constraints.txt --from md2typst==0.3.3 md2typst` pins the Markdown-to-Typst converter and its Python dependency set. Node version `22.16.0` is declared by `.node-version` and `package.json`; the current Makefile invokes `node` and `npm` from `PATH`, so host Node selection remains an operator/CI environment responsibility. `package-lock.json` pins `@mermaid-js/mermaid-cli` and its Puppeteer dependency tree; `make mermaid-cli-ensure` runs `npm ci --ignore-scripts` with `PUPPETEER_SKIP_DOWNLOAD=1`, so Puppeteer's install script cannot fetch a browser during dependency installation. Mermaid rasterization uses the explicit `MERMAID_BROWSER_BIN` Chromium/Chrome executable, passes it to Puppeteer as `PUPPETEER_EXECUTABLE_PATH`, and renders PDF diagrams at `MERMAID_PDF_SCALE=3` by default; `tools/mermaid-puppeteer-config.json` disables the browser sandbox for local and gVisor build containers. `tools/docs-bundle.js` reads the explicit manual page list from `docs/manual.typ`, generates `target/docs-bundle/manual.md` plus one Markdown file per manual page, and `docs/manual.typ` owns the PDF title page, contents, page order, page styling, and override placeholders. | `make docs-pdf` converts each generated manual Markdown page to Typst with md2typst, normalizes anchors and links with `tools/build-typst-manual.js`, uses any matching checked-in `docs/manual-overrides/<page-id>.typ` instead of the generated page, rasterizes Mermaid diagrams through the explicit browser executable, and compiles `target/docs-bundle/manual.pdf` with pinned Typst. `make docs` copies that generated PDF to `target/docs-site/manual.pdf` for Cloudflare Pages publication. Generated Markdown, Typst body pages, and PDF files are ignored build artifacts, not tracked source. |
| QEMU and firmware | `Makefile:85-96`, `tools/build-provenance.sh`, `.github/workflows/ci.yml` `qemu-smoke` | The `qemu-smoke` CI job installs `qemu-system-x86=1:8.2.2+ds-0ubuntu1.16` (amd64, `noble-updates`/main or `noble-security`/main, Ubuntu 24.04) and `ovmf=2024.02-2ubuntu0.8` (amd64, `noble-updates`/main, Ubuntu 24.04). OVMF delivers `/usr/share/ovmf/OVMF.fd` -- the first entry in the Makefile's `OVMF_CODE_CANDIDATES` list, so the wildcard discovery resolves to that path on the pinned runner. The Makefile now also pins the selected OVMF firmware blob by SHA-256 (`OVMF_CODE_SHA256`) and gates the ISO and cloud-disk rules on `ovmf-verify`, which fails on hash drift and emits a NOTICE skip when no OVMF candidate is installed. Local boot verification still uses the host-installed `qemu-system-x86_64`. | `make build-provenance` records the current QEMU version, selected executable path, package identity when discoverable, OVMF selected path or explicit absence, OVMF package identity when discoverable, and OVMF firmware hash when the configured firmware path exists. QEMU and OVMF are identified on the CI runner by package name, exact version, architecture, normalized apt source pocket, and selected path; the QEMU binary identity is captured via `dpkg-query`/`apt-cache policy` by `make build-provenance` per run and the OVMF firmware-blob SHA-256 is captured the same way. `make ovmf-verify` fails the build when the on-host OVMF firmware blob does not match the pinned `OVMF_CODE_SHA256`. |
| ISO and host filesystem tools | `Makefile:317-341`, `tools/build-provenance.sh`, `.github/workflows/ci.yml` `qemu-smoke` | The `qemu-smoke` CI job installs `xorriso=1:1.5.6-1.1ubuntu3` (amd64, `noble`/main, Ubuntu 24.04), `make=4.3-4.1build2` (amd64, `noble`/main, Ubuntu 24.04), and `git=1:2.43.0-1ubuntu7.3` (amd64, `noble-updates`/main or `noble-security`/main, Ubuntu 24.04). Local builds still use host-installed `xorriso`, `sha256sum`, `git`, `make`, and shell utilities. | `make build-provenance` records selected executable paths and package identities when discoverable for xorriso, sha256sum, make, git, and related local build tools, plus final ISO hashes. xorriso, make, and git are identified on the CI runner by package name, exact version, architecture, normalized apt source pocket, and selected path; the per-run identity is captured via `dpkg-query`/`apt-cache policy` by `make build-provenance`. The remaining host tools, including `sha256sum`, shell, `build-essential`, and `curl`, remain host-provided or package-observed rather than repo-digest-pinned. |
| Boot manifest and embedded binaries | `system.cue:1-144`, `tools/mkmanifest/src/lib.rs:339-379`, `tools/mkmanifest/src/main.rs`, `tools/build-provenance.sh`, `tools/compare-build-provenance.py`, `Makefile:168-169`, `Makefile:332-341` | Source manifest is checked in; embedded ELF payloads are build artifacts or inline manifest bytes. | Manifest validation checks references and path containment. `make build-provenance` now writes a local provenance record with runner OS/kernel/architecture identity, Rust toolchain details, selected host-tool paths and package identities when discoverable, hashes for the selected manifest, ISO, kernel, OVMF firmware when present, and every embedded binary reported by `mkmanifest --print-binaries`, including file-backed and inline payloads. `make build-provenance-compare` compares two retained records for material drift while ignoring generated timestamp and allowed local `target/` or `.capos-tools/` path-root movement. |
| Vendored upstream snapshots | `vendor/wasmi-no_std/`, `vendor/dns-c-wahern/`, `vendor/fatfs-no_std/`, `vendor/rustls-webpki/`, `vendor/webpki-roots/`, `vendor/embedded-tls/`, each with a `VENDORED_FROM.md` | Each vendored tree is a static, pinned snapshot recorded by version/tag, commit SHA when available, commit date when available, vendoring date, and license. `vendor/wasmi-no_std/wasmi-1.0.9/` pins wasmi `v1.0.9` (commit `61ba65e6563d8b2f5b699b018349d3330b28b9f3`, Apache-2.0 OR MIT) consumed by `capos-wasm/`; `vendor/dns-c-wahern/src/` pins William Ahern's `dns.c` `rel-20160808` (commit `4ec718a77633c5a02fb77883387d1e7604750251`, MIT). `vendor/rustls-webpki/rustls-webpki-0.103.13/` pins `rustls-webpki 0.103.13` (artifact SHA-256 `61c429a8…f756e`, commit `2879b2ce…728e86`, ISC) and `vendor/webpki-roots/webpki-roots-1.0.7/` pins `webpki-roots 1.0.7` (artifact SHA-256 `52f5ee44…2eb9d`, commit `be948464…221688`, CDLA-Permissive-2.0); both are the certificates/TLS Phase-1 verifier deps consumed by the `capos-tls/` Phase-1 verifier crate. `vendor/embedded-tls/embedded-tls-0.19.0/` pins the `embedded-tls 0.19.0` crates.io package (embedded VCS commit `865e1fd983c583228e3bbeb9f4996f1abc454ca3`, Apache-2.0) consumed only by the local TLS client handshake smoke. No source patches (one integration-only empty-`[workspace]` marker per crate); each path dep carries an exact `version = "=X.Y.Z"` pin so cargo-deny's wildcards gate stays happy. | The wasmi snapshot is exercised by `make capos-wasm-build` and the WASI smokes plus `make dependency-policy-check` (cargo-deny + cargo-audit against `capos-wasm/Cargo.lock`). The `rustls-webpki` / `webpki-roots` snapshots are exercised by `capos-tls/` under `cargo build` / `cargo build --features qemu` (bare-metal `x86_64-unknown-none`) and by `make dependency-policy-check` against the root `Cargo.lock`. The `embedded-tls` snapshot is exercised by `make run-cloud-tls-client-handshake`, the focused capOS demo build, and `make dependency-policy-check` against `demos/Cargo.lock`. The dns.c snapshot is not yet on the v0 build path; `demos/posix-dns-resolver/` compiles only `main.c` with a commented-out `dns.h` include. Refreshes follow the procedure recorded in each `VENDORED_FROM.md`. No `vendor/dash/` source-build is present. |
| Build downloads | `Makefile`, Cargo lockfiles, `rust-toolchain.toml` | Limine, CUE, and documentation tool tarballs are explicitly fetched and SHA-256-verified; Cargo and rustup downloads are implicit when caches/toolchains are absent. The build no longer uses a Go toolchain: CUE is now a hash-verified release binary rather than a `go install` compile, so the `actions/setup-go` CI step and the floating `go-version` pin were removed. | Limine artifacts, the CUE release binary, and documentation tool tarballs are verified by SHA-256. Cargo downloads rely on upstream tooling and lockfiles, with no separate repo policy beyond the lockfile checksums. Rustup downloads are now gated by the dated `nightly-2026-04-20` channel pin (see Rust Toolchain section); only the dist tarballs themselves are not yet mirrored. |
| GitHub Actions identities and runner OS | `.github/workflows/ci.yml` | Every third-party Action is pinned by 40-character commit SHA with a trailing `# v<X.Y.Z>` comment marker. The runner OS is pinned to `ubuntu-24.04` rather than the floating `ubuntu-latest` label. | Pin bumps are review-visible as workflow diffs and the trailing version comment makes the intended release auditable. See the GitHub Actions Runner and Workflow Pinning section below for the current pin table and the bump procedure. |

## Security Verification Track S.10.3 Dependency Policy

Dependency changes are accepted only if they satisfy this policy and are
recorded in the owning task checklist.

### Dependency classes

Use these classes when reviewing a dependency change:

- **Kernel-critical no_std:** crates used directly by `kernel`, `capos-lib`,
  `capos-config`, and `capos-abi`.
- **Userspace-runtime no_std:** crates used by `init`, `demos`, and `capos-rt`.
- **Host/build:** crates used by `tools/*`, `build.rs` helpers, and generated
  output pipelines.
- **Test/fuzz/dev:** crates gated by `dev-dependencies` or `target-specific` for
  fuzz/proptests/smoke support.

### Required pre-merge criteria

For any added dependency (or bump in any class):

1. **Manifest and features are explicit.** Dependency entries must include
   explicit feature choices; avoid `default-features = true` unless justified.
2. **No_std compatibility is proven for no_std classes.** Kernel-critical and
   userspace-runtime dependencies must compile in a `#![no_std]` mode with
   `alloc` where expected. `cargo build -p <crate> --target x86_64-unknown-none`
   must succeed for every kernel/no_std crate affected.
3. **Security policy checks run and pass.** CI-equivalent checks for the
   touched workspace are required through `make dependency-policy-check`, which
   runs `cargo deny check` on every Cargo manifest and `cargo audit` on every
   lockfile.
4. **Dependency class change is justified in review.** PR text must include
   target class, ownership rationale, transitive graph impact, and why the crate
   is not a transitive replacement for an already-allowed dependency.
5. **Lockfile behavior is explicit.** Update only intended lockfiles and record
   intentional cross-workspace drift in this document if workspace purpose differs.

### No_std add/edit checklist

- Reject crates that require `std`, OS I/O, or unsupported platform APIs in the
  dependency path intended for kernel classes.
- Reject dependencies that re-export broad platform facades or large unsafe
  surface unless there is a replacement with smaller scope and better audit
  visibility.
- Record a license and supply-chain review result (via policy checks) before
  merge.
- Confirm no `unsafe` contract escapes are added without a review surface note in
  the relevant module.

### Standing requirements

- Add Security Verification Track S.10.3 checks to the target branch plan item
  for any kernel/no_std crate dependency change and document the exact pass
  command set.
- Keep lockfile deltas review-visible in normal PR flow; lockfile pinning is the
  minimum bar, not the gate.
- Keep transitive drift in sync with the trust class: class-wide divergence across
  lockfiles requires explicit justification.

## Remaining gaps after Security Verification Track S.10.3 policy

- Mirror the resolved dated nightly dist tarballs (and their SHA-256
  checksums) into the per-user tool cache as a further hardening step, so
  bumping the pin does not depend on rustup retaining its historical
  manifests. The dated pin closes the floating-channel gap; tarball
  mirroring would close the historical-availability gap.
- Decide whether the local `make kani-lib` workflow should grow a repo-managed
  installer/bootstrap helper or continue to rely on separately provisioned
  user-local `cargo-kani` plus the Kani bundle/toolchain setup path.
- CI now publishes `target/build-provenance.txt` as a named artifact on every
  `qemu-smoke` run (see `actions/upload-artifact` step in `.github/workflows/ci.yml`)
  and, on `pull_request` events, downloads the most recent successful main-branch
  artifact via `actions/download-artifact` and runs
  `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment`
  against it as a blocking PR gate. Missing base provenance is a CI failure,
  not a silent skip; artifact retention is therefore part of the gate.

## Build Provenance Retention And Comparison Policy

Status 2026-06-07 06:35 UTC: this policy applies to local and CI proof
artifacts produced by `make build-provenance`. The `qemu-smoke` CI job now
publishes the candidate record as a named artifact on every run and, on
`pull_request` events, runs `make build-provenance-compare` against the most
recent successful main-branch artifact with
`BUILD_PROVENANCE_COMPARE_POLICY=ci-environment`. That CI policy is
PR-blocking for runner, tool, Rust, OVMF package, and OVMF hash drift while
allowing expected base-vs-head source commit, ISO/kernel/manifest hash, and
embedded-payload hash differences. This remains a reproducibility evidence
policy, not a claim that production images are third-party reproducible before
the unresolved pinning gates below are closed.

Package-pin bumps for `qemu-system-x86`, `xorriso`, `make`, `git`, or `ovmf`
are the only planned baseline-refresh case where the PR comparison can fail on
purpose: the candidate provenance records the new reviewed package identity,
while the base-branch artifact still records the old one. That failure is not a
green PR exception and is not a workflow bypass. The bump can land only through
a reviewed local-main integration or maintainer push path after the branch's
`qemu-smoke` build, `make run-smoke`, and `make build-provenance` steps pass
with the new pins, and the compare diff contains only the reviewed
package-identity changes introduced by the same branch. After the bump lands,
the next successful main-branch `qemu-smoke` push artifact becomes the refreshed
base provenance; unrelated PRs must wait for that artifact before their
blocking environment comparisons can pass.

For every externally cited QEMU proof, release candidate, paper artifact, or
public performance/security claim, retain the following as one immutable
evidence bundle:

- `target/build-provenance.txt` from the exact checked commit, manifest, and
  recorded worktree state;
- the kernel, manifest, ISO, OVMF firmware if used, and embedded-binary hashes
  recorded in that provenance file;
- the exact command set and QEMU transcript or host-test log used as evidence;
- the source commit hash, clean-tree assertion or retained `git diff` plus
  untracked-file inventory, and any non-default Make variables such as
  `MANIFEST_SOURCE`, `CAPOS_CUE_TAGS`, `QEMU_NET`, `MERMAID_BROWSER_BIN`, or
  `CAPOS_TOOLS_ROOT`;
- the `system.local.cue` overlay state for default-manifest builds: record
  explicit absence, or retain the file content plus SHA-256 and size. Because
  the overlay is gitignored and unified into `system.cue`, a commit hash alone
  is not enough to reconstruct a default-manifest build that used it;
- the runner identity: either a pinned CI/container image digest, or the host
  package identities for Rust, QEMU, xorriso, make, git, OVMF firmware package,
  and the operating-system image when the runner is not pinned.

Retention requirements:

- Keep evidence bundles for any tagged release, published paper result, public
  benchmark, or public security claim for at least the lifetime of that claim.
- Keep pre-merge task evidence until the reviewed branch has merged and the
  next full relevant verification has superseded it.
- Keep failed evidence when it explains a known regression, review finding, or
  release blocker; otherwise failed local scratch logs may be discarded.
- Do not rely on `target/` as the retention store. `target/` artifacts are
  local build output; retained evidence must be copied to the release, CI, or
  paper artifact store that owns the claim.

Comparison requirements:

- Run local comparisons with `make build-provenance-compare
  BASE_PROVENANCE=... CANDIDATE_PROVENANCE=...` or
  `tools/compare-build-provenance.py BASE CANDIDATE`. The command exits zero
  only when records differ by generated timestamp and allowed local path roots
  such as worktree `target/` or `.capos-tools/`, while all hashes, versions,
  package identities, and runner identities match.
- Run PR base-vs-head environment comparisons with
  `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment
  BASE_PROVENANCE=... CANDIDATE_PROVENANCE=...`. This policy compares the
  default manifest source, host target, runner identity, GitHub-hosted image
  identity when present, Rust toolchain, selected executable identities, tool
  versions, OVMF selection, and OVMF hash, but ignores expected source commit,
  kernel/manifest/ISO hash, and embedded-binary hash changes between the base
  branch and PR head.
- For package-pin bump branches, treat a `ci-environment` comparison failure as
  acceptable review evidence only when every reported difference is an intended
  package-identity change for `qemu-system-x86`, `xorriso`, `make`, `git`, or
  `ovmf` from the same branch. Any runner image, Rust toolchain, OVMF firmware
  hash, tool-version, or unrelated package drift remains blocking.
- Compare two provenance records by commit, clean or retained-diff state,
  `system.local.cue` absence/hash/content policy, manifest source, manifest
  binary hash, kernel hash, ISO hash, embedded-binary table, OVMF hash or
  explicit absence, host-tool versions, package identities, and operating-system
  image identity.
- A byte-identical ISO requires all recorded hashes to match. Equal source
  commits with different Rust, QEMU, xorriso, OVMF, or host package identities
  are compatible proof reruns, not reproducible-production evidence.
- If a comparison differs only in paths under `.capos-tools` or worktree-local
  `target/` directories while all hashes and versions match, treat the result
  as the same proof environment.
- If a comparison differs in worktree state, overlay state, package identity,
  operating-system image identity, host-tool version, OVMF hash, Rust compiler
  commit/date, embedded-binary hash, or ISO hash, record the difference in the
  owning review or release note before citing the result.

Minimum runner identity for production-hardening branches:

- Rust must be a date-pinned nightly or stronger hash-pinned toolchain, not the
  floating `nightly` channel.
- QEMU, xorriso, make, and git must come from a pinned runner image digest or a
  documented package set with package name, version, architecture, repository,
  and distribution release.
- OVMF firmware must be either repo-pinned by digest or identified by package
  name, version, architecture, repository, distribution release, selected path,
  and SHA-256.
- Any runner image used for production reproducibility claims must be cited by
  immutable digest. Mutable tags are acceptable only for local proof evidence.

Production hardening must treat the following as unresolved supply-chain gates,
not as cosmetic reproducibility work:

```text
immutable runner image digest or repo-managed tool digests for qemu/xorriso/make/git
```

The Rust nightly date pin (currently `nightly-2026-04-20`) closes the
floating-channel gate; tarball mirroring is tracked as a further hardening
step in the Remaining gaps section above. The `qemu-smoke` job now installs
`qemu-system-x86=1:8.2.2+ds-0ubuntu1.16` (amd64,
`noble-updates`/main or `noble-security`/main, Ubuntu 24.04),
`xorriso=1:1.5.6-1.1ubuntu3` (amd64, `noble`/main, Ubuntu 24.04),
`make=4.3-4.1build2` (amd64, `noble`/main, Ubuntu 24.04),
`git=1:2.43.0-1ubuntu7.3` (amd64, `noble-updates`/main or
`noble-security`/main, Ubuntu 24.04), and `ovmf=2024.02-2ubuntu0.8` (amd64,
`noble-updates`/main, Ubuntu 24.04) so the QEMU, ISO writer, make, git, and
OVMF firmware identities are all captured for UEFI smoke builds: package name,
exact version, architecture, normalized apt source pocket, and the per-run
identity captured via `dpkg-query`/`apt-cache policy` by
`make build-provenance`. OVMF additionally records the selected path
(`/usr/share/ovmf/OVMF.fd`) and per-run SHA-256, and the Makefile now pins the
selected firmware blob by SHA-256 through the `ovmf-verify` gate wired into
the ISO and cloud-disk rules. Repo-pinned digests (download-and-verify rather
than apt-installed) for `qemu-system-x86`, `xorriso`, `make`, and `git`, or an
immutable runner image digest that contains them, remain future hardening
tracked in `docs/design-risks-register.md` (R13).
`xorriso` has no version in `noble-updates`; the pin uses the only available
`noble`/main version, which is what every Ubuntu 24.04 host resolves to.

Until those gates land, generated ISO/manifest/payload artifacts plus
`target/build-provenance.txt` are suitable for local and CI proof evidence, but
not for claims that a third party can reproduce an identical production boot
image from source alone.

## Bootloader and ISO Inputs

The Makefile now pins Limine at commit
`aad3edd370955449717a334f0289dee10e2c5f01` and verifies these copied artifacts:

| Artifact | Checksum reference |
| --- | --- |
| `$(LIMINE_DIR)/limine-bios.sys` | `LIMINE_BIOS_SYS_SHA256` in `Makefile` |
| `$(LIMINE_DIR)/limine-bios-cd.bin` | `LIMINE_BIOS_CD_SHA256` in `Makefile` |
| `$(LIMINE_DIR)/limine-uefi-cd.bin` | `LIMINE_UEFI_CD_SHA256` in `Makefile` |
| `$(LIMINE_DIR)/BOOTX64.EFI` | `LIMINE_BOOTX64_EFI_SHA256` in `Makefile` |

`$(LIMINE_DIR)` resolves to
`$(CAPOS_TOOLS_ROOT)/limine/<LIMINE_COMMIT>` (default
`$HOME/.capos-tools/limine/<commit>` unless `CAPOS_TOOLS_ROOT` is
overridden), shared with the rest of the per-user pinned tool cache.

`make limine-ensure` clones `https://github.com/limine-bootloader/limine.git`
only when `$(LIMINE_DIR)/.git` is absent, fetches the pinned commit if needed,
checks it out detached, and runs `make` inside the Limine tree (the
`limine-ensure` recipe). `make limine-verify` then checks the repository HEAD
and artifact checksums (the `limine-verify` recipe). The ISO copies the
kernel, generated `manifest.bin`, Limine config, and verified Limine
artifacts into `iso_root/`, runs `xorriso`, then runs `limine bios-install`
(the `$(ISO)` recipe).

Remaining reproducibility gap: Limine source is pinned, but the Limine
build host compiler and environment are not pinned or recorded.

## Rust Toolchain

`rust-toolchain.toml` specifies:

- `channel = "nightly-2026-04-20"`
- `targets = ["x86_64-unknown-none", "aarch64-unknown-none", "wasm32-wasip1"]`
- `components = ["rust-src"]`

The `wasm32-wasip1` target is needed for the WASI Preview 1 demo payloads
(`demos/wasi-hello-rust/`, `demos/wasi-cli-args/`, `demos/wasi-random/`) built
by `make wasi-hello-rust-build`, `make wasi-cli-args-build`, and
`make wasi-random-build`; the wasm-host binary itself is built for the booted
`x86_64-unknown-capos` userspace target instead.

The pinned dated channel resolves to:

- `rustc 1.97.0-nightly (e22c616e4 2026-04-19)`
- host target `x86_64-unknown-linux-gnu`

The 2026-04-20 manifest packages the rustc commit cut on 2026-04-19; that is the
upstream `dist` naming convention, not a drift. Rustup will continue to install
the same dist tarball for `nightly-2026-04-20` as long as upstream retains it.

The Makefile derives `HOST_TARGET` from `rustc -vV` (`Makefile:12`) and uses
that for `tools/mkmanifest` (`Makefile:28-29`). Cargo aliases in
`.cargo/config.toml:4-48` hard-code `x86_64-unknown-linux-gnu` for host tests.
The custom userspace target aliases in `.cargo/config.toml` use
`targets/x86_64-unknown-capos.json` plus `-Zjson-target-spec` and
`-Zbuild-std=core,alloc`, so `rust-src` is a required toolchain component.
The CI `host-baseline` and `qemu-smoke` jobs install the same
`nightly-2026-04-20` toolchain so CI matches the local
`rust-toolchain.toml` resolution. The `kani-proofs` job stays on
`nightly-2025-11-21` because Kani requires its own paired nightly bundle
installed by `cargo kani setup`; advancing the Kani pin is tracked separately
through that bundle's compatibility matrix.

### Rust Nightly Date Pin Policy

The pin is one of the supply-chain-trust controls listed in this proposal
alongside the Limine commit, OVMF firmware SHA-256, capnp tarball SHA-256,
CUE binary, mdBook/mdbook-mermaid release assets, Typst binary, uv binary,
and pinned `cargo-deny`/`cargo-audit`/`cargo-kani` releases. All of these
must be pinned at the same trust level -- date- or hash-anchored, never a
floating channel or moving tag. This subsection states the policy for the
Rust nightly entry; the next subsection states the mechanical advance
procedure.

**Where the pin lives.** Exactly one source: `rust-toolchain.toml` as a
date-anchored nightly channel of the form `nightly-YYYY-MM-DD`. The CI
workflow's `host-baseline` and `qemu-smoke` `toolchain:` values must
mirror that same dated channel. No other file may declare a nightly date;
no float, no `nightly` shorthand, no commit-hash override.

**Promotion criteria.** A bump is accepted only when the candidate
nightly satisfies all of the following against the worktree where the pin
lands:

- `make` builds the full workspace clean (kernel + standalone userspace +
  ISO) with no new warnings under `cargo build --features qemu`.
- `make fmt-check` passes across the workspace and all standalone crates.
- `make workflow-check` passes (CLAUDE.md token budget, mandatory-context
  budgets, slice trailers).
- `make check` passes (the aggregate build/test gate that includes
  `generated-code-check` and the host-test aliases).
- `make run-smoke` passes on the developer host when QEMU smoke is
  feasible there; if QEMU is unavailable locally, the bump branch's CI
  `qemu-smoke` run is the authoritative gate.
- Any new rustc warning, lint, or unrelated build failure introduced by
  the new nightly is treated as a real gate failure. Do not relax capOS
  code or silence the lint to land the bump.

**Rollback.** If a promotion exposes a regression in a downstream crate
that capOS depends on (`limine`, `x86_64`, `spin`, `smoltcp`, `wasmi`,
`capnp`/`capnpc`, or any cargo-deny/cargo-audit pinned tool), revert the
pin to the prior dated channel on `main`, file a tracking note in
`docs/tasks/` with the failing date, the failing crate, and the upstream issue
if one exists, and resume normal cadence only after the downstream regression
is resolved or worked around.

**Cadence.** Bump the pin at least once per quarter even without a
specific feature trigger so production-provenance evidence does not lag
upstream. Bump out of cadence when (a) a security advisory affects the
current pinned nightly's `rustc`/`cargo`/`std` (consult
`rust-lang/rust`, `rustsec/advisory-db`, and the cargo-audit output for
the pinned dist), or (b) a compiler feature, fix, or lint that capOS
depends on lands upstream. Unbounded float is not permitted: the dated
channel must always resolve to a concrete `YYYY-MM-DD`.

**Approvals.** Maintainer-driven, single reviewed slice per bump. No
automated promotion bot. The pin bump is its own contract change and
must not be bundled with unrelated behavior changes; the reviewed diff
must show only `rust-toolchain.toml`, the CI workflow, this proposal's
summary table and resolved-`rustc` line, and any minimal lint/code
adjustments forced by the new nightly with an inline justification.

**Trust-input dimension.** The pin closes the floating-channel supply-
chain gate listed in the Build Provenance Retention And Comparison
Policy ("Minimum runner identity for production-hardening branches:
Rust must be a date-pinned nightly or stronger hash-pinned toolchain,
not the floating `nightly` channel"). Mirroring the resolved dist
tarballs into the per-user tool cache (the same shape as Limine, capnp,
CUE, mdBook, and Typst pins) remains a future hardening step tracked in
the Remaining gaps section.

### Advance procedure (bumping the dated nightly)

When to bump:

- A compiler feature, fix, or lint that capOS depends on lands in upstream
  nightly after `2026-04-20`. Example triggers: a Cargo or rustc fix that
  unblocks a build path; a `core`/`alloc` change that affects `-Zbuild-std`;
  a `rustfmt` change required for the project formatting baseline.
- Toolchain drift hygiene: schedule a bump at least once per release window
  even without a specific feature trigger, so production-provenance evidence
  does not lag too far behind upstream.

How to bump:

1. Choose a candidate nightly date and verify all required targets and the
   `rust-src` component are simultaneously available for that date:

   ```bash
   rustup toolchain add nightly-<YYYY-MM-DD> \
       --target x86_64-unknown-none \
       --target aarch64-unknown-none \
       --target wasm32-wasip1 \
       --component rust-src
   ```

   If any target or component is missing, try adjacent dates (rustup's
   nightly dist manifests sometimes drop a target for a single day) until
   one is found that provides the full set.

2. Update both files in the same commit:

   - `rust-toolchain.toml` `channel` value.
   - `.github/workflows/ci.yml` -- both the `host-baseline` and `qemu-smoke`
     `toolchain:` values. Leave `kani-proofs` on its own pin.

3. Run the full local gate set against the candidate before pushing:
   `make fmt-check`, `cargo build --features qemu`, `make check`,
   `make workflow-check`, `make run-smoke`. Treat any new warning or
   unrelated build failure as a real gate failure -- do not patch around
   compiler drift by relaxing capOS code.

4. Update the `Rust toolchain` row in this file's summary table, the
   resolved `rustc` line above, and the `last_reviewed` front-matter
   timestamp. Cite the new dated channel.

5. Land the pin bump as its own reviewed slice, not bundled with unrelated
   behavior changes. The pin is itself the provenance contract.

Remaining reproducibility gap: rustup retains nightly dist manifests for a
finite window. A future hardening slice may mirror the resolved dist tarballs
plus their SHA-256 checksums into the per-user tool cache the same way Limine
and capnp are pinned today, so a bump-without-mirror does not become a silent
loss of historical reproducibility.

## CI Runner Package Pins

The `qemu-smoke` CI job installs `qemu-system-x86`, `xorriso`, `make`, `git`,
and `ovmf` via apt on an `ubuntu-24.04` runner. Those packages provide the QEMU
emulator that executes every QEMU smoke, the ISO writer that builds the
bootable image consumed by smokes, the build and repository tools used after
checkout, and the UEFI firmware blob selected by `make run-uefi` and the
cloud-disk path. A floating apt install (no `=<version>` specifier) would let
upstream Ubuntu silently roll any of them on the next CI run, so this section
names the version pins, the file that owns them, and the procedure for
advancing them.

### CI Package Pin Policy

The pin is one of the supply-chain-trust controls listed in this proposal
alongside the Limine commit, OVMF firmware SHA-256, Rust nightly date pin,
capnp tarball SHA-256, CUE binary, mdBook/mdbook-mermaid release assets,
Typst binary, uv binary, and pinned `cargo-deny`/`cargo-audit`/`cargo-kani`
releases. All of these must be pinned at the same trust level -- date- or
hash-anchored, never a floating channel or moving tag. This subsection
states the policy for the QEMU, xorriso, make, git, and OVMF package entries;
the next subsection states the mechanical advance procedure.

**Where the pin lives.** Exactly one source: the `Install boot smoke
dependencies` step of the `qemu-smoke` job in `.github/workflows/ci.yml`.
Each package must be invoked as `<name>=<exact-version>` (no `*` wildcard
and no major-only floor) so the apt resolver fails closed rather than
silently rolling forward. The currently pinned versions are
`qemu-system-x86=1:8.2.2+ds-0ubuntu1.16` (amd64,
`noble-updates`/main or `noble-security`/main, Ubuntu 24.04),
`xorriso=1:1.5.6-1.1ubuntu3` (amd64, `noble`/main, Ubuntu 24.04),
`make=4.3-4.1build2` (amd64, `noble`/main, Ubuntu 24.04),
`git=1:2.43.0-1ubuntu7.3` (amd64, `noble-updates`/main or
`noble-security`/main, Ubuntu 24.04), and `ovmf=2024.02-2ubuntu0.8` (amd64,
`noble-updates`/main, Ubuntu 24.04). The summary table, the `QEMU and firmware`
and `ISO and host filesystem tools` rows, the `Host Tools` section, and the
Build Provenance Retention And Comparison Policy mirror these strings; the
policy text is the single source of truth and the other locations track it.

**Promotion criteria.** A bump is accepted only when all of the following
hold against the bump branch:

- The Ubuntu base image rolls (`noble`/`noble-updates`/`noble-security`
  publishes a newer version of the package) or a security advisory affects
  the currently pinned version. Cosmetic version bumps without an upstream
  trigger are not accepted; the pin moves forward when there is a reason
  to move it.
- `apt-cache madison <package>` on a current Ubuntu 24.04 host lists the
  candidate version, and the candidate is available from
  `noble-updates`/main (or `noble`/main when no `noble-updates` entry
  exists, as is the case for `xorriso` today). Versions sourced from
  third-party PPAs or `*-proposed` pockets are not accepted.
- The bump branch's `qemu-smoke` execution reaches and passes the new-pin
  build evidence steps: `make` build, `make run-smoke`,
  `make build-provenance`, and candidate provenance artifact upload. The
  pull-request
  `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment`
  step is expected to fail only on reviewed package-identity fields that match
  the new pinned strings rather than the previous ones; every other comparison
  difference remains blocking.
- No new QEMU, xorriso, make, git, or OVMF behavior is silently relied on: if the
  bump unlocks a smoke that previously failed, that smoke must be enabled
  and reviewed in the same bump branch rather than treated as incidental.
- The `Trusted Build Inputs` summary table, `QEMU and firmware` row, `ISO
  and host filesystem tools` row, `Host Tools` section, and Build
  Provenance Retention And Comparison Policy text are updated to cite the
  new versions and the new resolved repository (`noble`/`noble-updates`)
  in the same commit.

**Rollback.** If a promotion exposes a regression in the QEMU smoke path,
the ISO writer, build orchestration, repository operations, or UEFI boot,
revert the `.github/workflows/ci.yml` change to the prior pinned version on
`main`, file a tracking task under
`docs/tasks/` with the failing version, the failing smoke, and the upstream
Ubuntu/QEMU/xorriso/OVMF issue if one exists, and resume normal cadence only
after the regression is resolved or worked around. Reverting also requires
reverting the summary-table and policy text mirrors so the recorded versions
stay consistent with the workflow file.

**Cadence.** Bump the pins at least once per quarter even without a
specific security trigger, so production-provenance evidence does not lag
upstream Ubuntu point releases. Bump out of cadence when (a) a security
advisory affects the current pinned version of any of the packages
(consult the Ubuntu Security Notices, the QEMU security mailing list, the
Git security advisories, GNU make release notes, and the `ovmf`/`edk2`
advisories), or (b) a fix that capOS depends on lands
in a newer Ubuntu point release. Unbounded float is not permitted: each
package must always resolve to a concrete
`<epoch>:<upstream>-<debian>` version string.

**Approvals.** Maintainer-driven, single reviewed slice per bump. No
automated promotion bot. The pin bump is its own contract change and
must not be bundled with unrelated behavior changes; the reviewed diff
must show only `.github/workflows/ci.yml`, this proposal's summary table and
resolved-version mirrors, the relevant production-provenance task record when
sub-items move, and any minimal smoke adjustments forced by the new package
versions with an inline justification.

**Trust-input dimension.** The pin closes the runner/OS/tool identity
gate listed in the Build Provenance Retention And Comparison Policy
("Minimum runner identity for production-hardening branches: QEMU,
xorriso, make, and git must come from a pinned runner image digest or a
documented package set with package name, version, architecture,
repository, and distribution release") for the apt-installed package set it
owns. A pinned runner image digest (replacing the `ubuntu-24.04` mutable label
with an immutable image SHA) or repo-managed tool digests for those packages
remain future hardening tracked in `docs/design-risks-register.md` (R13).

### Advance procedure (bumping the apt-pinned versions)

When to bump:

- An Ubuntu Security Notice affects the currently pinned version of
  `qemu-system-x86`, `xorriso`, `make`, `git`, or `ovmf`.
- A QEMU, xorriso, make, git, or OVMF point release lands in
  `noble-updates`/main that capOS needs (typically a virtio, MSI-X, ISO
  writer, build-tool, repository-tool, or UEFI fix).
- Quarterly hygiene cadence with no specific feature trigger, so the pin
  does not lag too far behind upstream.

How to bump:

1. On a current Ubuntu 24.04 host (or a `ubuntu:24.04` container that has
   refreshed `apt-get update`), list available versions of each package:

   ```bash
   apt-cache madison qemu-system-x86
   apt-cache madison xorriso
   apt-cache madison make
   apt-cache madison git
   apt-cache madison ovmf
   ```

   Pick the highest stable version from `noble-updates`/main. If a
   package has no `noble-updates` entry (as is the case for `xorriso`
   today), pick from `noble`/main. Do not select from `*-proposed`,
   `*-backports`, or third-party PPAs.

2. Update the single source in the `Install boot smoke dependencies` step
   of the `qemu-smoke` job in `.github/workflows/ci.yml` so each package
   line reads `<name>=<exact-version>`.

3. Update the mirrors in this file in the same commit: the summary-table
   rows for `QEMU and firmware` and `ISO and host filesystem tools`, the
   `Host Tools` section, the Build Provenance Retention And Comparison
   Policy text, and the `Remaining gaps for Security Verification Track
   S.10.2/S.10.3` block under `Manifest, Embedded Binaries, and
   Downloaded Artifacts`. Refresh the `last_reviewed` front-matter
   timestamp.

4. If the OVMF package version moves, the OVMF firmware blob SHA-256 may
   change. Recompute `OVMF_CODE_SHA256` in `Makefile` from the resolved
   firmware path (`/usr/share/ovmf/OVMF.fd` on Ubuntu 24.04) and verify
   `make ovmf-verify` passes against the new digest. Land the
   `OVMF_CODE_SHA256` change in the same commit as the package bump.

5. Push the bump branch and let `qemu-smoke` exercise the new pins through
   `make`, `make run-smoke`, `make build-provenance`, and candidate
   provenance artifact upload. The acceptance gate for the bump itself is those
   steps passing plus a reviewed PR
   `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment`
   failure whose diff is limited to the intended package-identity strings
   replacing the previous ones. Land the bump through the reviewed local-main
   integration or maintainer push path; after it reaches `main`, the next
   successful main-branch `qemu-smoke` push artifact is the new base record for
   unrelated PR comparisons.

6. Land the pin bump as its own reviewed slice, not bundled with
   unrelated behavior changes. The pin is itself the provenance contract.

Remaining reproducibility gap: the `ubuntu-24.04` runner label is still
managed by GitHub Actions, not by an immutable image digest, so the host
package set underneath the apt-installed `qemu-system-x86`, `xorriso`,
`make`, `git`, and `ovmf` pins can still roll between runs. A future hardening slice may
move the `qemu-smoke` job to a self-built runner image referenced by
digest, mirror the apt package files into the per-user tool cache the
same way Limine and capnp are pinned today, or both, so a bump-without-
mirror does not become a silent loss of historical reproducibility.

## Cargo Dependencies

The root workspace members are `capos-abi`, `capos-config`, `capos-lib`,
`capos-tls`, `kernel`, and the host-only `tools/capnp-build` build-support
crate. `Cargo.toml` keeps default members to `capos-config`, `capos-lib`,
`capos-tls`, and `kernel` so ordinary root bare-metal builds do not build the
host helper as a target package but do build the `capos-tls` certificates/TLS
verifier-dependency probe. The vendored `rustls-webpki` / `webpki-roots` path
dependencies declare their own `[workspace]` and are listed in the root
`Cargo.toml` `exclude` set (the same isolation as the vendored `fatfs` crate),
so they are not workspace members. The vendored `embedded-tls` client-state
machine snapshot follows the same workspace isolation and is consumed only by
the standalone `demos/` workspace. `init/`, `demos/`, `tools/mkmanifest/`,
`tools/ringtap-viewer/`, `capos-rt/`, `shell/`, `libcapos/`,
`libcapos-posix/`, `capos-wasm/`, and `fuzz/` are standalone workspaces
with their own lockfiles.

Important direct dependencies and current root-lock resolutions:

| Dependency | Manifest references | Root lock resolution |
| --- | --- | --- |
| `capos-abi` | `capos-config/Cargo.toml`, `capos-lib/Cargo.toml` | local path package in `Cargo.lock` |
| `argon2` | `capos-lib/Cargo.toml`; optional `capos-config/Cargo.toml` `credential-validation` feature used by kernel/init/mkmanifest bootstrap validation | `0.5.3` in `Cargo.lock` |
| `capnp` | `capos-config/Cargo.toml`, `capos-lib/Cargo.toml`, `kernel/Cargo.toml` | `0.25.4` in `Cargo.lock` |
| `capos-capnp-build` | `capos-config/Cargo.toml` | local path package in `Cargo.lock` |
| `capnpc` | `tools/capnp-build/Cargo.toml` | `0.25.3` in `Cargo.lock` |
| `limine` crate | `kernel/Cargo.toml:8` (`"0.6"` range) | `0.6.3` in `Cargo.lock` |
| `spin` | `kernel/Cargo.toml:9` (`"0.9"` range) | `0.9.8` in `Cargo.lock` |
| `x86_64` | `kernel/Cargo.toml:10` (`"0.15"` range) | `0.15.4` in `Cargo.lock` |
| `linked_list_allocator` | `kernel/Cargo.toml:11` (`"0.10"` range) | `0.10.6` in `Cargo.lock` |
| `smoltcp` | `kernel/Cargo.toml:16` (`"0.13.0"` caret range) | `0.13.0` in `Cargo.lock` |
| `loom` | `capos-config/Cargo.toml:27` | `0.7.2` in `Cargo.lock` |
| `proptest` | `capos-lib/Cargo.toml` | `1.11.0` in `Cargo.lock` |
| `rustls-webpki` (vendored path) | `capos-tls/Cargo.toml` (`=0.103.13`, `default-features = false`, `alloc`) | local path package (`vendor/rustls-webpki/rustls-webpki-0.103.13`) in `Cargo.lock` |
| `webpki-roots` (vendored path) | `capos-tls/Cargo.toml` (`=1.0.7`, `default-features = false`) | local path package (`vendor/webpki-roots/webpki-roots-1.0.7`) in `Cargo.lock` |
| `rustls-pki-types` | transitive of the vendored `rustls-webpki`/`webpki-roots` (`alloc`) | `1.14.1` in `Cargo.lock` |
| `untrusted` | transitive of the vendored `rustls-webpki` | `0.9.0` in `Cargo.lock` |
| `zeroize` | transitive of `rustls-pki-types` (`alloc`) | `1.8.2` in `Cargo.lock` |

The four kernel-critical crates `limine`, `spin`, `x86_64`, and `smoltcp` are
declared with semver-range requirements (`"0.6"`, `"0.9"`, `"0.15"`, and the
caret `"0.13.0"`), not the exact `=X.Y.Z` requirements applied to `capnp`
(`=0.25.4`) in `kernel/Cargo.toml` and `sha2` (`=0.10.9` in
`capos-lib/Cargo.toml`). This requirement-level asymmetry is currently
unintentional drift in manifest style rather than a deliberate policy: the
exact crate version that ships is still pinned by the checked-in `Cargo.lock`
checksums above and is review-visible through lockfile diffs, so a range
requirement does not widen what actually compiles without a lockfile change.
Tightening these four manifest requirements to `=X.Y.Z` to match `capnp`/`sha2`
is a separate build-risk change (a manifest edit plus lockfile regeneration and
re-verification), tracked as a doc-accuracy gap here rather than changed in
this inventory pass.

Standalone lockfile drift observed during this inventory:

The TLS client handshake smoke adds a userspace-runtime no_std dependency in the
standalone `demos/` workspace: `embedded-tls = "=0.19.0"` as a path dependency
under `vendor/embedded-tls/embedded-tls-0.19.0/`, with `default-features =
false` and only the `rustpki` feature enabled. `demos/Cargo.lock` pins the
resulting RustCrypto TLS 1.3 closure. The capOS custom target forces the
software AES and POLYVAL backends in `.cargo/config.toml` so those crypto
dependencies do not select x86 accelerated backend code that is outside the
custom-target build contract.

| Lockfile | Notable direct/runtime resolution |
| --- | --- |
| `init/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6` |
| `demos/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6` |
| `demos/wasi-hello-rust/Cargo.lock` | Single-package leaf lockfile for the `wasm32-wasip1` Rust hello payload; no third-party direct dependencies. |
| `demos/wasi-cli-args/Cargo.lock` | Single-package leaf lockfile for the Phase W.3 argv-grant `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `demos/wasi-env/Cargo.lock` | Single-package leaf lockfile for the WASI environment-grant `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `demos/wasi-fs/Cargo.lock` | Single-package leaf lockfile for the WASI filesystem `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `demos/wasi-random/Cargo.lock` | Single-package leaf lockfile for the Phase W.4 `random_get` `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `demos/wasi-preview1-refusals/Cargo.lock` | Single-package leaf lockfile for the WASI Preview 1 refusal-coverage `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `demos/wasi-stdio-fd/Cargo.lock` | Single-package leaf lockfile for the WASI stdio-fd `wasm32-wasip1` Rust payload; no third-party direct dependencies. |
| `tools/mkmanifest/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `serde_json 1.0.149` |
| `tools/adventure-content-gen/Cargo.lock` | Host generator for adventure content; locked dependencies include `serde_json` and the `cue`-export-to-JSON pipeline; no `capnp` runtime dependency. |
| `tools/paperclips-content-gen/Cargo.lock` | Host generator for Paperclips content; locked dependencies include `serde_json` and `capnp 0.25.4` for schema-aware JSON-to-binary conversion through `mkmanifest cue-to-capnp`. |
| `tools/remote-session-client/Cargo.lock` | Standalone Linux host-side remote-session client; pins `capnp 0.25.4` and `serde 1.0.228`; no transitive `wasmi`, Argon2, or smoltcp dependency. Covered by `make dependency-policy-check`. |
| `tools/ringtap-viewer/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`; no Argon2 because it uses baseline `capos-config` |
| `capos-rt/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6` |
| `capos-service/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6` (the same allocator resolution as `capos-rt`/`demos`/`libcapos`; no cross-workspace drift). |
| `libcapos/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6` plus the local `capos-rt` path dependency. |
| `libcapos-posix/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6`, plus local `capos-rt` and `libcapos` path dependencies. |
| `shell/Cargo.lock` | `blake2 0.10.6`, `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6`; no Argon2 because it uses baseline `capos-config` |
| `capos-wasm/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `linked_list_allocator 0.10.6`, `wasmi 1.0.9` (vendored static-pinned at `vendor/wasmi-no_std/wasmi-1.0.9/`); no Argon2. |
| `fuzz/Cargo.lock` | `capnp 0.25.4`, `capnpc 0.25.3`, `libfuzzer-sys 0.4.12` |
| `tools/remote-session-client/src-tauri/Cargo.lock` (not yet under policy gates) | Tauri scaffold lockfile carrying ~435 transitive packages pinned through `tauri = "=2.11.1"`; only reachable through `make remote-session-tauri` `policy` / `check` / `dev` modes. Not covered by `make dependency-policy-check` today; promotion is gated on the Tauri authority decision. |
| `vendor/wasmi-no_std/wasmi-1.0.9/Cargo.lock` (vendored snapshot lockfile) | Upstream wasmi workspace lockfile preserved with the static-pinned snapshot; `capos-wasm` consumes wasmi only through its own `=1.0.9` path dependency, which lands in `capos-wasm/Cargo.lock` and is covered there. The vendored lockfile is not separately gated; see `vendor/wasmi-no_std/VENDORED_FROM.md` for the refresh procedure and policy re-check. |

Cargo lockfiles pin exact crate versions and crates.io checksums, so ordinary
crate upgrades are review-visible through lockfile diffs. They do not, by
themselves, define whether a dependency is acceptable for kernel/no_std use,
whether multiple lockfiles must converge, or whether advisories/licenses block
the build.

Security Verification Track S.10.3 policy gate:

- `deny.toml` defines the shared license, advisory, ban, and source baseline.
- The allowed license set is intentionally limited to permissive licenses used
  by current locked dependencies. `BSD-3-Clause` is accepted for the Argon2
  credential-validation dependency closure (`subtle` through `password-hash`,
  `digest`, and `blake2`); it is OSI-approved, FSF-free, and carries only the
  standard non-endorsement clause beyond the already-allowed `BSD-2-Clause`.
  `0BSD` is accepted for the smoltcp networking dependency closure (`smoltcp`
  and `managed`); it is OSI-approved and carries no attribution or
  non-endorsement condition beyond the existing permissive-license baseline.
- `make dependency-policy-check` runs `cargo deny check` on the root workspace,
  `init`, `demos`, `tools/mkmanifest`, `tools/ringtap-viewer`, `capos-rt`,
  `shell`, and `fuzz`.
- The same target runs `cargo audit --deny warnings` on every checked-in
  lockfile, with one explicit audit ignore: `RUSTSEC-2026-0173`
  (`proc-macro-error2` unmaintained warning). The ignored path is pulled into
  lockfiles through `smoltcp`'s optional `defmt` logging feature; capOS does not
  enable `defmt` for `smoltcp`, but `cargo audit` scans lockfiles rather than the
  target feature set. Remove the ignore when upstream `smoltcp` / `defmt` no
  longer resolves that crate.
- The same target copies `package.json` and `package-lock.json` into a private
  temporary directory and runs a dry-run install there:

  ```bash
  PUPPETEER_SKIP_DOWNLOAD=1 npm ci --ignore-scripts --dry-run
  ```

  That preserves the `npm ci` package/lock synchronization check without
  modifying the worktree install. It also runs
  `npm audit --package-lock-only --audit-level=high`. Lifecycle scripts stay
  disabled for the docs dependency install path; the browser used by Mermaid
  PDF rendering is an explicit host executable selected by `MERMAID_BROWSER_BIN`.
- `capos-config` keeps Argon2 behind the `credential-validation` feature.
  Bootstrap/config validation remains available in the baseline feature set,
  while validators that need to parse PHC credential strings enable the feature.
  Runtime clients and inspection tools that only need ring/schema/CapSet data
  use the baseline feature set.
- Local packages are marked `publish = false` so cargo-deny treats them as
  private, and local path dependencies include `version = "0.1.0"` so registry
  wildcard requirements can remain denied.
- CI installs pinned `cargo-deny 0.19.4` and `cargo-audit 0.22.1` and runs the
  target.

Remaining dependency-policy gap: decide whether standalone lockfiles may
intentionally drift from the root lockfile, especially for `capnp` and
allocator crates used by userspace.

## Cap'n Proto Compiler, Runtime, and Generated Bindings

The trusted Cap'n Proto inputs are:

- `schema/capos.capnp`, the source schema.
- Repo-local pinned `capnp`, invoked through the `capnpc` Rust build
  dependency via `CAPOS_CAPNP`.
- `capnp` runtime crate with `default-features = false` and `alloc`.
- `capnpc` codegen crate.
- Generated `capos_capnp.rs` written to Cargo `OUT_DIR`.
- Local no_std patching applied after generation by `tools/capnp-build`.

`capos-config/build.rs` delegates schema generation to `tools/capnp-build`.
That shared helper runs `capnpc::CompilerCommand` over
`schema/capos.capnp`, reads the generated `capos_capnp.rs`, asserts that the
expected `#![allow(unused_variables)]` anchor is present, and injects:

```rust
#![allow(unused_imports)]
use ::alloc::boxed::Box;
use ::alloc::string::ToString;
```

The generated code used by builds is included from `OUT_DIR` in
`capos-config/src/lib.rs:10-12`. The expected patched output is checked in as
`tools/generated/capos_capnp.rs`, so schema, compiler, capnpc crate, and
patch-output changes must update that baseline and become review-visible as a
source diff.

Security Verification Track S.10.2 generated-code drift check:

- `make generated-code-check` first builds the checked-in init ELF required by
  kernel build-script validation, exports its absolute path as `CAPOS_INIT_ELF`,
  and runs `tools/check-generated-capnp.sh`,
  `tools/check-generated-adventure-content.sh`, and
  `tools/check-generated-paperclips-content.sh`.
- The script invokes the actual Cargo build-script path for `capos-config` in
  an isolated target directory, so it checks the generated artifact that crate
  would include from `OUT_DIR`.
- During that build, `tools/capnp-build` also copies the patched binding to a
  deterministic package-scoped path under the isolated target directory. The
  checker consumes those explicit paths rather than searching Cargo's hashed
  build-script output directories.
- The script verifies that the patched file still contains the capnpc anchor
  plus the local no_std patch imports, compares the output against
  `tools/generated/capos_capnp.rs`, and fails if a kernel-generated output path
  appears in the isolated target directory.
- Any intentional schema/codegen/patch change must update the checked-in
  baseline in the same review, making generated output drift review-visible.
- `make check` runs `fmt-check` plus `generated-code-check` for a single local
  or CI entry point.
- Current pinned compiler source is `capnproto-c++-1.2.0.tar.gz` from
  `https://capnproto.org/` with SHA-256
  `ed00e44ecbbda5186bc78a41ba64a8dc4a861b5f8d4e822959b0144ae6fd42ef`.
  The checked-in `tools/generated/capos_capnp.rs` baseline must be regenerated
  with that compiler when schema or codegen behavior intentionally changes.
  The current pinned baseline SHA-256 is
  `5ab84731324fe9cc984d7aba7dd97963a773800cc52c4c1693fcb6bb448329a6`.

Adventure content generation uses:

- `demos/adventure-content/content/prototype.cue` as the checked-in source.
- `tools/adventure-content-gen`, a standalone Cargo host tool with
  `tools/adventure-content-gen/Cargo.lock`.
- `demos/adventure-content/src/generated.rs` as the checked-in generated
  no_std Rust baseline consumed by `demos/adventure-content/src/lib.rs`.
- `tools/check-generated-adventure-content.sh`, which derives the same
  `$(CAPOS_TOOLS_ROOT)/cue/0.16.0/bin/cue` path as the Makefile,
  rejects a mismatched `CAPOS_CUE`, checks `cue version v0.16.0`, exports
  explicit JSON, runs the generator with `cargo run --locked`, formats the
  output with `rustfmt --edition 2024`, and fails if the result differs from
  `demos/adventure-content/src/generated.rs`.

Any intentional content-source or generator change must update the checked-in
generated Rust baseline in the same review. The generator manifest and
lockfile are included in `make dependency-policy-check`.

The no_std patch source is single-owned by `tools/capnp-build`;
`capos-config/build.rs` emits its crate-specific rerun directives and calls the
helper.

## Alloy Analyzer (DMA Assurance Model)

The DMA assurance Alloy model (`models/dma/dma_authority.als`) is checked by a
pinned Alloy Analyzer, the same trust level as the Limine, capnp, CUE, Typst,
and uv pins.

- **Pinned artifact:** `alloy-6.2.0-linux-amd64.tar.gz` from the official
  `AlloyTools/org.alloytools.alloy` GitHub release `v6.2.0`
  (`https://github.com/AlloyTools/org.alloytools.alloy/releases/download/v6.2.0/alloy-6.2.0-linux-amd64.tar.gz`),
  SHA-256
  `5a5494a4bac6e243e471590bb44a91e25a35794a5af1ae1f332be30b9c54a9e7`. This is
  the self-contained linux/amd64 app image: it bundles a Temurin JRE under
  `lib/runtime/` and the native SAT solver libraries, so the gate needs no
  host JVM and pins the analyzer *and* its runtime by one hash. The
  `org.alloytools.alloy.dist.jar` (bare jar, host-JVM dependent) is
  deliberately not used.
- **Where the pin lives:** `Makefile` `ALLOY_VERSION` / `ALLOY_PLATFORM` /
  `ALLOY_TARBALL_URL` / `ALLOY_TARBALL_SHA256`. `make alloy-ensure` downloads
  the tarball (curl with retry), verifies the SHA-256, extracts the app image
  into `$(CAPOS_TOOLS_ROOT)/alloy/6.2.0/` (shared per-user cache, default
  `$HOME/.capos-tools`), and confirms the launcher reports version `6.2.0`.
  The jar is not vendored into the repository.
- **Drift review:** `make model-dma-alloy` re-verifies the tarball SHA-256 and
  the reported launcher version on every run before invoking the model. A bump
  is a `Makefile` `ALLOY_VERSION` + `ALLOY_TARBALL_SHA256` diff plus a refreshed
  checked-result record in `models/dma/README.md`.
- **Why output is parsed, not exit-code-gated:** the Alloy CLI `exec`
  subcommand always exits 0; a `check` that finds a counterexample, a `run`
  that finds no instance, and a syntax/resolution error all return success with
  the failure visible only in the printed verdict table.
  `tools/run-dma-alloy-model.sh` parses that table and fails closed on any
  `check` that is not `UNSAT`, any `run` that is not `SAT`, or any analyzer
  error marker.
- **Platform/CI scope:** the pinned app image is linux/amd64 (the dev/CI host
  architecture). GitHub CI runs `make model-dma-alloy` in the
  `dma-assurance-models` job on `ubuntu-24.04`. Other architectures would need
  the matching Alloy app image (or the bare jar plus a host JVM). Ownership of
  the Alloy pin is shared with the scheduler lease model track
  (`scheduler-cpu-isolation-lease-authority-model`).

## TLC Model Checker (DMA Assurance Lifecycle Model)

The DMA assurance TLA+ lifecycle model (`models/dma/dma_authority.tla`) is checked
by a pinned TLC, the same trust level as the Limine, capnp, CUE, Typst, uv, and
Alloy pins. Unlike the self-contained Alloy app image, `tla2tools.jar` is a bare
Java jar, so a JVM is pinned alongside it.

- **Pinned artifacts:** `tla2tools.jar` from the official `tlaplus/tlaplus`
  GitHub release `v1.7.4` (TLC 2.19),
  `https://github.com/tlaplus/tlaplus/releases/download/v1.7.4/tla2tools.jar`,
  SHA-256 `936a262061c914694dfd669a543be24573c45d5aa0ff20a8b96b23d01e050e88`; and a
  Temurin JRE `17.0.19+10` linux/x64 tarball
  (`OpenJDK17U-jre_x64_linux_hotspot_17.0.19_10.tar.gz` from the
  `adoptium/temurin17-binaries` release), SHA-256
  `adb5a2364baa51de1ef91bb9911f5a61d24b045fe1d6647cb8050272a3a8ee75`. Pinning the
  JRE as well as the jar fixes both the checker *and* its runtime by hash.
- **Where the pin lives:** `Makefile` `TLA_TOOLS_VERSION` / `TLA_TOOLS_JAR_URL` /
  `TLA_TOOLS_JAR_SHA256` / `TLA_JRE_URL` / `TLA_JRE_SHA256`. `make tla-ensure`
  downloads both (curl with retry), verifies their SHA-256, extracts the JRE into
  `$(CAPOS_TOOLS_ROOT)/tla/jre/` and places the jar at
  `$(CAPOS_TOOLS_ROOT)/tla/1.7.4/tla2tools.jar` (shared per-user cache, default
  `$HOME/.capos-tools`), and confirms the launcher reports `17.0.19`. Neither is
  vendored into the repository.
- **Drift review:** `make model-dma-tla` re-verifies the jar SHA-256 and the JRE
  launcher version on every run before invoking the model. A bump is a `Makefile`
  pin diff plus a refreshed checked-result record in `models/dma/README.md`.
- **Why output is parsed *and* exit-code-gated:** TLC returns a non-zero exit
  code (12) on an invariant violation, a deadlock, or a parse/semantic error, but
  `tools/run-dma-tla-model.sh` additionally asserts the `Model checking completed.
  No error has been found.` marker and rejects any violation/error marker, so a
  future TLC behaviour change cannot turn a violation into a green gate. The model
  is checked with deadlock detection enabled; the spec provides an explicit
  terminating self-loop for the all-pages-parked state, so any other stuck state
  is a genuine modelling gap.
- **Platform/CI scope:** the pinned JRE tarball is linux/x64 (the dev/CI host
  architecture). GitHub CI runs `make model-dma-tla` in the
  `dma-assurance-models` job on `ubuntu-24.04`; other architectures would need
  the matching Temurin JRE. Ownership of the TLC pin is shared by the
  scheduler/IRQ TLA+ model tracks
  (`scheduler-nohz-activation-model`, `irq-msix-waiter-determinism-model`).

## Cargo Build Scripts

Build scripts currently do these trusted operations:

| Script | Behavior |
| --- | --- |
| `kernel/build.rs` | Watches `kernel/linker-x86_64.ld` and itself. |
| `capos-config/build.rs` | Calls `tools/capnp-build` to watch `schema/capos.capnp`, generate bindings, and apply the shared no_std patch. Checked by `make generated-code-check`. |
| `tools/capnp-build/src/lib.rs` | Host build-support helper for pinned capnp path validation, schema generation, and no_std generated-binding patching. Unit tests cover patch injection and missing-anchor rejection. |
| `tools/adventure-content-gen/src/main.rs` | Host generator for the prototype adventure CUE source. Checked by `make generated-code-check` through `tools/check-generated-adventure-content.sh`, which uses pinned CUE and locked Cargo dependencies. |
| `init/build.rs` | Emits a linker script argument for `init/linker.ld`. |
| `demos/*/build.rs` | Emits a linker script argument for `demos/linker.ld`. |
| `capos-rt/build.rs` | Emits a linker script argument for `capos-rt/linker.ld` when building current `target_os = "none"` userspace or custom-target `target_os = "capos"` probes. |
| `capos-wasm/build.rs` | Emits a linker script argument for `capos-wasm/linker.ld` (Phase W.2 onward; uses `cargo:rustc-link-arg-bins` so the script applies only to the wasm-host bin and not the lib). |

The linker build scripts derive `CARGO_MANIFEST_DIR` from Cargo and only emit
link arguments plus rerun directives. The capnp build scripts read and rewrite
generated code under `OUT_DIR`. None of these scripts fetch network resources.

Security Verification Track S.10.2 coverage: `make generated-code-check`
exercises the canonical `capos-config` capnp build script through Cargo,
validates the patched generated file, fails if kernel-generated output
reappears, and fails if the canonical output no longer matches the checked-in
generated baseline.

## Manifest, Embedded Binaries, and Downloaded Artifacts

`system.cue` declares named binaries and services. `Makefile` builds
`manifest.bin` by running `tools/mkmanifest` on the host. `mkmanifest` runs:

1. Resolve the pinned CUE compiler from `$(CAPOS_TOOLS_ROOT)`, reject missing
   or mismatched `CAPOS_CUE`, check `cue version v0.16.0`, then run
   `cue export system.cue --out json` or package-mode equivalent.
2. JSON-to-`CueValue` conversion and manifest validation
   (`tools/mkmanifest/src/lib.rs`).
3. Binary embedding from relative paths (`tools/mkmanifest/src/lib.rs`).
4. Binary-reference validation and Cap'n Proto serialization
   (`tools/mkmanifest/src/main.rs`).

The adjacent `mkmanifest cue-to-capnp` subcommand uses the same pinned CUE
export path but does not parse the result as `SystemManifest`. Instead, it
resolves and validates `CAPOS_CAPNP`, checks `Cap'n Proto version 1.2.0`, and
passes the exported JSON to Cap'n Proto:

```bash
capnp convert json:binary <schema.capnp> <RootType>
```

It is the supported schema-aware path for CUE-authored data messages rooted at
arbitrary specified Cap'n Proto structs; live capabilities and interface
objects are outside that data-file contract.

Path handling rejects absolute paths, parent traversal, non-normal components,
and canonicalized paths that escape the manifest directory
(`tools/mkmanifest/src/lib.rs`). The generated `manifest.bin` is copied
into the ISO as `/boot/manifest.bin` and loaded by Limine via
`limine.conf:5`.

Downloaded or generated artifacts in the current build:

| Artifact | Producer | Pinning/drift status |
| --- | --- | --- |
| `$(LIMINE_DIR)` checkout (`$(CAPOS_TOOLS_ROOT)/limine/<commit>`) | `git clone`/`git fetch` in the `limine-ensure` recipe | Commit-pinned and artifact-verified. |
| Cargo registry crates | `cargo build`, `cargo run`, tests, fuzz | Lockfile-pinned checksums plus CI-enforced deny/audit checks through `make dependency-policy-check`. |
| Node registry packages | `npm ci --ignore-scripts` for docs Mermaid rendering | `package-lock.json` pins package tarball integrity. Lifecycle scripts are disabled, Puppeteer's browser download path is skipped, and `make dependency-policy-check` enforces the `npm ci` package/lock synchronization invariant plus high-severity npm audit state. |
| Chromium/Chrome for Mermaid PDF rendering | Host executable selected by `MERMAID_BROWSER_BIN` or auto-detected from `chromium-browser`, `chromium`, `google-chrome-stable`, or `google-chrome` | Host-provided browser, not repo-pinned. The docs PDF target fails closed if no executable is available and passes the selected path to Puppeteer as `PUPPETEER_EXECUTABLE_PATH`, rather than allowing Puppeteer's npm install script to download an implicit browser artifact. |
| Rust toolchain, targets, and `rust-src` | `rustup` from `rust-toolchain.toml` when absent | Date-pinned `nightly-2026-04-20` channel; `rust-src` is declared for custom-target `-Zbuild-std` userspace builds. The advance procedure for bumping the pin lives in the Rust Toolchain section above. |
| `target/` kernel and host artifacts | Cargo | Generated, not checked in. |
| `init/target/`, `demos/target/`, `capos-rt/target/`, `capos-wasm/target/` ELFs | Cargo standalone builds | Generated, embedded into `manifest.bin` where referenced; `make build-provenance` records hashes for embedded file-backed and inline payloads. |
| `target/x86_64-unknown-capos/`, `init/target/x86_64-unknown-capos/`, `demos/target/x86_64-unknown-capos/`, `shell/target/x86_64-unknown-capos/`, `capos-rt/target/x86_64-unknown-capos/`, `libcapos/target/x86_64-unknown-capos/`, `libcapos-posix/target/x86_64-unknown-capos/`, and `capos-wasm/target/x86_64-unknown-capos/` userspace artifacts | Cargo aliases using `targets/x86_64-unknown-capos.json` | Generated artifacts for booted userspace manifests, the `capos-rt` smoke binary, the `wasm-host` Phase W.2 binary, and the libcapos / libcapos-posix C-substrate staticlibs. |
| `manifest.bin` | `tools/mkmanifest` | Generated from `system.cue` plus ELF payloads; not checked in. Hash is recorded by `make build-provenance`. |
| `iso_root/` and `capos.iso` | Makefile, xorriso, Limine installer | Generated and gitignored; Limine inputs verified. Final ISO hash is recorded by `make build-provenance`. |
| `target/build-provenance.txt` | `tools/build-provenance.sh` via `make build-provenance` | Generated and gitignored; records runner OS/kernel/architecture identity, GitHub Actions image identity when present, Rust toolchain details, selected executable paths, package identities when discoverable, OVMF selected path/package/absence state, tool versions, git commit, manifest/ISO/kernel/OVMF hashes, and embedded payload origin plus hashes. CI publishes the artifact as `build-provenance-<sha>` on every `qemu-smoke` run (30-day retention). On `pull_request` events the `qemu-smoke` job locates the most recent successful main-branch `build-provenance-<sha>` artifact, downloads it via `actions/download-artifact`, and runs `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment` against the candidate record as a blocking PR gate. |

Remaining gaps for Security Verification Track S.10.2/S.10.3:

- CI now publishes `target/build-provenance.txt` as a named artifact on every
  `qemu-smoke` run (30-day retention) and, on `pull_request` events,
  downloads the most recent successful main-branch `build-provenance-<sha>`
  artifact and runs `make build-provenance-compare` against the candidate
  record with `BUILD_PROVENANCE_COMPARE_POLICY=ci-environment`. The compare
  step is PR-blocking for runner/tool/Rust/OVMF environment drift and fails
  when the base artifact cannot be found.
- `qemu-smoke` apt-pins `qemu-system-x86`, `xorriso`, `make`, `git`, and
  `ovmf`, and `make build-provenance` records normalized package identity
  where the runner exposes it. Repo-pinned digests (download-and-verify rather
  than apt-installed packages) for `qemu-system-x86`, `xorriso`, `make`, and
  `git`, or an immutable runner image digest containing that package set, remain
  future production-reproducibility hardening tracked in
  `docs/design-risks-register.md` (R13).
- Decide whether CI should record the pinned `cue export` JSON or final
  `manifest.bin` bytes if manifest reproducibility becomes release-critical.

## Vendored Upstream Snapshots

The repository carries static, pinned snapshots of selected upstream sources
under `vendor/`. Each snapshot has its own `VENDORED_FROM.md` recording the
upstream URL, tag/version, commit SHA, commit date, vendoring date, license,
vendoring posture, and refresh procedure. Snapshots are kept byte-identical to
their pinned upstream artifact (git commit, or the crates.io published crate as
noted below); the only non-upstream changes permitted without a `patches/`
unified diff are the documented integration-only empty-`[workspace]` marker and
build-inert files restored from the same upstream commit when the publish
`include` omitted them (for `rustls-webpki`, `src/test_utils.rs` and
`rustfmt.toml`, both recorded in its `VENDORED_FROM.md`). Any future functional
patch must be recorded as a unified diff under the snapshot's `patches/`
directory plus a `Patches` entry per the procedure in the snapshot's
`VENDORED_FROM.md`.

| Snapshot | Upstream | Tag/Version | Commit SHA | License | Consumer |
| --- | --- | --- | --- | --- | --- |
| `vendor/wasmi-no_std/wasmi-1.0.9/` | https://github.com/wasmi-labs/wasmi | `v1.0.9` | `61ba65e6563d8b2f5b699b018349d3330b28b9f3` | Apache-2.0 OR MIT (dual) | `capos-wasm/` (WASI host adapter wasm-host bin and Preview 1 import surface) |
| `vendor/dns-c-wahern/src/` | https://github.com/wahern/dns | `rel-20160808` | `4ec718a77633c5a02fb77883387d1e7604750251` | MIT | POSIX adapter Phase P1.2 Phase B DNS smoke; not yet on the v0 build path (the smoke compiles only `demos/posix-dns-resolver/main.c` with a commented-out `dns.h` include) |
| `vendor/rustls-webpki/rustls-webpki-0.103.13/` | https://github.com/rustls/webpki | `0.103.13` (crates.io crate) | `2879b2ce7a476181ac3050f73fe0835f04728e86` | ISC | `capos-tls/` Phase-1 verifier (WebPKI X.509 path building + signature verification, `no_std + alloc`, no crypto provider in the default build) |
| `vendor/webpki-roots/webpki-roots-1.0.7/` | https://github.com/rustls/webpki-roots | `1.0.7` (crates.io crate) | `be948464fd5907af6227213a066743a161221688` | CDLA-Permissive-2.0 | `capos-tls/` Phase-1 trust-anchor bootstrap (compiled-in Mozilla NSS root bundle, `no_std`) |
| `vendor/embedded-tls/embedded-tls-0.19.0/` | https://github.com/drogue-iot/embedded-tls | `0.19.0` (crates.io crate) | `865e1fd983c583228e3bbeb9f4996f1abc454ca3` | Apache-2.0 | `demos/cloud-tls-client-handshake-smoke/` TLS 1.3 client state machine (`no_std + alloc`, default `std`/`tokio` features disabled, `rustpki` enabled) |

The `rustls-webpki` and `webpki-roots` snapshots use a published-crate posture
distinct from the git-clone snapshots above: each is the crates.io published
`.crate` artifact, SHA-256-verified against the crates.io index
(`61c429a8…f756e` and `52f5ee44…2eb9d` respectively), with the upstream commit
recorded from the artifact's embedded `.cargo_vcs_info.json`. For
`rustls-webpki`, two build-inert files the publish `include` omitted
(`src/test_utils.rs`, a `#[cfg(test)]` module, and `rustfmt.toml`) are restored
from the same upstream commit so `cargo fmt` resolves the module tree and
formats the snapshot under upstream's own style config; see its
`VENDORED_FROM.md`. `capos-tls/`
(a root-workspace member and the Phase-1 host verifier crate) depends on both as
exact-pinned path dependencies (`version = "=0.103.13"` / `version = "=1.0.7"`)
and forces them to link for `x86_64-unknown-none` under `cargo build` and
`cargo build --features qemu`. `rustls-webpki` is selected with
`default-features = false, features = ["alloc"]`, so neither `std` nor a
`ring` / `aws-lc-rs` crypto provider is compiled; the active compiled closure is
`rustls-pki-types` (`alloc`, pulling `zeroize`) and `untrusted` (ISC), all
pinned in the root `Cargo.lock` and covered by `make dependency-policy-check`.
The `ring` optional dependency appears in `Cargo.lock` as an unselected optional
entry; `aws-lc-rs` is a feature-gated optional / dev-only dependency and does
not resolve into the root lockfile at all. Neither is ever feature-activated, so
no crypto provider is compiled and `cargo deny` does not evaluate `ring`
(`cargo tree -p capos-tls -e features` activates only `rustls-pki-types`,
`zeroize`, `untrusted`, `webpki-roots`, and `rustls-webpki[alloc]`). The
`webpki-ring` feature is host-test-only and supplies the signature algorithms
for `cargo test-tls`; the default bare-metal build remains provider-free.

The `embedded-tls` snapshot uses the same published-crate posture: the vendored
tree is the crates.io `0.19.0` package with `.cargo_vcs_info.json` recording
upstream commit `865e1fd983c583228e3bbeb9f4996f1abc454ca3`. The local
handshake smoke depends on it with an exact path pin, disables default `std` and
`tokio`, and enables only `rustpki` so the TLS 1.3 client path can run under
`target_os = "capos"` over a `TcpSocket` cap. The empty `[workspace]` marker is
the only local integration change.

`capos-wasm/Cargo.toml` pins the wasmi path dependency to `version =
"=1.0.9"` so cargo-deny's wildcards gate continues to pass; the snapshot is
exercised by `make capos-wasm-build`, every `make run-wasi-*` smoke, and
`make dependency-policy-check` (cargo-deny + cargo-audit on
`capos-wasm/Cargo.lock`). Refreshing wasmi to a newer tag requires the rsync
pattern, manifest pin bump, lockfile regeneration, and policy re-check
recorded in `vendor/wasmi-no_std/VENDORED_FROM.md`.

The dns.c snapshot is intentionally a strict subset (only `src/dns.c`,
`src/dns.h`, `LICENSE`, and `README.md`); ancillary upstream files
(`cache`, `mem`, `spf`, `zone`, `regress`) are excluded because the v0
build path does not need them. Future POSIX-adapter phases that widen
`libcapos-posix` enough to compile `dns.c` whole will start consuming the
snapshot in the build instead of carrying it as a documentation-only
reference.

`vendor/dash/` is not present at this revision. If a future POSIX-adapter
phase imports `dash`, add a new row above plus a `vendor/dash/VENDORED_FROM.md`
recording the same provenance fields.

## Host Tools

Current local host versions observed during this inventory:

| Tool | Observed version | Build role |
| --- | --- | --- |
| `capnp` | `1.2.0` | Repo-selected schema compiler built by `make capnp-ensure` from a SHA-256-pinned official source tarball into `$(CAPOS_TOOLS_ROOT)`. |
| `cue` | `v0.16.0` | Repo-selected manifest compiler installed by `make cue-ensure` into `$(CAPOS_TOOLS_ROOT)` from the SHA-256-verified official release binary. |
| `qemu-system-x86_64` | `10.2.2` | Boot verification via `make run` and `make run-uefi`. |
| `xorriso` | `1.5.8` | ISO generation. |
| `make` | `4.4.1` | Build orchestration. |
| `git` | `2.53.0` | Limine checkout/fetch and review workflow. |

These are local environment observations, not repository pins. On the
`qemu-smoke` CI runner, `qemu-system-x86`, `xorriso`, `make`, and `git` are
apt-pinned to
`qemu-system-x86=1:8.2.2+ds-0ubuntu1.16` (amd64,
`noble-updates`/main or `noble-security`/main, Ubuntu 24.04),
`xorriso=1:1.5.6-1.1ubuntu3` (amd64, `noble`/main, Ubuntu 24.04),
`make=4.3-4.1build2` (amd64, `noble`/main, Ubuntu 24.04), and
`git=1:2.43.0-1ubuntu7.3` (amd64, `noble-updates`/main or
`noble-security`/main, Ubuntu 24.04) by the "Install boot smoke dependencies"
step; the per-run identity for each is captured via `dpkg-query` and normalized
apt source pockets by `tools/build-provenance.sh`. The bump procedure mirrors
OVMF: run `apt-cache madison <tool>` on a current Ubuntu 24.04 host, pick the
highest stable version from `noble-updates/main` (or `noble/main` when no
`noble-updates` entry exists, as is the case for `xorriso` and `make` today),
and update the pinned version string in `.github/workflows/ci.yml` plus this
row.
`make run-uefi` selects an OVMF firmware blob from `OVMF_CODE_CANDIDATES` in
`Makefile:96-97`; the Makefile pins the expected blob via `OVMF_CODE_SHA256`
and the `ovmf-verify` target enforces the match before ISO and cloud-disk
construction. On the `qemu-smoke` CI runner the `ovmf=2024.02-2ubuntu0.8`
apt-install resolves the first candidate (`/usr/share/ovmf/OVMF.fd`),
`ovmf-verify` succeeds against the pinned digest, and `make build-provenance`
records the resulting firmware-blob SHA-256 per run. Build hosts without any
OVMF candidate installed see an `ovmf-verify` NOTICE skip rather than a
failure, so research workflows that never invoke `make run-uefi` continue to
build the ISO unchanged.

Remaining gap for Security Verification Track S.10.3: decide whether full
production reproducibility uses an immutable runner image digest, repo-managed
download-and-verify tool digests for the apt-pinned build/boot tools, or both.
`build-essential`, `curl`, `sha256sum`, the shell, and the checkout-time git
used by `actions/checkout` remain runner-provided; the PR-blocking provenance
gate records and compares the post-checkout build environment, but it does not
turn the mutable `ubuntu-24.04` runner label into an immutable production image.

## GitHub Actions Runner and Workflow Pinning

The CI harness in `.github/workflows/ci.yml` is itself a supply-chain input:
its identities determine which third-party code runs against every push and
pull request, and the chosen runner image determines the host package set
underneath every host-baseline, Kani, and optional QEMU job. Mutable `@v<N>`
or `@master` references on third-party Actions would allow upstream owners to
swap out the executed code at any time without a repository diff, and
`ubuntu-latest` would silently roll the runner OS when GitHub re-points it.

The current policy is to pin every third-party Action to a 40-character
commit SHA and to pin the runner OS to a specific release rather than the
floating label. Each pinned `uses:` line carries a trailing `# v<X.Y.Z>`
comment so reviewers and bump PRs can read the intended release without
following the SHA through the GitHub UI.

| Identity | Pinned reference | Notes |
| --- | --- | --- |
| `runs-on:` runner image | `ubuntu-24.04` | Replaces `ubuntu-latest`; applied to `host-baseline`, `kani-proofs`, `dma-assurance-models`, and `qemu-smoke`. GitHub-hosted `ImageOS` and `ImageVersion` are recorded in `target/build-provenance.txt` when present and are compared by the PR-blocking CI environment policy. Bump only when the next LTS is needed and the full `make check` plus QEMU smokes are reverified against the new image. |
| `actions/checkout` | `34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1` | Resolved from the `actions/checkout` `v4` major-version tag. |
| `swatinem/rust-cache` | `c19371144df3bb44fab255c43d04cbc2ab54d1c4  # v2.9.1` | Canonical `Swatinem/rust-cache` `v2.9.1` release commit. The `v2` major-tracking tag carries the same `2.9.1` message but points at a distinct republication commit; always dereference the exact release tag rather than the major tag. |
| `dtolnay/rust-toolchain` | `3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9  # master @ 2026-03-27` | The upstream action does not publish numbered releases; its documented usage is `@master`. The pin is a snapshot of `master` at the dated commit. |
| `actions/upload-artifact` | `ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.2` | Resolved from the `actions/upload-artifact` `v4.6.2` lightweight tag (same SHA as the moving `v4` major tag at resolution time). Used in `qemu-smoke` to publish `target/build-provenance.txt`. |
| `actions/download-artifact` | `d3f86a106a0bac45b974a628896c90dbdf5c8093  # v4.3.0` | Resolved from the `actions/download-artifact` `v4.3.0` release tag. Paired with `actions/upload-artifact@v4.6.2` (both v4 series) and used in `qemu-smoke` on `pull_request` events to fetch the most recent successful main-branch `build-provenance-<sha>` artifact for the blocking `make build-provenance-compare BUILD_PROVENANCE_COMPARE_POLICY=ci-environment` step. |

Bump procedure for any of the entries above:

1. Resolve the candidate release to its commit SHA via the upstream
   release tag, e.g. `gh api repos/<owner>/<repo>/git/ref/tags/v<X.Y.Z>`
   (dereference any annotated tag through
   `gh api repos/<owner>/<repo>/git/tags/<sha>`), or
   `gh api repos/<owner>/<repo>/commits/<branch>` for branch-tracked
   actions like `dtolnay/rust-toolchain`. Always dereference the exact
   release tag (`vX.Y.Z`) rather than the moving major-version tag
   (`vX`): major-version tags can be re-cut at a republication commit
   whose tag message still names the same release (as observed for
   `swatinem/rust-cache@v2`), so following `vX` can pin a different
   commit than the canonical `vX.Y.Z` release.
2. Update both the SHA and the trailing `# v<X.Y.Z>` comment in
   `.github/workflows/ci.yml` so the reviewer sees the intended release.
3. Run `make fmt-check` and `make workflow-check` locally for the bump
   branch. Workflow hygiene plus YAML well-formedness must pass before
   review. The acceptance gate for the bump itself is a green CI run on
   the bump branch -- `make check` plus the existing QEMU smokes -- which
   exercises the new Action versions end-to-end.
4. Treat any `master`-branch SHA pin (currently
   `dtolnay/rust-toolchain`) as a manual-bump dependency: the upstream
   action does not publish release tags, so bumping its SHA is the only
   way to absorb upstream fixes. Schedule those bumps explicitly rather
   than relying on a floating reference.

This pinning closes the mutable-tag supply-chain gap for the CI harness
itself. It does not by itself satisfy the "pinned runner image digest" line
of the Build Provenance Retention And Comparison Policy: `ubuntu-24.04` is
still a label managed by GitHub Actions, not an immutable image digest. The
current documented equivalent for PR gating is to retain the GitHub-hosted
`ImageOS`/`ImageVersion` fields in `target/build-provenance.txt` and compare
them against the latest successful main-branch record. A future
production-hardening slice may move to a self-built runner image referenced by
digest, mirror the build-tool packages, or both.

## Inventory Method

This inventory is based on source inspection, Cargo metadata, lockfile checks,
and local host-tool version queries. Local host-tool versions are observations,
not repository pins; the tables above distinguish enforced pins from observed
environment state.

Useful commands for refreshing the inventory:

- `git status --short --branch`
- `rg -n "S\\.10|trusted|supply|Limine|limine|capnp|capnpc|QEMU|qemu|download|curl|git clone|wget|build\\.rs|rust-toolchain|Cargo\\.lock" ...`
- `rg --files`
- `cargo metadata --locked --format-version 1 --no-deps`
- `rg -n '^name = |^version = |^checksum = ' Cargo.lock init/Cargo.lock demos/Cargo.lock tools/mkmanifest/Cargo.lock tools/ringtap-viewer/Cargo.lock capos-rt/Cargo.lock shell/Cargo.lock libcapos/Cargo.lock libcapos-posix/Cargo.lock capos-wasm/Cargo.lock fuzz/Cargo.lock`
- `command -v rustc cargo capnp cue qemu-system-x86_64 xorriso sha256sum git make`
- `rustc -Vv`, `cargo -V`, `capnp --version`, `cue version`,
  `qemu-system-x86_64 --version`, `xorriso -version`, `make --version`,
  `git --version`
