Pre-Publish Checklist for a New npm Package in 2026 (Security, Provenance, README)
A grounded pre-publish checklist for a new npm package in 2026: trusted publishing wired, provenance enabled, scoped access, files allowlist, README and license correct, and the search-and-tags that get you found.
Sources: S-001 S-002 S-003 S-005
The npm-side defaults you want set before you tag v1.0.0. Use the interactive preflight checklist on the landing page for the trusted-publishing and provenance rows. This post covers the broader package quality checks the artifact does not.
TLDR
Before you run npm publish the first time:
- Trusted publisher registered on npmjs.com, OIDC working (the preflight confirms).
package.jsonname,version,description,license,repository,homepage,bugsfilled.filesallowlist set — don’t shipnode_modulesor.git(S-001).publishConfig.access: publicfor scoped packages.README.mdhas install instructions, a minimal example, and a “what this is not” section.- License file present (
LICENSE). npm pack --dry-runshows only the files you want to ship.npm publish --dry-run --provenance --access publicruns clean in CI.- First publish is a pre-release (
v1.0.0-rc.0), not the canonical version.
Why each step is on the list
Trusted publishing wired
If your workflow does not have id-token: write and actions/setup-node@v4 with registry-url, your first publish silently bypasses trusted publishing (GitHub OIDC docs — S-002). Run the preflight before you do anything else.
Manifest fields
name, version, description, license, repository.url, homepage, bugs.url should all be filled. repository.url matters specifically — npm checks that the URL in your manifest matches the trusted publisher repo (npm docs — S-001).
Example:
{
"name": "@you/super-lib",
"version": "1.0.0-rc.0",
"description": "A small library that does one thing well.",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/you/super-lib.git"
},
"homepage": "https://github.com/you/super-lib#readme",
"bugs": {
"url": "https://github.com/you/super-lib/issues"
}
}
files allowlist
Without files, npm packs everything except whatever is in .npmignore. The common failure (npm/cli issues — S-005) is shipping tests/, .github/, *.local, or your editor config. The safer pattern is allowlist:
"files": [
"dist",
"README.md",
"LICENSE"
]
package.json and README.md are always included by npm. LICENSE and LICENCE (any case) are auto-included if present.
publishConfig.access for scoped packages
Scoped packages (@you/lib) default to private. The most common “I published but no one can install it” thread is forgetting publishConfig.access:
"publishConfig": {
"access": "public"
}
Or pass --access public on the command line. The preflight flags either path as a pass.
README
A README that ships with your package needs:
- One-line description matching the manifest.
- Install command (
npm install <name>). - Smallest possible working example.
- A “what this is not” section. Saves you from filing the same “is this for X?” issue ten times.
- License line at the bottom matching the LICENSE file.
LICENSE file
MIT, Apache-2.0, BSD-3-Clause — pick one. Match the license field in package.json exactly (SPDX identifier). The license text goes in LICENSE at the repo root.
Dry runs
npm pack --dry-run
Lists the files that would ship. Eyeball the list before publishing.
npm publish --dry-run --provenance --access public
Goes through the full publish flow except the actual upload. Verifies OIDC, manifest, packaging, and the registry contract.
Pre-release first
Tag v1.0.0-rc.0 (or v0.1.0-rc.0 for v0 packages). Let the workflow run, verify the provenance badge appears on npm, verify npm install @you/lib@rc works in a separate folder. Only after that, tag v1.0.0.
The cost of a bad 1.0.0 is high — npm unpublish is only allowed within 72 hours and is a noisy event you would prefer not to need.
Concrete example: minimum package.json
{
"name": "@you/super-lib",
"version": "1.0.0-rc.0",
"description": "Tiny library that does one thing well.",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist", "README.md", "LICENSE"],
"repository": {
"type": "git",
"url": "git+https://github.com/you/super-lib.git"
},
"homepage": "https://github.com/you/super-lib#readme",
"bugs": "https://github.com/you/super-lib/issues",
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"test": "node --test"
},
"engines": {
"node": ">=20"
}
}
The checklist as a table
| Row | What | Where to check |
|---|---|---|
| 01 | Trusted publisher registered on npm | npmjs.com → your package → Trusted Publishers |
| 02 | id-token: write in workflow | .github/workflows/publish.yml |
| 03 | actions/setup-node@v4 + registry-url | same file |
| 04 | --provenance --access public on publish | same file |
| 05 | No NPM_TOKEN in workflow env | same file |
| 06 | repository.url matches GitHub repo | package.json |
| 07 | files allowlist set | package.json |
| 08 | publishConfig.access: public (scoped only) | package.json |
| 09 | LICENSE file present | repo root |
| 10 | README has install + example + “not for” | README.md |
| 11 | npm pack --dry-run lists only what you want | terminal |
| 12 | npm publish --dry-run succeeds in CI | workflow logs |
| 13 | Pre-release published first | npm version page |
| 14 | Provenance badge visible on pre-release | npm version page |
Rows 02 through 08 are exactly what the preflight artifact verifies automatically. Rows 09 through 14 are manual.
Common mistakes
- Skipping the pre-release and going straight to
v1.0.0. The first publish almost always exposes one config mistake. - Setting
publishConfig.access: publicand forgetting to bump the manifest version (publish fails withversion already exists). - Putting a sensitive file like
.env.localin the package tarball becausefilesis not set and.npmignoreis missing. - Leaving
private: trueinpackage.json. The publish refuses withCannot publish private package.
FAQ
Can I unpublish a botched publish?
Within 72 hours of publish, yes — npm unpublish <name>@<version>. After 72 hours, only deprecation. Plan as if unpublish does not exist.
How do I see the provenance after publish?
npm view <name>@<version> shows the metadata. The version page on npmjs.com shows the badge. npm audit signatures <name>@<version> verifies the Sigstore chain.
Do I need 2FA on my npm account if I’m using trusted publishing?
Yes. Trusted publishing reduces the value of a leaked NPM_TOKEN, not the value of a stolen npm account session. 2FA is still required for manual web actions.
Next step
Run the preflight on your package.json and workflow now. For the deeper background, see the trusted publishing setup guide and why long-lived NPM_TOKEN is deprecated in practice.