Software Supply Chain Security

Provenance, Attestation, and Signing: A Practical Glossary

Provenance describes how software was built, attestations are signed claims about that process, and signing proves origin. Here's how the pieces fit.

Shadab Khan
Security Engineer
8 min read

Half the confusion in supply chain security conversations comes from three overlapping terms that engineers use interchangeably and auditors use incorrectly. Provenance, attestation, and signing describe different layers of the same trust stack, and getting them straight matters because each one answers a different question. Provenance answers how was this built. An attestation answers who claims that, and can I verify them. A signature answers was this byte-for-byte produced by the key I expect. Lose the distinction and you end up with policies that check the wrong property.

What is provenance, precisely?

Provenance is a structured description of how a software artifact was produced — the builder, the source commit, the build steps, the inputs, the timestamp, and any materials consumed. It's metadata, nothing more. A SLSA v1.0 provenance document is a JSON blob that says, for example, this container image was built by GitHub Actions running the workflow at .github/workflows/release.yml on commit a1b2c3d of github.com/example/app, starting at a specific UTC timestamp, using golang:1.22 as its base builder.

The provenance spec most teams encounter is SLSA provenance v1.0, which is itself an in-toto predicate — in-toto being the underlying schema for structured supply chain metadata, standardized by the Cloud Native Computing Foundation. But provenance can also be expressed in other predicate types: VEX statements describe exploitability, SPDX or CycloneDX attestations carry SBOMs, and SLSA VSA (Verification Summary Attestation) describes what verification was performed on a downstream artifact. The common thread is that provenance is a factual claim about the artifact; on its own it has no integrity properties at all.

What does an attestation add to raw provenance?

An attestation is provenance wrapped in a signed envelope, so the claim has a cryptographically verifiable author. The dominant envelope format in 2026 is DSSE (Dead Simple Signing Envelope), which bundles the payload, the payload type URI, and one or more signatures into a JSON structure. The specific combination of DSSE envelope plus in-toto predicate is what most tools mean when they say attestation — Cosign, GitHub Attestations, npm provenance, and Sigstore's own cosign attest command all produce DSSE-wrapped in-toto statements.

The distinction matters because a provenance document by itself can be fabricated by anyone. An attestation says this specific identity claims this specific provenance, and because the signature binds the claim to a key, downstream consumers can evaluate whether they trust that identity. When GitHub issues an attestation via actions/attest-build-provenance, the signing identity is not a long-lived key but an OIDC identity representing the workflow, repository, and commit — so the attestation encodes not just what was built but who built it, under what policy, in which repository.

What does signing actually prove?

Signing proves that a specific byte sequence was approved by the holder of a specific private key at some point in time, and nothing else. That's narrower than most people assume. A signature does not tell you the key holder is trustworthy, that the key hasn't been stolen, or that the signed payload is accurate. It only tells you the payload hasn't been modified since signing and that whoever had the key at signing time vouched for it.

Traditional code signing (Authenticode on Windows, Apple Developer certificates on macOS, GPG signatures on Linux packages) relies on long-lived keys protected by HSMs or developer hardware. Sigstore changed the calculus by introducing keyless signing: instead of managing private keys, developers authenticate via OIDC to Fulcio, which issues a short-lived X.509 certificate (typically valid for 10 minutes) bound to the OIDC identity. The signature is made with a key generated on the fly and destroyed immediately after. The signature plus the certificate plus a Rekor transparency log entry is what verifies, not a persistent key. That shifts the trust anchor from key management to identity management, which most organizations already do well via SSO.

How do Sigstore's components fit together?

Sigstore has three core components — Cosign, Fulcio, and Rekor — that together deliver keyless signing with transparency. Cosign is the command-line tool (and increasingly, a library) that developers and CI systems use to sign, attest, and verify artifacts. Fulcio is the certificate authority that issues short-lived signing certificates bound to OIDC identities. Rekor is the immutable, append-only transparency log that records every signature issued, so later verifiers can prove a signature was entered into the log at a given time and can detect inconsistency if Fulcio ever misbehaves.

A typical flow for signing a container image looks like this: Cosign generates an ephemeral key pair, prompts the caller (or reads from GitHub Actions OIDC) to authenticate, sends the OIDC token plus public key to Fulcio, receives a certificate binding the public key to the identity, signs the image digest, uploads the signature and certificate to the image's OCI registry, and posts a transparency log entry to Rekor. Verification reverses the chain: fetch the signature, verify it with the certificate, check that the certificate was issued by Fulcio for the expected identity, and confirm Rekor has a matching entry. Here's a minimal verification command:

cosign verify \
  --certificate-identity-regexp '^https://github.com/example/app/\.github/workflows/release\.yml@refs/tags/v.*$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/example/app:v1.4.2

That command says: verify the signature on the image, but only accept it if the signing certificate was issued to the specified GitHub Actions workflow running on a tag push. An attacker who compromises a secret or a service account cannot produce a signature that passes this check unless they can also run that specific workflow, which closes most realistic signing-key theft scenarios.

What changed with GitHub Attestations and npm provenance?

GitHub Attestations and npm provenance made Sigstore-based signing a default rather than an opt-in, which is why signed artifacts went from rare in 2023 to common in 2026. GitHub's actions/attest-build-provenance action, added in mid-2024 and stable since, generates a SLSA v1.0 L3 provenance statement for any artifact produced in Actions, signs it keylessly via Sigstore, and uploads it to a repository-scoped storage backend plus Rekor. The action runs in the workflow's trust boundary but cannot forge a provenance that claims a different workflow identity, because Fulcio only signs for the OIDC identity presented.

npm provenance, launched in 2023 and made easier to enable in every major release since, does the same for npm packages. When a package is published with --provenance, npm's registry requires the publisher to present an OIDC token from a supported CI (currently GitHub Actions and GitLab CI), calls Fulcio to sign, and records the attestation in a Sigstore bundle stored alongside the tarball. Downstream verification with npm audit signatures confirms that the tarball was built by the workflow referenced in the provenance, preventing the class of attack where an attacker with a stolen maintainer token publishes a malicious version — the attacker would not be able to forge a matching provenance. PyPI's PEP 740 attestation format, live since 2024, delivers the equivalent for the Python ecosystem.

Which terms get confused most often, and why does it matter?

The three terms most often conflated are signed artifact, signed attestation, and provenance, and the conflation leads to policies that verify the wrong thing. A signed artifact (a container image with a Cosign signature) only proves the image hasn't been modified since signing — it doesn't say how the image was built. A signed attestation adds a claim about the build process, but only if your verifier actually checks the predicate type and its contents. Provenance by itself, without a signed envelope, is a claim that anyone can write.

The practical consequence: a policy that requires images to be signed but doesn't inspect the attestation is trivially satisfied by an attacker who can push to your registry with any Fulcio-issued cert. A policy that requires a SLSA provenance attestation with a specific builder.id and source repository actually proves something about origin. Spell out what you're verifying, not just whether a signature is present — tools like Cosign's policy-controller, Kyverno's verifyImages rule, and Gatekeeper's ratify integration all support predicate-level assertions, and you should use them.

How Safeguard.sh Helps

Safeguard.sh treats provenance, attestations, and signatures as first-class data in the same graph as your SBOMs and CVEs. Our SBOM generation and ingestion module consumes DSSE-wrapped in-toto attestations, SLSA provenance v1.0, npm provenance bundles, and PEP 740 attestations, correlating each artifact with its builder identity, source commit, and verification state. Reachability analysis cuts 60-80% of CVE noise by eliminating vulnerabilities in code paths that are never invoked, so the signed artifacts your team actually ships surface first. Griffin AI autonomous remediation uses the provenance metadata to open pull requests against the exact source revisions recorded in attestations, rather than guessing which fork to patch. The TPRM module evaluates whether vendors ship artifacts with verifiable attestations and flags the gap when they don't. With 100-level dependency depth scanning and container self-healing, signed provenance stops being a compliance checkbox and becomes an operational signal your pipelines act on.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.