Skip to content
pkgfire.
What it checksDocsPricing
Sign in Join the waitlist ↗
What it checks Docs Pricing Sign in ↗
Documentation
  1. Getting started
  2. The install wrapper
  3. CI and pull requests
  4. Policy reference
  5. What PkgFire checks
  6. What is uploaded
  7. API overview
DOCUMENTATION

The install wrapper

How pkgfire checks an install before it runs, which commands it wraps, its own flags, and what each exit code means.

How an install is checkedWhich commands are wrappedPkgFire's own flagsExit codesLocal packagesMaking it the default

pkgfire <manager> <arguments> runs your package manager in two steps, with a verdict in between. Your arguments reach the package manager unchanged.

How an install is checked

  1. Resolve. PkgFire saves your manifest and lockfile, asks the package manager to work out the new dependency graph with install scripts off and without installing anything, reads the result, and puts the saved files back byte for byte. The restore is checked by hash, so your working tree is the same afterwards whatever the verdict.
  2. Check. Every package in the resulting graph is checked, not only the ones that changed: a cloned lockfile can already pin a malicious version, and checking only the change would let it through. What changed decides what is reported as new, upgraded or downgraded.
  3. Decide. Your policy turns what was found into ALLOW, WARN or BLOCK, and every verdict lists its reasons.
  4. Install. On ALLOW or WARN your own package manager runs, with install scripts still off. On BLOCK it never starts: nothing is installed and nothing runs.

Resolution happens in your real project directory rather than a copy, so workspaces, relative file: dependencies and a project's own registry configuration resolve exactly as they would without PkgFire.

Which commands are wrapped

Only commands that change dependencies, because only those have a moment before anything runs:

ManagerWrapped
npminstall, ci, add, update, uninstall, dedupe, prune and their short forms
pnpminstall, add, update, remove, prune, dedupe
Buninstall, add, remove, update
uvsync, lock, add, remove
Gogo get, go mod tidy and the other go mod subcommands
Cargoadd, remove, update, generate-lockfile, fetch

Commands whose whole purpose is to run code, such as npm exec, npm run, pnpm dlx, bun x, go run, go test or cargo build, are refused with an explanation. There is no point before execution at which they could be checked. It is an allow list, so a new code-running subcommand in a package manager does not quietly become a way around the check.

A flag that only shapes resolution is passed through to both the check and the real install, so what is installed is what was judged. Three kinds of flag are refused, each with the reason: a global install (there is no project lockfile to approve), anything that points the manager at another directory, configuration or registry, and anything that leaves no lockfile change to judge. A flag PkgFire does not recognise is refused rather than guessed at.

Python with uv

In Python the risky moment is the build: resolving a source distribution can run its setup.py. So pkgfire uv resolves with nothing built, and installs wheels only. A project that can only be resolved by building something is refused with uv's own explanation. Your own project is not installed into the environment by the wrapped sync; uv run does that.

Go and Cargo

Neither runs package code while resolving. For Cargo, the approved Cargo.toml and Cargo.lock are applied with a fetch that downloads exactly those crates and compiles nothing. Commands that compile are refused; run them after the check.

PkgFire's own flags

PkgFire's flags start with --pkgfire-, so a flag such as --json still means your package manager's.

FlagEffect
--pkgfire-dry-runStop after the verdict; never install
--pkgfire-allow-scriptsLet install scripts run, and only on an ALLOW verdict
--pkgfire-forceInstall despite a BLOCK. Recorded as a forced install; scripts stay off
--pkgfire-jsonPrint the result as JSON
--pkgfire-plainPlain lines instead of the live view

Exit codes

Every command that reaches a verdict exits with it, so a script or a CI job can act on the result:

CodeMeaning
0ALLOW. Nothing needs attention
1WARN. The install ran, but something wants a human to look at it
2BLOCK. Nothing was installed and no install script ran
3Error. PkgFire could not reach a verdict: the service was unreachable, a lockfile could not be read, or there was nothing to check
130Interrupted. The manifest and lockfile are put back first

A scan that failed is never an ALLOW. "We could not check" exits 3, never 0.

A CI job that should fail on anything worth a look treats every non-zero code as a failure. One that should fail only on a refusal checks for 2 and 3. Examples are on the CI page.

Local packages

Workspace members, link: and file: entries and a Go replace that points at a directory have nothing to fetch, so they are noted and the command carries on. They are reported as unverifiable, which warns by default. An entry PkgFire cannot parse is different: an unparsed dependency is an unchecked one, so it stops with exit code 3.

Making it the default

PkgFire cannot stop someone typing npm install directly. It can make the checked path the easy one:

  • Alias it in your shell: alias npm='pkgfire npm'.
  • Use it in your project's own setup scripts.
  • Run it in CI, where it cannot be skipped. A laptop is advisory; the pipeline is the control.
PreviousGetting startedNextCI and pull requests
pkgfire.A dependency firewall for npm, pnpm, Bun, uv and Go.
DocsPricingPrivacyLegal noticeSign in ↗
A dependency firewall for npm, pnpm, Bun, uv and Go modules.