# Route drivers Codex MMO is model-maker neutral, but protocols and access products are not interchangeable. In MMO generation 8, an executable `route` selects one driver and records its API operator, access product, wire protocol, billing mode, transport capabilities, credential names, and resource group. The filename remains `PROVIDER_DRIVERS.md` for documentation organization only; the public catalog object is `routes`, not the old provider-shaped schema. ## Driver matrix | Driver | Execution path | Typical use | Managed gateway | |---|---|---|:---:| | `codex_builtin` | Codex reserved provider | ChatGPT-backed Codex models | no | | `codex_custom` | Direct Codex Responses endpoint | Conforming private/third-party Responses API | no | | `codex_oss` | Codex native local integration | Ollama or LM Studio | no | | `switchyard` | Immutable snapshot-specific route | Chat, Responses, Anthropic Messages, compatible local APIs | yes | | `catalog_only` | Metadata only | Media/service endpoint or unsupported transport | no execution | ## Common route identity ```toml [routes.example_openai_chat] name = "Example API" api_operator = "example" access_product = "example_api" wire_protocol = "openai_chat" billing_mode = "api" driver = "switchyard" base_url = "https://models.example.test/v1" credential_envs = ["EXAMPLE_API_KEY"] resource_group = "example_pool" transport_modalities = ["text"] transport_output_modalities = ["text"] tool_calling = true parallel_tool_calls = false max_retries = 1 ``` Required identity fields are never inferred from a route key. A recognized field that does not apply to the selected driver is rejected instead of retained inertly. ## `codex_builtin` ```toml [routes.codex_chatgpt_builtin] name = "Built-in Codex with ChatGPT authentication" api_operator = "openai" access_product = "chatgpt_codex" wire_protocol = "codex_builtin" billing_mode = "chatgpt_subscription" driver = "codex_builtin" provider_id = "openai" auth = "chatgpt" resource_group = "chatgpt_subscription" transport_modalities = ["text", "image"] tool_calling = true parallel_tool_calls = true ``` This preserves Codex’s native authentication, entitlement, tools, images, and active model catalog. A generated home uses the configured base home’s file-backed `auth.json` according to `auth_link_mode`; Codex 0.149 keyring-only state cannot be transferred to another `CODEX_HOME`. Account-visible models and capabilities remain authoritative. The bundled catalog is a release baseline, not an entitlement claim. ## `codex_custom` ```toml [routes.private_responses] name = "Private Responses" api_operator = "example" access_product = "private_responses" wire_protocol = "openai_responses" billing_mode = "api" driver = "codex_custom" base_url = "https://llm.example.internal/v1" wire_api = "responses" credential_envs = ["PRIVATE_LLM_API_KEY"] transport_modalities = ["text", "image"] preserves_tool_media = true tool_result_modalities = ["text", "image"] [routes.private_responses.http_headers] "X-Static-Tenant" = "engineering" [routes.private_responses.env_http_headers] "X-Private-Token" = "PRIVATE_HEADER_VALUE" ``` Use direct mode only when the endpoint faithfully implements Responses and direct transport is preferable to translation. The Codex process necessarily receives declared credential/header variables, so place sensitive work in a suitable OS boundary. `wire_api` is currently `responses`. Direct retry fields are `request_max_retries`, `stream_max_retries`, and `stream_idle_timeout_ms`. Codex interprets zero as immediate expiry, not “disabled,” so route validation rejects it. Generated homes raise the effective idle floor to at least one hour and twice the role's warning-only stall interval. That value detects a genuinely broken stream; it is not a task deadline, goal budget, or instruction for the model to keep time. ## `codex_oss` ```toml [routes.ollama_codex_oss] name = "Codex native Ollama OSS mode" api_operator = "local" access_product = "ollama" wire_protocol = "codex_oss" billing_mode = "local" driver = "codex_oss" provider_id = "ollama" resource_group = "local_gpu_0" transport_modalities = ["text"] ``` Supported `provider_id` values are `ollama` and `lmstudio`. Switchyard variants also exist for compatible local Chat endpoints when common routing and telemetry matter more than native transport. ## `switchyard` Switchyard 0.2.0 supports these route protocols: - `openai_chat`; - `openai_responses`; - `anthropic_messages`. The compiler emits only routes used by one immutable snapshot. A profile with no Switchyard binding starts no gateway. Managed ingress is restricted to a loopback IP literal because the generated gateway has no client-auth layer. Bundled Switchyard routes include: - OpenCode Go Chat, Responses, and Anthropic Messages through `OPENCODE_API_KEY`; - OpenCode Zen Chat, Responses, and Anthropic Messages through the same `OPENCODE_API_KEY`; - OpenRouter Chat through `OPENROUTER_API_KEY`; - Z.AI Coding Plan Chat and Responses through `ZAI_CODING_API_KEY`; - Z.AI General API through the separately explicit `ZAI_API_KEY`; - a bundled llama.cpp model binding; - direct OpenAI/Anthropic and LM Studio/Ollama route definitions for explicit user catalog extensions. A route without a model record is not an executable bundled model binding. Switchyard receives `extra_headers`, not environment-backed per-header mappings. API authentication uses `credential_envs`. MMO emits `max_retries = 1` by default, meaning one additional attempt; the accepted range is 0–10. ### Temporary Codex MCP namespace bridge Codex 0.149 represents MCP servers as Responses `namespace` tools. Switchyard 0.2.0's Responses decoder retains top-level function and ID tools but does not retain that namespace shape. For a gateway whose recorded executable version is exactly `0.2.0`, MMO therefore asks the same Codex app-server for its granted MCP tool schemas, registers equivalent flat `mmo_mcp__SERVER__TOOL` dynamic functions when a thread is first created, and routes each dynamic call back through Codex's `mcpServer/tool/call`. The MMO-owned prefix is deliberate because Codex rejects dynamic declarations using its reserved native `mcp__` prefix. Codex remains the MCP connection owner and enforces its configured server/tool allowlists; the bridge does not read supervisor files or connect to MCP servers independently. MCP text, image, audio, error state, and non-redundant structured content are preserved; a structured value is omitted only when an existing text block parses to that exact value (including the common single-key result wrapper), avoiding duplicate context without discarding distinct information. Durable Codex threads retain the dynamic tool declarations, while every replacement host reconstructs only the in-memory routing map. This is a release-scoped compatibility shim, not a second permanent MCP architecture. It is disabled for every Switchyard version other than `0.2.0`. The baseline/bridge guard test deliberately fails when the reviewed Switchyard baseline changes. Before accepting a newer baseline: 1. prove with the exact candidate binary that Codex 0.149 namespace tools reach both the Go and Zen model routes and that a real MCP call completes; 2. prove new-thread, resumed-thread, worker, allowlist, error, and cancellation behavior without `dynamicTools`; 3. remove the bridge constants, dynamic schema discovery/call routing, root/worker injection, and their compatibility tests; 4. update this section, the architecture/veracity records, changelog, package manifest, and installed runtime. An operator-supplied newer Switchyard is never assigned the 0.2.0 shim merely because the profile uses the `switchyard` driver. Its native namespace path must pass live qualification before production use. #### Open removal tracker Upstream [Switchyard PR #384](https://github.com/NVIDIA-NeMo/Switchyard/pull/384), merged as commit [`c7beccd4891fa5cfe3a3b94fdd376f5765864507`](https://github.com/NVIDIA-NeMo/Switchyard/commit/c7beccd4891fa5cfe3a3b94fdd376f5765864507) on 2026-08-20, implements native Codex namespace preservation. The current `v0.2.0` tag predates that commit, and no published tag contains it as of 2026-08-23. This tracker remains open until the first official `switchyard-server` release whose tag contains that commit is available. The release containing that commit is an upgrade/removal trigger, not permission to retain both paths. The baseline bump must be one atomic change: run the qualification matrix above against the exact released binary, delete the dynamic bridge and its version constant, then update the pin. The existing `test_temporary_switchyard_bridge_must_be_removed_when_baseline_advances` test blocks a baseline-only update. If native qualification fails, keep `0.2.0` pinned and record the failure; do not extend the bridge to the newer release. Z.AI Coding Plan uses distinct documented bases by wire protocol: Responses/Codex uses `https://api.z.ai/api/v1`, while OpenAI-compatible Chat uses `https://api.z.ai/api/coding/paas/v4`. Switchyard appends the protocol endpoint suffix. MMO does not retry one route through the other and does not fall back to the separately credentialed General API. The Coding Plan Anthropic-compatible endpoint remains catalog-only: its documented Bearer authentication cannot be represented by Switchyard 0.2.0’s fixed Anthropic `x-api-key` behavior. No hidden transport fallback is attempted. ## `catalog_only` Catalog-only records preserve complete inventory information without pretending the endpoint can run a Codex agent. Examples include OCR, image/video generation, audio transcription, hosted application agents, Google-native Zen models without an installed driver, and the incompatible Coding Plan Anthropic route. ```toml [routes.zai_general_catalog] name = "Z.AI media and specialist API catalog" api_operator = "zai" access_product = "zai_general_api" wire_protocol = "catalog_only" billing_mode = "catalog_only" driver = "catalog_only" transport_modalities = ["text", "image", "video", "audio", "file"] [models.zai_general_catalog__glm_image] maker = "zai" route = "zai_general_catalog" upstream_id = "glm-image" kind = "image_generation" agent_compatible = false ``` ## Driver-specific fields | Field | Drivers | Meaning | |---|---|---| | `api_operator` | all | API operator identity | | `access_product` | all | Plan/service identity | | `wire_protocol` | all | Exact protocol enum | | `billing_mode` | all | `api`, `subscription`, `chatgpt_subscription`, `local`, or `catalog_only` | | `provider_id` | built-in/OSS | Codex-owned provider identifier | | `base_url` | custom/Switchyard/catalog-only | Absolute HTTP(S) API root | | `credential_envs` | custom/Switchyard/catalog-only | Ordered valid environment names | | `wire_api` | custom | Direct Codex API; `responses` | | `http_headers` | custom | Static Codex headers | | `env_http_headers` | custom | Header name to environment name | | `extra_headers` | Switchyard | Static Switchyard headers | | `max_retries` | Switchyard | Additional attempts, 0–10 | | `request_max_retries` | custom | Direct request retries | | `stream_max_retries` | custom | Stream reconnections | | `stream_idle_timeout_ms` | custom | Provider stream-idle floor (at least 1,000 ms); generated app-server configs raise it through the role's execution window plus finalization grace when that is longer | | `transport_modalities` | all | Inputs the complete path preserves | | `transport_output_modalities` | all | Outputs the complete path preserves | | `preserves_tool_media` | all | Whether tool media survives transport | | `tool_result_modalities` | all | Preserved tool-result media types | | `supports_documents` | all | Complete file/document path | | `openrouter_policy` | OpenRouter only | Route-wide typed serving policy | There are no singular credential aliases or legacy header-name aliases in the v2 contract. Use exactly `credential_envs`, `http_headers`, `env_http_headers`, and `extra_headers` for their documented drivers. `base_url` must be an absolute HTTP(S) root with a nonempty host. Userinfo, control/space characters, backslashes, queries, and fragments are rejected. Header names and values are validated before downstream configuration is generated. ## Model transport fields ```toml [models.example_openai_chat__example_coder] maker = "example" route = "example_openai_chat" upstream_id = "example-coder" kind = "chat" agent_compatible = true modalities = ["text"] output_modalities = ["text"] tool_calling = true parallel_tool_calls = false structured_output = true context_window = 200000 max_output_tokens = 32000 reasoning_levels = ["none", "high"] default_reasoning = "high" resource_group = "example_pool" ``` The compiler checks model capabilities against route capabilities. A maker/model name alone is never evidence that a particular transport preserves tools, images, documents, reasoning selectors, or structured output. `extra_body` is supported only for Switchyard-backed model targets and must contain finite JSON-compatible values. ## Credentials Credential assignments belong in `~/.config/codex-mmo/credentials.env` or the launch environment. Profiles, catalogs, snapshots, and generated static configuration store names, never values. Worker/gateway environments filter conventional unrelated credential names and explicitly re-admit only the selected route and Tool MCP variables. That filtering is name-based, not an OS sandbox or secret oracle. Use conventional secret names and sanitize unusual variables before launching untrusted work. ## Adding a route Add a generation-8 fragment under `~/.config/codex-mmo/catalog.d/`, then run: ```bash codex-mmo catalog verify codex-mmo catalog routes codex-mmo catalog models --route ROUTE_KEY codex-mmo profile validate PROFILE ``` No runtime plugin is needed when an existing driver fits. Executable driver plugins are intentionally outside profile packs.