To publish an npm package with provenance, build in a supported cloud CI system (GitHub Actions or GitLab CI/CD) on a cloud-hosted runner, use npm 9.5.0 or later, give the job an identity token, and publish with the provenance option. Consumers can then check the attestation.
What does npm provenance prove?
It links a published package to the source and build instructions it came from. The npm docs are careful about the limit: provenance does not guarantee a package is free of malicious code. It gives you something to audit.
Treat it as a claim about where and how the package was built, signed by the CI provider's identity. A reader can check it. They still have to decide whether to trust the build.
What do you need first?
From the npm documentation:
- npm CLI 9.5.0 or later.
- A public repository field in package.json that matches the source you publish from, case sensitive.
- A supported cloud CI/CD provider, with a cloud-hosted runner. The page does not explicitly address self-hosted runners, but its wording excludes them.
- Awareness that statements go to a public transparency log, and the docs ask you to review the Linux Foundation notice about it.
How does it work in GitHub Actions?
Grant the job id-token: write permission, along with contents: read, and run on a GitHub-hosted runner. The publish command is:
npm publish --provenance --access public
The --access public flag is for a package's first publish. The example workflow runs npm ci first and sets NODE_AUTH_TOKEN from a secret.
If you publish through a third-party tool, any of these turns provenance on: the NPM_CONFIG_PROVENANCE environment variable set to true, "provenance": true inside a publishConfig block in package.json, or provenance=true in an .npmrc file. Yarn needs 4.9.0 or later.
And in GitLab CI?
Add an id_tokens entry named SIGSTORE_ID_TOKEN with aud set to sigstore, run the job on tags, keep your token in an NPM_TOKEN variable, and run the same publish command.
The docs also mention trusted publishing, where provenance is generated automatically and the flag is unnecessary. We did not read the trusted publishers page, so check it for setup steps.
How does a consumer verify it?
Run npm audit signatures. It checks attestations on downloaded packages. The docs add that attestation formats may change, so keep the npm CLI current.
What goes wrong in practice?
A constructed example: a maintainer renames the GitHub repository but package.json still points at the old name. The repository field no longer matches the source, and publishing with provenance fails until they are aligned.
Another trap: a long-lived NPM_TOKEN sits in CI. Provenance says where the package was built; it does not protect that token. If an attacker steals the token and publishes from elsewhere, the package simply lacks provenance. That absence is only useful if consumers look for it.
What to do
- Upgrade to npm 9.5.0 or later in CI.
- Fix the repository field in package.json to match the repository exactly.
- Add
id-token: writeandcontents: readto the publish job, and use a hosted runner. - Publish with the provenance option and
--access publicon the first release. - Limit the publish job to tags or a protected branch.
- Check the package page for the provenance badge, then run
npm audit signaturesin a clean project. - Look at trusted publishing to retire the stored token.
- Tell your consumers how to verify, and treat a missing attestation on a new release as a reason to stop and ask.
Safeguard can scan the dependencies you consume, and Gold open source lookups help you check a package before adding it. Provenance on your own releases is a separate duty.
Quick check
Does provenance mean an npm package is safe?
No. The npm docs say it does not guarantee a package is free of malicious code. It shows the source and build instructions.
Can I use a self-hosted runner?
The docs require a supported cloud CI/CD provider with a cloud-hosted runner, which excludes self-hosted ones by their wording. Check the current page before relying on it.
Sources
- npm docs: generating provenance statements (fetched 18 October 2026)
Last reviewed: 18 October 2026.