Security

A remote “Allow” runs code on your machine. We treat it that way.

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.

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.

Per-device signing keys

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.

Agent-caller checks

Local decisions are only accepted on a 0600 Unix socket, and refused if any ancestor of the caller is an agent process.

Self-protection rule

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.

No telemetry

Sessions, prompts, commands and paths never leave the machine except through notification channels you configure.

Private by default on disk

Data directory 0700, database, keys and config 0600. Device and action tokens are stored as SHA-256 hashes and compared in constant time.

What a phone approval goes through

  1. Hook — the agent’s PermissionRequest hook reaches the daemon over a 0600 Unix socket.
  2. Rules — deny and ask rules, then grants, are evaluated. A deny can’t be overridden from a phone.
  3. Presence — only when you’re away is the request held and pushed.
  4. Transport — your phone reaches 127.0.0.1:7517 only through Tailscale Serve over HTTPS.
  5. Signature — the decision is signed by the device key, checked for freshness and nonce reuse.
  6. First answer wins — the request is closed with a compare-and-set; later answers get “already decided”.

Threats, mitigations and limits

Pilothouse threat model
ThreatMitigationLimit
Someone on the internet tries to approve a requestNothing 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 APIHost allow-list (DNS-rebinding defence), SameSite=Strict cookies, a custom header and same-origin Origin on every state change.—
A stolen session cookieDecisions 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 decisionSignatures cover request ID, decision, scope, timestamp and a single-use nonce.—
A leaked ntfy topicAction 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 requestSocket-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 crashesFail-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 rulesUntrusted repos can only add deny and ask rules; their allow rules are ignored until you trust the repo.—

The honest part

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.

Reporting a vulnerability

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.