gh attestation verify: Verify Build Provenance
gh attestation verify confirms that an artifact was built by the expected GitHub Actions workflow via its signed attestation.
Supply-chain gates verify provenance before deploying; gh attestation verify proves an artifact came from a trusted workflow, not an attacker.
What it does
gh attestation verify checks the Sigstore-signed build provenance attestation for an artifact. You give it the artifact and the expected --owner or --repo; it verifies the signature and that the attestation references that source, printing the verified policy.
Common usage
gh attestation verify ./myapp --owner my-org
gh attestation verify ./myapp --repo my-org/myapp
gh attestation verify oci://ghcr.io/my-org/app:1.0 --owner my-org
gh attestation verify ./myapp --repo my-org/myapp --bundle attestation.jsonlFlags
| Flag | What it does |
|---|---|
| -o, --owner <name> | Expected owner of the source repository |
| -R, --repo <owner/repo> | Expected source repository |
| -b, --bundle <file> | Verify against a local attestation bundle |
| --predicate-type <uri> | Filter attestations by predicate type |
| --format json | Output the verification result as JSON |
In CI
Set GH_TOKEN with permissions: { contents: read } so gh can fetch attestations from the API; for offline verification supply --bundle and no token call is made. Either --owner or --repo is required so the check is bound to a trusted source.
Common errors in CI
"failed to verify attestation: no attestations found" means the artifact has none published, or --owner/--repo does not match. "gh: To use GitHub CLI in a GitHub Actions workflow, set the GH_TOKEN environment variable" means GH_TOKEN is unset when fetching from the API. A digest mismatch means the file differs from what was attested.
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached