AI 编程 CLI 的权限怎么配:Claude Code、Codex CLI 与 OpenCode 对比
这篇文章只聚焦一个问题:三款 AI 编程 CLI 分别怎样控制权限,规则写在哪里,真正生效时又按什么顺序匹配。
如果还没决定用哪一款工具,建议先看总览文,先把选型框架建立起来,再回来研究权限细节。
总览文入口:AI 编程 CLI 工具怎么选:Claude Code、Codex CLI 与 OpenCode 对比
写在前面
如果把指令文件比作 AI 的“项目说明书”,那权限配置更像机房门禁。说明书决定它应该怎么做,权限系统决定它到底能做什么。
三款工具的差别主要集中在三个地方:
- Claude Code 侧重“配置层级 + 白名单规则”,适合需要严格边界的场景。
- Codex CLI 侧重“OS 沙箱 + 审批策略 + 命令前缀 Rules”,既能配置整体权限边界,也能对重复命令做细粒度决策。
- OpenCode 侧重“工具级权限 + 命令匹配”,灵活度高,但也更依赖使用者自己设计规则。
如果你还在研究项目说明文件怎么加载、怎么合并,可以配合下面这篇专题一起看:
AI 编程 CLI 的指令文件怎么生效:CLAUDE.md、AGENTS.md 与 OpenCode 对比
如果主要想解决 Codex 执行 Git、GitHub CLI 等命令时反复申请权限的问题,可以直接看 Codex Rules 实用指南:用 prefix_rule 减少重复授权。
三款工具的权限机制概览
| 维度 | Claude Code | Codex CLI | OpenCode |
|---|
| 权限配置文件 | settings.json | config.toml + .rules | opencode.json |
| 控制粒度 | 细粒度(工具 + 路径模式) | 沙箱、审批策略 + 命令参数前缀 | 细粒度(工具 + 命令模式) |
| 沙箱机制 | 规则白名单 | macOS Seatbelt / Linux bwrap + seccomp | 规则白名单 |
| 企业管控 | ✅ managed 层不可覆盖 | ✅ Team Config + requirements.toml | ⚠️ 远程配置 |
配置文件层级
Claude Code — settings.json(4 层级,固定路径,不遍历目录)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| 优先级从高到低:
① 系统托管(不可被任何层级覆盖)
macOS: /Library/Application Support/ClaudeCode/managed-settings.json
Linux: /etc/claude-code/managed-settings.json
Windows: C:\Program Files\ClaudeCode\managed-settings.json
HKLM\SOFTWARE\Policies\ClaudeCode(注册表)
② 命令行参数(临时会话级覆盖)
③ 本地项目(个人私有,git 忽略)
<repo>/.claude/settings.local.json
④ 共享项目(团队共享,git 追踪)
<repo>/.claude/settings.json
⑤ 用户全局(最低优先级)
~/.claude/settings.json
|
与 CLAUDE.md 加载行为的核心差异:
1
2
| CLAUDE.md → 目录向上遍历,沿途所有文件全部加载(越过 git root 继续)
settings.json → 固定路径,不做任何目录遍历
|
关键限制:~/projects/.claude/settings.json 这样的路径即使存在也不会被加载,跨项目共享权限只能放入 ~/.claude/settings.json。
Codex CLI — config.toml 与 .rules
Codex 的权限配置分成两个部分:
1
2
3
4
5
6
| ~/.codex/config.toml
├── sandbox_mode 控制文件系统与网络访问边界
└── approval_policy 控制什么时候需要申请批准
~/.codex/rules/*.rules
└── prefix_rule 按命令参数前缀设置 allow / prompt / forbidden
|
项目也可以提供自己的配置层:
1
2
| <repo>/.codex/config.toml
<repo>/.codex/rules/*.rules
|
项目级 .codex/ 只有在项目受信任时才会加载。CLI 与 IDE 扩展共享配置层;组织管理员还可以通过 Team Config 和 requirements.toml 施加约束。
本地运行时,macOS 使用 Seatbelt,Linux 当前使用 bwrap 配合 seccomp。Sandbox 决定命令技术上能访问什么,审批策略决定什么时候询问,Rules 再对具体命令前缀给出决策。
OpenCode — opencode.json(多层级,支持目录遍历)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| 加载顺序(优先级从低到高):
① 远程组织配置(最低)
.well-known/opencode(通过 HTTP 拉取,组织级默认值)
② 用户全局配置
~/.config/opencode/opencode.json
③ 环境变量指定路径
$OPENCODE_CONFIG 指向的文件
④ 项目配置(从当前目录向上遍历查找,停在 git root)
./opencode.json
⑤ .opencode 目录(agents/, commands/ 等子目录)
⑥ 内联配置(最高)
$OPENCODE_CONFIG_CONTENT 环境变量内容
|
OpenCode 特点:opencode.json 支持目录向上遍历查找(不同于 Claude Code 的固定路径),且多层级配置深度合并而非替换。
权限规则语法
Claude Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // .claude/settings.json
{
"permissions": {
"allow": [
"Bash(npm run *)", // Bash 命令,支持通配符
"Bash(git commit *)",
"Read(./src/**)", // 文件读取,gitignore 风格
"Read(~/data/work/**)", // 波浪号路径(指定目录免确认)
"Edit(./src/**/*.java)", // 文件编辑
"WebFetch(domain:github.com)", // 域名匹配
"Glob(**/*.ts)", // 文件搜索
"Grep(./src/**)" // 内容搜索
],
"deny": [
"Bash(rm -rf *)",
"Bash(curl *)",
"Read(./.env*)", // 拒绝读取环境变量文件
"Read(./secrets/**)"
],
"ask": [
"Bash(git push *)", // 敏感操作需二次确认
"Bash(docker *)",
"Bash(kubectl *)"
]
}
}
|
匹配规则:deny → ask → allow,首个匹配生效,后续规则忽略。
支持的工具名:
| 工具 | 说明 | 路径模式 |
|---|
Bash(pattern) | Shell 命令执行 | 命令通配符,如 npm run * |
Read(pattern) | 文件读取 | gitignore 风格,如 ./src/** |
Edit(pattern) | 文件编辑 | gitignore 风格 |
Write(pattern) | 文件写入 | gitignore 风格 |
Glob(pattern) | 文件名搜索 | glob 模式 |
Grep(pattern) | 内容搜索 | 目录路径 |
WebFetch(domain:x) | HTTP 请求 | 域名匹配 |
Agent(name) | 子 Agent 调用 | Agent 名称 |
mcp__srv__tool | MCP 工具 | 服务名__工具名 |
路径写法规则:
| 写法 | 含义 |
|---|
Read(./src/**) | 项目内相对路径 |
Read(~/data/work/**) | 家目录下的路径 |
Read(//absolute/path/**) | 双斜杠开头表示绝对路径 |
Read(./.env*) | 精确匹配特定文件 |
OpenCode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // opencode.json
{
"permission": {
// 工具级别全局设置
"read": "allow",
"edit": "allow",
"glob": "allow",
"grep": "allow",
"list": "allow",
"webfetch": "ask",
"lsp": "allow",
// bash 细粒度规则(最后匹配生效)
"bash": {
"*": "ask", // 默认所有命令需确认
"git *": "allow", // git 命令放行
"npm run *": "allow", // npm 命令放行
"rm *": "deny", // 拒绝 rm
"curl *": "deny" // 拒绝 curl
}
}
}
|
匹配规则:最后匹配生效(Last Match Wins),与 Claude Code 相反。
支持的工具名:
| 工具 | 说明 | 默认值 |
|---|
read | 文件读取 | allow |
edit | 文件编辑 | allow |
glob | 文件搜索 | allow |
grep | 内容搜索 | allow |
list | 目录列表 | allow |
bash | Shell 命令 | ask |
webfetch | HTTP 请求 | allow |
task | 任务执行 | allow |
lsp | 语言服务器 | allow |
skill | 技能调用 | allow |
external_directory | 外部目录访问 | ask |
doom_loop | 循环执行 | ask |
特殊默认行为:即使 read 为 allow,.env 文件默认仍为 deny,需显式解除。
Codex CLI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # ~/.codex/rules/github-readonly.rules
prefix_rule(
pattern = ["gh", "pr", "view"],
decision = "allow",
justification = "允许只读查看 Pull Request。",
match = [
"gh pr view 123",
"gh pr view 123 --json title,state",
],
not_match = [
"gh pr create",
"gh pr merge 123",
],
)
|
Codex Rules 使用参数前缀匹配。多条规则同时命中时,采用最严格的结果:forbidden > prompt > allow。match 和 not_match 是加载规则时执行的内联测试,不是运行时的允许和拒绝列表。
Rules 目前仍属于实验性功能,完整语法、尾随参数风险和 codex execpolicy check 测试方法见 Codex Rules 实用指南。
合并策略
Claude Code — 数组合并 + 标量覆盖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| 数组类字段(跨层级合并,去重):
permissions.allow / deny / ask
示例:
managed-settings.json: allow: ["Bash(//opt/company-tools/*)"]
~/.claude/settings.json: allow: ["Bash(npm run *)", "Bash(mvn *)"]
.claude/settings.json: allow: ["Bash(git *)", "Read(./src/**)"]
最终生效:
allow: [
"Bash(//opt/company-tools/*)", ← 来自 managed
"Bash(npm run *)", ← 来自用户全局
"Bash(mvn *)", ← 来自用户全局
"Bash(git *)", ← 来自项目
"Read(./src/**)" ← 来自项目
]
标量字段(高优先级完全覆盖低优先级):
model、theme 等
deny 的特殊地位:
任何层级的 deny 均生效,低层级无法解除
managed 层设置的 deny 不可被任何层级覆盖
|
OpenCode — 深度合并 + 最后匹配
规则一:多层级配置文件深度合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| ~/.config/opencode/opencode.json(用户全局):
{
"permission": {
"read": "allow",
"edit": "allow",
"bash": {
"*": "ask",
"git *": "allow"
}
}
}
./opencode.json(项目级):
{
"permission": {
"webfetch": "deny", // 项目级新增字段
"bash": {
"npm run *": "allow" // 项目级追加规则
}
}
}
合并结果:
{
"permission": {
"read": "allow", // 来自全局
"edit": "allow", // 来自全局
"webfetch": "deny", // 来自项目(新增)
"bash": {
"*": "ask", // 来自全局
"git *": "allow", // 来自全局
"npm run *": "allow" // 来自项目(追加)
}
}
}
|
规则二:bash 规则内部按"最后匹配"生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| 合并后的 bash 规则:
"*": "ask"
"git *": "allow"
"npm run *": "allow"
执行 "git commit -m fix" 时:
匹配 "*" → ask
匹配 "git *" → allow ← 最后匹配,最终生效 ✅
执行 "curl http://..." 时:
匹配 "*" → ask ← 唯一匹配,最终生效 ✅
执行 "rm -rf /tmp" 时:
匹配 "*" → ask ← 唯一匹配,最终生效 ✅
|
规则三:Agent 级别直接覆盖全局(不合并)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| {
"permission": {
"bash": { "*": "ask", "git *": "allow" } // 全局权限
},
"agent": {
"reviewer": {
"permission": {
"read": "allow",
"bash": "deny", // 直接覆盖,全局的 git * allow 被完全忽略
"edit": "deny"
}
}
}
}
|
1
2
3
4
5
6
7
| reviewer agent 最终权限:
read: allow ← agent 定义
bash: deny ← agent 定义(全局 git * allow 失效)
edit: deny ← agent 定义
默认 agent 最终权限:
bash: { "*": "ask", "git *": "allow" } ← 使用全局规则
|
与 Claude Code 的关键区别:Claude Code 的 deny 一旦设置不可逆;OpenCode 的 agent 级别可以完全覆盖全局规则,包括把全局的 deny 改回 allow。
多仓库场景实践
Claude Code 配置示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| ~/projects/
├── CLAUDE.md ✅ 指令文件,目录遍历自动加载
├── .claude/settings.json ❌ 不会被加载(不在固定路径)
│
├── order-service/
│ ├── CLAUDE.md ✅ 指令文件,自动加载
│ └── .claude/
│ ├── settings.json ✅ 项目级权限(团队共享)
│ └── settings.local.json ✅ 本地个人权限(git 忽略)
│
└── notify-service/
├── CLAUDE.md ✅ 指令文件,自动加载
└── .claude/
└── settings.json ✅ 项目级权限(团队共享)
|
~/.claude/settings.json(跨所有项目生效):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| {
"permissions": {
"allow": [
"Bash(git *)",
"Bash(mvn *)",
"Bash(npm run *)",
"Read(~/data/work/**)",
"Read(~/.claude/**)"
],
"deny": [
"Bash(rm -rf *)",
"Read(./.env*)",
"Read(./secrets/**)"
],
"ask": [
"Bash(git push *)",
"Bash(kubectl *)",
"Bash(docker *)"
]
}
}
|
~/projects/order-service/.claude/settings.json(仅 order-service 生效):
1
2
3
4
5
6
7
8
9
10
| {
"permissions": {
"allow": [
"Bash(java *)",
"Bash(mvn spring-boot:run)",
"Read(./src/**)",
"Edit(./src/**)"
]
}
}
|
~/projects/order-service/.claude/settings.local.json(个人本地,不提交 git):
1
2
3
4
5
6
7
8
| {
"permissions": {
"allow": [
"Bash(ssh *)",
"Read(~/.ssh/config)"
]
}
}
|
合并结果:三个文件的 permissions 数组跨层级合并去重,最终生效的是三者的并集。
Codex CLI 配置示例
用户级配置跨所有项目生效:
1
2
3
4
5
| ~/.codex/
├── config.toml
└── rules/
├── git-commit.rules
└── github-readonly.rules
|
项目可以在受信任的仓库中增加自己的配置层:
1
2
3
4
5
| ~/projects/order-service/
└── .codex/
├── config.toml
└── rules/
└── project.rules
|
配置优先级从高到低依次包括 CLI 参数、项目配置、Profile、用户配置、系统配置和内置默认值。项目被标记为不受信任时,Codex 会跳过项目内的配置、Hooks 和 Rules,但用户级与系统级配置仍然生效。
Rules 不适合直接放行整个 git、gh、bash 或 python。更稳妥的做法是细分到稳定子命令,并使用 match、not_match 和 codex execpolicy check 验证边界。
OpenCode 配置示例
~/.config/opencode/opencode.json(跨所有项目生效):
1
2
3
4
5
6
7
8
9
10
11
12
13
| {
"permission": {
"read": "allow",
"edit": "allow",
"bash": {
"*": "ask",
"git *": "allow",
"mvn *": "allow",
"rm -rf *": "deny",
"curl *": "deny"
}
}
}
|
~/projects/order-service/opencode.json(仅 order-service 生效,与全局深度合并):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"permission": {
"bash": {
"java *": "allow",
"mvn spring-boot:run": "allow"
}
},
"agent": {
"reviewer": {
"permission": {
"read": "allow",
"edit": "deny",
"bash": "deny"
}
}
}
}
|
横向对比总表
配置文件对比
| 特性 | Claude Code | Codex CLI | OpenCode |
|---|
| 配置文件 | settings.json | config.toml + .rules | opencode.json |
| 层级数量 | 4层(managed/user/project/local) | CLI / project / profile / user / system | 多层(remote/global/project/inline) |
| 项目配置加载 | 固定项目路径 | 受信任项目的 .codex/ 配置层 | 向上遍历查找(停在 git root) |
| 企业管控层 | ✅ managed 不可覆盖 | ✅ Team Config + requirements.toml | ⚠️ 远程配置(需 HTTPS) |
| 跨项目共享 | ~/.claude/settings.json | ~/.codex/config.toml、~/.codex/rules/ | ~/.config/opencode/opencode.json |
权限规则对比
| 特性 | Claude Code | Codex CLI | OpenCode |
|---|
| 细粒度 allow/deny/ask | ✅ | ✅ allow/prompt/forbidden | ✅ |
| 匹配规则逻辑 | 首个匹配(deny→ask→allow) | 参数前缀匹配,最严格决策生效 | 最后匹配 |
| 跨层规则处理 | permissions 数组跨层合并去重 | 扫描活动配置层中的 Rules | 深度合并 |
| deny 可逆性 | 任意层级 deny 不可被低层解除 | 多规则命中时 forbidden 最严格 | agent 级可覆盖全局 |
| Agent 级别权限 | ❌ | ❌ Rules 本身不按 Agent 划分 | ✅ 可覆盖全局 |
| .env 文件保护 | ⚠️ 需手动 deny | ⚠️ 无专门默认 deny,依赖文件权限与 Sandbox | ✅ 默认 deny |
规则匹配逻辑对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| Claude Code(首个匹配):
规则检查顺序:deny → ask → allow
第一个匹配的规则立即生效,后续规则忽略
陷阱示例:
deny: ["Bash(rm *)"]
allow: ["Bash(rm -rf /tmp/*)"] ← 永远不会生效(已被 deny 首先匹配)
正确写法:把宽泛的 deny 放后面,具体的 allow 放前面
allow: ["Bash(rm -rf /tmp/*)"] ← 先匹配,放行 /tmp 下的删除
deny: ["Bash(rm *)"] ← 再匹配其他 rm 命令
Codex(参数前缀 + 最严格决策):
pattern = ["gh", "pr", "view"]
可以匹配 "gh pr view 123 --json title,state"
多条规则同时匹配时:
forbidden > prompt > allow
注意:前缀后仍可追加参数,Rules 不提供命令结尾锚点。
例如允许 ["git", "push", "origin", "main"] 时,
还必须测试 "git push origin main --force" 这类尾随参数。
OpenCode(最后匹配):
越靠后的规则优先级越高,适合从宽泛默认逐步精细化
示例:
"bash": {
"*": "ask", // 默认询问
"rm *": "deny", // 拒绝 rm
"rm /tmp/*": "allow" // 最后匹配:/tmp 下 rm 允许 ✅
}
|
总结
如果你的首要目标是把风险压到最低,Claude Code 的层级和规则模型更容易建立清晰边界。
如果你希望同时保留 OS 沙箱、整体审批策略和命令级规则,Codex CLI 可以用 config.toml 与 .rules 分层控制。Rules 的前缀匹配很直接,但需要特别审阅尾随参数,不能把它当作完整的命令语法防火墙。
如果你需要更自由的命令匹配和多 Agent 权限设计,OpenCode 的可塑性会更强,但也更考验配置习惯。
文中的 Codex Rules 行为已在 2026-07-12 使用 codex-cli 0.144.1 重新验证。Rules 目前仍属于实验性功能,真正上线前仍然建议再对照一次官方文档。
参考资料: