Build provenance and SLSA levels for application teams: what to adopt
This guide is for application teams that ship containers, packages, or binaries and need to decide how far to go with build provenance and SLSA. It explains where provenance fits in a CI/CD pipeline, how verification works in practice, and what level of rigor is worth the operational cost.
TL;DR — Build provenance is signed evidence about what source, build steps, and environment produced an artifact. For most application teams in 2026, the practical target is: generate provenance in CI from an isolated runner, sign it with your CI identity, verify it before deploy, and treat SLSA as a maturity ladder rather than a compliance badge. Reading time: ~7 min
What it is and where it sits
Build provenance is the machine-readable receipt attached to an artifact: container image, binary, package, SBOM, or release archive. It answers the questions your deploy system, security tooling, and incident responders actually care about:
- Which repo and commit produced this artifact?
- Which workflow/build definition ran?
- Was it built by your CI or on a developer laptop?
- Were the inputs declared, or could something have been injected at build time?
SLSA is the framework that describes how trustworthy that provenance is. The levels are not magic; they are shorthand for controls around source integrity, build isolation, provenance generation, and policy enforcement. For an application team, the useful interpretation is:
- Provenance = the evidence file and signature.
- SLSA level = how much you can trust that evidence.
In a typical delivery path, provenance sits between build and deploy. It is produced by CI, stored next to the artifact in your registry or artifact store, and checked by a deploy gate, admission controller, or release promotion job.
What it replaces:
- Manual trust in CI logs.
- "We tagged it in Git so it must be fine."
- Ad hoc release notes as the only record of what shipped.
What talks to it:
- CI runners/build systems generate it.
- Signing systems or keyless identity flows sign it.
- Registries store it as an attached artifact or attestation.
- Verifiers in CD, Kubernetes admission, or release scripts validate it.
View as text
[Developer] ---- 1. git push ----> [Source Repo]
|
| 2. webhook
v
[CI Build]
|
| 3. build artifact + provenance
v
[Registry]
|
| 4. verify provenance
v
[Deploy Pipeline]
|
| 5. admit only trusted builds
v
[Runtime]Architecturally, provenance is not in the request path of your app. It is in the software supply chain path: source control -> CI -> registry -> deploy. That matters because the cost is mostly pipeline complexity and policy work, not user-facing latency.
How it actually works
Take one realistic example: a team builds a container image for payments-api, pushes it to an OCI registry, and wants production deploys to accept only images built from the main branch by the team CI workflow.
Step 1: CI builds the artifact in an isolated environment
The build runs in CI, not on a laptop. Isolation is the first meaningful trust boundary. If the image is built locally and then pushed, any provenance you attach later is weak because the build environment was uncontrolled.
Typical build command shape:
docker buildx build \
--platform linux/amd64 \
--provenance=true \
--sbom=true \
-t registry.example.com/payments-api:git-4f3c2d1 \
--push .
What this does:
- Builds and pushes the image.
- Emits provenance and SBOM metadata when the builder supports it.
- Associates the metadata with the pushed image in the registry’s OCI metadata model.
Gotcha: --provenance=true is not enough by itself if your builder is running somewhere you do not trust or if the build pulls mutable dependencies without pinning. Provenance can honestly report a bad process.
Step 2: CI signs the provenance
The provenance statement is signed so a verifier can tell it came from your CI identity and was not modified in transit. In 2026, many teams use keyless signing tied to the CI workload identity because it avoids long-lived private keys in secrets storage.
A verification-oriented command shape looks like this:
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity "https://github.com/your-org/your-repo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registry.example.com/payments-api:git-4f3c2d1
Expected output shape when things line up:
Verified OK
{
"payloadType": "application/vnd.in-toto+json",
"critical": {
"identity": {
"docker-reference": "registry.example.com/payments-api"
},
"type": "cosign container image signature"
},
"optional": null
}
Common failure shape when the artifact was signed by the wrong workflow or branch:
Error: no matching attestations:
certificate identity mismatch:
expected https://github.com/your-org/your-repo/.github/workflows/build.yml@refs/heads/main
got https://github.com/your-org/your-repo/.github/workflows/build.yml@refs/pull/184/merge
That error is exactly the point: you can block deploys from pull-request builds even if the image exists in the registry.
Step 3: Verifier checks the provenance content, not just the signature
A valid signature only proves who signed the statement. You still need to inspect the statement fields:
- source repo URI n- commit digest
- builder identity
- build type
- invocation parameters
- subject digest matching the artifact you are deploying
If your deploy script only checks "has a signature," you have implemented theater, not provenance enforcement.
A practical policy is:
- The image digest being deployed must match the provenance subject digest.
- The builder identity must be your CI workflow on
main. - The source repo must be your production repo, not a fork.
- The provenance must exist for every production artifact.
Step 4: CD or admission control enforces policy
Your deploy pipeline resolves the image tag to a digest, verifies the attestation, then proceeds. In Kubernetes environments, this often happens in admission control. In simpler setups, it is just a shell step before helm upgrade or kubectl apply.
If verification fails, the pipeline exits non-zero and does not promote the release.
Example failure shape in a shell gate:
$ ./verify-provenance.sh registry.example.com/payments-api:prod
resolved digest: sha256:6b9d...a12c
verifying attestation...
ERROR: provenance subject digest sha256:1f2e...9c44 does not match image digest sha256:6b9d...a12c
exit status 1
That usually means one of two things:
- You rebuilt and retagged the image after provenance was generated.
- Your deploy system is using a mutable tag and verifying the wrong digest.
The fix is almost always: deploy by digest, not by tag.
When to use it (and when not to)
You probably want provenance before you need SLSA-level bragging rights. The operational value shows up during deploy gating, incident response, and third-party assurance.
| Scenario | Recommendation |
|---|---|
| You deploy container images to production from CI | Adopt provenance now; verify before deploy; target SLSA controls roughly equivalent to isolated CI builds and signed provenance |
| You publish packages or binaries for customers to consume | Adopt provenance and signatures now; external consumers increasingly expect this |
| You are under regulated or enterprise procurement review | Treat provenance as table stakes; map your controls to SLSA terminology for audits and questionnaires |
| Small internal tool, no external distribution, one maintainer | You may not need full provenance yet; start with reproducible CI builds and immutable artifact digests |
| Team still builds releases from laptops | Fix that first; SLSA discussion is premature until CI is the sole release path |
| Monorepo with many services and frequent hotfixes | Use provenance, but keep policy narrow at first: verify builder identity and source branch only |
You probably don’t need to chase higher SLSA levels if:
- Your main risk is still unreviewed code merges, not build tampering.
- You cannot yet prevent manual production changes.
- Your dependencies are mostly unpinned and fetched dynamically during builds.
- Your release process still relies on mutable tags and one-off scripts.
In other words: provenance is valuable early; high SLSA maturity only pays off when the rest of your pipeline is disciplined enough to benefit.
Trade-offs
Every benefit here has a cost.
-
Benefit: stronger deploy trust
Cost: more CI/CD steps, more failure modes, and more policy debugging when a legitimate build gets blocked. -
Benefit: faster incident response because you can answer "what produced this artifact?" quickly
Cost: artifact storage grows; attestations, SBOMs, and signatures add metadata management overhead. -
Benefit: reduced reliance on long-lived signing keys with keyless/workload identity approaches
Cost: tighter coupling to your CI identity provider and certificate verification rules. -
Benefit: better supplier/customer assurance
Cost: pressure to formalize build pipelines, pin dependencies, and document exceptions. -
Benefit: path toward higher SLSA maturity
Cost: build isolation, hermeticity, and reproducibility work can be expensive and may slow teams initially.
The biggest non-obvious trade-off: provenance can expose process gaps you were previously ignoring. Teams often discover they are rebuilding artifacts during promotion, using mutable base images, or allowing branch builds to publish release-like artifacts. That is useful, but it creates work.
In practice
Example 1: CI build and push with provenance for a container image
name: build
on:
push:
branches: [main]
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: registry.example.com
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push
run: |
docker buildx build \
--platform linux/amd64 \
--provenance=true \
--sbom=true \
-t registry.example.com/payments-api:${GITHUB_SHA} \
--push .
This builds only on main, uses CI identity permissions, and pushes an image with provenance metadata. Gotcha: if your Dockerfile uses FROM node:22 instead of a pinned digest like node@sha256:..., the provenance will record that mutable input, but it will not make the build reproducible.
Example 2: Shell gate before deploy
#!/usr/bin/env bash
set -euo pipefail
IMAGE_REF="$1"
EXPECTED_IDENTITY="https://github.com/your-org/your-repo/.github/workflows/build.yml@refs/heads/main"
EXPECTED_ISSUER="https://token.actions.githubusercontent.com"
DIGEST=$(crane digest "$IMAGE_REF")
echo "resolved digest: $DIGEST"
cosign verify-attestation \
--type slsaprovenance \
--certificate-identity "$EXPECTED_IDENTITY" \
--certificate-oidc-issuer "$EXPECTED_ISSUER" \
"${IMAGE_REF%@*}@${DIGEST}"
echo "provenance verification passed"
This resolves the image to a digest and verifies the attestation against the expected CI identity. Gotcha: do not verify a mutable tag directly in one step and deploy it in another; resolve once to a digest and carry that digest through the rest of the pipeline.
⚠️ If you add this gate to a production pipeline, expect some initial failed deploys. Roll it out in audit mode first: run verification, log failures, but do not block until you know which repos and workflows produce compliant provenance.
Example 3: What a policy failure looks like in CI logs
$ cosign verify-attestation --type slsaprovenance \
--certificate-identity "$EXPECTED_IDENTITY" \
--certificate-oidc-issuer "$EXPECTED_ISSUER" \
registry.example.com/payments-api@sha256:6b9d...a12c
Error: no matching attestations:
1. certificate identity mismatch
2. no attestations found for predicate type: https://slsa.dev/provenance/v1
The first line means the artifact was signed, but by the wrong workflow identity. The second means either provenance was never generated, or your tooling expects a different predicate/version than what your builder emitted.
Further reading
- SLSA Specification
- in-toto Attestation Framework
- Sigstore Cosign documentation
- OCI Image and Distribution specifications
- NIST Secure Software Development Framework (SSDF)
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI