name: github-release-integrity description: Decide, implement, or debug GitHub Release asset integrity for projects using GitHub Releases, immutable releases, release attestations, artifact attestations, checksums, package-manager release workflows, or direct latest download links. Use when Codex is asked whether immutable releases replace actions/attest, why release assets show or do not show GitHub's verified badge, how to verify assets with gh, how to structure release workflows around draft/immutable releases, or how to publish stable binary filenames safely.
GitHub Release Integrity
Use this skill to separate three related but different guarantees:
- Immutable release integrity: proves an asset belongs to a specific immutable GitHub Release and enables GitHub's visible release verification UI.
- Workflow provenance: proves an artifact was produced by a specific GitHub Actions workflow; generated by
actions/attest-*or equivalent. - Checksums/signatures: user-facing integrity material such as
SHA256SUMS, GPG signatures, package-manager hashes, or updater metadata.
The best model is usually hybrid: enable immutable releases for GitHub release integrity and UI, keep workflow provenance only when the project needs SLSA/build-chain evidence, and keep checksums/package hashes for installers, package managers, and offline verification.
Decision Model
- If the user wants the GitHub release asset list to show verified/attested UI, recommend immutable releases.
- If the user asks whether
actions/attestis redundant, answer by purpose:- Redundant for GitHub release UI and release-asset integrity once immutable releases are enabled.
- Not redundant for build provenance, because immutable releases create release attestations, not SLSA provenance attestations.
- If the user wants
gh release verifyorgh release verify-asset, immutable releases are the right mechanism. - If the user wants
gh attestation verifyto prove workflow identity, keep or add explicit workflow provenance attestations. - If binaries are consumed by Scoop, Homebrew, Winget, auto-updaters, or direct README links, keep checksums and exact asset names deterministic.
Empirical Checks
Prefer checking real repository state instead of guessing.
Check whether a repo has immutable releases enabled:
gh api repos/OWNER/REPO/immutable-releases --jq .
Download an asset and inspect its public attestations by digest:
tmp=$(mktemp -d)
cd "$tmp"
gh release download TAG --repo OWNER/REPO --pattern ASSET_NAME
digest=$(sha256sum ASSET_NAME | awk '{print $1}')
gh api "users/OWNER/attestations/sha256:$digest" \
--jq '[.attestations[].bundle.dsseEnvelope.payload | @base64d | fromjson | .predicateType]'
gh api "users/OWNER/attestations/sha256:$digest?predicate_type=release" --jq '.attestations | length'
gh api "users/OWNER/attestations/sha256:$digest?predicate_type=provenance" --jq '.attestations | length'
Interpretation:
https://in-toto.io/attestation/release/v0.2means immutable-release release attestation.- SLSA/provenance predicate types mean workflow/build provenance.
- A repo can have release attestations without explicit
actions/atteststeps.
Workflow Shape
For immutable releases, publish safely by finishing all mutable work while the release is still draft:
- Build assets.
- Upload binaries, metadata, blockmaps, checksums, signatures, and release notes to a draft release.
- Normalize or delete duplicate assets before publishing.
- Verify expected asset names and checksum contents.
- Publish/undraft as the last step.
- Do not design post-publish asset edits; immutable releases intentionally block them.
For stable direct download URLs, prefer versionless asset names on future releases:
https://github.com/OWNER/REPO/releases/latest/download/PROJECT-win-x64-Setup.exe
Keep old versioned assets on historical releases. For migrations, upload one-time aliases to the current mutable/latest release before turning on immutable behavior for future releases.
Verification Commands
Use current GitHub CLI docs or gh <command> --help if syntax differs by installed gh version.
Release-level verification:
gh release verify TAG --repo OWNER/REPO
gh release verify-asset TAG ASSET_NAME --repo OWNER/REPO
Workflow provenance verification:
gh attestation verify ./ASSET_NAME --repo OWNER/REPO
If gh attestation verify fails while release verification succeeds, do not call the release broken. It usually means the project has immutable-release attestations but no workflow provenance attestations.
Known Example With Both
astral-sh/uv release 0.11.19 is a useful reference pattern for a project that has both immutable-release attestations and explicit workflow provenance attestations:
- Its release configuration enables GitHub attestations and pins
actions/attest-build-provenance. - Its release workflow grants
attestations: write, callsactions/attest-build-provenance, and publishes assets withgh release create ... artifacts/*. - Its asset
uv-x86_64-unknown-linux-gnu.tar.gzexposes both:https://in-toto.io/attestation/release/v0.2https://slsa.dev/provenance/v1
- It separately publishes checksum material such as
sha256.sumand per-asset.sha256files.
Use this as evidence that a project can combine immutable-release integrity with explicit actions/attest-build-provenance workflow provenance.
To re-check the asset:
tmp=$(mktemp -d)
cd "$tmp"
gh release download 0.11.19 --repo astral-sh/uv --pattern uv-x86_64-unknown-linux-gnu.tar.gz
digest=$(sha256sum uv-x86_64-unknown-linux-gnu.tar.gz | awk '{print $1}')
gh api "users/astral-sh/attestations/sha256:$digest" \
--jq '[.attestations[].bundle.dsseEnvelope.payload | @base64d | fromjson | .predicateType] | unique'
gh api "users/astral-sh/attestations/sha256:$digest?predicate_type=release" --jq '.attestations | length'
gh api "users/astral-sh/attestations/sha256:$digest?predicate_type=provenance" --jq '.attestations | length'
Expected result: the predicate list includes both release and SLSA provenance predicate types, and the release/provenance counts are both 1.