Remote connections can mix project/thread host state when SSH hosts share HOME
Summary
Codex Desktop remote connections can mix up remote projects and threads when multiple SSH hosts share the same remote $HOME directory.
In this setup, a project can appear under one remote machine in the sidebar, while opening a thread from that project resumes against another machine. The visible symptom is a remote-host mismatch plus Current working directory missing, even though SSH connectivity and the remote Codex app servers are otherwise healthy.
What Happened
I configured multiple SSH remotes in Codex Desktop and opened the same remote project path on different hosts in an HPC/research-style environment.
The app entered a mixed state:
- The sidebar showed the project under the expected remote host.
- Opening an existing thread showed a different remote host in the bottom-right connection badge.
- The composer showed
Current working directory missing/This chat's working directory no longer exists. - Follow-up turns could not proceed normally because the thread was effectively attached to the wrong host/context.
This was not caused by SSH auth failure or a stale remote Codex binary:
- Both SSH hosts were reachable.
- Both
codex app-serverprocesses could run. - Updating the remote Codex CLI and restarting stale app-server processes fixed version/model-list issues, but not this project/thread host mismatch.
Who This Affects
This likely affects users on systems where multiple SSH hosts share the same network home directory, including:
- university/research clusters;
- GPU/login/compute node fleets;
- enterprise Linux environments;
- servers using NFS, AFS, Lustre, or similar shared home mounts.
This is a common pattern in exactly the kinds of remote-first environments where Codex Desktop remote connections are most useful.
Why It Happens
The remote Codex app server appears to use $HOME/.codex as its state root unless CODEX_HOME is explicitly set.
On shared-home systems, different SSH hosts can resolve to distinct machines but still share the same $HOME, so all of these files are shared across hosts:
$HOME/.codex/history.jsonl
$HOME/.codex/session_index.jsonl
$HOME/.codex/state_5.sqlite
$HOME/.codex/archived_sessions/...
That means sessions created or indexed from host A are visible to host B. The desktop UI can then group a project by one host/path while the opened conversation still carries or resolves to another host binding.
A diagnostic that exposed the issue was checking both hosts:
ssh <host-a> 'hostname -f; realpath ~/.codex; df -hT "$HOME"; ls -lh ~/.codex/session_index.jsonl ~/.codex/state_5.sqlite'
ssh <host-b> 'hostname -f; realpath ~/.codex; df -hT "$HOME"; ls -lh ~/.codex/session_index.jsonl ~/.codex/state_5.sqlite'
In the failing case, both hosts reported the same shared home filesystem and the same .codex state files.
Potential Product Remedy
Codex Desktop should not assume that ~/.codex is machine-local when launching or connecting to a remote app server.
A robust fix would be to namespace mutable remote state by remote connection identity, for example by launching the remote app server with:
CODEX_HOME="$HOME/.codex/remotes/<stable-remote-connection-id>"
where <stable-remote-connection-id> is derived from the configured SSH connection identity, such as the connection UUID, user, host alias/resolved hostname, and port.
Important details:
- The namespace should be per remote connection, not just per project path or folder label.
- Project/thread keys should include both
hostIdandremotePath. - Thread resume should validate that the thread's stored host matches the currently selected/attached host.
- If a mismatch is detected, the UI should show a clear message like
This thread belongs to <host>instead of opening it under a different project row. - Auth/config/plugin state may need a migration/copy/symlink strategy so users do not need to re-authenticate separately for every namespaced remote state directory.
A smaller partial fix would be to keep global config/auth shared but split mutable runtime/session stores by remote host:
history.jsonl
session_index.jsonl
state_5.sqlite
archived_sessions/
logs/
shell_snapshots/
Local Hotfix
As a workaround, I set a host-specific CODEX_HOME in the remote shell startup before codex app-server launches:
# In remote shell startup, e.g. ~/.bashrc
_codex_remote_host="$(hostname -s 2>/dev/null || hostname)"
case "$_codex_remote_host" in
<host-a>|<host-b>|<host-c>)
export CODEX_HOME="$HOME/.codex-hosts/$_codex_remote_host"
mkdir -p "$CODEX_HOME" 2>/dev/null || true
;;
esac
unset _codex_remote_host
Then I seeded each host-specific directory with the shared config/auth files needed by the app server:
mkdir -p ~/.codex-hosts/<host>/rules ~/.codex-hosts/<host>/archived_sessions ~/.codex-hosts/<host>/log
cp -p ~/.codex/auth.json ~/.codex/config.toml ~/.codex/AGENTS.md ~/.codex-hosts/<host>/ 2>/dev/null || true
cp -a ~/.codex/rules/. ~/.codex-hosts/<host>/rules/ 2>/dev/null || true
touch ~/.codex-hosts/<host>/history.jsonl ~/.codex-hosts/<host>/session_index.jsonl
After restarting the remote app servers, I verified each process had a distinct CODEX_HOME:
tr '\0' '\n' < /proc/<codex-app-server-pid>/environ | grep '^CODEX_HOME='
Expected:
host A: CODEX_HOME=$HOME/.codex-hosts/<host-a>
host B: CODEX_HOME=$HOME/.codex-hosts/<host-b>
New remote chats then used the correct machine-local state. Existing broken threads may still carry stale host metadata, so the reliable validation path is to create a fresh thread after splitting the state directories.
Expected Behavior
Remote project and thread state should be scoped to the selected remote host/connection. A thread created on host A should not appear resumable as if it belonged to host B solely because both machines share $HOME/.codex.
Actual Behavior
With shared $HOME, host A and host B share Codex remote state. This can cause the sidebar project host, opened thread host, and working directory to disagree.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗