让密钥远离你的编码智能体(5 分钟)
把密钥放进本机的加密保险库,让你的编码智能体只使用占位标记而不是真实值。保险库、.env、钩子、命令、API 和 MCP 配置。
zn-gate 让密钥的真实值不会随编码智能体发送给它的模型。智能体使用 __ZN_GITHUB_TOKEN__ 这样的占位标记;真实值保存在你本机的加密保险库中,只会填入你允许的位置。一切都在本地运行,无需账号。
支持 Claude Code、Codex CLI、Cursor(IDE)和 OpenCode。各个智能体能做什么、不能做什么见支持的智能体;局限见威胁模型。
1. 安装并解锁保险库
npm install -g zn-gate
zn unlock
在 Windows PowerShell 中,npm 和 npx 可能报错“在此系统上禁止运行脚本”。请改用 npm.cmd 和 npx.cmd,从命令提示符运行命令,或用 Set-ExecutionPolicy -Scope CurrentUser RemoteSigned 为你的账号允许本地脚本。详见在 Windows 上安装。
第一次运行 zn unlock 会创建保险库:一个 age 身份,保存在系统钥匙串中(macOS 钥匙串、Windows 凭据管理器,或 Linux 上的 Secret Service);没有钥匙串时则由口令保护。保险库会保持解锁 8 小时,或在 30 分钟未使用后锁定,或直到你运行 zn lock。
更新 zn-gate 之后,仍在运行旧版本的保险库在重启之前,会在智能体钩子中把密钥保留为占位标记。zn vault status 会显示正在运行的保险库的版本,并标出过期的版本,zn unlock 会替你重启它。
zn 会要求你在智能体之外确认某些操作(第 4 步)。在桌面上它会弹出系统对话框。在服务器或 SSH 上没有对话框:请在单独的终端中运行保险库,并在那里回答。
zn unlock --foreground
2. 把 .env 的值移入保险库
zn env shadow .env
每个敏感值都会移入保险库,.env 只保留占位标记:
DATABASE_URL=postgres://app:__ZN_DATABASE_URL_PASSWORD__@db:5432/app
STRIPE_KEY=__ZN_STRIPE_KEY__
PORT=3000
原文件的加密副本会被保留,zn env unshadow 可以恢复它。要添加单个密钥,请在隐藏的输入提示中输入(切勿作为参数):
zn secret set GITHUB_TOKEN --host api.github.com
3. 为你的智能体安装钩子
zn hooks install claude-code # 或:codex、cursor、opencode
从现在起,每次读取文件、命令输出和粘贴的提示词,在到达模型之前都会经过清理器。已知的值、60 多种密钥格式(云密钥、令牌、私钥、连接字符串)以及能通过校验和的个人信息,都会被替换为标记。详见脱敏。
钩子会阻止:
- 读取含有真实值的
.env文件。 - 凭据文件,例如
~/.aws/credentials、~/.ssh/id_*和~/.npmrc,以及 zn 自己的~/.zn文件夹。 - 含有密钥的提示词:它不会被发送。在提示词中,zn 还会阻止赋给凭据名称的 8 个字符及以上的值,即使它出现在句子中间(
my key is STRIPE_KEY=...、DB_PASSWORD=...)。示例值(changeme123、<key>)、引用(${API_KEY}、__ZN_STRIPE_KEY__)或代码则会放行。请用zn secret set NAME保存该值,并在提示词中写__ZN_NAME__。
Codex 只会在你信任新钩子之后才运行它们:打开 Codex 并使用 /hooks。
在由组织管理的电脑上,Claude Code 可能被设置为忽略你自己安装的钩子和 MCP 服务器(disableAllHooks、allowManagedHooksOnly、managed-mcp.json,或 MCP 允许列表或拒绝列表),而且不会通知你。zn hooks install claude-code 和 zn-gate status 会读取这台电脑上的托管设置文件和你自己的设置,并在其中某项阻止了 zn 的钩子或它的 secrets 服务器时告诉你。请让管理员把 zn 加入托管设置。通过 claude.ai、设备管理(MDM)或 Windows 注册表下发的策略不会被读取,因此存在这类策略时,Claude Code 可能会忽略托管设置文件。claude doctor 和 /status 会显示 Claude Code 使用了哪个来源。
4. 让命令使用密钥
允许需要该值的程序:
zn secret allow DATABASE_URL_PASSWORD --command psql
当智能体运行 psql "postgres://app:__ZN_DATABASE_URL_PASSWORD__@db/app" 时,钩子会把它交给 zn run。该值只进入那个进程,其输出会被遮盖:原始、base64、十六进制和 URL 编码的形式都会以标记返回。第一次使用时,保险库会在它的对话框或终端中请你确认,提示会显示将要运行的确切程序。更改密钥的策略(zn secret allow)也以同样方式确认。智能体无法在它自己的终端中回答这些提示。
请只允许特定的程序。node、python、npm、make 和其他解释器会运行任何交给它们的代码,因此允许其中之一,意味着它运行的每个脚本都能读取该密钥。你这样做时 zn 会发出警告。
5. 让智能体调用 API
对于 HTTP API,请使用 secrets MCP 服务器,而不是把值放进进程。zn hooks install 会打印如何在你的智能体中注册它。智能体在请求头中带上标记来调用 secrets_http_request:
{ "method": "GET", "url": "https://api.github.com/user",
"headers": { "Authorization": "Bearer __ZN_GITHUB_TOKEN__" } }
zn 只会把请求发往该密钥允许的主机(allowHosts),只会把值放在请求头中(要用于 URL 或请求体,请运行 zn secret allow NAME --in query|body),并在返回响应时把密钥替换为标记。没有任何工具会返回密钥的值。
MCP 配置
上面的 secrets 服务器是智能体使用密钥的安全方式。相反,写在智能体配置文件中的明文密钥会随仓库一起传播。zn audit 会报告智能体配置或指令文件中的密钥值(发现项 ZNA011),并且从不打印该值。详见审计。
命令参考
| 命令 | 作用 |
|---|---|
zn unlock |
创建或解锁保险库;--foreground 在单独的终端中运行它 |
zn lock |
立即锁定保险库 |
zn vault status |
显示正在运行的保险库的版本以及是否过期 |
zn env shadow .env |
把 .env 文件的值移入保险库并留下标记 |
zn env unshadow |
从加密副本恢复原始的 .env |
zn secret set NAME |
通过隐藏的输入提示保存密钥;--host 限制它可以发送到的主机 |
zn secret allow NAME --command PROG |
让某个程序使用该密钥;--in query|body 用于 URL 和请求体 |
zn secret list |
名称、模式、主机和命令(绝不显示值) |
zn secret rm NAME |
删除一个密钥 |
zn vault log |
本地审计日志:谁在哪里使用了哪个密钥,不含值。也可用 zn secret log;--since 1h 限定时间 |
zn vault verify |
检查审计日志的 HMAC 链 |
zn hooks install <agent> |
为 claude-code、codex、cursor 或 opencode 安装钩子 |
zn hooks uninstall <agent> |
移除钩子。对于 Claude Code,还会移除 zn 自己的 secrets MCP 服务器,并保留任何其他同名服务器 |
如果保险库已锁定,钩子仍会对能识别的内容做脱敏,但使用的标记无法恢复(__ZN_REDACTED_AWS_KEY__),并提醒你运行 zn unlock。
要检查你的机器上哪些内容受到保护,请运行 npx -y zn-gate check。