配置系统
codex 的配置 (config) 不是单文件加载,而是多层叠加:config.toml 来自 system / user / project / cloud bundle / session flags 多个来源,按 precedence 合并成 effective config,再叠加 ConfigRequirements 做约束校验。config crate 负责层叠与合并,codex-home 提供 CODEX_HOME 解析与全局 AGENTS.md 加载,core/src/config/mod.rs 把合并结果装进 Config 供 Agent 主循环使用,core/src/config_lock.rs 用 lockfile 保证可复现执行。cli/src/login.rs 写 auth.json,登录态也是配置层的一部分。
职责
- 定位 codex home:
find_codex_home读CODEX_HOME,未设置则回退~/.codex(codex-rs/core/src/config/mod.rs:4440-4442)。 - 多层加载:
load_config_layers_state顺序装 system / managed / cloud bundle / user / project / session flags 各层,产出ConfigLayerStack(codex-rs/config/src/loader/mod.rs:116-168)。 - 合并产出 effective config:
ConfigLayerStack::effective_config按 precedence 从低到高 merge TOML(codex-rs/config/src/state.rs:483-492)。 - 装配运行时
Config:ConfigBuilder把 codex_home、cli_overrides、harness_overrides、cloud bundle 拼到一起build().await(codex-rs/core/src/config/mod.rs:1258-1297)。 - 写入登录态:
login_with_chatgpt/run_login_with_api_key跑完登录流程,把凭证落到auth.json(codex-rs/cli/src/login.rs:137-165)。
设计动机
单文件 config.toml 简单,但企业部署要求多来源叠加:MDM 推下来的强制策略不能被用户改、项目 .codex/config.toml 要覆盖用户全局、CLI flag 又要压过项目配置。codex 把每层抽象成 ConfigLayerEntry,用 ConfigLayerSource::precedence 返回数字决定合并顺序——Session flags(30)压过 Project(25)压过 User(20)压过 System(10)压过 MDM(0)。新增来源只需加 enum variant + 返回数字,不用改合并逻辑。cloud bundle 是后加的层,从 ChatGPT 后端拉 enterprise-managed config 和 requirements,在 load_config_layers_state 里通过 CloudConfigBundleLayers::from_bundle 转成普通 layer 塞进 stack,跟本地 TOML 走同一条合并路径。config_lock.rs 为可复现性加:把合并出来的 ConfigToml + codex version 序列化成 ConfigLockfileToml,重放时 validate_config_lock_replay 对比 expected vs actual,版本或字段不一致就报错,避免同一 prompt 在不同机器上跑出不同结果。
关键文件
codex-rs/config/src/config_layer_source.rs:6-49 — ConfigLayerSource enum 与 precedence(),定义 8 种来源与合并顺序。codex-rs/config/src/loader/mod.rs:116-168 — load_config_layers_state,把 system / managed / cloud bundle / user / project 各层按序装入 stack。codex-rs/config/src/state.rs:483-492 — effective_config,按 precedence 从低到高 merge_toml_values。codex-rs/core/src/config/mod.rs:614-673 — Config 结构,运行时拿到的"最终配置"对象,含 model_provider、permissions、enforce_residency 等。codex-rs/core/src/config/mod.rs:1258-1297 — ConfigBuilder,装配 codex_home、cli_overrides、harness_overrides、cloud bundle 后 build().await。codex-rs/core/src/config/mod.rs:1721-1728 — Config::load_with_cli_overrides,最常用入口。codex-rs/core/src/config_lock.rs:38-74 — config_lockfile 与 validate_config_lock_replay,锁文件生成与重放校验。codex-rs/codex-home/src/instructions/mod.rs:14-68 — CodexHomeUserInstructionsProvider,从 ~/.codex/AGENTS.md 加载全局用户指令。ConfigLayerSource 把 8 种来源压成 enum,precedence 数字决定合并顺序:
// config/src/config_layer_source.rs:6-48 — 来源 + 优先级
pub enum ConfigLayerSource {
Mdm { domain: String, key: String }, // 0
System { file: AbsolutePathBuf }, // 10
EnterpriseManaged { id: String, name: String }, // 15
User { file: AbsolutePathBuf, profile: Option<String> }, // 20 或 21
Project { dot_codex_folder: AbsolutePathBuf }, // 25
SessionFlags, // 30
LegacyManagedConfigTomlFromFile { .. }, // 40
LegacyManagedConfigTomlFromMdm, // 50
}
impl ConfigLayerSource {
pub fn precedence(&self) -> i16 {
match self {
ConfigLayerSource::Mdm { .. } => 0,
ConfigLayerSource::System { .. } => 10,
ConfigLayerSource::SessionFlags => 30,
// ...
}
}
}effective_config 按 precedence 从低到高逐层 merge,后写的覆盖先写的:
// config/src/state.rs:483-492 — 合并层叠
pub fn effective_config(&self) -> TomlValue {
let mut merged = TomlValue::Table(toml::map::Map::new());
for layer in self.get_layers(
ConfigLayerStackOrdering::LowestPrecedenceFirst,
/*include_disabled*/ false,
) {
merge_toml_values(&mut merged, &layer.config);
}
merged
}config_lock 把当时合并结果锁住,下次重放时对比 TOML 字段差异:
// core/src/config_lock.rs:46-74 — 锁文件重放校验
pub(crate) fn validate_config_lock_replay(
expected_lock: &ConfigLockfileToml,
actual_lock: &ConfigLockfileToml,
options: ConfigLockReplayOptions,
) -> io::Result<()> {
validate_config_lock_metadata_shape(expected_lock)?;
validate_config_lock_metadata_shape(actual_lock)?;
if !options.allow_codex_version_mismatch
&& expected_lock.codex_version != actual_lock.codex_version
{
return Err(config_lock_error(format!(
"config lock Codex version mismatch: lock was generated by {}, current version is {}; ...",
expected_lock.codex_version, actual_lock.codex_version
)));
}
// ...继续 TOML 字段对比,不一致时用 similar 算 diff 报错
}数据流
边界与失败
- CODEX_HOME 必须是目录:环境变量设置但不存在或非目录会直接报错,不会静默回退到
~/.codex(codex-rs/core/src/config/mod.rs:4436-4442)。 - 无效配置不致命:
Config::load_default_with_cli_overrides在用户配置文件解析失败时回退到默认 + cli overrides,保证 CLI 还能起来(codex-rs/core/src/config/mod.rs:1731-1740)。 - config_lock 版本不匹配默认报错:codex version 不一致直接拒绝重放,需显式设
debug.config_lockfile.allow_codex_version_mismatch=true才放行(codex-rs/core/src/config_lock.rs:54-61)。 - AGENTS.md 优先级:同目录下
AGENTS.override.md优先于AGENTS.md,且只读第一个非空文件,避免叠加歧义(codex-rs/codex-home/src/instructions/mod.rs:26-67)。 - cloud bundle 只对企业账号拉取:
cloud_config_eligible_auth要求uses_codex_backend且 plan 是 Business/Enterprise/Edu(codex-rs/cloud-config/src/service.rs:47-54)。
小结
配置系统把多来源(system / user / project / cloud / session flags)按 precedence() 数字合并成 Config,cloud bundle 跟本地 TOML 走同一条 merge_toml_values 路径,登录态写在 auth.json 由 Model Provider 的 AuthManager 读取。ConfigLockfileToml 把合并结果锁住以保证重放一致性。云端任务用到的 enterprise 策略由 Cloud Tasks 路径里的 cloud-config 服务定期刷新,落到本地 cache 后再被这里读进 effective config。