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

@luckystack/secret-manager

v0.2.6

Published

Rotation-aware secret resolver client for LuckyStack. Commit `.env` pointers (e.g. OPENAI_KEY=OPENAI_AUTHORIZATION_KEY_V5) instead of real secrets; at boot this client resolves them against an external append-only secret-manager server and writes the real

Readme

@luckystack/secret-manager

Rotation-aware secret resolver client. Commit .env pointers instead of real secrets; resolve them against an external secret-manager server at boot. Part of LuckyStack.

@luckystack/secret-manager is the thin client that lives inside a LuckyStack app. Your committed .env holds pointers like OPENAI_KEY=OPENAI_AUTHORIZATION_KEY_V5 — never the real secret. At boot the client collects every pointer-shaped value, asks the server to resolve them in one request, and overwrites process.env with the real values. Rotating a secret means publishing a new version (..._V6) on the server; old git branches that still point at ..._V5 keep booting.

For the full design (pointer model, append-only versioning, the external server's wire contract), read docs/architecture.md.

Install

npm install @luckystack/secret-manager

Requires Node >= 20 (uses global fetch). For older Node, inject a polyfill via fetchImpl.

Quickstart

Call initSecretManager as the very first line of your server.ts, before any other framework code reads process.env.

import { initSecretManager } from '@luckystack/secret-manager';
import { bootstrapLuckyStack } from '@luckystack/server';

await initSecretManager({
  url: process.env.LUCKYSTACK_SECRET_MANAGER_URL!,
  token: { fromFile: '.secret-manager-token' }, // gitignored file, one line = the token
  source: 'hybrid', // try the server, keep local env on failure
});

const server = await bootstrapLuckyStack({ /* ... */ });
await server.listen();

In your committed .env:

OPENAI_KEY=OPENAI_AUTHORIZATION_KEY_V5
STRIPE_KEY=STRIPE_SECRET_KEY_V2

After initSecretManager resolves, process.env.OPENAI_KEY holds the real sk-... value.

Modes

| source | Behavior | | --- | --- | | 'remote' (default) | Resolve from the server. A missing pointer or an unreachable server throws — production hard-stop. | | 'local' | No network. Pointers are left untouched. Use in tests + offline dev. | | 'hybrid' | Try the server; on failure warn and leave whatever process.env already holds. |

A value that is not pointer-shaped (e.g. NODE_ENV=production, or a real secret you pasted locally) is treated as a literal and never sent to the server — so local overrides win automatically.

Dev hot reload (opt-in)

Pass a dev object to live-reload while a long-running dev process is up. Ignored when NODE_ENV === 'production'.

await initSecretManager({
  url: process.env.LUCKYSTACK_SECRET_MANAGER_URL!,
  token: { fromFile: '.secret-manager-token' },
  dev: {
    watch: true,          // re-read .env / .env.local on change (default true)
    pollIntervalMs: 5000, // also re-resolve every 5s (default off)
    // envFiles: ['.env', '.env.local'], // override which files are watched
  },
});
  • On file change (watch): the env files are re-parsed and applied — plain values (e.g. ENVIRONMENT=production, PORT=123) are injected straight into process.env (live config reload), and pointer-shaped values are re-resolved against the server. A pointer added or bumped after boot is picked up here — no restart.
  • On the poll interval (pollIntervalMs): the current pointers are re-resolved, catching server-side rotations. The interval lives in your config.ts, so it's changeable in one place.

So .env (plain config) and .env.local (pointers/secrets) both live-reload — .env values are injected as-is, .env.local pointers are resolved from the server.

Public API

| Export | Purpose | | --- | --- | | initSecretManager(config) | Boot-time entry point. Resolves pointer-shaped env values and writes the real values into process.env. | | refreshSecretManager() | Re-resolve the captured pointers against the server (the poll channel; call manually after an admin rotates a secret). | | reloadSecretManagerFromFiles() | Re-parse the configured env files and apply them — plain values injected, pointers resolved. The file-watch channel; callable manually. | | getCachedResolution() | Returns the last { fetchedAt, values } resolution (pointer -> value) for diagnostics, or null. | | resetSecretManagerForTests() | Test-only — clears module state and tears down dev watchers / timers. |

The token file

The shared bearer token is the only real secret on the developer machine. Keep it in a single-line file (e.g. .secret-manager-token) that is gitignored, and reference it with token: { fromFile: '.secret-manager-token' }. CI runners can inject the file from their secret store. You may also pass the token as a literal string if you read it from your own secret source.

Peer dependencies

  • None. This package speaks plain HTTP via global fetch and reads the token file with Node's built-in fs.
  • Optional: any fetch polyfill (e.g. undici) for non-Node-20 hosts — pass via SecretManagerConfig.fetchImpl.

Documentation

  • docs/architecture.md — pointer model, append-only versioning, the external server's POST /resolve wire contract, and what this package does NOT do.

License

MIT — see LICENSE.