SLSA Build Level 3 Is an Isolation Property, Not a Project
This is part five of Ship the Proof. Admission control, the previous part, only buys assurance if the attestations it checks were produced at a build level worth trusting. This part is about reaching that level, and about how much cheaper it is than its reputation suggests, as long as you are honest about what it actually requires.
SLSA Build Level 3 sounds like a project. People hear “hardened builds” and picture a dedicated isolated builder, a key management ceremony, an audit. For a team that already produces hosted, signed provenance, the reality is closer to a structural change you can land in an afternoon, plus two conditions you must not skip. The trap is treating one of those conditions as the whole story.
The levels grade adversary cost, not tooling
SLSA build levels are graded by how hard it is to forge provenance or evade verification, not by which product you use. Build Level 2 means “forging the provenance or evading verification requires an explicit ‘attack’, though this may be easy to perform.” Build Level 3 means it “requires exploiting a vulnerability that is beyond the capabilities of most adversaries” (SLSA v1.2 build-track-basics).
The jump from L2 to L3 is the jump from “an attacker needs to mount an explicit attack, which might be easy” to “an attacker needs a vulnerability beyond most adversaries’ reach.” That is a real increase in assurance, and on a hosted build platform it is unusually cheap to claim honestly.
Where you probably already are
If you build on a hosted platform that generates and signs provenance for you, you are likely at L2 already. On GitHub Actions, the documentation states it directly: “Artifact attestations by itself provides SLSA v1.0 Build Level 2,” signing and verifying through Sigstore (GitHub artifact attestations). You did not have to stand up a transparency log or manage a signing key, because the keyless flow handles both. (That documentation quotes “SLSA v1.0” because it is describing the level against the version it was written for. The Build L2 and L3 definitions are unchanged in the current v1.2 spec.)
So the only question is what stands between L2 and L3.
What L3 actually requires, stated as a property
Here is the correction that matters, because skipping it is how teams claim a level they have not reached. L3 is a requirement on the build platform, and it has two parts: the platform must “prevent runs from influencing one another, even within the same project,” and it must “prevent secret material used to sign the provenance from being accessible to the user-defined build steps” (SLSA v1.2 build-track-basics).
Read both halves. Run isolation is one. Keeping the signing material out of reach of the steps a user controls is the other. An implementation that gives you the first but not the second does not reach L3. This is why a single structural change is necessary but not sufficient on its own: it has to actually deliver both properties, and you have to keep the build steps from reaching the signing path.
One implementation, and the conditions that make it real
On GitHub Actions, the documented path to that isolation is a reusable workflow. The phrase “reusable workflow” is not a SLSA term and appears nowhere in the specification. It is GitHub’s mechanism: “reusable workflows can provide isolation between the build process and the calling workflow, to meet SLSA v1.0 Build Level 3” (GitHub artifact attestations). Put the build-and-sign logic in its own workflow whose steps and permissions the caller cannot alter at runtime (callers with repo access can still read the source and job logs), keeping id-token: write and the attestation permissions inside the called workflow rather than in user-controlled steps, so the calling job cannot reach the signing token or influence the provenance:
# build-and-attest.yml - the caller invokes this but cannot alter its steps or permissions
on: { workflow_call: { inputs: { image-name: { required: true, type: string } } } }
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write # mint the OIDC token for signing, scoped to this isolated job
attestations: write
contents: read
packages: write
steps:
- uses: actions/checkout@<pinned-commit-sha>
- name: Build, push, and attest by digest
# build -> push -> attest the resulting sha256 digest
The structural change is real, but two conditions decide whether it actually buys L3. First, the signing permission lives in the isolated workflow, never in the caller, so the user-controlled steps cannot reach the token that signs provenance. That is the second half of the L3 property, and it is the half people forget. Second, pin every action by commit SHA rather than a mutable tag. A tag can be force-pushed to point at attacker code, and the isolation buys nothing if the action you call gets swapped underneath you. (That hazard is real and recent; a later part in this series is about the supply-chain incidents that made it concrete.)
Declare your level and your format
Here is a habit that costs nothing and pays off constantly. Once you produce L3 provenance and a software bill of materials, say so, in your release notes, in your README, in the metadata a downstream consumer reads. “Built at SLSA Build Level 3, SBOM in CycloneDX 1.7” is one line, and it changes what a verifier downstream has to do.
CycloneDX 1.7 is the current release, published 21 October 2025, superseding 1.6 (cyclonedx.org). Generate the bill of materials against the digest you built and signed, not a mutable tag, and attach it to that digest so it travels with the artifact.
The reason to declare it in human-facing material is that machine consumers match provenance on a literal predicate type, not on a human label. The canonical type is the string https://slsa.dev/provenance/v1, and the spec tells consumers to “always use the above string for predicateType rather than what is in the URL bar” (slsa.dev/provenance/v1). Your machine-readable provenance already carries the precise claim. Declaring the level and format in prose tells a human auditor what you assert without making them reverse-engineer your pipeline.
The end state is modest and concrete. Move the build-and-sign step into isolation, keep the signing material out of the user-controlled steps, pin your actions, attach a current-format bill of materials keyed to the digest, and write one line stating the level and format you produce. That is SLSA Build Level 3, an isolation property you can hold honestly, not a project.
The next part stays on that bill of materials and asks a sharper question: what makes one actually useful, now that no regulation is forcing you to produce it?