在整个团队中部署 zn-gate
通过各智能体的托管配置把 zn-gate 的 hook 放到每台机器上:Claude Code 托管设置、Cursor 企业 hook 和 Codex 的 requirements.toml,以及如何检查一台机器。
本页面向为团队配置笔记本电脑的人。你不必让每位开发者各自运行 zn hooks install,而是用自己的工具(设备管理、Ansible、系统镜像、登录脚本)在系统文件夹中为每个智能体放置一个配置文件。之后智能体会为所有人运行 zn-gate 的 hook;在 Claude Code 和 Codex 中,你还可以阻止用户和项目关闭它们。
zn-gate 不会替你部署这些文件:它是本地防护,不是 MDM(参见 Team 概览)。下面的所有内容现在都可以手动完成。
各智能体的相关事实来自该智能体自己的文档,阅读于 2026-10-19;大规模部署前请再次查阅,因为这些设置会变化。hook 命令与 zn-gate 1.12.0 或更高版本 中 zn hooks install 为项目安装写入的命令相同。
开始之前
- 在每台机器上安装 zn-gate,所有机器使用相同版本。它需要 Node.js 18 或更高版本:
npm install -g zn-gate。请先做这一步。无法运行的 hook 并非无害:Claude Code 和 Codex 会在未检查的情况下放行工具调用,而当 hook 设置了failClosed时,Cursor 会阻止文件读取和提示词。 - 确保智能体能找到
zn。 下面的命令调用zn hook <agent>,所以zn必须在智能体运行时使用的 PATH 中。桌面应用并不总是能获得你终端的 PATH。如果不确定,请写完整路径(macOS 和 Linux 上用command -v zn,Windows 上用where zn),例如/usr/local/bin/zn hook claude-code。 - 先在一台机器上试用,检查它(见下文“检查一台机器”),然后再推广到其他机器。
Claude Code
Claude Code 从系统文件夹读取托管设置:
| 系统 | 文件夹 |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/ |
| Linux 和 WSL | /etc/claude-code/ |
| Windows | C:\Program Files\ClaudeCode\ |
该文件夹包含 managed-settings.json、可选的 managed-settings.d/(放 *.json 文件)和 managed-mcp.json。Claude Code 先合并 managed-settings.json,再按字母顺序合并 managed-settings.d/ 中的文件,并把所有文件中的 hook 列表合在一起。因此你可以把 zn-gate 的 hook 放在单独的文件里,例如 managed-settings.d/50-zn-gate.json,与其他团队维护的策略并列。
Hook 与 allowManagedHooksOnly
{
"allowManagedHooksOnly": true,
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|PowerShell|Monitor|Read|Grep|LSP|Edit|MultiEdit|Write|NotebookEdit|WebFetch",
"hooks": [{ "type": "command", "command": "zn hook claude-code", "timeout": 15 }]
}
],
"PostToolUse": [
{
"matcher": "^(Bash|PowerShell|Monitor|Read|Grep|LSP|WebFetch|ReadMcpResourceTool)$|^mcp__.*",
"hooks": [{ "type": "command", "command": "zn hook claude-code", "timeout": 15 }]
}
],
"UserPromptSubmit": [
{
"hooks": [{ "type": "command", "command": "zn hook claude-code", "timeout": 15, "onFailure": "block" }]
}
]
}
}
"onFailure": "block" 让 Claude Code 在 hook 无法启动时阻止提示词。它需要 Claude Code 2.1.295 或更高版本;更早的版本会忽略该字段。
从 zn-gate 1.13.0 起,zn-gate 还会检查输入的 skill 或命令展开后的内容。为此,请在 hooks 下添加这一项:"UserPromptExpansion": [{ "hooks": [{ "type": "command", "command": "zn hook claude-code", "timeout": 15, "onFailure": "block" }] }]。
allowManagedHooksOnly 的作用。 设为 true 时,Claude Code 只运行来自托管设置的 hook(再加上 Agent SDK 注册的 hook,以及你的托管设置强制启用的插件的 hook)。其余全部被阻止:用户 ~/.claude/settings.json 中的 hook、项目 .claude/settings.json 和 .claude/settings.local.json 中的 hook、其他插件的 hook,以及智能体 frontmatter 中的 hook。它有副作用:/goal 命令将无法使用,自定义状态栏必须来自托管设置。请有意识地做决定,并告知你的开发者。
只有当 zn-gate 的 hook 位于托管设置中时,该设置才保护 zn-gate。 如果你设置了 allowManagedHooksOnly 却没有放入这些 hook,开发者用 zn hooks install claude-code 或 zn-gate 插件安装的 hook 会停止运行,而 Claude Code 不会给出任何提示。zn-gate 会在 zn hooks install claude-code 和 zn-gate status 中告知开发者;当托管文件中包含 zn-gate 的 hook 时,它不会出声。
还请注意:
- 托管设置中的
disableAllHooks: true会关闭所有 hook,包括你的。请保持不设置。 - 如果你通过多种方式下发策略(服务器托管设置、设备管理、Windows 注册表和文件),Claude Code 默认使用设置了策略的最高优先级来源,并忽略其他来源,因此某个文件可能被忽略。请把 zn-gate 的 hook 放在 Claude Code 在该机器上使用的来源中。
/status会在Setting sources一行显示它。zn-gate 只读取文件。
MCP 服务器(可选)
保护智能体的是 hook。如果你还希望开发者使用 zn-gate 的 secrets MCP 服务器(它让智能体在看不到内容的情况下使用保存在 zn 保险库中的密钥),那么托管 MCP 规则决定它能否启动。请注意:managed-mcp.json 文件会取得独占控制权。 该文件一旦存在,Claude Code 只加载其中的服务器(以及少数内置服务器),用户无法添加任何其他服务器。请列出你的团队需要的所有服务器,而不只是 zn-gate 的。若 mcpServers 对象为空,则不会加载任何服务器。
managed-mcp.json 的格式与项目的 .mcp.json 相同:
{
"mcpServers": {
"secrets": {
"type": "stdio",
"command": "zn",
"args": ["secrets-mcp", "--agent", "claude-code"]
}
}
}
如果你不想要独占控制,可改用托管设置中的允许列表 allowedMcpServers 和 allowManagedMcpServersOnly,并把 secrets 服务器加入列表。参见 Claude Code 的托管 MCP 配置。当托管文件或列表阻止 secrets 服务器启动时,zn-gate status 会告知开发者。
Cursor
Cursor 从系统的 hooks.json 读取企业 hook:
| 系统 | 文件 |
|---|---|
| macOS | /Library/Application Support/Cursor/hooks.json |
| Linux 和 WSL | /etc/cursor/hooks.json |
| Windows | C:\ProgramData\Cursor\hooks.json |
{
"version": 1,
"hooks": {
"beforeMCPExecution": [{ "command": "zn hook cursor", "timeout": 30 }],
"beforeReadFile": [{ "command": "zn hook cursor", "timeout": 30, "failClosed": true }],
"beforeShellExecution": [{ "command": "zn hook cursor", "timeout": 30 }],
"beforeSubmitPrompt": [{ "command": "zn hook cursor", "timeout": 30, "failClosed": true }],
"postToolUse": [{ "command": "zn hook cursor", "timeout": 30 }],
"preToolUse": [{ "command": "zn hook cursor", "timeout": 30, "matcher": "Grep|Write|Delete" }]
}
}
这与 zn hooks install cursor 是同一组 hook,只是在读取文件和提示词的两个 hook 上加了 failClosed。设置 failClosed: true 后,hook 崩溃、超时或没有返回,都会阻止该操作,而不是放行。这就是必须先安装 zn-gate 的原因:在没有 zn 的机器上,Cursor 会阻止文件读取和提示词。
Cursor 如何处理多个来源:它会运行每个 hooks.json(企业、团队、项目和用户)中的 hook,企业级优先,其中任何一个返回 deny 都会生效。因此开发者自己的 hook 仍会与你的 hook 一起运行。Cursor 的文档(阅读于 2026-10-19)没有描述能让只运行企业 hook 的设置,所以在 Cursor 中无法像 Claude Code 和 Codex 那样强制使用 zn-gate。Cursor 的企业版方案还可以从其网页控制台分发 hook;本页只涉及文件方式。
zn-gate 不覆盖 Cursor 命令行(cursor-agent)。
Codex CLI
Codex 从系统的 requirements.toml 读取管理员要求:
| 系统 | 文件 |
|---|---|
| macOS 和 Linux | /etc/codex/requirements.toml |
| Windows | %ProgramData%\OpenAI\Codex\requirements.toml |
在 macOS 上,设备管理也可以在 com.openai.codex 域中以 requirements_toml_base64 的形式下发相同内容。参见 OpenAI 的托管配置。
allow_managed_hooks_only = true
[features]
hooks = true
[hooks]
managed_dir = "/opt/example-managed-hooks"
windows_managed_dir = 'C:\ProgramData\ExampleCorp\managed-hooks'
[[hooks.PreToolUse]]
matcher = "^(Bash|apply_patch)$"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "zn hook codex"
command_windows = "zn hook codex"
timeout = 30
statusMessage = "zn: checking for secrets"
[[hooks.PostToolUse]]
matcher = ".*"
[[hooks.PostToolUse.hooks]]
type = "command"
command = "zn hook codex"
command_windows = "zn hook codex"
timeout = 30
statusMessage = "zn: checking for secrets"
[[hooks.UserPromptSubmit]]
[[hooks.UserPromptSubmit.hooks]]
type = "command"
command = "zn hook codex"
command_windows = "zn hook codex"
timeout = 30
statusMessage = "zn: checking for secrets"
这些键的作用,依据 Codex 的文档:
allow_managed_hooks_only = true让 Codex 跳过用户、项目、会话和插件的 hook,只加载托管 hook。[features] hooks = true即使在用户于自己的config.toml中关闭了 hook 的情况下,也保持 hook 开启。managed_dir和windows_managed_dir必须是已存在文件夹的绝对路径,否则 Codex 不会加载托管 hook。zn-gate 不需要其中放任何脚本,所以你自己创建一个空文件夹即可。- 托管 hook 被视为由策略信任:与开发者自己安装的 hook 不同,它们不需要在
/hooks中审核,用户也无法在那里关闭它们。
如果 hook 无法启动,Codex 会放行该调用(以及提示词),所以同样的规则适用:先安装 zn-gate。
检查一台机器
1.12.0 版本的 zn-gate check 读取每位用户自己的配置,而不读取托管文件。 在仅通过托管配置部署 zn-gate 的机器上,即使托管 hook 正常工作,它也会显示 “zn is not set up for this agent”。请不要把它当作部署失败。请改用下面的检查。
-
询问智能体它使用了哪些设置。
- Claude Code:运行
/status,查看Setting sources一行。它必须列出企业托管设置(文件方式显示 “file”)。claude doctor会列出 Claude Code 丢弃的内容。 - Cursor:在 Customize 中打开 Hooks 标签页,并查看 Hooks 输出通道,了解已运行的 hook 和任何错误。
- Codex:运行
/hooks。托管 hook 会被标记为托管。
- Claude Code:运行
-
在 Claude Code 机器上运行
zn-gate status。 它会用一行报告阻止 zn-gate hook 或其secrets服务器的托管设置(disableAllHooks、没有 zn-gate hook 的allowManagedHooksOnly、没有该服务器的managed-mcp.json、MCP 允许列表或拒绝列表)。没有这一行表示它在文件中没有发现任何此类设置;它看不到来自服务器托管设置、设备管理或注册表的策略。 -
测试 hook 命令本身。 在 macOS 或 Linux 上,在一个空文件夹中创建带假密钥的测试
.env文件,然后像智能体那样让 hook 读取它:printf 'ZN_CHECK_EXAMPLE_KEY=zn-check-fake-value-not-a-real-key\n' > .env printf '{"hook_event_name":"PreToolUse","session_id":"test","cwd":"%s","tool_name":"Read","tool_input":{"file_path":"%s/.env"}}' "$PWD" "$PWD" | zn hook claude-code返回内容必须包含
"permissionDecision":"deny"。对于README.txt这样的普通文件,则不能包含。Codex 的同类测试是把带"command":"cat .env"的Bash调用传给zn hook codex;Cursor 则是把{"hook_event_name":"beforeReadFile","cwd":"…","file_path":"…/.env","content":"x","workspace_roots":["…"]}传给zn hook cursor,它会返回"permission":"deny"。这证明你写的命令能够启动,且 zn 的规则会阻止该文件。它不能证明智能体会调用它:这要靠第 1 步和第 2 步。 -
端到端查看。 在只含上述假
.env的临时文件夹中,让智能体读取它。它必须被拒绝,并显示以zn:开头的消息。
保持其正常运作
- 同时更新所有机器。 在所有机器上安装相同的 zn-gate 版本,只在该版本需要时才修改托管文件;更新日志会说明 hook 条目何时变化(例如 1.13.0 为 Claude Code 增加了一项)。
- 加入组织。 托管 hook 负责执行检查;组织策略和审计记录要求每台机器用
zn team join加入。参见加入设备和组织策略。 - 要移除 zn-gate, 请删除托管文件中的条目;
zn hooks uninstall只会移除它自己写入的内容。
本页不涉及的内容
OpenCode,以及 zn-gate 尚不支持的智能体(参见面向 AI 智能体的文档)。Cursor 和 Codex 的文件未在 Windows 上测试。如果这里的内容与你看到的不符,请发邮件至 support@usezn.com。