Provenance is the answer to one question, asked about a specific artifact:
Where did this come from, and how was it made?
The question sounds administrative. It is not, and the reason is that for most software the honest answer is a guess. A binary in a registry, a package on a mirror, a container image somebody deployed last quarter — the connection between those bytes and the source anybody reviewed is usually a convention rather than a fact.
The short answer
Section titled “The short answer”Provenance is a record of how an artifact was produced, ideally signed by something the build cannot forge.
The core fields:
| Field | Records |
|---|---|
| Subject | The artifact, by digest — what this record is about |
| Builder identity | Which system performed the build |
| Build type | What kind of build process ran |
| Source | Which repository and commit |
| Invocation | What triggered it, and with what parameters |
| Resolved inputs | The top-level inputs, including dependencies where known |
| Metadata | When it started and finished |
Read together, those fields let somebody who was not there reconstruct what happened — and, more usefully, detect when what happened is not what they expected.
Why “rebuild and compare” does not work
Section titled “Why “rebuild and compare” does not work”The intuitive verification is to rebuild the artifact from the stated source and check the bytes match. This mostly fails, and understanding why explains the existence of provenance.
Two builds of the same commit typically differ:
- Timestamps embedded in archives, binaries and metadata
- Absolute paths from the build directory baked into debug information
- File ordering determined by filesystem enumeration
- Toolchain versions that differ between machines
- Hash seeds randomised per process
- Locale and timezone affecting string formatting and sorting
None of these are defects. They are ordinary properties of build tooling that was never asked to be deterministic, and eliminating them is the practice known as reproducible builds — real, valuable, and achieved by a minority of projects because it requires effort across an entire toolchain.
Provenance is the pragmatic alternative. Rather than proving the artifact can be independently reconstructed, it records verifiable facts about how it was produced, signed by something the build being described cannot influence.
Provenance, signature, SBOM
Section titled “Provenance, signature, SBOM”The three artefacts most often conflated, each answering a different question about the same artifact.
| Question | Establishes | Silent about | |
|---|---|---|---|
| Signature | Who vouches for these bytes? | An identity produced or endorsed this, and it has not changed | Where it came from; what is in it; whether it is good |
| Provenance | Where and how was it built? | Source, builder, process, inputs | What is inside it; whether it is good |
| SBOM | What is inside it? | Components and versions | Where it came from; whether any component is safe |
A worked example. You have a container image and want to know whether to deploy it.
The signature tells you it was signed by an identity you recognise and has not been modified since. It does not tell you the identity built it from your source — an identity can sign anything.
The provenance tells you it was built by workflow X in repository Y from commit Z. That is the link back to code somebody reviewed, and it is the claim a deployment policy asserts on.
The SBOM tells you it contains a library at a version with a known critical vulnerability.
Three documents, three answers, and the deployment decision needs all three. In practice the signature and the provenance travel together — provenance is normally delivered as a signed attestation, which is why the two get merged in casual usage. They remain distinct claims: the signature makes the provenance trustworthy, and the provenance is what the signature is about.
Practical determinism
Section titled “Practical determinism”Full reproducibility is a large project. Several of its components are individually cheap, help debugging as much as security, and make provenance more useful by narrowing what could differ between two builds of the same commit.
Fix timestamps. SOURCE_DATE_EPOCH is honoured by a growing set of build tools and archivers, and
setting it from the commit date makes archive contents stable:
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)export SOURCE_DATE_EPOCHPin the toolchain. A compiler version in a workflow file rather than “whatever the runner image has this month”. The runner image changes; your build should not change with it silently.
Sort what enumerates. Archive and bundle creation that iterates a directory picks up filesystem ordering. Sorting explicitly removes a source of variation that also produces confusing diffs.
Fix locale and timezone. LC_ALL=C and TZ=UTC eliminate a category of formatting and sorting
differences that are invisible until they are not.
Strip build paths where the toolchain supports it, so debug information does not embed the directory the build happened in.
None of this makes a build reproducible on its own. All of it makes builds more comparable, which means that when two builds of the same commit do differ, the difference is more likely to be interesting. That is a debugging benefit that happens to be a security benefit.
Trust boundaries
Section titled “Trust boundaries”Provenance does not eliminate trust. It relocates it, and knowing where it lands is what stops provenance being over-read.
A vertical chain: the build platform's honesty, the platform's isolation, the signing infrastructure, the transparency log, and the verifier's policy. Provenance makes claims checkable but each of these remains trusted.
Every stage is trusted. What provenance changes is that the trust is now placed in specific, auditable systems rather than in an unstated assumption that the artifact in the registry is the right one.
That is a real improvement and it is not the same as trustlessness. The honest formulation: provenance moves you from “we assume this is the right binary” to “we trust GitHub’s statement that this binary came from this commit, and we can check that statement has not been altered.”
The last stage in the diagram is the one under your control and the one most often weak. A verifier that checks a signature exists, without asserting the repository, the workflow and the ref, is trusting the artifact to describe itself.
What provenance cannot establish
Section titled “What provenance cannot establish”That the source is good. It names a commit. It has no opinion about the commit’s contents.
That the build did the right thing. A build script that compiles the wrong file produces accurate provenance about having done so.
That the artifact is free of vulnerabilities. Origin is not quality.
That the build environment was clean. Unless the platform provides isolation guarantees — which is what the higher SLSA build levels are about — a compromised build step may have influenced the output while the provenance remains accurate about which workflow ran.
That the inputs were what you expected. Provenance records resolved inputs to the extent the platform can determine them. A build that downloads a binary from a URL at run time may record the URL and not the bytes.
Each of these is a reason for a different control, not a criticism. Provenance is a narrow instrument that does one thing well.
Using it in a decision
Section titled “Using it in a decision”Provenance that nobody evaluates is metadata. The evaluation is a policy, and a policy is a list of assertions plus an action on failure.
A minimal useful policy for a deployment:
-
An attestation exists for the artifact’s digest.
-
It was built from the expected repository. Named by you, not read from the artifact.
-
By the expected workflow. The release workflow specifically, not any workflow in the repository.
-
From an expected ref. A release tag, or the default branch — not an arbitrary branch, which would let anyone with push access produce a deployable artifact.
-
The commit is an ancestor of the branch you believe you released from, if you want to close the gap where a valid build came from a legitimate but unintended commit.
-
On failure, the deployment does not proceed.
Steps 2 to 4 are what turn verification from a formality into a control. Step 6 is what turns the control from documentation into a boundary.
Questions provenance answers in ordinary operations
Section titled “Questions provenance answers in ordinary operations”The security case is the compromised artifact. It is not the case that gets provenance adopted, because it is rare. The operational cases are frequent, and they are what makes the record earn its place.
“Which commit is running in production?” Asked during every incident. Without provenance the answer is derived from deployment records and CI logs, both of which rotate. With it, the artifact answers for itself.
“Who owns this image?” A vulnerability is reported against a container in your registry. Nothing in the image says which team built it or from what. Provenance names the repository, and the repository has owners.
“Was this deployed from the release branch or from somebody’s feature branch?” More common than it should be, and unanswerable after the fact without a record of the ref.
“Is the artifact in staging the same one we are about to promote?” Comparing digests answers this; comparing tags does not, because tags move.
“When did this artifact enter the registry, and what was in the source then?” The archaeology question, asked months later, usually while somebody is waiting.
Each of these is a question a team asks anyway, answers slowly, and answers with uncertainty. That is a stronger argument for adoption than the compromise scenario, and it is the one to make internally — provenance pays for itself in incident response long before it catches an attack.
Where to enforce it
Section titled “Where to enforce it”In the deployment workflow is the easy place, and it covers artifacts your pipeline deploys. It does not cover anything deployed another way.
At admission — a Kubernetes admission controller, a registry policy, a package manager configuration — covers everything arriving, including artifacts pushed by somebody bypassing your pipeline. This is the stronger position and the harder one to operate, because a failed admission during an incident is a conversation nobody wants to have at 03:00.
The practical progression is to start in the pipeline, get the policy right, and move it to admission once failures are rare and understood. Moving it before then produces an outage that gets the control disabled.
Build-once, promote-by-digest
Section titled “Build-once, promote-by-digest”Provenance describes one artifact. That is only useful if one artifact is what travels through your environments, and a surprising number of pipelines rebuild instead.
The pattern that makes provenance meaningful end to end:
Build once, in the release workflow, producing an artifact identified by its digest.
Attest it, binding the provenance to that digest.
Promote by digest. Staging deploys image@sha256:abc…. Production deploys the same
image@sha256:abc…. Not the same tag — the same digest.
Verify at each promotion, so the artifact entering production is confirmed to be the one that was built and tested.
The alternative — rebuilding for each environment — breaks the chain in a way that is easy to miss. Two builds of the same commit produce two different artifacts with two different digests and two separate provenance records. Everything you tested in staging applies to a binary that is not the one in production, and no amount of attestation fixes that, because both attestations are accurate.
This is also the argument for tags being labels rather than identifiers. A tag pointing at a digest is a convenience for humans. The deployment should reference the digest, so that moving the tag cannot change what runs.
Provenance for what you consume
Section titled “Provenance for what you consume”The producer side gets the attention. The consumer side is where provenance changes your risk.
For each significant input — base images, third-party actions, key packages — ask:
Does it publish provenance? Increasingly, yes. npm packages published with provenance, container images with attestations, actions from repositories that attest their releases.
Do you verify it? Almost certainly not, and this is the gap worth closing.
What would you do on failure? If the answer is “log it”, you have not built a control.
A realistic starting point that is not a programme: verify the inputs to your release pipeline. It is the workflow where a compromised input reaches the most places, and it usually has few enough inputs to enumerate in an afternoon.
The threat model
Section titled “The threat model”Threat. An artifact reaches production that was not built from the source that was reviewed, and nothing establishes the discrepancy.
Attack surface. The build platform, the build definition, the build’s inputs, artifact storage, and the deployment configuration that selects what runs. Also the provenance record itself, if anything other than the platform can write it.
Impact. A compromised artifact distributed through your own trusted channel — which is the highest impact available in this pillar, because it arrives by the route everything legitimate arrives by.
Control. Provenance generated by a platform the build cannot influence, and a deployment policy that refuses artifacts whose provenance does not match a locally-held expectation.
Verification. Two tests, and both are needed. Confirm that a legitimate artifact passes. Then construct one that should fail — built from a different workflow or a different ref — and confirm the policy rejects it.
The second test is what distinguishes a working control from a step that always passes. Policies fail open in mundane ways: a typo in a workflow path, a variable that is empty so the assertion matches nothing, a step whose failure is swallowed. None of those are visible until something is supposed to be rejected.
Provenance for things that are not builds
Section titled “Provenance for things that are not builds”The concept generalises further than the tooling usually implies, and it is worth knowing because the same question applies to artifacts nobody thinks of as build outputs.
Infrastructure. A Terraform plan applied to production — which commit produced it, which run applied it, against which state? The question is identical and the answer is usually less well recorded than for application builds. See Deploy Terraform.
Configuration. A configuration bundle deployed to a fleet. Same question.
Machine learning models. Which training code, which dataset version, which hyperparameters, which run? Model provenance is the same structure with different fields, and it is the case where “we cannot reproduce it” is most often accepted as normal.
Documents and datasets. Anything generated by a pipeline and consumed downstream.
In each case the useful move is the same: record the origin at production time, sign it with an identity the producer cannot forge, and check it at the point of use. The tooling in this cluster is oriented at software artifacts, and Cosign will sign any blob — so where the standard actions do not fit, the pattern still does.
Common mistakes
Section titled “Common mistakes”Expecting to verify by rebuilding. Builds are not deterministic by default, which is why provenance exists.
Treating provenance as a safety claim. It records origin.
Verifying existence rather than content. “An attestation exists” is nearly no assertion.
Deriving the expected identity from the artifact. Self-vouching.
Verifying in the pipeline only. Covers your path, not everything that arrives.
Provenance from an untrusted builder. A record signed by a build platform an attacker controls is an accurate record of a compromised build.
Recording provenance and retaining it for less time than the artifact lives. The question arrives after the evidence has gone.
Confusing provenance with an SBOM. Where it came from and what is inside it are different documents, and consumers usually need both.
Mental model
Section titled “Mental model”Provenance is a chain of custody record for an artifact. It does not make the artifact good, and it does not remove the need to trust the builder. It makes the builder’s claim about the artifact checkable — which is the difference between believing the right binary is deployed and being able to tell.
What you learned
Section titled “What you learned”- Provenance records subject, builder, build type, source, invocation, resolved inputs and timing
- Rebuilding and comparing usually fails, because builds are non-deterministic by default
- Reproducible builds are the stronger property; provenance is the achievable one
- Signature, provenance and SBOM answer who vouches, where from, and what is inside
- Provenance relocates trust to specific auditable systems; it does not eliminate it
- The weakest link is usually the verifier’s policy, not the cryptography
- A policy must assert the repository, workflow and ref, and must fail the deployment on mismatch
- The expectation has to come from your configuration, never from the artifact
- Enforcing at admission covers everything arriving; enforcing in the pipeline covers your path
- Verifying provenance on what you consume is the half that protects you rather than your consumers
Exercise
Section titled “Exercise”Use a disposable repository that builds an artifact.
-
Build the same commit twice, in two runs. Compare the artifacts byte for byte. Predict: are they identical?
-
If they differ, find out why —
diffoscopeif available, or compare sizes and embedded strings. Predict: timestamps or paths? -
Generate provenance for one build. Read the attestation and identify the source commit, the workflow and the trigger.
-
Verify it asserting the correct repository, then a different one. Compare the outputs.
-
Build the same artifact from a branch rather than the default branch and generate provenance. Predict: does a policy that only checks the repository distinguish the two?
-
Add a ref assertion and re-run step 5. Predict: does it now fail?
-
Write down what your policy would have to assert to be meaningful for your real pipeline. That list is the deliverable.
-
Delete the repository.