Codex Remote surface (codex.justinsforge.com)¶
URL: https://mkdocs.justinsforge.com/memory/general/reference_codex_chat_surface/
A mobile-first web client for OpenAI Codex, running live on Console. It drives
one long-lived codex app-server process using the ChatGPT login stored in
CODEX_HOME. It does not use an OpenAI API key or proxy requests through a third
party. Pick a repo, configure model and effort, start a thread, send text and
images, steer or stop an active turn, answer native requests, and watch plans,
commands, file changes, reasoning summaries, diffs, usage, and final responses
stream into the browser.
Each local thread is bound to a repo and runs in its own throwaway git worktree, so
Codex never dirties main. The service preserves its own searchable thread and
message index while the native Codex thread remains the execution authority.
Originally built 2026-07-08 and migrated from one-shot codex exec processes to
the native app-server protocol on 2026-07-13.
Pieces¶
| Piece | Where |
|---|---|
| Production | https://codex.justinsforge.com (Cloudflare Access, email allowlist [email protected], app uid 3432a3ef-8182-4f40-b1bd-9d8a05f5ed07, AUD fce9858c...5584b0; VR Alliance tunnel fb20b500-0119-4c84-9536-fa85751987c3 ingress -> http://127.0.0.1:7366) |
| Service | scripts/forge_codex_chat_api.py, FastAPI, port 7366, bind 0.0.0.0 |
| App-server client | scripts/forge_codex_app_server.py, restartable async JSON-RPC over stdio |
| Unit | systemctl --user status forge-codex-chat (user unit, linger) |
| Venv | infra/codex-chat/.venv (fastapi, uvicorn, pyjwt) |
| UI | sites/justinsforge.com/codex/{index.html,app.js,app.css} (served at /) |
| DB | data/codex_chat.db (sqlite: threads, messages, thread_events) |
| Uploads | data/codex_uploads/<thread>/ |
| Worktrees | ~/codex-work/<repo>/<thread> on branch codex/<thread> |
| Repo registry | scripts/forge_codex_repos.py (forge, creatortrack, new-app; edit to add) |
| AGENTS.md writer | scripts/forge_codex_agents_md.py + templates/codex_AGENTS.md.tmpl |
| Log | journalctl --user -u forge-codex-chat |
Codex install (Console)¶
- Binary:
~/.local/bin/codex(codex-cli 0.144.2 when this page was updated). - Sandbox helper:
~/.local/bin/bwrap(Codex-bundled bubblewrap). - CODEX_HOME
~/.codex:config.toml(approval_policy=never, sandbox_mode=workspace-write, network_access=true) +auth.json(ChatGPT login,codex login --device-auth). No OpenAI API key is involved. Separate from Anthropic credentials. Pinned in~/.forge-secrets/codex.env(CODEX_HOME, PATH). - Userns gotcha (load-bearing): Ubuntu 24.04 ships
kernel.apparmor_restrict_unprivileged_userns=1, which blocks bwrap withsetting up uid map: Permission denied. Fixed bykernel.apparmor_restrict_unprivileged_userns=0persisted at/etc/sysctl.d/60-codex-userns.conf. Without it, Codex's shell/file tools fail before any write. This keeps the real workspace-write sandbox (writes confined to the worktree) instead of bypassing it.
Auth¶
CF Access JWT (Cf-Access-Jwt-Assertion, validated PyJWT vs CF_ACCESS_TEAM_DOMAIN
/ comma-separated CF_ACCESS_AUD in forge/.env) OR bearer CODEX_CHAT_TOKEN
(forge/.env) for LAN/tailnet. Same pattern as [[reference_session_launcher]]. The UI
stores an optional bearer token in localStorage (Settings gear) for direct LAN use;
behind Access it rides the login cookie, no token needed.
Native architecture¶
The FastAPI process owns one restartable codex app-server --listen stdio://
child. The browser never talks directly to the experimental app-server websocket.
The service initializes the experimental protocol locally, correlates JSON-RPC
requests, fans notifications out by native thread id, holds server-initiated
approval and input requests until the browser responds, and records active turn
ids. Browser SSE streams are reconnectable: the native turn keeps running after a
tab disconnects, with a bounded event replay buffer when the client returns.
The native protocol supplies:
- Thread start, resume, rename, archive, unarchive, delete, and context compaction.
- Turn start, live steering, interruption, diff and token-usage updates.
- Inline review turns for uncommitted changes.
- Command, file, permission, and
request_user_inputresponses. - Background terminal listing and termination.
- Account, model catalog, rate-limit, and native remote-control status.
- Native remote-control enablement, manual pairing codes, and paired-client list.
Web capabilities¶
- Searchable active and archived thread lists with persistent history.
- Model, effort, build or plan mode, and approval-policy controls per new thread.
- Coding scenarios: feature, bug fix, review, integrate, and explore only.
- Optional launch modifiers for plan-first, dev preview, frequent commits, push, notification, tool registration, and bounded parallel-agent instructions.
- Up to six pasted, dropped, or selected images per turn.
- Streaming messages, reasoning summaries, plans, command and file activity, diffs, warnings, usage, and reconnectable completion state.
- Send while running to steer; explicit stop to interrupt.
- Rename, archive or unarchive, review changes, compact context, and delete.
- Stable per-thread dev-server port and preview link detection.
- Responsive drawer and bottom-sheet layout with reduced-motion support.
Endpoints¶
All /api/* endpoints require Cloudflare Access or the bearer token. /healthz is
open at the origin but still Access-gated at the public edge.
GET /api/runtime: ChatGPT account type, model catalog, rate limits, app-server, and native remote-control status. Account email is never returned.GET /api/repos: allowlisted repo picker rows.POST /api/threads: creates worktree and AGENTS.md, starts a native thread, and applies model, effort, mode, approval policy, scenario, and modifiers.GET /api/threads?archived=&search=andGET /api/threads/{id}: list and read local metadata, messages, usage, native pending requests, and preview details.- Thread controls:
rename,archive,unarchive,compact,review, andDELETE /api/threads/{id}. - Turn controls:
POST .../turnfor SSE,GET .../eventsto reconnect,POST .../steer, andPOST .../stop. - Images and preview:
POST .../images,GET .../img/{fid}, andGET .../preview. - Native requests:
GET .../pendingandPOST /api/approvals/{request_id}. - Background terminals:
GET .../terminalsandDELETE .../terminals/{process_id}. - Remote control:
POST /api/remote-control/enable,disable,pair, andGET /api/remote-control/clients.
Ops¶
systemctl --user restart forge-codex-chat # edits to the API/UI go live on restart
journalctl --user -u forge-codex-chat -n 50
curl http://127.0.0.1:7366/healthz
python3 scripts/forge_codex_repos.py --list # repos in the picker
python3 scripts/forge_codex_repos.py --self-test
- Add a repo: edit
REPOSinscripts/forge_codex_repos.py, restart. - Worktrees are disposable; deleting a thread removes its worktree +
codex/<thread>branch. - Remote-control pairing is opt-in. Keep Cloudflare Access in front of the web UI and do not expose the app-server websocket through the tunnel.
Related¶
- [[reference_session_launcher]] (the Claude equivalent, chat.justinsforge.com)
- [[reference_code_server]] | [[reference_cloudflare]] | [[feedback_no_threads_with_subprocess_run]]