Sistema de configuración
La configuración (config) de codex no se carga de un único archivo: se apila en múltiples capas. El config.toml viene de varias fuentes — system / user / project / cloud bundle / session flags — y se fusiona por precedence hasta formar el effective config, sobre el que después se aplican los ConfigRequirements para validar restricciones. El crate config se ocupa del apilado y la fusión; codex-home ofrece la resolución de CODEX_HOME y la carga del AGENTS.md global; core/src/config/mod.rs envuelve el resultado de la fusión en un Config que usa el bucle principal del Agent; core/src/config_lock.rs usa un lockfile para garantizar ejecución reproducible. cli/src/login.rs escribe auth.json, así que el estado de login también es una capa de configuración.
Responsabilidades
- Localizar codex home:
find_codex_homeleeCODEX_HOME; si no está fijado, retrocede a~/.codex(codex-rs/core/src/config/mod.rs:4440-4442). - Carga multi-capa:
load_config_layers_statecarga en orden system / managed / cloud bundle / user / project / session flags y produce unConfigLayerStack(codex-rs/config/src/loader/mod.rs:116-168). - Fusionar y producir effective config:
ConfigLayerStack::effective_configmergea TOML por precedence de baja a alta (codex-rs/config/src/state.rs:483-492). - Ensamblar el
Configde runtime:ConfigBuilderpega codex_home, cli_overrides, harness_overrides y cloud bundle y luegobuild().await(codex-rs/core/src/config/mod.rs:1258-1297). - Escribir el estado de login:
login_with_chatgpt/run_login_with_api_key, tras completar el flujo de login, vuelcan las credenciales aauth.json(codex-rs/cli/src/login.rs:137-165).
Motivación de diseño
Un único config.toml sería simple, pero el despliegue enterprise exige apilar varias fuentes: las políticas obligatorias bajadas por MDM no deben poder cambiarse por el usuario; el .codex/config.toml del proyecto debe sobrescribir el global del usuario; y los flags del CLI tienen que prevalecer sobre la configuración del proyecto. codex abstrae cada capa como ConfigLayerEntry, y ConfigLayerSource::precedence devuelve un número que decide el orden de fusión — Session flags (30) prevalece sobre Project (25) prevalece sobre User (20) prevalece sobre System (10) prevalece sobre MDM (0). Añadir un origen nuevo es añadir una variante al enum y devolver el número, sin tocar la lógica de fusión. El cloud bundle es una capa añadida después: tira del backend de ChatGPT para traer config y requirements gestionados por enterprise, y en load_config_layers_state se convierte en una capa normal vía CloudConfigBundleLayers::from_bundle para que entre al stack por la misma ruta de fusión que los TOML locales. config_lock.rs se añadió por reproducibilidad: serializa el ConfigToml resultante + la versión de codex en un ConfigLockfileToml, y al reproducir, validate_config_lock_replay compara expected vs actual; si la versión o los campos no coinciden, suelta error, evitando que el mismo prompt corra resultados distintos en máquinas distintas.
Archivos clave
codex-rs/config/src/config_layer_source.rs:6-49 — enum ConfigLayerSource y precedence(), define 8 orígenes y el orden de fusión.codex-rs/config/src/loader/mod.rs:116-168 — load_config_layers_state, carga system / managed / cloud bundle / user / project en orden en el stack.codex-rs/config/src/state.rs:483-492 — effective_config, hace merge_toml_values por precedence de baja a alta.codex-rs/core/src/config/mod.rs:614-673 — struct Config, el objeto «configuración final» que ve el runtime, con model_provider, permissions, enforce_residency, etc.codex-rs/core/src/config/mod.rs:1258-1297 — ConfigBuilder, ensambla codex_home, cli_overrides, harness_overrides y cloud bundle y luego build().await.codex-rs/core/src/config/mod.rs:1721-1728 — Config::load_with_cli_overrides, la entrada más usada.codex-rs/core/src/config_lock.rs:38-74 — config_lockfile y validate_config_lock_replay, generación del lockfile y validación de replay.codex-rs/codex-home/src/instructions/mod.rs:14-68 — CodexHomeUserInstructionsProvider, carga las instrucciones globales del usuario desde ~/.codex/AGENTS.md.ConfigLayerSource comprime los 8 orígenes en un enum; el número de precedence decide el orden de fusión:
// config/src/config_layer_source.rs:6-48 — origen + prioridad
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 o 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 mergea por precedence de baja a alta; lo que se escribe después sobrescribe a lo anterior:
// config/src/state.rs:483-492 — fusión del apilado
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 congela el resultado de la fusión del momento; al reproducir, compara las diferencias de los campos TOML:
// core/src/config_lock.rs:46-74 — validación de replay del lockfile
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
)));
}
// ...sigue comparando campos TOML; si hay inconsistencia, usa similar para hacer diff y reporta
}Flujo de datos
Bordes y fallos
- CODEX_HOME tiene que ser un directorio: si la variable de entorno está fijada pero no existe o no es un directorio, suelta error directo, no retrocede silenciosamente a
~/.codex(codex-rs/core/src/config/mod.rs:4436-4442). - Configuración inválida no es fatal:
Config::load_default_with_cli_overrides, si falla el parseo del archivo de configuración del usuario, retrocede a default + cli overrides, para que la CLI siga arrancando (codex-rs/core/src/config/mod.rs:1731-1740). - config_lock por defecto rechaza mismatch de versión: si la versión de codex no coincide, rechaza el replay; hay que fijar explícitamente
debug.config_lockfile.allow_codex_version_mismatch=truepara permitirlo (codex-rs/core/src/config_lock.rs:54-61). - Prioridad de AGENTS.md: en el mismo directorio,
AGENTS.override.mdprevalece sobreAGENTS.md, y solo se lee el primer archivo no vacío para evitar ambigüedades por apilado (codex-rs/codex-home/src/instructions/mod.rs:26-67). - cloud bundle solo se tira para cuentas enterprise:
cloud_config_eligible_authexigeuses_codex_backendy que el plan sea Business/Enterprise/Edu (codex-rs/cloud-config/src/service.rs:47-54).
Resumen
El sistema de configuración fusiona múltiples fuentes (system / user / project / cloud / session flags) por número de precedence() en un Config; el cloud bundle va por la misma ruta merge_toml_values que los TOML locales, y el estado de login se escribe en auth.json y lo lee el AuthManager de Model Provider. ConfigLockfileToml congela el resultado de la fusión para garantizar consistencia al reproducir. Las políticas enterprise usadas por las tareas en la nube se refrescan periódicamente por el servicio cloud-config en la ruta de Cloud Tasks y, tras caer a la caché local, se leen aquí dentro del effective config.