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

@lucentive-labs/ferry

v0.1.0

Published

Agent-era secrets broker — inject declared secrets into a child process's env without ever exposing them to the calling agent. Zero runtime deps, output redaction, per-command policy allowlist, append-only audit.

Readme

@lucentive-labs/ferry

Agent-era secrets broker. A secret should be usable by an agent or an automation without ever being visible to it.

Ferry runs your command as a child process and injects declared secrets into that child's environment. The value reaches vercel / convex / curl, but it never touches Ferry's own stdout, its logs, the audit file, or the calling agent's captured transcript. You authorize a typed policy once — no per-use biometric prompt, works headless. Even if the child echoes a secret, Ferry redacts it to [redacted:NAME] before it reaches the terminal.

Ferry is a broker, not another place secrets sleep: values come from pluggable backends and are never persisted by Ferry in plaintext.

Zero runtime dependencies. Ferry is built entirely on Node built-ins (node:crypto, node:child_process, node:stream, …). For a tool that handles secrets, a small, auditable attack surface is a feature — there is no third-party runtime code between your secret and the child process.

Install

pnpm add @lucentive-labs/ferry

Not published to npm yet (@lucentive-labs/ferry is at 0.0.0) — publishing via Trusted Publishing is wired and lands shortly. Until then, use it from source: git clone this repo, pnpm install && pnpm build, then run node dist/cli.js … (or pnpm link --global to get the ferry command).

Quickstart

ferry init                         # scaffold ferry.config.mjs + gitignore .ferry/
# edit ferry.config.mjs (see below)
ferry check                        # confirm every secret resolves (by NAME)
ferry run -- vercel deploy --prod  # inject only the secrets whose allow matches

That's the whole loop: declare once, then prefix any command with ferry run --.

The config

ferry.config.ts (also .mjs / .js) declares which secrets exist, where each resolves from, and which commands may receive it:

import { defineFerry, op, file, env } from "@lucentive-labs/ferry";

export default defineFerry({
  secrets: {
    VERCEL_TOKEN: {
      backend: op("op://Vault/Prod - Vercel/credential"),
      allow: ["vercel *"],
      description: "Vercel deploy token",
    },
    CONVEX_DEPLOY_KEY: {
      backend: file("prod.convex"),
      allow: ["convex deploy", "convex env *"],
    },
    MY_TOKEN: {
      backend: env(), // reads the secret's own NAME from process.env / .env
      allow: ["curl *"],
    },
  },
  audit: ".ferry/audit.log",
});
  • secrets — the map key is the environment variable injected into the child (must be a valid env identifier).
  • backend — where the value comes from, built with op() / file() / env().
  • allow — glob(s) permitted to receive this secret, matched positionally against the command's argv (per-arg, not a flattened string). Within a token * matches any run of characters; a trailing bare * soaks up any remaining args, otherwise the arity must match exactly — so a caller can't append an unauthorized extra argument, and a binary literally named "vercel deploy" can't spoof the token vercel. Ferry refuses to inject a secret for a command outside its allowlist — so an agent can't reuse a deploy token for curl evil.com.

ferry init scaffolds ferry.config.mjs, which loads with zero dependencies via native ESM — so ferry run works out of the box. A .ts config is also supported but loads through tsx (the one place Ferry leans on a dev tool). Your config is trusted code you author (like a Makefile or vite.config); Ferry does not sandbox it, so don't print a secret inside it.

Backends

| Backend | Built with | Security | Use for | | --- | --- | --- | --- | | 1Password | op(ref) | High | Team/prod secrets via the op CLI (op read <ref>). | | Encrypted file | file(name) | Medium | Headless reuse. AES-256-GCM, scrypt-derived key from FERRY_FILE_KEY. | | Process env | env(varName?) | Low | Local dev; reads process.env / a local .env. |

Headless, without biometric timeouts. op's biometric prompt fails in a headless agent loop. Decrypt once while present, into the encrypted file store, then run headless forever after:

ferry cache VERCEL_TOKEN   # resolves VERCEL_TOKEN from op → encrypts into .ferry/secrets.enc.json
# then switch its backend to file("VERCEL_TOKEN") and later runs never prompt again

The value passes through Ferry's memory only; it is written to disk encrypted and never printed.

Commands

| Command | What it does | | --- | --- | | ferry init | Scaffold ferry.config.mjs and gitignore .ferry/. | | ferry run [--only N[,N]] [--clean-env] -- <cmd> | Resolve + inject the matching secrets, run <cmd>, stream its output redacted, audit each access. --only restricts to named secrets; --clean-env forwards only a minimal safe base env. | | ferry check | Validate the config and resolve every secret; reports ok / missing / denied by NAME, exits non-zero on any failure. | | ferry list | Table of NAME → backend → allowed commands. No values. | | ferry audit [--tail N] | Print the append-only access log. | | ferry cache <NAME> | Decrypt <NAME> from its backend once into the encrypted file store for headless reuse. |

Security model

  • The value's only homes are (a) the backend, (b) the child process env, and (c) Ferry's transient memory. It is never in Ferry's stdout, its logs, the audit file, or the returned metadata. This is enforced and covered by a test.
  • Redaction. Child stdout and stderr stream through a single, shared boundary-safe redactor that replaces every occurrence of each injected value with [redacted:NAME] — even when a value is split across two output chunks, or split half-to-stdout / half-to-stderr in a combined capture. (Because the redactor's held-back tail is shared, Ferry does not preserve strict stdout/stderr separation: a few boundary bytes of one stream may surface on the other. No bytes are lost and no value leaks — a deliberate tradeoff for cross-stream safety.)
  • Policy allowlist. A secret is injected only when the argv matches one of its allow globs; otherwise it is refused and the denial is audited. For every declared secret Ferry owns its env in the child — it strips the destination NAME, the env() source variable, and its own broker vars (FERRY_FILE_KEY), then adds back only the authorized values. So a denied or --only-excluded declared secret can never reach the child through the ambient environment, and the file-store master key is never handed to a child.
  • Audit. Every access appends one JSONL row { ts, secret, command, backend, decision } — a decision record, never a value. command is the executable name + arg count; arguments are not logged (they are caller-controlled and may contain secrets Ferry never resolved), so nothing on the command line can accumulate in the audit log.
  • Zero runtime dependencies. Nothing but Node built-ins stands between your secret and the child.

What Ferry does NOT do

  • It only manages the secrets you DECLARE. By default the child still inherits the rest of Ferry's environment, so an undeclared ambient secret (say AWS_SECRET_ACCESS_KEY that Ferry doesn't know about) is passed through and is not redacted. Pass --clean-env (or cleanEnv: true) to forward only a minimal safe base env plus the injected secrets.
  • It does not stop a command you explicitly allowed from misusing a secret it legitimately received — allow scopes which command, not what that command then does with the value.
  • It does not defend against a malicious child reading /proc/self/environ, nor against a compromised host. It shrinks the blast radius from the agent's context/transcript, which is the agent-era leak that motivated it.
  • Your ferry.config.* is trusted code you author and is not sandboxed (same trust model as any config file); don't run ferry against a config from an untrusted repo, and don't print secrets inside your config.
  • The file backend's key comes from FERRY_FILE_KEY in v0.1; a real OS-keychain integration is post-v0.1.

Not in v0.1

Hosted service, secret rotation, cloud backends (AWS/GCP/Vault), team sync, GUI. Portable OSS CLI first — no service required.

Contributing & security

Contributions welcome — see CONTRIBUTING.md. Ferry brokers secrets, so please report vulnerabilities privately per SECURITY.md, not as a public issue.


Part of the Lucentive Labs open-source catalog — homepage & docs at https://labs.lucentive.io/libraries/ferry

MIT © 2026 Lucentive Labs.