{"id":50479506,"url":"https://github.com/davidnussio/envsec","last_synced_at":"2026-06-01T16:30:55.145Z","repository":{"id":344859907,"uuid":"1183174440","full_name":"davidnussio/envsec","owner":"davidnussio","description":"Secure CLI tool for managing environment secrets using native OS credential stores (macOS Keychain, Linux Secret Service, Windows Credential Manager)","archived":false,"fork":false,"pushed_at":"2026-04-21T17:08:29.000Z","size":1359,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-21T18:32:00.375Z","etag":null,"topics":["cli","credential-manager","cross-platform","devtools","dotenv","effect-ts","environment-variables","keychain","nodejs","secrets","secrets-management","typescript"],"latest_commit_sha":null,"homepage":"https://envsec.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidnussio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-16T10:50:29.000Z","updated_at":"2026-04-21T17:08:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/davidnussio/envsec","commit_stats":null,"previous_names":["davidnussio/secenv"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/davidnussio/envsec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidnussio%2Fenvsec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidnussio%2Fenvsec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidnussio%2Fenvsec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidnussio%2Fenvsec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidnussio","download_url":"https://codeload.github.com/davidnussio/envsec/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidnussio%2Fenvsec/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33784625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","credential-manager","cross-platform","devtools","dotenv","effect-ts","environment-variables","keychain","nodejs","secrets","secrets-management","typescript"],"created_at":"2026-06-01T16:30:54.116Z","updated_at":"2026-06-01T16:30:55.139Z","avatar_url":"https://github.com/davidnussio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# envsec\n\nSecure environment secrets management using native OS credential stores.\n\n## Demo\n\n\u003c!-- https://github.com/user-attachments/assets/ce744e1f-7a6f-4571-8bdc-9dc63cf42ed8 --\u003e\n\n![Image](https://raw.githubusercontent.com/davidnussio/envsec/328e0773aad6a4c10f399f40f1a750904df334c3/assets/terminal-1.gif)\n\n## Features\n\n- Store secrets in your OS native credential store (not plain text files)\n- Cross-platform: macOS, Linux, Windows\n- Organize secrets by context (e.g. `myapp.dev`, `stripe-api.prod`, `work.staging`)\n- Track secret metadata (key names, timestamps) via SQLite\n- Search contexts and secrets with glob patterns\n- Run commands with secret interpolation\n- Save and rerun commands with `cmd` (search, list, run, delete)\n- Export secrets to `.env` files (with generation tracking via `audit`)\n- Export secrets as shell environment variables (`eval $(envsec env)`)\n- Load secrets from `.env` files (with conflict detection)\n- Share secrets encrypted with GPG for team members\n- Interactive terminal UI (`envsec tui`) for managing secrets without memorizing commands\n\n## Packages\n\nThis is a monorepo containing the following packages:\n\n| Package | Description | npm |\n|---------|-------------|-----|\n| [`envsec`](./packages/cli) | CLI tool for managing secrets | [![npm](https://img.shields.io/npm/v/envsec)](https://www.npmjs.com/package/envsec) |\n| [`@envsec/sdk`](./packages/sdk) | Node.js / Bun SDK for loading secrets programmatically | [![npm](https://img.shields.io/npm/v/@envsec/sdk)](https://www.npmjs.com/package/@envsec/sdk) |\n| [`@envsec/core`](./packages/core) | Core engine — OS credential store adapters + metadata DB | [![npm](https://img.shields.io/npm/v/@envsec/core)](https://www.npmjs.com/package/@envsec/core) |\n| [`@envsec/tui`](./packages/tui) | Interactive terminal UI for secrets management | [![npm](https://img.shields.io/npm/v/@envsec/tui)](https://www.npmjs.com/package/@envsec/tui) |\n\n## SDK Quick Start\n\nFor programmatic access to secrets from Node.js or Bun, use `@envsec/sdk`:\n\n```bash\nnpm install @envsec/sdk\n```\n\n```typescript\nimport { loadSecrets } from \"@envsec/sdk\";\n\n// Load and inject into process.env\nawait loadSecrets({ context: \"myapp.dev\", inject: true });\n\n// Or use the client for full control\nimport { EnvsecClient } from \"@envsec/sdk\";\nconst client = await EnvsecClient.create({ context: \"myapp.dev\" });\nconst apiKey = await client.get(\"api.key\");\nawait client.close();\n```\n\nSee the full [SDK documentation](./packages/sdk/README.md) for all APIs, multi-context support, and options.\n\n## Requirements\n\n- Node.js \u003e= 22\n\n### macOS\n\nNo extra dependencies. Uses the built-in Keychain via the `security` CLI tool.\n\n### Linux\n\nRequires `libsecret-tools` (provides the `secret-tool` command), which talks to GNOME Keyring, KDE Wallet, or any Secret Service API provider via D-Bus.\n\n```bash\n# Debian / Ubuntu\nsudo apt install libsecret-tools\n\n# Fedora\nsudo dnf install libsecret\n\n# Arch\nsudo pacman -S libsecret\n```\n\nA running D-Bus session and a keyring daemon (e.g. `gnome-keyring-daemon`) must be active. Most desktop environments handle this automatically.\n\n### Windows\n\nNo extra dependencies. Uses the built-in Windows Credential Manager via `cmdkey` and PowerShell.\n\n## Installation\n\n### Homebrew (macOS / Linux)\n\n```bash\nbrew tap davidnussio/homebrew-tap\nbrew install envsec\n```\n\n### npm\n\n```bash\nnpm install -g envsec\n```\n\n### npx (no install)\n\n```bash\nnpx envsec\n```\n\n### mise\n\n```bash\nmise use -g npm:envsec\n```\n\n## Usage\n\nMost commands require a context specified with `--context` (or `-c`).\nA context is a free-form label for grouping secrets — e.g. `myapp.dev`, `stripe-api.prod`, `work.staging`.\n\n### Global options\n\nThese options are available on all commands:\n\n- `--context`, `-c` — Context name (e.g. `myapp.dev`, `stripe-api.prod`). Also reads `ENVSEC_CONTEXT` env var\n- `--debug`, `-d` — Enable debug logging\n- `--json` — Output in JSON format for scripting\n- `--db` — Path to SQLite database file (default: `~/.envsec/store.sqlite`). Also reads `ENVSEC_DB` env var\n\n### Custom database path\n\nBy default, metadata is stored at `~/.envsec/store.sqlite`. You can override this with `--db` or the `ENVSEC_DB` environment variable:\n\n```bash\n# Use a project-local database\nenvsec --db ./local-store.sqlite -c myapp.dev list\n\n# Or via environment variable\nexport ENVSEC_DB=/shared/team/envsec.sqlite\nenvsec -c myapp.dev list\n```\n\nThe `--db` flag takes precedence over `ENVSEC_DB`. Use cases include per-project databases, team-shared databases on network drives, and CI/CD with ephemeral storage.\n\n### Add a secret\n\nStore a secret in the OS credential store.\n\n- `\u003ckey\u003e` — Secret key name (e.g. `api.key`, `db.password`)\n- `--value`, `-v` — Value to store (omit for interactive masked prompt)\n- `--expires`, `-e` — Expiry duration (e.g. `30m`, `2h`, `7d`, `4w`, `3mo`, `1y`)\n\n```bash\n# Store a value inline\nenvsec -c myapp.dev add api.key --value \"sk-abc123\"\n\n# Or use the short alias\nenvsec -c myapp.dev add api.key -v \"sk-abc123\"\n\n# Omit --value for an interactive masked prompt\nenvsec -c myapp.dev add api.key\n\n# Set an expiry duration with --expires (-e)\nenvsec -c myapp.dev add api.key -v \"sk-abc123\" --expires 30d\n\n# Supported duration units: m (minutes), h (hours), d (days), w (weeks), mo (months), y (years)\n# Combinable: 1y6mo, 2w3d, 1d12h\nenvsec -c myapp.dev add api.key -v \"sk-abc123\" -e 6mo\n```\n\n### Get a secret\n\nRetrieve a secret value from the OS credential store.\n\n- `\u003ckey\u003e` — Secret key name to retrieve\n- `--quiet`, `-q` — Print only the raw value (no warnings or extra output)\n- `--json` — Output in JSON format (includes context, key, value, expires_at)\n\n```bash\nenvsec -c myapp.dev get api.key\n\n# Print only the raw value (no warnings or extra output)\nenvsec -c myapp.dev get api.key --quiet\nenvsec -c myapp.dev get api.key -q\n```\n\n### Delete a secret\n\nRemove a secret from the OS credential store.\n\n- `\u003ckey\u003e` — Secret key name to delete (optional if `--all` is used)\n- `--yes`, `-y` — Skip confirmation prompt\n- `--all` — Delete all secrets in the context\n\n```bash\nenvsec -c myapp.dev delete api.key\n\n# or use the alias\nenvsec -c myapp.dev del api.key\n```\n\n### Rename a secret\n\nRename a secret key within the same context. The value and expiry metadata are preserved.\n\n- `\u003cold-key\u003e` — Current secret key name\n- `\u003cnew-key\u003e` — New secret key name\n- `--force`, `-f` — Overwrite target if it already exists\n\n```bash\n# Rename a key\nenvsec -c myapp.dev rename old.key new.key\n\n# Overwrite target if it already exists\nenvsec -c myapp.dev rename old.key existing.key --force\n```\n\n### List all secrets in a context\n\nList all secret keys and metadata in a context.\n\n- `--json` — Output in JSON format\n\n```bash\nenvsec -c myapp.dev list\n```\n\n### List all contexts\n\nList all available contexts with secret counts.\n\n- `--json` — Output in JSON format\n\n```bash\n# Without --context, lists all available contexts with secret counts\nenvsec list\n```\n\n### Search secrets\n\nSearch secrets or contexts using glob patterns.\n\n- `\u003cpattern\u003e` — Glob pattern to search for (e.g. `api.*`, `myapp.*`)\n- `--json` — Output in JSON format\n\n```bash\n# Search secrets within a context\nenvsec -c myapp.dev search \"api.*\"\n\n# Search contexts by pattern (without --context)\nenvsec search \"myapp.*\"\n```\n\n### Move secrets between contexts\n\nMove secrets from one context to another. The source secrets are removed after moving.\n\n- `\u003cpattern\u003e` — Glob pattern or exact key to move (optional if `--all` is used)\n- `--to`, `-t` — Target context to move secrets to\n- `--all` — Move all secrets from source context\n- `--force`, `-f` — Overwrite existing secrets in the target context\n- `--yes`, `-y` — Skip confirmation prompt\n\n```bash\n# Move a single secret\nenvsec -c myapp.dev move api.token --to myapp.prod\n\n# Move secrets matching a glob pattern\nenvsec -c myapp.dev move \"redis.*\" --to myapp.prod -y\n\n# Move all secrets from one context to another\nenvsec -c myapp.dev move --all --to myapp.prod -y\n\n# Overwrite existing secrets in the target context\nenvsec -c myapp.dev move \"redis.*\" --to myapp.prod --force -y\n```\n\n### Copy secrets between contexts\n\nCopy secrets from one context to another. The source secrets remain intact.\n\n- `\u003cpattern\u003e` — Glob pattern or exact key to copy (optional if `--all` is used)\n- `--to`, `-t` — Target context to copy secrets to\n- `--all` — Copy all secrets from source context\n- `--force`, `-f` — Overwrite existing secrets in the target context\n- `--yes`, `-y` — Skip confirmation prompt\n\n```bash\n# Copy a single secret\nenvsec -c myapp.dev copy api.token --to myapp.staging\n\n# Copy secrets matching a glob pattern\nenvsec -c myapp.dev copy \"redis.*\" --to myapp.staging -y\n\n# Copy all secrets from one context to another\nenvsec -c myapp.dev copy --all --to myapp.staging -y\n\n# Overwrite existing secrets in the target context\nenvsec -c myapp.dev copy \"redis.*\" --to myapp.staging --force -y\n```\n\n### Run a command with secrets\n\nExecute a command with secret values interpolated via placeholders or injected as environment variables.\n\n- `\u003ccommand\u003e` — Command to execute. Use `{key}` placeholders for secret interpolation\n- `--inject`, `-i` — Inject all context secrets as environment variables (`KEY.NAME` → `KEY_NAME`)\n- `--save`, `-s` — Save this command for later use\n- `--name`, `-n` — Name for the saved command (prompted interactively if omitted with `--save`)\n\n```bash\n# Placeholders {key} are resolved with secret values before execution\nenvsec -c myapp.dev run 'curl {api.url} -H \"Authorization: Bearer {api.token}\"'\n\n# Any {dotted.key} in the command string is replaced with its value\nenvsec -c myapp.prod run 'psql {db.connection_string}'\n\n# Inject ALL context secrets as environment variables (KEY.NAME → KEY_NAME)\nenvsec -c myapp.dev run --inject 'node server.js'\nenvsec -c myapp.dev run -i 'docker compose up'\n\n# Combine --inject with placeholders\nenvsec -c myapp.dev run --inject 'curl {api.url} -H \"Authorization: Bearer $API_TOKEN\"'\n\n# Save the command for later use with --save (-s) and --name (-n)\nenvsec -c myapp.dev run --save --name deploy 'kubectl apply -f - \u003c\u003c\u003c {k8s.manifest}'\n\n# If you use --save without --name, you'll be prompted interactively\nenvsec -c myapp.dev run --save 'psql {db.connection_string}'\n```\n\nIf any placeholder references a secret that doesn't exist, the command won't execute and you'll see a clear error:\n\n```\n❌ Missing secrets in context \"myapp.dev\":\n  - api.url\n  - api.token\n\nAdd them with: envsec -c myapp.dev add \u003ckey\u003e\n```\n\n### Saved commands\n\nSaved commands live under the `cmd` subcommand, keeping them separate from secret operations.\n\n#### cmd list\n\nList all saved commands.\n\n```bash\nenvsec cmd list\n```\n\n#### cmd run\n\nRun a saved command (uses the context it was saved with).\n\n- `\u003cname\u003e` — Name of the saved command to execute\n- `--override-context`, `-o` — Override the saved context at execution time\n- `--quiet`, `-q` — Suppress informational output (print only command output)\n- `--inject`, `-i` — Inject all context secrets as environment variables\n\n```bash\nenvsec cmd run deploy\n\n# Run quietly (suppress informational output like \"Resolved N secret(s)\")\nenvsec cmd run deploy --quiet\nenvsec cmd run deploy -q\n\n# Override the context at execution time\nenvsec cmd run deploy --override-context myapp.prod\nenvsec cmd run deploy -o myapp.prod\n\n# Inject all context secrets as env vars when running a saved command\nenvsec cmd run deploy --inject\nenvsec cmd run deploy -i\n```\n\n#### cmd search\n\nSearch saved commands by name or command string.\n\n- `\u003cpattern\u003e` — Search pattern\n- `--name`, `-n` — Search only in command names\n- `--command`, `-m` — Search only in command strings\n\n```bash\nenvsec cmd search psql\n\n# Search only by name\nenvsec cmd search deploy -n\n\n# Search only by command string\nenvsec cmd search kubectl -m\n```\n\n#### cmd delete\n\nDelete a saved command.\n\n- `\u003cname\u003e` — Name of the command to delete\n\n```bash\nenvsec cmd delete deploy\n```\n\n### Generate a .env file\n\nExport all secrets from a context to a `.env` file.\n\n- `--output`, `-o` — Output file path (default: `.env`)\n\n```bash\n# Creates .env with all secrets from the context\nenvsec -c myapp.dev env-file\n\n# Specify a custom output path\nenvsec -c myapp.dev env-file --output .env.local\n```\n\nKeys are converted to `UPPER_SNAKE_CASE` (e.g. `api.token` → `API_TOKEN`).\n\n### Export secrets as environment variables\n\nOutput export statements for use with `eval` or shell sourcing.\n\n- `--shell`, `-s` — Target shell syntax: `bash` (default), `zsh`, `fish`, `powershell`\n- `--unset`, `-u` — Output unset/remove commands instead of export\n\n```bash\n# Output export statements for eval (bash/zsh)\neval $(envsec -c myapp.dev env)\n\n# Specify target shell syntax\nenvsec -c myapp.dev env --shell fish\nenvsec -c myapp.dev env --shell powershell\n\n# Output unset commands to clean up exported variables\neval $(envsec -c myapp.dev env --unset)\n\n# Combine shell and unset\nenvsec -c myapp.dev env --unset --shell fish\n```\n\nSupported shells: `bash` (default), `zsh`, `fish`, `powershell`. Keys are converted to `UPPER_SNAKE_CASE` (e.g. `api.token` → `API_TOKEN`). Output goes to stdout so it can be piped to `eval` or sourced directly — no file is written to disk.\n\n### Start a secrets-scoped shell session\n\nSpawn an interactive subshell with all secrets from the context injected as\nenvironment variables. When you `exit`, the secrets are gone — no cleanup needed.\n\n- `--shell`, `-s` — Shell to spawn (`bash`, `zsh`, `fish`, `powershell`). Default: auto-detect\n- `--no-inherit` — Do not inherit parent environment variables\n- `--quiet`, `-q` — Suppress startup/exit banner\n\n```bash\nenvsec -c myapp.dev shell\n```\n\n```\n▶ envsec shell — context: myapp.dev (8 secrets loaded)\nType 'exit' or press Ctrl+D to leave the session.\n\n(envsec:myapp.dev) ~ $ echo $DATABASE_URL\npostgres://user:pass@localhost/mydb\n\n(envsec:myapp.dev) ~ $ exit\n→ Exiting envsec shell — secrets cleared.\n```\n\n```bash\n# Force a specific shell\nenvsec -c myapp.dev shell --shell zsh\n\n# Only envsec secrets in env (no parent variables, except PATH)\nenvsec -c myapp.dev shell --no-inherit\n\n# Suppress the startup/exit banner\nenvsec -c myapp.dev shell --quiet\n```\n\nThe variable `ENVSEC_CONTEXT` is always set inside the session, so you can\nreference it in scripts or prompt customizations.\n\n### Load secrets from a .env file\n\nImport secrets from a `.env` file into a context.\n\n- `--input`, `-i` — Input `.env` file path (default: `.env`)\n- `--force`, `-f` — Overwrite existing secrets without prompting\n- `--batch`, `-b` — Batch mode: defer database persistence until all secrets are imported\n\n```bash\n# Import secrets from .env into the context\nenvsec -c myapp.dev load\n\n# Specify a custom input file\nenvsec -c myapp.dev load --input .env.local\n\n# Overwrite existing secrets without warning\nenvsec -c myapp.dev load --force\n```\n\nKeys are converted from `UPPER_SNAKE_CASE` to `dotted.lowercase` (e.g. `API_TOKEN` → `api.token`). If a key already exists, it is skipped with a warning unless `--force` (`-f`) is provided.\n\n### Share secrets (GPG encrypted)\n\nEncrypt all secrets from a context for a team member using GPG.\n\n- `--encrypt-to` — GPG recipient key (email, key ID, or fingerprint) to encrypt for\n- `--output`, `-o` — Output file path (default: stdout). Use `-` for stdout explicitly\n- `--json` — Use JSON format inside the encrypted payload (default: `.env` format)\n\n```bash\n# Encrypt all secrets from a context for a team member\nenvsec -c myapp.dev share --encrypt-to alice@example.com\n\n# Save encrypted output to a file\nenvsec -c myapp.dev share --encrypt-to alice@example.com -o secrets.enc\n\n# Use JSON format inside the encrypted payload\nenvsec -c myapp.dev --json share --encrypt-to alice@example.com -o secrets.enc\n```\n\nThe recipient can decrypt with `gpg --decrypt secrets.enc` and pipe the result into `envsec load`. By default the encrypted payload uses `.env` format (`KEY=\"value\"`); with `--json` it uses a structured JSON object. Requires GPG to be installed and the recipient's public key to be in your keyring.\n\n### Audit secrets for expiry\n\nCheck for expired or expiring secrets and tracked `.env` file exports.\n\n- `--within`, `-w` — Show secrets expiring within this duration (default: `30d`). Use `0d` to show only already-expired\n- `--json` — Output in JSON format\n\n```bash\n# Check for expired or expiring secrets in a context (default window: 30 days)\nenvsec -c myapp.dev audit\n\n# Specify a custom window\nenvsec -c myapp.dev audit --within 7d\n\n# Show only already-expired secrets\nenvsec -c myapp.dev audit --within 0d\n\n# Audit across all contexts (omit --context)\nenvsec audit\n\n# JSON output\nenvsec -c myapp.dev audit --json\n```\n\nSecrets with an `--expires` duration set via `envsec add` are tracked in metadata. The `audit` command scans for secrets that are already expired or will expire within the specified window. The `get` and `list` commands also display expiry warnings inline.\n\nThe `audit` command also tracks generated `.env` files. Every time `env-file` is used, the output path, context, and timestamp are recorded. The audit output includes a second section listing these files. If a tracked `.env` file no longer exists on disk, audit automatically removes it from the metadata and reports the cleanup.\n\n### Generate a random secret\n\nGenerate a cryptographically secure random secret, optionally storing it.\n\n- `\u003ckey\u003e` — Secret key name (optional; omit for standalone password generation)\n- `--length`, `-l` — Length of the generated secret (default: `32`)\n- `--prefix`, `-p` — Prefix to prepend to the generated secret (e.g. `sk_`)\n- `--expires`, `-e` — Expiry duration (e.g. `30m`, `2h`, `7d`, `4w`, `3mo`, `1y`)\n- `--alphanumeric`, `-a` — Use only alphanumeric characters `[a-zA-Z0-9]` (default)\n- `--special`, `-s` — Include common special characters `[a-zA-Z0-9!@#$%^\u0026*]`\n- `--all-chars`, `-A` — Use all printable ASCII characters for maximum entropy\n\n```bash\n# Generate and store a 32-char alphanumeric secret\nenvsec -c myapp.dev secret api.key\n\n# Custom length and prefix\nenvsec -c myapp.dev secret api.key --prefix \"sk_\" --length 48\n\n# Character sets:\n#   --alphanumeric (-a)  [a-zA-Z0-9] (default)\n#   --special (-s)       [a-zA-Z0-9] + !@#$%^\u0026*\n#   --all-chars (-A)     all printable ASCII\nenvsec -c myapp.dev secret db.password --special --length 64\n\n# With expiry\nenvsec -c myapp.dev secret api.key --prefix \"sk_\" -l 48 --expires 90d\n\n# Standalone password generator (no store, just print)\nenvsec secret --length 32\nenvsec secret --special --length 64 --prefix \"pk_\"\n```\n\nWhen both context and key are provided, the generated value is stored and printed. Without either, the raw value goes to stdout — useful for piping to `pbcopy`, `xclip`, or other tools.\n\n### Interactive TUI\n\nenvsec includes a full-screen terminal UI for managing secrets interactively — no need to memorize commands.\n\n```bash\n# Launch the TUI\nenvsec tui\n\n# Launch with a pre-selected context\nenvsec -c myapp.dev tui\n```\n\nThe TUI provides eight screens accessible from the main menu:\n\n- **Contexts** — browse all contexts, set active context with `s`, clear context with `x`, view secret counts, delete entire contexts\n- **Secrets** — list secrets in a table, reveal values, add or delete secrets\n- **Add Secret** — interactive form with masked input and optional expiry duration\n- **Search** — glob pattern search across secrets or contexts\n- **Saved Commands** — list, view, and delete saved command templates\n- **Audit** — check for expired/expiring secrets, review tracked `.env` file exports\n- **Import .env** — load secrets from a `.env` file into the current context\n- **Export .env** — export secrets to a `.env` file (tracked for audit)\n\nKeyboard shortcuts:\n\n| Key | Action |\n|-----|--------|\n| `↑` / `↓` | Navigate menu items and table rows |\n| `Enter` | Select / confirm |\n| `c` | Open contexts view (main menu) |\n| `s` | Set selected as active context (contexts view) |\n| `x` | Clear active context (contexts view) |\n| `a` | Add a new secret (secrets view) |\n| `d` | Delete selected item |\n| `r` | Reveal secret value (detail view) |\n| `Esc` | Go back / cancel |\n| `q` | Quit the TUI |\n\n### Diagnose your setup\n\nRun health checks to verify your envsec installation.\n\n- `--json` — Output in JSON format for scripting\n\n```bash\n# Run all health checks\nenvsec doctor\n\n# JSON output for scripting\nenvsec --json doctor\n```\n\nThe `doctor` command verifies your envsec installation is working correctly. It checks:\n- Platform support and Node.js version\n- Credential store availability (macOS Keychain, Linux secret-tool, Windows cmdkey)\n- Keychain read/write access\n- Database path, permissions, and schema integrity\n- Orphaned secrets (metadata without keychain entry)\n- Expired secrets\n- Environment variables (`ENVSEC_DB`, `ENVSEC_CONTEXT`)\n- Current shell\n\n### Shell completions\n\nenvsec supports dynamic tab completion for bash, zsh, and fish. Completions are context-aware: they suggest your actual context names, secret keys, and saved command names in real time by querying the metadata database.\n\n```bash\n# Bash (add to ~/.bashrc)\neval \"$(envsec --completions bash)\"\n\n# Zsh (add to ~/.zshrc)\neval \"$(envsec --completions zsh)\"\n\n# Fish (add to ~/.config/fish/config.fish)\nenvsec --completions fish | source\n```\n\nWhat gets completed dynamically:\n- `--context` / `-c` — lists all your contexts\n- Secret key arguments (`get`, `add`, `delete`) — lists keys for the current context\n- `cmd run` / `cmd delete` — lists saved command names\n- `--override-context` / `-o` — lists contexts for `cmd run`\n- Subcommands, flags, and static choices (shells, etc.) are also completed\n\n## Comparison\n\nHow does envsec compare to other tools for managing environment secrets?\n\n| Feature | envsec | dotenv / dotenvx | 1Password CLI (`op`) |\n|---|---|---|---|\n| Secret storage | OS credential store (Keychain, Secret Service, Credential Manager) | `.env` files on disk (dotenvx adds encryption) | 1Password cloud vault |\n| Encryption at rest | Delegated to OS (Keychain, GNOME Keyring, DPAPI) | None (dotenv) / ECIES per-file (dotenvx) | AES-256 in 1Password cloud |\n| Secrets on disk | Never — values go straight to OS credential store | Always — `.env` files are plaintext by default | Never locally (fetched at runtime from cloud) |\n| Offline access | Full — secrets are local in OS store | Full — files are local | Requires network (cached items available offline in app) |\n| Account / subscription | None — free, open source, no signup | Free (dotenv) / free open source (dotenvx) | Paid subscription (from ~$3/mo individual, ~$8/user/mo business) |\n| Cross-platform | macOS, Linux, Windows | Any platform with Node.js / any runtime (dotenvx) | macOS, Linux, Windows |\n| Context / environment organization | Contexts (e.g. `myapp.dev`, `stripe.prod`) | Separate `.env` files per environment | Vaults and items |\n| Run commands with secrets | `envsec run` — placeholder interpolation + `--inject` env vars | `dotenvx run -- cmd` — injects from encrypted `.env` | `op run -- cmd` — injects via secret references |\n| Export to `.env` file | `envsec env-file` (tracked for audit) | Native format — `.env` files are the source of truth | `op inject --out-file` |\n| Import from `.env` file | `envsec load` (with conflict detection) | N/A — `.env` is the primary store | Manual item creation |\n| Shell env export | `eval $(envsec env)` — bash, zsh, fish, powershell | `dotenvx run` or `node -r dotenv/config` | `op run --env-file` |\n| Interactive shell session | `envsec shell` — scoped subshell with auto-cleanup | Not built-in | Not built-in |\n| Secret search | Glob patterns on keys and contexts | Not built-in | `op item list --tags/--category` filtering |\n| Expiry / rotation audit | `envsec audit` — expired, expiring, tracked `.env` files | Not built-in | Watchtower (in app, not CLI) |\n| Saved commands | `envsec cmd` — save, list, search, run, delete | Not built-in | Not built-in |\n| Move / copy secrets | `envsec move` and `envsec copy` between contexts | Manual file copy | `op item move` between vaults |\n| Rename secrets | `envsec rename` (preserves value and metadata) | Manual edit of `.env` file | `op item edit` |\n| GPG-encrypted sharing | `envsec share --encrypt-to` | Encrypted `.env` files committed to git (dotenvx) | Built-in vault sharing, team provisioning |\n| Interactive TUI | `envsec tui` — full-screen terminal UI | Not built-in | Not built-in |\n| Health diagnostics | `envsec doctor` — checks platform, keychain, DB integrity | Not built-in | Not built-in |\n| Shell completions | Dynamic (contexts, keys, commands) for bash, zsh, fish | Not built-in | Static completions for bash, zsh, fish, powershell |\n| SDK / programmatic access | `@envsec/sdk` for Node.js / Bun | `require('dotenv').config()` — core use case | 1Password SDKs (Node.js, Python, Go, etc.) |\n| Team / multi-user | GPG sharing (manual) | Git-based sharing with encrypted `.env` (dotenvx) | Built-in team management, RBAC, audit logs |\n\u003c!-- | CI/CD integration | Standard CLI — works anywhere Node.js runs | `dotenvx run` in any CI pipeline | Service accounts, native CI/CD integrations | --\u003e\n| Biometric auth | Inherits OS biometrics (e.g. macOS Keychain unlock) | None | Fingerprint / Touch ID via app integration |\n| Metadata tracking | SQLite (key names, timestamps — never values) | None | Cloud-based item history and audit logs |\n\nIn short: dotenv is the simplest approach (files on disk), 1Password CLI is the most feature-rich for teams with cloud sync and RBAC, and envsec sits in between — offering OS-native encryption with zero accounts, zero cloud dependencies, and a developer-focused workflow that goes beyond what `.env` files can do.\n\n## How it works\n\nSecrets are stored in the native OS credential store. The backend is selected automatically based on the platform:\n\n| OS      | Backend                        | Tool / API                          |\n|---------|--------------------------------|-------------------------------------|\n| macOS   | Keychain                       | `security` CLI                      |\n| Linux   | Secret Service API (D-Bus)     | `secret-tool` (libsecret)           |\n| Windows | Credential Manager             | `cmdkey` + PowerShell (advapi32)    |\n\nMetadata (key names, timestamps) is kept in a SQLite database at `~/.envsec/store.sqlite` (configurable via `--db` or `ENVSEC_DB`). Keys must contain at least one dot separator (e.g., `service.account`) which maps to the credential store's service/account structure.\n\n## Security\n\nenvsec is built around a simple principle: your secrets belong in your OS, not in dotfiles. Every design decision starts from that foundation.\n\n### How envsec protects your secrets\n\n**OS-native encryption, zero custom crypto.** Secret values are stored directly in macOS Keychain, GNOME Keyring / KDE Wallet, or Windows Credential Manager. envsec never invents its own encryption — it delegates to the battle-tested credential stores your operating system already provides, protected by your user session and (on macOS) the login keychain.\n\n**Full Unicode support.** Secret values can contain any Unicode characters, including emoji and accented letters. Values are base64-encoded before being stored in the OS credential store, avoiding platform-specific encoding quirks (e.g. macOS `security` CLI hex-encoding non-ASCII output). Legacy plaintext secrets are read transparently for backward compatibility.\n\n**Secrets never touch disk as plaintext.** Values go straight from your terminal into the OS credential store. They are never written to config files, logs, or intermediate storage.\n\n**No secrets in terminal output.** The `list` and `search` commands display key names only — values are never printed. This keeps secrets out of scrollback buffers, screen recordings, and shoulder-surfing range.\n\n**Safe command execution.** The `run` command injects secrets as environment variables of the child process rather than interpolating them into the command string. This means secret values don't appear in `ps` output or shell history. If any referenced secret is missing, the command is blocked entirely — no partial execution with incomplete credentials.\n\n**Input validation and injection prevention.** Context names are validated against a strict allowlist (alphanumeric, dots, hyphens, underscores) with path traversal and prototype pollution checks. All SQLite queries use prepared statements with bind parameters, preventing SQL injection. PowerShell arguments on Windows are escaped to guard against command injection.\n\n**Restrictive file permissions.** The metadata directory (`~/.envsec/`) is created with `0700` permissions and the SQLite database with `0600`, limiting access to the owning user.\n\n### Known limitations and areas for improvement\n\nWe believe in being upfront about what envsec does not yet cover. These are real trade-offs, not bugs — and understanding them helps you make informed decisions.\n\n**Metadata is visible.** The SQLite database at `~/.envsec/store.sqlite` stores key names, context names, and timestamps — never secret values, but enough to reveal *what* secrets exist. Saved command templates (with `{key}` placeholders) are also stored there. If metadata confidentiality matters to you, ensure your home directory is on an encrypted volume.\n\n**`env-file` exports are plaintext.** The `env-file` command writes secret values to a `.env` file on disk. This is inherently sensitive — treat the output file accordingly and never commit it to version control. Consider it a convenience bridge, not a storage mechanism.\n\n**Shell execution carries inherent risk.** The `run` command passes your command template through `/bin/sh` (or `cmd.exe` on Windows). If the template itself comes from untrusted input, shell injection is possible. Only run command templates you wrote or trust.\n\n**No cross-context access control.** Any process running as your OS user can read all secrets across all contexts. envsec relies on OS-level user isolation — it does not add its own authorization layer between contexts.\n\n**Linux headless environments.** On Linux, envsec depends on an active D-Bus session and a keyring daemon (e.g. `gnome-keyring-daemon`). In containers or headless servers without a graphical session, the keyring may be unavailable or may store secrets with weaker protection.\n\n**Encryption depends on your OS.** envsec adds no additional at-rest encryption beyond what the native credential store provides. On systems without full-disk encryption, an attacker with physical access could potentially extract secrets from the keychain. We recommend enabling full-disk encryption (FileVault, LUKS, BitLocker) for the strongest protection.\n\n## Development\n\n### Prerequisites\n\n- Node.js \u003e= 22\n- pnpm\n\n### Setup\n\n```bash\ngit clone https://github.com/davidnussio/envsec.git\ncd envsec\npnpm install\npnpm run build\n```\n\n### Project Structure\n\n```\npackages/\n  cli/     → envsec CLI (published as `envsec`)\n  sdk/     → Node.js/Bun SDK (published as `@envsec/sdk`)\n  core/    → Core engine, shared by CLI and SDK (published as `@envsec/core`)\n  tui/     → Interactive terminal UI (published as `@envsec/tui`)\napps/\n  website/ → Documentation website\n```\n\n### Common commands\n\n```bash\n# Build all packages\npnpm run build\n\n# Lint and format check (all packages)\npnpm run check\n\n# Auto-fix lint and formatting\npnpm run fix\n\n# Release (build + changeset publish)\npnpm run release\n```\n\n### Running locally without installing\n\nCreate a temporary alias to use the local build as if it were installed globally:\n\n```bash\n# Bash / Zsh\nalias envsec=\"node $(pwd)/packages/cli/dist/main.js\"\n\n# Fish\nalias envsec \"node (pwd)/packages/cli/dist/main.js\"\n```\n\n### Testing shell completions locally\n\nAfter building and setting up the alias, load the completions in your current session:\n\n```bash\n# Bash\nalias envsec=\"node $(pwd)/packages/cli/dist/main.js\"\neval \"$(envsec --completions bash)\"\n\n# Zsh\nalias envsec=\"node $(pwd)/packages/cli/dist/main.js\"\neval \"$(envsec --completions zsh)\"\n\n# Fish\nalias envsec \"node (pwd)/packages/cli/dist/main.js\"\nenvsec --completions fish | source\n```\n\nThen press TAB after `envsec -c ` to see your contexts, or after `envsec -c myapp.dev get ` to see secret keys.\n\n### Running tests\n\nEnd-to-end integration tests cover the full CLI lifecycle (add, get, list, search, env-file, load, delete, run, cmd, audit, share, completions).\n\n```bash\n# Build first\npnpm run build\n\n# macOS / Linux\nbash packages/cli/test/e2e-test.sh\n\n# Windows (PowerShell)\npwsh packages/cli/test/e2e-test.ps1\n```\n\nCI runs automatically on push/PR to `main` via GitHub Actions, executing `e2e-test.sh` on macOS and Ubuntu, and `e2e-test.ps1` on Windows.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidnussio%2Fenvsec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidnussio%2Fenvsec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidnussio%2Fenvsec/lists"}