Yoda
参考Agent 设计指南控制与安全

信任模型

首用信任:CC 用全局 config 里的 hasTrustDialogAccepted 按目录树记账,Codex 用 config.toml 的 projects.trust_level + hook 内容哈希——Codex 把"信任什么"拆得更细

信任模型

结论

两家都遵循同一原则:仓库内容是不可信输入,必须由用户显式授信后才能影响 agent 行为。CC 的实现是单比特目录信任:首次进入目录弹 TrustDialog,接受后在 ~/.claude.jsonprojects[path].hasTrustDialogAccepted 记账,查验时向上遍历父目录;信任 gate 的对象包括所有 hooks、项目 settings 中的权限规则、项目 skills 的 allowed-tools、MCP headersHelper 等"项目可携带的可执行配置" [一手源码+一手文档]。Codex 拆成两层:目录级 projects."<path>".trust_level = trusted/untrusted 写在用户 config.toml,未授信时项目层 config/hooks/execpolicy 加载但禁用;hook 级再加一道内容哈希信任——每个 hook 的规范化身份哈希须与用户态 trusted_hash 匹配才执行,仓库改了 hook 命令即自动回到 untrusted [一手源码]。此外 Codex 的信任直接改变默认权限档:trusted → workspace-write,未决/untrusted → read-only。harness 应代理而非绕过这套首用信任流程。

研究问题

  • 信任记录存在哪里?按什么粒度(目录/git root/repo)查找?
  • 信任到底 gate 了哪些能力?
  • Codex 的 hook 内容哈希如何防"已授信项目后续篡改 hook"?
  • 信任状态如何影响默认权限/沙箱档位?

各 Agent 设计与实现

Claude Code

对话框与存储。首次在新目录启动时渲染 TrustDialog:"Quick safety check: Is this a project you created or one you trust? … Claude Code'll be able to read, edit, and execute files here.",选项只有 Yes, I trust this folder(value enable_all)和 No, exitcomponents/TrustDialog/TrustDialog.tsx:208-232)[一手源码]。接受后写入全局配置(~/.claude.json)的 projects[<path>].hasTrustDialogAccepted: trueutils/config.ts:111,144)。

查验逻辑computeTrustDialogAccepted 按序检查:① 会话级内存信任(home 目录场景不落盘,仅本会话有效)→ ② 配置写入点(git root 或原始 cwd)→ ③ 从 cwd 逐级向上遍历父目录,任一祖先已授信即视为可信(utils/config.ts:705-743)[一手源码]。isPathTrusted(dir) 提供任意目录版本(:752-763)。含义:信任父目录等于信任全部子目录,无反向撤销粒度。

信任 gate 什么。源码侧最硬的一条:交互模式下 所有 hooks 都要求已过信任对话框——// In interactive mode, ALL hooks require trust; const hasTrust = checkHasTrustDialogAccepted()utils/hooks.ts:293-295)[一手源码]。文档侧补充的 gate 清单 [一手文档]:

  • 项目 .claude/settings.json / settings.local.json 中的 CLAUDE_CODE_* 路径类设置(memory.md:356)
  • 项目 skills 目录的 plugin 化加载与 allowed-tools 生效(skills.md:113, :353;plugins-reference.md:375)
  • MCP headersHelper(执行任意 shell)在 project/local scope 仅信任后运行(mcp.md:697)
  • /goal 评估器(hooks 体系一部分)仅在已授信 workspace 可用(goal.md:135)

历史 bug 也佐证 gate 范围:changelog 记录过"trust dialog 静默启用全部 .mcp.json servers"与"home 目录信任未启用 hooks"两次修复(changelog.md:2229, :2946)[一手文档]。

与权限档的关系。CC 的信任不改变 permission mode;它控制的是"项目自带配置是否参与"。权限收口仍靠规则与模式(见 permissions 章)。

Codex CLI

目录信任。配置形态(core/src/config/mod.rs:1888-1892 注释给出目标渲染)[一手源码]:

[projects."/path/to/project"]
trust_level = "trusted"   # 或 "untrusted"

TrustLevelTrusted | Untrusted 两值(protocol/src/config_types.rs:544-548);ProjectConfig.is_trusted() 判定(config/src/config_toml.rs:546-560)。TUI 首启 onboarding 渲染 trust 界面,文案明确说明授信效果:"Trusting the directory allows project-local config, hooks, and exec policies to load.",且在 git 子目录时警告"信任将应用到仓库根"(tui/src/onboarding/trust_directory.rs:53-79)[一手源码]。接受后经 ConfigEdit::SetProjectTrustLevel 写回用户 config.toml(core/src/config/edit.rs:867-877)。

查验粒度ProjectTrustDecision::decision_for_dir 依次查:目录自身的归一化 key → project root(按 root marker 探测)→ git repo root(resolve_root_git_project_for_trust)(config/src/loader/mod.rs:838-882)[一手源码]。

未授信的后果:加载但禁用。config loader 注释直接写明:cwd / 目录树 / repo 三类项目层 config "loaded but disabled when the directory is untrusted"(loader/mod.rs:100-102);disabled_reason_for_decision 生成的提示语限定了被禁面:"To load project-local config, hooks, and exec policies, add <key> as a trusted project in <user config>"(loader/mod.rs:884-900)[一手源码]。另外无论是否授信,项目层 config 永远不允许设置敏感键(openai_base_urlmodel_providersnotifyotel 等),见 PROJECT_LOCAL_CONFIG_DENYLIST(loader/mod.rs:61-72)[一手源码]——这是"信任也有上限"的设计。

信任 → 默认权限档default_builtin_permission_profile_name:项目有明确 trust 标记(trusted 或 untrusted)且非 Windows-sandbox-disabled 时默认 workspace profile,否则(未决目录)read-onlycore/src/config/permissions.rs:48-60)[一手源码]。即信任决策直接参与沙箱/审批的默认值推导,这是 CC 没有的耦合。

hook 内容哈希信任。每个 command hook 计算"规范化身份哈希":事件名 + matcher + 归一化 handler 配置序列化为 TOML 后哈希,确保 config.toml 与 hooks.json 写法等价的 hook 收敛到同一身份(hooks/src/engine/discovery.rs:556-580 command_hook_hashNormalizedHookIdentity 注释)[一手源码]。信任状态机(discovery.rs:582-596):

fn hook_trust_status(is_managed, current_hash, trusted_hash) -> HookTrustStatus {
    if is_managed { Managed }                 // managed 层 hook 免哈希信任
    else { match trusted_hash {
        Some(h) if h == current_hash => Trusted,
        Some(_) => Modified,                  // 内容变了 → 重新失信
        None => Untrusted,
    }}
}

Managed | Trusted 状态(或显式 bypass_hook_trust)的 hook 进入执行表(discovery.rs:520-538);用户的授信记录是 [hooks.state.<key>] trusted_hash = "..."config/src/hook_config.rs:25-30)[一手源码]。效果:即使项目已 trusted,仓库后续偷改 hook 命令也会因哈希失配回到 Modified 状态而被拒。

差异矩阵

维度Claude CodeCodex CLI
存储位置~/.claude.json projects[path].hasTrustDialogAccepted(布尔)用户 config.toml [projects."<path>"] trust_level(trusted/untrusted 三态:含未决)
查找粒度cwd 向上遍历所有祖先目录目录 key → project root marker → git repo root
拒绝信任的后果退出进程(No, exit)可继续用,但项目层 config/hooks/execpolicy 禁用、默认 read-only
gate 对象hooks(全部)、项目 settings 的可执行配置、项目 skills、headersHelper、.mcp.json项目层 config.toml、hooks、exec policy rules
信任的上限—(信任后项目 settings 全量生效)PROJECT_LOCAL_CONFIG_DENYLIST:项目层永远不能改 base_url/providers/notify/otel
二级信任无(目录级单比特)hook 内容哈希(trusted_hash),改内容自动失信
与权限档耦合不直接影响 permission mode直接决定默认 profile:trusted→workspace-write,未决→read-only
home 目录特例信任只存内存、不落盘(config.ts:706-711)未见特例 [推断:无对应代码]

最小复现

# Codex:手动标记 untrusted,观察项目层配置被禁用
cat >> ~/.codex/config.toml <<'EOF'
[projects."/tmp/demo"]
trust_level = "untrusted"
EOF
cd /tmp/demo && codex
# 预期 startup warning:"/tmp/demo is marked as untrusted in ~/.codex/config.toml.
#   To load project-local config, hooks, and exec policies, mark it trusted."
# [一手源码 loader/mod.rs:893-896 的格式化字符串]

# CC:删除项目信任记录后重启,应重新弹 TrustDialog
jq 'del(.projects["/tmp/demo"].hasTrustDialogAccepted)' ~/.claude.json

(本机未实测;预期输出直接取自源码中的字符串模板。)

Harness 接入建议(Yoda 实践)

  • 代理而非跳过首用信任。Yoda 以 PTY/终端方式驱动 CLI(runtime-registry 的 terminalOnly: true),首启时 TrustDialog / trust_directory 屏会出现在终端流里。正确做法是把它识别为结构化事件并渲染成 Yoda 自己的信任卡片(带目录路径与 gate 说明),而不是自动回车——自动接受等于替用户签了"项目配置可执行任意命令"。
  • workspace 导入即询问。Yoda 作为 workspace 产品在加入新项目时就知道目录路径,可以提前问一次"信任此项目?",然后分别落到两家的原生存储:CC 写 ~/.claude.json(或让对话框自然通过),Codex 用 codex 配置编辑(ConfigEdit::SetProjectTrustLevel 对应的 CLI/app-server 接口)。保持单一事实源在 CLI 侧,Yoda 不另存信任表。
  • 显示信任状态与后果。会话头部应显示当前项目 trusted/untrusted 与其含义(Codex:untrusted = read-only + 项目 hooks 不生效;CC:未信任 = 直接没法进入会话)。Codex 的 Modified hook 状态尤其值得 UI 化——它意味着"仓库有人改了 hook"。
  • 不要用 bypass_hook_trust:该开关存在于 hooks registry(registry.rs:33),是测试/受控环境用的,产品默认路径不应触碰。

失效条件

  • CC TrustDialog 的选项与文案来自 2026-03-31 快照,线上版本已知在迭代(changelog 多次修改 trust 流程);enable_all 这种取值属内部实现
  • Codex hook 信任 TODO 注明 key 是"positional suffix"将换成 durable hook id(discovery.rs:495-496),届时 hooks.state 的 key 格式变化
  • Codex projects 信任与 profile 推导的 Windows 分支依赖 WindowsSandboxLevel,Windows 沙箱转正后行为可能变
  • CC 信任 gate 的能力清单分散在多篇文档(skills/mcp/memory/goal),新能力加入时本章清单需要回归

参考资料

  • CC 源码(重建快照 2026-03-31):utils/config.ts(computeTrustDialogAccepted/isPathTrusted)、utils/hooks.ts:293-295components/TrustDialog/TrustDialog.tsx
  • CC 文档:docs/skills.mddocs/mcp.mddocs/memory.mddocs/goal.mddocs/changelog.md
  • Codex 源码(b89ce9a):config/src/loader/mod.rs(trust context / disabled reason / denylist)、config/src/config_toml.rscore/src/config/permissions.rscore/src/config/edit.rshooks/src/engine/discovery.rsconfig/src/hook_config.rstui/src/onboarding/trust_directory.rs

On this page