Loopback only
The web listener binds to 127.0.0.1. Remote access goes through Tailscale Serve over HTTPS; Pilothouse never opens a port on another interface.
Pilothouse is designed so that approvals are hard to forge, hard to replay and never routed through someone else’s server — and so that we’re clear about what it can’t protect against.
The web listener binds to 127.0.0.1. Remote access goes through Tailscale Serve over HTTPS; Pilothouse never opens a port on another interface.
Pairing creates a non-extractable ECDSA P-256 key in the phone’s browser. Every remote decision is signed, time-stamped (±120 s) and carries a single-use nonce.
Local decisions are only accepted on a 0600 Unix socket, and refused if any ancestor of the caller is an agent process.
A built-in rule that can’t be disabled forces “ask” when an agent runs Pilothouse admin commands or edits Pilothouse’s or the agents’ settings.
Sessions, prompts, commands and paths never leave the machine except through notification channels you configure.
Data directory 0700, database, keys and config 0600. Device and action tokens are stored as SHA-256 hashes and compared in constant time.
PermissionRequest hook reaches the daemon over a 0600 Unix socket.127.0.0.1:7517 only through Tailscale Serve over HTTPS.| Threat | Mitigation | Limit |
|---|---|---|
| Someone on the internet tries to approve a request | Nothing listens beyond loopback. Tailscale Serve is reachable only from your tailnet. | Anyone you add to your tailnet can reach the login page (but still needs a paired device key). |
| A malicious web page in your browser calls the local API | Host allow-list (DNS-rebinding defence), SameSite=Strict cookies, a custom header and same-origin Origin on every state change. | — |
| A stolen session cookie | Decisions also need a signature from the device’s non-extractable key. | Malware with full control of the phone’s browser could use the key in place. |
| A replayed or re-targeted decision | Signatures cover request ID, decision, scope, timestamp and a single-use nonce. | — |
| A leaked ntfy topic | Action tokens are random, single-use, bound to one request and decision, and expire with the request. | Whoever reads the topic sees request summaries and could press a button before you. Use an unguessable topic or a private server. |
| An agent tries to approve its own request | Socket-only local decisions with a process-ancestry check, signed remote decisions, and the built-in self-protection rule. | Pilothouse runs as you. A program with your user’s privileges can ultimately edit files or kill processes. This is defense in depth, not a sandbox. |
| Pilothouse itself crashes | Fail-open by default: agents behave as if Pilothouse weren’t installed. Opt into fail-closed for PreToolUse. | With fail-open, your rules don’t run while the daemon is down. Keep critical denies in the agent’s own settings too. |
| A cloned repo ships permissive rules | Untrusted repos can only add deny and ask rules; their allow rules are ignored until you trust the repo. | — |
Pilothouse runs as your user, like your agents do. Its protections make accidental and casual self-approval hard and make remote approvals trustworthy. They don’t turn your laptop into a sandbox. If an agent must not be able to do something even when it misbehaves, run it in a container, VM or as a different user — Pilothouse works there too.
Hooks are a cooperative mechanism: they run because the agent runs them. For hard limits, keep a matching deny rule in the agent’s own permission settings as well.
Please report security issues privately throughGitHub’s private vulnerability reporting forxasdta/pilothouse. Don’t open a public issue. We’ll acknowledge reports within a few days and credit you in the release notes if you’d like.
The full technical write-up is in the docs: Security model.