Programmatic access to npm package publish settings. Bulk-update publishing access, trusted publishers, and maintainers across all your packages.
npm now requires trusted publishing via OIDC and has revoked all classic tokens, enforcing 2FA and 90-day token expiry by default. Every package needs its GitHub/GitLab repo linked as a trusted publisher and its publishing access updated.
As of npm v11.10.0, npm trust can configure trusted publishers from the CLI. However, it requires two-factor authentication (with a 5-minute skip window) and recommends adding a 2-second sleep between calls to avoid rate limiting. There's also still no CLI or API for updating publishing access settings or managing maintainers in bulk.
This package automates all of it — trusted publishers, publishing access, and maintainers — with automatic 2FA handling. Log in once, then script changes across all your packages.
Caution
This package requires your TOTP secret to automatically generate 2FA codes. This fundamentally bypasses the security of two-factor authentication — anyone with access to your secret can generate valid codes. Only use this in a trusted environment, never commit your secret to version control, and understand that you are trading 2FA security for automation convenience.
TOTP secret — The base32 secret key used to generate your npm 2FA codes. If you already know your secret, you can use it directly. Otherwise, this package includes a helper command to extract it from a Google Authenticator QR code screenshot or migration URL:
npx npm-pkg-settings decode-secret ./qr-code.png
npx npm-pkg-settings decode-secret 'otpauth-migration://offline?data=...'npm install npm-pkg-settingsThe CLI automatically loads a .env file from the current directory if it exists:
NPM_OTP_SECRET=YOUR_TOTP_BASE32_SECRET
NPM_USERNAME=your-npm-username
NPM_PASSWORD=your-npm-passwordTip
NPM_USERNAME and NPM_PASSWORD are used to log in and create a session. Once a session exists, they can be omitted — only NPM_OTP_SECRET is needed for subsequent runs.
List all packages for the authenticated user.
npx npm-pkg-settings list
npx npm-pkg-settings list --sort dateView package settings (publishing access, trusted publishers, maintainers).
npx npm-pkg-settings view my-packageUpdate package settings. Supports multiple changes in a single command.
npx npm-pkg-settings update my-package --publishing-access strict
npx npm-pkg-settings update my-package --trusted-publisher 'github:owner/repo?workflow=release.yml'
npx npm-pkg-settings update my-package --add-maintainer some-userCombine multiple changes:
npx npm-pkg-settings update my-package \
--publishing-access strict \
--trusted-publisher 'github:owner/repo?workflow=release.yml'Skip confirmation with -y:
npx npm-pkg-settings update my-package --publishing-access strict -yNote
For GitHub trusted publishers, the command validates via the GitHub API that the repository and workflow file exist before applying. If the repo is public and the workflow file is missing, it errors. Private repos show an "unverified" warning since they can't be checked without auth.
Example output:
$ npx npm-pkg-settings update type-flag \
--trusted-publisher 'github:privatenumber/type-flag?workflow=release.yml' \
--publishing-access strict
✔ Already logged in
Changes to type-flag:
Trusted publisher
Provider GitHub Actions
Repository privatenumber/type-flag ✔
Workflow release.yml ✔
Publishing access
2FA required, disallow tokens
Apply changes? (y/N) y
✔ Linked trusted publisher
✔ Set publishing access
https://www.npmjs.com/package/type-flag/access
Generate a one-time password from your TOTP secret. Handy when using the npm website and you need to enter a 2FA code.
npx npm-pkg-settings otpExtract the TOTP secret from a Google Authenticator QR code image or migration URL.
npx npm-pkg-settings decode-secret ./qr-code.png
npx npm-pkg-settings decode-secret 'otpauth-migration://offline?data=...'import { createClient } from 'npm-pkg-settings'
const npm = createClient({
username: process.env.NPM_USERNAME,
password: process.env.NPM_PASSWORD,
otpSecret: process.env.NPM_OTP_SECRET
})
// Logs in if needed, skips if session is already valid
await npm.login()The session is saved to .npm-pkg-settings.cookies.json, so subsequent runs reuse it without re-authenticating.
Store credentials in a .env file:
NPM_USERNAME=your-npm-username
NPM_PASSWORD=your-npm-password
NPM_OTP_SECRET=YOUR_TOTP_BASE32_SECRETThen run your script with the --env-file flag:
node --env-file=.env my-script.tsconst packages = await npm.listPackages()
for (const pkg of packages) {
console.log(pkg.name, pkg.version, pkg.lastPublishRel)
}const settings = await npm.getPackageAccess('my-package')
console.log(settings.publishingAccess) // 'tfa-required-unless-automation'
console.log(settings.oidcConnections) // trusted publishers
console.log(settings.maintainers) // [{ name, permissions }]await npm.setPublishingAccess('my-package', 'tfa-always-required')import { validateTrustedPublisher } from 'npm-pkg-settings'
// GitHub Actions
const publisher = {
type: 'github',
owner: 'my-org',
repository: 'my-package',
workflow: 'release.yml'
}
// Optionally validate before linking (GitHub only) — uses the public GitHub API
// to check that the repo and workflow file exist. Throws if the repo is public but
// the workflow is missing. Silently passes for private/inaccessible repos.
const { repoVerified, workflowVerified } = await validateTrustedPublisher(publisher)
await npm.linkTrustedPublisher('my-package', publisher)
// GitLab CI/CD
await npm.linkTrustedPublisher('my-package', {
type: 'gitlab',
namespace: 'my-group',
project: 'my-package',
ciFilePath: '.gitlab-ci.yml'
})await npm.addMaintainer('my-package', 'npm-username')import { setTimeout } from 'node:timers/promises'
import { createClient } from 'npm-pkg-settings'
const npm = createClient({
username: process.env.NPM_USERNAME,
password: process.env.NPM_PASSWORD,
otpSecret: process.env.NPM_OTP_SECRET
})
await npm.login()
const packages = await npm.listPackages()
for (const pkg of packages) {
// npm recommends a 2s sleep between calls to avoid rate limiting
await setTimeout(2000)
const settings = await npm.getPackageAccess(pkg.name)
if (!settings.repository?.includes('github.com/my-org/')) {
continue
}
const repoName = settings.repository.split('/').pop()
if (settings.publishingAccess !== 'tfa-always-required') {
await npm.setPublishingAccess(pkg.name, 'tfa-always-required')
}
const hasPublisher = settings.oidcConnections.some(
c => c.repositoryName === repoName
)
if (!hasPublisher) {
await npm.linkTrustedPublisher(pkg.name, {
type: 'github',
owner: 'my-org',
repository: repoName,
workflow: 'release.yml'
})
}
}Returns an NpmClient.
| Option | Type | Description |
|---|---|---|
otpSecret |
string |
TOTP secret (base32) for 2FA |
username |
string? |
npm username (required for login()) |
password |
string? |
npm password (required for login()) |
sessionFile |
string? |
Session file path (default: .npm-pkg-settings.cookies.json) |
| Method | Description |
|---|---|
login() |
Logs in if session is expired, skips if already authenticated |
listPackages() |
Returns all packages for the authenticated user |
getPackageAccess(name) |
Returns package settings (publishing access, trusted publishers, maintainers) |
setPublishingAccess(name, access) |
Sets publishing access (see values below) |
linkTrustedPublisher(name, publisher) |
Links a GitHub Actions or GitLab CI/CD trusted publisher |
addMaintainer(name, username) |
Adds a maintainer to the package |
getUsername() |
Returns the authenticated user's npm username |
| CLI flag | API value | npm UI label |
|---|---|---|
strict |
tfa-always-required |
Require 2FA and disallow tokens (recommended) |
default |
tfa-required-unless-automation |
Require 2FA or granular token with bypass |