
Fast, small, secure-by-default package manager for JS
jpm is a JavaScript package manager built for speed, low resource use, and security defaults. It ships as a single ~2 MB binary with no runtime dependency, installs npm packages, and claims the fastest cold install times on GitHub CI in most tested scenarios. It imports existing lockfiles from npm, pnpm, yarn, and bun without registry lookups, uses a content-addressed store with hardlinks so each file and package is stored/built once per machine, and blocks common npm supply-chain attack paths by default: install scripts wait for explicit approval, packages published less than a day ago are rejected, and git/tarball dependencies are refused unless your own project declares them.
One binary with no runtime to install and no dependency tree of its own — far smaller than pnpm (~60 MB), bun (~80 MB), or aube (~150 MB).
In the site's benchmarks across three real projects (nitro, nuxt, next) on GitHub CI, jpm was fastest in 9 of 12 install-phase cells; a cold nuxt install of 591 packages took 1.22 s vs 18.54 s for npm, and peak memory was 34 MB vs 387 MB for npm.
jpm install reads your existing lockfile (package-lock.json / npm-shrinkwrap.json from npm 7+, pnpm-lock.yaml from pnpm 9+, bun.lock, or yarn.lock from yarn 1 or 2+) and writes jpm.lock with the same versions. Workspaces, pnpm-workspace.yaml, catalogs, overrides, resolutions, and patches are also re
Tarballs are verified against their integrity, unpacked once into ~/.jpm/store, and stored by hash read-only. Packages are built once as named@version entries with a digest of their dependency graph; project node_modules just links to those entries, and warm installs make only a few links.
A dependency's install scripts (the usual malware vector in npm) don't run until you run jpm approve for that package and version, and each new version requires approval again.
Versions published in the last day aren't picked (min-release-age), even when pinned exactly deep in the tree, so a newly hijacked release can't ride in on a pin.
Published packages can't pull in git or tarball dependencies or paths outside themselves — only your project and its workspaces choose where code comes from.
Every package is checked against its lockfile integrity before any of its files are visible to your project, and credentials never land in jpm.lock — tokens stay in .npmrc and git URLs carrying passwords are refused.
jpm imports your current npm, pnpm, yarn, or bun lockfile into jpm.lock with the same versions and tree, using no registry lookups for fully-specified lockfiles. Your old lockfile is left untouched until you're ready to delete it.
Verified tarballs are unpacked once into a content-addressed store (~/.jpm/store); each package version is built once as an entry with its dependencies linked beside it, and every project's node_modules hardlinks to those entries — so repeat installs finish in 1–2 ms.
Install scripts stay dormant until approved, day-old releases are skipped, foreign dependency sources are refused, and packages are integrity-checked before their files become visible.
jpm is a package manager for JavaScript that installs npm packages from a single ~2 MB binary, with no runtime to install, fast install times, and security restrictions enabled by default.
On macOS/Linux run `curl -fsSL https://getjpm.sh | sh`; on Windows run `irm https://getjpm.sh/install.ps1 | iex`. Release binaries are also downloadable from GitHub.
Yes. Run `jpm install` in your project and it imports your existing lockfile (package-lock.json / npm-shrinkwrap.json, pnpm-lock.yaml, bun.lock, or yarn.lock) into jpm.lock with the same versions and tree. For npm, pnpm, and bun lockfiles it needs no registry lookups; for yarn.lock it only queries the registry for what the lockfile doesn't record (peers, platforms, bins). Your old lockfile is left untouched.
Yes — `jpm ci` and `jpm install --frozen-lockfile` install from the old lockfile as-is until jpm.lock is committed.
It's one ~2 MB binary with no runtime, uses little memory during installs (34 MB vs npm's 387 MB for a cold nuxt install on GitHub CI), and keeps a content-addressed store where each file is stored once and each package built once — warm installs mostly create hardlinks (1–2 ms for a repeat install).
Install scripts don't run until you run `jpm approve` for the package and version; versions published in the last day are skipped (min-release-age); published packages can't introduce git or tarball dependencies; packages are integrity-checked against the lockfile before their files are visible; credentials stay out of jpm.lock; cloned repos can't disable TLS checks or loosen the release age; and requested Node.js runtimes are verified against Node's release signatures. Note that this is not a sandbox — an approved script runs with your permissions.
Yes. It reads workspaces, pnpm-workspace.yaml, catalogs, overrides, resolutions, and patches.
Every line was written by Claude Code (running Claude Opus 5.5) under the direction of engineer JT Turner. External verification was a stated condition: the custom TLS implementation is tested against Project Wycheproof and RFC test vectors, and the tool is checked against the test suites of npm, pnpm, yarn, and bun.