Skip to content

Repository files navigation

secrets

Named bundles of environment variables, stored in the OS keychain, an encrypted file, or an age-encrypted vault — and injected into a child process without ever printing them.

npm i -g @phnx-labs/secrets-cli

secrets create prod --description "Production keys for the api stack"
secrets add prod STRIPE_API_KEY                 # prompts; value never echoes
secrets add prod LOG_LEVEL --value info         # a non-sensitive literal
secrets exec prod -- ./deploy.sh                # values ride the child env only

secrets runs on its own. It has no daemon, no fleet registry, and no dependency on any other tool; other programs drive it through a bounded private pipe (secrets __serve) or the SDK (@phnx-labs/secrets-cli).

Where things live

Everything hangs off SECRETS_HOME (default ~/.secrets):

Path What
secrets.yaml defaults (backend, policy, agent.*) and named provider connections
.cache/secrets/ the AES-256-GCM encrypted file store
.secrets-key/ the 0600 machine-local key for that store
vault.age the age-encrypted synced vault
.cache/helpers/secrets-agent/ the macOS broker's socket, pid and token
.cache/events.jsonl the value-free event log (secrets activity)

Point SECRETS_HOME at a directory that already holds this layout and the stores are adopted in place — nothing is copied or re-encrypted, and macOS Keychain items are keyed by service name, not by path.

Backends

  • keychain (macOS) — items live in the login keychain behind Touch ID. The broker (secrets unlock, secrets status) holds an unlocked bundle in memory for ~7 days so concurrent readers do not each prompt. secrets unlock <bundle> --scope <scope> narrows that grant to one opaque scope (a harness name, a job id); a reader passes the same scope (SECRETS_SCOPE) to see it. Without --scope the grant is global.
  • file — AES-256-GCM under a machine-local key; readable headlessly.
  • vault — age-encrypted file you sync yourself (secrets vault unlock).

Commands

secrets list [query]             secrets view <name>          secrets activity [name]
secrets create [name]            secrets add <bundle> <key>   secrets rotate <bundle> <key>
secrets remove <bundle> <key>    secrets rename <old> <new>   secrets delete <name>
secrets describe <name> [text]   secrets policy <bundle> [p]  secrets generate [length]
secrets exec <bundle> -- <cmd>   secrets get <item> [key]     secrets set <item>
secrets import [bundle] …        secrets export [bundle] …    secrets migrate
secrets unlock [names…]          secrets lock [names…]        secrets status
secrets start | stop             secrets vault unlock | lock  secrets push | pull | remote-list
secrets import-keyring           secrets migrate-acl          secrets rekey | rotate-passphrase
secrets mcp

secrets <command> --help prints an ordered example sequence before the flags.

Remote hosts

--host <target> on exec, import, export, list, view and unlock takes an OpenSSH alias or user@host, exactly as ssh would. exec --host resolves the bundle on that host and runs the child locally; to run the child remotely, put secrets exec on the other side of ssh. Credential transfer refuses a host whose key is not pinned (secrets hosts pin <target>).

Development

bun install
bash scripts/test.sh            # vitest, real stores and processes, no mocks
bash scripts/build.sh           # tsc -> dist/
bash scripts/install.sh         # side-by-side dev install as `secrets-dev`
bash scripts/release.sh <ver>   # dry run; --apply publishes and smoke-installs

See AGENTS.md for the contract this repository holds itself to.

About

Secrets CLI: portable secret bundles, native stores and provider-backed injection

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages