- Once
- Nothing to do after setup.
- Phone approvals
- Yes
- Replies
- Yes
Start it with pilothouse run claude to reply and watch from your phone.
Pilothouse sits between your coding agents and you. It checks your rules before every tool call, and when a request needs a person it finds you: in the terminal, on your phone, in herdr or in Telegram. This page walks through every part, one step at a time.
Written for Pilothouse 0.4.6. Commands work on macOS and Linux.
Pilothouse runs on the computer where your agents run. Each agent reports to it through a small hook or plugin. Pilothouse checks your rules first, then decides where a request should go. Nothing passes through a Pilothouse server, because there isn’t one.
Your rules answer allow, ask or deny. Any deny wins. If no rule matches, the agent’s own settings decide.
At the desk, the agent’s own dialog shows as usual. When you’re away, Pilothouse holds the request and notifies your devices. The first answer wins.
Each card links to the steps for that feature.
One binary for macOS or Linux. No account, nothing to sign up for.
brew install sabr-software/tap/pilothouse
# or: curl -fsSL https://pilothouse.dev/install.sh | shIt connects every agent it finds (hooks and the OpenCode plugin, merged with what you have and backed up), installs the background service so Pilothouse starts at login, and checks everything.
pilothouse setup # hooks for every agent, the background service, checksPlain claude works and follows your rules. Starting through Pilothouse adds replies from your phone, interrupt, a live terminal, and an agent that keeps running when you close the window.
pilothouse run claude # or: codex, opencode, grok
pilothouse run -- claude --continueDoctor lists what works and what to fix, one line each.
pilothouse doctorWant to look around first? pilothouse demo runs the whole app with pretend agents and touches nothing.
pilothouse setup (or pilothouse hooks install) connects all four. Some agents ask you to trust the hooks once.
Start it with pilothouse run claude to reply and watch from your phone.
Its hooks honour deny only; allow and ask go to Codex’s own prompt, which Pilothouse routes to you.
Activity, and tokens and cost in Insights.
Activity only. Grok has no permission-request hook, so approvals stay in its terminal.
The full matrix is in How it works.
Your phone talks straight to your computer over your own Tailscale network. Nothing is exposed to the internet, and there’s no account.
Log in to the same tailnet on the computer and the phone. In the Tailscale admin console, turn on MagicDNS and HTTPS certificates. Phones need HTTPS for notifications and for the signing key.
This runs Tailscale Serve and saves the address.
pilothouse remote tailscale
# → https://studio.your-tailnet.ts.netiPhone and iPad: open the address in Safari, then Share → Add to Home Screen, and open Pilothouse from the Home Screen. Do this before pairing: the installed app keeps its own storage. Android: Chrome offers Install app.
On the computer, run pair. In the installed app, tap Scan QR code and point it at the code (or type the code). Don’t use the Camera app: it opens Safari instead of the app.
pilothouse pairThe app offers it right after pairing. If iOS ever clears the app, Face ID or Touch ID brings the signing key back without pairing again.
Tap Set up notifications, allow them, then send a test.
pilothouse notify testAnswering: press and hold Allow for a second (a stray tap never approves anything), or Deny with a note to the agent. Questions show their options; plans can be approved or sent back with what to change.
Every device pairs on its own and gets its own signing key. Pair as many as you like: a tablet, a second phone, a laptop browser. They all see the same inbox, and whichever answers first wins.
Same as the phone: open the address, install the app, then run pilothouse pair again and scan or type the new code. Each code works once, for ten minutes.
That computer must be on your tailnet. Open the address, then pair with a fresh code.
One command opens the app here, signed in with its own key.
pilothouse open # this computer’s browser, signed inRevoking a device signs it out and deletes its key and notifications. You can also manage devices in the app under Settings.
pilothouse devices # list paired devices
pilothouse devices revoke <id>When a request needs you and you’re away, Pilothouse notifies every channel you set up at once. When it’s answered anywhere, the others update to show the outcome.
| Channel | Answer from it? | Set up |
|---|---|---|
| Web Push (the app) | Yes | Pairing, above |
| Telegram | Yes: buttons, options, plans, replies | pilothouse notify telegram |
| ntfy | Yes: Allow and Deny buttons | pilothouse notify ntfy |
| Slack, Discord | No, it links to the app | notify.webhooks in config |
| Desktop | No, it brings you to the terminal | On by default |
The command walks you through making a bot with @BotFather and shows a one-time code to send it. Only that private chat is answered.
pilothouse notify telegramPick a long, random topic: on ntfy.sh, anyone who knows it can read the messages. The buttons call back over your tailnet.
pilothouse notify ntfyAdd incoming-webhook URLs to ~/.pilothouse/config.yaml.
notify:
webhooks:
- https://hooks.slack.com/services/T000/B000/XXXX
- https://discord.com/api/webhooks/123/abcTelegram, Slack, Discord and ntfy.sh see the text you send them. Telegram answers aren’t device-signed. For sensitive repositories, answer in the paired app.
Rules are plain YAML that you can review and share. They run before every tool call, whoever’s at the keyboard. Any deny wins, then ask, then allow.
pilothouse policy init # a starter file with examples
pilothouse policy check # validate it
pilothouse explain "git push --force"~/.pilothouse/policy.yamlversion: 1
rules:
- name: no-force-push
match: { kind: shell, command: "git push*" }
when: 'command.matches("(--force|-f)\\b")'
action: deny
message: Force-push is blocked. Open a PR instead.
- name: ask-before-push-main
match: { kind: shell, command: "git push*main*" }
action: ask
- name: tests-are-fine
match: { kind: shell, command: "{npm,pnpm} test*" }
action: allowOpen a request and tap Make it a rule, or use the Rules tab to switch presets on and off. Rule changes from the app are signed like approvals.
A repository can carry its own .pilothouse/policy.yaml. Its deny and ask rules always apply; its allow rules only after you trust the repo. Full reference: Policies.
Start an agent with pilothouse run and Pilothouse holds its terminal. From the app you can then send follow-ups, stop the current step, and open a live terminal to watch and type. The agent keeps running when you close the window.
pilothouse run claude # detach with ctrl+\ — it keeps running
pilothouse attach <session> # from any terminal, laterSessions started with plain claude still show up and follow your rules. Claude Code may hold phone replies to those until you allow them in its terminal, so pilothouse run claude is the smoother way.
A task is one prompt with its own git worktree, branch and agent, so several agents can work in one repository without stepping on each other. Start one from the terminal, or from your phone under Settings → Tasks.
cd ~/src/api
pilothouse task new "make the auth test deterministic"
pilothouse task diff <task>
pilothouse task done <task> --pr # push and open a PRA repository can copy files such as .env into each new worktree and run setup first.
# .pilothouse/task.yaml in your repo
copy:
- .env.local
setup:
- npm ci # only in repos you trustThe app shows progress and the diff as it grows. Ship commits what’s left, pushes and opens a pull request with gh. Done closes a task that changed nothing. Discard removes the worktree and the branch.
A task’s agent runs in a terminal Pilothouse holds in the background, so it doesn’t show up in herdr on its own. Open in herdr puts it there: a new tab in the session’s folder, attached to the agent, with its recent output. Type in it like any other tab.
Install herdr and start it on the computer where Pilothouse runs. Nothing else to configure: Pilothouse finds it.
herdr # start herdr once; leave it runningIt’s on the session page, the task page and the live terminal, for every session started with pilothouse run or as a task. The tab goes into the herdr workspace for that repository when there is one, otherwise the one you have open.
Tasks you start from your phone then wait for you in herdr, opened in the background without taking focus.
# ~/.pilothouse/config.yaml
desktop:
open_tasks: true # open every task from your phone as it startspilothouse doctor
✓ desktop sessions open in a new herdr tabNo herdr? On a Mac the same button opens a Terminal window instead (desktop.terminal_app: iTerm to use iTerm). Detach in the tab with ctrl+\; tapping again attaches it again. Sessions an agent runs in its own terminal can’t be opened this way: switch to that terminal.
Run agents on your laptop, your desktop and a server, and see all of them in one app. One computer is the hub: your phone, tablet and browsers pair with it. The others link to it. Every machine keeps its own rules, and checks your answers itself.
Pilothouse 0.4 or later on each computer, set up as usual, and each on your tailnet so they can reach one another.
pilothouse setup
pilothouse remote tailscale # on every computerEach needs its own name. It shows on each session, on the machine chips and in notifications.
# ~/.pilothouse/config.yaml on each computer
machine: studio # the name your phone showsPick the computer your phone is paired with. It prints a command with a one-time code, valid for ten minutes.
$ pilothouse link add
On the machine to link, run:
pilothouse link join https://studio.your-tailnet.ts.net 4KQ2-9XHT-M3PB
The code works once and expires in 10 min.Run the printed command. The machine lists the hub’s paired devices with a fingerprint of each key and asks whether to trust them. Compare each with the one on the device (Settings → This device) or in pilothouse devices on the hub. Trusted devices can answer this machine’s requests, reply to its agents and use its terminals.
$ pilothouse link join https://studio.your-tailnet.ts.net 4KQ2-9XHT-M3PB
✓ Linked to studio (https://studio.your-tailnet.ts.net).
These devices are paired with studio. …
iPhone d_7k2m key 3f9a 71c2 08be 5d11
? Trust them on this machine? [Y/n] yToday shows every machine’s sessions, each labelled, with chips to narrow it to one machine. Answers, replies, the live terminal and Open in herdr reach the computer that runs the agent. Away and At my desk apply to all of them. Settings → Machines shows which are online.
A phone you pair with the hub later (or one that gets a new key) can see the other machines’ sessions but can’t act on them until you trust it on each machine. Revoking a device on the hub revokes it everywhere.
pilothouse link trust # on each linked machine| Command | What it does |
|---|---|
pilothouse link | The hub this machine reports to, or the machines linked to it, and whether they’re online. |
pilothouse link add | On the hub: a one-time code for another machine. |
pilothouse link join <url> <code> | On another machine: link it to the hub. |
pilothouse link trust | On a linked machine: trust the hub’s new devices. |
pilothouse link remove <machine> | On the hub: unlink a machine. On a linked machine: leave the hub. |
Rules, tasks and insights stay per machine; add a rule for another machine on that machine with pilothouse policy add. Telegram and ntfy deliver linked machines’ requests without one-tap buttons (those would need a signature the hub can’t make), so answer those in the app. Machines must reach each other over a tailnet; different networks without Tailscale aren’t supported yet. Want to see it first? pilothouse demo --linked.
Pilothouse watches keyboard and mouse idle time. At the desk, the agent’s own dialog shows and you answer it there. After a minute idle you count as away, and requests are held for your devices. You can also set it yourself, from the terminal or the app.
pilothouse away # hold requests for your devices now
pilothouse here # you’re back
pilothouse auto # idle detection againWhile agents work, Pilothouse keeps the computer from idle sleep (keep_awake: off turns that off). approvals.always_hold: true sends every request to your devices, even at the desk.
Every session shows its tokens and what they would cost at API prices. Insights in the app adds up the last 7, 30 or 90 days and shows what your rules allowed, asked and blocked. On a plan such as Claude Max? Add it under usage.plans to see what you pay beside what it covers. Set a daily budget to get alerts.
usage:
daily_budget_usd: 25 # alerts at 80 % and 100 %Cost estimates cover Claude Code and Codex, plus OpenCode per session.
Details and the threat model: Security.
More in Troubleshooting, or ask in GitHub Discussions.
brew install sabr-software/tap/pilothouse
# or: curl -fsSL https://pilothouse.dev/install.sh | sh
pilothouse setup