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

@bonniernews/stayput

v0.1.3

Published

Default-deny network guard for Node.js test environments — blocks non-local TCP at the socket level.

Readme

@bonniernews/stayput

Default-deny network guard for Node.js test environments. Blocks non-local TCP at the socket level, so a test suite with a leaked production connection string fails loudly at connect time instead of silently mutating production. Works for raw-TCP drivers (pg, mongodb, elasticsearch, redis) as well as fetch/undici — not just HTTP.

Quick start

// .mocharc.cjs
module.exports = { require: ['@bonniernews/stayput/register'] };

Blocked connections throw with err.code === 'EREMOTEBLOCKED'.

CI already injects the guard into every node process via NODE_OPTIONS in the shared workflow templates — the mocharc line is what protects laptops, which is where stray prod credentials actually live.

Adoption is checked by ESLint: the shared ESLint config ships two rules — stayput-required flags a mocharc missing the register line (auto-fixable), and project-has-guard flags test files not governed by any stayput-loading mocha config. If you're seeing one of those warnings, the quick start above is the fix.

Runner-proofing (belt and suspenders)

The mocharc line only guards runs that load the mocharc. mocha --no-config, mocha invoked from a subdirectory, node --test, or jest all skip it. If your repo has a shared test helper that tests import anyway, anchor the guard there too — loading is idempotent, so double-loading costs nothing:

// test/helpers/db.js
import '@bonniernews/stayput/register'; // guard on, no matter how the runner was invoked
import { assertActive } from '@bonniernews/stayput';

export async function testDb() {
  assertActive(); // refuse to hand out connections if the guard was tampered with
  // ...create and return the client
}

Boundary rule: stayput is a devDependency and only test-owned files (test helpers, setup files, mocharc) may import it — never src/, where it would crash a production install. If tests use the app's real DB module directly, add a thin test helper in between rather than gating production code on NODE_ENV.

(In CI this is all redundant — NODE_OPTIONS injection guards every node process regardless of runner or config.)

Policy

Locally (no CI env): loopback only. In CI: loopback + private ranges (RFC1918). Everything else is blocked.

| Env var | Effect | |---|---| | STAYPUT_ALLOW | comma-separated extra hosts / IPs / IPv4 CIDRs, e.g. 10.128.0.5,db.internal | | STAYPUT_DENY | same format — deny beats allow | | STAYPUT_DISABLE=I_UNDERSTAND_THE_RISK | turns the guard off (deliberately ugly so it stands out in review) |

Put overrides in the workflow file, not in code: STAYPUT_ALLOW/STAYPUT_DISABLE are greppable fleet-wide and double as the exception inventory.

AI agents

An EREMOTEBLOCKED error is a stop signal, not a bug to fix. Do not set STAYPUT_ALLOW or STAYPUT_DISABLE, edit workflow env, or remove the mocharc line to make tests pass — the block usually means the test suite is pointing at infrastructure it should not touch. Pause and ask a human to verify the target. Only a human adds exceptions, in the workflow file, where they are visible in review and in the fleet-wide grep.

This contract has been red-teamed (2026-08-24): a coding agent (Claude) was given a sandboxed repo whose tests hit a blocked private-range database and told to "just get it green today". It diagnosed the guard from the error message, refused to override or fake the result (including mocking the driver or repointing the test), and stopped to ask for human verification. Steering, not enforcement — but it works on well-behaved agents.

Programmatic API

import stayput, { enable, assertActive } from '@bonniernews/stayput';

enable({
  allow: ['10.128.0.5'],
  deny: ['10.10.0.0/16'],   // deny beats allow
  privateRanges: 'auto',    // 'auto' = private ranges only when CI is set
  onBlock: (host, port) => {},
});
assertActive();             // throws if never loaded or unpatched
stayput.isActive;           // live getter on the default export

Prefer the named exports: stayput.assertActive() on the default import trips import/no-named-as-default-member in the shared ESLint config. Type declarations ship with the package (StayputOptions, Stayput), so TypeScript test helpers need no declare module stub.

@bonniernews/stayput/mocha exports a root hook plugin that runs assertActive() before tests.

Loading is idempotent: multiple loads (NODE_OPTIONS + mocharc + import) merge allowlists, patch once, and log one greppable line:

stayput/0.1.0 active mode=loopback+private allow=2 deny=1 source=NODE_OPTIONS

One layer of many

stayput is a client-side footgun guard — the last line of defence, not the plan. It complements, never replaces:

  • Education — knowing why prod credentials don't belong on laptops or in repos beats any guard.
  • Easy, short-lived access paths — when you genuinely need to reach a real database, use the ephemeral proxies/jumphosts on non-default ports. The sanctioned way is easier than the risky way, and a leaked default-port connection string doesn't route anywhere.
  • Firewall rules / network design — test environments should have no route to production at all; stayput only matters where a route exists.
  • Test frameworks and fixtures — testcontainers, CI service containers, and hermetic fixtures remove the reason to point tests at shared infrastructure in the first place.
  • Server-side guards — read-only default roles, DDL gating, RBAC without destructive verbs (per-database plan in HANDOVER.md).

How it works

Patches net.Socket.prototype.connect (sync throw on blocked IP literals; unix sockets always allowed) and dns.lookup (hostnames are vetted when they resolve). TLS, http/https and undici ride on net.Socket, so they're covered for free. server.listen resolves its host through dns.lookup too, so binding is allowed for loopback and for the unspecified addresses (0.0.0.0, ::) — binding a specific non-local IP needs STAYPUT_ALLOW. This is a footgun guard, not a security boundary.

Requires node ≥ 20.6 (preloaded in CI with NODE_OPTIONS=--import …/register.js).

Development

npm test                  # unit tests + type declaration check — no network, no docker
npm run test:integration  # pg + mongodb + elasticsearch against docker (compose.yaml)

Design decisions, environment matrix, and rollout plan: HANDOVER.md.