Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ on:
workflow_dispatch: {}

permissions:
contents: write # create the GitHub Release + tag, push the CHANGELOG promote
contents: write # create the GitHub Release + tag, push the CHANGELOG promote
id-token: write # OIDC for npm provenance + build-provenance attestation (no secrets)
attestations: write # store signed build-provenance attestations for the release archives

jobs:
release:
Expand Down Expand Up @@ -127,6 +129,22 @@ jobs:
( cd release && sha256sum codegraph-* > SHA256SUMS )
cat release/SHA256SUMS

- name: Attest build provenance
# Cryptographically ties every release archive + the SHA256SUMS file to
# THIS workflow run, signed via Sigstore using the job's OIDC identity —
# no secret/key to manage or rotate. This is the authenticity layer the
# SHA256SUMS file alone can't provide: a hash published next to the
# artifact it describes proves integrity, not provenance (an attacker who
# can replace the archive can replace the hash). Verify a downloaded
# bundle with:
# gh attestation verify codegraph-<target>.tar.gz --repo ${{ github.repository }}
# Opt-in for consumers, so it never breaks the dependency-free curl|sh path.
uses: actions/attest-build-provenance@v2
with:
subject-path: |
release/codegraph-*
release/SHA256SUMS

- name: Release notes from CHANGELOG.md
# The [<version>] block was guaranteed-populated by the
# "Promote" step above, so the [Unreleased] fallback should
Expand Down Expand Up @@ -167,7 +185,12 @@ jobs:
echo "skip $name@$V (already published)"
else
echo "publishing $name@$V"
( cd "$dir" && npm publish --access public )
# --provenance: npm generates a signed Sigstore provenance statement
# (via the job's OIDC identity) linking the published tarball to this
# source repo + workflow run. Requires the id-token:write permission
# above and a `repository` field in each package.json (added by
# pack-npm.sh). Visible as the "Provenance" badge on npmjs.com.
( cd "$dir" && npm publish --access public --provenance )
fi
done

Expand Down
27 changes: 24 additions & 3 deletions BUNDLING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,31 @@ triggered. Reads the version from `package.json`, builds every platform bundle o
one runner, creates the GitHub Release (notes from `CHANGELOG.md`), and publishes
the npm shim + per-platform packages. Requires the `NPM_TOKEN` repo secret.

## Supply-chain authenticity

Every release is signed via [Sigstore](https://www.sigstore.dev/) using the
release job's OIDC identity — no maintainer keys to manage or rotate:

- **GitHub Release archives + `SHA256SUMS`** carry a build-provenance attestation
(`actions/attest-build-provenance`). Verify a download before trusting it:

```bash
gh attestation verify codegraph-linux-x64.tar.gz --repo colbymchenry/codegraph
```

This is the authenticity layer `SHA256SUMS` alone can't provide: a checksum
published next to the artifact it describes proves integrity, not provenance —
anyone who can replace the archive can replace the hash. The attestation ties
the bytes to a specific workflow run in this repo. Verification is **opt-in**,
so the dependency-free `curl | sh` path keeps working unchanged.

- **npm packages** are published with `--provenance`, so npmjs.com shows a
"Provenance" badge linking each tarball to its source commit + build.

Still TODO:
- **Code signing** — the main gap for "download & run": macOS Gatekeeper needs a
Developer ID + notarization; Windows needs Authenticode. Homebrew softens the
macOS case (handles quarantine).
- **OS code signing** — for "download & run" UX (separate from provenance):
macOS Gatekeeper needs a Developer ID + notarization; Windows needs
Authenticode. Homebrew softens the macOS case (handles quarantine).
- Retire the now-vestigial Node-version gate in `src/bin/codegraph.ts` — the
bundle always runs Node 24, and the npm shim does no tree-sitter work.
- Re-wire `npm uninstall` cleanup (the agent-config `preuninstall`) through the
Expand Down
6 changes: 6 additions & 0 deletions scripts/pack-npm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ for archive in "${archives[@]}"; do
name: `${process.env.SCOPE}/codegraph-${process.env.TARGET}`,
version: process.env.VERSION,
description: `CodeGraph self-contained bundle for ${process.env.TARGET}`,
// repository is REQUIRED for `npm publish --provenance` (release.yml):
// npm matches it against the building repo to mint the Sigstore
// provenance statement; publish fails without it.
repository: { type: "git", url: "git+https://github.com/colbymchenry/codegraph.git" },
os: [process.env.OSV], cpu: [process.env.ARCHV],
files: [process.env.NODEFILE, "lib", "bin"],
license: "MIT"
Expand All @@ -85,6 +89,8 @@ VERSION="$VERSION" SCOPE="$SCOPE" TARGETS="${targets[*]}" \
version: process.env.VERSION,
description: "Local-first code intelligence for AI agents (MCP). Self-contained — bundles its own runtime.",
bin: { codegraph: "npm-shim.js" },
// Required for `npm publish --provenance` (see release.yml).
repository: { type: "git", url: "git+https://github.com/colbymchenry/codegraph.git" },
optionalDependencies: opt,
files: ["npm-shim.js","README.md"],
license: "MIT"
Expand Down