A checksum tells you whether a file matches a published digest. It does not, by itself, tell you who made that digest, which repository produced the file, or which workflow made the claim. That distinction matters when a release archive is about to become an executable on an administrator workstation or a CI runner.
GitHub CLI can verify an artifact attestation before that handoff. gh attestation verify calculates the artifact digest, obtains the matching signed attestation, and checks its provenance against an expected owner or repository. The repository boundary is not optional decoration: without it, a valid attestation for a different project is not evidence that the intended project produced the file.
This guide tests that boundary with the Linux x86_64 archive from uv 0.12.17, released on September 18, 2026. In a disposable directory, GitHub CLI 2.96.0 accepted the original archive when verification was scoped to astral-sh/uv. A copy with one byte changed failed because GitHub had no attestation for its new SHA-256 digest. The useful result is not that a command printed a success line; it is that the check rejected the changed file before it could be extracted or executed.
This is a verification step for a release artifact you have intentionally downloaded from an approved project. It is not a substitute for the project selection, a vulnerability review, a local change-control process, or testing the tool in an appropriate environment.
Set the identity before downloading
Start with two inputs that a reviewer can recognize: the exact release version and the expected GitHub repository. Do not use a mutable latest URL in an automated path. The example uses uv only because its 0.12.17 release publishes both a Linux archive and GitHub artifact attestations; replace the URL and repository with the tool your team has approved.
version=0.12.17
repo=astral-sh/uv
archive="uv-x86_64-unknown-linux-gnu.tar.gz"
base="https://github.com/$repo/releases/download/$version"
curl --fail --location --remote-name "$base/$archive"
--fail makes an HTTP error nonzero, and --location permits the release download’s normal redirects. Those flags make the transfer failure visible, but they do not authenticate the final bytes. Keep the archive in a temporary directory until its verification succeeds. Do not unpack it, mark a binary executable, or place it in a shared tool directory first.
GitHub CLI must be available before this step. Confirm the locally installed version and inspect the command help on the runner that will enforce the policy:
gh --version
gh attestation verify --help
The command’s default predicate is SLSA provenance v1. Its help also makes an important policy point explicit: a verifier must supply --owner or --repo, and a narrower identity is stronger. For a release adopted from one known repository, use --repo owner/name, not a broad owner-wide rule.
Verify the downloaded bytes against the repository
Run the verification before any extraction or installation action:
gh attestation verify "$archive" --repo "$repo"
A zero exit status means the CLI found and verified an appropriate attestation for the archive digest while enforcing the repository identity. In the validation run, the original uv archive returned zero with this command. The archive SHA-256 was fa82fd8dde8e8eefdecada6aa0889666556cfceb690d06e0c3bca49eb3070a63.
The default terminal format can be intentionally quiet on success. Treat the exit status as the initial machine decision. When a change record needs structured evidence, request JSON and save it outside the source repository with other release evidence:
gh attestation verify "$archive" \
--repo "$repo" \
--format json > attestation-result.json
The JSON includes the verified statement, certificate information, and verified timestamps. It can support a later policy check, but do not blindly trust every field in the statement as independent evidence. GitHub CLI documents that certificate and verified-timestamp values are protected by the signature and transparency or timestamp services, while workflow-controlled predicate content may be influenced by a compromised workflow. The repository scope is therefore a minimum. For a higher-assurance policy, also constrain the signer with --signer-workflow or --signer-repo after confirming the project’s published build design.
Avoid placing an authenticated GitHub token in the command line, a release URL, or a saved JSON result. Normal public-attestation retrieval may work without special credentials; if the environment uses authenticated GitHub CLI, let its configured credential handling supply access rather than copying a token into a script.
Prove that a changed archive is rejected
A positive check alone does not prove that the gate is connected to the file a later step will consume. Make a disposable modified copy and require the same verification command to fail. Do not use a real release archive as the mutation target.
python3 - "$archive" modified-archive.tar.gz <<'PY'
from pathlib import Path
import sys
source = Path(sys.argv[1]).read_bytes()
changed = bytearray(source)
changed[-1] ^= 1
Path(sys.argv[2]).write_bytes(changed)
PY
if gh attestation verify modified-archive.tar.gz --repo "$repo"; then
printf '%s\n' "unexpected attestation success" >&2
exit 1
fi
The byte change is deliberately uninteresting. It changes the SHA-256 digest without needing to understand the archive format. In the validation run, the modified copy exited nonzero and GitHub CLI returned an HTTP 404 while looking up the altered digest’s attestation. That response is expected for this test: the repository has an attestation for the released bytes, not for an arbitrary local derivative.
Key automation on the nonzero exit status rather than the exact 404 wording. A missing attestation, a repository mismatch, a revoked or invalid signature, a network failure, and an API authorization problem are all reasons to stop automatic execution, but they need different operator follow-up. Preserve the error text in restricted build evidence and identify which case occurred before retrying.
Put the gate before execution
A small shell boundary prevents a later command from accidentally consuming an unchecked archive. Use an explicit artifact path and repository rather than accepting values from an unreviewed environment variable.
#!/usr/bin/env sh
set -eu
archive=$1
repo=astral-sh/uv
gh attestation verify "$archive" --repo "$repo"
# Extraction or a controlled installation step belongs after this line.
In CI, download the release, verify it, then extract it into the job’s disposable workspace. Pin the gh version through the same approved delivery method used for other build tools. GitHub CLI 2.101.0 was released on September 15, 2026, but do not assume a runner has that release merely because it is current upstream; record the version actually used to make the verification decision.
An attestation verifies provenance for a particular artifact digest. It does not prove that the binary is safe for every deployment, compatible with the target operating system, or free from later-discovered vulnerabilities. Keep checksum verification when a project publishes it, because it provides an independent transfer-integrity check and a useful audit value. Then add provenance verification to connect those downloaded bytes to the expected repository and signing workflow.
The decisive test is simple: the original release archive verifies under its expected repository identity, while a one-byte modification does not. Put that test before extraction and execution so a release artifact becomes runnable only after the provenance boundary has held.