Repository navigation
[Feature]: Support routing third-party models in remote Codex sessions (Remote SSH) #7228
Copy link
Copy link
Open
Labels
account-poolOAuth, credentials, Codex pool, quota, failover, plansOAuth, credentials, Codex pool, quota, failover, planscatalogModel catalog, slugs, visibility, routed entriesModel catalog, slugs, visibility, routed entriesenhancementNew feature or requestNew feature or requestneeds-design설계 필요. Implementation waits on a written design.설계 필요. Implementation waits on a written design.proxyHTTP proxy, routing, reverse-proxy / management authHTTP proxy, routing, reverse-proxy / management auth
Description
Area
Proxy and routing
What are you trying to accomplish?
Enable routed third-party models (Claude, Gemini, DeepSeek, Grok, etc.) when using OpenAI Codex connected to a remote server via Remote SSH or remote execution environments.
User workflow:
- A developer runs OpenCodex locally on their workstation (macOS/Windows), where all API keys, OAuth sessions (Anthropic, xAI, Google), and ChatGPT account pools are configured.
- The developer connects to a remote development server or cloud VM via Codex Desktop (Settings > Connections / Remote SSH) or launches a remote
codexsession over SSH. - The developer wants the remote Codex session to route its model requests through the local OpenCodex proxy so that routed models, combos, and custom providers are accessible in the remote workspace just like in a local session.
What prevents this today?
When Codex connects to a remote host:
- Execution moves to the remote machine, where
codex app-serverreads$HOME/.codex/config.tomland model catalogs on the remote filesystem, not the local workstation. - The remote server does not have OpenCodex listening on
127.0.0.1:10100and itsconfig.tomldoes not point to the proxy, resulting in direct requests to OpenAI endpoints. - The remote environment lacks the synced
opencodex-catalog.json, causing the model picker in remote workspaces to hide routed models. - Installing OpenCodex directly on headless remote servers is cumbersome, creates duplicate credential management, and complicates browser-based OAuth flows.
What should OpenCodex do?
OpenCodex should provide first-class support or streamlined tooling for remote Codex environments:
- Remote Sync / Setup CLI: Provide a command such as
ocx remote sync <ssh-host>orocx remote setup <ssh-host>that can:- Export and upload the current
opencodex-catalog.jsonto the target host's~/.codex/directory. - Inject
openai_base_url = "http://127.0.0.1:10100/v1"into the remote~/.codex/config.toml. - Verify or guide the setup of an SSH reverse tunnel (
RemoteForward 127.0.0.1:10100 127.0.0.1:10100in~/.ssh/config).
- Export and upload the current
- Safe Reverse Tunnel Support: Ensure that data-plane requests originating from a forwarded loopback tunnel continue to be admitted cleanly without credential conflicts.
- Documentation / Guidance: Provide documentation in
docs-siteon configuring Codex Desktop Remote SSH hosts to work seamlessly with OpenCodex.
Example usage or interface
# Set up a remote host configured in ~/.ssh/config for OpenCodex routing
ocx remote setup my-devbox
# Output:
# ✓ Connected to my-devbox via SSH
# ✓ Synced opencodex-catalog.json (42 models) to my-devbox:~/.codex/opencodex-catalog.json
# ✓ Injected openai_base_url = "http://127.0.0.1:10100/v1" into my-devbox:~/.codex/config.toml
# ✓ Verified RemoteForward rule in ~/.ssh/config for my-devbox
# Remote Codex is now ready to use local OpenCodex models!Alternatives or workarounds
- Manually maintaining an SSH reverse tunnel (
ssh -R 10100:127.0.0.1:10100 user@host), manually SCPingopencodex-catalog.json, and hand-editingconfig.tomlon every remote machine. - Installing full OpenCodex instances on every remote server and manually duplicating API keys and configurations.
Additional context
- Upstream Codex Desktop supports remote connections via
~/.ssh/confighost aliases. - Care must be taken so that
RemoteForwardis host-scoped rather than defined globally underHost *to avoid the known Codex SSH path-probe hang issue.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
account-poolOAuth, credentials, Codex pool, quota, failover, plansOAuth, credentials, Codex pool, quota, failover, planscatalogModel catalog, slugs, visibility, routed entriesModel catalog, slugs, visibility, routed entriesenhancementNew feature or requestNew feature or requestneeds-design설계 필요. Implementation waits on a written design.설계 필요. Implementation waits on a written design.proxyHTTP proxy, routing, reverse-proxy / management authHTTP proxy, routing, reverse-proxy / management auth
Activity
Ingwannu commentedon Oct 16, 2026
There are two separate boundaries here: the remote app-server must reach the intended proxy and read the right remote Codex-home catalog, and some Desktop builds additionally filter remote model IDs after model/list. Copying a catalog and opening a reverse tunnel does not by itself settle both.
Current documentation describes the second boundary in the remote-allowlist section. #6157, now carried by #7215, is an RFC with executable probes, not a shipped remote setup command or a supported switch that disables this client filter.
Please specify the initial target (Desktop Remote SSH versus a CLI launched inside SSH), exact Codex/OpenCodex versions, and whether the observed failure is proxy reachability, an absent catalog row, or a row removed by Desktop after model/list. No account/catalog dump is needed.
A setup design must identify the remote CODEX_HOME, snapshot only owned configuration/catalog fields for restore, bind the reverse listener explicitly to remote loopback, use explicit data-plane admission rather than treating forwarded loopback as caller identity, and fail safely when the tunnel disappears. Provider OAuth sessions and auth.json must not be uploaded as setup convenience. A nativeAlias compatibility route is a bounded documented option for some picker cases, not a universal remote-support claim.
Keeping this as needs-design; no remote host, SSH configuration or credentials were changed, and no new remote CLI is claimed to exist.
Yuxin-Qiao commentedon Oct 16, 2026
Thanks for the detailed breakdown and clear boundary separation @Ingwannu.
Here are the specific environment details and observed workflow:
Initial Target Scenario:
~/.ssh/confighost alias).Environment & Versions:
26.1007.71326(macOS)codex-cli 0.161.0(local and remote)opencodex 2.86.0(running locally)Observed Failure & Workflow:
~/.codexwithoutopenai_base_urlpointing to the reverse tunnel and without the generatedopencodex-catalog.json.model/listallowlist filter. For Desktop Remote SSH users, leveraging OpenCodex's existingnativeAlias/ combo mapping (shadowing an allowlisted slug likegpt-5.6-sol) in conjunction with the remote injection/catalog would be an effective and bounded way to ensure the model survives the Desktop renderer filter while executing through the intended remote session.Alignment on Security & Architecture Constraints:
auth.json, or API keys must never be transferred or stored on the remote host. All model dispatch and credentials should stay strictly local.127.0.0.1).CODEX_HOMEand fail safely if the tunnel drops.Yuxin-Qiao commentedon Oct 16, 2026
Concrete reproduction & real-world impact (sanitized)
To provide concrete evidence on why this workflow is critical:
1. Setup & Connection State
0.162.1).2. The Bottleneck & Impact
GPT-6.1 SolGPT-6 AstraGPT-6 SolGPT-6 LunaGPT-5.6 SolGPT-5.6 TerraGPT-5.6 LunaThis demonstrates why having first-class support (e.g.,
ocx remote sync/ reverse-tunnel integration) is so essential: without it, any remote Codex development session loses the multi-provider and account-pool advantages of OpenCodex whenever an account quota is exhausted.Yuxin-Qiao commentedon Oct 16, 2026
RFC: Native Remote SSH Model Routing — Technical Feasibility & Design Proposal
1. Problem Decomposition
The feature request involves two distinct boundaries that must be bridged:
codex app-serversends requests directly toapi.openai.comopenai_base_urloverride injected into remote~/.codex/config.tomlavailableModelsset; non-OpenAI slugs silently droppedBoth boundaries have existing infrastructure that can be extended. Below is the design for each.
2. Boundary 1: Tunnel Path & Authentication
Finding: SSH reverse tunnels work today with zero auth friction.
The loopback admission gate in
src/server/auth-cors.ts(L46-L61) keys exclusively on hostname — port checks were removed in WP2 (commite4e06125b), verified bytests/server-loopback-host-gate.test.ts:Host: 127.0.0.1:10100→isLoopbackHostname("127.0.0.1") === true→ accepted withoutOPENCODEX_API_AUTH_TOKEN-R 20100:127.0.0.1:10100) also works:Host: 127.0.0.1:20100→ hostname-only check passesunauthenticatedLoopbackListener(L590-L637 insrc/server/index.ts) is the ideal tunnel ingress target:/v1/responses,/v1/models, WebSocket)/api/*) return 404 — complete isolationEdge cases to document:
127.0.0.1orlocalhost(not custom hostnames)GatewayPorts yes/ssh -gmust be avoided (exposes unauthenticated proxy to remote LAN)ssh -R /tmp/ocx-$UID/codex.sock:...) with mode07003. Boundary 2: Desktop Model Visibility
Finding:
nativeAliascombo mechanism already solves this.Desktop's renderer uses
use_hidden_modelsflag with a hardcodedavailableModelsallowlist (gpt-5.5,gpt-5.4,gpt-5.4-mini,gpt-5.3-codex-spark,gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna,gpt-daybreak-blue-latest). Non-matching slugs are silently dropped.The existing
nativeAliascombo mechanism insrc/codex/catalog/sync.ts(L943-L968) bypasses this by:slug = nativeAlias.alias(e.g.gpt-5.6-sol) → passes Desktop allowlistvisibility: "hide", preventing Desktop resurrection)tryPickComboModelinsrc/router.ts(L596-L644) evaluates beforeisBareOpenAiFamilyModel→ requests route to combo targetsrc/codex/catalog/provider-fetch.ts(L1766-L1789) synthesizes context window, modalities, and reasoning effort ladders from native templatesFor the
ocx remoteflow, this means the remote catalog injection should pre-configure nativeAlias combos so third-party models appear natively in Desktop's picker.Known limitation: Codex Multi-Agent v2 encrypted payloads (
NEW_TASK) use OpenAI public keys — non-OpenAI targets fail withunreadable_encrypted_agent_task. Workaround:multiAgentMode: "v1".4. Remote Configuration Injection (Minimal Footprint)
Finding: Only 2 files are strictly required on the remote host. No SQLite touch.
Using Design B (Loopback Override) from
src/codex/inject.ts— the path taken whenopenai_base_urlpoints to127.0.0.1:$CODEX_HOME/config.tomlopenai_base_url = "http://127.0.0.1:<port>/v1"at TOML root$CODEX_HOME/opencodex-journal.json$CODEX_HOME/opencodex-catalog.json$CODEX_HOME/models_cache.json$CODEX_HOME/state_5.sqlitemodel_provider = "openai"— no session migration neededRemote
CODEX_HOMEdiscovery (zero runtime dependencies):Two-phase remote rollback (mirrors
restoreJournalState()insrc/codex/journal.tsL157-L190):config.tomlmatches journal'sinjectedConfigHash→ full restore from base64 backupopenai_base_urlandopencodex_catalog_pathlines, preserving user changesopencodex-journal.jsonandopencodex-catalog.jsonSafe transport — never interpolate content into shell strings:
5. Proposed CLI Design:
ocx remoteFinding: Clean extension point exists — no legacy remote code to work around.
The CLI uses a 4-stage architecture: entry (
src/cli/index.ts) → root (src/cli/root.ts) → registry (src/cli/registry.ts) → dispatch (src/cli/dispatch.ts). Adding a command requires:CliCommandEntrytoCLI_COMMANDSinsrc/cli/registry.tscommandRunnersinsrc/cli/dispatch.tssrc/cli/remote.tsReusable infrastructure:
runCliAction(error mapping),createPrompt(interactive wizard),findLiveProxy(liveness check), config-export engine.Proposed subcommands:
6. Tunnel Failsafe Design
Finding: Fail-closed is the only safe design. Never fall back to native OpenAI.
If
openai_base_urlwere stripped on tunnel drop, private prompt turns intended for DeepSeek/Claude/Ollama would silently divert toapi.openai.com— leaking context and burning credits.Proposed behavior:
tunnel_disconnected,Retry-After: 5relaySseWithFailedTailprotocol:event: response.failed(code: upstream_reset) +data: [DONE]/healthzevery 5-10s (1.5s timeout). State machine:CONNECTED→DEGRADED(1 miss) →DISCONNECTED(3 misses / 15s)previous_response_id: opencodex keepalive\n\nevery 15s (already implemented inrelaySseWithHeartbeat) prevents tunnel timeout during deep reasoningCritical invariant: OpenCodex never auto-retries a failed mid-stream turn (prevents double-billing / duplicate side effects).
7. End-to-End Data Flow
8. Implementation Phases
ocx remote setup/restore— config injection + journal + rollback via SSHocx remote tunnel— managed reverse tunnel with health monitoring + keepaliveocx remote status— JSON-friendly status output for tooling integrationTotal estimated effort: ~8-10 days for a complete, tested implementation.
9. Security Considerations
127.0.0.1:<port>is accessible to all local users on the remote host. Mitigate with UNIX socket forwarding (ssh -R /tmp/ocx-$UID/codex.sock:..., mode0700) or per-session bearer token.unauthenticatedLoopbackListenerexposes only data-plane — no access to/api/*, keys, config, or GUI.api.openai.com.umask 077.sh+base64— no Node/Bun/Python required on remote.10. Compatibility with Existing Work
ocx remotecommand could later become a subcommand of a broader remote architecture.nativeAliasmechanism already addresses Desktop visibility. Native upstream support (Codex Desktop model picker filters out models returned from model_catalog_json openai/codex#19694) would eventually make this unnecessary.Offer to Contribute
I have a real-world test environment ready (macOS → Oracle Cloud Linux via Tailscale SSH) and would be happy to:
devonce the design direction is confirmedLooking forward to feedback on the design direction.
Ingwannu commentedon Oct 18, 2026
The RFC makes the two boundaries clearer. Please do not treat hostname acceptance as authorization for a remote multi-user tunnel: the local data-plane listener's loopback contract does not identify users on the remote host. A private directory/socket or an explicitly scoped admission mechanism needs an actual client-compatible design;
GatewayPortsbehavior and remote loopback binding must be verified, not inferred from the shortssh -Rexample.Reuse current Remote Link SSH/config/ownership primitives rather than adding an independent tunnel supervisor. For remote file ownership, discover the actual app-server CODEX_HOME (an interactive shell can disagree), journal owned fields with compare-before-restore, refuse occupied/foreign managed paths, and preserve user edits. The suggested shell/stdin snippet and destructive cleanup are design sketches, not an accepted atomic rollback implementation. Catalog/cache changes are additional writes and require their own ownership and recovery contract.
Native aliases can address picker visibility but do not remove the quota composer gate tracked in #6196 or prove remote execution/admission. Draft #7273 is only an alias/helper slice; it is not the managed setup/restore flow. Keep a tunnel outage from silently rerouting or restoring native billing, and verify exact client-visible failure without promising a local HTTP 503 when no remote listener exists to generate one. A first offline Draft PR should demonstrate manifest/rollback and transport ownership with injected SSH/files; actual remote injection and owner UI acceptance remain separate.