# Privacy architecture (Browser Pilot)

**Status:** M0 — engine guarantees  
**Audience:** users installing Browser Pilot on their own machines, and agents implementing the product.

## One-line promise

**In local (downloadable) mode, Browser Pilot cannot see your screenshots, cookies, tabs, or page content.** Everything that controls the browser runs on your machine. Optional shared flow packs are sanitized playbooks only — never session data.

## Deployment modes

| Mode | Env | Bind | Who |
|---|---|---|---|
| **local** (default) | `BP_DEPLOYMENT_MODE=local` or unset | `127.0.0.1` only | Downloadable product / single user |
| **fleet** | `BP_DEPLOYMENT_MODE=fleet` (required) | `0.0.0.0` | Operator bridge (e.g. Hetzner + remote extensions) |

Code: `src/server/deployment-mode.ts`.

Default is **local** so a binary run without config cannot open a LAN control plane or public screenshot path. Operator hosts **must** set `fleet` explicitly (systemd drop-in on Hetzner).

## Trust boundary

```
┌──────────── Your machine ────────────┐
│  Agent (Claude/Grok/Cursor)          │
│       │ local MCP / stdio            │
│       ▼                              │
│  bp-server  ──WS──► Chrome extension │
│  screenshots, cookies, tabs stay here│
└──────────────────┬───────────────────┘
                   │ optional, explicit
                   ▼
            CDN flow pack only
         (recipes + site quirks)
         NO screenshots / cookies
```

## Guarantees (G1–G7)

| ID | Guarantee | How it is enforced |
|---|---|---|
| **G1** | Screenshots never leave the machine (local mode) | `isServerContext()` / `persistScreenshot` refuse unless `fleet` + `BROWSER_PILOT_SERVER=1`. Public `BROWSER_PILOT_VIEWER_BASE` ignored in local. Screenshots only go to the local agent MCP channel or user-chosen `save_to`. |
| **G2** | No product-plane outbound except pack CDN (when configured) | Local outbound allowlist empty unless `BP_PACK_URL` host is set (M2). Reported on `GET /privacy`. Chrome still loads the websites **you** automate — that is your browser, not our server. |
| **G3** | Local store is the SSOT | `~/.browser-pilot/` on your machine. We do not mirror your store to our servers in local mode. |
| **G4** | Promote cannot leak secrets (M3) | Sanitizer + preview before any upload. Contribute **off by default**. Not shipped in M0. |
| **G5** | You can prove isolation | `GET /privacy` (and future popup panel) shows mode, bind, `screenshotEgress`, outbound allowlist, `uploads24h`. |
| **G6** | Kill switch | Stop the server process; disconnect the extension. (Product tray kill switch lands with M1/M4.) |
| **G7** | Inspectable boundary | This doc + open source server paths; signed installers later (M4). |

## What stays local (always in local mode)

- Screenshots and accessibility snapshots  
- Cookies and logged-in sessions  
- MCP / extension tokens  
- Step tree / run history  
- Logs  

## What may leave (only if you opt in later)

| Data | When | Notes |
|---|---|---|
| Sanitized flow recipes | M3 contribute ON + promote | Steps + `{{params}}` shapes — secrets stripped |
| Site quirks / selectors | Same | No full page HTML |
| Pack pull (download) | M2, pull ON by default | Read-only from CDN |

**Never leaves:** full page HTML dumps, cookies, auth tokens, screenshots, private project paths.

## Verify yourself

```bash
# Privacy report (server must be running)
curl -s http://127.0.0.1:3202/privacy | jq .

# Expect local install:
#   mode: "local"
#   bind: "127.0.0.1"
#   screenshotEgress: false
#   screenshotServerPersist: false
#   uploads24h: 0

# Confirm loopback-only listen
ss -ltnp | grep -E '3200|3201|3202'
# addresses should be 127.0.0.1, not 0.0.0.0
```

Red team: set `BROWSER_PILOT_VIEWER_BASE=https://evil.example` and `BROWSER_PILOT_SERVER=1` while `BP_DEPLOYMENT_MODE=local`. `/privacy` must still report `screenshotEgress: false` and `screenshotServerPersist: false`. `persistScreenshot` throws.

## Fleet mode (operators only)

Fleet is for multi-machine operator setups (central MCP, remote extensions, optional screenshot disk + viewer URL). It is **not** the downloadable product default. Setting `BP_DEPLOYMENT_MODE=fleet` is an explicit trust decision.

## Related

- Milestones: `docs/downloadable-product-milestones.md`  
- Issue: https://github.com/ThomasHartDev/browser-pilot/issues/132  
- Auth model (tokens): `docs/auth-model.md`  
- Storage: `docs/storage-architecture.md` (fleet SSOT history; local product uses device-canonical store)  
