SLSA Build Level 3 means your artifacts ship with provenance that is generated and signed by the build platform itself, out of reach of the build's own steps — so even a fully malicious Makefile cannot forge where the artifact came from. That one property is the whole point of L3, and it dictates the practical decision most teams face: use a hosted builder that already provides it, or accept a serious platform engineering project to build the equivalent yourself.
SLSA v1.0, published April 2023, trimmed the framework down to a single Build track with four levels. Here is what each buys you, and what the jump to L3 costs in practice.
The levels, without the marketing
| Level | Requirement | What it defeats |
|---|---|---|
| L0 | Nothing | — |
| L1 | Provenance exists (can be self-reported) | Honest mistakes, wrong-version confusion |
| L2 | Provenance signed by a hosted build platform | Tampering after the build |
| L3 | Provenance non-forgeable; builds isolated from each other and from signing material | Tampering during the build, by the build itself |
The L2-to-L3 gap is the interesting one. At L2, a compromised build step — a malicious dependency's postinstall script, say — can potentially read the signing credentials and mint provenance for anything. At L3, the platform generates and signs provenance in a context the user-defined steps cannot touch, and each build runs in a fresh, ephemeral environment so one build cannot poison the cache or filesystem of the next.
The realistic path: a hosted L3 builder
On GitHub Actions, the slsa-github-generator project provides reusable workflows that run provenance generation in a separate, isolated job whose OIDC identity — not yours — signs the attestation. For a generic artifact:
jobs:
build:
runs-on: ubuntu-latest
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- uses: actions/checkout@v4
- run: ./build.sh && sha256sum dist/* | base64 -w0 > hashes.txt
- id: hash
run: echo "hashes=$(cat hashes.txt)" >> "$GITHUB_OUTPUT"
provenance:
needs: [build]
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
with:
base64-subjects: ${{ needs.build.outputs.hashes }}
Two details matter here. The reusable workflow is pinned to a tag of the generator repo, and it must be called as a reusable workflow — not copied into your repo — because the isolation claim depends on the generator's job running code you cannot edit. Container images get the same treatment via generator_container_slsa3.yml, which signs provenance with cosign against the image digest.
GitLab users get comparable coverage through runner-generated attestations (RUNNER_GENERATE_ARTIFACTS_METADATA), and Google Cloud Build issues L3-grade provenance natively. The pattern is identical: the platform, not your job, holds the pen.
Verification is the half everyone skips
Provenance nobody checks is a compliance sticker. The consuming side uses slsa-verifier:
slsa-verifier verify-artifact dist/myapp-linux-amd64 \
--provenance-path myapp.intoto.jsonl \
--source-uri github.com/myorg/myapp \
--source-tag v1.4.2
This checks that the attestation was signed by the trusted builder identity, that the subject digest matches the file on disk, and that the source repository and ref are what you expect. Put it in the deploy pipeline, not just release documentation. For container images, admission-time verification with the Sigstore policy controller or a Kyverno verifyImages rule closes the loop — we covered identity-pinning pitfalls in cosign verification policies in production.
npm took this mainstream: npm publish --provenance from a CI workflow attaches SLSA provenance that the registry verifies and displays. If you publish packages, this is the cheapest L3-adjacent win available.
Where teams get stuck
Self-hosted runners. The moment your build runs on a persistent self-hosted runner, the isolation requirement is in question — a previous build could have planted anything. Ephemeral, single-use runners (fresh VM per job) restore the property; a long-lived runner with a workspace directory does not. This is the single most common disqualifier I see.
Jenkins. Out of the box, Jenkins cannot give you L3: builds share controllers and workspaces, and any job can typically read credentials available to the agent. You can approximate L3 with ephemeral agents, an external signer, and strict credential scoping, but you are now building and defending a bespoke trust boundary. Budget accordingly, or route release builds through a hosted L3 builder and let Jenkins keep the CI feedback loop.
Monorepos. Provenance records the repo and workflow, not which subproject's code actually mattered. If ten teams share one repo, an attestation saying "built from monorepo@sha" is true but coarse. Convention fixes it: per-artifact workflows and subject naming discipline.
The base image gap. L3 provenance covers your build step. Your FROM image, your compiler, and your dependencies each have their own provenance story — or none. SLSA composes; it does not cascade automatically. An SBOM per artifact, tracked in something like SBOM Studio, is how you keep the full input graph queryable, and dependency-level checks belong in SCA regardless of build level.
Is it worth the effort?
For the hosted-builder path: yes, and the cost is low. Our first repo took about four days, most of it spent restructuring the release workflow to output subject hashes cleanly and teaching the deploy side to verify. Repos two through twenty took under an hour each, because it is all templated. The self-built path is a different animal — a quarter-scale platform project — and I would only take it on for regulated environments that cannot use hosted CI.
The honest framing for leadership: L3 does not stop a malicious maintainer from committing bad source. It guarantees the artifact you deploy came from the source and builder you think it did, which converts an entire class of "trust me" into "check the signature."
Frequently asked questions
Do I need Sigstore to reach SLSA Level 3?
No, but you will probably use it anyway. The spec requires non-forgeable, verifiable provenance without mandating a signature scheme; GitHub's generators and artifact attestations use Sigstore under the hood, and slsa-verifier expects it. A private PKI works if you control both ends.
Can I be Level 3 with self-hosted runners?
Only if every runner is ephemeral — provisioned fresh per job and destroyed after — and the provenance signer runs outside the job's reach. Persistent runners with shared workspaces fail the isolation requirement, whatever the rest of the pipeline looks like.
What happened to SLSA Level 4?
SLSA v1.0 dropped it. The v0.1 draft's L4 requirements (two-person review, hermetic and reproducible builds) were split out for future tracks, so the Build track currently tops out at L3. Hermeticity and reproducibility remain excellent controls; they are just no longer a numbered level.
Does SLSA L3 protect against another XZ Utils?
Mostly no — that payload entered through source and release tarballs, upstream of any build platform. L3 would have made the tarball-versus-repo divergence detectable, since verified provenance binds artifacts to a source revision, but a malicious maintainer committing to the actual repo is outside SLSA's blast radius.