Watch the yard from your pocket.

CC Dispatch attaches to the Claude Code, Codex and Grok sessions already running in iTerm2. See who's working, answer the one that's asking, get pinged when one finishes. Nothing restarts.

macOS + iTerm2Python 3.11TailscaleApache-2.0
THE YARD my-mac ⌄
4 panes62%
Edit auth/middleware.py
api-server 0:19
Bash vitest --coverageBash rm -rf dist/
dashboard-ui 4:07
apply terraform plandone · 12 changed
infra-scripts 12:41
awaiting follow-up
data-pipeline 2h 10m
dashboard-ui wants to run
rm -rf dist/
AllowDeny
FleetUsageHistoryAuth

the problem

You left the desk. The fleet didn't.

A prompt is a full stop.

One agent wants to run a command. Until you say yes, that whole session sits idle. The other five carry on without you knowing which one stalled.

Status is invisible from the sofa.

Working, stuck, or done? From across the room a terminal is just a rectangle. You end up walking back to check.

Reconnecting loses context.

Most remote tools spin up a fresh session. CC Dispatch attaches to the one that's already running, with its history and its trust.

what it does

Everything the yard needs.
Nothing it doesn't.

Attach, never restart

Reads the panes that already exist through the iTerm2 Python API. Kill the server and the fleet is exactly as it was.

Answer prompts from anywhere

See exactly what an agent wants to do. Allow, deny, or pick an option. Numbered menus and checkbox lists both work.

A push when it needs you

Web-push the moment a session finishes or stalls on input. No app open, no polling on your side.

Send the next thing

Type a follow-up, dictate it, or drop a file into the pane. Switch a Claude session's permission mode, effort, or model.

Passkey only

The QR at startup carries a single-use bootstrap token. It registers your first passkey and then it's spent. Nothing else gets in.

Loopback behind Tailscale

The server binds 127.0.0.1. tailscale serve terminates TLS and proxies it to your Tailnet. No listener for a stranger to find.

Secrets on request

An agent runs dispatch-auth request github. Your phone buzzes. You approve. The key is released from an encrypted vault, just in time, and never lands on disk in plaintext.

Claude, Codex, and Grok

Each pane gets its own critter, coloured by what's running in it. Claude-only controls hide themselves on the others.

install

Six commands.
Then scan.

The server runs on the Mac that owns the panes. Your phone reaches it over your Tailnet. Everything below is copy-paste.

1

Clone and install into a venv.

git clone https://github.com/charlie-818/claude-dispatch.git
cd claude-dispatch && make venv
2

Turn on iTerm2's Python API. Settings → General → Magic → Enable Python API.

3

Install the fleet-status hooks so every pane shows up.

make hooks   # then merge examples/settings.snippet.json into ~/.claude/settings.json
4

Publish the port over your Tailnet with TLS.

tailscale serve --bg 8788
5

Start the server from inside an iTerm2 window, so it inherits ITERM2_COOKIE.

make run
6

Scan the QR it prints, register a passkey, add the app to your home screen.

After that it's passkeys only. The token in the QR is already spent.

the fence

Four things that are true
by construction.

reach

Loopback only. Tailscale is the only way in, and it terminates TLS before the server ever sees a packet.

identity

Passkeys require a real HTTPS origin, so nobody registers one over a bare IP. The bootstrap token dies after one use.

writes

A pane only receives keystrokes if its foreground job is a known agent. Scratch shells are unreachable, on purpose.

secrets

Integration keys sit in a Fernet-encrypted vault. The key lives in the macOS login keychain, not in the repo, not on disk.

What it does not protect against, and every control with the line of code that enforces it: the threat model.

contribute

Small codebase.
Real fleet needed.

One aiohttp server, one PWA, a pytest suite that runs offline. The parsers and gates are unit-tested; the iTerm2 side you check by hand against your own panes.