Skip to content

Verifying a clickwork release

Every release from 1.0.1 onward is provenance-protected three ways: the PyPI package carries PEP 740 attestations, the GitHub Release carries Sigstore .sigstore bundles, and the git tag is GPG-signed (workflow key by default; maintainer key in fallback). Pick whichever verify path matches how you installed.

(Examples below use 1.0.1 as the target version — substitute the version you installed.)

Verifying the PyPI package

Manual verify today

pip's built-in auto-verify of PEP 740 attestations is not yet GA. Today, verification is a manual step via the pypi-attestations CLI. When installers ship auto-verify, this section will update to reference the flag.

Install the pypi-attestations CLI in a scratch venv:

pip install pypi-attestations

Verify the attestations for clickwork 1.0.1 on PyPI:

pypi-attestations verify pypi clickwork==1.0.1

Expected output: "OK" per artifact, with the workflow identity (https://github.com/qubitrenegade/clickwork/.github/workflows/publish.yml@refs/tags/v1.0.1) named.

Verifying a GitHub Release asset

Download the wheel (or sdist) + its .sigstore bundle from the Release page. Install the sigstore-python CLI from PyPI (sigstore):

pip install sigstore

Verify (adjust the --cert-identity tag ref to match the version you downloaded — for a prerelease use the hyphenated form, e.g. @refs/tags/v1.0.1-rc0):

sigstore verify identity \
  ./clickwork-1.0.1-py3-none-any.whl \
  --bundle ./clickwork-1.0.1-py3-none-any.whl.sigstore \
  --cert-identity https://github.com/qubitrenegade/clickwork/.github/workflows/publish.yml@refs/tags/v1.0.1 \
  --cert-oidc-issuer https://token.actions.githubusercontent.com

(Repeat with the sdist if you pulled the sdist.)

Expected output: OK: ./clickwork-1.0.1-py3-none-any.whl.

Verifying the git tag

git verify-tag v1.0.1

Expected output: "Good signature from" followed by either the release-signing key UID (workflow path, default for 1.0.1+) or the maintainer's personal key UID (local-GPG fallback path, documented in CONTRIBUTING.md for emergency release cuts).

The public half of whichever key signed the tag is published on the maintainer's GitHub account (Settings → SSH and GPG keys), which is what gives signed tags a green "Verified" badge on the tag detail page.

Troubleshooting

No .sigstore bundle on the Release page

Releases before 1.0.1 were not signed. If the Release page has no .sigstore files, the bundle verify path is unavailable and you should either upgrade to 1.0.1+ or fall back to the hash-pinning verify path documented in security.md.

pypi-attestations reports no attestations

Check you're on 1.0.1 or later: pip show clickwork. Attestations start with 1.0.1.

git verify-tag says "Can't check signature: No public key"

The tag is signed (by the workflow key for the recommended release path, or the maintainer's personal key for the local-GPG fallback documented in CONTRIBUTING.md), but your local GPG keyring doesn't have the signer public key yet. Both keys are published as GPG keys on the maintainer's GitHub account, which GitHub exposes via https://github.com/<user>.gpg:

curl -fsSL https://github.com/qubitrenegade.gpg | gpg --import

This returns every public GPG key associated with the account (workflow key + maintainer key); GPG will pick whichever one matches the tag's signature.

If git verify-tag instead reports "no signature", the tag is unsigned and this fallback flow does not apply.

sigstore verify identity rejects the certificate identity

The --cert-identity is the workflow URL that signed the artifact. For a final release vX.Y.Z, the ref form is refs/tags/vX.Y.Z; for a hyphenated prerelease vX.Y.Z-rc0, it's refs/tags/vX.Y.Z-rc0 exactly as the tag reads. If your --cert-identity string doesn't match the tag you installed from, verification will (correctly) reject — adjust and retry.

See also