Skip to content

ci(release): sign artifacts with Sigstore provenance + attestation#537

Open
oso0x34 wants to merge 1 commit into
colbymchenry:mainfrom
oso0x34:security/release-provenance
Open

ci(release): sign artifacts with Sigstore provenance + attestation#537
oso0x34 wants to merge 1 commit into
colbymchenry:mainfrom
oso0x34:security/release-provenance

Conversation

@oso0x34
Copy link
Copy Markdown

@oso0x34 oso0x34 commented May 28, 2026

What & why

The release pipeline generates SHA256SUMS and the npm shim verifies against it, but nothing is signed: SHA256SUMS is unsigned and lives in the same release as the artifacts, and npm publish runs without --provenance. So neither install channel can prove a download actually came from this repo's build.

A checksum published next to the artifact it describes proves integrity, not authenticity — anyone who can replace the archive can replace the hash. This PR adds the missing authenticity layer using Sigstore, with no keys or secrets to manage (everything uses the release job's OIDC identity).

Changes

  • .github/workflows/release.yml
    • Add id-token: write + attestations: write permissions.
    • Attest every release archive and SHA256SUMS via actions/attest-build-provenance@v2.
    • Add --provenance to npm publish.
  • scripts/pack-npm.sh — add the repository field to the generated package.json files. npm publish --provenance fails without it, so this is required for the workflow change to work.
  • BUNDLING.md — document gh attestation verify and the npm provenance badge; reframe the "code signing" TODO as OS code signing (Gatekeeper/Authenticode), which is separate.

How to verify after a release

# GitHub Release archive
gh attestation verify codegraph-linux-x64.tar.gz --repo colbymchenry/codegraph
# npm package → "Provenance" badge on npmjs.com

Notes for the maintainer

  • Verification is opt-in — the dependency-free curl | sh path is unchanged. (Wiring gh attestation verify into install.sh would break its "no dependencies" design, so I deliberately left the installers alone.)
  • I couldn't run the release workflow myself (needs workflow_dispatch + NPM_TOKEN), so please sanity-check on a test release. The two things to confirm: the repository URL matches this repo (provenance is strict about it), and the attestation step finds release/codegraph-*.
  • Out of scope / possible follow-up: scripts/npm-shim.js verifyChecksum() is fail-open (skips when SHA256SUMS is absent/unlisted). Once releases reliably ship sums, making it fail-closed for versions that publish them would close the downgrade gap. Happy to do that as a separate PR if you want it.

🤖 Generated with Claude Code

The release publishes SHA256SUMS but nothing signs it, and `npm publish`
runs without provenance — so neither install channel can prove a download
came from this repo's build. A checksum published next to the artifact it
describes proves integrity, not authenticity: whoever can replace the
archive can replace the hash. This adds the missing authenticity layer.

- release.yml: add id-token:write + attestations:write permissions;
  attest every release archive and SHA256SUMS via
  actions/attest-build-provenance@v2; add --provenance to npm publish.
- pack-npm.sh: add the `repository` field the generated packages require
  for `npm publish --provenance` to succeed.
- BUNDLING.md: document `gh attestation verify` and the npm provenance badge.

No keys or secrets introduced — all signing uses the release job's OIDC
identity via Sigstore. Verification is opt-in, so the dependency-free
`curl | sh` path is unchanged. OS code signing (Gatekeeper/Authenticode)
remains a separate TODO.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@oso0x34 oso0x34 marked this pull request as ready for review May 28, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants