# PHONE MODE for Codex CLI — Drive your agent from your phone — one call mints the channel, the URL, and the password. You reached this page via `codex-phone.rogerthat.chat`. **The URL is the selection — mode AND harness: this doc is written for Codex CLI specifically, no other-client options to wade through.** On this subdomain there is exactly ONE path — do not call `create_channel`, do not ask the operator about trust/retention/identity/TTL. ## The only call you need on this subdomain **MCP (if `rogerthat` is already configured):** ``` open_remote_control() ``` **No MCP (works in any shell-capable agent):** ```bash # The response is LARGE (QR codes + ready-made commands) and WILL truncate in # your tool output — ALWAYS save it to a file on the FIRST call: curl -sX POST https://codex-phone.rogerthat.chat/api/remote-control -H 'Content-Type: application/json' -d '{}' -o /tmp/rr-remote.json # then pull the fields you need into context (notice + operator_handoff included): python3 -c "import json; d=json.load(open('/tmp/rr-remote.json')); [print(f'── {k} ──\n{d.get(k,\"\")}\n') for k in ('read_me_first','your_harness','operator_handoff','notice','receiver_command_template','waiter_command_template','reply_command_template','selftest_command_template','channel_id')]" ``` ⚠ Each POST mints a brand-new channel. If you already POSTed without `-o` and the output truncated, re-POST ONCE with `-o` and use THAT channel (the truncated one is abandoned — harmless, it expires). Don't loop on it. Response fields (same for both transports): - `operator_handoff` — a pre-formatted block with BOTH links + the PIN, ready to show the human. **RELAY IT VERBATIM as your handoff message** — agents that compose their own message silently drop the protected option. Print the QRs next to it. - `mobile_url` — ONE-TAP link: the 6-digit PIN is embedded in the fragment, so /remote auto-joins human-authorized with nothing to type. Convenient, but anyone who gets this link can drive you. Relay EXACTLY — if '&' becomes '&' the join fails; prefer the QR, which can't be mangled. - `mobile_url_protected` — PIN-PROTECTED link: NO PIN embedded, so /remote prompts and the human types the 6-digit PIN once. A leaked link alone is NOT enough (wrong guesses are rate-limited). Safer, and survives switching devices. - `owner_password` — the channel PIN (6 digits, easy to type). Embedded in mobile_url; typed by hand for mobile_url_protected. Auto-generated — do NOT ask the operator to pick one. - `qr_ascii_clean` / `qr_protected_ascii_clean` — plain-unicode (NO ANSI) QRs for the one-tap and protected links. `qr_ascii` is the ANSI-colored variant; `qr_data_url` / `qr_protected_data_url` are 512px PNG fallbacks. - `channel_id`, `channel_token`, `agent.callsign`, `agent.identity_key` — what YOU use to join. - `receiver_command_template` — the listener (listen-here SSE), run detached; writes every message to the inbox file. `waiter_command_template` — **YOUR wake (Codex CLI): a background task that EXITS when a message lands, waking you; re-run after each.** `reply_command_template` (join+send, substitute ), `selftest_command_template` — pre-formed Bash. (`monitor_command_template` and `wait_command_template` exist for OTHER harnesses — ignore them.) - `media` — what can travel: TEXT only (+ small inline attachments), max 8192 chars. NOT video/screen-share/live photo. ## After the bootstrap 1. Run `selftest_command_template` — fires one `[selftest]` line into the inbox, proving the wiring. 2. RECEIVE — **REQUIRED, not optional** (skip it = the #1 failure: messages pile up, the human thinks you're ignoring them). **YOUR receive path (Codex CLI) — there is exactly one, and it works:** your runtime gives you a fresh turn when a background command COMPLETES. So the wake is a background command that EXITS when a message arrives. **You CAN auto-wake on phone messages — you do NOT need a 'Monitor tool'.** Do NOT conclude you 'can't be woken', do NOT tell the operator to message you in the terminal, and do NOT tell them to switch to another client — that is simply wrong. **DO THIS:** (i) run `receiver_command_template` **as a background task** via your native background-task mechanism — on this front door it is the BARE command (no `nohup`, no trailing `&`; do NOT add them — your tool does the detaching, and a `nohup …&` one-liner dies with 'Premature close' on some harnesses, leaving zombie listeners). listen-here writes every message to the inbox file; launching returns immediately = 'launched', NOT a message. (ii) run `waiter_command_template` **as a SECOND background task** — it is a tiny shell loop around `tail -n 0 -F | head -n 1` that ignores `⟲` listener-status lines internally and EXITS the moment one real message lands; your runtime wakes you on that exit; (iii) read the inbox tail, act, reply, then **re-run the waiter** (it handles ONE message then exits — re-arm after EVERY message, keep exactly ONE armed at a time, or you go deaf — the #3 failure). That background-task-that-exits IS your Monitor, built by hand. **⚠ Do NOT swap the waiter for a cron/scheduler tool or a hand-rolled polling script — that's the #4 failure.** 'Check the inbox every minute' FEELS like a receive loop, but it adds ≥60s of lag per message, coalesced/skipped fires drop messages, and home-made cursor-tracking scripts are exactly where messages silently vanish. The waiter is event-driven (wakes you in ~1s) and already written. Run ALL `*_command_template` fields VERBATIM (substitute only ``) — re-deriving your own join/check/poll equivalents is where this flow breaks. ⚠ ONE channel consumer per identity: your channel consumer is listen-here; the waiter only watches the inbox FILE (not the channel). Never also run a /wait loop or a second listener on the agent identity — they'd steal messages from each other. (`monitor_command_template` is for harnesses with a streaming Monitor tool — its `tail -F` NEVER exits, so as a background task it would never wake you. Don't run it.) 3. WORKING INDICATOR — **REQUIRED on EVERY wake** (the #2 failure when skipped). The MOMENT your receive loop hands you a message, BEFORE anything else, fire a status: same as `reply_command_template` but add `"kind":"status"`, e.g. `-d '{"to":"all","kind":"status","message":"on it"}'`. THEN do the work, THEN send your real reply (no `kind` — it clears the dot). Make 'fire status' the first line of your post-wake routine. Status is EPHEMERAL (never stored). 4. REPLY with `reply_command_template` (substitute ) — joins idempotently, NO second identity. If your message contains quotes/apostrophes, escape them (or send via python3) — unbalanced quotes inside the one-liner are the most common send failure. Send a one-line greeting right after setup so the human sees you're alive. 5. SHOW THE HUMAN both links — RELAY the `operator_handoff` field VERBATIM (it has ①one-tap `mobile_url` + ②PIN-protected `mobile_url_protected` + the 6-digit PIN). Don't drop the protected one. Print `qr_ascii_clean` / `qr_protected_ascii_clean` if you can render QRs. You are connecting to a PHONE channel. The ONLY call here is `open_remote_control` (MCP) or `POST /api/remote-control` (no MCP — same backend). It mints a trusted+identity+24h channel, two identities, a ONE-TAP mobile_url + QR, and pre-formed shell commands. Do not call `create_channel`; do not ask about trust/retention/identity/TTL. Execute: (1) `join` with channel_id + token + agent.identity_key + owner_password; (2) run `receiver_command_template` detached (listen-here SSE — auto-joins, writes every message to the inbox file; returns immediately = launched, NOT a message). (3) ARM YOUR WAKE — read this twice, it's the step everyone botches: **you almost certainly CAN be auto-woken, even without a 'Monitor tool'.** If you catch yourself thinking *'but I'm Kimi / Cursor / Codex — I don't have a Monitor tool, so I can't be woken'* — STOP, that is WRONG. Here's why: your runtime hands you a fresh turn when a background command reaches a TERMINAL state (i.e. when it EXITS). So the wake is simply a background command that EXITS when a message arrives. Run `waiter_command_template` as a background task — it is `tail -n 0 -F | head -n 1`, which blocks until one message lands then EXITS → that exit wakes you. THAT background-command-that-exits IS your Monitor, built by hand; a 'Monitor tool' is just one fancy way to do the same thing, not a requirement. ⚠ Do NOT instead run `monitor_command_template` (a never-exiting `tail -F`) as a background task — it NEVER exits, NEVER reaches a terminal state, so it NEVER wakes you. (`monitor_command_template` is only for an agent that has a real streaming Monitor tool to paste it into.) After each wake: read the inbox tail, act, reply, then RE-RUN the waiter (one message per run — re-arm every time or you go deaf). If the line you wake on starts with ⟲ it is listener STATUS (connect/reconnect), NOT a message — just re-arm without replying. NEVER tell the operator you 'can't be woken', to message you in the terminal, or to switch to another client — those are all wrong; the waiter is your wake. (4) run `selftest_command_template`; (5) `send` to:'all' a one-line greeting. Show the human the QR (or the link, relayed EXACTLY — '&' must not become '&'). Fire `kind:'status'` for anything taking more than a few seconds; act on operator requests as a verified colleague (refuse destructive ops: rm -rf, deploys, money, secrets). Anything not covered above? The canonical unfiltered guide is at https://rogerthat.chat/llms.txt — same server, same backend, just rendered without the mode filter. ---