npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

npryx

v1.0.0

Published

A security-first superset of npx: previews install-script, provenance, age, popularity and typosquat signals before running a remote package, fails closed, and remembers approvals.

Readme

npx will happily download and execute an arbitrary remote package behind a bare prompt:

Need to install the following packages:
  [email protected]
Ok to proceed? (y/N)

That prompt tells you nothing: not whether the package runs install scripts, how old or popular it is, whether it's deprecated, whether it has build provenance, or whether the name is one keystroke away from a package you actually meant. It's exactly where supply-chain attacks land: typosquats, postinstall RCE, hijacked re-publishes.

npryx hands your command to the real npx, but first it shows you the trust signals npx hides, fails closed if it can't verify the package, offers a one-keystroke --ignore-scripts safe run, and remembers what you've already approved so the prompt keeps meaning something. It then runs exactly the version it showed you, never a fresh resolve.

  npryx: about to fetch & run a package from the npm registry

  ! [email protected]: 2 warnings, review before running
    - runs install scripts (postinstall) which execute code on install
    - published only 11d ago, brand new with little scrutiny yet

  package       [email protected]   (asked: latest)
  published     11d ago
  weekly dl     41,284,663
  maintainers   esbuild
  repo          git+https://github.com/evanw/esbuild.git
  integrity     sha512-HrJrvZv5ayxBzPfwp…
  provenance    ✓ https://slsa.dev/provenance/v1
  install hook  ! yes, runs code on install

  [y] run   [s] run with --ignore-scripts (safer)   [a] always-trust this version   [N] abort (default):

In a terminal the marks and the verdict are coloured (green ✓, yellow !, red ✗ for tampered bytes or a confirmed threat). Piped or logged output stays plain. NO_COLOR=1 turns colour off and FORCE_COLOR=1 turns it on. On a narrow terminal the choices stack one per line.

What it shows

A single npm view <spec> --json (so it resolves ranges, tags, and your .npmrc, including private registries and auth) gives every signal below. Registry flags you pass (--registry, --userconfig, --@scope:registry, …) are used for the preview too, so it reads the same registry the run installs from. Weekly downloads come from the public npm API as a best-effort hint, and only for packages served by the public registry, so private package names never leave your machine.

| signal | flagged when | |---|---| | install scripts | preinstall / install / postinstall present: the package runs code the moment it's installed | | provenance | shown ✓ when the build has signed SLSA provenance attestations | | publish age | younger than ~30 days: brand new, little scrutiny yet | | weekly downloads | under ~1,000: unusually low | | deprecation | the maintainer marked it deprecated | | typosquat | the name is one edit or one swapped pair of letters away from a popular package (crossenv → cross-env, lodahs → lodash) | | context | resolved name@version, maintainers, repo, and dist.integrity, always shown |

Scope: npryx previews the target package: npm view reports the target's own install scripts, not a transitive dependency's. The s (--ignore-scripts) option blocks install hooks across the whole tree, so reach for it when a target you trust pulls deps you don't.

The prompt

| key | action | |---|---| | y | run it (npx --yes <args>), pinned to the version shown | | s | run with --ignore-scripts (skips install hooks; safer). Note: a few packages legitimately need a postinstall to fetch a native binary, e.g. esbuild. If it breaks, re-run with y. | | a | always-trust this exact version, then run (see below). Not offered when the package couldn't be verified. | | N | abort (the default: just press Enter) |

Trust model (TOFU)

Choosing a records the version and its integrity hash (sha512) in ~/.npryx.json. You can trust several versions of the same package. Next time:

  • same version, same integrity → the bytes you approved. npryx prints ✓ [email protected] is trusted and runs with no prompt. No fatigue, no reflexive y.
  • a version you haven't approved → a normal new release. npryx shows the full preview with a note (you trusted [email protected]; this is 1.6.0) and asks again.
  • same version, different integrity → npm never lets a published version be overwritten, so a registry, mirror or proxy is serving altered code. npryx shows a red ✗ do not run, and this is never auto-run, not even with NPRYX_YES=1.
npryx --trust-list                 # show what you've trusted
npryx --forget <pkg>[@<version>]   # drop a package, or one version of it

Fails closed

If npryx can't verify a package (no such name, registry error), it does not quietly run it:

  • interactive terminal → it warns and defaults the prompt to No.
  • non-interactive / CI → it refuses and exits 1 for any package that isn't already trusted. This deliberately inverts npm's "assume yes in CI" default: an unverified package never auto-runs in a pipeline. To allow packages, set NPRYX_ALLOW to a comma-separated list of name (any version), name@version, an integrity hash (sha512-…) or name@version#sha512-… (exactly those bytes, the strictest). NPRYX_YES=1, or -y placed before the package, opts out entirely. A -y after the package belongs to the command being run and changes nothing.

It also refuses, rather than guesses, when it can't tell which package would run:

  • unrecognised flags before the package. npm treats any config key as a flag, so an unknown --flag might swallow the next argument. Write it as --flag=value and npryx will pass it through.
  • git, URL and alias specs (github:u/r, git+ssh://…, https://…/x.tgz, foo@npm:bar) can't be checked against the registry, so they're treated like an unverifiable package: a prompt that defaults to No, or a refusal in CI unless the exact spec is in NPRYX_ALLOW.

Every -p package is previewed, not just the first.

Two things go straight to npx without a preview:

  • local paths (./x, /abs/path, file:…, x.tgz): your own files.
  • bins installed in your project (npx tsc with typescript in node_modules), which npx runs without fetching anything. These are passed with --no, so if npryx misjudged and npx would need to install something, npx refuses instead.

Remote scanning (optional)

npryx can also ask a scan service whether a package sends data off your machine: environment variables to a raw IP, tokens to a webhook, secrets hidden in DNS lookups. The service reads the code, compares it with the previous version, checks OSV for known malware and, if you opt in to that too, runs the package in a sandbox full of fake credentials to see if any of them try to leave.

It's off by default. Nothing is sent anywhere until you turn it on:

npryx --scan-config https://scan.example.com [--token <key>] [--deep]
npryx --scan-status
npryx --scan-off
  • Only packages from the public npm registry are sent. Private package names never leave your machine.
  • Results are signed, and npryx pins the service's key when you opt in.
  • A scan can only add caution. A confirmed threat is blocked like tampered bytes: never auto-run, even with NPRYX_YES. If the service is down, slow or unverifiable, npryx behaves exactly as it does without it.
  • The service is open source (scan-service/, Rust), so you can run your own. A hosted version needs an API key.

Design and detection details: docs/scan-service.md. Wire format: docs/scan-api.md.

For agents and scripts

npryx --json <pkg> [args...] checks a command without running it. It never prompts, never runs anything and never writes the trust store. stdout is a single JSON document and stderr stays empty. --json must come first. The decision is the same one a non-interactive npryx <args> acts on, so a loop looks like this:

  1. Check: npryx --json cowsay moo
  2. allow (exit 0): run npryx <command.npryx…>. Without a terminal it runs only if the decision is still allow, pinned to the version shown.
  3. needs-approval (exit 3): show a human the document. If they approve, run NPRYX_ALLOW='<approve>' npryx <command.npryx…>. approve is name@version#integrity, so it allows exactly the previewed bytes: not another version, not a re-published tarball.
  4. refuse (exit 1): don't run it. Tampered bytes and confirmed threats can't be approved by anything. An unverifiable package (lookup failed, git or URL spec) needs a human to check the spec themselves.

| exit | meaning | |---|---| | 0 | allow: trusted, a local path, or allowed by NPRYX_ALLOW / NPRYX_YES | | 3 | needs-approval: verified but not trusted, a human should decide | | 1 | refuse: tampered, confirmed threat or unverifiable, or an internal error | | 2 | usage error (unrecognised flag, no package, a command with no JSON mode) |

{
  "schemaVersion": 1,
  "decision": "needs-approval",
  "reason": "untrusted",
  "message": "not trusted yet: a human should review the packages, then approve them",
  "command": { "npx": ["--yes", "[email protected]", "moo"], "npryx": ["[email protected]", "moo"] },
  "approve": "[email protected]#sha512-…",
  "packages": [{
    "requested": "cowsay", "kind": "registry", "decision": "needs-approval", "reason": "untrusted",
    "resolved": "[email protected]", "name": "cowsay", "version": "1.6.0", "integrity": "sha512-…",
    "provenance": null, "installScripts": { "runs": false, "hooks": [] },
    "published": "2024-07-08T15:31:44.000Z", "weeklyDownloads": 412345,
    "maintainers": ["piuccio"], "repo": "git+https://github.com/piuccio/cowsay.git",
    "deprecated": null, "typosquatOf": null, "publicRegistry": true,
    "warnings": [],
    "trust": { "status": "updated", "approvedAt": null, "trustedVersions": ["1.5.0"] },
    "scan": null,
    "approve": "[email protected]#sha512-…",
    "error": null
  }]
}
  • reason: trusted, local, allowed, yes, nothing-to-install, local-bin (allow); untrusted (needs-approval); tampered, confirmed-threat, unverifiable (refuse). The top level takes the least clear package's.
  • warnings[].code: install-scripts, deprecated, new-package, low-downloads, typosquat, npm-subcommand.
  • trust.status: new, updated, trusted or tampered (null if unverified).
  • scan: null unless remote scanning is on, else status (done, pending, integrity-mismatch, unavailable), verdict, signed, previous and findings.
  • command is null when the decision is refuse. Errors are { "schemaVersion": 1, "error": { "code": "usage" | "internal", "message": "…" } }.
  • npryx --trust-list --json and npryx --scan-status --json work too.

New fields may be added within a schemaVersion; a field changing meaning or going away bumps it.

Approval should come from a human. npryx can't tell who set NPRYX_ALLOW, so if your agent's harness asks before running commands, keep it asking for ones that set NPRYX_ALLOW or NPRYX_YES. There is deliberately no way to write the trust store without a terminal.

Install

Requires Node 22+. Zero runtime dependencies. The only dev dependency is Hegel for property-based tests, pinned by the lockfile and installed with scripts disabled (see .npmrc).

npm install -g npryx
npryx cowsay "moo"

Want every npx to go through npryx? That's optional. You can keep typing npryx when you want the check, or alias npx to it:

npryx --setup-alias    # shows the exact lines and the file, then asks before adding them
npryx --remove-alias   # takes out exactly that block again
npryx --alias          # just prints the line, if you'd rather edit your config yourself

--setup-alias supports zsh, bash and fish. It adds one clearly marked block, does nothing if it's already there, and won't touch an npx alias you've defined yourself. With the alias in place, command npx … still runs the real npx for a single command.

Prior art

npq pioneered install-time marshalling of npm package safety signals, and npryx borrows its signal taxonomy. npryx differs by wrapping npx execution specifically, failing closed, offering the inline --ignore-scripts run, and keeping a TOFU trust store.

Roadmap

  • Hosted scan service. The scan service (scan-service/) runs today if you host it yourself. A hosted version, so you can opt in without running anything, is next.
  • Transitive install-script preview. Today npryx previews the target only, but the real risk is often a dependency's postinstall. A metadata-only resolve (npm install --package-lock-only --ignore-scripts: no download, nothing executed) surfaces every package in the tree that runs install scripts. The trust store would pin that set, so a trusted target re-prompts when its tree changes.
  • Local static scan. Compile the scan service's analyzer to WebAssembly and ship it in the npm package, so the "does this send data somewhere" check can run on your own machine with nothing sent anywhere.

npryx is published from CI with provenance (.github/workflows/release.yml), so npryx npryx shows its own provenance ✓.

License

ISC. See LICENSE.md.