The topic SLSA Provenance Hands-on: Generate with GitHub Actions, Verify with slsa-verifier is currently the subject of lively discussion — readers and analysts are keeping a close eye on developments.
This is taking place in a dynamic environment: companies’ decisions and competitors’ reactions can quickly change the picture.
I wrote Supply Chain Security: A Deep Dive into SBOM and Code Signing earlier. That post pinned down “what’s in it” via SBOM and “who signed it” via Cosign.
SolarWinds’ SUNSPOT was malware that lived on the build server, swapped the source code the moment a build started, and put it back when the build finished. The resulting binaries were signed with the legitimate certificate. Signatures: perfect. SBOMs: clean. And the world still got a backdoor distributed to it.
Why? Signatures only prove “I signed this with this key.” SBOMs only describe “what was in the artifact at build time.” Nobody was verifying “was this really built from the right source, on an unaltered builder, following the steps it claims?”
The thing that closes that hole is Provenance. SLSA (Supply-chain Levels for Software Artifacts) is a framework built around provenance, treating “from where (source), how (build), by what (builder)” as verifiable metadata.
I covered the spec in SLSA Deep Dive. This is the follow-up: actually generating SLSA L3 provenance and verifying it on real machines.
So a SLSA Provenance is “an in-toto Statement whose predicate matches the SLSA spec, signed inside a DSSE envelope.”
Just enough to keep us oriented. For the threat model, see SLSA Deep Dive.
slsa-github-generator, the implementation we use here, is one of the few that actually clears L3. By leaning on GitHub’s OIDC tokens and reusable workflows, it is structured so that user code cannot reach the builder’s internal state.
A GitHub Actions uses: org/repo/.github/workflows/foo.yml@vX runs in a separate process, separate shell, separate filesystem from the caller. So whatever the calling repo’s owner puts in their YAML, they cannot touch the signing keys or OIDC tokens that the builder workflow handles.
That structurally satisfies SLSA L3’s requirement: “separate user-defined build steps (data plane) from the provenance generation logic (control plane).”
The user just calls in via uses:. They never touch the provenance assembly. That structural split is the heart of L3.
Theory only goes so far. Let’s verify a real provenance on your machine first. Subject of choice: the slsa-verifier release itself. Since slsa-verifier is built with slsa-github-generator, every release ships with .intoto.jsonl alongside the binary.
A one-shot script that runs everything in this section lives at hello-slsa/verify-real-artifact/run.sh. If you just want to see the flow, clone the repo and run ./run.sh.
To see what happens when the assertion is wrong, point –source-tag at a different tag.

The hash mismatch fails immediately. SUNSPOT-style attacks (“swap the binary after the build finishes”) die right here.
.intoto.jsonl is a DSSE envelope; the payload is base64-encoded JSON. Pry it open with jq.
The environment block has an even finer fingerprint of where the build ran.
With this much retained, “did a binary born from tag v2.7.1 actually come out of a GitHub-hosted runner running the legitimate workflow?” is fully reconstructable after the fact.
materials is “the list of build inputs”: source code and the builder environment, side by side.
Combine this with the SBOM and the chain “source → builder → artifact → internal components” becomes verifiable end to end.
Verification works. Now flip to the producer side: build a Go hello-world and bolt slsa-github-generator onto it.
The finished version lives at github.com/0-draft/hello-slsa. Type along by hand or git clone, whichever you prefer.
The slsa-github-generator Go builder reads a goreleaser-compatible YAML. Nothing fancy.
-trimpath and CGO_ENABLED=0 are reproducibility incantations. The next post on Reproducible Builds digs into why these matter.
This is the heart of it. uses: calls the slsa-github-generator reusable workflow.
The build job only compiles the user’s code; it never participates in assembling or signing the provenance. Because builder_go_slsa3.yml runs as a separate workflow, no matter how dirty your user code is, it cannot reach the signing key. That’s the L3 separation.
Before pushing the tag, run the build locally to see it work.

When the same build runs in CI, that SHA256 is what ends up in subject.digest.sha256 of the provenance, and .intoto.jsonl is uploaded alongside the binary. The hash will differ across environments and toolchains; the point is that the value you computed locally matches the value in the provenance subject.
[!NOTE]
id-token: write requires OIDC to be enabled on the repo. On github.com it is on by default. On GitHub Enterprise Server you have to configure it separately.
Provenance flows out the producer side. Back on the consumer side, what does slsa-verifier verify-artifact actually confirm?
What it can do: cryptographically prove “the binary at tag v2.7.1 really did come out of the legitimate GitHub Actions builder.” SUNSPOT-style attacks (tampering during the build) and Codecov-style attacks (uploading without going through the build) stop here.
What it cannot do: if the source itself contains malicious changes, the provenance faithfully signs “we correctly took this malicious code as input.” That needs a different layer: SLSA Source Track, four-eyes review, and so on.
In other words, provenance is the layer that detects build-path tampering but cannot detect human malice. Combine it with SBOM (transparency about contents) and Cosign (identity of the author), and only then do you get layered defense.
Once “build path + contents + author” are all in place, the artifact has all-around evidence. If the Kubernetes admission layer can enforce reject unless provenance, SBOM, and signature are all present, the operator’s trust boundary firms up considerably.
Provenance, unlike SBOM or Cosign, isn’t something you can produce locally with a one-shot CLI. The builder itself has to be running in a place you trust, which is why something like a GitHub Actions reusable workflow (with its structural separation) is non-negotiable. The flip side: if you have that environment, a single line of uses: carries you all the way to L3.
The next post moves to the operational headache that always shows up once SBOMs hit production: “the vulnerability scanner threw 800 warnings, but only 5 are actually exploitable.” We pin that down with VEX. Where provenance is “authenticity of the build path”, VEX is “authenticity of vulnerability triage.”
Templates let you quickly answer FAQs or store snippets for re-use.
Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment’s permalink.
For further actions, you may consider blocking this person and/or reporting abuse
Thank you to our Diamond Sponsors for supporting the DEV Community
Google AI is the official AI Model and Platform Partner of DEV
DEV Community — A space to discuss and keep up software development and manage your software career
Built on Forem — the open source software that powers DEV and other inclusive communities.
We’re a place where coders share, stay up-to-date and grow their careers.
