关于 sealkeep
sealkeep 为 AI Agent 管理机密信息,可将命名机密注入命令环境,并从输出中脱敏。它包含命令行工具、MCP 服务和防护钩子。
主要功能
- 01将机密注入命令
- 02脱敏命令输出中的机密值
- 03使用系统密钥环和 Vault 存储
- 04扫描并导入机密文件
- 05拦截会打印机密的调用
适用场景
- 01运行需要 API 密钥的 Agent 命令
- 02从 .env 或 INI 文件迁移凭据
- 03避免机密进入 Agent 对话记录
MCP 相关
`sealkeep mcp` 服务提供 `list_secrets`、`run_with_secrets` 和 `store_status` 工具。任何工具都不会返回机密值。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

sealkeep
sealkeep 让 AI Agent 使用你的密钥,却无法看到密钥。
你只需将每个密钥保存一次,并为它命名,例如 shared/stripe/test/SECRET_KEY。Agent 通过 sealkeep list 查找名称,然后用它运行命令。Agent 永远不会读取密钥的值,也不需要向你索要密钥。
Agent 按名称请求密钥。sealkeep 会将密钥值放入某个命令的环境变量中,运行该命令,并在所有输出中将密钥值替换为 [sealkeep:NAME]。密钥值不会进入上下文、对话记录或文件。
agent ──► sealkeep run OPENROUTER_API_KEY -- sh -c 'curl -H "Authorization: Bearer $OPENROUTER_API_KEY" …'
│
├─ reads the value from a store ──► OS keyring (macOS Keychain, Secret Service, Windows Credential Manager)
│ └─► Vault KV v2 (a replica)
├─ starts the command with the value in its environment
└─ redacts the value from stdout and stderr ──► agent sees [sealkeep:OPENROUTER_API_KEY]
它由四部分组成:
- 一个 CLI(
sealkeep):set、list、run、import、scan、sync、别名和存储命令。 - 一个 MCP 服务(
sealkeep mcp):提供list_secrets、run_with_secrets和store_status工具。没有任何工具会返回密钥值。 - 一个防护钩子(
sealkeep guard):拒绝会打印密钥的工具调用,例如cat .env或sealkeep get。 - 一项技能,告诉 Agent 何时以及如何使用 CLI。
安装
仅作为 MCP 服务使用(适用于任何客户端)时,命令为 npx -y @munhq/sealkeep:
{ "mcpServers": { "sealkeep": { "command": "npx", "args": ["-y", "@munhq/sealkeep"] } } }
在 Claude Code 中,插件只需一步即可添加 MCP 服务、技能和防护钩子:
/plugin marketplace add munhq/sealkeep
/plugin install sealkeep@sealkeep
下面的完整安装过程也可为 Claude Code、Codex 和 Cursor 完成相同操作。
Linux 和 macOS:
curl -fsSL https://raw.githubusercontent.com/munhq/sealkeep/main/install.sh | sh
该脚本会下载最新版本的二进制文件,通过 SHA256SUMS 校验文件,并将其复制到 ~/.local/bin,然后运行 sealkeep install。
使用 Rust:
cargo install --locked --git https://github.com/munhq/sealkeep
sealkeep install
sealkeep install 会查找这台机器上的 AI 客户端,并为每个客户端添加技能、MCP 服务和防护钩子:
| 客户端 | 技能 | MCP 服务 | 防护钩子 |
|---|---|---|---|
| Claude Code(每个配置文件夹) | <config>/skills/sealkeep | claude mcp add --scope user | <config>/settings.json,对 Bash、Read、Grep 执行 PreToolUse |
| Codex | $CODEX_HOME/skills/sealkeep | codex mcp add | $CODEX_HOME/hooks.json,PreToolUse(通过 /hooks 批准一次) |
| Cursor | ~/.cursor/skills/sealkeep | ~/.cursor/mcp.json | ~/.cursor/hooks.json,beforeShellExecution |
选项:--client claude,codex 仅安装到指定客户端。--dry-run 显示执行计划。--no-skills、--no-mcp 和 --no-hooks 可跳过对应部分。sealkeep 修改 JSON 文件前,会先创建备份(<file>.bak-<unix time>)。sealkeep uninstall 会移除 install 添加的内容。
运行 sealkeep doctor 检查存储和客户端。
名称
名称的格式为 <scope>/<project>/<env>/<KEY>:
| 部分 | 值 | 示例 |
|---|---|---|
| scope | 密钥的所有者:personal、雇主或客户的名称,或者供多个 scope 共用的单个账户对应的 shared | personal、acme、shared |
| project | 仓库或服务 | example-app、stripe、ovh |
| env | prod、dev、test、local。密钥没有环境时(例如账户的 API 密钥),省略此项 | prod |
| KEY | 程序读取的环境变量 | DATABASE_URL |
文件夹名称使用小写字母(a-z、0-9、.、_、-)。密钥使用大写字母(A-Z、0-9、_),它就是 run 设置的变量。
personal/example-app/prod/DATABASE_URL
personal/example-app/dev/DATABASE_URL
personal/example-app/dev/STRIPE_SECRET_KEY -> shared/stripe/test/SECRET_KEY (alias)
acme/ovh/ovh-eu/APPLICATION_KEY
acme/cloudflare/API_TOKEN
shared/stripe/test/SECRET_KEY
shared/openrouter/API_KEY
一个值对应一个名称。 两个项目使用同一个账户时,将该值存一次,放在 shared/… 下,然后在每个项目文件夹中创建别名。这样,项目文件夹就包含该项目所需的所有变量,而轮换时只需更改一个值。
存储密钥
sealkeep set shared/openrouter/API_KEY -d "OpenRouter, the main account" # writes to every store
sealkeep 会要求输入两次值,且不会回显。若要通过管道传入值,请使用 --stdin:
some-password-tool show openrouter | sealkeep set shared/openrouter/API_KEY --stdin
若要查找机器上的密钥,请运行 scan。它会打印每个 .env 文件的路径、密钥名称、每个密钥的类别(secret、config 或 empty),以及出现在多个位置的每个值对应的组编号。它绝不会打印任何值。它还会按路径列出其他常用于存放凭据的文件:
sealkeep scan ~/code --max-depth 4
若要将文件复制到文件夹中:
sealkeep import ../example-app/.env --to personal/example-app/dev \
--map STRIPE_SECRET_KEY=shared/stripe/test/SECRET_KEY
sealkeep import ~/.ovh.conf --to acme/ovh # INI: one folder for each section
sealkeep set personal/github/PAT --from-file ~/.github-token
import 会读取 dotenv 文件和 INI 文件(.ini、.conf、AWS credentials)。它只会打印名称。--map KEY=NAME 会将值存储在 NAME,并将项目密钥设为该值的别名。如果 NAME 已有不同的值,import 会停止并说明原因。导入后,请删除源文件,或将其放在 Agent 无法访问的位置。
让 Agent 使用密钥
告诉 Agent 要做什么。该技能会指示它运行 sealkeep list 获取名称,并运行 sealkeep run 执行命令:
sealkeep run shared/openrouter/API_KEY -- sh -c 'curl -sS -H "Authorization: Bearer $API_KEY" https://openrouter.ai/api/v1/key'
sealkeep run --all personal/example-app/dev -- npm run dev
sealkeep run -e GITHUB_TOKEN=personal/github/PAT -- gh api user
名称会设置由其密钥指定的变量。--all FOLDER 会为文件夹中的每个密钥设置一个变量,效果与 .env 文件相同。--recursive 还会包含子文件夹;此时,两个密钥若名称相同,就会报错。-e VAR=NAME 会设置一个名称不同的变量。STORE:NAME 仅从一个存储中读取。
对于从文件读取密钥的工具,--dotenv 会写入一个只有你能读取的临时文件。如果 $XDG_RUNTIME_DIR/sealkeep 存在,文件就会放在其中。sealkeep 会将文件路径填入命令中的 {dotenv},并在命令结束时删除该文件。此示例会将测试账户的登录信息提供给 Playwright MCP。Agent 输入名称 ADMIN_PASSWORD,Playwright 会填入对应的值:
sealkeep run --dotenv personal/example-app/dev/ADMIN_EMAIL --dotenv personal/example-app/dev/ADMIN_PASSWORD \
-- npx @playwright/mcp@latest --secrets {dotenv}
--secrets <path> 是 @playwright/mcp 的选项(请参阅其 README)。
自行查看值
sealkeep get shared/openrouter/API_KEY
只有在 stdin 和 stdout 都是终端时,get 才能使用。Agent 运行命令时没有终端,因此 get 会拒绝执行,防护钩子也会拒绝执行。
存储
sealkeep 会按配置文件中的顺序在各存储中查找不带限定的名称。第一个包含该名称的存储会提供对应的值。
在 Linux 上,配置文件为 ~/.config/sealkeep/config.toml;在 macOS 上为 ~/Library/Application Support/sealkeep/config.toml。$SEALKEEP_CONFIG 可指定其他路径。没有配置文件时,sealkeep 会有一个名为 local 的密钥环存储。
[[store]]
name = "local"
kind = "keyring"
[[store]]
name = "vault"
kind = "vault"
address = "http://127.0.0.1:{port}"
mount = "agent"
auth = "kubernetes"
role = "sealkeep"
jwt_command = ["kubectl", "-n", "vault", "create", "token", "sealkeep", "--duration", "10m"]
port_forward = ["kubectl", "-n", "vault", "port-forward", "svc/vault", "{port}:8200"]
[aliases]
"personal/example-app/dev/STRIPE_SECRET_KEY" = "shared/stripe/test/SECRET_KEY"
[guard]
block_dotenv = true
deny = ["vault kv get", "ansible-vault view"]
OS 密钥环
| OS | 存储位置 |
|---|---|
| macOS | 登录钥匙串 |
| Linux、BSD | 会话 D-Bus 上的 Secret Service(GNOME Keyring、KWallet) |
| Windows | Credential Manager |
每个机密都是一条条目,服务名为 sealkeep,机密名称用作账户。索引条目(__index__)保存名称及其描述。
在 Linux 上,密钥环必须处于解锁状态。登录时,桌面会话会解锁密钥环。没有桌面的会话(SSH、登录时未设置密钥环密码)会导致密钥环处于锁定状态,且不会显示解锁提示。每次启动后运行一次:
sealkeep unlock
此命令会询问登录密钥环的密码,并将其传给 gnome-keyring-daemon --unlock。每次调用密钥环最多等待 30 秒(SEALKEEP_KEYRING_TIMEOUT),因此 Agent 会收到错误,调用也不会一直卡住。
将 Vault 用作副本
Vault(或 OpenBao)KV v2 挂载可以保存密钥环的副本,用于另一台机器或备份。每个文件夹对应一个 KV 机密,每个键对应其中的一个字段:shared/stripe/test/SECRET_KEY 是 agent/shared/stripe/test 中的 SECRET_KEY 字段。每个机密的 custom_metadata 会列出其键及其描述,因此 list 不会读取任何值。
对于 Kubernetes 内的 Vault,sealkeep 可以通过 Kubernetes API 使用端口转发访问 Vault,并使用短期 ServiceAccount 令牌登录。这样,机器上就不会保留 Vault 令牌:
sealkeep store add-vault vault \
--address 'http://127.0.0.1:{port}' --mount agent --auth kubernetes --role sealkeep \
--jwt-command 'kubectl -n vault create token sealkeep --duration 10m' \
--port-forward 'kubectl -n vault port-forward svc/vault {port}:8200'
sealkeep sync --from local --to vault # one time, for the secrets that are already in the keyring
set、import 和 mv 会写入每个存储(Vault 存储只接受带文件夹的名称),因此各存储会保持一致。--store NAME 则只写入一个存储。Vault 端需要 KV v2 挂载、一个对 agent/data/* 具有 create、read 和 update 权限,并对 agent/metadata/* 具有 read、list 和 update 权限的策略,以及绑定到 ServiceAccount 的 Kubernetes 身份验证角色。使用 --auth token 时,sealkeep 会读取 $VAULT_TOKEN,或读取由 sealkeep store token vault 保存在密钥环中的令牌。
其他机密管理器
以下每一种都是一种存储类型。它会运行供应商的官方 CLI,因此适用供应商自身的登录方式(SSO、服务账户、生物识别),而 sealkeep 不会保留供应商令牌。值会通过标准输入或仅你本人可读的文件传给 CLI,不会作为命令参数传递,因此不会被 ps 显示。
| 存储 | 添加方式 | 布局 | 值的传入方式 |
|---|---|---|---|
| AWS Secrets Manager | sealkeep store add-aws aws --region eu-west-1 | 每个文件夹对应一个 JSON 机密,sealkeep/<folder> | --secret-string file://… |
| Google Secret Manager | sealkeep store add-gcp gcp --project acme-prod | 每个名称对应一个机密;注释中保存名称 | --data-file=-(标准输入) |
| Azure Key Vault | sealkeep store add-azure azure --vault acme-kv | 每个名称对应一个机密;标签中保存名称 | --file … |
| 1Password(CLI 2.23+) | sealkeep store add-1password op --vault Engineering | 每个文件夹对应一条安全备注,每个键对应一个隐藏字段 | 通过标准输入传入条目 JSON |
| Bitwarden / Vaultwarden | sealkeep store add-bitwarden bw | 一条 sealkeep:<folder> 安全备注,每个键对应一个隐藏字段 | 通过标准输入传入条目 JSON |
| OpenBao | sealkeep store add-vault … | 同 Vault | 同 Vault |
Google 和 Azure 的机密 ID 只允许使用部分字符,因此 ID 会将名称中的 / 替换为 --,并附加一个短哈希;实际名称保存在注释或标签中。Azure 会将已删除的机密保留在软删除状态,之后对同名机密执行 set 会将其恢复。对于 Bitwarden,请先解锁:export BW_SESSION=$(bw unlock --raw)。Bitwarden Secrets Manager CLI(bws)只接受通过命令参数传入的值,因此 sealkeep 使用密码管理器 CLI(bw)。
SSH 密钥
SSH Agent 仅在内存中保存带有口令的密钥。重启后,Agent 为空,Agent 的 git push 会因 Permission denied (publickey) 而失败。sealkeep 会保存口令并加载密钥:
sealkeep set personal/ssh/ID_ED25519_PASSPHRASE
sealkeep ssh-add ~/.ssh/id_ed25519 --passphrase personal/ssh/ID_ED25519_PASSPHRASE
sealkeep ssh-load # unlock runs it too
ssh-load 会将 sealkeep 作为 SSH_ASKPASS 程序运行 ssh-add。askpass 步骤仅在其父进程是 ssh-add 且一次性令牌匹配时才会响应,并从存储中读取口令,因此口令不会写入文件或输出。--agent 用于选择 Agent 套接字;默认值为 $SSH_AUTH_SOCK。
MCP
sealkeep mcp 会通过 stdio 提供以下工具:
| 工具 | 功能 |
|---|---|
list_secrets | 列出所有名称或某个 folder 的名称、存储和说明。 |
run_with_secrets | 在环境中提供 folders 和 secrets,并在临时文件中提供 dotenv,运行 command(参数列表,不使用 shell)。返回经过脱敏的退出代码、stdout 和 stderr。默认时间限制为 120 s,最长为 900 s。每个流最多截取 100 KiB。 |
store_status | 检查各个存储当前是否可用。 |
防护钩子
该钩子会拒绝以下工具调用,并告知 Agent 使用 sealkeep run:
sealkeep get,包括在$(…)、反引号和bash -c中使用的情况。- 读取 sealkeep 密钥环条目:
secret-tool lookup service sealkeep …、security find-generic-password -s sealkeep … -w、keyring get sealkeep …。 - 当
block_dotenv启用时,通过 shell 命令或 Read 和 Grep 工具读取.env文件。.env.example、.env.sample、.env.template和.env.dist仍可读取。 guard.deny中的每个命令前缀。
Claude Code 还采用另外两层防护:
- 输出防护(
sealkeep guard --after,适用于 Bash、Read 和 Grep 的 PostToolUse 钩子)。工具调用后,它会将结果与已存储密钥的值进行比对,使用与run相同的脱敏形式。如果结果中包含某个值,钩子会在 Agent 读取结果前将其替换为[sealkeep:NAME],并在说明中指出密钥名称。这也涵盖先运行env > out.txt再读取out.txt,以及路径中使用通配符或变量的情况。存储会并行加载,最长等待 4 秒;未响应的存储不会提供任何值。Claude Code 会在其自身的遥测中记录原始输出。 - 拒绝规则(
settings.json中的permissions.deny)。sealkeep install会添加Read(**/.env)、Read(**/.env.local)、Read(**/*.env),以及针对其他.env.*名称的规则。Claude Code 会根据路径检查这些规则,因此 Read 工具和它能识别的文件命令无法通过 shell 技巧访问文件。启用block_dotenv时,安装程序会添加这些规则;sealkeep uninstall会将其移除。
sealkeep 能保护什么,不能保护什么
sealkeep 会将密钥排除在 Agent 上下文、对话记录和项目文件之外。每次使用都会记入审计日志(Linux 上为 ~/.local/share/sealkeep/audit.jsonl,或 $SEALKEEP_AUDIT_LOG),记录名称和命令,但绝不记录密钥值。
脱敏会匹配原始值、JSON 转义形式、百分号编码形式和 Base64 形式。当某个值的键名看起来像密钥(KEY、TOKEN、SECRET、PASS、AUTH 等类似单词),当它是带密码的 URL,或长度不少于 16 个字符时,该值会被脱敏。PORT=3000 这样的配置值仍可读取。短于 4 个字符的值不会被脱敏。以其他方式转换值的命令(例如生成哈希或截取部分值)可能会以 sealkeep 无法匹配的形式输出该值。
能够以你的用户身份运行任意 shell 命令的 Agent,也能读取你的用户可以读取的内容。sealkeep 不会改变这一点。它让安全的使用方式更简单,拒绝获取密钥值的常见途径,并记录每次使用。为 Agent 提供权限范围较小的密钥:受限的 Stripe 密钥、有支出限额的 OpenRouter 密钥,以及用于网页登录的测试账户。
构建和测试
cargo build --release
cargo test --features test-store
test-store 功能会用文件替代操作系统密钥环,但仅用于测试。发布版二进制文件不包含此功能。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。