Skip to content
Projects

yunhaoli24/codex-gateway has 36 GitHub stars and 14 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-09 06:37 UTC. Last push 2026-11-05.

How these counts are measured

A self-hosted web frontend and SSH gateway for the official Codex app-server.

What you can do

  • Server-side accounts and config: manually created users, Bearer token login, and encrypted host, pinned-thread, and notification config in SQLite. Directory groups are discovered from Codex thread working directories.
  • Remote hosts: SSH password, private key, ssh-agent, SOCKS proxy and automatically detected keyboard-interactive MFA support.
  • Codex runtime management: standalone installation, resumable SFTP upgrades, official daemon lifecycle management, visible upgrade progress and manual restart.
  • Thread discovery and restore: remote directory grouping, cursor-paginated timelines, cached route switching and multi-browser pin synchronization.

Measured star growth

in 24h
0
in 7 days
0
in 15 days
Update delayed
in 1 month
Update delayed

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

README.md

Original

Codex Gateway

Nuxt Vue TypeScript Tailwind CSS Playwright Docker License

English | 中文

Features · Architecture · Quick Start · Deployment · Contributing

Codex Gateway is a web frontend and connection gateway for the official Codex app-server.

Gateway connects to remote machines over SSH, manages the official Codex daemon, and renders conversations, approvals, file changes, reasoning, tools and sub-agent activity. Each Gateway account manages its own hosts, pins and notification preferences, with a responsive workspace for desktop and mobile browsers.

The goal is simple: open Codex sessions from many servers in a browser while keeping Codex app-server as the source of truth. If Codex Desktop, another client, and Codex Gateway connect to the same app-server thread, they should observe the same state stream.

Codex Gateway showing remote hosts, projects, a Codex agent loop, and workspace tabs

A browser workspace for Codex sessions running across remote SSH hosts.

Why

  • Use Codex from any browser while Gateway handles SSH and remote runtime access.
  • Manage multiple SSH hosts, projects, and Codex threads from one workspace.
  • Keep official Codex app-server semantics instead of inventing a parallel protocol.
  • Share one gateway-side SSH/RPC lifecycle per user and Host across browser tabs.
  • Connect to keyboard-interactive MFA hosts without a separate SSH adapter.
  • Recover thread state after browser reloads, app-server restarts, or temporary SSH disconnects.
  • Open a direct SSH terminal next to the agent loop when you need to inspect or fix the remote environment manually.
  • Preview remote web applications in an isolated Browser panel without publishing their ports.
  • Monitor long-running training or inference jobs in tmux across all hosts and get notified when a pane exits or returns to its shell.
  • Watch CPU, memory, network, disk, and GPU activity without leaving the conversation workspace.

Feature Tour

These views are captured from the real Playwright E2E environment. Select any image to open it at full resolution.

Codex workflows

Goal details with objective, elapsed time, token usage, and controls
Goal lifecycle
Track app-server goal progress, edit the objective, pause or resume execution, and clear completed work.
Plan mode with a structured implementation plan and actions
Plan mode
Review a structured plan, continue planning, or move directly into implementation.

Remote workspace

Dockable file workspace with a remote tree and rendered Markdown preview
Files, editing, and previews
Browse remote trees, edit text, and preview Markdown, LaTeX, images, PDF, and Office documents beside the agent.
Remote browser preview with HTTP, WebSocket, and resource diagnostics
Remote browser preview
Open a Host's private web service through SSH with full-origin HTTP/WebSocket forwarding and visible resource errors.

Operations and notifications

Live CPU, memory, network, and disk monitoring charts
Live host metrics
Stream CPU, memory, network, and disk telemetry over the shared Gateway realtime connection.
GPU metrics and process ownership table
GPU process attribution
Inspect utilization, temperature, VRAM, users, PIDs, runtimes, and commands for remote training jobs.
Cross-host tmux monitor with sessions, panes, and active jobs
Cross-host tmux monitoring
Discover panes, inspect recent output, and monitor one run or every future run from one user-wide view.
Actionable conversation completion notification
Browser and Bark notifications
Receive de-duplicated completion alerts and open the matching conversation or tmux pane directly.

Architecture

flowchart LR
    B[Browser / installed PWA] -->|HTTPS + page WebSocket| G[Nuxt / Nitro Gateway]
    G --> D[(SQLite)]
    G --> C[User-scoped runtime and caches]
    G --> S[Shared SSH transport per user / remote identity]
    S -->|codex app-server proxy| A[Official Codex daemon / Unix socket]
    S --> F[SFTP files and attachments]
    S --> T[PTY terminals / tmux / metrics]
    G -->|SSH forwarding| W[Remote web services]
    G --> N[Web Push, then Bark fallback]

The stack is Nuxt 4.6 / Vue 3 / TypeScript, with Pinia for browser state, Dockview for workspace layout, TanStack Virtual for the transcript, and precompiled shadcn-vue / AI Elements workspace packages. Codex is the only implemented provider; protocol mapping lives behind the server's provider adapter. The current protocol baseline is Codex 0.166.0, defined in codex-version.ts and mirrored by the source submodule.

Connections and account boundaries

Browsers connect to Gateway, which owns SSH and App Server access. Each page uses one business WebSocket for thread events/actions, file/Git queries, host state, terminal streams, metrics, tmux and notifications. Terminal output uses binary frames on that same connection. Login, configuration, catalog queries, multipart uploads and file/image responses still use HTTP; browser previews have their own proxied HTTP/WebSocket traffic.

The SSH pool reuses a transport across features and browser tabs for the same Gateway user and remote identity. RPC sessions are shared per user, Host and provider. MFA authentication and subscriptions are isolated between Gateway accounts. Accounts configured with the same remote SSH user and CODEX_HOME still access the same remote files and Codex conversations; use separate remote accounts or Codex homes when those resources must be isolated.

State and history

State Storage / source of truth
Threads, turns, timeline order, settings, Goals and attachment membership Official remote App Server and its CODEX_HOME
Users, password/token hashes, detected MFA capability and tmux monitors/history Gateway SQLite
Host credentials, pins, notification preferences, Web Push subscriptions and VAPID keys Encrypted Gateway SQLite records
Connections, subscriptions, snapshots, event replay, directory groups and metric samples User-scoped Gateway memory, rebuilt after restart
Loaded history and rendering state Bounded browser Pinia caches
Login, route, layout and file-tab preferences Browser-local storage
Uploaded file bytes Remote $CODEX_HOME/attachments/<UUID>/<original-filename>

Directory navigation derives from remote thread working directories (cwd). Pins identify a Host and thread, and their updates synchronize across browsers. Gateway's SQLite and caches do not replace Codex's transcript storage.

Cold thread activation uses thread/resume for subscription/settings, including Plan collaboration mode, plus thread/timeline/list for history. The initial page requests 20 timeline entries, followed by explicit cursor pagination, normally 100 entries per page. Expanding intermediate content can read details with thread/items/read. Warm opens reuse the continuously updated server snapshot; events update that snapshot and fan out to subscribed pages. Reconnect and stale-running reconciliation refresh authoritative App Server state when needed.

Input to an idle conversation uses turn/start; input to a running turn uses turn/steer. turn/interrupt is the explicit stop action. User messages, including steer input, remain transcript content; intermediate-process folding is a presentation concern. Returning to a page or reconnecting its WebSocket refreshes thread settings changed by another client.

One flat virtual timeline bounds mounted rows. Browser retention separately keeps 10 recent thread caches, releases older completed/collapsed intermediate content beyond the latest three turns, and limits command-output windows to 128 Ki characters. Older content can be fetched again. Server snapshots and per-thread event replay expire after 10 idle minutes; reads and updates renew their lifetime. Historical pages use a global LRU with expiry, and rendering tasks are cleaned up with their owners. These measures reduce retained memory but are not a fixed byte ceiling for a large active turn. Defaults live in shared/config.ts.

Codex installation and lifecycle

Gateway downloads and verifies official standalone archives, then uploads them over SFTP. Linux/macOS x64/arm64 targets are supported; remote Node/npm is not required. Existing npm installations are migrated to the standalone entrypoint. Unrelated npm packages and existing Codex credentials remain in place.

CLI installation and the running daemon version are checked separately. Gateway uses official codex app-server daemon start and daemon update --from-cli --yes, then attaches through codex app-server proxy. Codex owns shutdown grace and eligible thread/Goal recovery; recovery is best effort, and upgrades can interrupt work. Host settings expose upgrade progress and a manual daemon restart action.

Independent remote targets upgrade concurrently; equivalent SSH identity/credential aliases share an upgrade lock. Verified archives are cached in the Gateway data volume, while content-addressed remote staging permits interrupted SFTP uploads to resume. SHA-256 verification rejects corrupt archives. Transfer/lifecycle progress is shared with matching Host entries without an extra remote collector.

MFA is detected from real SSH keyboard-interactive challenges. Known MFA hosts require a manual reconnect action after disconnect: Gateway connects first and shows the code dialog when the server requests it. Files, terminals and RPC reuse that authenticated SSH connection.

Metrics and tmux collectors use adaptive delays/backoff and a per-SSH-connection background-work limiter. Interactive RPC and user actions bypass that limiter; it limits concurrency rather than reserving bandwidth. Linux metrics keep 300 timestamped samples, so slower sampling covers a longer time range. In the current release, SSH readiness starts metric collection; browser subscriptions control panel delivery.

Features

  • Server-side accounts and config: manually created users, Bearer token login, and encrypted host, pinned-thread, and notification config in SQLite. Directory groups are discovered from Codex thread working directories.
  • Remote hosts: SSH password, private key, ssh-agent, SOCKS proxy and automatically detected keyboard-interactive MFA support.
  • Codex runtime management: standalone installation, resumable SFTP upgrades, official daemon lifecycle management, visible upgrade progress and manual restart.
  • Thread discovery and restore: remote directory grouping, cursor-paginated timelines, cached route switching and multi-browser pin synchronization.
  • Realtime turns: start new turns, steer running turns, interrupt active turns, and answer app-server dynamic requests over WebSocket.
  • Plan and goal modes: review and execute structured plans; set, edit, pause, resume, stop, or clear app-server goals with token/time progress in the composer and details dialog.
  • Agent loop UI: reasoning, command execution, terminal waits, file edits, streaming diffs, images, context compaction, sleep, MCP/tool calls, notifications, and sub-agent side panels.
  • Dockable IDE workspace: split, resize, float, or pop out Agent, Files, Terminal, Browser, and Sub-agent panels with per-thread layouts persisted locally.
  • Remote file workspace: browse directories and symlinks, search files, copy paths, edit text, and preview Markdown, code, images, PDF and Office files. Git decorations and the review view expose changes; diff headers open the corresponding file. Use Upload beside Refresh to choose files, or drop files onto a directory in the tree. The selected directory (or a selected file's parent) is the destination; with no selection, uploads go to the project root. The destination and individual results are shown in the tree, including on mobile. Uploads accept up to eight files of 25 MiB each, reject existing names, and stay inside the server-known project after resolving symlinks. Folder uploads are not supported.
  • Attachments: stream uploads through Gateway to remote Codex attachment storage over SFTP, then register membership through official thread/attachment/* APIs. Removing membership does not delete a file still referenced by history.
  • Remote terminal tabs: open independent SSH PTY terminals beside the agent loop with @xterm/xterm; terminal sessions are isolated per user and host.
  • Remote browser tabs: preview a Host's localhost HTTP/HTTPS application in Dockview through SSH, including full-origin resources and WebSocket traffic, without exposing an additional Gateway port. Per-resource failures are reported inside the preview.
  • Host and GPU observability: stream CPU, memory, network, disk, GPU utilization, temperature, and VRAM metrics over the shared realtime connection. GPU process tables identify the remote user, PID, runtime, memory, and command behind each workload.
  • User-wide tmux monitoring: scan tmux sessions across every configured Host, inspect recent pane output, and bind a monitor to the relevant Codex thread. One-shot monitors notify when the current job exits or returns to its shell; permanent monitors wait for later runs and notify after each completed run. Active monitors and history are persisted in SQLite.
  • Multi-client sync: multiple browser tabs can subscribe to the same thread and receive the same gateway-side app-server event stream.
  • State repair: after SSH/app-server reconnect, Gateway refreshes running thread state; a Nitro scheduled task also checks stale running threads.
  • Actionable notifications: Sonner alerts, optional Web Push and Bark fallback for completed main turns, Goals, user questions and tmux jobs. Authorize each browser in Settings → Notifications; HTTPS is required, and iPhone/iPad require installation on the Home Screen. Clicking a notification opens its conversation or pane. External delivery is de-duplicated; push-service acceptance does not guarantee an OS notification was displayed.
  • Mobile layout: responsive sidebar, composer, long-press context actions, and sub-agent panels.
  • Real E2E coverage: Playwright tests run against a real Nuxt server, real SSH Docker target, and real Codex app-server.

Project Structure

.
├── app/                       # Nuxt frontend, Pinia store, chat/thread/settings UI
├── packages/gateway-ui/       # Precompiled shadcn-vue component package
├── packages/gateway-ai-elements/ # Precompiled AI Elements component package
├── packages/gateway-browser-runtime/ # Precompiled browser runtime and rendering helpers
├── server/api/                # Browser-facing HTTP and WebSocket API
├── server/tasks/              # Nitro scheduled task entrypoints
├── server/utils/gateway/      # SSH, Codex RPC, runtime broker, storage, notifications
├── shared/                    # Shared DTOs, config, protocol helpers, thread history
├── service-worker/            # Push display and click navigation
├── i18n/locales/              # Chinese and English UI messages
├── tests/e2e/                 # Real SSH + app-server Playwright E2E
├── third_party/openai-codex/  # Official Codex source submodule for protocol reference
├── Dockerfile
└── docker-compose.yml

Quick Start

Prerequisites: Docker with Compose, Git, a reverse proxy with HTTPS, and network access from Gateway to your SSH hosts and the official Codex release endpoints. Remote hosts need a working shell, SFTP, archive/checksum utilities, and writable home/install directories. Model access and credentials must be configured for Codex on each remote account.

git clone --recurse-submodules https://github.com/yunhaoli24/codex-gateway.git
cd codex-gateway

cp .env.example .env
# Replace CODEX_GATEWAY_CONFIG_SECRET in .env with: openssl rand -hex 32

docker network create web-common 2>/dev/null || true
docker compose build
docker compose run --rm codex-gateway \
  node scripts/create-user.mjs admin '<a-password-with-at-least-8-characters>'
docker compose up -d

Route your public hostname through the reverse proxy to codex-gateway:3000 on web-common, preserving WebSocket upgrades. Sign in with the created account, add an SSH host in Settings → Hosts, and wait for its installation/connection status. Select a remote directory to start a conversation or open an existing thread. MFA prompts appear after SSH requests verification.

Create additional accounts with the same script; each can configure its own hosts. Re-running the script for an existing username resets its password and reactivates it. There is no separate admin role or public signup flow.

Local Development

corepack enable
pnpm install
export CODEX_GATEWAY_CONFIG_SECRET="development-only-secret"
export CODEX_GATEWAY_DB_PATH="$PWD/data/codex-gateway.db"
pnpm user:create <username> <password>
pnpm dev

Use Node 24 as in the Dockerfile and the pnpm version pinned in package.json. Keep the same database path and secret for the app and maintenance commands. Common commands:

pnpm lint
pnpm build
pnpm test:e2e

Installation builds and typechecks the three workspace packages, then prepares Nuxt. pnpm lint checks business code, service worker and E2E types, Oxlint rules and formatting; it does not rebuild or separately typecheck workspace-package source. After editing a package, run pnpm build:dependencies and pnpm typecheck:dependencies. Turbo and the Docker dependency layer reuse unchanged package outputs; application changes still rebuild Nuxt.

Environment variables:

Variable Required Description
CODEX_GATEWAY_CONFIG_SECRET Yes in production Stable secret used to encrypt stored host, pinned-thread, and notification config.
CODEX_GATEWAY_DB_PATH No SQLite database path, default /data/codex-gateway.db. Set a writable path for local development.
HOST No Nuxt listen host. Docker uses 0.0.0.0.
PORT No Nuxt listen port. Docker uses 3000.
BROWSER_PREVIEW_DOMAIN Browser preview Parent domain for isolated preview origins such as p-<hmac>.example.com; configure wildcard DNS and TLS for *.example.com.
BROWSER_PREVIEW_SECRET No HMAC secret for stable per-user/Host/target preview origins. Defaults to CODEX_GATEWAY_CONFIG_SECRET.
BROWSER_PREVIEW_SCHEME No Public preview scheme, https by default. Use http only for local E2E/development.
BROWSER_PREVIEW_PUBLIC_PORT No Optional public port included in preview origins for local development.

Create or reset a user in the running deployment:

docker compose exec codex-gateway node scripts/create-user.mjs <username> <password>

CODEX_GATEWAY_CONFIG_SECRET encrypts stored connection config. Use a stable, sufficiently long secret in production. Changing it makes existing encrypted config unreadable.

Compose passes the variables listed in its environment section. If you need an optional runtime variable beyond that list, add it there or in a Compose override; adding it only to .env does not inject it into the container. Remote CODEX_HOME and CODEX_INSTALL_DIR belong to the remote login environment.

Security Model

  • Gateway performs SSH authentication and remote access on the server; authenticated host settings/config exports are sensitive and may contain credentials.
  • Browser clients authenticate to Gateway with a Bearer token.
  • Stored connection config is encrypted in SQLite with CODEX_GATEWAY_CONFIG_SECRET.
  • Gateway accounts scope host configuration, pins, subscriptions and caches; remote permissions are those of the configured SSH account.
  • Codex API credentials remain in the remote Codex environment. MFA codes are used for the pending SSH challenge, not stored as configuration.
  • Direct terminal tabs, tmux inspection and Browser proxy connections use server-side SSH channels; no remote service port needs to be published publicly.
  • Public deployments should run behind a trusted reverse proxy with HTTPS.

Docker Deployment

docker compose up -d --build codex-gateway
docker compose ps codex-gateway
docker compose logs --since 10m --tail 100 codex-gateway

The compose service exposes container port 3000 only to Docker networks. Put it behind nginx, Caddy, Cloudflare Tunnel, or another trusted reverse proxy. SQLite data is stored at /data/codex-gateway.db and persisted through ./data:/data.

The bundled service has a 1 GiB container memory limit and a 512 MiB V8 old-space limit. Native SSH/TLS buffers also use memory; raising only the JavaScript heap is not a substitute for capacity planning. The healthcheck is a side-effect-free TCP probe. Nitro performs periodic memory housekeeping separately. Logs use Docker json-file rotation (1m, three files).

For more users, run one Gateway instance with its persistent data volume and monitor memory, SSH connections and remote load. Connections, event fan-out and pending MFA live in process memory; multiple replicas behind a load balancer are not currently a shared runtime.

Remote Browser panels use isolated origins such as p-<hmac>.example.com. Set BROWSER_PREVIEW_DOMAIN=example.com in .env, configure wildcard DNS/TLS for *.example.com, and route the preview hostnames to the same Gateway port (3000). Preserve the original Host header and WebSocket upgrades. Upstream Content-Security-Policy and X-Frame-Options are preserved, so applications that prohibit embedding remain blocked by the browser.

Back up ./data and the stable configuration secret together using a consistent SQLite backup or a stopped-container volume snapshot. Keep backups private. Codex transcripts, credentials and uploaded attachments are remote data and need their own backup. Upgrades may rebuild in-memory subscriptions; MFA hosts require a manual connection afterward. The PWA service worker handles notifications and does not precache authenticated API responses or application bundles.

Troubleshooting

Symptom What to check
Host cannot connect Find its hostId/hostName in Gateway logs; distinguish proxy/TCP, SSH banner/handshake, authentication, SFTP and daemon errors.
MFA dialog does not appear Click the Host connection action and wait for the real SSH challenge. A handshake failure occurs before a code can be submitted.
CLI is updated but conversations still fail Check codex app-server daemon version in the remote login environment. The CLI and running daemon have separate versions.
“app server is running but is not managed” An unmanaged server owns the socket. Identify its process/launcher and stop it gracefully before starting the official daemon; do not delete sockets or kill every Codex process.
Upgrade transfer fails Check writable home/cache/install paths and SFTP availability. Failed staging is retained for the next attempt; corrupt archives are discarded after verification.
Browser preview fails Check wildcard DNS/TLS, Host/Upgrade forwarding, target reachability from the remote Host, and the preview's resource diagnostics.
Push does not arrive Check per-browser permission/subscription and account notification settings, then delivery logs. Web Push is attempted first; Bark requires its own configuration.

Logs distinguish CLI inspection, download/cache, SFTP progress and daemon readiness. Remove credentials and private content before posting diagnostics in an issue. See SECURITY.md for the trust boundaries.

Testing

E2E tests do not mock Codex app-server:

  • Docker builds the production Nuxt app and runs the app and browsers in separate containers.
  • SSH fixtures cover fresh standalone installation, migration from npm, daemon upgrades and real keyboard-interactive MFA.
  • Gateway connects to the target over SSH and starts or resumes a real Codex app-server.
  • Playwright verifies login, config, thread restore, realtime sync, mobile layout, diff rendering, dynamic requests, notifications, sub-agent UI, remote files, browser preview, and real tmux monitoring.

Run:

pnpm test:e2e

If the host machine does not have pnpm, use the containerized runner directly:

tests/e2e/run-in-containers.sh

Run the full E2E suite for changes involving SSH, RPC, WebSocket, thread state, config, upload, diff rendering, mobile layout, or app-server protocol handling.

Relationship With Codex

Codex Gateway targets the pinned official App Server release, including the experimental timeline APIs it uses. third_party/openai-codex/ is a source submodule for protocol and behavior reference, not the runtime shipped to remote hosts. The version gate, protocol parsers and installation fixtures are updated together. Gateway renders Codex state and delegates execution, approvals and daemon recovery to the official runtime.

Contributing

Issues and pull requests are welcome. Read CONTRIBUTING.md before changing SSH, RPC, realtime state, or app-server protocol behavior. Please report vulnerabilities privately according to SECURITY.md.

License

MIT

Explore related projects