| id | how-to-verify-a-release |
|---|---|
| type | procedural |
| created | 2026-07-03 10:00:27 -0400 |
| diataxis_type | how-to |
| title | Verify a release |
Every tagged release publishes a git archive tarball carrying SLSA
build-provenance, a CycloneDX SBOM, six quality-gate verdicts, and an OpenVEX
disposition attestation; the marketplace.json catalog is cosign-signed. This
walks through re-verifying all of it from a clean workstation. Unlike a
multi-repo org, this plugin has no separate central signer: this repo's own
release.yml produces every attestation and the catalog signature. Pin
--signer-workflow to that workflow explicitly in every command below —
--repo alone only proves "some workflow in this repo signed it," not
specifically the release workflow.
-
GitHub CLI
gh≥ 2.49.0, authenticated:gh auth login gh auth status
-
cosign, for the catalog signature.
Substitute the actual release tag you're verifying for TAG below — the
commands are written against a variable, not a hard-coded version, since a
new tag ships each release.
TAG="v0.4.0" # substitute the release tag you're verifying
gh release download "$TAG" --repo zircote/cdc-error-plugin --dir ./dl
cd ./dl
ls
# cdc-error-plugin-<version>.tar.gz
# cdc-error-plugin-<version>-sbom.cdx.json
# cdc-error-plugin-<version>-checksums.txt
# marketplace.json.cosign.bundlesha256sum -c cdc-error-plugin-*-checksums.txtThis only proves the download wasn't corrupted or truncated — it says nothing about who built it. That's what the attestation is for.
gh attestation verify cdc-error-plugin-*.tar.gz \
--repo zircote/cdc-error-plugin \
--signer-workflow zircote/cdc-error-plugin/.github/workflows/release.yml \
--predicate-type https://slsa.dev/provenance/v1A passing verification looks like:
Loaded digest sha256:... for file://cdc-error-plugin-0.4.0.tar.gz
Loaded 1 attestation from GitHub API
✓ Verification succeeded!
This proves the tarball was built by zircote/cdc-error-plugin's own
release.yml workflow, from the commit the release tag points at, and hasn't
been modified since. gh attestation verify exits non-zero on any mismatch
— treat a non-zero exit as a reason not to trust the artifact.
gh attestation verify cdc-error-plugin-*.tar.gz \
--repo zircote/cdc-error-plugin \
--signer-workflow zircote/cdc-error-plugin/.github/workflows/release.yml \
--predicate-type https://cyclonedx.org/bomProves the tarball is bound to the CycloneDX SBOM published alongside it
(cdc-error-plugin-*-sbom.cdx.json).
Each gate (SAST, SCA, IaC/license, Semgrep, secrets, manifest review) is attested separately, bound to the same tarball digest:
for pt in sast sca iac-license semgrep secrets manifest; do
gh attestation verify cdc-error-plugin-*.tar.gz \
--repo zircote/cdc-error-plugin \
--signer-workflow zircote/cdc-error-plugin/.github/workflows/release.yml \
--predicate-type "https://zircote.github.io/attestations/${pt}/v1"
doneA passing verification proves the gate ran and recorded a verdict bound to this exact tarball — read the predicate body (Step 8) for the verdict itself.
gh attestation verify cdc-error-plugin-*.tar.gz \
--repo zircote/cdc-error-plugin \
--signer-workflow zircote/cdc-error-plugin/.github/workflows/release.yml \
--predicate-type https://openvex.dev/ns/v0.2.0The marketplace.json catalog is a blob (not an OCI image), so it's signed
with cosign keyless rather than gh attestation verify:
cosign verify-blob .claude-plugin/marketplace.json \
--bundle marketplace.json.cosign.bundle \
--certificate-identity-regexp '^https://github\.com/zircote/cdc-error-plugin/\.github/workflows/release\.yml@' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comNote this checks the catalog file in your local checkout of the repo
(.claude-plugin/marketplace.json), not a release asset — download or
checkout the repo at the release tag first if you don't already have it.
To see the actual details bound into any attestation (build provenance shown here; substitute any predicate type from the steps above):
gh attestation verify cdc-error-plugin-*.tar.gz \
--repo zircote/cdc-error-plugin \
--signer-workflow zircote/cdc-error-plugin/.github/workflows/release.yml \
--predicate-type https://slsa.dev/provenance/v1 \
--format json | jq '.[0].verificationResult.statement.predicate'- Proves: this exact file was produced by this repository's release workflow from a specific commit, and hasn't been altered since.
- Doesn't prove: the code is bug-free, or that the maintainer's account or the GitHub Actions runner it built on wasn't itself compromised. See SECURITY.md for the full threat-model caveat.
For why this pipeline exists and looks the way it does, see Why this plugin ships an attested release pipeline.