npm Install Scripts Opt-In RFC on May 14, 2026: How JavaScript Teams Should Prepare Now
npm's new install-scripts opt-in RFC is a bigger deal than another supply-chain postmortem. This piece breaks down the proposal, the advisory-only Phase 1 work, and a realistic way to rehearse the workflow in current projects without waiting for npm to ship it.
npm is finally aiming at the right problem
Every time the JavaScript ecosystem gets punched in the face by another supply chain mess, the advice is usually the same: review lockfiles, pin more things, trust fewer maintainers, and pray your CI catches the weird stuff.
The May 14 RFC to make npm install scripts opt-in feels different. It does not try to give us one more checklist. It goes after the default that keeps making these attacks ugly in the first place: dependency install scripts run automatically during npm install.
That means code can execute before your app ever imports anything. No app boot, no test run, no actual usage required. A compromised transitive dependency lands in the tree and gets a shot at your machine immediately.
The RFC proposes a pretty direct fix:
block dependency
preinstall,install,postinstall, and auto-detectednode-gypbuilds by defaultlet projects opt specific packages back in with a new
allowScriptsfield inpackage.jsonadd
npm approve-scriptsandnpm deny-scriptsso teams can manage that allowlist without hand-editing everything
That is the right shape. It is boring in a good way.
Why this matters more than the usual panic cycle
The strongest line in the RFC is also the most embarrassing one for npm: according to the proposal, npm is the last major package manager still running dependency install scripts by default, while pnpm, Yarn Berry, Bun, and Deno already block them.
That is the real story here. Not that JavaScript has supply chain risk. We know that. The story is that npm is finally being pushed toward the security posture other package managers already decided was normal.
I also like that the proposal is not pretending install scripts can disappear overnight. In the RFC discussion, leobalter points out the annoying truth: packages like canvas, sharp, and sqlite3 still depend on install-time behavior in real projects. You can hate that state of affairs and still admit it exists.
So the proposal goes for explicit trust instead of magical trust. That is a much better trade.
A rough example of the workflow npm is moving toward looks like this:
{
"allowScripts": {
"[email protected]": true,
"sqlite3": true
}
}The interesting detail is that the proposed commands default to pinned approvals. That matters. Approving [email protected] is a lot saner than approving every future sharp script forever just because one version looked fine on Tuesday.
The part teams should pay attention to right now
This is still an RFC, not a shipped npm release. But the thread is already more concrete than a lot of JavaScript proposals ever get.
The RFC author linked a Phase 1 implementation in `npm/cli` on May 15. That first phase is advisory only. Scripts still run. npm just ends the install with a grouped warning that lists packages whose install scripts have not been approved yet.
Honestly, that is smart.
If npm tried to hard-block everything in one jump, half the ecosystem would scream, people would slap on a global escape hatch, and we would be right back where we started. An advisory pass gives teams a way to discover where install-script dependency still exists before the real gate flips.
It also exposes something most repos do not currently know: which dependencies are quietly asking for execution during install.
That visibility alone is useful.
How I would rehearse this workflow today
Do not wait for npm to finish the whole rollout before changing habits. You can practice the important part now.
First, split installation from rebuild work. Treat those as separate acts.
{
"scripts": {
"bootstrap": "npm ci --ignore-scripts && npm run rebuild:native",
"rebuild:native": "npm rebuild sharp sqlite3"
}
}This is not perfect, but it forces a useful question: which packages in this repo genuinely need install-time code execution?
Second, keep that list short and annoying on purpose. If a package needs to be in your rebuild step, someone on the team should be able to explain why. Native addons are one thing. Random frontend utilities with a postinstall banner are not.
Third, make new install-script dependencies visible in review. The future npm workflow is basically saying, "trust should be explicit." You can start doing that culturally even before the command exists.
Fourth, if you run workspaces, remember one subtle gotcha from the RFC discussion: allowScripts is meant to live at the project root. A workspace-level policy is treated as a mistake and will warn. That makes sense. Trust policy wants one place of truth, not five slightly different copies scattered around a monorepo.
What still looks unresolved
The GitHub discussion is not just applause, and that is good.
One commenter argued npm should remove install scripts completely instead of wrapping them in approvals. I get the impulse. If a feature keeps turning into an attack path, deleting it sounds cleaner than managing it.
Another concern is sharper: package-level approvals may still be too broad. If the trust decision is attached only to package identity, a script can change later. In the thread, there is a push for stronger approval binding, like exact script files or hashes.
I think that criticism is fair. If npm ships name-only approvals as the easy path, some teams will absolutely use the easy path forever. That would still be better than today's default, but it would not be the finish line.
Still, I would not let the perfect-version argument distract from the obvious win here. Moving from implicit execution to explicit approval is a real step. A hash-bound model can come after that.
Bottom line
The RFC matters because it changes the social contract of npm install.
Right now, the contract is basically: "if it is in the tree, it can run." The proposal changes that to: "if it wants to run, your project has to say yes."
That is the kind of default JavaScript tooling should have had years ago.
If you maintain production JavaScript apps, I would start rehearsing this now. Run installs with fewer assumptions. Separate rebuilds from dependency resolution. Keep a tiny list of packages that truly need install-time behavior. Make that list reviewable.
When npm eventually flips the default for real, teams that already work this way will barely notice. Everyone else is going to discover just how much code they were auto-executing out of habit.