Shared App-Server Socket
August 13, 2026 ยท View on GitHub
This opt-in feature makes the Codex app-server used by Desktop available on a user-private Unix socket. It does not implement, inspect, filter, or translate the app-server protocol.
From an SSH client's point of view, this behaves like an ordinary Codex SSH
app-server connection. The remote codex app-server proxy command still
provides the same stdio/WebSocket byte stream and the same app-server methods,
notifications, approvals, and thread authority. The only difference is that the
proxy attaches to Desktop's existing authority instead of starting a separate
app-server with a separate thread namespace.
Desktop owns one selected Codex CLI child running app-server --listen unix://PATH. Desktop connects through the CLI's stock app-server proxy --sock PATH byte tunnel and its existing WebSocket transport. Other local clients use
the same stock proxy command to attach to the Unix socket and receive the normal
WebSocket /rpc byte stream. Closing Desktop stops the authority.
The launcher uses the official CLI bundled in resources/codex by default.
An explicit CODEX_CLI_PATH remains supported and is preserved by the feature
hook.
The default socket is scoped by Linux app id under XDG_RUNTIME_DIR, preventing
side-by-side Desktop instances from sharing an authority accidentally. Override
it with CODEX_LINUX_APP_SERVER_BRIDGE_SOCKET when a stable path is required.
The Codex app-server creates the socket with user-only permissions. A shell
wrapper may route bare codex app-server proxy SSH sessions to this path.
Keep the socket in a directory accessible only to the owning user. It is a local
control endpoint and must not be exposed directly over TCP or forwarded as a
network service.
Authority startup is serialized by an owner-only lock next to the socket. The lock records the owning Linux process identity, so a later Desktop launch can reclaim it only when that exact process no longer exists. An existing socket is probed before recovery: connectable endpoints and live or unverifiable owners still fail closed, while an unbound socket inode from the dead owner is removed only if its filesystem identity is unchanged. Legacy locks without owner metadata remain protected for 15 seconds, longer than the authority startup timeout, before they can be reclaimed when no socket exists.
The launcher also cleans up a live authority orphaned by a terminated Desktop
process. Cleanup is limited to a same-user codex app-server --listen unix://PATH
process serving the exact locked socket after direct PID 1 adoption or adoption
by a verified same-user systemd --user manager whose own parent is PID 1. Once
the authority is ready, its PID and process-start identity are recorded in the
ownership lock. The lock owner, socket inode, listener identity, command line,
and process start identities are rechecked before signaling it. Unknown
listeners, live Desktop owners, changed identities, and pathnames with multiple
live listener inodes remain untouched. The same cleanup runs after Electron exits
and before a later cold start.
SSH setup
Use a stable socket path when the Desktop instance will be reached over SSH:
export CODEX_LINUX_APP_SERVER_BRIDGE_SOCKET="$HOME/.codex/app-server-control/app-server-control.sock"
codex-desktop
Then place a small codex wrapper earlier in the SSH user's PATH. Set
real_codex to the actual CLI executable, not to the wrapper itself:
#!/usr/bin/env bash
set -eu
real_codex="/absolute/path/to/real/codex"
desktop_socket="$HOME/.codex/app-server-control/app-server-control.sock"
if [ "$#" -eq 2 ] && [ "\$1" = "app-server" ] && [ "\$2" = "proxy" ]; then
exec "$real_codex" app-server proxy --sock "$desktop_socket"
fi
exec "$real_codex" "$@"
The upstream SSH transport normally starts its own authority before invoking the proxy. Configure the remote account's login-shell environment to skip that bootstrap when this wrapper is used:
export CODEX_SSH_SKIP_APP_SERVER_BOOT=true
Put that export in the startup file read by the account's SSH login shell (for
example ~/.profile when that is the active login profile). This is remote
account configuration; setting it only in the local Desktop launcher does not
propagate it through SSH. Use it only for an account whose wrapper is dedicated
to this Desktop-owned socket.
Make the wrapper executable and verify that non-interactive SSH resolves it:
chmod 0755 "$HOME/.local/bin/codex"
ssh host 'command -v codex'
ssh host 'printf "%s\n" "$CODEX_SSH_SKIP_APP_SERVER_BOOT"'
Codex SSH clients can then connect normally; no client-side protocol option or
special method allowlist is required. Only the exact two-argument proxy command
is redirected. Interactive CLI commands and all other subcommands continue to
use the real CLI normally. CODEX_CLI_PATH used to launch Desktop must also
point to the real CLI so Desktop cannot recursively invoke the wrapper.
Enable the feature in the ignored linux-features/features.json file:
{
"enabled": ["shared-app-server-socket"]
}
Then rebuild and launch the app. The feature is disabled by default and does not run independently of Desktop.
Run focused tests with:
node --test linux-features/shared-app-server-socket/test.js
Set CODEX_CLI_PATH to include the stock authority/socket/proxy lifecycle test:
CODEX_CLI_PATH="/absolute/path/to/real/codex" node --test linux-features/shared-app-server-socket/test.js
The feature depends on upstream's current local transport factory, WebSocket
adapter, and app-server proxy command. Bundle drift causes the optional patch
to warn and skip instead of modifying an unknown surface.