CodeCarry speaks the Codex app-server JSON-RPC protocol over WebSocket. It supports thread history and runtime status, streaming items, new messages, mid-turn steering, interruption, approvals, questions, and reconnect.
Codex TUI (--remote unix://) ─┐
├─ shared Codex app-server daemon
CodeCarry → HTTPS/WSS proxy → authenticated bridge → Unix control socket
On the server, start the managed daemon if it is not running, then attach the terminal to that daemon:
codex app-server daemon start
codex --remote unix:// -C /root/CODE/your-project
# Open an existing thread in the same daemon:
codex --remote unix:// resume THREAD_IDIn CodeCarry, add a Codex server, enter its wss:// address and bearer token,
connect, then open a thread. Sending while a turn is running calls turn/steer
with the current turn ID; otherwise it calls turn/start. Stop calls
turn/interrupt. Closing the mobile connection does not stop the agent.
An independently started app-server is a different live runtime, even when it
shares CODEX_HOME. A standalone codex exec process is not automatically
controlled through this bridge. Use the terminal attachment above for shared
live control. The bridge does not scrape terminal output or inject keystrokes.
Requires Python 3.10+ and the dependency in requirements.txt. The listener is
restricted to 127.0.0.1; terminate TLS at the reverse proxy. Every WebSocket
handshake requires Authorization: Bearer <token>. A token is never accepted
in a URL. Browser Origin requests are rejected. Use a randomly generated token
and a mode-0600 file outside this repository.
python3 -m pip install -r requirements.txt
python3 codex_bridge.py \
--socket /root/.codex/app-server-control/app-server-control.sock \
--token-file /path/to/private/token \
--port 18767The included systemd unit documents the like_server paths. Adjust paths and
the service user before deploying elsewhere. The bridge owns only its client
connections. It never starts, restarts, resumes, or stops the daemon or agent
threads. A missing daemon closes the client with retryable status 1011.
Reverse-proxy the WebSocket endpoint, preserve Authorization, disable buffering, and allow long-lived connections:
location / {
proxy_pass http://127.0.0.1:18767;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Authorization $http_authorization;
proxy_set_header Host $host;
proxy_buffering off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}On like_server, codecarry-codex-bridge.service uses port 18767 and
codex.wuxie233.com. It reuses the existing token file at
/root/.config/opencode/secrets/codex-app-server.token. The old independent
listener on port 18766 is not stopped by this deployment. Rollback can point
the public proxy back to that port, then stop only the bridge service.
The bridge-to-phone WSS connection and the daemon-to-model connection are
separate. A successful list or turn/start response does not prove that the
model endpoint is reachable. Verify an assistant message delta and a completed
turn, including a mid-turn steering request when checking mobile control.
On like_server, CPA uses Mihomo port 7897. The shared daemon is owned by the
user unit codex-app-server-bootstrap.service; its 30-cpa-proxy.conf drop-in
sets HTTP(S) proxy variables to http://127.0.0.1:7897, ALL_PROXY to
socks5h://127.0.0.1:7897, and keeps localhost in NO_PROXY. The shell CLI wrapper
and the fallback unit also need the proxy environment. Setting variables on
the bridge or in an SSH shell cannot update an already-running daemon.
Apply environment changes during an idle maintenance window, after checking all loaded thread statuses, then reconnect and verify a real model reply. Keep the existing login and model provider when repairing network egress.
Full history is returned as a single JSON-RPC message. The bridge accepts up to 64 MiB per daemon response (including tool output and inline images), while client requests remain limited to 16 MiB. The daemon receive queue is limited to four frames to bound buffering. Responses beyond the limit close with code 1009 and a safe size-limit diagnostic; abnormal daemon closure becomes 1011 instead of an apparently normal client disconnect. No history is truncated to fit the transport.
Updating the Android APK alone does not update a deployed bridge. Deploy
codex_bridge.py and restart the owning bridge service in an approved
maintenance window; existing mobile sockets reconnect. The daemon and its
running threads do not need a restart.
python3 -m unittest -vAndroid tests cover JSON-RPC correlation, steering, approvals, connection generations, reconnect, event reduction, thread UI state, URL validation, and server persistence. Newly created blank threads keep their start response on the current connection because no durable rollout exists before the first turn. Thread lists explicitly include all model providers.
Protocol reference: https://developers.openai.com/codex/app-server/.
A loaded thread can have ephemeral: true and no saved rollout. Current
app-server implementations may reject thread/resume for that thread with
no rollout found for thread id, even while thread/read with
includeTurns: false succeeds. excludeTurns: true does not bypass the resume
history requirement; reading metadata does not establish a chat subscription.
CodeCarry confirms the ephemeral flag with a bounded metadata read before ending automatic recovery attempts for that recovery cycle. It keeps cached messages visible and explains the unsupported recovery, without enabling thread controls. Ordinary missing-rollout failures remain retryable because a newly started thread may not have flushed its history yet. Continue an ephemeral conversation in its original client; CodeCarry does not manufacture a replacement conversation or reconstruct missing history.