Skip to content

Config System

源码版本rust-v0.145.0

codex's config is not a single-file load but a multi-layer stack: config.toml comes from system / user / project / cloud bundle / session flags sources, merged by precedence into the effective config, then ConfigRequirements is applied for constraint validation. The config crate handles layering and merging; codex-home provides CODEX_HOME resolution and global AGENTS.md loading; core/src/config/mod.rs packages the merged result into Config for the Agent main loop; core/src/config_lock.rs uses a lockfile for reproducible execution. cli/src/login.rs writes auth.json, so login state is also part of the config layer.

Responsibilities

  1. Locate codex home: find_codex_home reads CODEX_HOME, falling back to ~/.codex if unset (codex-rs/core/src/config/mod.rs:4440-4442).
  2. Multi-layer load: load_config_layers_state loads system / managed / cloud bundle / user / project / session flag layers in order, producing a ConfigLayerStack (codex-rs/config/src/loader/mod.rs:116-168).
  3. Merge into effective config: ConfigLayerStack::effective_config merges TOML from low to high precedence (codex-rs/config/src/state.rs:483-492).
  4. Assemble runtime Config: ConfigBuilder combines codex_home, cli_overrides, harness_overrides, cloud bundle and build().await (codex-rs/core/src/config/mod.rs:1258-1297).
  5. Persist login state: login_with_chatgpt / run_login_with_api_key complete the login flow and write credentials to auth.json (codex-rs/cli/src/login.rs:137-165).

Design motivation

A single config.toml file is simple, but enterprise deployment requires multi-source stacking: MDM-pushed mandatory policies cannot be modified by users, project .codex/config.toml must override user global, CLI flags must override project config. codex abstracts each layer into a ConfigLayerEntry; ConfigLayerSource::precedence returns a number deciding merge order — Session flags (30) override Project (25) override User (20) override System (10) override MDM (0). Adding a source only requires adding an enum variant + returning a number; the merge logic is untouched. The cloud bundle is a newer layer, pulling enterprise-managed config and requirements from the ChatGPT backend; load_config_layers_state converts it into a plain layer via CloudConfigBundleLayers::from_bundle and pushes it into the stack, going through the same merge path as local TOML. config_lock.rs is added for reproducibility: it serializes the merged ConfigToml + codex version into a ConfigLockfileToml; on replay, validate_config_lock_replay compares expected vs actual, erroring on any version or field mismatch, to prevent the same prompt from producing different results on different machines.

Key files

codex-rs/config/src/config_layer_source.rs:6-49 — the ConfigLayerSource enum and precedence(), defining 8 sources and merge order.codex-rs/config/src/loader/mod.rs:116-168load_config_layers_state, loading system / managed / cloud bundle / user / project layers into the stack in order.codex-rs/config/src/state.rs:483-492effective_config, merge_toml_values from low to high precedence.codex-rs/core/src/config/mod.rs:614-673 — the Config struct, the "final config" object the runtime sees, including model_provider, permissions, enforce_residency, etc.codex-rs/core/src/config/mod.rs:1258-1297ConfigBuilder, assembling codex_home, cli_overrides, harness_overrides, cloud bundle and then build().await.codex-rs/core/src/config/mod.rs:1721-1728Config::load_with_cli_overrides, the most common entry.codex-rs/core/src/config_lock.rs:38-74config_lockfile and validate_config_lock_replay, lockfile generation and replay validation.codex-rs/codex-home/src/instructions/mod.rs:14-68CodexHomeUserInstructionsProvider, loading global user instructions from ~/.codex/AGENTS.md.

ConfigLayerSource compresses 8 sources into an enum; the precedence number decides merge order:

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 merges layer by layer from low to high precedence; later writes override earlier ones:

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 locks the merged result at the time, and on replay compares TOML field differences:

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

Data flow

Boundaries and failures

  • CODEX_HOME must be a directory: if the env var is set but does not exist or is not a directory, it errors directly rather than silently falling back to ~/.codex (codex-rs/core/src/config/mod.rs:4436-4442).
  • Invalid config is not fatal: Config::load_default_with_cli_overrides falls back to default + cli overrides when the user config file fails to parse, ensuring the CLI can still come up (codex-rs/core/src/config/mod.rs:1731-1740).
  • config_lock version mismatch errors by default: a codex version mismatch directly rejects replay; you must explicitly set debug.config_lockfile.allow_codex_version_mismatch=true to allow it (codex-rs/core/src/config_lock.rs:54-61).
  • AGENTS.md precedence: in the same directory, AGENTS.override.md takes precedence over AGENTS.md, and only the first non-empty file is read, to avoid stacking ambiguity (codex-rs/codex-home/src/instructions/mod.rs:26-67).
  • cloud bundle only fetched for enterprise accounts: cloud_config_eligible_auth requires uses_codex_backend and the plan to be Business / Enterprise / Edu (codex-rs/cloud-config/src/service.rs:47-54).

Summary

The config system merges multiple sources (system / user / project / cloud / session flags) by their precedence() number into Config; the cloud bundle goes through the same merge_toml_values path as local TOML; login state is written to auth.json and read by the AuthManager in Model Provider. ConfigLockfileToml locks the merged result to guarantee replay consistency. Enterprise policies used by cloud tasks are periodically refreshed by the cloud-config service on the Cloud Tasks path, land in a local cache, and then are read into the effective config here.