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

Sigstore Provenance Signing Flow A left-to-right flow showing the GitHub Actions runner requesting an OIDC token, exchanging it with Sigstore's Fulcio authority for a short-lived signing certificate, signing the tarball digest, and recording the attestation in the Rekor transparency log before the registry stores it against the published version. GitHub Actions runner (id-token: write) OIDC token short-lived, run-scoped Fulcio CA issues signing cert npm CLI signs digest Rekor transparency log public, append-only entry npm registry stores attestation link Consumers verify with: npm audit signatures

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 --provenance fails with an error indicating no OIDC token is available, even though the workflow ran successfully.

Root cause: id-token: write was 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 forwards permissions: inherit if 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 --provenance flag 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



Back to CI/CD, Publishing & npm Provenance