Unofficial mobile handoff for local CLI agent sessions.
Codoxear runs a small web server on your computer and exposes a phone-friendly UI for continuing the same live CLI agent session from mobile. Your environment stays local (filesystem, tools, credentials). The phone is a view/controller.
Currently supported agent backends:
- Codex
- Pi
Pi support covers both web-owned sessions started from Codoxear and brokered terminal-owned sessions started via piox. Codoxear talks to Pi through a pi-rpc broker sidecar, surfaces live UI requests and streamed assistant output, and keeps bridge-sent prompts buffered while Pi is compacting. It still does not attach to an arbitrary already-running interactive Pi TUI process unless that session was launched through the Codoxear broker path.
Name: "codoxear" = "codex dogear" (dog-ear a page so you can pick up where you left off), meaning you can seamlessly continue the same work from different devices.
Not affiliated with OpenAI or the Pi Coding Agent project. "Codex" and "Pi" are referenced only for CLI compatibility.
Supported:
- Linux (uses
/proc, PTYs) - macOS (uses
lsof/pgrep, PTYs)
Not supported:
- Windows (no POSIX PTY/termios model; use WSL2 if you want a Linux environment)
Requires Python 3.10+.
Install Codoxear (installs codoxear-server and codoxear-broker):
python3 -m pip install .
If you are running from a source checkout and want the latest frontend bundle, build the web app before starting the server:
cd web && npm install && npm run buildnpm run buildwrites the production bundle toweb/dist/and copies it intocodoxear/static/dist/, which is whatcodoxear-serverserves
-
Create
.env:- Copy
.env.exampleto.env - Set
CODEX_WEB_PASSWORD - Codoxear reads
.envfrom your current working directory
- Copy
-
Start the server:
codoxear-server- Default bind:
::(IPv6, usually reachable on LAN) - Default port:
8743
For Pi support, make sure
piis installed and available inPATHon the same machine that runs the server. -
Add separate wrappers for terminal-owned brokered sessions (zsh/bash function, not an alias):
Never wrap or replace
codex()orpi()themselves. Web-owned sessions launch the underlying CLI directly, so wrapping the original command to callcodoxear-brokercan recurse back into the broker and create an unbounded session-spawn loop.Add to
~/.zshrcor~/.bashrc:codox() { codoxear-broker -- "$@" } piox() { CODEX_WEB_AGENT_BACKEND=pi codoxear-broker -- "$@" }
Restart your shell or
sourceyour rc file. -
Use
codoxfor terminal-owned Codex sessions andpioxfor terminal-owned Pi sessions when you want them registered with Codoxear. Leave plaincodexandpiunwrapped. -
On your phone, open
http://<your-computer>:8743, enter the password, and select the session.The New session dialog can start either a Codex-backed session or a Pi-backed session. Codex remains the default. The dialog also has a Focus tab for pinning a manual shortlist of sessions you want to jump back to quickly.
-
(Optional) Enable Harness mode for a session:
- Click the Harness icon in the top bar, toggle it on, tune cooldown minutes and injection count, and edit the optional extra request.
- Harness runs in the server process (not the browser tab), so it continues even if you close the web page.
- Settings are per session; each injection decrements the remaining count and harness turns itself off at zero. Enabled sessions show a
harnessbadge in the sidebar.
If you want browser notifications or iOS Web Push, use HTTPS instead of plain http://<host>:8743.
The simplest setup is Tailscale Serve on port 8443:
tailscale serve --bg --yes --https=8443 http://127.0.0.1:8743Then open Codoxear at:
https://<device>.<tailnet>.ts.net:8443/
Example:
https://yiwen-workstation.tail0de6f7.ts.net:8443/
Notes:
- Browser notification APIs require a secure context (
https://...orhttp://localhost). - iOS Web Push requires an installed Home Screen web app on HTTPS; a normal Safari tab is not enough.
- Tailscale-issued HTTPS works for the
*.ts.netname, not for a bare local hostname.
If you run Codoxear as a user systemd service, you can attach Tailscale Serve to the same lifecycle with:
[Service]
ExecStartPost=/usr/bin/tailscale serve --bg --yes --https=8443 http://127.0.0.1:8743
ExecStopPost=-/usr/bin/tailscale serve --bg --yes --https=8443 offThen reload and restart:
systemctl --user daemon-reload
systemctl --user restart codoxear-server.service
tailscale serve status- Desktop Linux: start Codex or Pi in your GUI terminal emulator, then continue the same live session on your phone or a laptop browser.
- Headless Linux: start Codex or Pi inside
tmux, then attach from your phone or a laptop browser. This avoids using a mobile terminal emulator for TUI interaction (for example Termius). - Web-owned sessions: start a new Codex or Pi session from the Codoxear UI, use it from mobile, and kill it from the UI when finished.
- Web-owned tmux sessions: start a new Codex or Pi session from the Codoxear UI with
Create in tmuxenabled to run it inside tmux sessioncodoxearfor shell-side observability.
Codoxear shows three kinds of sessions:
- Terminal-owned: sessions started from your local terminal via
codoxorpiox(the broker wrappers). They are markedTin the UI. - Web-owned: sessions started from the Codoxear UI ("New session"). They are marked
Win the UI. - Web-owned tmux: sessions started from the Codoxear UI with
Create in tmuxenabled. They are marked with the tmux split-pane icon in the UI and run under tmux sessioncodoxear.
The current UI offers Delete for all session kinds. Delete sends a shutdown request to the underlying broker, so deleting a terminal-owned session also stops the corresponding terminal session.
Live Pi sessions expose two identifiers:
session_id: durable session identity used by the UI, history, and persisted page stateruntime_id: the current live broker/runtime handle for a running session
This split lets a resumed Pi session keep one durable identity while the live runtime sidecar changes.
If you start a web-owned session and later want to continue it in your terminal while keeping it registered with Codoxear, use the matching backend workflow: Codex sessions resume through codox ..., Pi sessions through piox ... or plain pi --session <session-file> if you want to continue the same Pi session file directly.
For Pi, Codoxear separates durable conversation identity from the currently running broker process:
- A live Pi session has one durable
session_idand one currentruntime_id. - The durable conversation lives in the Pi JSONL session file under
PI_HOME. - The live broker sidecar owns the current socket, tmux metadata, bridge queue, live UI state, and streamed events.
- If that live runtime exits and the session is resumed later, Codoxear keeps the same durable
session_idand discovers a newruntime_id.
This produces four common states:
- terminal-owned live Pi session: started through
piox; Codoxear discovers the broker sidecar and maps its live runtime back to the durable Pi session - web-owned live Pi session: started from the New session dialog; Codoxear creates both the durable Pi session file and the live runtime
- historical Pi session: no live runtime exists; Codoxear reads title, cwd, and message history from the durable session file and SQLite-backed page state
- resumed Pi session: a historical session is launched again; the old durable
session_idstays stable while a newruntime_idbecomes active
When the UI targets a Pi session, route selection follows that split:
- history- and page-state operations target the durable
session_id - live polling, bridge sends, live UI requests, and interrupt/shutdown flow through the current
runtime_id - historical Pi rows can be reopened into a fresh live runtime without losing alias, Focus state, hidden state, or other persisted page metadata
For Pi pi-rpc sessions, Codoxear shows more than the durable JSONL history:
- live streamed assistant text before it lands in the session file
- live browser UI requests such as AskUser prompts
- compact machine-trace rows for tool, reasoning, and todo snapshots
- bridge transport/system events, including buffered prompts during compaction and visible compaction start/end events
- model context usage derived from Pi token updates
Browser-submitted prompts to a busy Pi session do not go straight into Pi's internal queue. Codoxear keeps its own outbound bridge queue and drains it only when the broker reports the session is ready. During compaction, requests stay buffered instead of failing immediately.
Codoxear cannot drive Codex confirmation prompts in default mode or plan mode from the browser UI.
For full remote interaction, run Codex in YOLO mode so confirmations do not block on interactive terminal prompts.
Codex does not always materialize (open) the new rollout-*.jsonl file immediately after /new. Codoxear tracks the active rollout by scanning the Codex process tree for open rollout-log file descriptors, so the UI may show the session as pending until the first prompt is sent and the rollout file is created/opened.
This project intentionally keeps security out of scope. It provides password gating only and does not provide TLS.
Assume anyone who can reach the port can:
- observe traffic (including the password)
- modify traffic
Use your own secure channel (VPN, SSH port-forward, reverse proxy with TLS) if you need network security.
Set these in .env (or in the process environment):
CODEX_WEB_PASSWORD(required)CODEX_WEB_HOST(default::)CODEX_WEB_PORT(default8743)CODEX_WEB_URL_PREFIX(default empty). Example:/codoxearserves the UI at/codoxear/and the API under/codoxear/api/*.CODEX_WEB_DEFAULT_AGENT_BACKEND(defaultcodex) - default backend tab for new web-owned sessionsCODEX_HOME(default~/.codex)CODEX_BIN(defaultcodex)PI_HOME(default~/.pi)PI_BIN(defaultpi)CODEX_WEB_COOKIE_TTL_SECONDS(default2592000, 30 days)CODEX_WEB_COOKIE_SECURE(default0; set to1behind HTTPS)CODEX_WEB_HARNESS_SWEEP_SECONDS(default2.5)CODEX_WEB_QUEUE_SWEEP_SECONDS(default1.0)CODEX_WEB_QUEUE_IDLE_GRACE_SECONDS(default10.0)CODEX_WEB_DISCOVER_MIN_INTERVAL_SECONDS(default1.0)CODEX_WEB_METRICS_WINDOW(default256)CODEX_WEB_FILE_READ_MAX_BYTES(default2097152)CODEX_WEB_FILE_HISTORY_MAX(default20)CODEX_WEB_GIT_DIFF_MAX_BYTES(default819200)CODEX_WEB_GIT_DIFF_TIMEOUT_SECONDS(default4.0)CODEX_WEB_GIT_CHANGED_FILES_MAX(default400)CODEX_WEB_FD_POLL_SECONDS(default1.0) - how often the broker scans/procto detect the activerollout-*.jsonl
Runtime state is stored under ~/.local/share/codoxear by default (legacy ~/.local/share/codex-web is no longer used). Override this with CODOXEAR_APP_DIR when you want an isolated app dir for another deployment or test instance.
Durable page state is stored in SQLite under the app dir. Session conversation content remains in the backend JSONL logs.
Backend-specific session logs live under the backend home:
- Codex:
~/.codex/sessions/rollout-*.jsonl - Pi:
~/.pi/agent/sessions/*.jsonl
- Start the Python server:
python3 -m codoxear.server - Start Vite:
cd web && npm install && npm run dev - Open the Vite URL for frontend work;
/api/*is proxied to Python. - Build production assets with
cd web && npm run build.
MIT, see LICENSE.