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

企业托管配置

CC 用「managed-settings.json/MDM/server-managed 单源最高层 + managed-only 键」,Codex 用「requirements.toml 约束集 + 多层 config 合成」——CC 下发"值",Codex 下发"允许的取值范围"

企业托管配置

结论

两家的企业控制面在哲学上分叉。CC 的 managed settings 是最高优先级的一层普通 settings:与 settings.json 同 schema,经 MDM(plist/注册表)、系统目录文件(/Library/Application Support/ClaudeCode/ 等)或 Anthropic 服务器(server-managed)下发,任何其他层(含 CLI 参数)不可覆盖;另配一组 managed-only 键(allowManagedHooksOnlyallowManagedPermissionRulesOnly 等)实现"只听管理员"模式 [一手文档+一手源码]。Codex 把管理面拆成两个文件语义:config.toml(managed 层会被更高层 session flags 覆盖部分字段)与 requirements.toml——后者不是配置值而是约束集allowed_approval_policiesallowed_sandbox_modes 白名单),所有层合成出的最终配置必须落在约束内,否则直接报 ConstraintError [一手源码]。CC 的模式简单可审计,Codex 的模式保留了用户在范围内自由调档的空间。harness 永远不能帮用户绕 managed 层,且应把"哪条策略来自管理员"显式标注。

研究问题

  • 托管配置的下发通道与文件路径(各 OS)?
  • 优先级裁决:同时存在多个管理源时谁赢?
  • 管理员能强制什么?哪些键只在 managed 层生效?
  • 客户端篡改/断网时的失效模式?

各 Agent 设计与实现

Claude Code

五层设置源。源码定义 SETTING_SOURCES = [userSettings, projectSettings, localSettings, flagSettings, policySettings],注释明确"后面的覆盖前面的",policySettings 即 managed(utils/settings/constants.ts:7-22)[一手源码]。文档侧的精确优先级:managed > CLI 参数 > local > project > user(docs/permissions.md:354-358)。--setting-sources 只能裁剪 user/project/local,policy 与 flag 永远强制加载(constants.ts:159-167 getEnabledSettingSources 无条件 add)[一手源码]。

下发通道与路径。文件路径由 getManagedFilePath 决定:macOS /Library/Application Support/ClaudeCode、Windows C:\Program Files\ClaudeCode、其他(Linux/WSL)/etc/claude-codeutils/settings/managedPath.ts:8-25)[一手源码]。支持 systemd 风格 drop-in:managed-settings.json 为底,managed-settings.d/*.json 按字母序覆盖合并(managedPath.ts:27-34;docs/settings.md:105-107)。OS 策略通道:macOS com.anthropic.claudecode managed preferences、Windows HKLM\SOFTWARE\Policies\ClaudeCode(settings.md:92-93)[一手文档]。

管理源内部优先级:单源不合并。server-managed > MDM/OS 策略 > 文件(managed-settings.d + base)> HKCU;"只用一个管理源,跨源不合并"——server 端只要交付了任意键,端点管理文件整体被忽略(docs/settings.md:530-534; server-managed-settings.md:131-137)[一手文档]。

server-managed 的失效模式(docs/server-managed-settings.md)[一手文档]:启动异步拉取 + 每小时轮询(:141);首启无缓存且拉取失败→不带策略继续跑,存在短暂未受控窗口(:143-147);forceRemoteSettingsRefresh: true 改为 fail-closed——拉不到就退出(:157-171)。明确的绕过面:用户切 Bedrock/Vertex/自定义 ANTHROPIC_BASE_URL 即完全绕开 server-managed(:214);篡改缓存文件可生效至下次成功拉取(:210)。含 shell 命令/hook/自定义 env 的托管配置在交互模式下要求用户过一次安全确认对话框,拒绝则退出;-p 非交互模式跳过对话框直接应用(:175-187)。

managed-only 键。只在 managed 层读取的开关包括:allowManagedHooksOnly(仅 managed/SDK/强制插件 hooks 生效)、allowManagedPermissionRulesOnly(user/project 不得定义 allow/ask/deny)、allowManagedMcpServersOnlysandbox.network.allowManagedDomainsOnlysandbox.filesystem.allowManagedReadPathsOnlystrictPluginOnlyCustomizationforceRemoteSettingsRefreshpluginTrustMessage 等(docs/permissions.md:325-342 完整表)[一手文档]。policyHelper(管理员部署的可执行文件,启动时动态计算策略)只从 MDM 或系统 managed-settings.json 读取,其他 scope 一律忽略(settings.md:236, :502)。disableBypassPermissionsMode 则任何 scope 都生效,只是放 managed 层才不可解除(permissions.md:344)。

Codex CLI

双文件模型。loader 注释完整给出两套层栈(config/src/loader/mod.rs:79-108)[一手源码]:

  • requirements(约束)层,升序优先:system /etc/codex/requirements.toml(Windows %ProgramData%\OpenAI\Codex\requirements.toml)→ cloud(enterprise-managed cloud config bundle)→ legacy managed_config.toml 重释为 requirements → admin(macOS managed preferences,base64 编码的 requirements_toml_base64 键,loader/macos.rs:22
  • config(取值)层:admin MDM → system /etc/codex/config.toml → cloud fragments → user $CODEX_HOME/config.toml → profile → cwd/tree/repo 项目层(untrusted 时禁用)→ runtime(CLI -c flags)→ 最顶再叠 legacy managed_config.toml/etc/codex/managed_config.tomlloader/layer_io.rs:19;MDM 版同理)

层来源枚举见 ConfigLayerSource:Mdm / System / EnterpriseManaged / User / Project / SessionFlags / LegacyManagedConfigToml{FromFile,FromMdm}(config/src/config_layer_source.rs:1-31)[一手源码]。

管理面还配有严格模式与可观测性:strict_config 开启时 managed config 的 base64 内容会被严格校验、解析错误直接失败(loader/macos.rs:162, :184);启动时可通过内部 override ignore_managed_requirements 跳过管理约束(仅测试路径,loader/mod.rs:133-137)[一手源码]。

requirements = 约束而非值ConfigRequirementsToml 的字段是白名单与策略开关(config/src/config_requirements.rs:820-845)[一手源码]:

# /etc/codex/requirements.toml(字段名即 TOML 键)
allowed_approval_policies = ["untrusted", "on-request"]
allowed_sandbox_modes     = ["read-only", "workspace-write"]
allow_managed_hooks_only  = true
# 另有 allowed_permission_profiles / mcp_servers / plugins / rules(execpolicy)
# / enforce_residency / experimental_network / computer_use ...

运行时载体是 Constrained<T>:值 + 校验器,违反约束抛 ConstraintError::InvalidValue,错误信息带上 requirement_sourceconfig/src/constraint.rs:7-58)[一手源码]。ConfigRequirements 把 approval_policy、permission_profile、web_search_mode 等都包成 ConstrainedWithSource(config_requirements.rs:144-164)。多层 requirements 用 merge_unset_fields 合成——先到者占位,后层只能补未设置的字段(config_requirements.rs:900-915)[一手源码]。

allow_managed_hooks_only。Codex 本仓文档唯一实质条目:管理员在 requirements.toml 顶层设 allow_managed_hooks_only = true 可忽略 user/project/session hooks,仅保留 managed 层 hooks;放进 config.toml 不生效(codex/docs/config.md:11-15)[一手文档]。对应运行时行为:managed hook 在信任状态机里恒为 Managed(免哈希信任,hooks/src/engine/discovery.rs:582-588),requirements 注入的 managed hooks 经 append_managed_requirement_handlers(discovery.rs:169)。

MDM(macOS)。managed preferences 经 CFPreferences/profiles 下发,requirements 与 config 都以 base64 TOML 字符串键传入并解析(loader/macos.rs:40-75, :140-190)[一手源码]。cloud bundle(企业云端下发)由 CloudConfigBundleLayers 拆成 enterprise_managed_config 与 enterprise_managed_requirements 两组层(config/src/cloud_config_bundle.rs:27-135)[一手源码]——与 CC server-managed 同位,但与本地层是合并关系而非单源互斥 [推断:基于 merge 代码路径,未见互斥逻辑]。

项目层的结构性降权。即便没有任何企业策略,Codex 的项目层 config 也被结构性限权:PROJECT_LOCAL_CONFIG_DENYLIST 禁止仓库改写 openai_base_url / model_providers / notify / profile / otel 等"决定凭据流向与本地执行"的键(loader/mod.rs:58-72)[一手源码]——管理员不需要专门下发规则来防"仓库自带恶意 base_url"。CC 的对应防线在信任模型(项目 settings 须过 trust dialog)与 managed-only 键,而非键级 denylist。

差异矩阵

维度Claude CodeCodex CLI
管理面语义下发"配置值"(最高优先层,同 schema)下发"取值约束"(requirements)+ 可选"配置值"(system/managed config)
系统文件路径macOS /Library/Application Support/ClaudeCode/managed-settings.json;Linux /etc/claude-code/;Win C:\Program Files\ClaudeCode\Unix /etc/codex/requirements.toml + /etc/codex/config.toml;Win %ProgramData%\OpenAI\Codex\;legacy managed_config.toml
OS 策略通道macOS plist com.anthropic.claudecode;Win HKLM 注册表macOS managed preferences(base64 TOML);[推断] Win 侧未见注册表通道
云端下发server-managed settings(claude.ai admin console,单源互斥优先)enterprise-managed cloud config bundle(与其他层合并)
多管理源裁决单源 wins:server > MDM > 文件 > HKCU,不跨源合并requirements 各层 merge_unset_fields(先占位先赢);config 层栈叠加
"只听管理员"开关allowManagedHooksOnly / allowManagedPermissionRulesOnly / allowManagedMcpServersOnly / allowManagedDomainsOnly / allowManagedReadPathsOnlyallow_managed_hooks_only / managed_allowed_domains_only / allowed_* 白名单天然排他
动态策略policyHelper 可执行(仅 MDM/系统文件可配)[推断] 无直接对应物;cloud bundle 承担动态性
fail-closedforceRemoteSettingsRefresh: true → 拉取失败即退出约束违反 → ConstraintError 启动报错;ignore_managed_requirements 仅内部 override(loader/mod.rs:133)
用户可感知性/status 显示管理源通道(remote/plist/HKLM/file);安全确认对话框层来源格式化输出(config_layer_source.rs),错误信息带 requirement source

最小复现

# CC:放一条 managed deny,验证 CLI 参数无法覆盖
sudo tee /etc/claude-code/managed-settings.json <<'EOF'
{ "permissions": { "deny": ["Bash(curl *)"] } }
EOF
claude -p 'run: curl https://example.com' --allowedTools 'Bash(curl *)'
# 预期:仍被拒绝(managed deny > CLI args)[一手文档 permissions.md:354-360]

# Codex:requirements 约束 approval policy,越界值应启动报错
sudo tee /etc/codex/requirements.toml <<'EOF'
allowed_approval_policies = ["untrusted"]
EOF
codex -c approval_policy=never
# 预期:ConstraintError:"invalid value for `approval_policy`: `never` is not in
#   the allowed set ... (set by system requirements)" [一手源码 constraint.rs:9-16]

(本机未实测;预期输出取自源码错误模板与文档优先级表。)

Harness 接入建议(Yoda 实践)

  • 铁律:永不绕过 managed 策略。Yoda 注入的任何 flag(--dangerously-skip-permissions-c sandbox_mode=...)都在 managed/requirements 之下:CC 的 managed deny 会压过 CLI 参数,Codex 的越界 -c 会直接启动失败。Yoda 应在启动前探测(读 /etc/claude-code/managed-settings.json 是否存在、codex 启动是否报 ConstraintError),把不可用的开关在 UI 上置灰并标注"由组织策略锁定"。
  • 暴露"策略来源"维度。两家都给了来源元数据(CC /status 的 Setting sources、Codex 的 ConfigLayerSource 格式化)。Yoda 的设置面板应把 effective config 按层展示:用户改的 vs 项目带的 vs 管理员锁的,避免"为什么我设置了没生效"类工单。
  • 企业部署 Yoda 本体时复用同一通道:Yoda 是 Electron 应用,自身的组织策略(哪些 runtime 允许、autoApprove 是否可用)应学 CC 的样式走 MDM/系统目录 JSON,而不是发明应用内远程开关——并把 Yoda 策略与 CLI 策略做一致性检查(例如 Yoda 允许 autoApprove 但 CC managed 设了 disableBypassPermissionsMode,应提示冲突而不是失败在运行时)。
  • 注意非交互模式的差异:CC server-managed 的安全确认对话框在 -p 下被跳过(server-managed-settings.md:186),Yoda 用 headless 方式跑 CC 时等于替用户接受了 managed hooks——嵌入前应先在交互终端里完成一次确认流程。

失效条件

  • Codex managed_config.toml 已是 legacy(loader/mod.rs:104-107 "best-effort 兼容"),删除该路径即本章 config 层栈描述过期
  • Codex cloud config bundle / enterprise-managed 仍在快速迭代(cloud-config crate),"与本地层合并而非互斥"的推断需在新版本回归
  • CC 的 managed-only 键列表(permissions.md:325-342)随版本增删频繁;policyHelper 是 2.1.136 新键,行为可能调整
  • CC Windows 路径已从 C:\ProgramData\ClaudeCode 迁到 C:\Program Files\ClaudeCode(v2.1.75,settings.md:102),后续可能再变

参考资料

  • CC 文档:docs/settings.md(settings files / precedence / policyHelper)、docs/permissions.md(managed-only 表)、docs/server-managed-settings.mddocs/admin-setup.md
  • CC 源码(重建快照 2026-03-31):utils/settings/constants.tsutils/settings/managedPath.tsutils/settings/changeDetector.ts
  • Codex 源码(b89ce9a):config/src/loader/mod.rs(层栈全注释)、loader/layer_io.rsloader/macos.rsconfig/src/config_requirements.rsconfig/src/constraint.rsconfig/src/cloud_config_bundle.rsconfig/src/config_layer_source.rs
  • Codex 文档:codex/docs/config.md(allow_managed_hooks_only);外链 https://developers.openai.com/codex/security

On this page