Remote terminal viewer for cmux — access your cmux workspaces from anywhere via iPhone PWA.
cmux-remote is a lightweight bridge that lets you monitor and switch between cmux terminal workspaces from your iPhone. Designed for developers who use AI-assisted coding and primarily need to watch terminal output on the go.
iPhone PWA (React + xterm.js)
↕ WebSocket
Bridge Server (Bun + Hono)
↕ Unix Domain Socket
cmux (~/.../cmux.sock)
- Real-time terminal display via xterm.js with 1-second polling
- Gesture navigation — 2-finger swipe up/down to switch workspaces, left/right to switch panes
- PWA — install to home screen for a native app experience
- Lightweight — ~144KB gzipped client, zero cloud infrastructure required
- Secure — deploy behind Tailscale or Cloudflare Tunnel
- cmux running on your local machine
- Bun runtime
- Network tunnel for remote access (e.g., Tailscale, Cloudflare Tunnel)
cd client
bun install
bun run buildcd server
bun install
bun run startThe server starts on http://localhost:3456 by default.
Open http://<your-host>:3456 in Safari and add to home screen.
Copy server/.env.example to server/.env and adjust as needed — Bun loads it automatically.
| Environment Variable | Default | Description |
|---|---|---|
PORT |
3456 |
Bridge server port |
CMUX_SOCKET_PATH |
~/Library/Application Support/cmux/cmux.sock |
cmux Unix socket path |
CMUX_BIN_PATH |
/Applications/cmux.app/Contents/Resources/bin/cmux |
cmux binary path |
CLOUDFLARE_TUNNEL_ENABLED |
false |
Spawn a Cloudflare Tunnel for the bridge server |
CLOUDFLARE_TUNNEL_TOKEN |
(empty) | If set, runs the named tunnel matching that token. If empty, a free Quick Tunnel (*.trycloudflare.com) is started instead |
CLOUDFLARED_BIN |
cloudflared |
Path to the cloudflared binary |
NGROK_ENABLED |
false |
Spawn an ngrok tunnel for the bridge server |
NGROK_AUTHTOKEN |
(empty) | If set, ngrok is started with this authtoken. If empty, ngrok uses the authtoken already configured on the system (ngrok config add-authtoken <token>) |
NGROK_BIN |
ngrok |
Path to the ngrok binary |
When CLOUDFLARE_TUNNEL_ENABLED or NGROK_ENABLED is true, the bridge server spawns the corresponding CLI as a child process and prints the public URL to its log output:
[cloudflared] public URL: https://random-words.trycloudflare.com
[ngrok] public URL: https://abcd-1-2-3-4.ngrok-free.app
Both tunnels can be enabled simultaneously. Without a token, each provider's free tier is used (Cloudflare Quick Tunnels require no account; ngrok still requires a free account, but the authtoken can be supplied either via NGROK_AUTHTOKEN or once-globally with ngrok config add-authtoken).
# Terminal 1: Start the server in watch mode
cd server && bun run dev
# Terminal 2: Start the client dev server (with proxy to bridge server)
cd client && bun run devThe Vite dev server proxies /ws and /health to localhost:3456.
# Client tests
cd client && bun run test
# Server tests
cd server && bun testBuilt with Bun + Hono. Handles:
- WebSocket relay — transparent JSON-RPC proxy between the PWA and cmux Unix socket
- CLI fallback — some methods (e.g.,
surface.read_text) use thecmuxCLI for reliability - Static file serving — serves the built PWA from
client/dist/ - Health check —
GET /healthreturns server and cmux socket status
Built with React 19 + TypeScript + Vite. Components:
| Component | Role |
|---|---|
Terminal |
xterm.js terminal renderer |
Header |
Workspace name + hamburger menu |
Drawer |
Workspace list sidebar (responsive) |
StatusBar |
Connection status indicator |
Hooks:
| Hook | Role |
|---|---|
useWebSocket |
Connection management with exponential backoff reconnect |
useCmux |
cmux JSON-RPC wrapper |
useGesture |
Hammer.js 2-finger gesture handling |