The Pilothouse guide

Everything it does, and how to set it up.

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.

The big picture

How it fits together

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.

Agents report to Pilothouse on your computer, which checks your rules and then reaches you at the desk or awayYour agentsYour computerYouClaude CodeCodexOpenCodeGrok BuildhooksPilothouse1 · your rules2 · are you here?3 · find youAt the deskterminal · herdrPhone · tabletthe web appAnother browserpaired like a phoneTelegram · ntfySlack · Discord
Every tool call passes your rules. Only what needs a person goes on, to wherever you are.
Before every tool call

Your rules answer allow, ask or deny. Any deny wins. If no rule matches, the agent’s own settings decide.

When the agent asks a person

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.

Feature list

Everything it does

Each card links to the steps for that feature.

Step by step

Set up in five minutes

  1. Install Pilothouse

    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 | sh
  2. Run setup

    It 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, checks
  3. Start your agents as usual

    Plain 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 --continue
  4. Check it

    Doctor lists what works and what to fix, one line each.

    pilothouse doctor

Want to look around first? pilothouse demo runs the whole app with pretend agents and touches nothing.

Platforms

Connect your agents

pilothouse setup (or pilothouse hooks install) connects all four. Some agents ask you to trust the hooks once.

Claude CodeHooks
Once
Nothing to do after setup.
Phone approvals
Yes
Replies
Yes

Start it with pilothouse run claude to reply and watch from your phone.

CodexHooks
Once
Trust the Pilothouse hooks once with /hooks.
Phone approvals
Yes
Replies
Under pilothouse run

Its hooks honour deny only; allow and ask go to Codex’s own prompt, which Pilothouse routes to you.

OpenCodePlugin
Once
Setup installs the plugin.
Phone approvals
Yes
Replies
Yes

Activity, and tokens and cost in Insights.

Grok BuildPersonal hooks
Once
Trust hooks per project with /hooks-trust.
Phone approvals
No
Replies
Under pilothouse run

Activity only. Grok has no permission-request hook, so approvals stay in its terminal.

The full matrix is in How it works.

Phone

Connect your phone

Your phone talks straight to your computer over your own Tailscale network. Nothing is exposed to the internet, and there’s no account.

The phone reaches the computer over your private Tailscale network, with no server in betweenYour phonesigning key insideyour tailnet · HTTPS · only your deviceshttps://studio.your-tailnet.ts.netYour computerPilothouse on 127.0.0.1No Pilothouse server · no open ports · nothing on the public internet
  1. Install Tailscale on both

    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.

  2. Put the app on your tailnet

    This runs Tailscale Serve and saves the address.

    pilothouse remote tailscale
    # → https://studio.your-tailnet.ts.net
  3. Install the app on the phone

    iPhone 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.

  4. Pair

    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 pair
  5. Add a passkey

    The 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.

  6. Allow notifications and test

    Tap Set up notifications, allow them, then send a test.

    pilothouse notify test

Answering: 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.

More devices

Tablets and other browsers

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.

One computer with several paired devices, each holding its own keyPilothouseone inboxiPhone · key AiPad · key BLaptop · key CThis Mac · key D
  1. Tablet or second phone

    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.

  2. A browser on another computer

    That computer must be on your tailnet. Open the address, then pair with a fresh code.

  3. This computer’s browser

    One command opens the app here, signed in with its own key.

    pilothouse open      # this computer’s browser, signed in
  4. See or remove devices

    Revoking 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>
Notifications

Telegram, ntfy, Slack and Discord

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.

ChannelAnswer from it?Set up
Web Push (the app)YesPairing, above
TelegramYes: buttons, options, plans, repliespilothouse notify telegram
ntfyYes: Allow and Deny buttonspilothouse notify ntfy
Slack, DiscordNo, it links to the appnotify.webhooks in config
DesktopNo, it brings you to the terminalOn by default
  1. Telegram (works without Tailscale)

    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 telegram
  2. ntfy

    Pick a long, random topic: on ntfy.sh, anyone who knows it can read the messages. The buttons call back over your tailnet.

    pilothouse notify ntfy
  3. Slack or Discord

    Add incoming-webhook URLs to ~/.pilothouse/config.yaml.

    notify:
      webhooks:
        - https://hooks.slack.com/services/T000/B000/XXXX
        - https://discord.com/api/webhooks/123/abc

Telegram, 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.

Policy

Write your rules

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.

A tool call is checked against your rules, and ends as deny, ask or allowAgent wants togit push --forceYour rulespolicy.yamldeny · blocked, agent told whyask · a person decidesallow · runs without a prompt
  1. Start from the examples

    pilothouse policy init      # a starter file with examples
    pilothouse policy check     # validate it
    pilothouse explain "git push --force"
  2. Edit ~/.pilothouse/policy.yaml

    version: 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: allow
  3. Or make rules from your phone

    Open 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.

Wrap mode

Reply, interrupt and watch live

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, later

Sessions 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.

Tasks

Hand off a task

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.

A task goes from a prompt to its own worktree and agent, then to review, and ends shipped or discardedYou askphone or terminalNew worktreepilothouse/<slug>Agent worksyour rules applyReviewdiff on the phoneShip · PRDiscard+ a tab in herdr
  1. Start it

    cd ~/src/api
    pilothouse task new "make the auth test deterministic"
    pilothouse task diff <task>
    pilothouse task done <task> --pr     # push and open a PR
  2. Prepare the worktree (optional)

    A 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 trust
  3. Review and finish

    The 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.

herdr New

Connect herdr

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.

Tapping Open in herdr on the phone makes Pilothouse add a herdr tab on the computer that is attached to the running agentPhone or browserOpen in herdrPilothousechecks your device’ssignatureapifix-flaky-test$ pilothouse attach --replay s_3fq9● Bash(go test ./internal/cache) ✓ ok internal/cache 0.41s● Update(lru_test.go)> ship it when greenherdr on your computersignednew tabTap again → the same tab comes to the front.Close the tab → only detaches; the agent keeps going.
  1. Run herdr

    Install herdr and start it on the computer where Pilothouse runs. Nothing else to configure: Pilothouse finds it.

    herdr             # start herdr once; leave it running
  2. Tap Open in herdr

    It’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.

  3. Open every task automatically (optional)

    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 starts
  4. Check where sessions open

    pilothouse doctor
      ✓ desktop   sessions open in a new herdr tab

No 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.

Several computers New in 0.4

Several machines, one app

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.

Linked machines report to the hub; the phone pairs with the hub; answers are forwarded to the machine running the agent, which checks the signatureLinked machinesThe hubYour devicesbuild-boxLinux server · its own ruleslaptopMacBook · its own rulesstudioone inbox for all threechips per machinePhone · tabletBrowsersessions →← your signed answerThe machine running the agent checks your device’s signature itself. The hub can’t answer for you.
  1. Get every machine ready

    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 computer
  2. Name them

    Each 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 shows
  3. On the hub, make a code

    Pick 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.
  4. On each other computer, join

    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] y
  5. Use the app as before

    Today 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.

  6. Added a device later? Trust it there

    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
CommandWhat it does
pilothouse linkThe hub this machine reports to, or the machines linked to it, and whether they’re online.
pilothouse link addOn the hub: a one-time code for another machine.
pilothouse link join <url> <code>On another machine: link it to the hub.
pilothouse link trustOn 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.

Presence

Away and at the desk

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 again

While 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.

Insights

Usage and budget

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.

Security

What keeps it safe

  • Signed answers. Pairing makes a key on the device that can’t be copied out. Every answer, reply, rule change, task and “Open in herdr” is signed with it, time-stamped and single-use.
  • Loopback only. Pilothouse listens on 127.0.0.1. Your devices reach it through Tailscale, which only your tailnet can use.
  • Agents can’t approve themselves. Admin commands are refused from inside an agent, and a built-in rule asks before an agent touches Pilothouse or agent settings.
  • No account, nothing about your work. Sessions and commands stay on your computer. The only automatic request is an anonymous daily update check, which we use to count active installs; you can turn it off.

Details and the threat model: Security.

Troubleshooting

When something’s off

No notifications on iPhone
Open Pilothouse from the Home Screen icon (not Safari) and allow notifications there. Settings → Notifications → Send a test.
“This device has no signing key”
iOS cleared the app’s storage. Tap Restore with your passkey, or run pilothouse pair and pair again inside the installed app.
A reply to Claude never arrives
Claude Code holds messages for sessions started with plain claude. Start it with pilothouse run claude.
“Trust this device on build-box”
You paired this device after linking that machine, or it got a new key. Run pilothouse link trust on that machine.
No “Open in herdr” button
Only sessions started with pilothouse run or as a task have one, and herdr (or Terminal on a Mac) must be available. pilothouse doctor says which.
The app looks out of date
Settings → Reload the app. The phone picks up a new version the next time it opens.
Something else
pilothouse doctor checks everything and prints what to fix.

More in Troubleshooting, or ask in GitHub Discussions.

Ready when you are.

brew install sabr-software/tap/pilothouse
# or: curl -fsSL https://pilothouse.dev/install.sh | sh
pilothouse setup