Mobile Remote Control cannot connect to Linux SSH remote project on Codex 0.133+/0.134, works after downgrading remote host to 0.130.0
What version of the Codex App are you using?
Codex App for macOS: 26.519.81530 (build 3178)
Desktop bundled CLI:
codex-cli 0.133.0
Desktop app-server:
appServerVersion: 0.135.0
managedCodexVersion: 0.135.0
cliVersion: 0.133.0
What subscription do you have?
ChatGPT Pro
What platform is your computer?
Desktop host:
Darwin 25.5.0 arm64 arm
Linux SSH remote host that fails on newer Codex versions:
Linux 6.17.0-29-generic x86_64 x86_64
WSL SSH remote host that works:
Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 x86_64
What issue are you seeing?
ChatGPT mobile Remote Control can connect to my paired macOS Codex Desktop host, and it can control SSH remote projects on WSL. However, it cannot connect to an SSH remote project on a Linux physical host when that remote host runs Codex CLI/app-server 0.133.0 or 0.134.0.
On mobile, selecting the Linux SSH remote project shows "Unable to connect".
The same Linux remote project works from Codex Desktop. SSH connectivity is healthy, the remote project path exists, and the remote app-server/proxy processes are running.
Downgrading only the remote Linux host to @openai/codex@0.130.0 immediately restores mobile Remote Control for the same host, same SSH route, same macOS Desktop host, same mobile pairing, and same remote project path.
This looks like a regression in the SSH remote app-server/proxy path used by mobile Remote Control after 0.130.0.
What steps can reproduce the bug?
- Pair ChatGPT mobile with Codex App for macOS.
- Add two SSH remote hosts/projects to the macOS Codex App:
- WSL2 remote host running
@openai/codex@0.130.0 - Linux physical remote host running
@openai/codex@0.133.0or0.134.0
- Confirm both SSH remote projects work from Codex Desktop.
- Confirm the Linux host has Remote Control enabled:
``text``
remote_control = true
- Confirm the Linux remote app-server/proxy processes are running.
- Open ChatGPT mobile -> Codex Remote Control.
- Select the paired macOS Codex Desktop host.
- Select the Linux SSH remote project.
Observed:
Unable to connect
- Downgrade only the Linux remote host:
``bash``
npm install -g @openai/codex@0.130.0
pkill -TERM -f 'codex.*app-server'
- Let Codex Desktop/mobile reconnect to the same Linux remote project.
Observed after downgrade:
Mobile Remote Control works again.
What is the expected behavior?
Mobile Remote Control should be able to connect to SSH remote projects through the paired macOS Codex Desktop host when the same remote project already works in Codex Desktop.
The mobile connection should not fail only because the remote Linux host is running 0.133.0 or 0.134.0, especially when the same setup works after downgrading the remote host to 0.130.0.
Additional information
Diagnostics already checked:
- Mobile pairing is not globally broken: WSL SSH remote projects still work from mobile.
- The macOS Codex Desktop host is online and controllable from mobile.
- The Linux remote host is reachable over SSH.
- The Linux remote project works from Codex Desktop.
- The Linux remote project path exists.
- Recreating the Linux remote project did not fix the issue.
- Switching the Linux remote project path to an ASCII-only path did not fix the issue.
- Restarting the macOS app-server did not fix the issue.
- Downgrading the Linux remote host from
0.133.0/0.134.0to0.130.0did fix the issue.
Version evidence:
Failing:
Linux remote host @openai/codex@0.134.0 -> mobile unable to connect
Linux remote host @openai/codex@0.133.0 -> mobile unable to connect
Working:
Linux remote host @openai/codex@0.130.0 -> mobile works
Current working Linux remote state:
codex-cli 0.130.0
@openai/codex@0.130.0
remote_control = true
remote project path: /home/atk/codex-workspace
Related issues:
- #23527
- #23062
- #23403
Those issues appear related to mobile Remote Control with SSH remote projects, but this report is specifically about a version regression on the remote Linux app-server/proxy side: 0.130.0 works while 0.133.0 and 0.134.0 fail for the same Linux SSH remote project.
3 Comments
Same on 0.137.0, ubuntu.
Analysis (community)
Thanks for the careful multi-version bisect and the dual-remote control (WSL @ 0.130 works vs Linux physical @ 0.133+ fails). That isolation is unusually helpful.
26.519.81530/ bundled CLI0.133; Linux remote0.133/0.134fail for mobile; Linux remote0.130restores mobile; WSL @0.130works; Desktop can still use the Linux remote; config includesremote_control = true. A later comment reports the same on0.137.0(Ubuntu).remoteControl/statusdumps land.Related: #23699 describes the same process-shape failure more explicitly — manual
codex remote-control startyieldsapp-server --remote-control --listen unix://and mobile works; a Desktop restart yields plainapp-server --listen unix://without--remote-controleven when settings still showremoteControlEnabled: trueand the features list showsremote_controlremoved.Root-cause hypothesis
This looks like a regression in how the remote Linux app-server enables the ChatGPT Remote Control transport, not SSH reachability or macOS mobile pairing.
Enablement cutover (0.130 → 0.131+):
config.features.enabled(Feature::RemoteControl)when[features] remote_control = true(or equivalent). That matches the recovery when only the remote host is pinned to0.130.0.Feature::RemoteControlisStage::Removedincodex-rs/features(key: "remote_control") — always off / ignored as a feature. Enablement moves to runtime options: CLI--remote-control, daemon settings (DaemonSettings.remote_control_enabled/ JSONremoteControlEnabled), and laterRemoteControlStartupMode(EnabledEphemeral|DisabledEphemeral|ResolvePersisted) viastart_remote_control.codex-rs/app-server-daemonpidbackendcommand_args/command_env):remote_control_enabled: true→app-server --remote-control --listen unix://remote_control_enabled: false→app-server --listen unix://andCODEX_INTERNAL_APP_SERVER_REMOTE_CONTROL_DISABLED=1codex-rs/app-servermain): hidden--remote-controlsetsEnabledEphemeral; the disabled env var forcesDisabledEphemeral; otherwiseResolvePersisted. RPCsremoteControl/enable|disable|status/read(see app-server README +remote_control_processor) are the explicit control plane.Why Desktop can work while mobile cannot: Desktop’s SSH remote path can keep a healthy unix-socket app-server / proxy for project control even when the remote-control websocket / enrollment path is disabled. Mobile needs that RC transport (or a clear status) — so a silent capability downgrade to plain
--listen unix://fits “Desktop OK, mobile Unable to connect.”Why 0.130 restores: downgrading reintroduces config-feature auto-enable from
remote_control = true, so the live process regains RC without needing daemonremoteControlEnabled/--remote-control/remoteControl/enable.I may be wrong on the exact Desktop restart command line (daemon vs bare app-server); the invariant that matters is: settings / UI “available from signed-in devices” must not be paired with a live process that has RC disabled.
High-level fix outline (not a PR)
Small, focused, capability-preserving fix — happy to be corrected if Desktop already has a different intended contract:
daemon start/restart/enable-remote-control/codex remote-control start) or equivalent args so the live process includes--remote-controlwheneverremoteControlEnabled(or the UI equivalent) is on. Never replace an RC-capable process with plain--listen unix://(and avoid stampingCODEX_INTERNAL_APP_SERVER_REMOTE_CONTROL_DISABLED=1in that case).[features] remote_control = trueas a one-time signal to set daemonremoteControlEnabled=true/ callremoteControl/enable. Docs should stop implying the removed feature alone enables RC on 0.131+.remoteControl/status/readfields (disabledvsconnecting/connected/errored,environmentId) over opaque mobile Unable to connect when the remote process is RC-disabled.Risks / non-goals
codex remote-control startas the only workaround (works, but brittle across Desktop restarts — see #23699).Feature::RemoteControlas a full feature flag without a deliberate migration plan.I am not opening a PR unless the team invites one — sharing analysis only for alignment.
Test ideas
Failing-first ideas if the team (or an invitee) implements:
DaemonSettings { remote_control_enabled: true }→command_argsinclude--remote-controland do not set the disabled env;false→ opposite (pid backend already has nearby unit coverage for the arg shape).--listen unix://only →remoteControl/status/readis disabled /environmentIdnull; start with--remote-control --listen unix://→ status moves onto connecting/connected path (mock enroll WS as needed).--remote-controlwhile settings still sayremoteControlEnabled: true→ must fail or re-apply the flag (#23699 invariant).[features] remote_control = trueon a post-0.130 binary results in an RC-enabled process after bootstrap/start.psshowsapp-server --remote-control --listen unix://; mobile connects; repeat after Desktop “restart Codex on remote.”Questions for maintainers
codex app-server, and isCODEX_INTERNAL_APP_SERVER_REMOTE_CONTROL_DISABLEDset?remoteControl/status/read, and process argv — does it match plain--listen unix://without--remote-control?environmentIdon Linux, or only through the macOS host’s project list? (#23699 suggests the Android client can hit the remote app-server directly.)npm -gvs standalone~/.codex/packages/ daemon bootstrap)?enable-remote-controlsuccessfully on the Linux host (settings true, process without flag)?installation_id/server_name) break on some Linux hosts independent of enablement?Happy to refine this analysis with any process dumps or correct the approach if the intended contract differs. If the team later wants an invited PR along these lines, I would be glad to help — no pressure either way.
Building on the root-cause analysis above with the exact introducing PR my behavioral "0.130 -> 0.131+ cutover" maps to, plus a unit test that locks the config-ignore as intended. Traced against
mainas of5dd992a(2026-07-24).Regression introducer. The cutover is PR #22578 (
4e368aa2e9, merged 2026-05-14, "enable/disable remote control at runtime, not via features"). Its body states it "reapplies #22386 which was previously reverted", "MarksFeature::RemoteControlas removed and ignores[features].remote_control", and "Updates managed daemon startup to usecodex app-server --remote-control --listen unix://". #22578 touchescodex-rs/features/src/lib.rs(+5) +features/src/tests.rs+app-server-transport/.../remote_control/mod.rs+remote_control_processor.rs+app-server-daemon/.../pid.rs- including theStage::Removed+ config-ignore change (plusenable/disablesurfaces). This is the commit that made[features] remote_control = trueconfig-inert. Split for precision: #22578 introducedenable/disable+ the--remote-controlflag +Stage::Removed;remoteControl/status/readand the extension ofRemoteControlStatusChangedNotification(e.g.server_name) landed in #22877 (merged 2026-05-15, "update remote control APIs for better UX") - the notification itself predates #22877.Full archaeology (for the timeline).
Feature::RemoteControlStage::Removed.Stage::Removed+ config-ignore) and addedremoteControl/enable|disable+ the--remote-controlflag.remoteControl/status/readand extended the existingRemoteControlStatusChangedNotification(e.g.server_name).RemoteControlStartupMode::ResolvePersisted).Not fixed as of
5dd992a.Stage::Removed+ the config-ignore continue.DaemonSettings(codex-rs/app-server-daemon/src/settings.rs:9,#[derive(Default)]) yieldsremote_control_enabled: falseby default, and I found no code path from legacy[features] remote_control = trueintoDaemonSettingsor theenableRPC - i.e. no migration.Two distinct disabled-path mechanisms (do not conflate).
app-server-daemon/src/backend/pid.rs:413-430) startsapp-server --listen unix://(no--remote-control) withCODEX_INTERNAL_APP_SERVER_REMOTE_CONTROL_DISABLED=1env (command_env()returns(REMOTE_CONTROL_DISABLED_ENV_VAR, "1")). This is the daemon-disabled path.--listenwithResolvePersistedand no priorenable:desired_state_from_persisted_enrollment(app-server-transport/src/transport/remote_control/desired_state.rs) resolves a user with no persisted enrollment to Disabled.Which path the OP hit depends on whether remote was started via the managed daemon vs a bare
app-server(the first comment left this open). If via the managed daemon with default settings, the disabled env is set - but asserting that for the OP specifically needs host evidence, so I'd hold the reporter's exact process shape as inferred, not proven.Unit test that locks config-ignore as intended.
codex-rs/features/src/tests.rs:165-174 remote_control_config_is_ignoredassertsfeatures.enabled(Feature::RemoteControl) == falseafterapply_map({remote_control: true}). Any migration fix must update or delete this test - a concrete anchor for maintainers.Source-of-truth pointer.
REMOTE_CONTROL_DISABLED_ENV_VAR = "CODEX_INTERNAL_APP_SERVER_REMOTE_CONTROL_DISABLED"is defined atcodex-rs/app-server-transport/src/transport/remote_control/mod.rs:86-87.