A Small Mechanic With Large Implications
On May 22, 2026, GitHub shipped staged publishing for npm as a generally-available feature in npm CLI v11.15.0. The change is small in surface area and large in practical effect: a publish command in CI no longer pushes a package live. It pushes the package into a staging queue, where a human maintainer must complete a 2FA challenge to release it.
A stolen npm automation token, a leaked OIDC credential, a compromised GitHub Actions workflow secret, none of these can satisfy that 2FA gate. They can stage a publish. They cannot finalize one.
This is the most direct registry-level response we have seen to the supply chain wave of the last six months: the mini Shai-Hulud worm hidden in Microsoft's durabletask PyPI package, the recent TeamPCP campaign that breached GitHub via Nx Console, the TanStack-rooted GitHub workflow token leak that hit Grafana, and the RubyGems flood that pushed 500+ malicious gems in 48 hours. Every one of those attacks ends in the same place: an attacker holding valid-but-stolen publishing credentials, ready to push poison upstream.
Staged publishing makes those credentials, at the final mile, useless.
How It Works
The new flow lives in the npm CLI as a stage subcommand family:
# Push to the staging queue instead of going live
npm stage publish
# List pending staged versions
npm stage list
# Inspect a staged version before approval
npm stage view <package>@<version>
# Approve a staged version (requires interactive 2FA challenge)
npm stage approve <package>@<version>
# Reject a staged version
npm stage reject <package>@<version>
npm stage publish accepts the same authentication methods as npm publish: long-lived tokens, granular access tokens, and OIDC trusted publishing. Anything that can publish today can stage today. The asymmetry sits on the approve side: npm stage approve and npm stage reject require an interactive 2FA challenge and refuse to authenticate via OIDC or any granular access token.
This is the load-bearing piece. CI can stage. Only a human present with a TOTP code or hardware key can finalize.
Stage-Only Mode
The cleanest configuration pairs staged publishing with npm trusted publishing (OIDC) and configures the package as stage-only. When that flag is on, any direct npm publish call against the package is rejected at the registry. Only npm stage publish is accepted.
Stage-only is what makes the mechanism a real defense. Without it, a careless or compromised CI job could still call npm publish directly and bypass the staging queue entirely. With it, the only path to the registry is through the queue, and the only path past the queue is through 2FA.
This is the configuration any maintainer of a high-impact package should adopt within the next quarter.
Companion Install-Time Controls
The same release ships three new install-side flags that pair naturally with staged publishing:
--allow-fileenables consuming local tarball paths.--allow-remoteenables consuming remote URL tarballs (HTTPS).--allow-directoryenables consuming local directories.
The default behavior across all three becomes deny-by-default. Production environments and CI runners that should only consume from the registry can now refuse any other source, eliminating the "I accidentally installed from a random GitHub tarball URL" class of supply chain mistake.
This closes a real attack pattern: poisoned tarballs delivered via README links, malicious "install from this URL" instructions, and compromised forks that ask developers to install from non-registry sources.
The Opt-In Caveat
Staged publishing is generally available, but it is opt-in. Existing npm publish workflows continue to work unchanged. The packages that benefit are the ones whose maintainers explicitly adopt the new flow by updating CI to npm stage publish and, ideally, enabling stage-only enforcement.
This is the right call for backward compatibility, but it means the registry as a whole does not automatically benefit. Every high-impact package that does not adopt staged publishing is still publishable by anyone with a stolen automation token, the same as last week.
Expect this to remain opt-in for somewhere between 6 and 18 months while adoption stabilizes, and expect npm and GitHub to eventually flip the default for packages above a download threshold once enough of the ecosystem is comfortable with the staging flow. The trajectory is clear.
CI Adoption, What Actually Changes
For a single-package repository, the migration is two lines. Replace:
- run: npm publish --access public
With:
- run: npm stage publish --access public
The CI workflow finishes. The package sits in the staging queue. A maintainer with phone in hand opens the npm web UI or runs npm stage approve locally, completes the 2FA challenge, and the package goes live.
For monorepos publishing many packages per release (changesets, lerna, Nx, Turborepo with publish workflows), the adoption is more involved. Each staged package needs an approval. A 12-package release becomes 12 approvals. Some maintainer teams will want a "bulk approve" UX that npm does not yet expose; for now the practical answer is either to script npm stage approve calls behind an interactive 2FA wrapper or to accept that release nights take a few extra minutes.
For automated release tooling (release-please, semantic-release, changesets), each one needs to be reviewed for how it expects npm publish to behave. Most assume synchronous publish-and-done. Staged publishing breaks that assumption gracefully (the stage call returns success), but downstream notification steps that expect the package to be live immediately will need to be moved to a post-approval workflow or tolerated as eventually-consistent.
What Staged Publishing Does Not Fix
Adopting staged publishing closes the "stolen automation token publishes a poisoned version" attack. It does not close several adjacent attacks:
- A compromised developer machine, where the attacker can wait for the maintainer to legitimately stage a version and then trick them into approving a backdoored variant.
- Account takeover of the maintainer themselves, where the attacker holds the 2FA second factor and can complete the gate.
- Compromise of a high-trust dependency that is consumed downstream. Staged publishing protects the package author, not their dependencies.
- Build-time tampering inside the CI pipeline between source code and the staged tarball.
The defensive principle remains the same as ever: defense in depth. Staged publishing makes the cheapest, most common attack significantly harder. The expensive, more targeted attacks are still expensive and still targeted, but the bar moves up.
What To Do This Week
For any team that publishes packages to npm:
- Update CI runners to npm CLI 11.15.0 or later. This is the only version that supports the new
stagesubcommands. - Audit which of your packages are high-impact. Packages that are direct dependencies of customer-facing applications, packages published from CI, packages consumed by other teams' CI: these are the targets that justify staged publishing first.
- Migrate CI to
npm stage publishfor those packages and define who on the team holds approval rights. - Configure stage-only mode through npm trusted publishing once the team is comfortable with the flow.
- Document an approval runbook: who approves on weekdays, who approves during incidents, what the rollback path is if a staged version is approved by mistake.
For teams that consume packages, switch CI to deny-by-default install sources and add explicit --allow-file, --allow-remote, or --allow-directory flags only where genuinely required.
Bottom Line
The cheapest mass-volume supply chain attack of the last year has been: steal an automation credential, publish a poisoned package version, harvest downstream environments. Staged publishing makes that mechanic stop working for any package that adopts the new flow.
It does not stop a determined attacker who can take over a maintainer account or compromise a developer's 2FA hardware. It does stop the long tail of stolen-token mass attacks that defined Shai-Hulud, TeamPCP, the TanStack-rooted Grafana breach, and the RubyGems flood. That is a meaningful narrowing of the threat surface, and the adoption work for most projects is one CI line and a runbook.
If your team publishes packages at any scale, our security and compliance work covers the migration: CI changes, OIDC trusted publishing setup, stage-only enforcement, approval runbook, and an inventory of which long-lived tokens you can stop minting next quarter.
Want to learn more?
Get in touch with our team to discuss how we can help your infrastructure.
Related News
How a TanStack npm Compromise Got Grafana's GitHub Codebase Stolen
Grafana Labs confirmed that attackers downloaded source code from its GitHub environment after a TanStack npm package compromise leaked one developer's GitHub workflow token. One token missed in the rotation, in one of the better-instrumented companies on the internet.
SecurityTeamPCP Breaches GitHub via Poisoned Nx Console Extension
TeamPCP exfiltrated about 3,800 GitHub-internal repositories after a poisoned Nx Console VS Code extension reached a GitHub employee. The full supply-chain chain, and what to do.
SecurityMini Shai-Hulud Worm Hits Microsoft's durabletask PyPI
TeamPCP's Mini Shai-Hulud worm backdoored durabletask v1.4.1-1.4.3 on PyPI, stealing AWS, GitHub and Vault secrets and spreading via SSM and kubectl exec.