Remote control service for codex app-server with a mobile-first web interface
  • Rust 97.9%
  • Shell 1.9%
  • Makefile 0.2%
Find a file
florian cbe655af28
Some checks failed
CI / Build (aarch64-apple-darwin) (push) Waiting to run
CI / Build (aarch64-unknown-linux-gnu) (push) Waiting to run
CI / Build (x86_64-pc-windows-gnu) (push) Waiting to run
Prerelease Test Build / publish (push) Blocked by required conditions
CI / conventional-commits (push) Has been skipped
CI / quality (push) Successful in 1m13s
Prerelease Test Build / prepare (push) Successful in 2s
Prerelease Test Build / Build (aarch64-apple-darwin) (push) Waiting to run
Prerelease Test Build / Build (aarch64-unknown-linux-gnu) (push) Waiting to run
Prerelease Test Build / Build (x86_64-pc-windows-gnu) (push) Waiting to run
Release-plz / Release-plz release (push) Failing after 36s
CI / Build (x86_64-unknown-linux-gnu) (push) Successful in 1m18s
Release-plz / Release-plz PR (push) Failing after 32s
Prerelease Test Build / Build (x86_64-unknown-linux-gnu) (push) Failing after 2m4s
Merge pull request 'chore: Configure Renovate' (#7) from renovate/configure into main
Reviewed-on: #7
2026-06-14 02:53:00 +02:00
.github/workflows fix: switch windows release target to gnu 2026-04-04 17:28:40 +02:00
assets docs: integrate logo and emoji headings 2026-04-04 18:15:26 +02:00
crates/esper-relay chore: release v0.1.2 2026-04-04 18:43:57 +02:00
docs fix: switch windows release target to gnu 2026-04-04 17:28:40 +02:00
scripts chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
vendor fix: allow windows gnu cgo warnings 2026-04-04 17:36:13 +02:00
.gitignore chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
.release-plz.toml chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
AGENTS.md docs: integrate logo and emoji headings 2026-04-04 18:15:26 +02:00
Cargo.lock chore: release v0.1.2 2026-04-04 18:43:57 +02:00
Cargo.toml chore: release v0.1.2 2026-04-04 18:43:57 +02:00
CHANGELOG.md chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
CONTRIBUTING.md chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
LICENSE chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
Makefile chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00
README.md docs: integrate logo and emoji headings 2026-04-04 18:15:26 +02:00
renovate.json Add renovate.json 2026-06-14 00:43:34 +00:00
rust-toolchain.toml chore: bootstrap esper-relay release automation 2026-04-04 15:12:51 +02:00

esper-relay logo

esper-relay

Secure remote Codex control from your phone

crates.io version Rust 1.85+ Tailscale secured access Mobile-first web UI MIT license


esper-relay keeps codex app-server running on a machine you trust and gives you a mobile-friendly remote control surface over Tailscale. You get your Codex sessions on your phone, without exposing the host directly to the public internet.

📱 See It First

Mobile thread view in esper-relay Mobile prompt composer in esper-relay Mobile session controls in esper-relay

Thread view, prompt composer, and session controls on a phone-sized screen.

Why It Exists

  • Remote Codex access: resume threads, send prompts, and handle approvals from your phone.
  • Private by default: keep remote access inside your tailnet instead of opening public ports.
  • Host-side supervision: let esper-relay keep codex app-server running instead of managing it manually.
  • Mobile-first UI: use a touch-friendly web interface instead of remoting into a full desktop.
  • Better file links: referenced host files open through the relay instead of dead local filesystem URLs.

📦 Install

crates.io

cargo install esper-relay

If you want a prebuilt archive instead, use GitHub Releases.

🚀 Quick Start

What You Need

  • Codex CLI installed and authenticated so codex app-server works on the host machine.
  • Tailscale installed on the host if you want remote access from your phone.
  • Tailscale installed on your phone if you want to use the tailnet URL there.
  • Rust and Cargo available for cargo install.

Local-only setup

esper-relay --daemon

Then open http://127.0.0.1:3000.

Remote setup over Tailscale

Create ~/.config/esper-relay/config.toml:

bind_addr = "127.0.0.1:3500"
ready_timeout_ms = 60000

[tailnet]
enabled = true
hostname = "esper-relay"
listener_mode = "https"
listen_addr = ":443"
advertise_tags = ["tag:esper-relay"]
oauth_client_secret = "tskey-client-REPLACE_ME"

Then start the relay:

esper-relay --daemon

This gives you:

  • local access on your configured bind_addr
  • tailnet-only https://<hostname>.<tailnet>.ts.net/ access once MagicDNS and tailnet HTTPS are enabled

Stop cleanly

esper-relay --stop

When tailnet.oauth_ephemeral = true, a clean stop logs the ephemeral node out immediately instead of waiting for delayed tailnet cleanup.

🧭 How People Use It

  1. Run esper-relay on the machine where Codex lives.
  2. Open the web UI locally or through your tailnet.
  3. Resume a thread or start a new one from your phone.
  4. Send prompts, approve actions, and keep the session moving without sitting at the host machine.

⚙️ Configuration

Essentials

  • ~/.config/esper-relay/config.toml is loaded automatically when it exists.
  • bind_addr controls the local web UI address.
  • app_server_cmd defaults to codex app-server.
  • tailnet.listener_mode = "https" replaces the raw tailnet listener with tailnet-only HTTPS on :443.
  • tailnet.advertise_tags is required when you use tailnet.oauth_client_secret.
  • tailnet.oauth_ephemeral defaults to true; set it to false if you want persistent OAuth-derived authkeys instead.

Environment Variables

Full environment variable reference
  • ESPER_RELAY_BIND (default: 127.0.0.1:3000)
  • ESPER_RELAY_APP_SERVER_CMD (default: codex app-server)
  • ESPER_RELAY_READY_TIMEOUT_MS (default: 8000)
  • ESPER_RELAY_RESTART_BASE_DELAY_MS (default: 500)
  • ESPER_RELAY_RESTART_MAX_DELAY_MS (default: 8000)
  • ESPER_RELAY_EVENT_BUFFER (default: 256)
  • ESPER_RELAY_TRUSTED_LAN (default: true)
  • ESPER_RELAY_TAILNET_LISTENER_MODE (default: tcp, valid values: tcp, https)
  • ESPER_RELAY_TAILNET_OAUTH_EPHEMERAL (default: true, only applies to tailnet.oauth_client_secret)

🛠️ Build Options

GitHub Releases

Stable and prerelease archives are published on GitHub Releases for:

  • x86_64-unknown-linux-gnu
  • aarch64-unknown-linux-gnu
  • x86_64-pc-windows-gnu
  • aarch64-apple-darwin

Build from source

git clone https://github.com/beisel-it/esper-relay.git
cd esper-relay
make run

🔌 Technical Reference

Core Commands

  • make run
  • make fmt
  • make fmt-check
  • make clippy
  • make test
  • make ci-local
  • make fetch-codex-sources
  • make fetch-themes

API Surface

  • POST /api/sessions
  • GET /api/themes
  • GET /api/files/content?path=/absolute/path[&line=n][&column=n]
  • GET /api/sessions/latest
  • GET /api/sessions/{session_id}
  • GET /api/sessions/{session_id}/timeline
  • GET /api/sessions/{session_id}/events (WebSocket)
  • POST /api/sessions/{session_id}/turns
  • POST /api/sessions/{session_id}/interrupt
  • POST /api/sessions/{session_id}/approvals/{approval_id}
  • POST /api/sessions/{session_id}/server-requests/{request_id}/respond
  • GET /api/threads?limit=50
  • POST /api/threads/{thread_id}/resume

Approval and tool-input prompts surface the related turn/item context from the app-server payload, and explicit Codex file-reference links are rewritten through the relay so remote users can request host files safely through GET /api/files/content.

Operational Endpoints

  • GET /health
  • GET /ready
  • GET /metrics

Repository Layout

.
├── .github/workflows/
├── crates/esper-relay/
├── docs/
├── scripts/
└── vendor/

Codex Source References

  • Canonical captures: docs/sources/codex/
  • Latest snapshot metadata: docs/sources/codex/manifest.json
  • Primary protocol reference: docs/sources/codex/snapshots/.../remote/app-server.md

🔐 Maintainers

  • Release and publishing details: docs/releasing.md
  • release-plz manages release PRs, tagging, and crates.io publish.
  • Stable binaries are published from v* tags, and prerelease binaries are published from pushes to main.