使用操作系统原生凭据存储进行安全的环境秘密管理。

myapp.dev、stripe-api.prod、work.staging)cmd 保存并重新运行命令(搜索、列出、运行、删除).env 文件(通过 audit 跟踪生成)eval $(envsec env)).env 文件加载秘密(含冲突检测)envsec tui)无需记忆命令即可管理秘密这是一个包含以下包的单一仓库:
如需从 Node.js 或 Bun 以编程方式访问秘密,请使用 @envsec/sdk:```bash
npm install @envsec/sdk
(由于输入内容为空,此处输出空字符串)```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、多上下文支持和选项。
无需额外依赖。通过 security CLI 工具使用内置的 Keychain。
需要 libsecret-tools(提供 secret-tool 命令),它通过 D-Bus 与 GNOME Keyring、KDE Wallet 或任何 Secret Service API 提供者通信。```bash
sudo apt install libsecret-tools
sudo dnf install libsecret
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 install -g envsec
### npx (无需安装)```bash
npx envsec
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)```bashenvsec -c myapp.dev add api.key --value "sk-abc123"
envsec -c myapp.dev add api.key -v "sk-abc123"
envsec -c myapp.dev add api.key
envsec -c myapp.dev add api.key -v "sk-abc123" --expires 30d
envsec -c myapp.dev add api.key -v "sk-abc123" -e 6mo
### 获取一个密钥
从操作系统凭据存储中检索一个密钥值。
- `<key>` — 要检索的密钥名称
- `--quiet`, `-q` — 仅输出原始值(无警告或额外信息)
- `--json` — 以 JSON 格式输出(包含上下文、键名、值、过期时间)```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.keyenvsec -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 模式搜索 secrets 或 contexts。
<pattern> — 用于搜索的 Glob 模式(例如 api.*、myapp.*)--json — 以 JSON 格式输出```bashenvsec -c myapp.dev search "api.*"
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 — 跳过确认提示```bashenvsec -c myapp.dev copy api.token --to myapp.staging
envsec -c myapp.dev copy "redis.*" --to myapp.staging -y
envsec -c myapp.dev copy --all --to myapp.staging -y
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":
Add them with: envsec -c myapp.dev add
### 已保存的命令
已保存的命令位于 `cmd` 子命令下,与秘密操作分开。
#### cmd list
列出所有已保存的命令。```bash
envsec cmd list
运行一个已保存的命令(使用保存时的上下文)。
<name> — 要执行的已保存命令的名称--override-context, -o — 在运行时覆盖已保存的上下文--quiet, -q — 抑制信息输出(仅打印命令输出)--inject, -i — 将所有上下文密钥作为环境变量注入```bash
envsec cmd run deployenvsec cmd run deploy --quiet envsec cmd run deploy -q
envsec cmd run deploy --override-context myapp.prod envsec cmd run deploy -o myapp.prod
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
删除已保存的命令。
<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 — 输出取消设置/删除命令而非导出```basheval $(envsec -c myapp.dev env)
envsec -c myapp.dev env --shell fish envsec -c myapp.dev env --shell powershell
eval $(envsec -c myapp.dev env --unset)
envsec -c myapp.dev env --unset --shell fish
Supported 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.
### Start a secrets-scoped shell session
Spawn an interactive subshell with all secrets from the context injected as
environment variables. When you `exit`, the secrets are gone — no cleanup needed.
- `--shell`, `-s` — Shell to spawn (`bash`, `zsh`, `fish`, `powershell`). Default: auto-detect
- `--no-inherit` — Do not inherit parent environment variables
- `--quiet`, `-q` — Suppress startup/exit banner```bash
envsec -c myapp.dev shell
(空)``` ▶ 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.
通过使用多个端口或服务(如SSL/TLS、SSH、HTTP/2、WebSocket等)来增强与C2服务器的通信安全,以避免被检测。集成了多种流量混淆技术,如Base64编码、AES加密、随机数据填充等,以绕过网络监控。支持JARM指纹混淆,使C2通信流量看起来像正常流量。```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 文件导入秘密到上下文中。
--input, -i — 输入的 .env 文件路径(默认:.env)--force, -f — 无需提示直接覆盖现有秘密--batch, -b — 批处理模式:延迟数据库持久化直到所有秘密被导入```bashenvsec -c myapp.dev load
envsec -c myapp.dev load --input .env.local
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
The 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.
检查已过期或即将过期的机密以及跟踪的 .env 文件导出。
--within, -w — 显示在此期限内即将过期的机密(默认:30d)。使用 0d 仅显示已过期的--json — 以 JSON 格式输出```bashenvsec -c myapp.dev audit
envsec -c myapp.dev audit --within 7d
envsec -c myapp.dev audit --within 0d
envsec audit
envsec -c myapp.dev audit --json
Secrets 通过 `envsec add` 设置的 `--expires` 持续时间会被记录在元数据中。`audit` 命令会扫描已过期或在指定时间窗口内即将过期的密钥。`get` 和 `list` 命令也会内联显示过期警告。
`audit` 命令还会跟踪生成的 `.env` 文件。每次使用 `env-file` 时,输出路径、上下文和时间戳都会被记录。审计输出包含第二部分列出这些文件。如果被跟踪的 `.env` 文件在磁盘上已不存在,审计会自动从元数据中移除该文件并报告清理情况。
### 生成随机密钥
生成一个加密安全的随机密钥,可选地将其存储。
- `<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_"
当同时提供 context 和 key 时,生成的值将被存储并打印。如果两者都未提供,则原始值将输出到 stdout — 适用于管道传输到 pbcopy、xclip 或其他工具。
envsec tui
envsec -c myapp.dev tui
交互式文本界面(TUI)提供八个屏幕,可从主菜单访问:
- **上下文** — 浏览所有上下文,使用 `s` 设置活动上下文,使用 `x` 清除上下文,查看密钥数量,删除整个上下文
- **密钥** — 以表格形式列出密钥,显示值,添加或删除密钥
- **添加密钥** — 交互式表单,带掩码输入和可选有效期
- **搜索** — 在密钥或上下文间进行通配符模式搜索
- **已保存命令** — 列表、查看和删除已保存的命令模板
- **审计** — 检查过期/即将过期的密钥,审查已追踪的 `.env` 文件导出
- **导入 .env** — 从 `.env` 文件加载密钥到当前上下文
- **导出 .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 安装是否正常工作。它会检查:
ENVSEC_DB、ENVSEC_CONTEXT)envsec 支持 bash、zsh 和 fish 的动态 Tab 补全。补全功能是上下文感知的:通过查询元数据数据库,它会实时建议您的实际上下文名称、密钥键和已保存的命令名称。```bash
eval "$(envsec --completions bash)"
eval "$(envsec --completions zsh)"
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,自动清理 | 无内置 | 无内置 |
| 密钥搜索 | 基于全局模式匹配密钥和上下文 | 无内置 | `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` —— 全屏终端 UI | 无内置 | 无内置 |
| 健康诊断 | `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 字符,包括 emoji 和带重音字母。值在存入操作系统凭据存储前会经过 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
### 设置```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
# Release (build + changeset publish)
pnpm run release
创建一个临时别名,以使用本地构建,就好像它是全局安装的一样:```bash
alias envsec="node $(pwd)/packages/cli/dist/main.js"
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
Then press TAB after envsec -c to see your contexts, or after envsec -c myapp.dev get to see secret keys.
端到端集成测试涵盖了完整的 CLI 生命周期(add、get、list、search、env-file、load、delete、run、cmd、audit、share、completions)。```bash
pnpm run build
bash packages/cli/test/e2e-test.sh
pwsh packages/cli/test/e2e-test.ps1
CI 通过 GitHub Actions 在向 `main` 推送或提交 PR 时自动运行,在 macOS 和 Ubuntu 上执行 `e2e-test.sh`,在 Windows 上执行 `e2e-test.ps1`。
## License
MIT
| 包 | 描述 | npm |
|---|
envsec | 用于管理秘密的 CLI 工具 | |
@envsec/sdk | 用于以编程方式加载秘密的 Node.js / Bun SDK | |
@envsec/core | 核心引擎 — 操作系统凭据存储适配器 + 元数据库 | |
@envsec/tui | 用于秘密管理的交互式终端 UI |