Safeguard
Software Supply Chain•

How do you sign and prove provenance for GitHub Actions builds?

Add GitHub's attest action to a workflow, verify with gh attestation verify before deploy, and know what a signed build record does and does not prove.

Safeguard Security Team
4 min read

You sign and prove provenance in GitHub Actions by adding the actions/attest step after your build, with id-token and attestations write permissions. The workflow then emits a signed record of what was built, where and from which commit. Consumers check it with the GitHub CLI before they deploy anything.

What does an attestation actually say?

An attestation is a signed statement about an artifact, identified by its digest. For build provenance, the statement says which workflow, repository and commit produced the file. The SLSA specification calls this provenance: what entity built the artifact, what process they used, and what the inputs were.

It does not say the code is safe. A malicious commit built by your own workflow gets a perfectly valid attestation. What you gain is the ability to reject anything that did not come from the pipeline you expect.

Where does this sit on the SLSA levels?

SLSA splits the build track into levels. Build L1 only needs provenance to exist, and the spec notes it is trivial to forge. Build L2 needs a hosted build platform that signs the provenance, which defends against tampering after the build. Build L3 adds a hardened platform that resists tampering during the build.

Running the attest step on a hosted runner moves you from "we have a build log" to signed provenance. Whether you reach L3 depends on how the platform isolates runs, which is not something your YAML alone decides.

What does the workflow change look like?

Constructed example, a Go binary release job. The permissions block is the part people forget.

permissions:
  id-token: write
  contents: read
  attestations: write

steps:
  - uses: actions/checkout@v4
  - run: go build -o dist/app ./cmd/app
  - name: Generate artifact attestation
    uses: actions/attest@v4
    with:
      subject-path: 'dist/app'

For container images, GitHub's documentation asks for packages write permission as well, a subject-name without a tag, the sha256 subject-digest, and push-to-registry set to true. You can also attach a signed SBOM attestation by passing an sbom-path next to the subject.

What are the plan limits?

GitHub's documentation says artifact attestations are available for all current plans, but on Free, Pro and Team plans only for public repositories. Private and internal repositories need GitHub Enterprise Cloud. Check this before you promise a private repo rollout, because plan terms change.

How does a consumer verify it?

Verification is the step that makes signing worth doing. For a binary, run gh attestation verify with the file path and the repository flag. For a container image, pass the image name prefixed with oci:// after logging in to the registry. For SBOM attestations, name the non-default predicate type.

If nobody verifies, the signature is decoration. Put the check in the deploy job or the admission step, and fail closed.

What to do

  1. Pick one release artifact, a binary or image, and add the attest step to its workflow.
  2. Set the three permissions at job level, not workflow level, so other jobs do not inherit id-token write.
  3. Pin third party actions to a full commit SHA, including the attest action if your policy requires it.
  4. Add a verify step to the deploy path and make it block on failure.
  5. Record the expected repository and workflow in your deploy configuration so a valid attestation from the wrong repo is still rejected.
  6. Generate an SBOM and attach it as an attestation once provenance works.
  7. Confirm your plan covers private repositories before enabling it org wide.

Safeguard can generate and scan SBOMs from the CLI and apply policy gates in CI, which pairs with attestations: the attestation says where it came from, the scan says what is inside.

Quick check

Does a valid attestation mean the software is safe?

No. It proves origin and integrity, not intent. A bad commit built by your workflow is still attested. Pair it with scanning and review.

Which permissions does the attest step need?

For binaries, id-token write, contents read and attestations write. Container images also need packages write, according to GitHub's documentation.

Sources

Last reviewed: 18 October 2026.

Never miss an update

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

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.