SLSA Level 3 builds
The from-source build runs on an ephemeral, offline VM with no secrets and no network. Everything downstream substitutes that build’s output rather than rebuilding it. That combination is what earns SLSA Level 3, and it’s a security boundary the component enforces (ADR-0035).
Choosing this tier
Include /ci/components/hardened-artifact-slsa-l3.yml. This requires access to
specific tagged runners, including an offline builder runner. The alternative,
hardened-artifact-slsa-l1.yml, runs the same build, SBOM and scan steps
on any available runner, but does not provide offline isolation or signing. See
Integrate an external project.
The two runner roles
CI uses two runner roles, and which one a job lands on is a security decision:
nix-slsa-l3-amd64/nix-slsa-l3-arm64— ephemeral, offline VMs that realise derivations from upstream source under isolation. No network egress, no Attic client, no secrets, no signing key. The runner-manager powers the VM off after the build and extracts the outputs out-of-band. This is the only place a from-source build runs.nix-attic-shared— the persistent, networked host: shared Nix store, Attic substituter/push, network, and the secrets (Sigstore OIDC, registry creds, signing key). Everything that is not a from-source build runs here.
How the component uses them
In hardened-artifact-slsa-l3.yml, the hardened:build job carries the
slsa-build-tag
(nix-slsa-l3-amd64 by default) and does exactly one thing:
script:
- nix build ".#$[[ inputs.flake-target ]]" --print-out-paths --no-link
It runs offline. Nothing secret happens. The runner-manager extracts the closure and pushes it to the shared Attic afterward.
Every downstream job — SBOM, scan, sign, attest — runs on nix-attic-shared and
resolves the artifact with:
- nix build ".#$[[ inputs.flake-target ]]" --print-out-paths --no-link --max-jobs 0
--max-jobs 0 means “do not build anything; get it from a substituter.” A cache
miss fails the job rather than quietly rebuilding on the shared runner. So
the SBOM’d, scanned, and signed artifact is provably the one the offline VM
produced — no off-VM rebuild can slip in.
Why this is SLSA L3
SLSA Level 3 asks for a hardened, isolated build platform whose provenance is non-forgeable. Here:
- the build is hermetic (every input is a pinned, hash-checked fetcher);
- it runs on an ephemeral, isolated VM with no network and no secrets;
- the VM cannot sign anything, so provenance is generated afterward on a separate, credentialed host over the exact substituted output.
The security invariant
Signing, secrets, and network must not touch the offline VM. The component
never adds a signing step, an Attic push, a registry login, or a secret to
hardened:build. Signing happens post-build on nix-attic-shared, where the
key lives and the transparency-log upload can reach the network. See
Signing & provenance.
Reference
The runner-role model, the offline-build invariant, and the substitute-only downstream discipline are ADR-0035.