npm provenance statements are now a baseline expectation for packages distributed by serious teams. If you ship a library on the public registry and you still do not attach provenance, your users are correct to downgrade their trust. But the feature has grown real rough edges since it shipped, and most of the problems I see in audits are not with Sigstore or with the registry; they are with how teams wire the feature into their pipelines.
What does an npm provenance statement actually prove?
A provenance statement proves that a specific tarball was built by a specific workflow, in a specific repository, at a specific commit, on a specific CI runner, using the OIDC identity that CI presented at build time. It is a signed SLSA-style attestation produced by the Sigstore public-good instance, stored in the Rekor transparency log, and surfaced on the registry as a small badge and a JSON blob you can fetch from the package metadata.
What it does not prove is that the source code is trustworthy, that the maintainer is trustworthy, that the build script is free of malicious steps, or that dependencies pulled during the build are what you think they are. It is a chain-of-custody artifact, not a safety certificate. Treating it as a safety certificate is the single most common mistake I see teams make when they adopt it.
How do you publish provenance from a real CI pipeline?
The canonical path is npm publish --provenance running in GitHub Actions or GitLab CI with OIDC enabled and the id-token: write permission granted to the job. The CLI handles the OIDC exchange, requests a short-lived Fulcio certificate, signs the tarball digest, submits to Rekor, and uploads the bundle alongside the package.
The parts that go wrong in practice are almost always environmental. Monorepos that publish multiple packages from a single workflow often forget to scope the OIDC token correctly, which either fails noisily or, worse, succeeds with a provenance statement that points at the wrong subpath. Workflows that build in one job and publish in another lose the provenance guarantees because the signer has no visibility into what happened in the build job. And workflows that use npm pack followed by npm publish on the resulting tarball sometimes skip provenance silently because the flag only applies when the CLI is the one producing the artifact.
The version of npm matters too. Older 9.x releases have known bugs around provenance in monorepos, and a surprising number of projects still pin those versions in their CI. If you are publishing from CI, you want npm 10.2 or newer, full stop.
When is a provenance statement actively misleading?
A provenance statement is misleading any time the subject of the attestation is not what you think it is. The most common variant is the "published from a fork" problem: a maintainer accepts a pull request, the bot-driven publish workflow runs against the merged commit, and the provenance points at a repository and commit that look legitimate but contain code from an outside contributor. Nothing about the attestation is wrong; it correctly says "this tarball was built from commit X in repo Y." The user's mental model is wrong: they assumed provenance implied human review.
A second variant is the "trusted builder, untrusted inputs" problem. Your CI runner is authentic, your workflow file is authentic, and the commit hash is the one in your repository. But the workflow ran npm install during the build, and one of the transitive dependencies was compromised that morning. The resulting tarball carries a valid provenance statement that tells you exactly which poisoned build produced it. This is genuinely useful for incident response, but it is not the defense a lot of engineering managers think it is.
Should consumers verify provenance at install time?
Yes, for packages you care about. No, not for every package. The npm CLI added npm audit signatures and, more recently, stricter verification flags that reject installs when provenance is missing or fails to verify. Turning these on globally in a large organization is a path to outages because too many popular packages still do not publish provenance and probably will not for another year.
The pragmatic pattern is a curated allowlist. Pick the 50 to 200 packages that you treat as high-trust load-bearing dependencies, require provenance for those, and let the long tail slide with normal SCA controls. Enforce the allowlist in CI rather than on developer laptops; laptop enforcement creates friction without meaningfully changing your threat model, because the attacker is targeting your build system, not the developer writing a feature branch.
What about Sigstore instance failures and transparency log outages?
Sigstore's public-good instance has had partial outages in the last eighteen months, and each one produced a flood of publish failures across the ecosystem. If your release process is blocking on npm publish --provenance and Rekor is degraded, you cannot ship. Teams with strict SLAs have started running mirrored private Fulcio and Rekor instances for their internal releases and only attaching public provenance as a secondary step. For most open-source publishers this is overkill, but if you are shipping paid libraries with contractual release windows, it is worth the operational overhead.
A smaller but more frequent failure is clock skew on self-hosted runners. Sigstore is strict about certificate validity windows, and a runner that is 90 seconds off from NTP will fail verification in ways that look like network errors. This one eats hours of debugging time every month across teams I talk to.
How should you think about provenance on private registries?
The three major private registries, Artifactory, Sonatype, and GitHub Packages, all handle npm provenance differently, and none of them handle it perfectly. Artifactory will proxy provenance bundles if you enable the right feature flag, but older versions strip them silently. Sonatype's npm support is catching up but still lags the public registry by about a release cycle. GitHub Packages stores provenance natively for packages published from GitHub-hosted workflows, which is the cleanest experience if you are already in that ecosystem.
If you operate an internal registry, the important design question is whether you re-sign proxied packages with your own attestations or pass through the upstream ones. Both are defensible. Passing through preserves the upstream chain of custody, which is auditable but ties your security posture to the public Sigstore instance. Re-signing lets you enforce an internal policy boundary but means your consumers lose visibility into the original publisher. I generally recommend passing through and adding an internal attestation layer rather than replacing the upstream one.
How Safeguard.sh Helps
Safeguard.sh ingests npm provenance statements, verifies them against Rekor and Fulcio, and correlates the workflow identity against your own allowlist of trusted repositories and publishers. We flag packages that silently dropped provenance, packages whose provenance points at unexpected build infrastructure, and transitive dependencies pulled during builds that would undermine the attestation's guarantees. The goal is to move provenance from a green badge into an enforceable policy control, so your teams can ship with confidence rather than guess whether the signature means what they hoped it meant.