# Privacy proof checklist (before a capture key)

**Gate:** Do **not** paste a live `bt_live_*` / `bt_test_*` capture key into an app that shows named clients, revenue, FX margin, or similar grids until this checklist is green.

Transmit model (honest): the SDK keeps a rolling buffer **in memory** until submit or automatic report. **On submit / auto-report, scrubbed events — including DOM snapshots when recording is on — are stored on BugTape (Fly + Neon)** for the plan event-retention window. Inputs are masked by default. **Visible page text and tables are still recorded unless you block those nodes.** Do not claim “raw data never leaves.”

Machine summary: [`/llms.txt`](https://bugtape.ai/llms.txt) · Privacy page: [`/privacy/`](https://bugtape.ai/privacy/) · Fixture: [`examples/privacy-fixture/`](./examples/privacy-fixture/)

---

## Built-in selectors (SDK defaults)

The browser SDK’s DOM recorder always uses these block / mask selectors (`packages/sdk/src/security/capture-privacy.ts`). There is **no** custom `blockSelector` init option today — mark nodes in the page:

| Mechanism | Selector / option | Effect |
|-----------|-------------------|--------|
| Block node (omit from replay) | `[data-bugtape-ignore]`, `.bugtape-ignore`, `.rr-block` | Node (and subtree) excluded from rrweb |
| Widget / capture UI | `#bugtape-widget`, `[data-bugtape-capture-ui]` | Always blocked |
| Mask editable text | inputs / textareas / selects / `[contenteditable]` / `.rr-mask` | Values not shown as typed text |
| Disable DOM entirely | `init({ disableDomRecording: true })` | No replay; keep for client-data surfaces when unsure |
| Disable network bodies path | `init({ disableNetworkRecording: true })` | No fetch/XHR waterfall events |
| PII regex scrub | `pii: { enabled: true }` (default) | Emails, cards, SSNs, JWTs, keys, IPs — **not** enough for OFX-style client names in table cells |

---

## Checklist (pass every row before a key)

| # | Step | Pass | Fail |
|---|------|------|------|
| 1 | Keep `pii.enabled` true (default). | Scrub left on | Scrub disabled “to debug” |
| 2 | Mark every client / revenue / margin / PII table or panel with `data-bugtape-ignore` (or `.bugtape-ignore` / `.rr-block`) on the **container** that holds the sensitive text. | Containers marked | Only CSS-hidden; text still in DOM without block attrs |
| 3 | Confirm network recording does not need request/response bodies for your use case. SDK records URL/method/status/timing — turn `disableNetworkRecording: true` if query strings carry client ids. | Query strings clean or network off | Client ids in URLs still recorded |
| 4 | For regulated / client-data apps where DOM scrub is unproven: set `disableDomRecording: true` **or** use **server-only** ingest (`platform: "server"`) instead of the browser SDK. | DOM off or server-only | Browser SDK on live client grids without proof |
| 5 | Run the [privacy fixture](./examples/privacy-fixture/) (or your twin page). Submit one report. In console replay / event JSON, search for the fixture’s **forbidden strings**. | Zero hits | Any forbidden string in replay or event payloads |
| 6 | Document the selectors you used in the app’s AGENTS.md / runbook so the next agent does not re-enable unmarked tables. | Selectors listed | “We’ll add ignore later” |

**Green** = every row Pass. **Red** = any Fail → no capture key on that surface.

---

## Fixture proof (local)

1. Open [`examples/privacy-fixture/index.html`](./examples/privacy-fixture/index.html) via any static server (or open the file and use the Setup / demo host that serves `/docs/examples/privacy-fixture/`).
2. Sensitive tables use `data-bugtape-ignore` and the documented `data-testid` values.
3. Control table is **not** blocked — its marker string **must** appear if DOM recording is on (proves the recorder works).
4. Pass/fail strings and selectors: see the fixture README.

Screenshots of console replay are optional evidence; hosted PNG storage is unavailable — do not block this checklist on screenshot upload.

---

## Server-path redaction (pairs with Python example)

Never put SQL, parquet paths with client ids, or row samples in `description` / `metadata` without an allowlist scrubber. Use [`examples/server/bugtape_report.py`](./examples/server/bugtape_report.py) (redacts query text and table-like tokens by default).

---

## Related

- [Server evidence floor](./examples/server/evidence-floor.md)
- [Beat Datadog on the agent loop](./beat-datadog-agent-loop.md)
- [SDK reference — PII](./sdk-reference.md#pii-scrubbing)
- [Capture semantics](./capture-semantics.md)
