Skip to content

配置系统

源码版本rust-v0.145.0

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,登录态也是配置层的一部分。

职责

  1. 定位 codex home:find_codex_homeCODEX_HOME,未设置则回退 ~/.codex(codex-rs/core/src/config/mod.rs:4440-4442)。
  2. 多层加载:load_config_layers_state 顺序装 system / managed / cloud bundle / user / project / session flags 各层,产出 ConfigLayerStack(codex-rs/config/src/loader/mod.rs:116-168)。
  3. 合并产出 effective config:ConfigLayerStack::effective_config 按 precedence 从低到高 merge TOML(codex-rs/config/src/state.rs:483-492)。
  4. 装配运行时 Config:ConfigBuilder 把 codex_home、cli_overrides、harness_overrides、cloud bundle 拼到一起 build().await(codex-rs/core/src/config/mod.rs:1258-1297)。
  5. 写入登录态: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-49ConfigLayerSource enum 与 precedence(),定义 8 种来源与合并顺序。codex-rs/config/src/loader/mod.rs:116-168load_config_layers_state,把 system / managed / cloud bundle / user / project 各层按序装入 stack。codex-rs/config/src/state.rs:483-492effective_config,按 precedence 从低到高 merge_toml_valuescodex-rs/core/src/config/mod.rs:614-673Config 结构,运行时拿到的"最终配置"对象,含 model_provider、permissions、enforce_residency 等。codex-rs/core/src/config/mod.rs:1258-1297ConfigBuilder,装配 codex_home、cli_overrides、harness_overrides、cloud bundle 后 build().awaitcodex-rs/core/src/config/mod.rs:1721-1728Config::load_with_cli_overrides,最常用入口。codex-rs/core/src/config_lock.rs:38-74config_lockfilevalidate_config_lock_replay,锁文件生成与重放校验。codex-rs/codex-home/src/instructions/mod.rs:14-68CodexHomeUserInstructionsProvider,从 ~/.codex/AGENTS.md 加载全局用户指令。

ConfigLayerSource 把 8 种来源压成 enum,precedence 数字决定合并顺序:

rust
// 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,后写的覆盖先写的:

rust
// 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 字段差异:

rust
// 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 报错
}

数据流

边界与失败

小结

配置系统把多来源(system / user / project / cloud / session flags)按 precedence() 数字合并成 Config,cloud bundle 跟本地 TOML 走同一条 merge_toml_values 路径,登录态写在 auth.jsonModel ProviderAuthManager 读取。ConfigLockfileToml 把合并结果锁住以保证重放一致性。云端任务用到的 enterprise 策略由 Cloud Tasks 路径里的 cloud-config 服务定期刷新,落到本地 cache 后再被这里读进 effective config。