配置系統
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。