Provenance is a signed, verifiable record of how an artifact was built — from which source, by which builder. SLSA grades that integrity. Generate provenance in CI and verify it before deploy.
A signature proves who released an artifact; provenance proves how it was produced — which source commit, which build system, which parameters. That record is what defeats a SolarWinds-style attack: even a validly signed artifact is suspicious if its provenance says it was built from an unknown source or an untrusted runner. Provenance makes the build process itself auditable.
Signature says: "org X released this."
Provenance says: "this was built by GitHub Actions workflow release.yml,
from commit abc123 of repo my-org/app, at 12:04 UTC,
producing digest sha256:def..."
SolarWinds was validly SIGNED — the attacker owned the build system.
Provenance would flag: "built from an unexpected process / source".
It turns 'trust the vendor' into 'verify the build'.SLSA (pronounced "salsa") defines levels of build integrity so you can set and prove a bar. The jump from L1 to L2/L3 is mostly about provenance being signed and generated by a trusted, isolated builder that the build steps themselves cannot forge. Aim for L3 on anything you distribute; managed builders make it attainable without inventing your own.
SLSA build levels (what each guarantees):
L1 build is scripted + provenance exists (may be unsigned)
L2 provenance is SIGNED by the build service
L3 build runs on a HARDENED, isolated builder; provenance is
non-falsifiable by the build steps <- realistic strong target
L4 hermetic + reproducible (highest assurance, hardest)
Higher level = harder for an attacker who tampers with the build
to produce believable provenance. Most orgs target L3 for released
artifacts.You do not write provenance by hand — a trusted builder generates it. GitHub's official attestation action produces signed SLSA provenance for an artifact as part of the workflow, using the same keyless Sigstore identity as signing. The result is a signed statement, stored in a transparency log, that anyone can later verify against the artifact's digest.
# .github/workflows/release.yml
build:
permissions:
id-token: write # OIDC identity for keyless signing
contents: read
attestations: write # allow writing the provenance attestation
steps:
- id: build
run: |
docker build -t "$IMAGE" .
echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")" >> "$GITHUB_OUTPUT"
- name: Generate SLSA provenance attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: registry.example.com/app
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: trueProvenance only protects you if deploy-time verification enforces it. Check that an artifact has provenance and that the provenance asserts the expected source repository and builder before allowing it to run. Wire this into the deploy gate or, better, cluster admission — so an image built from the wrong repo or an unknown builder simply cannot be scheduled.
# verify the image has provenance from OUR repo, built by GitHub's builder
gh attestation verify oci://registry.example.com/app@sha256:def... \
--repo my-org/app
# or with cosign, asserting the provenance predicate + identity:
cosign verify-attestation --type slsaprovenance \
--certificate-identity-regexp 'https://github.com/my-org/.+' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
registry.example.com/app@sha256:def...
# Fail closed: no valid provenance from the expected source => do not deploy.