Try it in your browser β no install Β·
colliepwa.dev
A real Collie build running in the page against faked data.
A mobile web interface for terminal-based AI agents, served over Tailscale. Collie connects to one multiplexer per instance: Herdr, tmux, or zellij. Open the URL on your phone to check which agent needs input and respond directly from the mobile keyboard.
The input box uses a standard text field compatible with system voice dictation. Collie also includes built-in voice input that remains disabled until explicitly configured.
Features
- React Router + Vite with TypeScript, Tailwind, shadcn, and a Bun bridge
- Status dashboard ordered by pending user input rather than recent output
- Push notifications when an agent blocks on user input
- Quick actions and slash commands configured per agent
- Keypad for terminal control keys:
Esc,Ctrl+C, arrows, and modifier combinations - Output search and full conversation history beyond standard terminal scrollback
- Image uploads directly from the local camera roll
- Device pairing as the write credential: once a device is paired, every write needs its token
- Packs: several machines' Collies behind one URL, with operator-triggered failover
- Six UI languages and a per-device typeface setting
- Herdr session switching managed from the web interface
- PWA support running locally on loopback with no external accounts or cloud dependencies
Using Collie from a phone: the dashboard places agents that need input at the top. You can inspect
spaces, tabs, and panes. Long-press a pane pill or tab chip to rename or close it; Claude panes
reflect names set via /rename. Tap to answer an AskUserQuestion prompt, switch between herds,
and receive push notifications when an agent blocks on input.
The interactive demo runs the web client in your browser against mock data without installation.
I wanted to check on my agents from my phone. The usual route is Termux to SSH
in and attach to the terminal multiplexer session. Driving a TUI through on-screen controls is
clumsy: special keys are fiddly, Ctrl/Esc/arrows require awkward chords, and simple text input
fights the soft keyboard. I wanted a mobile interface instead of a terminal shoehorned onto a
touchscreen. Collie lets you tap the agent that needs input, type normally, and send Esc or
Ctrl+C with one thumb.
Collie is for developers running AI agents in a terminal multiplexer who want to resume sessions from a phone. Herdr is the primary supported target in 1.0. Support for tmux and zellij is experimental: both run, but testing is limited to a single operator on one machine. If you use either, bug reports are welcome, including reports of working setups.
The setup assumes a Tailscale tailnet. Your phone and host must share a
tailnet, with tailscale serve configured as the default ingress. Collie is single-user: it
supports one operator on one tailnet, with no multi-tenant authentication. Do not use it for shared
or public access. Read the security section below before running it.
Collie provides remote shell access to your machine by design. A single Collie API call sends
arbitrary keystrokes directly into a live terminal pane. Anyone with access to the URL can read pane
output (source code, secrets, environment variables, agent output) and execute arbitrary commands
with your full user privileges. There is no sandbox and no command allow-list, as these would defeat
the core workflow. Treat the URL as a root login: bind it strictly to your tailnet, set
COLLIE_TRUSTED_USER, and pair only the physical phone you are using. Read
docs/security.md for details on the security model, defense layers, and
device gating before running the service.
π« Never
tailscale funnelthis:funnelexposes the port to the public internet, whereasservelimits access to your private tailnet. Do not funnel Collie under any circumstances.
Run this on the host, not your phone. It requires curl, tar, and a sha256 utility. It needs no
compiler toolchain and does not ask for sudo:
curl -fsSL https://colliepwa.dev/install.sh | shThe script downloads the latest release for your platform, verifies the sha256 checksum, installs
the files, and puts collie on your PATH. It then prints the remaining manual steps: seed a config,
then run collie start. You do not need to specify a multiplexer ahead of time. On its first run,
collie start detects Herdr, tmux, and zellij, then prompts for your choice. If you prefer to build
from source, docs/install.md covers the manual build, Herdr routes, the
requirements table, and what the initial run writes to the host.
| Install | Requirements, the two ways in β fresh install or through Herdr β first run, and opening it on your phone |
| Security | What a Collie exposes, the defenses, and pairing a device as the write credential |
| Configure | The .env, your own slash commands, keys, quick replies and typefaces; appearance, Zen mode, language |
| Deployment | Front doors other than the default: an identity-aware proxy, a reverse proxy with no Tailscale, an off-host ingress, several Collies on one host (one per user, or several instances for one user), and a pack's standby door |
| Commands | Every collie verb, putting collie on your PATH, and the Herdr actions that mirror the verbs on a Herdr-managed install |
| tmux and zellij | Running Collie without Herdr β both walkthroughs, what each multiplexer can answer, and agent beacons. Experimental in 1.0; bug reports wanted |
| Packs | Several machines' Collies behind one URL: invite, join, deputy, failover |
| Voice input and Web Push | The microphone in the composer, and notifications when an agent is waiting on you |
| Manage & update | Update, cross a major, stop, uninstall, and upgrading a 0.x install to 1.0 β each command in both spellings |
| Troubleshooting | Symptoms in the words you would actually search for |
Repository-level specifications live at the root: ARCHITECTURE.md Β·
docs/deployment.md Β· MUX_CONTRACT.md Β·
PACK_PROTOCOL.md Β· HERDR_API.md Β·
DESIGN.md Β· CONTRIBUTING.md.
Collie always binds loopback only; what changes between deployments is what sits in front
of it and how a request proves who it is. Variant A is the default and sits below; the other four
are in docs/deployment.md. Pick one.
The happy path from Install. tailscale serve terminates TLS on your MagicDNS name and
injects Tailscale-User-Login; set COLLIE_TRUSTED_USER to your tailnet login and Collie
rejects anyone else.
# in your .env
COLLIE_TRUSTED_USER=you@example.com- Granularity: the tailnet person, not the device.
- Why it's safe on bare
tailscale serve: serve is the trusted injector ofTailscale-User-Loginβ it sets that header itself and a client can't forge it through the proxy. - Nothing else to configure; origins match automatically on the MagicDNS name.
- Want per-device control without standing up a proxy? Pair the device β it composes on top of this variant.
This is the right choice unless you specifically need a proxy in the path. If you do, or if Tailscale
isn't in the path at all, docs/deployment.md has the rest:
- B β identity-aware proxy, authorised by device β a proxy on this host; some devices drive, others watch.
- C β reverse proxy as the only front door β no Tailscale anywhere in the path.
- D β off-host identity proxy over the tailnet β one central ingress node fronting Collie among your other services.
- E β any other mesh or tunnel β NetBird, ZeroTier, Cloudflare Tunnel: you own the ingress, Collie publishes nothing.
The bridge runs on Windows against the Herdr Windows beta; the launcher does not. Herdr on
Windows exposes its control socket as a named pipe derived from the full socket path instead of an
AF_UNIX socket. Collie connects via node:net rather than Bun.connect using a single shim,
bridge/dial.ts, which documents the path mapping.
Operational details:
- Run the bridge directly with
bun run bridge/index.ts. There is no systemd unit. Herdr action buttons invokebash, requiring Git Bash onPATH. The manifest lists onlylinuxandmacossupport to avoid exposing actions that might fail silently. tailscale serveintegration is unavailable on Windows. Follow Variant C: bind to loopback, place your own ingress in front, and setCOLLIE_PUBLIC_HOSTS. The rules in Β§Security still apply.- Set
COLLIE_MULTI_SESSION=off, as session discovery relies on POSIX paths. - The socket path defaults to
%APPDATA%\herdr\herdr.sock. Override it withHERDR_SOCKET_PATH. Explicit\\.\pipe\β¦values pass through directly.
Lifecycle management: The bridge added named pipe support in 0.15.0. An unsupported,
community-maintained Task Scheduler configuration for start, stop, and update routines is available
in contrib/windows/.
Verification: The bridge logs [events] stream up on startup. Event streaming runs over the
pipe, providing real-time updates without falling back to polling.
COLLIE_HERDR_DIAL=net forces the node:net dialer on Linux and macOS. This allows testing the
Windows connection path without a Windows environment; bridge/dial.test.ts relies on it.
A small Bun process sits between your phone and your multiplexer β the browser never touches the multiplexer.
phone (PWA)
β HTTPS over the tailnet
βΌ
tailscale serve terminates TLS, injects the identity header
β 127.0.0.1:PORT (the bridge binds loopback only)
βΌ
Collie bridge (Bun) serves the UI + a small JSON API; polls the multiplexer
β one mux adapter, chosen per install
βΌ
the multiplexer owns the panes, agents and terminal state
Herdr Β· tmux Β· zellij
Under Variant C a
reverse proxy replaces the tailscale serve box; everything below the front door is identical.
- Only the adapter touches the multiplexer (
bridge/mux/<name>/β Herdr dials a Unix socket, tmux and zellij shell out to their CLIs); everything else speaks the bridge's HTTP API. What every adapter must answer isMUX_CONTRACT.md. - Polling is still the model β the bridge takes one snapshot per tick from the adapter and the browser polls
/api/snapshot; where the multiplexer offers an event stream (Herdr does) it only pokes the bridge's poll to go faster, it never replaces it. No resync logic. - Actions are plain HTTP β a reply or key
POSTs to/api/pane/:id/{reply,keys}, and the adapter types it into a real terminal (hence the security posture). - The UI is a static PWA β Vite builds
web/dist, served from disk, so a rebuild is live with no restart. - A second Collie is a peer, not a second bridge β one machine's bridge mirrors one multiplexer, and a lead reads its peers over the pack link (
PACK_PROTOCOL.md).
Full design rationale in ARCHITECTURE.md.
Clone and build the repository (Install β the same result, from source), then edit in place.
- Every verb is implemented once, in
cli/, and runs asbin/collie <verb>(Commands). No other layer implements verbs.scripts/collie-ctl.shis a bootstrap shim that compiles the binary and passes your argv. The Herdr adapter'sherdr-plugin.tomlis a thin registration file whose[[actions]]call that shim (Herdr actions). Both files contain explanatory comments. - Development loop asymmetry:
web/rebuilds appear immediately without a restart because the bridge servesweb/distdirectly from disk. Changes tobridge/requiresystemctl --user restart collie. Build, test, and versioning rules live inCLAUDE.md. Versioning is enforced by git hooks, so check the document before committing. - Multiplexer adapters:
MUX_CONTRACT.mddefines the interface an adapter must implement, andMUX_CONTRIBUTING.mdcovers the integration boundaries.ARCHITECTURE.mdΒ§3 explains why Collie runs as a supervised service instead of an embedded pane, which keeps the Herdr manifest limited to[[actions]]and[[build]]. - Pull requests:
CONTRIBUTING.mddocuments base branches (mainfor bugfixes,v1for features), CI checks, and version bump requirements.
A development page that renders the web components across mock states (boot, idle, dashboard, pack, settings) without a running agent. This lets you inspect visual elements like banners, marks, boot screens, and lock states without manually reproducing each condition.
cd web && COLLIE_DEV_HOSTS=bluefin,localhost bun run playground
Open http://<host>:5199/playground.html. Port 5199 redirects root requests to the playground and
disables /api, preventing requests to a live Collie instance. Vite targets only index.html
during production builds, keeping playground.html and src/playground/ out of dist and the PWA
precache. This exclusion is tested in src/playground/playground-entry.test.ts.
To add a state, add a <Section> in src/playground/app.tsx and the corresponding mock data in
src/playground/fixtures.ts.
For Herdr adapter development, refer to upstream documentation for the plugin system:
authoring Β·
CLI reference Β·
example plugins. Collie's socket
integration is documented in HERDR_API.md.
- All how-to pages:
docs/ - Deployment variants B through E:
docs/deployment.md - Architecture and design rationale:
ARCHITECTURE.md - Multiplexer query interface and capabilities:
MUX_CONTRACT.md - Lead-to-peer pack protocol:
PACK_PROTOCOL.md(topology diagram in Β§2) - Pack recovery from a phone after lead failure:
docs/deployment.mdβ the standby door - Verified Herdr socket API:
HERDR_API.md - Operations, versioning, and project conventions:
CLAUDE.md - Contribution guidelines:
CONTRIBUTING.md - Release history:
CHANGELOG.md






