# How remobi works
remobi is a small web terminal stack tuned for phone-sized control of an existing terminal workflow. The server runs a single local PTY-backed command, the browser renders that terminal with `xterm.js`, and the remobi overlay adds the mobile controls on top.
This page is the high-level view. For the transport, message protocol, and request lifecycle, see [Networking and WebSocket flow](networking-and-websockets.md).
## System view
```mermaid
flowchart LR
Phone[Phone or desktop browser]
Tunnel[Trusted access layer
Tailscale / VPN / tunnel]
Server[remobi server
Hono HTTP + WebSocket]
Session[SharedTerminalSession
fan-out + snapshot state]
Pty[node-pty]
Cmd[tmux or custom command]
Overlay[remobi overlay
touch controls + gestures]
Xterm[xterm.js]
Phone --> Tunnel
Tunnel --> Server
Server --> Overlay
Server --> Xterm
Server --> Session
Session --> Pty
Pty --> Cmd
```
## Main pieces
| Piece | Role |
| --- | --- |
| Browser client | Loads the HTML shell, boots `xterm.js`, opens `/ws`, and renders terminal output |
| remobi overlay | Adds toolbar, drawer, gestures, reconnect handling, and mobile viewport behaviour |
| Hono server | Serves `/`, `/ws`, and optional PWA assets with security headers |
| `SharedTerminalSession` | Owns the PTY, mirrors terminal state, and fans output out to every browser client |
| `node-pty` | Spawns the local command and bridges raw terminal I/O |
| tmux or custom command | The actual program remobi exposes |
## Runtime boot path
`remobi serve` owns the whole path now. It does not launch `ttyd` or patch someone else's HTML.
```mermaid
flowchart TD
Start[remobi serve]
Bundle[Bundle browser client
JS + CSS in memory]
Html[Render HTML with inline config,
version, CSS, and CSP nonce]
Routes[Create Hono routes
/, /ws, manifest, icons]
Listen[Start HTTP server]
Spawn[Create SharedTerminalSession]
PTY[Spawn node-pty process
default: tmux new-session -A -s main]
Mirror[Mirror PTY output into
xterm-headless serializer]
Ready[Ready for browsers]
Start --> Bundle
Bundle --> Html
Html --> Routes
Routes --> Listen
Listen --> Spawn
Spawn --> PTY
PTY --> Mirror
Mirror --> Ready
```
## What is shared vs per-browser
- The PTY session is shared. Multiple browsers can attach to the same terminal session.
- Terminal output is broadcast to all connected clients.
- Each newly connected browser receives a serialized snapshot first, then live output.
- The overlay state is local to each browser. Font size, focus, reconnect UI, and viewport handling stay in the client.
## Where the code lives
| Area | Notes |
| --- | --- |
| `src/serve.ts` | HTTP server, WebSocket upgrade, headers, origin checks, shutdown |
| `src/session.ts` | PTY lifecycle, state mirroring, snapshots, fan-out to clients |
| `src/session-protocol.ts` | JSON message types and bounds checks |
| `src/client-entry.ts` | Browser bootstrap, `xterm.js`, WebSocket client, snapshot/output handling |
| `src/index.ts` | Overlay initialisation on top of the terminal instance |
## Why the snapshot layer exists
The browser does not become the source of truth for terminal state. `SharedTerminalSession` keeps a headless xterm mirror on the server so a newly attached browser can be brought up to date before live output resumes.
That gives remobi two useful properties:
- opening the same session from another device does not start a second shell
- reconnecting clients can rebuild the visible terminal without the PTY needing to replay history itself