Featured image of post AI 编程 CLI 的权限怎么配:Claude Code、Codex CLI 与 OpenCode 对比

AI 编程 CLI 的权限怎么配:Claude Code、Codex CLI 与 OpenCode 对比

聚焦 Claude Code、Codex CLI 与 OpenCode 的权限控制方式,对比配置文件层级、规则语法、匹配逻辑与多仓库实践,帮助快速设计安全边界。

-- 次浏览
-- 条评论

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 CodeCodex CLIOpenCode
权限配置文件settings.jsonconfig.toml + .rulesopencode.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__toolMCP 工具服务名__工具名

路径写法规则:

写法含义
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
bashShell 命令ask
webfetchHTTP 请求allow
task任务执行allow
lsp语言服务器allow
skill技能调用allow
external_directory外部目录访问ask
doom_loop循环执行ask

特殊默认行为:即使 readallow.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 > allowmatchnot_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/**)"                  来自项目
  ]

标量字段(高优先级完全覆盖低优先级):
  modeltheme 

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 不适合直接放行整个 gitghbashpython。更稳妥的做法是细分到稳定子命令,并使用 matchnot_matchcodex 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 CodeCodex CLIOpenCode
配置文件settings.jsonconfig.toml + .rulesopencode.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 CodeCodex CLIOpenCode
细粒度 allow/deny/askallow/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 目前仍属于实验性功能,真正上线前仍然建议再对照一次官方文档。

参考资料