English | 中文
Self-hosted web console for controlling a local logged-in Codex runtime from a phone, tablet, or desktop browser.
The browser is only a remote UI. The host machine keeps Codex credentials, starts the Codex runtime, reads and writes local project files, executes shell commands, and stores app state. Tunnel and reverse-proxy setup are intentionally outside this repository.
Ask Codex to install it:
Help me install https://github.com/chenyanshan/codex-web/blob/main/README.md
Codex Web keeps Codex credentials, shell execution, and local file access on the host machine, while your phone or browser becomes a remote console. When combined with your own tunnel or reverse proxy, it becomes a practical way to reconnect and operate Codex remotely at any time without moving execution into the browser.
| Mobile remote console | Desktop workspace |
|---|---|
![]() |
![]() |
- Remote UI for phone, tablet, and desktop browsers.
- Project-first workspace with live sessions, chat, and turn status.
- Fits LAN-only installs and remote-access setups built with your own network edge.
Codex Web includes a Web-layer multi-user facade for teams whose members fully trust one another. RBAC controls what the Web UI and HTTP API expose, but it is not tenant, OS-user, process, Codex-runtime, or filesystem isolation. Every turn still executes as the same host user with access allowed by that Codex runtime. Untrusted users must be separated with distinct OS users, containers, or hosts.
| Mobile admin audit | Desktop user management |
|---|---|
![]() |
![]() |
- Multi-user mode, project management, role management, and user management for a fully trusted group.
- Web-layer RBAC for project access, admin operations, and observer mode.
- Session audit views for reviewing activity across users, projects, and sessions.
- Password-protected single-host Codex web console.
- PWA-friendly mobile UI with persistent per-device browser sessions.
- Project-first responsive workspace: wide landscape desktops use a project rail, session list, and chat pane; narrow or portrait desktops use a focused single-session layout with a desktop-sized composer; phones use a project drawer.
- Weak-network resilience with cached session summaries, bounded conversation histories for the five most recently used sessions, and locally persisted optimistic and queued text messages.
- Earlier exchanges expand when you scroll upward at the top of a desktop conversation or pull down at the top of the timeline in the installed PWA.
- Live Codex turn stream with assistant deltas, final answers, command batches, file-change batches, approval requests, and runtime errors.
- Multi-user/RBAC facade for fully trusted users, project access, admin management, and observer mode.
- Optional read-only share links open a dedicated conversation page. Public sharing is disabled by default and must be explicitly enabled by the operator.
- Per-user webhooks can start a session and keep sending messages to the same
conversation from authenticated external
POSTrequests. - File and image attachments for turns, including direct clipboard paste on desktop browsers. The backend stores files locally and passes safe local paths to Codex.
- Model and reasoning choices from the active Codex CLI, with current-session controls kept separate from per-browser defaults for new sessions.
- Session-scoped file previews for Markdown, HTML, PDF, common images, web links, and retained attachments directly from the conversation.
- A bundled
codex-web-user-contextskill that fetches the current Codex Web user/project context on demand from a loopback HTTP API. - macOS launchd service helpers and Linux systemd setup instructions.
- English and Simplified Chinese UI language setting, plus a backend-managed site title for admins/single-user installs.
- Eleven contrast-checked themes with Sunlit yellow as the first-run default, including Paper, Graphite, Nordic, Forest, Rose, Graphite Amber, One Dark Pro, Gruvbox Dark, Catppuccin Mocha, and Dracula Dark alternatives.
packages/codex-native-api reusable Codex app-server integration
packages/codex-web HTTP API, auth, runtime bridge, and web UI
scripts/install AI-guided installer scripts
scripts/service launchd service helpers
skills/codex-web-user-context current Codex Web user/project context skill
docs/superpowers/specs design docs
docs/superpowers/plans implementation plans
docs/rendering local Markdown/file rendering fixtures
This repository was split out from CodexBridge-main.
- Node.js
>=24 - npm
- local Codex CLI installed
- local Codex login at
~/.codex/auth.jsonorCODEX_HOME/auth.json
Install dependencies:
npm installSet the web password:
npm run codex-web -- auth set-passwordStart the web service:
npm run serveBy default the service listens on 0.0.0.0:43210, so phones on the same LAN can
reach it. http://127.0.0.1:43210 is reachable only from the host machine.
Before entering a password from another device, put the service behind your own
HTTPS reverse proxy, tunnel, or private-network HTTPS endpoint; bearer tokens
and passwords are not protected on plain LAN HTTP, and Service Workers require
a secure context.
Run checks:
npm run build
npm run typecheck
npm test
npm run lintThe browser suite uses Playwright and its local fixture server:
npx playwright install chromium
npm run test:browserA complete Chinese usage guide is available in docs/webhook.md.
A webhook conversation is identified by its Idempotency-Key. The first
request creates a Codex Web session and starts its first turn. Every later
request with the same key sends a new message to that same session.
- Sign in to Codex Web and open Settings.
- Find Webhook and turn on Enable webhook.
- Copy the endpoint and webhook key. The key remains visible and copyable until you regenerate it.
- Send a
POSTrequest to the copied endpoint:
Single-user example:
curl --request POST 'https://codex-web.example/api/webhook' \
--header 'Authorization: Bearer cwwh_REPLACE_ME' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: support-conversation-123' \
--data '{"title":"Webhook task","text":"Review the latest changes"}'Continue the same conversation by reusing the key:
curl --request POST 'https://codex-web.example/api/webhook' \
--header 'Authorization: Bearer cwwh_REPLACE_ME' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: support-conversation-123' \
--data '{"text":"Now summarize the most important failures"}'Use a different Idempotency-Key when you want a separate session.
Replace the example host and key with the values shown in Settings. In
single-user mode, the session uses CODEX_WEB_DEFAULT_CWD and projectId must
be omitted.
Multi-user example:
curl --request POST 'https://codex-web.example/api/webhook' \
--header 'Authorization: Bearer cwwh_REPLACE_ME' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: support-conversation-123' \
--data '{"projectId":"CodeX Web","title":"Webhook task","text":"Review the latest changes","model":"gpt-5.6-sol","reasoningEffort":"high"}'In multi-user mode, set projectId to the project display name shown in Codex
Web. Display-name matching is case-insensitive, and new or renamed projects
cannot share a display name. The exact internal project ID is also accepted for
backward compatibility. The key owner must have permission to create sessions
in the selected project.
| JSON field | Required | Description |
|---|---|---|
text |
Yes | The new message sent to the conversation. |
title |
No | The session title, used only when the key first creates a session. |
projectId |
Multi-user only | Project display name or exact internal ID. Required in multi-user mode and rejected in single-user mode. |
model |
No | Exact, case-sensitive model ID for a newly started turn. |
reasoningEffort |
No | A supported reasoning value for a newly started turn. |
Model availability and reasoning choices are dynamic. Use the Model and
Reasoning options shown in Codex Web, or inspect GET /api/models with a normal
authenticated browser token (items[].id and supportedReasoningEfforts); a
webhook key cannot read that private API. Values such as max or ultra are
valid only when the selected model advertises them.
When both fields are omitted, the first request inherits the Codex configuration
for the target working directory, not browser-local New Thread defaults. Later
idle continuations retain the session's current settings. Supplying only
model uses that model's Codex default reasoning effort. Supplying only
reasoningEffort applies it to the effective model. Unsupported values are
passed to the Codex runtime, which may reject the request.
Idempotency-Key is required, contains 1 to 256 characters, and acts as a
conversation routing key. It is scoped to the webhook owner, survives service
restarts and webhook-key rotation, and remains bound to its original session
and project. Every accepted POST is a new message, even if its JSON body is
identical to an earlier request.
When the mapped session is idle, Codex Web starts a new turn. When a normal turn
is already running, it uses Codex turn/steer to add the message to that turn
without interrupting it. Review and compact turns cannot be steered; retry after
they finish. model and reasoningEffort apply when a new turn is started. A
message steered into an active turn inherits that turn's current settings.
HTTP success confirms that the message was accepted. There is no separate per-message deduplication identifier: after a connection timeout or lost response, retrying may send the message twice. Callers that cannot tolerate duplicates should reconcile the result before retrying.
201 Created: a new session and first turn were created.202 Accepted: a new message was started or steered in the existing session.400 Bad Request: a header or JSON field is missing or invalid.401 Unauthorized: the webhook is disabled or the key is invalid.404 Not Found: no enabled project the key owner can create sessions in matchesprojectId, or the mapped session no longer exists or is no longer writable by the key owner.409 Conflict: the key is already bound to another project, the mapped session is archived, the current review/compact turn cannot be steered, a legacy display name is ambiguous, or the active-session limit was reached while creating the first session.429 Too Many Requests: the per-webhook-credential limit of 10 requests per minute was exceeded.
Only text, title, model, reasoningEffort, and the mode-appropriate
projectId are accepted. The caller cannot override the working directory,
approval policy, sandbox, or other runtime settings. The turn uses the server
runtime permission defaults, which are currently danger-full-access and
approvalPolicy=never. Treat the webhook key like a password because it can
start Codex work on the host machine.
Use the root install.md when asking Codex or another coding agent to install this project from a GitHub blob link or local checkout.
Example Codex request:
Help me install https://github.com/chenyanshan/codex-web/blob/main/README.md
Expected agent behavior:
- If the user provides a GitHub
README.mdorinstall.mdblob link, derive the repo root and followinstall.md. - If the user says "help me install this project" from inside a local checkout,
find the repo root and follow
install.md. - On macOS, ask whether launchd autostart should be installed. Never ask the user to send a password in agent chat; the installer opens a hidden terminal prompt directly.
- On Windows, stop and report that this repository does not provide a Windows installer.
The automated macOS flow uses:
install.md
scripts/install/install-codex-web-macos.sh
The installer handles dependency install, password setup, service start, optional launchd autostart, and installation of the bundled user-context skill.
Runtime state lives outside the repo.
Default paths:
~/.config/codex-web/service.env
~/.codex-web/auth.json
~/.codex-web/identity.json
~/.codex-web/session-settings.json
~/.codex-web/session-timeline.json
~/.codex-web/logs/
~/.codex-web/reports/
~/.codex-web/report-index.json
~/.codex-web/uploads/
~/.codex-web/tasks/
reports/ and report-index.json are retained only so links created by older
Codex Web versions keep working. New session files stay in their current
project or attachment storage; Codex Web no longer uses a global Reports area.
auth.json stores single-user password and session-token hashes.
identity.json stores multi-user password, session-token, and share-capability
hashes together with Web-layer authorization metadata. Neither file stores
plaintext passwords or bearer tokens. For authentication, the browser stores
only an opaque session token. Do not store CODEX_WEB_PASSWORD in
service.env.
For non-interactive first setup, the one-time CODEX_WEB_PASSWORD environment
variable is supported, but it should only be injected by a local secret manager.
Do not put a literal password in shell history or a service env file.
The generated service env includes these core defaults:
CODEX_WEB_HOST=0.0.0.0
CODEX_WEB_PORT=43210
CODEX_WEB_DEFAULT_CWD=/Users/you/path/to/codex-web
CODEX_REAL_BIN=codex
CODEX_WEB_DEBUG=0
CODEX_WEB_PUBLIC_SHARES_ENABLED=false
CODEX_WEB_PUBLIC_SHARE_TTL_SECONDS=86400Edit ~/.config/codex-web/service.env to change host, port, default working
directory, or Codex binary. To restrict access to the local machine only:
CODEX_WEB_HOST=127.0.0.1Public share capabilities stay hidden and all /api/share/* routes return not
found unless CODEX_WEB_PUBLIC_SHARES_ENABLED=true. When enabled, new links
default to a 24-hour TTL set by CODEX_WEB_PUBLIC_SHARE_TTL_SECONDS and are
capped at seven days. A share is also invalid after revocation or after
multi-user mode is disabled. Treat a share URL as a bearer capability.
Each authenticated user has one webhook key. Codex Web stores the recoverable
key alongside its validation hash in the local identity.json, which is
restricted to mode 0600; the browser keeps it only in memory and never writes
it to local storage. Legacy hash-only keys must be regenerated once before they
can be copied. See Webhook Quick Start for request
examples and payload rules.
Codex Web shows cached session summaries immediately and then refreshes them from the host in the background. It also keeps bounded conversation data for up to five recently used sessions in browser-local storage. Stable sessions can therefore reopen quickly on a slow link, while newer server state is still reconciled when connectivity allows.
An optimistic user message is persisted before its turn request completes. Plain-text follow-up messages queued while a turn is running also survive a reload and are retried after the active turn is reconciled. Attachments cannot be queued while a turn is running.
This is weak-network recovery, not a fully offline Codex runtime. Starting turns, uploading files, and refreshing server state still require connectivity to the host; the Service Worker caches only the static application shell. The browser cache can contain conversation text, so use trusted browser profiles and clear site data when retiring a device.
Codex Web cleans only files it owns by managed filename or legacy report extension; it does not follow symlinks or delete unrelated project files. Cleanup runs at startup, before managed writes, and before legacy report access. Files opened from a project by the session viewer are not managed or deleted by Codex Web. When a quota is full, expired managed files go first, then the oldest managed files.
| Managed data | Default policy | Configuration |
|---|---|---|
| State uploads, turn snapshots, legacy reports, legacy runtime context | 2 GiB total | CODEX_WEB_MANAGED_STORAGE_MAX_BYTES |
| Project-local uploads | 512 MiB per project | CODEX_WEB_PROJECT_UPLOAD_MAX_BYTES |
| Uploaded source files | 7-day TTL | CODEX_WEB_UPLOAD_TTL_SECONDS |
| Turn attachment snapshots | 30-day TTL | CODEX_WEB_TURN_ATTACHMENT_TTL_SECONDS |
| Legacy reports | 365-day TTL | CODEX_WEB_REPORT_TTL_SECONDS |
| Legacy runtime context files from older versions | 30-day TTL | CODEX_WEB_RUNTIME_CONTEXT_TTL_SECONDS |
| App timeline | 500 entries per session, 16 MiB total | CODEX_WEB_TIMELINE_MAX_ENTRIES_PER_SESSION, CODEX_WEB_TIMELINE_MAX_BYTES |
Legacy files under ~/.codex-web/reports/ remain subject to their configured
retention and quota. This policy exists for backward compatibility and does not
apply to files in a session's project directory.
The composer can upload files and images for the next Codex turn. On desktop browsers, clipboard files and images can also be pasted directly into the composer. All upload routes require authentication.
Writable project directory:
<project-cwd>/uploads/<user-id>/
Fallback storage:
~/.codex-web/uploads/projects/<project-key>/<user-id>/
The backend returns the actual localPath and validates attachment paths
against allowed upload roots before starting a turn. Images are passed to Codex
as local images; other files are listed in the turn prompt with local paths.
Upload limits:
32 MiB request body
25 MiB per file
Uploaded source files and immutable turn snapshots are also subject to the storage lifecycle policy above.
Attachments shown in existing session history remain clickable while their stored source or immutable turn snapshot still exists. An attachment already removed by its retention policy cannot be reconstructed and is shown as unavailable.
Files are part of the conversation rather than a separate Reports product. When the assistant links a file in the current project, the link opens directly from that session. Relative paths resolve from the session project root, and access is restricted to that project and the session's authorized attachment roots.
- Markdown renders with the conversation Markdown renderer.
- Self-contained static HTML renders in a sandboxed viewer; scripts and relative or remote subresources are blocked.
- PDF and common image formats open in the in-app viewer.
- HTTP and HTTPS links open as normal web links and are not proxied by the backend.
Existing links under ~/.codex-web/reports/ remain readable through
authenticated compatibility routes, but there is no global Reports list or
favorite workflow.
The Codex Web user-context skill lives at:
skills/codex-web-user-context
Install it into local Codex skills:
mkdir -p ~/.codex/skills
mkdir -p ~/.codex/skills/codex-web-user-context
cp -R skills/codex-web-user-context/. ~/.codex/skills/codex-web-user-context/PowerShell:
$target = Join-Path $HOME ".codex\skills\codex-web-user-context"
New-Item -ItemType Directory -Force $target | Out-Null
Copy-Item "skills/codex-web-user-context/*" $target -Recurse -ForceFor active development, symlink it instead:
mkdir -p ~/.codex/skills
ln -s "$(pwd)/skills/codex-web-user-context" ~/.codex/skills/codex-web-user-contextUpgrade required: existing installations must copy/reinstall
skills/codex-web-user-contextagain and restart Codex so its skill catalog reloads. The old skill expectsCODEX_WEB_CONTEXT_FILE, which is no longer provided.
The skill reads the current CODEX_THREAD_ID and calls the Codex Web service
through CODEX_WEB_LOCAL_API_URL. The server accepts this endpoint only from
the real loopback socket (127.0.0.1 or ::1), maps the thread to its live
multi-user session, and returns a sanitized user/project projection. It does
not trust forwarded headers and does not expose a session-list endpoint.
No user identity, project metadata, skill hint, or context path is added to ordinary turn instructions. Only the loopback API origin is made available to the runtime; the skill performs the request when it is explicitly needed. Codex and Codex Web must therefore share the same host network namespace.
The status above the composer is the runtime state, not a request spinner. It is reconciled from live turn events and refreshed session history.
- Active turns show
Running. - A recoverable stream interruption shows
Reconnectingwhile the turn remains active. - Idle sessions and successful terminal turns show
Ready. - Interrupted, cancelled, or aborted turns show
Stopped. - Provider/runtime failures show
Failed; details such as401,403,429, or unexpected provider statuses are rendered as red system messages in the conversation timeline.
If the Codex Web service restarts while a turn is in progress, Codex may mark
that turn as interrupted without an error payload. The UI shows Stopped
instead of a red error because the turn ended due to service lifecycle
interruption.
Repository updates do not hot-reload the running Codex Web backend. For an existing macOS install managed by the user LaunchAgent, run the following from the repository checkout to pull the update, install dependencies, and restart the service:
git pull --ff-only
npm install
scripts/service/restart-codex-web-launchd-user.shAfter the restart, reopen or refresh the installed PWA.
The model catalog, configuration defaults, and advertised reasoning choices
shown by Codex Web come from the Codex CLI selected by CODEX_REAL_BIN. Unless
the browser has an explicit override for new sessions, the UI follows those
defaults instead of a fixed legacy gpt-5.4 / xhigh pair. The selected model
must advertise ultra for that choice to appear. Pulling this repository does
not upgrade the Codex CLI or add capabilities to the selected runtime.
Install the user LaunchAgent:
scripts/service/install-codex-web-launchd-user.shThe installer builds and verifies the production dist output before changing
the service. Existing plist files are backed up under
~/.codex-web/service-backups/. A detached helper reloads the plist because
kickstart alone retains the loaded job's previous arguments.
The helper waits for launchd to finish unloading, retries transient startup
errors, and restores the saved plist if the new process cannot stay running.
Its one-shot result is available with
launchctl print gui/$(id -u)/com.chenyanshan.codex-web.restart and in
~/.codex-web/logs/com.chenyanshan.codex-web.restart.log (exit 2 means the
requested change failed but the previous launch configuration was recovered).
This checks process startup; verify /version.json and the UI after deployment.
An isolated macOS lifecycle gate covers reload, failed startup recovery, and
restart without changing the real service: node scripts/service/verify-launchd-lifecycle.mjs.
To switch back to source mode for diagnosis or rollback of the delivery mode:
CODEX_WEB_SERVICE_MODE=source scripts/service/install-codex-web-launchd-user.shRun the installer without this variable to return to dist. This switches the
delivery mode of the current checkout; reverting application code requires the
corresponding source revision and a fresh build. State and service.env are
preserved in both modes.
Service helpers:
scripts/service/status-codex-web-launchd-user.sh
scripts/service/restart-codex-web-launchd-user.sh
scripts/service/restart-codex-web-launchd-user-detached.sh
scripts/service/logs-codex-web-launchd-user.sh
scripts/service/rotate-codex-web-logs.sh
scripts/service/stop-codex-web-launchd-user.sh
scripts/service/uninstall-codex-web-launchd-user.shUse the detached restart helper when Codex Web needs to restart itself from a
running Codex-controlled session. Uninstalling preserves both
~/.config/codex-web/service.env and ~/.codex-web/. Set
CODEX_WEB_LAUNCHD_LABEL when a deployment needs a custom service label.
The installer also creates an hourly ${CODEX_WEB_LAUNCHD_LABEL}.logrotate
LaunchAgent. It copy-truncates logs without restarting the running service,
keeps five private generations at 10 MiB each by default, and reads
CODEX_WEB_LOG_MAX_BYTES plus CODEX_WEB_LOG_GENERATIONS from service.env.
Create the service environment:
mkdir -p ~/.config/codex-web ~/.codex-web/logs
cat > ~/.config/codex-web/service.env <<EOF
CODEX_WEB_HOST=0.0.0.0
CODEX_WEB_PORT=43210
CODEX_WEB_DEFAULT_CWD=$(pwd)
CODEX_REAL_BIN=codex
CODEX_WEB_DEBUG=0
EOF
chmod 600 ~/.config/codex-web/service.envCreate and start a user service:
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/codex-web.service <<EOF
[Unit]
Description=Codex Web mobile console
After=network-online.target
[Service]
Type=simple
WorkingDirectory=$(pwd)
EnvironmentFile=%h/.config/codex-web/service.env
ExecStart=/usr/bin/env npm run serve --workspace packages/codex-web
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now codex-web.service
systemctl --user status codex-web.serviceRead logs:
journalctl --user -u codex-web.service -fAfter the server is running, open Codex Web from your phone browser and log in once.
On iPhone or iPad: open in Safari, tap Share, then Add to Home Screen.
On Android: open in Chrome, open the browser menu, then tap Install app or
Add to Home screen.
More notes: docs/pwa-setup.md.
Design and implementation notes are listed below. The Session File Viewer design is authoritative for file workflows.
docs/superpowers/specs/2026-05-17-codex-web-design.md
docs/superpowers/specs/2026-07-17-session-file-viewer-design.md
docs/superpowers/specs/2026-05-23-codex-web-desktop-workspace-design.md
docs/superpowers/specs/2026-05-27-codex-web-multi-user-rbac-design.md
docs/superpowers/specs/2026-05-28-role-project-new-session-design.md
docs/superpowers/specs/2026-05-29-codex-web-workspace-redesign-design.md
docs/superpowers/specs/2026-05-30-codex-web-attachments-design.md
docs/superpowers/specs/2026-06-01-session-card-first-message-design.md
docs/superpowers/plans/2026-05-17-codex-web-mvp.md
docs/superpowers/plans/2026-05-23-codex-web-desktop-workspace.md
docs/superpowers/plans/2026-05-27-codex-web-multi-user-rbac.md
docs/superpowers/plans/2026-05-28-role-project-new-session.md
docs/superpowers/plans/2026-05-29-codex-web-workspace-redesign.md
docs/superpowers/plans/2026-05-30-codex-web-attachments.md
docs/superpowers/plans/2026-06-01-session-card-first-message.md
docs/superpowers/plans/2026-06-01-timeline-error-ordering.md
Visual reference:
docs/assets/codex-web-reference.jpg



