---
title: npm-stage
section: 1
description: Stage packages for publishing
---

### Synopsis

<!-- AUTOGENERATED USAGE DESCRIPTIONS -->

### Description

Staged publishing allows package maintainers to require proof-of-presence
for all publishes. Proof-of-presence is where a human is involved,
interjects, and provides authentication (2FA) during an action — in this
case, publishing an npm package.

Typically when maintainers use automated workflows to publish,
proof-of-presence is lacking as there's no convenient way to interject the
process and provide 2FA, as is the case for publishing with a granular
access token with bypass and the trusted publishing flow. Staged publishing
allows users to have their automated workflows stage a package without a 2FA
prompt, deferring the act of 2FA, allowing the maintainer to approve the
staged package and publish at a later point.

The `npm stage publish` command packs the current working directory and
places that version of the package into the registry in a state where it's
not available for public access, allowing maintainers to approve the package
at a later point in time. The act of staging does not prompt for 2FA and can be done with any token
type, the act of approving will.

Key behaviors:

* Staged packages share the same semver version unique index as published
  packages — you cannot publish a version that already exists as a staged
  version for that package.
* You can still publish packages normally while you have staged packages
  pending.
* You can stage multiple versions of the same package.
* `npm stage publish` has parity with `npm publish` and will respect
  `"private": true` in `package.json`, refusing to stage the package.

### Prerequisites

Before using `npm stage` commands, ensure the following requirements are met:

* **Write permissions on the package:** You must have write access to the
  package you're configuring.
* **Package must exist:** The package you're configuring must already exist
  on the npm registry.
* **2FA enabled on your account:** Commands that require 2FA will prompt you
  to authenticate. If you don't already have 2FA enabled on your account,
  you must enable it before using these commands.

### Subcommands

* `npm stage publish [<package-spec>]` - Stage a package for publishing
* `npm stage list [<package-spec>]` - List all staged package versions
* `npm stage view <stage-id>` - View details of a specific staged package
* `npm stage approve <stage-id>` - Approve a staged package for publishing
* `npm stage reject <stage-id>` - Reject a staged package
* `npm stage download <stage-id>` - Download the tarball for inspection

### 2FA Requirements by Subcommand

| Command | Requires 2FA | Notes |
| --- | --- | --- |
| `npm stage publish` | No | Designed for automated workflows; defers 2FA to approval |
| `npm stage list` | No | View staged packages |
| `npm stage view` | No | View staged package details |
| `npm stage approve` | Yes | Prompts for 2FA to publish the staged package |
| `npm stage reject` | Yes | Prompts for 2FA to permanently remove the staged package |
| `npm stage download` | No | Downloads the tarball for local inspection |

### Tag Behavior

The `--tag` flag follows the same logic as `npm publish`. If no tag is
provided, the `latest` tag is used by default. For pre-release versions
(e.g., `1.0.0-beta.1`) and non-latest semver versions, the tag must be
explicitly provided — otherwise the CLI will error, just as `npm publish`
would.

The tag is an immutable property of the staged package. Once a package is
staged with a given tag, the tag cannot be changed. If you need to stage the
same version with a different tag, you must first reject the existing staged
package using `npm stage reject` and then re-stage it with the desired tag.

### Token Behavior

The key difference with staged publishing is that `npm stage publish` never
requires a 2FA prompt, regardless of token type. This is what makes it
suitable for automated workflows. The goal of `npm stage publish` is
deferring proof-of-presence to a later point in time.

| Token Type | `npm stage publish` | `npm publish` |
| --- | --- | --- |
| GAT with bypass | Can stage | Can publish (if allowed by package publishing access) |
| GAT without bypass | Can stage | 2FA prompt (if allowed by package publishing access) |
| Session token | Can stage | 2FA prompt |
| Trust token (OIDC) | Can stage (if allowed) | Can publish (if allowed) |

### Trust Relationship Permissions

With staged publishing, trust relationships now support granular command
permissions. Shortlived tokens issued through trust relationships can only be
used with `npm stage publish` and `npm publish`. Shortlived tokens cannot run
`npm stage` subcommands.

`npm trust <provider>` supports `--allow-publish` and `--allow-stage-publish`
to control which commands are available through each trust relationship.

### Best Practices

**Note:** The addition of staged publishing does not make your account or org
more secure. Maintainers must still use the best practices listed below.

1. **Delete Granular Access Tokens (GAT) with bypass 2FA enabled.**
   Now with staged publishing, we've eliminated the need for a GAT token
   that can bypass 2FA. We encourage you to delete all your tokens with
   bypass enabled and switch to using a trust relationship in your automated
   workflows, or create a GAT without bypass and use `npm stage publish`.

2. **Disallow tokens from publishing at the package level.**
   All packages have their own access controls under "package access"
   allowing packages to be published with bypass tokens, which is no longer
   a necessity. We encourage you to select "Require two-factor
   authentication and disallow tokens (recommended)" for all your packages
   on the package access page.

3. **Configure trust relationship permissions to prevent `npm publish`.**
   We encourage you to only enable `npm stage publish` on your trust
   relationships and disable `npm publish`.

### Configuration

<!-- AUTOGENERATED CONFIG DESCRIPTIONS -->

### See Also

* [npm publish](/commands/npm-publish)
* [npm unpublish](/commands/npm-unpublish)
* [npm trust](/commands/npm-trust)
