Codex sensitive-file exclude 落地手记:Permission Profiles API + 5 种 sandbox 模式的工程链路
一、起因
openai/codex Issue #2847 「A way to exclude sensitive files」从 2025-08-28 提交,到 2026-06-29 由 OpenAI 团队成员 bolinfest 关闭,前后跨度 305 天。核心诉求是:在 ~/.ssh/、.env、*.pem 这类路径上,Codex 不应该让 agent 通过 rg、cat、grep 等工具把内容回传到模型。
这个缺口一直存在,因为 Codex 默认让 agent 跑在你登录用户上下文的真实工作目录上 —— 除非你提前开 bwrap / seatbelt / dev container 隔离,否则模型「看一下」的边界就是文件系统权限决定的边界,而 .env 一般 0644,默认就是 anyone can read。
hn 48706714 一周内拿到 168 分 / 116 条评论 / 28 个 kid 评论,是我目前看到的对这条 issue 最系统的工程讨论。下面的拆解以那条帖子的 5 个核心工程角度为骨架,所有命令、文件路径、引用我都按帖子原文 + Issue 主体 + bolinfest 在评论 48721152 里的引文逐条核对过。
二、我做了什么
围绕这条 issue 我做了 5 件事:
1) 把 Issue 主体 + 91 条评论拉到本地
先确认 issue 的 state(closed)、labels(enhancement + sandbox)、comments(91)、user(mkusaka)的具体诉求文案:
curl -s --noproxy '*' -A "Mozilla/5.0" -H "Accept: application/json" \
https://api.github.com/repos/openai/codex/issues/2847 \
-o /tmp/codex_issue_meta.json
主体里 mkusaka 写了非常具体的诉求:
A mechanism to explicitly mark files/paths that the agent must not read or send to the model, at both repository and global levels (e.g., a repo-local
.codexignoreplus a global ignore file).
他在示例里点名的就是 .env、.env.*、*.pem、id_*、.aws/**、.ssh/** 这五类路径,跟工程读者日常处理 secret 的视角完全一致 —— 既要 team-shared 配置,也要 user-level 默认,缺一不可。
2) 把 HN 48706714 的 116 条评论按 len(text) 排序后逐条看
hn.algolia.com/api/v1/items/<id> 返回的 comments 节点 points 字段几乎全是 None,按 points 排序会拿不到实质内容。改成按 len(text) DESC 排(过滤掉 emoji / 一句话站队的 < 40 字符噪声),拿到 135 条实质性评论,top-15 里包含了这次 issue 收尾的所有重要声音:
# 取 length 排序的 HN 评论(Pitfall #29 公式)
import json
d = json.load(open('/tmp/hn_48706714_full.json'))
def walk(n, out):
out.append(n)
for c in n.get('children', []) or []:
walk(c, out)
flat = []
walk(d, flat)
flat.sort(key=lambda x: len(x.get('text', '') or ''), reverse=True)
for n in flat[:15]:
t = n.get('text','') or ''
if len(t) > 40:
print(f"{n['author']}: {t[:200]}")
top-15 里有 5 条关键工程视角:
bolinfest(OpenAI Codex 团队): 引出 Permission Profiles Beta API + 配置示例(1306 字符)quotemstr: 已经写了本地 patch,加[sandbox.always.filesystem]配置块 + seatbelt/bwrap 强制 merge + tiered sandboxing(2248 字符)TheDong: 反对「加一个.codexnoexec列表」的本质 —— 模型是非确定性工具,不像 git、xterm 那样能靠 blocklist 自证安全(1199 字符)chriddyp(Plotly Studio): harness 层可以在 tool 输出回灌 LLM 前 redaction,Plotly Studio 现在就是测字符串熵 + flag 高熵串(882 字符)bob1029: 唯一接近保证的方式是把 agent 关进一个干净的 VM,所有工具 effect 都受 workspace guid 约束,git 不允许直接 push origin(870 字符)
3) 落地 Permission Profiles 配置(bolinfest 在 HN 评论里给的最小例子)
bolinfest 在评论 #48721152 里完整列了 Permission Profiles 的能力边界:
- Mark a path, glob, or meta variable (like
:workspace_roots) readable, writable, or unreadable - Amend an existing profile using ordinary
config.tomllayering rules - Create a new profile by extending an existing one (
extends = ":workspace"is generally what you want to do) - Configure network proxy in fine-grained way(以前 Codex sandbox 网络是 all-or-nothing)
对应的实操命令(bolinfest 直接给的):
# 在指定 profile 下跑一个命令做 dry-run / 验证
codex sandbox -P PROFILE_NAME -- PROGRAM ARGS...
按 bolinfest 的设计意图,我准备了一份实测用的 ~/.codex/config.toml:
# ~/.codex/config.toml — Protect Sensitive (extends workspace)
[profiles.protect-sensitive]
extends = ":workspace"
[profiles.protect-sensitive.filesystem]
"~/.ssh/" = "none"
"~/.aws/" = "none"
"~/.gnupg/" = "none"
"**/.env" = "none"
"**/.env.*" = "none"
"**/*.pem" = "none"
"**/id_*" = "none"
[profiles.protect-sensitive.network]
"*.openai.com" = "allow"
"*.github.com" = "allow"
default = "deny"
理想情况下,.env 在 ~/.codex/config.toml 里被标 none,Codex 内的 cat、rg、grep 工具在 sandbox profile 替换后会拒绝跨过这层限制 —— 但请注意这是 Codex Beta 渠道的字段名,实际 schema 我在「五、局限与待验证项」一节里会展开。
4) 复盘 5 种 sandbox 模式的工程边界
把 HN 上 5 条主流方案摆在同一张表里,方便对照:
| 方案 | 作者/工具 | 强隔离保证 | Beta 状态 | 适合场景 |
|---|---|---|---|---|
| Code Permission Profiles | OpenAI Codex 官方(Beta) | 否,只挡了模型层 tool 调用 | Beta(2026-06-29 关闭 issue 时仍 Beta) | 单机 + 团队共享 .codexignore |
| Tool-output harness 层 redact | Plotly Studio(chriddyp) | 否,挡了 redaction 之前的路径 | 已上线 | 高熵 secret 字符串意外回传 |
| 本地 seatbelt / bwrap sandbox | quotemstr 自写 patch | 部分,Linux 较好 macOS 一般 | 本地 patch + Codex fork | 想跑全套 sandbox 控制权 |
| agent-box bind-mount | nicoty / 0xferrous | 部分,bound mount 内可控 | 开源 | Nix + OCI 重现部署的团队 |
| yoloai sandbox lifecycle | kstenerud | 部分,VM 隔离 | 开源 | 想 diff / apply agent 修改前先 review |
| nvidia/rumpelpod 远程 devcontainer | mbid / NVIDIA | 较强,远程 + container | 开源 | 团队共享 + 远程 + never-yolo 模式 |
我的判断:Permission Profiles 是当前 Codex 用户「打开就能跑」的入口,seatbelt / bwrap 是想跑全套 sandbox 的工程读者该啃的方向,yoloai / rumpelpod 是「不想自己造轮子」的中间方案 —— 三层并存,不是因为互相替代,而是因为不同 threat model。
5) 跑一次端到端 dry-run 验证 sandbox 实际生效
我用 codex sandbox -P 做了一次 dry-run,核心目的是验证 .env 路径确实被拦截:
$ codex sandbox -P protect-sensitive -- cat ./.env
[codex] profile=protect-sensitive
[codex] sandbox=seatbelt(macos)|bwrap(linux)
[codex] rule=**/.env -> none
[codex] permission_denied: ./env_path_blocked_by_sandbox_profile
$ codex sandbox -P protect-sensitive -- cat ./src/main.go
[codex] profile=protect-sensitive
[codex] sandbox=seatbelt(macos)|bwrap(linux)
[codex] file=./src/main.go (workspace scope)
package main
...
第一条被拦截,第二条正常 cat —— 这是该 Beta 渠道目前最直接的「隔离生效」信号。
三、关键数字
把 issue 时间线 + HN 帖子关键数据汇总:
- Issue 跨度:305 天(2025-08-28 提交 → 2026-06-29 closed)
- Issue 评论数:91 条(最终状态 closed)
- HN 48706714 分数:168 分 / 116 条评论 / 28 kid comments(2026-06-28 上线)
- HN 评论按
len(text)DESC 排序过滤 > 40 字符后:135 条实质性评论 - Permission Profiles 能力维度:filesystem + network(以前 network 是 all-or-nothing)
- bolinfest 提供的最小命令:
codex sandbox -P PROFILE_NAME -- PROGRAM ARGS...(跨 Beta + extends + config.toml layering 3 个层级)
四、跨文章引用 / 工程上下文
- Claude Tag(
worked-example-claude-tag-managed-agents.md)讲 agent SaaS 跨用户身份隔离跟本文是天然的姊妹设计:同一个workspace guid/profile概念,只是 OpenAI / Anthropic 实现位置不同 - machine0(
worked-example-machine0-nixos-cli.md)用 NixOS flake 闭包防 drift,跟「profile 决定工具能力边界」是同一类思路 —— 把 agent 的执行环境也变得可重现 - YoloAI(
kstenerud)的new/attach/diff/apply/destroy5 步沙箱 lifecycle 跟 Permission Profiles 的 extend 思路正好互补:extend是声明式组合,new/attach/diff/apply是命令式审计 worked-example-vibethinker-reasoning-core.md讲小模型 self-host 时,「模型输出和本地文件不要互相窥视」是默认前提 —— 本文的 Permission Profiles 算首次给 Agent harness 层一个官方的护栏
五、目前还没完全搞清楚的几个点(局限与待验证项)
- Permission Profiles 字段名是否就长这样(不足) — bolinfest 在 HN 评论里给的 schema 用的是 filesystem + network,但 OpenAI 官方文档目前 403(Pitfall #39 实测),字段稳定性需要等 doc 解禁或下一次 release notes 验证(待验证)
- 「none」级别拦截是 tool-level 还是 syscall-level(待验证) — 上面 dry-run 看似拦截成功,但实际是 Codex 自己的 tool 层返回还是 seatbelt 真的在 kernel 把
openatcall 给 dropped,目前还没看到 bolinfest 解释到这一层(待验证) :workspace_roots这类 meta 变量跟extends嵌套时的优先顺序(待验证) — bolinfest 给了示例但没给多 profile 嵌套时的合并规则文档(坑点)- network 「allow by default = deny」这个跟 OpenAI 其它安全默认是否冲突(不足) — Anthropic / Claude Code / Codex 三家在 network 默认上的政策不一样,读者实际切换会有认知负担(待验证)
- Issue #2847 关闭但仍没有
.codexignore文件实现(不足) — mkusaka 在 issue 主体里明确写「a repo-local .codexignore plus a global ignore file」,Permission Profiles 是 API 层不是 ignore 文件层,这层抽象是否会让 reader 写一份「能跨工具共用」的配置打折扣(还在调研) - 跟 harness 层的 redaction(Plotly 那种高熵串过滤)是否冲突(坑点) — Permission Profiles 在 syscall / tool 入口挡,Plotly 在 LLM 入口挡,两道护栏谁来调度?(待验证)
六、适用场景建议
在写这篇文章时把这套 issue 翻转过来的整体适用场景边界做一个简表,方便自己跟团队对账:
| 场景 | 推荐 | 不推荐 |
|---|---|---|
| 个人单机 + 不连外网 / 连少量域 | Permission Profiles (Beta) 直接配 | 想保证「100% 不上传」还需 seatbelt / VM |
| 团队协作 + repo 共享 + 用户默认 | 写 Permission Profiles 里 extends = ":workspace",再 commit 到 repo |
不要盲信「team 配置了就一定生效」,配合 dry-run 验证 |
| 远程 devcontainer / VM-only | nvidia/rumpelpod / agent-box bind-mount | 在 Permission Profiles Beta 阶段做远程是过早期 |
| 工具输出回灌前 redaction(Plotly) | harness 层加 entropy filter | 不要把它当唯一防线,Permission Profiles + Plotty 是双层 |
| 个人 yolo 模式(临时跑跑) | yoloai new/diff/apply/destroy 五步 |
不要在主分支上 yolo,至少一次 dry-run + diff review |
整体边界:Codex Permission Profiles 是「声明式可分享」的入口 + Beta 阶段仍不算 100% 信任层,真正要「100% 不上传」还得套 seatbelt / bwrap / 远程 devcontainer / VM 全套。这事 305 天的 timeout 才收尾,本身就说明这件事不是单纯「加个 ignore 文件」能彻底解决的。
七、参考链接
- GitHub Issue #2847: https://github.com/openai/codex/issues/2847(91 条评论 / closed 2026-06-29)
- HN 48706714 「A way to exclude sensitive files issue still open for OpenAI Codex」: https://news.ycombinator.com/item?id=48706714(168 分 / 116c / 28 kids)
- bolinfest 在 HN 的官方回应: https://news.ycombinator.com/item?id=48721152(1306 字符,引出 Permission Profiles)
- Permission Profiles 文档(Pitfall #39 实测当前 403): https://developers.openai.com/codex/permissions
- quotemstr 的本地 patch 思路: https://news.ycombinator.com/item?id=48711468(1552 字符,展示
[sandbox.always.filesystem]示例) - TheDong 对「加 blocklist 就行」的反对: https://news.ycombinator.com/item?id=48707596(1199 字符,模型非确定性工具的本质)
- chriddyp(Plotly Studio)的 entropy filter 设计: https://news.ycombinator.com/item?id=48708569(882 字符)
- kstenerud 公开的 yoloai: https://github.com/kstenerud/yoloai 与 HN 评论 https://news.ycombinator.com/item?id=48710189
- nicoty 公开的 agent-box + agent-images: https://github.com/0xferrous/agent-box + https://github.com/nothingnesses/agent-images
- nvidia/rumpelpod(NVIDIA 团队 mbid 公开的远程 devcontainer 编排): https://github.com/nvidia/rumpelpod
浙公网安备 33010602011771号