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

@ezecutin/secrets-cli

v0.1.0

Published

A local-first CLI for managing encrypted .env secrets.

Readme

secrets-cli

A local-first CLI for managing encrypted .env secrets. Store secret values in an AES-256-GCM encrypted vault that lives next to your project, commit the vault to git, and keep the key safe in your home directory.

No vendor accounts. No network calls. No magic.

Install

npm install -g secrets-cli

Requires Node 18 or later.

Quickstart

# 1. Create a vault in your project root
secrets init

# 2. Store a secret (prompts for the value — keeps it out of shell history)
secrets set DATABASE_URL

# 3. Pull secrets to .env.local for local development
secrets pull

# 4. See what changed between environments
secrets diff --env staging

How it works

secrets init generates a random 32-byte key and saves it to ~/.secrets/keys/<project>.key. Secret values are encrypted with AES-256-GCM and stored in .secrets.vault alongside your project. The vault is safe to commit — it is unreadable without the key.

your-project/
├── .secrets.vault       ← encrypted vault, commit this
├── .secrets/
│   └── config.json      ← project metadata, commit this
└── .env.local           ← decrypted output, git-ignored

The key lives at ~/.secrets/keys/<project>.key and never enters the repository. Back it up.

secrets init also adds safe local-secret patterns to .gitignore (.env, .env.local, .env.*.local, .secrets/tokens.json, and vault lock/tmp files). It does not ignore .env.example, so sanitized examples can stay in git.

Commands

secrets init [--force]

Create a new encrypted vault in the current directory. Writes:

  • .secrets.vault — the encrypted vault
  • .secrets/config.json — project name, used to locate the key file
  • ~/.secrets/keys/<project>.key — your 32-byte encryption key

Use --force to overwrite an existing vault (generates a new key — old vault becomes unreadable).

secrets set <key> [--env <env>] [--value <value>]

Store a secret. Prompts for the value by default so it does not appear in shell history.

secrets set DATABASE_URL
secrets set STRIPE_KEY --env staging
secrets set API_KEY --value "sk-..." --env production   # scripting only

Use --value only in automation where you control the environment. The value may appear in CI logs and process lists.

secrets get <key> [--env <env>]

Print the decrypted value to stdout. Safe to pipe.

secrets get DATABASE_URL
secrets get DATABASE_URL | pbcopy
DATABASE_URL=$(secrets get DATABASE_URL) node server.js

Exits with a non-zero status if the key is not found.

secrets list [--env <env>] [--show-values]

List all key names in an environment. Values are hidden by default.

secrets list
secrets list --env staging --show-values

secrets import <file> [--env <env>] [--force] [--allow-empty] [--dry-run]

Import all secrets from an .env file into the vault. Comments and blank lines are stripped. Prompts before overwriting existing keys unless --force is given.

secrets import .env
secrets import .env.production --env production --force
secrets import .env --dry-run   # preview without writing

Flags:

  • --force — overwrite existing keys without prompting
  • --allow-empty — include keys with empty string values (skipped by default)
  • --dry-run — show what would be written without touching the vault

secrets pull [--env <env>] [--output <file>] [--dry-run]

Decrypt all secrets from an environment and write them to a .env file (default: .env.local).

secrets pull
secrets pull --env staging --output .env.staging
secrets pull --dry-run   # list keys that would be written

secrets push [--env <env>] [--input <file>] [--force] [--allow-empty] [--dry-run]

Read a .env file and merge its values into the vault (default input: .env.local). Prompts before overwriting existing keys unless --force is given.

secrets push
secrets push --env staging --input .env.staging --force
secrets push --dry-run

secrets diff [--env <env>] [--env-a <env>] [--env-b <env>]

Compare two environments. Defaults to default vs the target env.

secrets diff --env staging
secrets diff --env-a default --env-b production

Output:

  KEY            DEFAULT  STAGING  STATUS
  ─────────────────────────────────────────────
  API_KEY        ✓        ✓        identical
  DATABASE_URL   ✓        ✓        values differ
  STRIPE_KEY     –        ✓        missing in default

secrets delete <key> [--env <env>] [--force]

Remove a key from the vault. Prompts for confirmation unless --force is given.

secrets delete OLD_KEY
secrets delete OLD_KEY --force

Global flags

| Flag | Description | | ------------------- | ---------------------------------------- | | -q, --quiet | Suppress all non-error output | | --non-interactive | Fail immediately if a prompt would occur | | --version | Print the version number |

Environment variables

| Variable | Description | | ------------------ | ---------------------------------------------------------- | | SECRETS_KEY_PATH | Override the key file location (useful in CI) | | CI | When set to any truthy value, disables interactive prompts |

CI usage

In CI, set SECRETS_KEY_PATH to the path of a key file injected as a secret, or use --value and --force to avoid prompts:

# GitHub Actions example
secrets pull --env production --output .env
- name: Write key file
  run: printf '%s\n' "$SECRETS_KEY" > /tmp/project.key
  env:
    SECRETS_KEY: ${{ secrets.SECRETS_KEY }}

- name: Pull secrets
  run: secrets pull --env production --output .env
  env:
    SECRETS_KEY_PATH: /tmp/project.key

.env file format

secrets-cli uses dotenv to parse .env files. Supported syntax:

KEY=value
KEY="value with spaces"
KEY='single quotes'
# comments are stripped
export KEY=value   # export prefix is ignored

Multiline values, unicode, values containing = and #, and quoted strings are all supported. Key ordering is not preserved — keys are written alphabetically.

Vault format

The vault is a YAML file with per-value encryption:

version: 1
project: my-app
environments:
  default:
    DATABASE_URL: enc:v1:<iv_b64>.<ciphertext_b64>.<tag_b64>
    API_KEY: enc:v1:<iv_b64>.<ciphertext_b64>.<tag_b64>
  staging:
    DATABASE_URL: enc:v1:<iv_b64>.<ciphertext_b64>.<tag_b64>

Key names are stored in plaintext so the vault is git-diffable. Only values are encrypted. Each value has a unique 12-byte IV so encrypting the same value twice produces different ciphertext.

The enc:v1: prefix identifies the format version. A future breaking change would use enc:v2:.

Security model

See SECURITY.md for the full threat model. The short version:

  • Protects secrets if someone can read the repository (vault without key = unreadable)
  • Does not protect against malware running as your user, or someone who has both the vault and the key
  • Key names are intentionally visible in the vault
  • AES-256-GCM provides authenticated encryption — tampered ciphertext is detected

Back up your key file. If ~/.secrets/keys/<project>.key is lost, the vault cannot be recovered.

License

MIT