npm Provenance & Sigstore Attestation
Generate Sigstore provenance attestations with npm publish --provenance, wire GitHub Actions permissions, and let consumers verify the supply-chain link.
Without provenance, a consumer installing your package has no cryptographic way to confirm it was built from the source they can read on GitHub — they are trusting the maintainer’s account, the registry, and every machine in between. This matters most for dual-format packages distributed through automated pipelines: the build step compiles both ESM and CJS artifacts, and provenance is what proves those artifacts came from the CI run that ran your published source, not a modified local build.
Prerequisites
Canonical Configuration Block
The minimal addition to an existing release workflow is the id-token: write permission and the --provenance flag — nothing else about the publish command changes:
name: Publish with Provenance
on:
push:
tags:
- 'v*'
permissions:
contents: read
id-token: write # required — grants the OIDC token Sigstore needs to sign
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
id-token: write is the permission GitHub grants a workflow to mint a short-lived OIDC token scoped to that specific run. npm’s CLI uses this token internally — it never appears in your logs or environment as a usable credential on its own.
Provenance Signing Flow
Step-by-Step Implementation
Step 1 — Enable the id-token permission
Add the permission at the workflow or job level. Job-level scoping is preferable when other jobs in the same workflow do not need it:
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
Expected result: the runner can request an OIDC token from GitHub’s identity provider during the job; no visible output changes until the publish step runs.
HAZARD PREVENTION
Symptom:
npm publish --provenancefails with an error indicating no OIDC token is available, even though the workflow ran successfully.Root cause:
id-token: writewas set on an unrelated job, or on a reusable workflow that does not propagate permissions to the caller.Fix: Confirm the permission is declared on the exact job that runs
npm publish, and that any reusable workflow explicitly forwardspermissions: inheritif applicable.
Step 2 — Configure the publish workflow
actions/setup-node must set registry-url so npm’s CLI writes an .npmrc pointing at the public registry — provenance cannot be generated against a private, unconfigured registry:
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm run build
Expected result: ~/.npmrc inside the runner contains an always-auth entry scoped to the registry, and npm run build has produced the dist/ artifacts the tarball will contain.
Step 3 — Publish with provenance
npm publish --provenance --access public
Expected output:
npm notice
npm notice 📦 @acme/[email protected]
npm notice === Tarball Contents ===
npm notice 12.4kB dist/index.mjs
npm notice 9.8kB dist/index.cjs
npm notice 3.1kB dist/index.d.ts
npm notice === Provenance Statement ===
npm notice sigstore verification successful
npm notice Signed provenance statement:
npm notice predicateType: https://slsa.dev/provenance/v1
npm notice subject: pkg:npm/%40acme/[email protected]
npm notice + @acme/[email protected]
HAZARD PREVENTION
Symptom: Publish succeeds, but the package page on npmjs.com does not show a “Provenance” badge.
Root cause: The
--provenanceflag was present but the OIDC exchange silently failed and npm fell back to a plain publish in an older CLI version, or the package was published from a fork whose GitHub identity does not match the source repository.Fix: Pin npm CLI to 9.5+ (bundled with Node.js 18.17+/20+), and always publish from the canonical repository — provenance for forks is intentionally rejected since the Fulcio certificate encodes the actual repository identity.
Step 4 — Inspect the transparency log entry
Every provenance attestation is recorded in Sigstore’s Rekor log, independent of npm’s own infrastructure. Confirm the entry exists and matches your release:
npm view @acme/[email protected] dist.attestations.url
Expected output: a URL pointing to the attestation bundle, which references a Rekor log index that can be independently queried against https://search.sigstore.dev. A missing or empty field means the publish did not attach an attestation — treat that as equivalent to a failed provenance publish, even if npm publish reported success.
Tooling Validation
Consumers — and your own CI, when installing dependencies — verify provenance and registry signatures with a single built-in command:
npm audit signatures
Sample pass output:
audited 148 packages
148 packages have verified registry signatures
12 packages have verified provenance attestations
Sample failure output when an attestation cannot be validated:
1 package has an invalid or missing provenance attestation
@acme/[email protected] - could not verify attestation against Rekor log
Wire this into CI as a hard gate before deployment:
- run: npm ci
- run: npm audit signatures
A nonzero exit code from npm audit signatures fails the job, preventing a deploy from proceeding on unverifiable dependencies. See verifying package provenance with npm audit signatures for the full verification workflow and how to interpret partial-coverage results.
Compatibility Matrix
| Environment | --provenance flag |
OIDC token support | Transparency log lookup | Notes |
|---|---|---|---|---|
| npm 9.5–9.x | Yes | GitHub Actions only | Yes | First release supporting provenance |
| npm 10.x | Yes | GitHub Actions, GitLab CI | Yes | Adds GitLab CI OIDC support |
| npm 11.x | Yes | GitHub Actions, GitLab CI, others | Yes | Adds OIDC trusted publishing (token-free) |
| Node.js 18.17+ | Bundled npm supports it | — | — | Minimum for provenance without manual npm upgrade |
| Node.js 20+ / 22+ | Bundled npm supports it | — | — | Recommended for new pipelines |
| GitHub Actions | Yes | Native id-token: write |
Yes | Most common provenance environment |
| GitLab CI | Yes | Native id_tokens |
Yes | Requires CI_JOB_JWT-successor OIDC config |
| Local machine / unsupported CI | No | No | N/A | No OIDC issuer available; flag has no effect |
Guides in This Section
- Publishing with npm Provenance in GitHub Actions — the exact workflow permissions, registry configuration, and troubleshooting steps for the most common provenance setup.
- Verifying Package Provenance with npm audit signatures — how consumers and downstream CI pipelines confirm an installed dependency’s provenance before trusting it.
Related
- Automating npm Releases with GitHub Actions — the full release workflow that this provenance configuration slots into, including build-before-publish ordering.
- Configuring OIDC Trusted Publishing for npm — removing the stored
NPM_TOKENentirely by trusting the workflow identity directly. - Validating Packages Before Publish — run
publintandattwbefore the provenance-signed publish step so a broken package is never attested. - Mastering the package.json Exports Field — the
exportsmap whose files end up inside the tarball this attestation signs.