
envsec v1.0.0-rc.2
使用原生操作系统凭据存储(macOS Keychain、Linux Secret Service、Windows Credential Manager)管理环境密钥的安全CLI工具
envsec
使用操作系统原生凭据存储的安全环境密钥管理。
演示

功能特性
- 将密钥存储在操作系统原生凭据存储中(而非纯文本文件)
- 跨平台:macOS、Linux、Windows
- 按上下文组织密钥(例如
myapp.dev、stripe-api.prod、work.staging) - 通过 SQLite 跟踪密钥元数据(键名、时间戳)
- 使用 glob 模式搜索上下文和密钥
- 运行带有密钥插值的命令
- 使用
cmd保存并重新运行命令(搜索、列出、运行、删除) - 将密钥导出到
.env文件(通过audit跟踪生成记录) - 将密钥导出为 shell 环境变量(
eval $(envsec env)) - 从
.env文件加载密钥(带冲突检测) - 使用 GPG 加密共享密钥给团队成员
- 交互式终端界面(
envsec tui),无需记忆命令即可管理密钥
包
这是一个包含以下包的 monorepo:
| 包 | 描述 | npm |
|---|---|---|
envsec | 用于管理密钥的 CLI 工具 | |
@envsec/sdk | 用于以编程方式加载密钥的 Node.js / Bun SDK | |
@envsec/core | 核心引擎 — 操作系统凭据存储适配器 + 元数据数据库 | |
@envsec/tui | 用于密钥管理的交互式终端界面 |
SDK 快速入门
如需从 Node.js 或 Bun 以编程方式访问密钥,请使用 @envsec/sdk:```bash
npm install @envsec/sdk
Please provide the Markdown content to translate.```typescript
import { loadSecrets } from "@envsec/sdk";
// Load and inject into process.env
await loadSecrets({ context: "myapp.dev", inject: true });
// Or use the client for full control
import { EnvsecClient } from "@envsec/sdk";
const client = await EnvsecClient.create({ context: "myapp.dev" });
const apiKey = await client.get("api.key");
await client.close();
请参阅完整的 SDK 文档,了解所有 API、多上下文支持及选项。
要求
- Node.js >= 22
macOS
无需额外依赖。通过 security 命令行工具使用内置的钥匙串(Keychain)。
Linux
需要 libsecret-tools(提供 secret-tool 命令),该工具通过 D-Bus 与 GNOME Keyring、KDE Wallet 或任何 Secret Service API 提供程序通信。```bash
Debian / Ubuntu
sudo apt install libsecret-tools
Fedora
sudo dnf install libsecret
Arch
sudo pacman -S libsecret
需要运行中的 D-Bus 会话和密钥环守护进程(例如 `gnome-keyring-daemon`)。大多数桌面环境会自动处理这些。
### Windows
无需额外依赖。通过 `cmdkey` 和 PowerShell 使用内置的 Windows 凭据管理器。
## 安装
### Homebrew(macOS / Linux)```bash
brew tap davidnussio/homebrew-tap
brew install envsec
npm```bash
npm install -g envsec
### npx(无需安装)```bash
npx envsec
mise```bash
mise use -g npm:envsec
## 用法
大多数命令需要通过 `--context`(或 `-c`)指定上下文。
上下文是用于对机密进行分组的自由格式标签——例如 `myapp.dev`、`stripe-api.prod`、`work.staging`。
### 全局选项
以下选项适用于所有命令:
- `--context`、`-c` — 上下文名称(例如 `myapp.dev`、`stripe-api.prod`)。同时读取 `ENVSEC_CONTEXT` 环境变量
- `--debug`、`-d` — 启用调试日志
- `--json` — 以 JSON 格式输出,便于脚本处理
- `--db` — SQLite 数据库文件路径(默认:`~/.envsec/store.sqlite`)。同时读取 `ENVSEC_DB` 环境变量
### 自定义数据库路径
默认情况下,元数据存储在 `~/.envsec/store.sqlite`。你可以通过 `--db` 或 `ENVSEC_DB` 环境变量覆盖此路径:```bash
# Use a project-local database
envsec --db ./local-store.sqlite -c myapp.dev list
# Or via environment variable
export ENVSEC_DB=/shared/team/envsec.sqlite
envsec -c myapp.dev list
--db 标志优先于 ENVSEC_DB。使用场景包括项目专属数据库、网络驱动器上的团队共享数据库,以及使用临时存储的 CI/CD。
添加密钥
将密钥存储到操作系统凭据存储中。
<key>— 密钥名称(例如api.key、db.password)--value、-v— 要存储的值(省略时显示交互式掩码提示)--expires、-e— 过期时长(例如30m、2h、7d、4w、3mo、1y)```bash
Store a value inline
envsec -c myapp.dev add api.key --value "sk-abc123"
Or use the short alias
envsec -c myapp.dev add api.key -v "sk-abc123"
Omit --value for an interactive masked prompt
envsec -c myapp.dev add api.key
Set an expiry duration with --expires (-e)
envsec -c myapp.dev add api.key -v "sk-abc123" --expires 30d
Supported duration units: m (minutes), h (hours), d (days), w (weeks), mo (months), y (years)
Combinable: 1y6mo, 2w3d, 1d12h
envsec -c myapp.dev add api.key -v "sk-abc123" -e 6mo
### 获取机密
从操作系统凭据存储中检索机密值。
- `<key>` — 要检索的机密键名
- `--quiet`, `-q` — 仅打印原始值(不显示警告或额外输出)
- `--json` — 以 JSON 格式输出(包含 context、key、value、expires_at)```bash
envsec -c myapp.dev get api.key
# Print only the raw value (no warnings or extra output)
envsec -c myapp.dev get api.key --quiet
envsec -c myapp.dev get api.key -q
删除机密
从操作系统凭据存储中移除一个机密。
<key>— 要删除的机密键名(如果使用--all则可选)--yes,-y— 跳过确认提示--all— 删除上下文中的所有机密```bash envsec -c myapp.dev delete api.key
or use the alias
envsec -c myapp.dev del api.key
### 重命名密钥
在同一上下文中重命名密钥。值和过期元数据将被保留。
- `<old-key>` — 当前密钥名称
- `<new-key>` — 新密钥名称
- `--force`, `-f` — 如果目标已存在则覆盖```bash
# Rename a key
envsec -c myapp.dev rename old.key new.key
# Overwrite target if it already exists
envsec -c myapp.dev rename old.key existing.key --force
列出上下文中的所有密钥
列出上下文中的所有密钥键及其元数据。
--json— 以 JSON 格式输出```bash envsec -c myapp.dev list
### 列出所有上下文
列出所有可用的上下文及其密钥数量。
- `--json` — 以 JSON 格式输出```bash
# Without --context, lists all available contexts with secret counts
envsec list
搜索机密
使用 glob 模式搜索机密或上下文。
<pattern>— 用于搜索的 Glob 模式(例如api.*、myapp.*)--json— 以 JSON 格式输出```bash
Search secrets within a context
envsec -c myapp.dev search "api.*"
Search contexts by pattern (without --context)
envsec search "myapp.*"
### 在上下文之间移动密钥
将密钥从一个上下文移动到另一个上下文。移动后,源密钥将被删除。
- `<pattern>` — 用于移动的 Glob 模式或精确密钥(使用 `--all` 时可省略)
- `--to`, `-t` — 将密钥移动到的目标上下文
- `--all` — 移动源上下文中的所有密钥
- `--force`, `-f` — 覆盖目标上下文中已有的密钥
- `--yes`, `-y` — 跳过确认提示```bash
# Move a single secret
envsec -c myapp.dev move api.token --to myapp.prod
# Move secrets matching a glob pattern
envsec -c myapp.dev move "redis.*" --to myapp.prod -y
# Move all secrets from one context to another
envsec -c myapp.dev move --all --to myapp.prod -y
# Overwrite existing secrets in the target context
envsec -c myapp.dev move "redis.*" --to myapp.prod --force -y
在上下文之间复制机密
将机密从一个上下文复制到另一个上下文。源机密保持不变。
<pattern>— 用于复制的 Glob 模式或精确键(如果使用--all则可选)--to,-t— 要将机密复制到的目标上下文--all— 从源上下文复制所有机密--force,-f— 覆盖目标上下文中已有的机密--yes,-y— 跳过确认提示```bash
Copy a single secret
envsec -c myapp.dev copy api.token --to myapp.staging
Copy secrets matching a glob pattern
envsec -c myapp.dev copy "redis.*" --to myapp.staging -y
Copy all secrets from one context to another
envsec -c myapp.dev copy --all --to myapp.staging -y
Overwrite existing secrets in the target context
envsec -c myapp.dev copy "redis.*" --to myapp.staging --force -y
### 使用机密运行命令
通过占位符插值机密值,或将其作为环境变量注入来执行命令。
- `<command>` — 要执行的命令。使用 `{key}` 占位符进行机密插值
- `--inject`, `-i` — 将所有上下文机密作为环境变量注入(`KEY.NAME` → `KEY_NAME`)
- `--save`, `-s` — 保存此命令以供后续使用
- `--name`, `-n` — 已保存命令的名称(与 `--save` 一起使用时,若省略则交互式提示)```bash
# Placeholders {key} are resolved with secret values before execution
envsec -c myapp.dev run 'curl {api.url} -H "Authorization: Bearer {api.token}"'
# Any {dotted.key} in the command string is replaced with its value
envsec -c myapp.prod run 'psql {db.connection_string}'
# Inject ALL context secrets as environment variables (KEY.NAME → KEY_NAME)
envsec -c myapp.dev run --inject 'node server.js'
envsec -c myapp.dev run -i 'docker compose up'
# Combine --inject with placeholders
envsec -c myapp.dev run --inject 'curl {api.url} -H "Authorization: Bearer $API_TOKEN"'
# Save the command for later use with --save (-s) and --name (-n)
envsec -c myapp.dev run --save --name deploy 'kubectl apply -f - <<< {k8s.manifest}'
# If you use --save without --name, you'll be prompted interactively
envsec -c myapp.dev run --save 'psql {db.connection_string}'
如果任何占位符引用了不存在的密钥,命令将不会执行,并且你会看到明确的错误提示:``` ❌ Missing secrets in context "myapp.dev":
- api.url
- api.token
Add them with: envsec -c myapp.dev add
### 已保存的命令
已保存的命令位于 `cmd` 子命令下,与机密操作分开管理。
#### cmd list
列出所有已保存的命令。```bash
envsec cmd list
cmd run
运行已保存的命令(使用其保存时的上下文)。
<name>— 要执行的已保存命令的名称--override-context,-o— 在执行时覆盖已保存的上下文--quiet,-q— 抑制信息性输出(仅打印命令输出)--inject,-i— 将所有上下文机密作为环境变量注入```bash envsec cmd run deploy
Run quietly (suppress informational output like "Resolved N secret(s)")
envsec cmd run deploy --quiet envsec cmd run deploy -q
Override the context at execution time
envsec cmd run deploy --override-context myapp.prod envsec cmd run deploy -o myapp.prod
Inject all context secrets as env vars when running a saved command
envsec cmd run deploy --inject envsec cmd run deploy -i
#### cmd search
按名称或命令字符串搜索已保存的命令。
- `<pattern>` — 搜索模式
- `--name`, `-n` — 仅搜索命令名称
- `--command`, `-m` — 仅搜索命令字符串```bash
envsec cmd search psql
# Search only by name
envsec cmd search deploy -n
# Search only by command string
envsec cmd search kubectl -m
cmd delete
删除已保存的命令。
<name>— 要删除的命令名称```bash envsec cmd delete deploy
### 生成 .env 文件
将上下文中的所有机密导出到 `.env` 文件。
- `--output`, `-o` — 输出文件路径(默认:`.env`)```bash
# Creates .env with all secrets from the context
envsec -c myapp.dev env-file
# Specify a custom output path
envsec -c myapp.dev env-file --output .env.local
密钥会被转换为 UPPER_SNAKE_CASE(例如 api.token → API_TOKEN)。
将密钥导出为环境变量
输出导出语句,供 eval 或 shell 引用使用。
--shell,-s— 目标 shell 语法:bash(默认)、zsh、fish、powershell--unset,-u— 输出取消设置/移除命令,而非导出命令```bash
Output export statements for eval (bash/zsh)
eval $(envsec -c myapp.dev env)
Specify target shell syntax
envsec -c myapp.dev env --shell fish envsec -c myapp.dev env --shell powershell
Output unset commands to clean up exported variables
eval $(envsec -c myapp.dev env --unset)
Combine shell and unset
envsec -c myapp.dev env --unset --shell fish
支持的 shell:`bash`(默认)、`zsh`、`fish`、`powershell`。密钥会被转换为 `UPPER_SNAKE_CASE`(例如 `api.token` → `API_TOKEN`)。输出会发送到 stdout,因此可以管道传递给 `eval` 或直接 source——不会写入任何文件到磁盘。
### 启动一个密钥作用域的 shell 会话
生成一个交互式子 shell,并将上下文中的所有密钥作为环境变量注入。当你 `exit` 时,密钥即消失——无需清理。
- `--shell`、`-s` — 要生成的 shell(`bash`、`zsh`、`fish`、`powershell`)。默认:自动检测
- `--no-inherit` — 不继承父进程的环境变量
- `--quiet`、`-q` — 抑制启动/退出横幅```bash
envsec -c myapp.dev shell
由于没有提供具体的输入内容,我无法进行翻译。请提供需要翻译的 Markdown 内容(第 53/85 块)。``` ▶ envsec shell — context: myapp.dev (8 secrets loaded) Type 'exit' or press Ctrl+D to leave the session.
(envsec:myapp.dev) ~ $ echo $DATABASE_URL postgres://user:pass@localhost/mydb
(envsec:myapp.dev) ~ $ exit → Exiting envsec shell — secrets cleared.
由于没有提供具体的输入内容,我无法进行翻译。请提供需要翻译的文本。```bash
# Force a specific shell
envsec -c myapp.dev shell --shell zsh
# Only envsec secrets in env (no parent variables, except PATH)
envsec -c myapp.dev shell --no-inherit
# Suppress the startup/exit banner
envsec -c myapp.dev shell --quiet
变量 ENVSEC_CONTEXT 始终在会话内设置,因此你可以在脚本或提示符自定义中引用它。
从 .env 文件加载机密
将 .env 文件中的机密导入到上下文中。
--input、-i— 输入.env文件路径(默认:.env)--force、-f— 覆盖现有机密,无需提示--batch、-b— 批处理模式:延迟数据库持久化,直到所有机密导入完成```bash
Import secrets from .env into the context
envsec -c myapp.dev load
Specify a custom input file
envsec -c myapp.dev load --input .env.local
Overwrite existing secrets without warning
envsec -c myapp.dev load --force
密钥从 `UPPER_SNAKE_CASE` 转换为 `dotted.lowercase`(例如 `API_TOKEN` → `api.token`)。如果密钥已存在,则会跳过并发出警告,除非提供 `--force`(`-f`)选项。
### 共享密钥(GPG 加密)
使用 GPG 将上下文中的所有密钥加密后共享给团队成员。
- `--encrypt-to` — 用于加密的 GPG 接收方密钥(邮箱、密钥 ID 或指纹)
- `--output`、`-o` — 输出文件路径(默认:stdout)。如需明确输出到 stdout,请使用 `-`
- `--json` — 在加密负载内使用 JSON 格式(默认:`.env` 格式)```bash
# Encrypt all secrets from a context for a team member
envsec -c myapp.dev share --encrypt-to [email protected]
# Save encrypted output to a file
envsec -c myapp.dev share --encrypt-to [email protected] -o secrets.enc
# Use JSON format inside the encrypted payload
envsec -c myapp.dev --json share --encrypt-to [email protected] -o secrets.enc
接收方可以使用 gpg --decrypt secrets.enc 解密,并将结果通过管道传递给 envsec load。默认情况下,加密负载使用 .env 格式(KEY="value");使用 --json 时则采用结构化 JSON 对象。需要安装 GPG,并且接收方的公钥必须存在于你的密钥环中。
审计密钥的过期情况
检查已过期或即将过期的密钥,以及被跟踪的 .env 文件导出。
--within、-w— 显示在此时间段内过期的密钥(默认:30d)。使用0d仅显示已过期的密钥--json— 以 JSON 格式输出```bash
Check for expired or expiring secrets in a context (default window: 30 days)
envsec -c myapp.dev audit
Specify a custom window
envsec -c myapp.dev audit --within 7d
Show only already-expired secrets
envsec -c myapp.dev audit --within 0d
Audit across all contexts (omit --context)
envsec audit
JSON output
envsec -c myapp.dev audit --json
通过 `envsec add` 设置了 `--expires` 时长的密钥会在元数据中被跟踪。`audit` 命令会扫描已过期或将在指定时间窗口内过期的密钥。`get` 和 `list` 命令也会内联显示过期警告。
`audit` 命令还会跟踪生成的 `.env` 文件。每次使用 `env-file` 时,输出路径、上下文和时间戳都会被记录。审计输出包含一个列出这些文件的第二部分。如果被跟踪的 `.env` 文件在磁盘上不再存在,audit 会自动将其从元数据中移除并报告清理情况。
### 生成随机密钥
生成一个加密安全的随机密钥,并可选择将其存储。
- `<key>` — 密钥名称(可选;省略则仅用于独立密码生成)
- `--length`, `-l` — 生成密钥的长度(默认:`32`)
- `--prefix`, `-p` — 添加到生成密钥前面的前缀(例如 `sk_`)
- `--expires`, `-e` — 过期时长(例如 `30m`、`2h`、`7d`、`4w`、`3mo`、`1y`)
- `--alphanumeric`, `-a` — 仅使用字母数字字符 `[a-zA-Z0-9]`(默认)
- `--special`, `-s` — 包含常见特殊字符 `[a-zA-Z0-9!@#$%^&*]`
- `--all-chars`, `-A` — 使用所有可打印 ASCII 字符以获得最大熵```bash
# Generate and store a 32-char alphanumeric secret
envsec -c myapp.dev secret api.key
# Custom length and prefix
envsec -c myapp.dev secret api.key --prefix "sk_" --length 48
# Character sets:
# --alphanumeric (-a) [a-zA-Z0-9] (default)
# --special (-s) [a-zA-Z0-9] + !@#$%^&*
# --all-chars (-A) all printable ASCII
envsec -c myapp.dev secret db.password --special --length 64
# With expiry
envsec -c myapp.dev secret api.key --prefix "sk_" -l 48 --expires 90d
# Standalone password generator (no store, just print)
envsec secret --length 32
envsec secret --special --length 64 --prefix "pk_"
当同时提供上下文和键时,生成的值会被存储并打印出来。如果两者都未提供,原始值将输出到标准输出——这对于通过管道传递给 pbcopy、xclip 或其他工具非常有用。
交互式 TUI
envsec 包含一个全屏终端界面,用于以交互方式管理机密——无需记忆命令。```bash
Launch the TUI
envsec tui
Launch with a pre-selected context
envsec -c myapp.dev tui
TUI 提供八个可从主菜单访问的屏幕:
- **上下文(Contexts)** — 浏览所有上下文,使用 `s` 设置活动上下文,使用 `x` 清除上下文,查看密钥数量,删除整个上下文
- **密钥(Secrets)** — 在表格中列出密钥,显示值,添加或删除密钥
- **添加密钥(Add Secret)** — 交互式表单,带掩码输入和可选的过期时长
- **搜索(Search)** — 在密钥或上下文中进行 glob 模式搜索
- **已保存命令(Saved Commands)** — 列出、查看和删除已保存的命令模板
- **审计(Audit)** — 检查已过期/即将过期的密钥,查看受跟踪的 `.env` 文件导出记录
- **导入 .env(Import .env)** — 从 `.env` 文件加载密钥到当前上下文
- **导出 .env(Export .env)** — 将密钥导出到 `.env` 文件(受审计跟踪)
键盘快捷键:
| 按键 | 操作 |
|-----|--------|
| `↑` / `↓` | 导航菜单项和表格行 |
| `Enter` | 选择 / 确认 |
| `c` | 打开上下文视图(主菜单) |
| `s` | 将选中项设置为活动上下文(上下文视图) |
| `x` | 清除活动上下文(上下文视图) |
| `a` | 添加新密钥(密钥视图) |
| `d` | 删除选中项 |
| `r` | 显示密钥值(详情视图) |
| `Esc` | 返回 / 取消 |
| `q` | 退出 TUI |
### 诊断你的环境
运行健康检查以验证你的 envsec 安装。
- `--json` — 以 JSON 格式输出,便于脚本处理```bash
# Run all health checks
envsec doctor
# JSON output for scripting
envsec --json doctor
doctor 命令用于验证你的 envsec 安装是否正常工作。它会检查:
- 平台支持与 Node.js 版本
- 凭据存储可用性(macOS Keychain、Linux secret-tool、Windows cmdkey)
- Keychain 读写权限
- 数据库路径、权限及模式完整性
- 孤立机密(有元数据但无 keychain 条目)
- 已过期机密
- 环境变量(
ENVSEC_DB、ENVSEC_CONTEXT) - 当前 shell
Shell 补全
envsec 支持 bash、zsh 和 fish 的动态 Tab 补全。补全具有上下文感知能力:它们会通过查询元数据库,实时提示你的实际上下文名称、机密密钥以及已保存的命令名称。```bash
Bash (add to ~/.bashrc)
eval "$(envsec --completions bash)"
Zsh (add to ~/.zshrc)
eval "$(envsec --completions zsh)"
Fish (add to ~/.config/fish/config.fish)
envsec --completions fish | source
动态完成的内容:
- `--context` / `-c` — 列出所有上下文
- 密钥参数(`get`、`add`、`delete`)— 列出当前上下文的密钥
- `cmd run` / `cmd delete` — 列出已保存的命令名称
- `--override-context` / `-o` — 为 `cmd run` 列出上下文
- 子命令、标志和静态选项(如 shell 等)也会自动完成
## 对比
envsec 与其他管理环境密钥的工具相比如何?
| 功能 | envsec | dotenv / dotenvx | 1Password CLI(`op`) |
|---|---|---|---|
| 密钥存储 | 操作系统凭据存储(Keychain、Secret Service、Credential Manager) | 磁盘上的 `.env` 文件(dotenvx 添加加密) | 1Password 云保险库 |
| 静态加密 | 委托给操作系统(Keychain、GNOME Keyring、DPAPI) | 无(dotenv)/ 每文件 ECIES(dotenvx) | 1Password 云中的 AES-256 |
| 磁盘上的密钥 | 从不 — 值直接进入操作系统凭据存储 | 始终 — `.env` 文件默认是明文 | 本地从不(运行时从云端获取) |
| 离线访问 | 完整 — 密钥位于操作系统存储中 | 完整 — 文件位于本地 | 需要网络(应用内可离线访问缓存项) |
| 账户 / 订阅 | 无 — 免费、开源、无需注册 | 免费(dotenv)/ 免费开源(dotenvx) | 付费订阅(个人约 $3/月,企业约 $8/用户/月) |
| 跨平台 | macOS、Linux、Windows | 任何支持 Node.js 的平台 / 任何运行时(dotenvx) | macOS、Linux、Windows |
| 上下文 / 环境组织 | 上下文(例如 `myapp.dev`、`stripe.prod`) | 每个环境单独的 `.env` 文件 | 保险库和条目 |
| 使用密钥运行命令 | `envsec run` — 占位符插值 + `--inject` 环境变量 | `dotenvx run -- cmd` — 从加密的 `.env` 注入 | `op run -- cmd` — 通过密钥引用注入 |
| 导出到 `.env` 文件 | `envsec env-file`(用于审计跟踪) | 原生格式 — `.env` 文件是事实来源 | `op inject --out-file` |
| 从 `.env` 文件导入 | `envsec load`(带冲突检测) | 不适用 — `.env` 是主要存储 | 手动创建条目 |
| Shell 环境导出 | `eval $(envsec env)` — bash、zsh、fish、powershell | `dotenvx run` 或 `node -r dotenv/config` | `op run --env-file` |
| 交互式 Shell 会话 | `envsec shell` — 带自动清理的作用域子 shell | 非内置 | 非内置 |
| 密钥搜索 | 对密钥和上下文的 glob 模式 | 非内置 | `op item list --tags/--category` 过滤 |
| 过期 / 轮换审计 | `envsec audit` — 过期、即将过期、受跟踪的 `.env` 文件 | 非内置 | Watchtower(应用内,非 CLI) |
| 已保存命令 | `envsec cmd` — 保存、列出、搜索、运行、删除 | 非内置 | 非内置 |
| 移动 / 复制密钥 | `envsec move` 和 `envsec copy` 在上下文之间 | 手动复制文件 | `op item move` 在保险库之间 |
| 重命名密钥 | `envsec rename`(保留值和元数据) | 手动编辑 `.env` 文件 | `op item edit` |
| GPG 加密共享 | `envsec share --encrypt-to` | 加密的 `.env` 文件提交到 git(dotenvx) | 内置保险库共享、团队配置 |
| 交互式 TUI | `envsec tui` — 全屏终端界面 | 非内置 | 非内置 |
| 健康诊断 | `envsec doctor` — 检查平台、钥匙串、数据库完整性 | 非内置 | 非内置 |
| Shell 补全 | 动态(上下文、密钥、命令)适用于 bash、zsh、fish | 非内置 | 静态补全适用于 bash、zsh、fish、powershell |
| SDK / 编程访问 | `@envsec/sdk` 适用于 Node.js / Bun | `require('dotenv').config()` — 核心用例 | 1Password SDK(Node.js、Python、Go 等) |
| 团队 / 多用户 | GPG 共享(手动) | 基于 git 的共享,使用加密的 `.env`(dotenvx) | 内置团队管理、RBAC、审计日志 |
<!-- | CI/CD 集成 | 标准 CLI — 可在任何运行 Node.js 的地方使用 | `dotenvx run` 在任何 CI 管道中 | 服务账户、原生 CI/CD 集成 | -->
| 生物识别认证 | 继承操作系统生物识别(例如 macOS Keychain 解锁) | 无 | 通过应用集成的指纹 / Touch ID |
| 元数据跟踪 | SQLite(密钥名称、时间戳 — 从不存储值) | 无 | 基于云的项目历史和审计日志 |
简而言之:dotenv 是最简单的方法(磁盘上的文件),1Password CLI 对需要云同步和 RBAC 的团队来说功能最丰富,而 envsec 介于两者之间 — 提供操作系统原生加密、零账户、零云依赖,以及超越 `.env` 文件能力的开发者优先工作流。
## 工作原理
密钥存储在操作系统原生凭据存储中。后端根据平台自动选择:
| 操作系统 | 后端 | 工具 / API |
|---|---|---|
| macOS | Keychain | `security` CLI |
| Linux | Secret Service API(D-Bus) | `secret-tool`(libsecret) |
| Windows | Credential Manager | `cmdkey` + PowerShell(advapi32) |
元数据(密钥名称、时间戳)保存在 `~/.envsec/store.sqlite` 的 SQLite 数据库中(可通过 `--db` 或 `ENVSEC_DB` 配置)。密钥必须包含至少一个点分隔符(例如 `service.account`),这映射到凭据存储的服务/账户结构。
## 安全性
envsec 围绕一个简单原则构建:你的密钥属于操作系统,而不是 dotfiles。每个设计决策都源于这一基础。
### envsec 如何保护你的密钥
**操作系统原生加密,零自定义加密。** 密钥值直接存储在 macOS Keychain、GNOME Keyring / KDE Wallet 或 Windows Credential Manager 中。envsec 从不自行发明加密 — 它委托给你操作系统已经提供的经过实战检验的凭据存储,由你的用户会话和(在 macOS 上)登录钥匙串保护。
**完整的 Unicode 支持。** 密钥值可以包含任何 Unicode 字符,包括表情符号和重音字母。值在存储到操作系统凭据存储之前会进行 base64 编码,避免平台特定的编码怪癖(例如 macOS `security` CLI 对非 ASCII 输出进行十六进制编码)。为向后兼容,旧版明文密钥会被透明读取。
**密钥从不以明文形式接触磁盘。** 值直接从终端进入操作系统凭据存储。它们从不写入配置文件、日志或中间存储。
**终端输出中无密钥。** `list` 和 `search` 命令仅显示密钥名称 — 值从不打印。这使密钥远离滚动缓冲区、屏幕录制和肩窥范围。
**安全的命令执行。** `run` 命令将密钥作为子进程的环境变量注入,而不是将它们插值到命令字符串中。这意味着密钥值不会出现在 `ps` 输出或 shell 历史中。如果任何引用的密钥缺失,命令会被完全阻止 — 不会以不完整的凭据部分执行。
**输入验证和注入防护。** 上下文名称根据严格的允许列表(字母数字、点、连字符、下划线)进行验证,并带有路径遍历和原型污染检查。所有 SQLite 查询使用带绑定参数的预处理语句,防止 SQL 注入。Windows 上的 PowerShell 参数会被转义以防止命令注入。
**限制性文件权限。** 元数据目录(`~/.envsec/`)以 `0700` 权限创建,SQLite 数据库以 `0600` 权限创建,限制为仅拥有用户可访问。
### 已知限制和改进领域
我们相信坦诚说明 envsec 尚未覆盖的内容。这些是真实的权衡,而非缺陷 — 理解它们有助于你做出明智的决策。
**元数据可见。** `~/.envsec/store.sqlite` 中的 SQLite 数据库存储密钥名称、上下文名称和时间戳 — 从不存储密钥值,但足以揭示*存在哪些*密钥。已保存的命令模板(带 `{key}` 占位符)也存储在那里。如果元数据机密性对你很重要,请确保你的主目录位于加密卷上。
**`env-file` 导出是明文。** `env-file` 命令将密钥值写入磁盘上的 `.env` 文件。这本质上很敏感 — 请相应处理输出文件,切勿将其提交到版本控制。将其视为便利桥接,而非存储机制。
**Shell 执行存在固有风险。** `run` 命令通过 `/bin/sh`(在 Windows 上为 `cmd.exe`)传递你的命令模板。如果模板本身来自不受信任的输入,则可能发生 shell 注入。只运行你编写或信任的命令模板。
**无跨上下文访问控制。** 任何以你的操作系统用户身份运行的进程都可以读取所有上下文中的所有密钥。envsec 依赖操作系统级别的用户隔离 — 它不会在上下文之间添加自己的授权层。
**Linux 无头环境。** 在 Linux 上,envsec 依赖活动的 D-Bus 会话和钥匙串守护进程(例如 `gnome-keyring-daemon`)。在没有图形会话的容器或无头服务器中,钥匙串可能不可用,或者可能以较弱的保护存储密钥。
**加密取决于你的操作系统。** envsec 不会在原生凭据存储提供的加密之外添加额外的静态加密。在没有全盘加密的系统上,具有物理访问权限的攻击者可能从钥匙串中提取密钥。我们建议启用全盘加密(FileVault、LUKS、BitLocker)以获得最强保护。
## 开发
### 先决条件
- Node.js >= 22
- pnpm
核心、SDK、CLI 和 TUI 包使用 Effect 4,目前固定为
`4.0.0-rc.112`。在 Effect 4 保持发布候选状态期间,请保持工作区中 Effect 和 `@effect/platform-node` 的版本对齐。
### 设置```bash
git clone https://github.com/davidnussio/envsec.git
cd envsec
pnpm install
pnpm run build
项目结构```
packages/
cli/ → envsec CLI (published as envsec)
sdk/ → Node.js/Bun SDK (published as @envsec/sdk)
core/ → Core engine, shared by CLI and SDK (published as @envsec/core)
tui/ → Interactive terminal UI (published as @envsec/tui)
apps/
website/ → Documentation website
### 常用命令```bash
# Build all packages
pnpm run build
# Lint and format check (all packages)
pnpm run check
# Auto-fix lint and formatting
pnpm run fix
# Run package unit and contract tests
pnpm run test:unit
# Run the CLI end-to-end suite with isolated database and credential fixtures
pnpm --filter envsec test
# Release (build + changeset publish)
pnpm run release
隔离的 E2E 测试套件从不访问原生凭据存储。要在 macOS 或 Linux 上运行真实的操作系统适配器,请先构建并显式选择加入:```bash
ENVSEC_E2E_CLI="$PWD/packages/cli/dist/main.js"
ENVSEC_E2E_ISOLATED=0
pnpm --filter envsec test
原生端到端测试使用专用的 `test.e2e*` 上下文,并在测试结束后将其移除。
### 不安装即可在本地运行
创建一个临时别名,将本地构建当作全局安装来使用:```bash
# Bash / Zsh
alias envsec="node $(pwd)/packages/cli/dist/main.js"
# Fish
alias envsec "node (pwd)/packages/cli/dist/main.js"
在本地测试 shell 补全
构建并设置好别名后,在当前会话中加载补全:```bash
Bash
alias envsec="node $(pwd)/packages/cli/dist/main.js" eval "$(envsec --completions bash)"
Zsh
alias envsec="node $(pwd)/packages/cli/dist/main.js" eval "$(envsec --completions zsh)"
Fish
alias envsec "node (pwd)/packages/cli/dist/main.js" envsec --completions fish | source
然后在 `envsec -c ` 后按 TAB 键即可查看你的上下文,或在 `envsec -c myapp.dev get ` 后按 TAB 键查看密钥键。
### 运行测试
端到端集成测试覆盖完整的 CLI 生命周期(add、get、list、search、env-file、load、delete、run、cmd、audit、share、completions)。```bash
# Build first
pnpm run build
# macOS / Linux
bash packages/cli/test/e2e-test.sh
# Windows (PowerShell)
pwsh packages/cli/test/e2e-test.ps1
CI 会在推送到 main 或提交 PR 时通过 GitHub Actions 自动运行,在 macOS 和 Ubuntu 上执行 e2e-test.sh,在 Windows 上执行 e2e-test.ps1。
许可证
MIT