Codex 自定义权限档:按目录和域名给 AI 发通行证

2026-08-09

用 Codex(OpenAI Codex)久了会碰到一个尴尬:沙箱那三档 read-only / workspace-write / danger-full-access,粒度是「整体松紧」,不是「具体谁能碰什么」。你想要的往往是「这个仓库里只准动 src/,配置文件一个字都别碰」「装依赖可以联网,但只准连包管理源」——三档模式表达不了这种诉求。

权限档(permissions profile)就是官方给这一层需求准备的东西。它在 config.toml 里是一组以 permissions.<name>. 开头的键,可以按路径、按 glob、按域名逐条发通行证。下面按「结构怎么读 → 怎么决定各项取值 → 怎么套用 → 哪些地方最容易误判」的顺序过一遍。

一、权限档的骨架:一个名字 + 四组键

在官方《Configuration Reference》里,权限档相关的键是这些:

说明
default_permissions沙箱化工具的默认权限档名
permissions.<name>.description权限档说明文字
permissions.<name>.extends父档::read-only:workspace 或某个命名档
permissions.<name>.workspace_roots.<path>boolean,把某路径纳入该档的工作区根
permissions.<name>.filesystem.<path-or-glob>read / write / deny,或嵌套表
permissions.<name>.filesystem.glob_scan_max_depthnumber(≥1),deny-read glob 展开的最大深度
permissions.<name>.filesystem.":workspace_roots".<subpath>相对工作区根的作用域授权
permissions.<name>.network.enabled该档是否允许联网
permissions.<name>.network.modelimitedfull
permissions.<name>.network.domains.<pattern>allowdeny,支持精确主机与通配
permissions.<name>.network.unix_sockets.<path>allowdeny

读这张表的关键是看出它的层次:<name> 是档名,你自己起;extends 决定从哪个基线出发;filesystemnetwork 是两条互不相干的授权线;default_permissions 是「不特别指定时用哪个档」。

这里先说一条重要的边界:官方在这一节没有给这些键的默认值。所以别照着别处的经验假设「network.enabled 默认是关的」或者「mode 默认是 limited」——文档没写就是没写,要么显式写出来,要么以官方文档为准。本文后面所有示例都采取「该写的都写出来」的策略,就是为了避开这类假设。

二、extends:先决定从哪儿起步

extends 接受三种值::read-only:workspace,或者你自己定义的另一个档名。前两个带冒号前缀,是官方内置的基档;冒号是它们和自定义档名的区分标记。

怎么选,取决于你希望「没写到的东西」默认是什么状态:

  • :read-only 起步 ——「默认不能写」,你要逐条把可写的目录加回来。适合做审计、做代码评审、跑分析类任务的档。
  • :workspace 起步 ——「默认工作区内可写」,你要逐条把不许碰的地方 deny 掉。适合日常改代码。
  • 从另一个命名档起步 —— 适合团队里已经有一个基线档,某个场景在它基础上再收紧或再放开一点点。

这一步定错,后面每条规则都得反着写,配置会长得非常难维护。判断方法很朴素:数一数「你要放行的条目」和「你要禁掉的条目」哪边少,就从让少的那边需要显式声明的基档起步。

三、文件系统:read / write / deny 三态,和两种写法

permissions.<name>.filesystem.<path-or-glob> 的取值是 readwritedeny 三选一(官方注明也可以是嵌套表)。三态的存在意味着「能读」和「能写」是分开的两件事——这一点比只有「工作区内/工作区外」的粗粒度有用得多:仓库里的密钥模板、生产配置,你完全可以给 deny,让它连读都读不到,而不必把整个目录踢出工作区。

授权路径有两种写法,用途不一样:

  • permissions.<name>.filesystem.<path-or-glob>:写路径或 glob,绝对与否由你写的路径决定。
  • permissions.<name>.filesystem.":workspace_roots".<subpath>相对工作区根的作用域授权。同一个档换个仓库用还成立,不用改路径。

如果你的权限档要在多个项目之间复用,尽量走 ":workspace_roots" 这条;只有涉及工作区之外的固定目录(比如某个共享的缓存目录)才写绝对路径。另外还有 permissions.<name>.workspace_roots.<path>,是布尔值,作用是把某个路径纳入这个档的工作区根——它决定「工作区」这个概念本身覆盖到哪儿,和上面那条按子路径授权的键不是一回事,别搞混。

glob_scan_max_depth 是个容易被忽略的数字键,官方说明是「deny-read glob 展开的最大深度」,要求 ≥1。它的存在提示了一件事:glob 形式的 deny 规则不是无限往下扫的,写一条层级很深的 glob 时,先确认深度够不够。目录层级本身也是要付出扫描代价的,这个上限就是那个代价的闸门。

顺带一个现实中的坑:官方《Windows sandbox》页面提到,若某个目录「writable by Everyone」(对所有人可写),Codex 会告警提示 Windows 权限过宽。在 Windows 上给权限档配可写根之前,值得先看一眼那个目录的 ACL——权限档管的是 Codex 这一层,操作系统那一层的松紧是另一回事。

四、网络:三层开关,不是一层

网络这块最常见的错误是「只配了 domains 就以为生效了」。实际上它是三层:

  1. network.enabled —— 这个档到底允不允许联网,总闸。
  2. network.mode —— 取值是 limitedfull边界说明:官方在权限档这一节只列了这两个取值,没有给出各自的具体释义,所以「limited 到底限制了什么」「full 是不是等于完全放开」这两个问题,本文不替它下定义,以官方文档为准。
  3. network.domains.<pattern> —— 逐个域名 allow / deny,官方说明支持精确主机与通配。

先把总闸和模式想清楚,再去列域名。这里给一条假设式的判断路径,好处是不依赖对 mode 语义的猜测:如果你写完 domains 清单后发现某个域名的放行/拦截跟预期不符,那就把 mode 当成第一嫌疑人——先在你要的那个 mode 取值下实际验一遍,而不是默认「配了 domains 就一定按 domains 走」。换句话说,modedomains 是两个必须一起交代清楚的键,别只写其中一个就当配好了。

通配的具体写法,官方在权限档这一节只写了「支持精确主机与通配」,没给样例。文档里给出通配样例的是另一个键 features.network_proxyexperimental 阶段),那里提到 *.example.com**.example.com 两种形式。这两处是不同的键,我不建议把语法直接搬过去;要用通配,以官方《Configuration Reference》上权限档那一节的说明为准,先用精确主机跑通再说。

网络这组还有一批更细的键:network.unix_sockets.<path>allow / deny)、network.proxy_urlsocks_urlnetwork.enable_socks5 / enable_socks5_udp / allow_upstream_proxynetwork.allow_local_binding(是否放开本地/内网访问),以及两个名字自带警告的:network.dangerously_allow_non_loopback_proxy(允许非回环监听地址)和 network.dangerously_allow_all_unix_sockets(允许任意 Unix socket 目标)。

dangerously_ 前缀的键,官方是把风险写进命名里了。这类开关的判断依据只有一条:你能不能说清楚打开它之后多出来的那条路通向哪儿。说不清就别开。还要提醒一句,Unix socket 这组键在不同平台上的意义不同——官方《Sandbox》页面写明 macOS 用系统内置的 Seatbelt、Linux / WSL2 需要装 bubblewrap(bwrap)、Windows 在 PowerShell 中使用原生 Windows 沙箱,是三套完全不同的实现。不要把某个平台上的经验直接搬到另一个平台。

五、写一个档出来,然后套用它

把上面的键组合成一份可读的配置(以下为按官方文档键位组合的示例,未逐项实测,以官方文档为准):

# ~/.codex/config.toml
default_permissions = "repo-dev"

[permissions.repo-dev]
description = "日常改代码:工作区内可写,敏感目录禁读,联网显式写出 mode 与 domains"
extends = ":workspace"

[permissions.repo-dev.filesystem]
glob_scan_max_depth = 4

[permissions.repo-dev.filesystem.":workspace_roots"]
"src" = "write"
"docs" = "write"
"secrets" = "deny"

[permissions.repo-dev.network]
enabled = true
mode = "limited"   # 取值只有 limited / full 两种,官方未给释义,落地前请以官方文档确认其行为

[permissions.repo-dev.network.domains]
"registry.npmjs.org" = "allow"

这份示例里唯一需要你亲自验一遍的就是 mode 那行:enableddomains 的作用官方写得明确,mode 的两个取值官方只给了枚举。所以把它显式写出来,比省略掉让它走某个你并不知道的取值要好——至少改坏了你知道该回来改哪一行。

再配一个更紧的审计档,从只读基线起步:

[permissions.audit-only]
description = "只读审计:不写盘、不出网"
extends = ":read-only"

[permissions.audit-only.network]
enabled = false

套用方式有两条:一条是上面的 default_permissions,指定「沙箱化工具的默认权限档名」;另一条是命令行按次指定。本机实测:在 codex-cli 0.147.0(Windows 11)上执行 codex sandbox --help,其中确实有 -P, --permission-profile <NAME>,官方说明是套用当前配置栈里的命名权限档。

六、三组最容易搞混的东西

第一组:-p-P 不是一回事,而且它们出自不同的 help 输出。 在 codex-cli 0.147.0(Windows 11)上,顶层 codex --help 里的 -p, --profile <CONFIG_PROFILE_V2> 的作用是把 $CODEX_HOME/<name>.config.toml 整个叠加到基础用户配置之上,换的是一整套配置;而 -P, --permission-profile <NAME> 是在 codex sandbox --help 里看到的,换的只是权限档(同一份 codex sandbox --help 里也另有一个 -p, --profile <CONFIG_PROFILE>)。大小写差一个字母,语义差得很远,敲错了不会有人提醒你;更要紧的是别把 -P 当成顶层 codex 命令的选项来敲——本机看到它的地方是 codex sandbox 这个子命令的 help。

第二组:权限档不等于沙箱模式。 sandbox_mode 那三档(read-only / workspace-write / danger-full-access)和权限档是两套键。官方对 danger-full-access 的原文是「移除文件系统与网络边界」——那是把边界整个拿掉,不是把边界画细。要画细边界,用的是权限档。

第三组:权限不等于审批。 approval_policyuntrusted / on-request / never 三档,官方对 never 的释义是「从不询问,执行失败直接回传给模型」。注意它改变的是「要不要问你」,沙箱边界还在——never 不等于放开权限,这是最常见的误读之一。另外 approval_policy 既可以写成字符串(粗粒度三档),也可以写成一张表,表里是 approval_policy.granular.sandbox_approval.rules.mcp_elicitations.request_permissions.skill_approval 五个布尔开关。想「只放行某一类弹窗」,只能用表形式,字符串做不到。

七、改完之后查哪里

配置改坏了最麻烦的是静悄悄不生效。这里有一条很实用的一手结论:在 codex-cli 0.147.0(Windows 11)上,故意用 -c 传一段语法不合法的 TOML 再跑 codex doctor --summary,命令没有崩溃退出,doctor 照常跑完,但 Notes 区会出现这一行:

✗ config       config could not be loaded - Fix the reported config error, then rerun codex doctor.

所以「权限档配了没反应」的第一步不是重读文档,是跑一次 codex doctor --summaryconfig 这行是不是绿的。另外,在 codex-cli 0.147.0(Windows 11)上跑 codex doctor --summary,Configuration 分组里 sandbox 行显示的是 restricted fs + restricted network · approval OnRequest,这一行可以用来交叉确认当前生效的大方向——文件系统受限、网络受限、审批策略是 OnRequest。

--strict-config 也可以用来抓拼写错误——它的作用是「config.toml 里出现本版本不认识的字段时直接报错退出」。但它有边界:同样在 0.147.0(Windows 11)上,故意写一个拼错的键再跑 --strict-config exec --help,help 正常打印,没有报未知字段错误。说明校验发生在真正加载配置去跑会话的路径上,--help 这类不进入会话的路径不触发。别把它当成「任何情况下都能拦住拼写错误」的护栏。

Windows 上如果沙箱本身起不来(而不是权限档写错),官方《Windows sandbox》给的排查顺序是:重启 Codex、重试 elevated 初始化、需要时回退到 unelevated、必要时发送诊断,日志在 CODEX_HOME/.sandbox/sandbox.log。顺便说清楚一件事:官方把 elevated 标为首选,unelevated 是拿不到管理员批准时的回退,官方自己写明它「保护更弱」。所以权限档写得再细,也别当成安全保证来用——它是降低误伤概率的工程手段,不是防线。

最后一句判断依据:什么时候值得花时间写权限档?当你反复在同一类审批弹窗上点确认,或者反复担心它碰到某个特定目录时。如果你的用法就是在一个干净的临时仓库里跑跑改改,sandbox_mode 那三档已经够了,没必要提前上这层复杂度。

相关阅读


本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Configuration Reference》《Sandbox》《Windows sandbox》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。