Skip to content

npm, behind a firewall

Carry npm packages across an air gap

One command. Every dependency it needs, checksum verified, in tarballs any registry can import.

Run it in your browser

MIT licensed. No account. --dry-run fetches nothing.

3
packages in react-dom
80
packages from one package.json
27 to 10
esbuild, pinned to Linux
$ packall react-dom@19.2.8 --dry-run
Checking registry…
Resolving 1 spec…
  resolved 3 packages in 3s

Dry run — nothing downloaded. 3 packages would be bundled:
  react-dom → 19.2.8  (3 packages in closure)

  Re-run without --dry-run to fetch them:

    packall react-dom@19.2.8

By hand

npm pack gives you one package. The rest is walking the graph yourself, and verifying none of it.

With packall

It walks the graph, checks every tarball against what the registry published, and writes a manifest naming every version.

What lands on the other side

The whole tree

react-dom is one name and three tarballs. You get all three.

Nothing lands unverified

A checksum mismatch discards the download and stops the run.

Nothing to rewrite

Packages sit in the layout every registry already serves.

Only your platforms

esbuild is 27 packages. Seventeen are binaries for machines you do not have.

Your registry, your credentials

Scoped registries and tokens come from your .npmrc.

Every dependency it needs, verified, in a layout your registry already understands.

Three commands. Only the middle one touches the network.

  1. $ packall react-dom@19.2.8 --dry-run

    01 Look first

    Reports the whole tree without fetching a byte.

  2. $ packall react-dom@19.2.8 --out ./bundles

    02 Bundle it

    Downloaded once, checksum verified, packed with a manifest.

  3. $ tar -xzf react-dom@19.2.8.tgz -C ./bundle

    03 Carry it over

    Unpack and upload into Artifactory, Nexus, Verdaccio or any npm registry.

The proof is what it printed

Every terminal block on this page is a real run, pasted as it came back. Here is the platform claim, twice.

every platform
$ packall esbuild@0.25.12 --dry-run
Checking registry…
Resolving 1 spec…
  resolved 27 packages in 1s

Dry run — nothing downloaded. 27 packages would be bundled:
  esbuild → 0.25.12  (27 packages in closure)

  Re-run without --dry-run to fetch them:

    packall esbuild@0.25.12
--platform linux
$ packall esbuild@0.25.12 --platform linux --dry-run
Checking registry…
Resolving 1 spec…
  resolved 10 packages in 1s

Dry run — nothing downloaded. 10 packages would be bundled:
  esbuild → 0.25.12  (10 packages in closure)

  Re-run without --dry-run to fetch them:

    packall esbuild@0.25.12 --platform linux

And a whole project, pinned to its lockfile:

$ packall --file package.json --prod --dry-run
Checking registry…
Resolving 3 specs…
  resolved 80 packages in 1s

  pinned by ./package-lock.json (npm, lockfileVersion 3)

Dry run — nothing downloaded. 80 packages would be bundled:
  express → 5.2.1  (66 packages in closure)
  pino → 9.14.0  (13 packages in closure)
  zod → 3.25.76  (1 package in closure)

  Re-run without --dry-run to fetch them:

    packall --file ./package.json --prod --lockfile ./package-lock.json

Questions

Does the restricted network need to reach npm?

No. You run packall where the registry is reachable, carry the tarballs across however you move files, and import them on the other side.

How is this different from npm pack?

npm pack gives you one package without its dependencies. packall walks the whole closure, verifies every checksum, and records what it did.

What is inside a bundle?

Package tarballs in the layout every registry serves, plus bundle-manifest.json naming each version, source and the edge that pulled it in.

Can I bundle a whole project?

Point --file at a package.json, a lockfile, or a list of approved names. A lockfile beside it pins the run, and the output says so.

Does it work with Artifactory, Nexus or Verdaccio?

Both directions. Registries and tokens come from your .npmrc. Proxy settings are the one thing it does not read.

What if a checksum does not match?

The download is discarded and the run stops rather than warning and carrying on.

Will it include the binaries the target machine needs?

By default, for every platform. Pass --platform when you know the target and want the rest left behind.

How do I run it in CI?

--yes takes every default, --json writes a summary to stdout, --quiet drops everything but warnings and errors.

What does it cost?

Nothing. MIT licensed, no account, no telemetry.

Nothing to commit to

MIT licensed. No account, no telemetry, no paid tier. --dry-run shows you what a command would fetch without fetching it.

Carry npm packages across an air gap

Run it in your browser

$ npm i -g @packall/cli