Understanding SLSA build levels for Git repositories Jump to heading

SLSA — Supply-chain Levels for Software Artifacts — is often presented as a checklist to be completed, and teams respond by producing a provenance file and declaring victory. The levels are more useful read as a set of questions about where an attacker could interfere between a commit and an artefact, and how much evidence you have that they did not. Several of those questions are answered, or not, by how you run Git: who can push, whether builds run from a known commit, whether the build configuration lives in the repository. This page explains the build track’s levels in terms of Git and CI practices you can inspect today, as background for the rest of build provenance and attestations.

When to use this approach Jump to heading

  • Someone has asked “what SLSA level are we?” and the honest answer is “unclear”.
  • You are planning provenance work and want to know which changes buy the most assurance.
  • You generate provenance already and want to understand what it does and does not prove — see generating provenance for a tagged release.
  • You need to explain to an auditor how source controls and build controls relate.

Step 1 — Read the levels as threats closed Jump to heading

The build track has levels one to three. Each adds requirements on the build platform and on the provenance it produces. A useful way to remember them is by the attacker each level defeats.

The build track, as threats it closesLevel one requires provenance to exist, which defeats mistakes and confusion about what was built. Level two requires a hosted build platform to sign the provenance, which defeats tampering after the build. Level three requires the platform to isolate builds and protect its signing material, which defeats tampering during the build by other tenants or the build's own steps.each level assumes the one above itBuild L1provenance exists — stops 'which commit was this?'Build L2hosted, signed provenance — stops forgery after the buildBuild L3isolated, hardened builds — stops tampering during the buildLevel 0 is simply 'no provenance' — the starting point for most repositories

The source side — whether the commit itself was reviewed and protected — is a separate concern that SLSA treats on its own track. In practice your branch protection, review requirements and commit verification gates are what make the commit named in provenance trustworthy.

Step 2 — Check Level 1: does provenance exist and name the commit? Jump to heading

Level 1 asks that each artefact has provenance describing how it was built: the build platform, the entry point, and the source — including the exact commit. You can test this without any special tooling: pick a release artefact and try to answer, from documents alone, which commit it came from.

# Does each release carry a provenance document?
gh release view v2.4.0 --json assets --jq '.assets[].name' | grep -E 'intoto|provenance'

# Does the provenance name the commit?
jq -r '.predicate.buildDefinition.resolvedDependencies[]? | select(.uri|test("git")) | .digest.gitCommit' \
  provenance.intoto.json

If the answer is a branch name rather than a commit hash, the provenance does not meet the intent of Level 1: a branch is a moving pointer.

Step 3 — Check Level 2: is the provenance signed by the platform? Jump to heading

Level 2 requires a hosted build platform that generates and signs the provenance itself, so a person cannot hand-edit it after the fact. “Hosted” rules out builds on a developer’s laptop. “The platform signs” rules out a build step that writes JSON and signs it with a key the build steps can also read.

# Verify a release attestation was produced by the expected workflow on the forge
gh attestation verify ./app-2.4.0.tar.gz --repo "$OWNER/$REPO" \
  --signer-workflow "$OWNER/$REPO/.github/workflows/release.yml"
Practices that do and do not reach Level 2A release built on a maintainer's laptop and uploaded by hand cannot reach Level 2, whatever it attaches. A hosted pipeline whose provenance is signed by the platform's identity can, provided the provenance names the exact source commit.Falls shortMeets Level 2where it buildsmaintainer laptophosted CI runnerwho writes provenancea script stepthe platformwho signs ita key steps can readplatform identitysource named asbranchcommit hashmost teams reach Level 2 by changing who signs, not by changing the build

Step 4 — Check Level 3: is the build isolated from itself? Jump to heading

Level 3 requires that builds cannot influence one another and that the signing material is inaccessible to user-defined build steps. Two common Git-adjacent practices break isolation: caches shared across branches, which let one branch’s build poison another’s, and self-hosted runners reused between jobs without cleanup.

# Are caches keyed by branch or shared across all refs?
grep -rn 'actions/cache' .github/workflows/ -A6 | grep -E 'key:|restore-keys:'

# Are self-hosted runners ephemeral (one job per runner)?
gh api "repos/$OWNER/$REPO/actions/runners" --jq '.runners[] | {name, labels: [.labels[].name]}'

The cache problem is covered in depth in fixing cache poisoning between branches. Ephemeral runners are covered in autoscaling self-hosted runners.

Step 5 — Write down where you are and what moves you up Jump to heading

A short assessment is more useful than a badge. For each level, list what is in place, what is missing, and the specific change that would close the gap.

A one-page level assessmentFor each build level, the assessment records the evidence you have, the gap that remains, and the next concrete change. The point is to make the next step obvious, not to claim a number.L1have: provenancegap: names branchnext: record SHAL2have: hosted CIgap: step signs itnext: platform attestL3have: hosted runnersgap: shared cachesnext: per-ref keysan honest 'Level 1 with two gaps' is worth more than an unexamined 'Level 3'
# slsa-assessment.md — kept in the repository, reviewed each quarter
Build L1: provenance attached to releases; names commit since 2026-08.        MET
Build L2: provenance signed by workflow identity via forge attestation.       MET
Build L3: hosted ephemeral runners; caches keyed per ref since 2026-09;
          release job has no cache restore.                                   MET (review Q1)
Source:   branch protection + required review + signed commits on main.       see access policy

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Does signing commits get us a SLSA level? Jump to heading

Not on the build track. Commit signing strengthens the source side — it makes the commit named in provenance attributable — but build levels are about how the artefact was produced from that commit.

Is Level 3 realistic for a small team? Jump to heading

Often, yes, on hosted CI. Hosted runners are ephemeral and isolated by default, and forge-native attestations keep signing material away from build steps. The usual remaining work is removing shared caches from release builds.

Do we need provenance for every build, or only releases? Jump to heading

Only for artefacts someone else consumes. Pull-request builds that are thrown away do not need provenance; anything deployed or published does.