This guide shows how to keep the standard OpenAI models in Codex Desktop while adding models routed through DroidProxy. It also documents a macOS workaround for a Desktop UI filter that can hide custom models even when the backend returns them correctly.
Related upstream issue: openai/codex#19694.
Start here: one prompt is all you need
For the quickest route, paste the prompt below into a new Codex task. This is all you need to get started. Codex can inspect what is already installed, identify the few values it cannot discover, and work through the setup safely with you.
Help me configure Codex Desktop on this Mac so I can keep the standard OpenAI models and also select models routed through DroidProxy.
Work through the setup end to end. Inspect my existing Codex, DroidProxy, shim, and model-catalogue configuration before changing anything. Reuse what is already working and ask me only for values you cannot discover locally, such as the DroidProxy model mappings or a non-default port. Never print or expose credentials, tokens, cookies, auth files, private prompts, or sensitive logs.
The target setup is:
- Codex Desktop talks to one local OpenAI-compatible Responses API shim, bound to 127.0.0.1 (prefer http://127.0.0.1:8765/v1 unless my setup already uses another port).
- The shim routes standard OpenAI models normally and sends clearly named custom model IDs to DroidProxy.
- Codex uses a combined local model catalogue that retains the standard OpenAI entries and adds visible DroidProxy entries with unique IDs.
- ~/.codex/config.toml uses a custom provider with wire_api = "responses" and an absolute model_catalog_json path.
First verify the backend with codex debug models. If the DroidProxy entries are missing there, fix the catalogue, shim, or routing layer before touching Codex Desktop. If the backend lists the custom models but the Desktop selector still hides them, check whether the current release or openai/codex#19694 has fixed the issue. Only if the workaround is still required, use a version-aware macOS patch that:
- checks /Applications/ChatGPT.app/Contents/Resources/app.asar;
- requires exactly one use_hidden_models marker and no use_hidden_modelZ marker;
- creates a version-specific backup without overwriting an existing backup;
- changes only the same-length marker use_hidden_models to use_hidden_modelZ;
- restores the backup automatically if verification fails;
- ad-hoc signs the app and verifies the signature;
- stops without forcing anything if the marker counts or expected byte differ.
Have me save my work and quit Codex Desktop before patching. Keep every proxy service on localhost unless I explicitly request and secure remote access. Afterward, verify the listening services, backend model list, patch marker, app signature, selector entries, and one harmless request through a DroidProxy model.
Show me each proposed change before making it, explain anything you cannot verify, and finish with the exact verification and rollback commands. Never reuse an app.asar backup from a different Codex Desktop version and never package or distribute the patched app, app.asar, or its backup.Prefer to understand or perform each step yourself? The complete guide follows below, including the configuration, patcher script, verification, update procedure, rollback, and troubleshooting.
Important warnings
Back up the application resource before modifying it.
The patch changes the installed application and replaces its official signature with a local ad-hoc signature.
A Codex Desktop update may overwrite the workaround.
Never restore an app.asar backup from a different Codex Desktop version.
Do not distribute a patched application, app.asar, or backup. Share the instructions and script only.
Keep API keys, authentication files, proxy configuration, logs, and model-provider credentials private.
Architecture
The useful shape is a single OpenAI-compatible Responses endpoint that merges two sources:
Codex Desktop
-> local Codex shim on 127.0.0.1
-> standard OpenAI models
-> DroidProxy modelsThe shim presents both sources through one Responses API. A combined local model catalogue tells Codex which entries should appear in the selector.
Prerequisites
Keep DroidProxy and the shim bound to localhost unless remote network access is deliberately required and protected.
Codex configuration
Use a custom provider for the merged shim and point Codex at the combined catalogue:
model_provider = "codex_shim"
model_catalog_json = "/Users/YOUR_USERNAME/.codex/combined-models.json"
[model_providers.codex_shim]
name = "OpenAI + DroidProxy"
base_url = "http://127.0.0.1:8765/v1"
wire_api = "responses"Replace YOUR_USERNAME with the local macOS account name. Codex configuration paths should be absolute; do not assume that $HOME expands inside TOML strings.
The top-level model entry is only the current selection. It can point to either a native model or a DroidProxy model without changing the routing setup.
Catalogue conventions
Retain the normal OpenAI entries in the combined catalogue. Give custom routes unique model IDs, for example:
droidproxy-grok-example
droidproxy-claude-example
droidproxy-gpt-exampleSet models intended for the picker to visible/listed according to the Codex catalogue schema. Keep internal helper models hidden.
Before changing Codex Desktop, confirm that the backend already returns the custom entries:
codex debug models | jq -r '.models[] | [.slug, .visibility] | @tsv'If the DroidProxy entries are absent here, fix the catalogue or shim first. The desktop patch cannot repair a missing backend model list.
Why custom models can disappear
Codex Desktop can receive the correct model/list response and still filter custom IDs through a remote available_models allowlist. In affected builds, the selector may show only official models even though codex debug models includes the custom entries.
The workaround changes one same-length string inside app.asar:
use_hidden_models
use_hidden_modelZThis causes the affected allowlist path to fail closed and lets the selector use the visible models returned by the backend.
Portable macOS patcher
Save the following as patch-codex-model-picker.sh in a private local scripts directory:
#!/bin/zsh
set -euo pipefail
app_path="/Applications/ChatGPT.app"
info_path="$app_path/Contents/Info.plist"
asar_path="$app_path/Contents/Resources/app.asar"
backup_dir="$HOME/.codex/backups"
if [[ ! -f "$info_path" || ! -f "$asar_path" ]]; then
print -u2 "ChatGPT/Codex Desktop application bundle was not found."
exit 1
fi
app_version=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$info_path")
backup_path="$backup_dir/ChatGPT-app.asar-$app_version-original"
count_marker() {
{ grep -abo "$1" "$asar_path" || true; } | wc -l | tr -d ' '
}
original_count=$(count_marker 'use_hidden_models')
patched_count=$(count_marker 'use_hidden_modelZ')
if [[ "$original_count" == "0" && "$patched_count" == "1" ]]; then
print "Already patched for ChatGPT/Codex Desktop $app_version"
exit 0
fi
if [[ "$original_count" != "1" || "$patched_count" != "0" ]]; then
print -u2 "Unexpected marker counts. The application layout may have changed."
print -u2 "Original marker: $original_count; patched marker: $patched_count"
exit 1
fi
offset=$(grep -abo 'use_hidden_models' "$asar_path" | cut -d: -f1)
target_offset=$((offset + 16))
current_byte=$(dd if="$asar_path" bs=1 skip="$target_offset" count=1 2>/dev/null)
if [[ "$current_byte" != "s" ]]; then
print -u2 "Expected the final marker byte to be s; found $current_byte"
exit 1
fi
mkdir -p "$backup_dir"
if [[ -e "$backup_path" ]]; then
print -u2 "Refusing to overwrite existing backup: $backup_path"
exit 1
fi
cp -p "$asar_path" "$backup_path"
printf 'Z' | dd of="$asar_path" bs=1 seek="$target_offset" count=1 conv=notrunc 2>/dev/null
if [[ "$(count_marker 'use_hidden_modelZ')" != "1" || "$(count_marker 'use_hidden_models')" != "0" ]]; then
cp -p "$backup_path" "$asar_path"
print -u2 "Patch verification failed. The original app.asar was restored."
exit 1
fi
codesign --force --deep --sign - "$app_path"
codesign --verify --deep --strict "$app_path"
print "Patched Codex Desktop version: $app_version"
print "Backup: $backup_path"
print "Patched SHA-256: $(shasum -a 256 "$asar_path" | cut -d' ' -f1)"Make it executable:
chmod 700 /ABSOLUTE/PATH/TO/patch-codex-model-picker.shRecommended procedure
1. Finish or save active work.
2. Quit Codex Desktop completely.
3. Confirm that DroidProxy and the shim are running.
4. Run the patcher using its absolute path.
5. Reopen Codex Desktop.
6. Open the model selector and confirm that both official and DroidProxy · entries appear.
7. Select one custom model and run a harmless test prompt.
Quitting first is the safest approach because the running interface has already loaded its JavaScript into memory.
Optional non-interrupting staging
The patch can be staged while Codex Desktop remains open so active work is not interrupted. The current window will continue using the old code and the selector will not change immediately. Finish the active work, then quit and reopen Codex normally.
Avoid this method if modifying a running application bundle is outside your comfort level. Quitting before patching remains the recommended procedure.
Verification
Check local services. Adjust the ports if your configuration differs:
lsof -nP -iTCP:8765 -sTCP:LISTEN
lsof -nP -iTCP:8317 -sTCP:LISTENCheck the backend catalogue:
codex debug models | jq -r '.models[] | [.slug, .visibility] | @tsv'Check the patch marker:
grep -abo 'use_hidden_modelZ' /Applications/ChatGPT.app/Contents/Resources/app.asar
grep -abo 'use_hidden_models' /Applications/ChatGPT.app/Contents/Resources/app.asarExpected result: the patched marker appears exactly once and the original marker does not appear.
Verify the application signature:
codesign --verify --deep --strict /Applications/ChatGPT.appAfter a Codex Desktop update
If custom models disappear after an update:
1. Run codex debug models.
2. If custom models still appear there, check the two marker strings in app.asar.
3. Run the same version-aware patcher. It will create a backup named for the newly installed version.
4. Restart Codex Desktop when convenient.
5. Verify the selector again.
Do not manually reuse the backup from an older application version.
Rollback
Quit Codex Desktop. Determine the installed version:
/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' /Applications/ChatGPT.app/Contents/Info.plistRestore only the backup whose filename matches that exact version:
cp -p "$HOME/.codex/backups/ChatGPT-app.asar-VERSION-original" \
/Applications/ChatGPT.app/Contents/Resources/app.asar
codesign --force --deep --sign - /Applications/ChatGPT.app
codesign --verify --deep --strict /Applications/ChatGPT.appReplace VERSION before running the command.
Troubleshooting
Safe sharing checklist
Before publishing this guide or asking someone for troubleshooting help:
Replace usernames and absolute personal paths with placeholders
Remove API keys, tokens, cookies, and authorization headers
Remove the contents of authentication and provider configuration files
Remove private logs and prompts
Do not upload app.asar, application backups, or the patched app
Keep proxy services on localhost unless they are deliberately secured
State that the workaround is unofficial and version-dependent
Encourage readers to check for an upstream fix first
