Experimental Remote Mobile Control
June 2, 2026 ยท View on GitHub
This feature is disabled by default. It patches the upstream Codex Desktop main bundle so Linux can try the remote-control host and outbound control flows that upstream currently limits to macOS.
Enable it by adding the feature id to linux-features/features.json before
building:
{
"enabled": [
"remote-mobile-control"
]
}
For the Nix flake build, use the declarative app variant instead because the
git-ignored features.json file is not part of the flake source:
nix run .#remote-mobile-control
Feature-specific Nix outputs are additive. To combine this feature with the Computer Use UI opt-in:
nix run .#computer-use-ui-remote-mobile-control
What it changes:
- Replaces the macOS-only
remote-control-device-key.noderequirement with a Linux JavaScript ECDSA P-256 key provider. - Lets the remote-control Connections UI render on Linux when upstream marks the feature unavailable or withholds the remote-control visibility rollout.
- Keeps the
Control other devicessettings tab reachable on Linux so this desktop can authorize outbound control of another enrolled device. - Refreshes the remote Connections settings state every 5 seconds and immediately after focus, visibility, online, or resume signals.
- Keeps Chrome Browser Use available to remote/mobile controlled sessions when the local Chrome plugin and native host are healthy, and adds a diagnostic when the native browser bridge is not exposed to the session.
- Persists the private key material at
~/.config/codex-desktop/remote-control-device-keys-v1.jsonwith0600file permissions. - Preserves
remote_control = true/features.remote_control = truein the local Codex config instead of letting upstream strip it before app-server startup. - Updates remote-control settings and Codex mobile setup copy so the Linux flow is not described as Mac-only.
- Stages
.codex-linux/cold-start.d/remote-mobile-control, a feature-owned cold-start hook that provisions the upstream managed standalone daemon runtime when it is missing, then starts the managed app-server daemon withremote-control start.
Remote mobile daemon requirement:
The interactive Codex CLI and the remote-control daemon are separate concerns.
You can keep using a Homebrew-installed codex for normal terminal and Desktop
app-server usage, but Android remote control currently expects the upstream
managed standalone daemon runtime at:
~/.codex/packages/standalone/current/codex
If that binary is missing, the feature's cold-start hook runs the upstream
standalone installer with CODEX_INSTALL_DIR pointed at a private bin directory
under ~/.codex/packages/standalone/.bin. That satisfies the managed daemon
layout without changing CODEX_CLI_PATH, creating ~/.local/bin/codex, or
adding PATH blocks to your shell profile.
The hook is launched best-effort in the background by the generic launcher hook
runner. When the system timeout command is available, the installer/start path
is capped by
CODEX_REMOTE_CONTROL_DAEMON_AUTOSTART_TIMEOUT_SECONDS (default 30), so
Desktop cold start is not blocked by network, GitHub, or installer stalls.
When timeout is unavailable, the hook continues the installer/start path in a
background subprocess. Hook output is written to the launcher log.
On NixOS, prefer the flake's Home Manager module instead of the launcher hook:
{
imports = [
inputs.codex-desktop-linux.homeManagerModules.default
];
programs.codexDesktopLinux = {
enable = true;
computerUseUi.enable = true;
remoteMobileControl.enable = true;
remoteControl.enable = true;
};
}
The module installs the remote-mobile package variant and manages
codex-remote-control.service as a user systemd unit running
codex app-server --remote-control --listen unix://. It also sets
CODEX_REMOTE_CONTROL_DAEMON_AUTOSTART_DISABLED=1 so the launcher does not
start a second mutable standalone daemon.
This is compatible with immutable Linux systems such as Bluefin / Universal
Blue because the managed daemon runtime is user-scoped state under
~/.codex/packages/standalone. It does not require dnf, rpm-ostree, host
package layering, or base-OS mutation. The private .bin directory is only a
launcher-owned target for the installer symlink; it is not prepended to the
user's persistent shell PATH.
Set CODEX_REMOTE_CONTROL_RUNTIME_AUTO_INSTALL_DISABLED=1 to disable that
runtime provisioning and only use an already-installed standalone runtime.
To force a specific daemon binary without affecting the interactive CLI, set:
CODEX_REMOTE_CONTROL_CODEX_PATH=/path/to/standalone/codex
To keep Desktop using Homebrew while the daemon uses standalone, set
CODEX_CLI_PATH to the Brew binary and leave
CODEX_REMOTE_CONTROL_CODEX_PATH unset or pointed at the standalone binary.
KDE Plasma smoke check:
Mobile control depends on the Linux Computer Use backend once the host is enrolled. On Plasma/Wayland, verify that the KWin backend is ready after building or installing the package:
./codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-linux doctor
./codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-linux windows
The doctor report should show the KWin window backend, XDG Desktop Portal, and
input checks as ready. The windows report should return "backend": "kwin" with
a non-empty windows list.
Known risks:
- This is not equivalent to macOS Secure Enclave-backed storage. Private key material is file-backed and protected by ordinary user file permissions.
- OpenAI may still reject Linux host enrollment or outbound authorization server-side. This feature only removes local macOS-only blockers in the repackaged app.
- Treat this as experimental account-level remote-control plumbing.
Run the feature tests with:
node --test linux-features/remote-mobile-control/test.js