remote-control daemon stays alive but device disappears from ChatGPT mobile app until app-server restart
What version of Codex CLI is running?
0.131.0
What subscription do you have?
chatGPT plus
Which model were you using?
gpt-5.5 low
What platform is your computer?
Linux 6.18.29+rpt-rpi-2712 aarch64 unknown
What terminal emulator and version are you using (if applicable)?
chatGPT on iPhone (chatGPT version:1.2026.125)
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1779160115s since unix epoch",
"overallStatus": "warning",
"codexVersion": "0.131.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is running",
"details": {
"control socket": "/home/twin_supersport/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "/home/twin_supersport/.codex/app-server-daemon",
"mode": "persistent",
"pid file": "/home/twin_supersport/.codex/app-server-daemon/app-server.pid (file)",
"settings": "/home/twin_supersport/.codex/app-server-daemon/settings.json (file)",
"status": "running",
"update-loop pid file": "/home/twin_supersport/.codex/app-server-daemon/app-server-updater.pid (file)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "/home/twin_supersport/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "/home/twin_supersport/.codex",
"config.toml": "/home/twin_supersport/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "/home/twin_supersport",
"enabled feature flags": "shell_tool, unified_exec, shell_snapshot, terminal_resize_reflow, sqlite, memories, hooks, enable_request_compression, multi_agent, apps, tool_search, tool_suggest, plugins, plugin_hooks, in_app_browser, browser_use, browser_use_external, computer_use, plugin_sharing, image_generation, skill_mcp_dependency_install, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, personality, fast_mode, tui_app_server, workspace_dependencies",
"feature flag overrides": "memories=true, goals=true",
"feature flags enabled": "30",
"log dir": "/home/twin_supersport/.codex/log",
"mcp servers": "0",
"model": "gpt-5.5",
"model provider": "openai",
"sqlite home": "/home/twin_supersport/.codex"
},
"remediation": null,
"durationMs": 0
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "/home/twin_supersport/.local/bin/codex",
"PATH codex #2": "/usr/local/bin/codex",
"PATH codex #3": "/home/twin_supersport/.local/bin/codex",
"PATH codex entries": "3",
"current executable": "/home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl/codex",
"install context": "standalone (unix, release /home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl, resources /home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl/codex-resources)",
"managed by bun": "false",
"managed by npm": "false",
"managed package root": "not set"
},
"remediation": null,
"durationMs": 1
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "no MCP servers configured",
"details": {},
"remediation": null,
"durationMs": 0
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars": "none"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 404)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 106
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "2 IPv4, 2 IPv6, first IPv6",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"models etag present": "true",
"provider name": "OpenAI",
"proxy env vars": "none",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 806
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running standalone on linux-aarch64",
"details": {
"commit": "unknown",
"current executable": "/home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl/codex",
"install method": "standalone (unix, release /home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl, resources /home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl/codex-resources)",
"platform": "linux-aarch64",
"version": "0.131.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search command": "/home/twin_supersport/.codex/packages/standalone/releases/0.131.0-aarch64-unknown-linux-musl/codex-resources/rg",
"search command readiness": "file exists",
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "Never",
"codex-linux-sandbox helper": "/home/twin_supersport/.codex/tmp/arg0/codex-arg0XLk5Ad/codex-linux-sandbox",
"execve wrapper helper": "/home/twin_supersport/.codex/tmp/arg0/codex-arg0XLk5Ad/codex-execve-wrapper",
"filesystem sandbox": "unrestricted",
"network sandbox": "enabled"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "/home/twin_supersport/.codex (dir)",
"active rollout files": "13 files, 93567037 total bytes, 7197464 average bytes",
"archived rollout files": "1 files, 61797 total bytes, 61797 average bytes",
"log DB": "/home/twin_supersport/.codex/logs_2.sqlite (file)",
"log DB integrity": "ok",
"log dir": "/home/twin_supersport/.codex/log (dir)",
"sqlite home": "/home/twin_supersport/.codex (dir)",
"standalone release cache": "1 entries in /home/twin_supersport/.codex/packages/standalone/releases",
"state DB": "/home/twin_supersport/.codex/state_5.sqlite (file)",
"state DB integrity": "ok"
},
"remediation": null,
"durationMs": 174
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "warning",
"summary": "width 57 cols - output may wrap (recommended >=80)",
"details": {
"SSH_CLIENT": "present",
"SSH_CONNECTION": "present",
"SSH_TTY": "present",
"TERM": "xterm-256color",
"color output": "disabled (stdout is not a terminal)",
"effective locale": "en_GB.UTF-8",
"stderr is terminal": "true",
"stdin is terminal": "true",
"stdout is terminal": "false",
"terminal": "unknown",
"terminal size": "57x28"
},
"issues": [
{
"severity": "warning",
"cause": "width 57 cols - output may wrap (recommended >=80)",
"measured": "57 x 28",
"expected": ">= 80 columns",
"remedy": "resize the window to at least 80 columns",
"fields": [
"terminal size"
]
}
],
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "ok",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.131.0",
"check for update on startup": "true",
"dismissed version": "0.121.0",
"last checked at": "2026-05-18T21:50:35.090578304Z",
"latest version": "0.131.0",
"latest version status": "current version is not older",
"update action": "standalone installer",
"version cache": "/home/twin_supersport/.codex/version.json"
},
"remediation": null,
"durationMs": 87
}
}
}
What issue are you seeing?
Environment:
- Raspberry Pi 5 (ARM64 Linux)
- Codex standalone install (rust-v0.131.0)
- ChatGPT iOS app
- Remote Control enabled
- Previously migrated from npm-managed Codex to standalone-managed Codex
Problem:
After some time, the Codex node disappears from the ChatGPT mobile app connection list, even though the local daemon and app-server are still running.
Observed behavior:
- codex remote-control returns:
{
"status": "alreadyRunning"
}
- ps aux | grep app-server still shows:
codex app-server --remote-control
codex app-server daemon pid-update-loop
- Unix socket remains alive:
~/.codex/app-server-control/app-server-control.sock
However, the device no longer appears in the ChatGPT mobile app’s Remote Connections list.
Workaround:
The node becomes visible again only after restarting the app-server:
pkill -f app-server
codex remote-control
After restart, the output changes to:
{
"status": "bootstrapped",
"remoteControlEnabled": true
}
and the device immediately reappears in the ChatGPT mobile app.
Observation:
This appears to be a mismatch between:
- local daemon/app-server liveness
- remote relay/discovery registration state
alreadyRunning may not be re-advertising the node to the relay/discovery layer after the registration expires or disconnects.
Additional notes:
- The issue was reproduced multiple times.
- Local CLI functionality continues to work normally even while the device is missing from the mobile app.
- This was observed after migrating from npm-managed Codex to standalone-managed Codex using the official installer.
What steps can reproduce the bug?
- Install standalone Codex (
rust-v0.131.0) on Raspberry Pi 5 (ARM64 Linux) using the official installer.
- Enable remote control:
codex remote-control
- Confirm the node appears in the ChatGPT iOS app under Remote Connections.
- Leave the daemon running for some time (the disconnect happened multiple times during normal use).
- Eventually, the device disappears from the ChatGPT mobile app connection list.
- On the Raspberry Pi, verify that the daemon is still alive:
ps aux | grep app-server
The following processes are still running:
- codex app-server --remote-control
- codex app-server daemon pid-update-loop
- Running:
codex remote-control
returns:
{
"status": "alreadyRunning"
}
but the device still does not appear in the mobile app.
- Restarting the app-server fixes the issue:
pkill -f app-server
codex remote-control
- After restart, the output changes to:
{
"status": "bootstrapped"
}
and the device immediately reappears in the ChatGPT mobile app.
What is the expected behavior?
The remote-control node should remain visible and reconnectable from the ChatGPT mobile app as long as the local app-server daemon is still running.
If the relay/discovery registration expires or disconnects, the daemon should automatically re-advertise itself without requiring a manual app-server restart.
Running:
codex remote-control
while the daemon is already alive should also restore remote discoverability if the node has disappeared from the mobile app.
Additional information
Additional observations:
- The issue started after migrating from npm-managed Codex to the standalone-managed install using the official installer.
- The old npm-managed app-server had to be manually terminated before standalone remote-control worked correctly.
- The issue appears to affect only remote discoverability from the ChatGPT mobile app. Local CLI functionality continues to work normally.
- The daemon and unix socket remain alive even while the node disappears from the mobile app.
- Running inside tmux was initially used, but the same issue was later observed outside tmux as well.
- The environment uses Tailscale and mobile app remote access, so network transitions (WiFi/mobile network changes, idle reconnects, etc.) may possibly contribute.
- The issue was reproduced multiple times.
2026/05/27 postscript
The current iOS Add SSH Host flow appears to require password authentication.
This prevents connecting to hosts where PasswordAuthentication is disabled,
which is standard for hardened SSH setups.
Even when the host is reachable only through a VPN/Tailscale network,
I do not want to enable password authentication just to use Codex mobile.
The macOS Codex App already supports key-based SSH workflows, so it would be
very useful if the iOS app supported either:
- importing/selecting a private key,
- using iOS Keychain / Files-provided SSH keys,
- reusing SSH hosts configured on the paired macOS Codex App,
- or showing SSH remote projects from the paired desktop host in the mobile app.
15 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Solution: openai/codex #23403 — Remote-Control Daemon Stays Alive but Device Disappears from ChatGPT Mobile App
Issue
After some time, the Codex node disappears from the ChatGPT mobile app's Remote Connections list, even though the local daemon and app-server are still running. Only restarting app-server restores visibility.
Root Cause Analysis
Primary Cause: Relay/Discovery Registration Expiry Without Re-Advertise
The codex remote-control architecture has three components:
app-server --remote-control) — runs on the deviceThe bug: the device registration with the relay expires (likely due to WebSocket disconnect, token refresh failure, or idle timeout), but the local daemon's
alreadyRunningcheck prevents re-advertising:The daemon assumes it's already registered and doesn't attempt to re-advertise.
Evidence Supporting This:
pkill -f app-server && codex remote-control→{ "status": "bootstrapped" }→ device reappearsps aux | grep app-servershows processes runningFix Options
Option A: Heartbeat + Re-Advertise on Registration Expiry (Recommended)
File:
codex-rs/app-server/src/remote_control.rs(approximate)Add a periodic heartbeat that validates relay registration:
Pros: Fixes root cause, automatic recovery without restart
Cons: Adds periodic network calls to relay
Option B: Make
codex remote-controlIdempotent with Re-AdvertiseFile:
codex-rs/cli/src/commands/remote_control.rsWhen
alreadyRunningis detected, also check relay registration:Pros: Simple change, makes CLI command more robust
Cons: Requires user to manually run
codex remote-controlto trigger re-advertiseOption C: WebSocket Reconnect with State Recovery
File:
codex-rs/app-server/src/relay_connection.rsImplement proper WebSocket reconnect logic that re-registers on reconnect:
Pros: Handles the network transition case properly
Cons: More complex, requires understanding of relay protocol
Recommendation
Implement Option A (heartbeat + re-advertise) as the primary fix, with Option B (idempotent CLI) as an immediate improvement. Option C should be implemented if WebSocket-level reconnect is needed.
Impact
Cross-Selling Opportunities
I’m seeing the same behavior on a Linux x86_64 box with Codex 0.132.0.
Setup:
@openai/codexCODEX_HOME=/home/admin/.codexcodex app-server --remote-control --listen unix://Observed behavior:
codex app-server daemon versioncontinues to return successfullySo this looks like the remote-control relay/discovery/websocket state can go stale independently of local daemon liveness.
This looks like a good example of why remote-control liveness needs two separate states:
alreadyRunningonly proves the first. For mobile users, the second is what matters operationally. A robust status surface probably needs to expose last successful relay heartbeat/registration time, remote-visible device id, and a "re-advertise" path that is safe to run while the daemon is alive.In practice the UX should distinguish "the CLI process is healthy but not discoverable from mobile" from "the local app-server is down". Otherwise users will keep treating a dead discovery registration as a local daemon crash and restarting more than necessary.
+1 this is currently unusable. Device disappears after a few idle minutes despite the daemon being up and reporting healthy
Additional isolation result:
I deleted and reinstalled the ChatGPT iOS app, then logged in again.
However, the same transient behavior still occurs:
This suggests the issue is unlikely to be caused only by stale local cache on the iPhone.
Additional local environment isolation:
codex doctorreports:readlink -f $(which codex)points to:~/.codex/packages/standalone/releases/0.134.0-aarch64-unknown-linux-musl/bin/codexSo if the device still disappears from the ChatGPT iOS app, npm/standalone installation mixing and stale iPhone local app cache are unlikely to be the root cause.
This points more toward server-side/account-level session/device snapshot state, or remote-control relay/discovery/presence reconciliation, rather than only local iOS app cache.
This was actually fixed for me with 0.134.0
It was not fixed for me in 0.134.0 version, this issue still happens for me
I'm on 135 and still seeing this.
Still getting this on 135
0.137.0 CLI, ChatGPT authenticated, refresh -> WebSocket connection -> immediate close frame from server. Works on the app side. Possibility of an issue specific to the CLI's WebSocket implementation.
Things seem fixed in the latest version to me
I'm envious. On my mac, it works in the app (Electron/Chromium WebSocket), but in the CLI (tungstenite/rustls), the connection is immediately dropped by the server. It looks like it's working on the surface, but if you follow the logs and processes, it's clear there is a problem. There is nothing I can do about it on the user side.
I tested a narrow fix for the reconnect case described here, specifically the case where the remote-control daemon/app-server still appears alive locally, but the mobile app remains stuck waiting for the desktop after the mobile app is backgrounded or loses its session.
Branch:
https://github.com/markrowan/codex/tree/fix/remote-control-reenable-reconnect
Commit:
https://github.com/markrowan/codex/commit/c2933b2064
The change makes
remoteControl/enablerequest a fresh websocket connection when remote control is alreadyConnected, instead of treating that call as a no-op. In my local testing, this allowed the mobile client to recover the environment without manually stopping and restarting remote control.A couple of caveats:
openai/codexbecause GitHub requires collaborator permission.So this appears to resolve the specific reconnect/no-op case we reproduced, but I have not validated broader remote-control lifecycle cases beyond that.
Closing as a duplicate of #22898.