Your machines. One self-hosted dashboard.

Run Claude Code, Codex, OMP, or any other CLI across your macOS and Linux machines. Roost v0.5.0 is released for accountless self-hosting, with automatic Tailscale Serve or direct HTTPS.

  • v0.5.0
  • self-hosted
  • macOS + Linux
The Roost control panel: sessions grouped by machine in the sidebar, a live terminal filling the workspace
One tab: every machine's live sessions on the left, a real PTY filling the workspace.

The problem

The machines are idle and the sessions are unreachable

There is a MacBook in a drawer, a Mac mini under the desk, and a Linux box in the corner. All of that capacity sits idle while one laptop does the work.

Meanwhile “remote” means opening a terminal, SSH-ing in, and hunting for the session you left running — assuming it survived the disconnect. From a phone it means nothing at all.

And an agent CLI is a single-session tool: one window, one machine, one thing at a time, with no way to see which of the five you started is waiting on an answer.

Fleet

A fleet, not a terminal app

One coordinator connects every supported machine you own. It is control plane only — auth, an append-only event log, and fan-out to every open browser — and the machine running it is usually a worker too. Every other macOS or Linux box joins as a worker.

Workers dial outbound and never listen, so a worker machine never opens a firewall hole, exposes an inbound port, or needs a publicly reachable name. The sidebar groups live sessions by machine and each machine carries tiles for CPU, memory, disk, and network from its own heartbeat, so “which box has capacity right now” is a glance instead of an SSH session.

Adding a machine is one command on an automatic-mode coordinator —roost add-machine --platform macos or linux — which mints a one-shot token valid for 24 hours and prints the enrollment command to paste on the new host. Machines join by pulling; the coordinator never SSHes out. See fleet.

The sidebar groups live sessions by machine, with per-machine CPU, memory, disk, and network tiles
Sessions grouped by machine, with live per-machine metrics.
A workspace: a tab bar of live sessions above one real terminal
Browse any machine's folders, press Open terminal here, and the workspace starts in that directory on that machine.

Terminal fidelity

Native, not a viewer

Streaming raw PTY bytes into a browser looks simple and corrupts in practice: every resize re-reflows history, and every reconnect duplicates or drops output. Roost uses the model server-side multiplexers use instead, and then adds the things that make a browser feel like a terminal emulator.

  • One authoritative grid, on the worker. The worker holds the grid and rebuilds it at a single agreed width when your viewport changes. The browser renders those cells as-is and never re-reflows history.
  • Reconnects splice instead of guessing. Every cell frame carries a monotonic seq, and a stale or unset viewer sequence is answered with exactly one authoritative full frame — so a reconnect can never duplicate or drop rows.
  • Scrollback is fetched on demand. History is never streamed. It is requested explicitly and served from the worker's retained buffer of roughly 10,000 lines per session, walked in slices so one backfill never blocks another session's live output.
  • Wide glyphs stay atomic. Column occupancy is stated on the wire, so a double-width CJK ideograph or emoji is one two-column span and no phantom continuation cell is ever emitted.
  • Links are real. OSC 8 hyperlinks travel as per-cell link identity. On top of plain output Roost infers URLs, owner/repo#123and bare #123 references, and file paths; Cmd- or Ctrl-click opens a path in the in-app file viewer, which honours the#L42 anchor.
  • The application decides about the mouse. Pointer gestures are forwarded only when the running program actually asked for them — DECSET 1000 and 1002 tracking, SGR 1006 encoding, focus events 1004 — so a click in vim or less is not silently swallowed.
  • Predictive echo, mosh-style. Above roughly 10 ms round trip, a keystroke is drawn immediately in a client-side overlay and reconciled against the authoritative frame that lands an RTT later. The overlay never reaches the worker, and alt-screen TUIs suppress it entirely.
  • Keyboard parity. The ⌘ and Ctrl shortcuts behave like a native app. A browser running on Windows gets a deliberately separate binding set — Alt and Alt+Shift chords — so a plain Ctrl plus letter still reaches the program in the PTY.

The whole data plane is described in terminal fidelity.

Agents

Any agent, any CLI

Roost never spawns, supervises, or owns an agent. There is no wrapper process, no transcript store, and no agent execution RPC — the CLI is an ordinary command in a shell PTY. Status reads and event waits observe that terminal without changing the boundary. That is what makes “any CLI” true rather than aspirational: a shell, a REPL,vim, htop, a build, ssh, tmux, and any coding agent, including Claude Code.

Ten agents are first-class in the launcher and in status detection — Codex, Gemini CLI, OpenCode, Cursor Agent, Amp, GitHub Copilot CLI, Droid, Grok, Pi, and OMP — beside a custom-command entry that launches anything you type. What the list adds is a status badge, not the ability to run; an unlabelled terminal is the normal case, not a failure.

Detection, its three tiers, and what the badges mean are inagents and status.

Layouts

Split panes, arranged the way you think

Drag a tab onto the edge of a pane to split right, left, up, or down, then drag the dividers to resize. Arrange applies a preset instead: Grid, Columns, Rows, Main + stack, or Equalize sizes.

In browsers on macOS and Linux, ⌘D and ⌘⇧D split,⌘⌥G / E / R / V /B arrange, ⌘1⌘8 jump to a tab with⌘9 for the last one in the pane, and ⌘↵ spotlights the focused pane. A browser on Windows uses a deliberately different set —Alt+Shift+D and Alt+Shift+S to split,Alt+1Alt+9 for tabs, Alt+Enter for spotlight.

Multiple terminals tiled in one workspace, auto-arranged to fill the screen
Several machines, several agents, one workspace.

Every device

Every device is a full client

The same web app runs on macOS, Windows, and Linux desktops, on iPhone and Android, and on iPad and Android tablets. There is nothing to install on the device you browse from, and nothing is cut down: it renders the same real PTY with full ANSI, colour, and scrollback.

A tablet with a keyboard keeps the desktop layout, panes, and shortcuts unchanged. A phone gets a layout designed for one thumb.

Desktop-grade on a tablet, the same real terminal and layout as a laptop
A tablet keeps the desktop layout, panes, and shortcuts.
A real terminal on a phone, with full ANSI, touch selection, and an on-screen key row
A real terminal on a phone — not a log viewer.
  • Terminal text is selectable by touch, and copy-on-select is a per-device toggle.
  • An on-screen key row carries esc, tab, backspace, home, end, page up and down, the arrows, enter, and a latchingctrl — so Ctrl+C is two taps, and closing the pad clears an armed ctrl instead of leaking it into the next thing you type.
  • Sessions become a swipeable deck of terminal cards: peek at the neighbour mid-gesture, commit on 40% of the card width or a fast flick.
  • The soft keyboard translates the layout instead of reflowing the terminal. Roost measures the covered area and shifts the content up by exactly that much, so the PTY keeps its size and grid while you type.
  • Add to Home Screen installs it as a standalone PWA with its own icon — and on iPhone and iPad that is also what makes OS notifications possible.

The full phone and tablet story is in phones and tablets.

Persistence

Sessions that don't die

A keeper subprocess hosts every PTY on a machine over a single capability-authenticated local endpoint, with one channel per session. Because it is a separate process, PTYs outlive the worker: a restarted or updated worker reattaches over the same endpoint and re-adopts its open sessions.

So drop WiFi, close the laptop, refresh the tab, or reopen the same session from a different machine — the process is still running, with full scrollback. The browser redials on capped backoff, sends the last event id it saw, and gets exactly the events it missed. When the coordinator itself restarts, every session is re-projected from the event log, because the log is the source of truth.

Status

See which agent needs you

Roost labels a terminal with the state of whatever agent is running inside it: working, needs input, ordone. It shows up in four places — the session's sidebar row, its tab, its card on mobile, and a rollup on the folder that contains it, for example 2 working · 1 needs input.

An in-app toast and an unseen count in the tab title need no setup at all. OS notifications, the kind that reach you when Roost is not the tab you are looking at, need one explicit grant per device, because browsers only prompt on a real click — and a device already viewing that session is deliberately left alone.

OMP and Pi report their own lifecycle over a per-worker Unix socket, validated against the reporting process id. The other eight are detected from pinned screen and OSC-title manifests plus a periodic process-tree scan, so an agent you started by hand still gets recognised. Nothing about status is persisted: a worker, coordinator, or browser restart re-derives it rather than leaving a stale badge behind.

Voice

Talk to your terminal

Typing a long prompt on a phone is miserable, so tap the mic and dictate. The recognized text is typed into the session as real input and sent, with no review step in between.

The zero-setup path is the browser's own speech recognition, which is a rough fallback. The recommended path is a Deepgram nova-3 key added once in Settings → Voice: it is stored on the coordinator and shared to every paired device, so you configure it once rather than per phone, and it can run a single language, automatic detection, or multilingual mode with live code-switching.

Roost also extracts terminal jargon from the screen you are dictating into and passes it to Deepgram as keyterm bias, so project vocabulary and identifiers transcribe as themselves rather than as phonetic mush.

Deployment

Self-hosted and released

Roost v0.5.0 is released for accountless self-hosting. You operate the coordinator on macOS or Linux and authorize browsers through QR, a one-shot bootstrap token, loopback registration, or approval from a device you already trust.

The managed deployment is qualified, not publicly launched.Production publishes no managed coordinator image and the shared dashboard origin is inactive. Email signup and Google authentication are off; managed accounts exist only when an operator creates them.

Each self-hosted browser mints an Ed25519 key pair with WebCrypto. The private key is non-extractable and persisted in IndexedDB, and requests are signed with short-lived device JWTs. Read the security modelor install Roost.

Pair a phone or tablet by scanning a QR; it signs itself in, nothing to type
Each browser keeps its own non-extractable device key.

Architecture

How it works

   Browser  (any device with a modern browser)
      │   Connect-RPC over HTTP/2, protobuf binary
      │   terminal data · session state, one connection
      ▼
 ┌─────────────────────────┐
 │ Coordinator  (Bun)      │   event-sourced SQLite · auth · session registry
 │ one HTTPS endpoint      │   fans live updates out to every open browser
 └───────────┬─────────────┘
             │   raw WebSocket · protobuf frames · workers dial outbound
      ┌──────┴──────────┐
      ▼                 ▼
   Worker             Worker        (Bun, one per machine)
   macOS              Linux
      │  a keeper subprocess hosts every PTY and outlives worker restarts

The browser and the coordinator fold the same foldEvent reducer over the same append-only event log, so they agree on session state by construction rather than by carefully mirroring a snapshot.

Install

Two commands on the first macOS or Linux machine

curl -fsSL https://raw.githubusercontent.com/cefege/roost/main/install-binary.sh | bash
"$HOME/.local/bin/roost" quickstart

macOS and Linux. The script verifies the release asset against its published .sha256 sidecar, then quickstart installs the coordinator, deploys a worker on the same machine, and opens your browser already authorized.

brew install cefege/tap/roost

macOS only. For automatic mode, run sudo tailscaled install-system-daemon && sudo tailscale up, followed by roost quickstart. Direct HTTPS instead uses the three endpoint flags shown in the install guide.

Landscape

Where Roost fits

Most agent terminals are one polished app on one machine, and most cloud agent platforms run on someone else's VM.

Roost sits between them: your own machines, real PTYs, and a browser client that happens to work on a phone.

The comparisons are factual, name names, and each one ends with when to use the other tool instead.

Roost vs the agent terminal landscape

FAQ

Questions people actually ask

Does Roost replace my terminal?
No. Roost replaces SSH-ing around to find a session, not the terminal itself. Every Roost session is a real shell PTY with full ANSI colour, scrollback, mouse modes and hyperlinks, rendered in a browser instead of a terminal emulator. If tmux or Zellij is how you like to work, run it inside a Roost session.
What ships in Roost v0.5.0?
v0.5.0 is the accountless self-hosted release for macOS and Linux coordinator and worker machines. It supports automatic Tailscale Serve and operator-managed direct HTTPS. The managed deployment is qualified but not publicly launched: production publishes no managed image, activates no shared dashboard origin, and keeps email signup and Google authentication off.
Does it work with Claude Code?
Yes. A Roost session is an ordinary shell PTY, so Claude Code runs in it exactly as it does in your own terminal, with its real interface rather than a chat wrapper. Roost never spawns, supervises or owns an agent process. Ten CLIs (Codex, Gemini CLI, OpenCode, Cursor Agent, Amp, GitHub Copilot CLI, Droid, Grok, Pi and OMP) additionally get a working / needs input / done badge; everything else runs fine, just unlabelled.
Do I need Tailscale to self-host?
Not for direct coordinator quickstart. With no endpoint flags, roost quickstart uses Tailscale Serve as the automatic convenience topology. Supply the HTTPS coordinator URL, absolute certificate path and absolute key path together to serve a browser-trusted certificate directly without Tailscale on the first coordinator. In v0.5.0, the extra-worker join script still requires a running Tailscale daemon even when it connects to that direct origin.
What does Tailscale provide to self-hosted Roost?
Automatic mode uses Tailscale for private coordinator reachability and convenient browser-trusted HTTPS without port forwarding. Direct mode leaves reachability, DNS and certificate issuance to you. A tailnet address is never an enrollment credential; every browser and worker still needs its own scoped one-shot grant or approved pairing.
Is my code uploaded to a managed sandbox?
No. The released product is self-hosted: the coordinator, workers and dashboard run on infrastructure you operate, and each shell PTY stays on the worker machine you connect. The qualified managed deployment is not publicly launched and does not provide vendor execution sandboxes. Optional integrations can still send the data they are configured for, such as dictation audio sent to Deepgram.
Does it run on Windows?
Windows is supported as a browser client, not as a Roost host in v0.5.0. This release publishes no Windows coordinator, worker, installer, join script or package, so there is no supported Windows host install, enrollment or update procedure while the Windows release tier is paused.
Can I use it from a phone?
Yes, and it is the same app rather than a cut-down companion. Add Roost to the home screen and you get a standalone PWA with touch text selection, an on-screen key row with a latching Ctrl, a swipeable deck of terminal cards, and a soft keyboard that shifts the layout instead of reflowing the terminal. On iPhone and iPad, installing to the home screen is also the prerequisite for OS notifications.
What happens when my laptop sleeps?
The work keeps running. PTYs live in a keeper subprocess on the worker machine that outlives worker restarts and updates, so closing the lid on the device you were browsing from does not touch the session. When you come back, on that device or another one, the browser reconnects, sends the last sequence number it applied, and receives exactly what it missed — nothing duplicated, nothing dropped, scrollback intact.
Can I sign up for managed Roost?
No. The managed deployment is qualified but not publicly launched. The shared dashboard origin is inactive, production email signup and Google authentication are off, and managed accounts exist only when an operator creates them. Install the released accountless self-hosted edition to use Roost today.
Is self-hosted Roost free?
Yes. Roost v0.5.0 is free and open source under GPL-3.0-only. It is accountless and runs the coordinator, workers and dashboard on infrastructure you operate.
How do I add a machine?
In either coordinator mode, use Settings, Machines, Add machine to create a one-shot pull command for a macOS or Linux worker. On an automatic Tailscale coordinator, roost add-machine with --platform macos or linux provides the equivalent CLI flow. The bootstrap token expires after 24 hours. In v0.5.0, join.sh still requires a running Tailscale daemon even when the worker connects to a direct HTTPS origin.
How do I update the fleet?
From a clean source checkout at the commit you pushed, roost push upgrades and proves the coordinator before touching the registered macOS and Linux workers, then deploys that exact commit and waits for a fresh post-update heartbeat from every target. Each host activates through a journal with a health proof and automatic rollback. A single machine can also self-update with roost update.