Back to publishing & release Automate semantic versioning Publish to the npm registry Harden the supply chain

Setting Up npm Provenance with GitHub Actions

npm provenance produces a signed, publicly verifiable attestation linking a published tarball to the exact source commit and CI workflow that built it. Consumers can confirm a package was built from the repository it claims, on a trusted CI runner, without taking the publisher's word for it. This page sets up provenance end to end from GitHub Actions: the OIDC permission, the publish flag, the sigstore attestation, and how to verify the result.

Why Provenance

A standard npm publish proves only that someone with a token uploaded a tarball. Provenance adds a cryptographically signed statement — anchored in the public sigstore transparency log — asserting this tarball was built from this commit by this workflow. That closes the gap between "the source on GitHub" and "the bytes on the registry," and it is a foundational building block for the broader controls in Supply-Chain Security Hardening.

Provenance attestation flow A GitHub Actions OIDC token is exchanged for a sigstore certificate that signs an attestation, which npm publishes alongside the tarball to the registry. OIDC token id-token: write sigstore signs attestation npm publish --provenance registry verified badge
GitHub's OIDC identity is exchanged for a short-lived sigstore certificate that signs the attestation npm publishes beside the tarball.

Prerequisites

  • The package is published to the public npm registry (provenance requires public publishing).
  • The repository is public, or you accept that provenance metadata references the repo.
  • You publish from GitHub Actions (the OIDC issuer npm trusts). The end-to-end publish mechanics are in npm Registry Publishing Workflows.
  • A recent npm CLI (provenance support landed in npm 9.5+; use Node 20's bundled npm or newer).

Numbered CI Setup

  1. Grant the workflow an OIDC token. Provenance is signed using GitHub's OIDC identity, which requires the id-token: write permission at the job (or workflow) level. Without it the publish aborts at the attestation step.
  2. Pin the registry in setup-node so the auth line is generated and the registry is unambiguous.
  3. Build deterministically before publishing so the attested artifact matches the committed source.
  4. Pass --provenance (or set publishConfig.provenance: true) on the publish step.
  5. Provide the publish token through NODE_AUTH_TOKEN, never echoed.

Manifest opt-in (optional but recommended)

Pinning provenance in the manifest means a local or alternate publish path cannot silently drop it:

{
  "publishConfig": {
    "access": "public",
    "provenance": true
  }
}

Full workflow

# .github/workflows/publish-provenance.yml
name: Publish with provenance
on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read
  id-token: write          # mandatory: lets npm fetch an OIDC token for signing

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/'
          cache: 'npm'

      - name: Install
        run: npm ci

      - name: Build
        run: npm run build

      - name: Verify tarball contents
        run: npm pack --dry-run

      - name: Publish with provenance
        run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

The id-token: write permission and the GitHub-hosted runner together let npm exchange GitHub's OIDC token for a short-lived sigstore signing certificate. The CLI then records a signed attestation (build metadata, source commit, workflow path) in the public transparency log and uploads it alongside the tarball.

Validation

After the workflow runs, confirm the attestation exists and verifies:

# Show the published version's metadata (provenance is recorded in the registry)
npm view @acme/widget

# Verify attestations for the installed package against sigstore
npm audit signatures

npm audit signatures checks both the registry signature and any provenance attestations for installed packages and reports how many verified. On the registry web UI, a package published this way shows a "provenance" / verified-build indicator linking back to the exact workflow run and commit.

Guardrails

  • Keep id-token: write scoped to the publish job only — do not grant it workflow-wide if other jobs do not need it.
  • Set publishConfig.provenance: true so provenance is the default and cannot be dropped by an ad-hoc publish.
  • Publish only from tag-triggered workflows on protected branches so the attested commit is always a reviewed, tagged release.
  • Pin action versions (@v4) and the Node version so the build environment recorded in the attestation is reproducible.
  • Layer provenance with the build-integrity controls in Adding SLSA Provenance to Package Releases for full supply-chain coverage.

Frequently Asked Questions

Why does my publish fail with a provenance error even though I added --provenance? The job almost certainly lacks the id-token: write permission. Provenance is signed with GitHub's OIDC identity, so without that permission npm cannot obtain the signing token and aborts before upload. Add permissions: id-token: write to the job.

Can I generate npm provenance from CI providers other than GitHub Actions? npm provenance currently trusts a specific set of OIDC issuers, with GitHub Actions and GitLab CI being the supported, documented paths. From an unsupported runner the attestation cannot be produced; publish from a supported provider instead.

Does provenance work for private or scoped-restricted packages? No. Provenance requires public publishing, because the attestation is recorded in a public transparency log. For a scoped package, ensure access is public; see Publishing Scoped Packages to npm.

How do consumers verify the provenance of a package they installed? They run npm audit signatures, which validates registry signatures and provenance attestations for installed packages against sigstore and reports how many verified. The registry web UI also surfaces a verified-build indicator linking to the originating commit and workflow run.

Related

npm Registry Publishing Workflows