Npm publishing best practices
Skill schalkneethling/claude-toolkit/skills/npm-publishing-best-practices
CLI for managing Agent commands, hooks, and skills across projects.
npx -y skills add schalkneethling/claude-toolkit --skill npm-publishing-best-practicesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Audit and improve the security posture of npm package publishing: account security, npm trusted publishing strategy, GitHub repository hardening, token removal, release governance, dependency update policy, provenance, and supply-chain risk. Use when the user asks about npm publishing best practices, publishing security, OIDC/trusted publishing strategy, npm token hygiene, release automation posture, Changesets versus changelog strategies, publint, provenance, or auditing an existing publishing pipeline. For writing or debugging the concrete GitHub Actions publish workflow file, use the npm-trusted-publishing-github-workflow skill instead.
SKILL.md
17.4 KB, as published. Nobody here has run it
npm Package Publishing — Best Practices
Based on the e18e publishing guide. Reference it for the canonical source; this skill distils the actionable steps.
Agent Workflow
- Inspect the repository before recommending changes:
package.json- lockfiles and
packageManager .npmrc,.yarnrc.yml,.github/workflows/*- release tooling such as Changesets, changelogithub, semantic-release, or release-it
- existing npm/GitHub tokens or
NODE_AUTH_TOKENusage in workflows
- Classify the request:
- Audit: report risks by severity, with file and line references where possible.
- Implementation: make scoped repository changes, then run relevant validation.
- Strategy: explain tradeoffs and identify user-only settings.
- Separate agent-doable work from user-only UI work. Do not claim account, npmjs.com, or GitHub settings are configured unless verified by authenticated tool/API access.
- For concrete GitHub Actions publish workflow creation or CI failure debugging, hand off to the
npm-trusted-publishing-github-workflowskill.
Freshness Rule
Before changing trusted publishing requirements, supported CI providers, npm CLI minimums, Node.js minimums, provenance behavior, or GitHub release/security settings, verify the current official npm and GitHub documentation when browsing is available. These requirements change over time.
Responsibility Split
Agent can usually:
- Edit
.github/workflows/*. - Add or update repository package-manager config such as
.npmrcor.yarnrc.yml. - Remove
NODE_AUTH_TOKENusage from publish steps when trusted publishing is used. If private dependencies require registry auth during install, keep that token scoped to read-only install steps only. - Add Dependabot/Renovate config.
- Run
publint, workflow linting, tests, package builds, and other local validation. - Report exact user steps for npm/GitHub settings.
User or authenticated UI/API access required:
- Enable npm/GitHub 2FA.
- Configure npm trusted publisher settings on npmjs.com.
- Set package publishing access to require 2FA and disallow tokens.
- Enable GitHub repository/org Actions restrictions.
- Configure branch/tag rulesets and immutable releases.
- Remove repository/org secrets when the agent lacks GitHub settings access.
Non-Negotiables
- Do not add
NODE_AUTH_TOKENto publish steps when trusted publishing is available. Use OIDC for publishing; if private dependencies require install auth, use a read-only token only for the install step. - Do not store npm publish tokens in GitHub Actions secrets for OIDC-capable publishing.
- Do not run install lifecycle scripts in release workflows unless the user explicitly accepts the risk.
- Do not show tag-pinned actions as compliant with SHA pinning; resolve all third-party and
GitHub-owned actions to full commit SHAs. Tag-based refs such as
@v6or@v7weaken supply-chain integrity and violate pinned-action policy. - Do not create or edit GitHub Actions workflows without explicit least-privilege
permissions:forGITHUB_TOKEN. Set a restrictive top-level default such ascontents: read, then grant elevated scopes only on the jobs that need them. - Do not use
actions/checkoutwith default credential persistence. Always setpersist-credentials: false; if a job must push to git, provide an explicit, narrowly scoped credential only for that push step. - Do not say npm/GitHub account settings are complete unless they were actually checked.
- Do not use self-hosted runners for npm trusted publishing unless official npm docs currently support them.
Package manager note. The example install and configuration commands in this skill use
npmto match the e18e source material, but should be adapted to whichever package manager the project already uses —pnpm,yarn,bun, etc. Adapt commands accordingly:
npm pnpm yarn npm ci --ignore-scriptspnpm install --frozen-lockfile --ignore-scriptsyarn install --frozen-lockfile --ignore-scriptsnpm i -g npmpnpm add -g pnpmyarn set version stableignore-scripts=truein.npmrcignore-scripts=truein.npmrcenableScripts: falsein.yarnrc.ymlDetect the project's package manager by checking for a lockfile (
pnpm-lock.yaml,yarn.lock,bun.lockb) or apackageManagerfield inpackage.jsonbefore generating any commands or workflow steps.Section 2.2's trusted-publishing requirement is npm-specific: the publish step must run with a supported Node.js version and npm CLI version even when the rest of the workflow uses another package manager.
For pnpm projects on GitHub Actions, prefer the Marketplace action
pnpm/setupto set up pnpm and the JavaScript runtime in one step. It installs pnpm from@pnpm/exe, can readdevEngines.runtimefrompackage.json, and replaces the separatepnpm/action-setup+actions/setup-nodepattern. In release workflows, pinpnpm/setupto a full commit SHA, setinstall: false, and run the explicit hardened install command yourself so flags like--frozen-lockfile --ignore-scriptsremain visible.
1 · Prerequisites
1.1 · Enforce 2FA everywhere
| Account | Location | Recommended method |
|---|---|---|
| npm | Account → Security | Security key (YubiKey, Touch ID, Windows Hello); fallback: authenticator app |
| GitHub | Settings → Password and authentication | Same priority order |
Use a password manager with generated passwords for both accounts.
1.2 · Harden GitHub Actions settings
Settings → Actions → General:
- ✅ Require actions to be pinned to a full-length commit SHA
- ✅ Require approval for first-time contributors
- ✅ Set default workflow permissions to "Read repository contents and packages"
If the repository belongs to an organisation, apply these settings at org level for consistency across all repositories.
1.3 · Configure branch protection
Settings → Branches → create a ruleset for main:
- ✅ Require a pull request before merging
- ✅ Require at least 1 approval
- ✅ Dismiss stale approvals when new commits are pushed
- ✅ Require approval of the most recent reviewable push
1.4 · Remove legacy npm tokens
Settings → Secrets & Variables → Actions: remove any stored npm tokens. OIDC trusted publishing
replaces them entirely — no long-lived secrets needed in the repository.
2 · Trusted Publishing (OIDC)
Trusted publishing means GitHub Actions authenticates directly with npm via OIDC — no npm token ever touches the repository.
2.1 · Configure on npmjs.com
- Open your package page → Settings tab → Trusted Publishing section.
- Add a trusted publisher:
- Organisation / user: your GitHub org or username
- Repository: the repository name
- Workflow filename: e.g.
publish.yml
- Check "Require two-factor authentication and disallow tokens" — this forces manual publishes to use 2FA as well.
For bulk configuration across many packages, use
open-packages-on-npmto open each package in a new tab, then the npm-trusted-publisher userscript to configure them rapidly.
2.2 · npm CLI version requirement
The publish step must use npm CLI ≥ 11.5.1 for automatic OIDC trusted publishing. Node.js 22.14.0 or newer can use trusted publishing when the workflow installs npm CLI 11.5.1 or newer before publishing:
- run: npm i -g npm
3 · Standard Publish Workflow
Use the e18e setup-publish template as your base, or scaffold it with:
npx @e18e/setup-publish
3.1 · Job structure
The workflow must separate build from publish. This ensures publish permissions (the OIDC token) are never exposed to build-time code.
test → build → publish
3.2 · Non-negotiable workflow constraints
| Constraint | Why |
|---|---|
| All actions pinned to full-length commit SHA | Prevents supply-chain attacks via mutable tags |
Explicit least-privilege GITHUB_TOKEN permissions | Avoids inheriting broad repository defaults |
actions/checkout uses persist-credentials: false | Reduces token exposure to later workflow steps |
npm ci --ignore-scripts (or --ignore-scripts flag) | Prevents malicious lifecycle scripts running during install |
| Build and publish in separate jobs | Isolates publish permissions from arbitrary build code |
Always include a top-level permissions: block with the workflow-wide minimum, usually
contents: read. Add job-level permissions only when a job requires more access; for npm trusted
publishing, id-token: write belongs on the publish job only.
Every checkout step must disable persisted credentials:
- uses: actions/checkout@<full-length-commit-sha>
with:
persist-credentials: false
This is especially important for jobs that build packages or upload artifacts, because later steps should not inherit a repository token unless they explicitly need one.
3.3 · Suppress lifecycle scripts project-wide
Add to .npmrc in the repository:
ignore-scripts=true
Also apply globally on developer machines:
npm config set -g ignore-scripts true
3.4 · Creating a release
git tag v1.0.0
git push origin v1.0.0
Then in GitHub UI: Releases → Draft a new release → choose the tag → Generate release notes → Publish release. This triggers the workflow.
4 · Alternative Workflow Strategies
4.1 · Changesets (recommended for teams)
Use the changesets template.
- Merged PRs automatically update a release pull request
- Changelog is generated by changesets and included in the release PR
- Releasing = merging the generated release PR — no manual tagging
4.2 · changelogithub (changelog from commit messages)
Use the changelogithub template.
- Tags are still pushed manually
- GitHub release + changelog are created automatically on tag push
- Package is published on tag push
5 · Ongoing Maintenance
5.1 · Keep dependencies updated
Set up Dependabot or Renovate to receive automated PRs for dependency updates, addressing security vulnerabilities promptly.
5.2 · Keep GitHub Actions updated
All third-party and GitHub-owned actions must be pinned to a full commit SHA, not a tag. Tag refs
such as @v6 or @v7 are mutable and must not be committed to workflow files. To migrate existing
workflows and keep them current:
npx actions-up
Run this periodically, or let Dependabot/Renovate manage action updates once SHAs are in place.
5.3 · Lint workflows for vulnerabilities
zizmor detects template injection vulnerabilities and
excessive permission scopes in GitHub workflow files:
zizmor .github/workflows/publish.yml
Integrate this into CI or run it before merging workflow changes.
5.4 · Validate package.json and exports
publint checks for common publishing issues: missing files, incorrect
exports fields, wrong main/module paths, and more.
npx publint
Review the full list of publint rules to understand what it checks. Run this locally before tagging a release.
5.5 · Visualise dependency changes
multiocular shows exactly what code changed
between dependency versions, helping catch unexpected changes or potential security issues.
6 · Further Security Hardening
6.1 · Use a GitHub environment (important for shared repos)
Without a GitHub environment, any account with write access can trigger a publish by creating a branch and modifying the release workflow — bypassing code review entirely.
Settings → Environments:
- Create an environment named
publish. - Do not allow administrator bypass of protection rules.
- Limit deployment to explicit branch names only (e.g.
main,v1). Do not use wildcards. Remove stale branches promptly. - Update the publish job in
publish.yml:
jobs:
publish:
environment: publish
- Update the npm trusted publisher settings on
npmjs.comto include the environment name.
Optionally configure the
publishenvironment to require manual approval before the job proceeds — providing a human gate even if malicious code reaches a release branch.
6.2 · Consider hardware security keys (optional)
Physical security keys (YubiKey, etc.) are significantly more resistant to phishing and credential theft than authenticator apps or SMS. They are worth recommending but should never be enforced — not everyone has access to one, and a good authenticator app is a perfectly reasonable alternative. Mention this as a suggestion, not a requirement.
6.3 · Protect all long-lived branches and tags
Apply branch protection rules not just to main but to all long-lived branches and to all tags.
6.4 · Enable immutable releases
Enable GitHub immutable releases to prevent modification or deletion of tags and releases after creation.
7 · Sole Maintainer Considerations
There is an open feature request to support 2FA for GitHub environment approvals. Until that lands, trusted publishing carries a risk for solo maintainers: a leaked GitHub token could be used to publish through the trusted workflow without a 2FA challenge.
Recommendation for sole maintainers: consider publishing locally with npm publish and
2FA-protected npm access until environment-level 2FA approval is supported. Continue to follow
all other security recommendations in this document regardless.
Audit Output Format
For audits, lead with findings:
- P0/P1/P2/P3 severity.
- File and line when repo-local.
- Risk.
- Recommended fix.
- Whether the agent can implement it now or the user must configure it externally.
Then include:
- Validation run.
- Remaining user-only checklist.
- Suggested next change.
Quick Reference Checklist
Use this when setting up a new package or auditing an existing one.
Account security
- npm 2FA enabled (security key preferred)
- GitHub 2FA enabled (security key preferred)
- Password manager in use
Repository settings
- Actions pinned to full-length commit SHAs (enforced in settings)
- First-time contributor approval required
- Default workflow permissions set to read-only
-
mainbranch protected (PR + review required) - No npm tokens in repository secrets
Trusted publishing
- OIDC trusted publisher configured on npmjs.com
- "Require 2FA, disallow tokens" enabled on npm
- Publish step uses npm CLI ≥ 11.5.1 and a Node.js version supported by official npm docs
- GitHub environment (
publish) configured with branch restrictions
Workflow hygiene
- Build and publish are separate jobs
- Workflows declare explicit least-privilege
GITHUB_TOKENpermissions - Every
actions/checkoutstep setspersist-credentials: false -
npm ci --ignore-scriptsused in all install steps -
ignore-scripts=truein.npmrc -
zizmorpasses on all workflow files
Package quality
-
npx publintpasses with no errors - Dependabot or Renovate configured
-
actions-uprun to migrate to SHA-pinned actions