lakehouse/docs/runbooks/BIPA_DESTRUCTION_RUNBOOK.md
root 4708717f6b phase 1.6 BIPA gates — engineering wave (4 of 7 staged)
Per docs/PHASE_1_6_BIPA_GATES.md. Status table now reflects:

  DONE (engineering-only, no counsel dependency):
  - Gate 4: name→ethnicity inference removed from mcp-server.
    Removal note in search.html:3372 + new Bun absence test
    (mcp-server/phase_1_6_gate_4.test.ts) with 3 assertions:
    walker actually scans files, regex catches synthetic positives,
    no offending DEFINITION patterns in any .html/.ts/.js source.
    3/3 pass.

  ENG-DONE, signature pending:
  - §2 attestation: scripts/staffing/attest_pre_identityd_biometric_state.sh
    runs three checks against the live state:
      1. workers_500k.parquet schema has no biometric/photo/face/image col
      2. data/_kb/*.jsonl + pathway state contain no base64 image magic
         bytes (JPEG /9j/, PNG iVBOR), no data:image/* MIME prefixes,
         no field-name patterns ("photo", "biometric", "deepface_*")
      3. data/headshots/manifest.jsonl is entirely synthetic-tagged
    3/3 evidence checks pass on the live data dir. Generates a
    signed-by-operator+counsel attestation document committed at
    docs/attestations/BIPA_PRE_IDENTITYD_ATTESTATION_2026-05-03.md
    with SHA-256 of the evidence summary so post-signature tampering
    is detectable.

  ENG-STAGED, awaiting counsel review:
  - Gate 1 retention schedule scaffold at
    docs/policies/consent/biometric_retention_schedule_v1.md (BIPA
    §15(a)). Engineering facts (categories, 18-month operational
    ceiling vs 3-year statutory cap, destruction procedure pointer
    to Gate 5 runbook) plus ⚖ COUNSEL markers for the binding text.
  - Gate 2 consent template scaffold at
    docs/policies/consent/biometric_consent_template_v1.md (BIPA
    §15(b)(1)-(3)). Required disclosures + plain-language summary +
    withdrawal procedure + the structured fields the consent UI must
    post to identityd.
  - Gate 5 destruction runbook at docs/runbooks/BIPA_DESTRUCTION_RUNBOOK.md.
    Triggers, pre-destruction checks (incl. chain-verified gate via
    /audit/subject/{id}), procedure (legal-tier endpoint), automatic
    audit row append (subject_audit.v1 with kind=biometric_erasure),
    backup-window disclosure, monthly reporting cadence, audit-trail
    attestation procedure cross-referencing the cross-runtime parity
    probe.

  BLOCKED on engineering design:
  - Gate 3 photo-upload endpoint. Requires identityd photo intake
    design + deepface integration scope. Deferred to its own session.

  DEFERRED:
  - §3 employee training material. Gate 5 runbook §7 may serve as
    substrate; counsel decides whether a separate program is needed.

Calendar bottleneck is now counsel review. Engineering can stage no
further deliverables until either (a) Gate 3's design conversation
happens or (b) counsel completes review of items 1/2/5/6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 04:38:49 -05:00

229 lines
8.0 KiB
Markdown

# BIPA Biometric Data Destruction Runbook
**Spec:** docs/PHASE_1_6_BIPA_GATES.md §1 Gate 5 (BIPA §15(a))
**Audience:** Operators (J + named operators with legal-tier credentials)
**Status:** Engineering scaffold — ⚖ COUNSEL must review for legal sufficiency before adoption
> This runbook tells an operator HOW to destroy biometric data when
> a destruction trigger fires. It is a procedural document, not a
> design document. The cryptographic substrate that the destruction
> writes against (per-subject HMAC audit log + tombstone manifests)
> already ships in `crates/catalogd/`.
---
## 1. When this runbook fires
Destruction is mandatory when ANY of the following occurs:
| Trigger | Source signal | SLA |
|---|---|---|
| **Retention expiry** | Daily `retention_sweep` flags `consent.biometric.retention_until < now` | 30 days from sweep flagging |
| **Consent withdrawal** | Candidate submits withdrawal per consent template §2 | 30 days from receipt |
| **Right-to-be-forgotten request** | Candidate submits RTBF request through documented contact channel | 30 days from receipt |
| **Court-ordered erasure** | Legal counsel directs erasure via a documented order | Per court order; default 30 days |
⚖ COUNSEL — confirm 30 days is correct for all four. Some deployments
have stricter contractual or jurisdictional clocks (CCPA: 45 days but
sooner is better; GDPR Art. 17: "without undue delay").
---
## 2. Pre-destruction checks (5 minutes)
Before initiating destruction, the operator MUST:
1. **Verify the trigger.** Cross-reference one of the four sources
above. If the trigger is a candidate-initiated request,
confirm identity per the standard PII verification procedure
(knowledge factor + possession factor; see counsel for the
threshold).
2. **Pull the current subject record.** Hit
`GET /audit/subject/{candidate_id}` with the legal-tier token.
The response includes:
- The current `SubjectManifest` (including `consent.biometric.status`)
- The full HMAC-chained audit log
- `chain_verified: true` (if false, STOP — chain integrity issue
must be investigated before destruction)
3. **Check for legal hold.** ⚖ COUNSEL — if a legal hold can apply
to a subject's data (litigation, regulatory inquiry, subpoena),
document the procedure for checking that no hold is in force
before erasing.
4. **Get the second-operator sign-off.** Per BIPA defensibility,
destruction is a two-operator action (operator-of-record + one
witness). The witness records their attestation in the
destruction-event audit row (§4 below).
---
## 3. Destruction procedure
### Step 1 — Erase via identityd
Invoke the legal-tier erasure endpoint:
```bash
curl -sf -X POST "http://localhost:3100/v1/identity/subjects/${CANDIDATE_ID}/erase" \
-H "Authorization: Bearer $(cat /etc/lakehouse/legal_audit.token)" \
-H "Content-Type: application/json" \
-d '{
"trigger": "retention_expiry|consent_withdrawal|rtbf|court_order",
"trigger_evidence_path": "<path to signed artifact>",
"operator_of_record": "<operator name>",
"witness": "<witness name>"
}'
```
⚖ ENGINEERING — `POST /v1/identity/subjects/{id}/erase` is Phase 1.6
Gate 3 dependent. Until it ships, the manual procedure is:
a. Set `SubjectManifest.consent.biometric.status = "withdrawn"` and
`SubjectManifest.status = "erased"` via direct registry write
(operator-of-record only).
b. Securely overwrite + unlink the quarantined photo path:
`shred -uvz data/biometric/uploads/${CANDIDATE_ID}/*.jpg`
(or equivalent for the configured backend).
c. NULL the deepface classification fields on the subject row.
d. Append the destruction-event audit row (Step 2 below).
### Step 2 — Append the destruction-event audit row
The erasure endpoint AUTOMATICALLY writes one row to the subject's
per-subject audit log:
```json
{
"schema": "subject_audit.v1",
"ts": "<ISO-8601>",
"candidate_id": "<id>",
"accessor": {
"kind": "biometric_erasure",
"daemon": "identityd",
"purpose": "biometric_erasure",
"trace_id": "<X-Lakehouse-Trace-Id>"
},
"fields_accessed": ["biometric_classifications", "biometric_data_path", "biometric_template_hash"],
"result": "erased",
"prev_chain_hash": "<previous row hmac>",
"row_hmac": "<new chain link>"
}
```
The HMAC chain extends through the erasure event, so the audit
log itself is preserved as anonymous-event proof of compliant
destruction even after the underlying biometric data is gone.
### Step 3 — Verify destruction
Run the verification script:
```bash
./scripts/staffing/verify_biometric_erasure.sh "${CANDIDATE_ID}"
```
⚖ ENGINEERING — script TODO. Acceptance:
- Subject row biometric fields are NULL
- `data/biometric/uploads/${CANDIDATE_ID}/` directory is empty
- Most recent audit log row has `result: "erased"`, `accessor.kind: "biometric_erasure"`
- Chain still verifies (`chain_verified: true`) under the legal-tier endpoint
If any check fails: STOP, do not mark the destruction complete,
escalate to engineering.
### Step 4 — Notify the candidate (when applicable)
For consent-withdrawal and RTBF triggers, the operator notifies
the candidate that destruction is complete. ⚖ COUNSEL — supply
the notification template (typically email; medium and language
are counsel-determined).
---
## 4. Backup window disclosure
Per `IDENTITY_SERVICE_DESIGN.md` v3-B12, biometric data may persist
in encrypted system backups for up to **30 days** after destruction
(rolling backup window). The candidate must be informed of this
when destruction is requested, and the destruction-event audit row
records the backup-window expiry date so the operator knows when
the residual is fully eliminated.
⚖ COUNSEL — confirm whether the 30-day backup window is acceptable
under BIPA. Some interpretations require backups to be addressed
within a shorter window; some accept the operational reality of
backup retention.
---
## 5. Reporting cadence
Monthly, the operator-of-record produces a destruction-events
report:
```bash
./scripts/staffing/biometric_destruction_report.sh \
--month "$(date +%Y-%m)" \
--output reports/biometric/destruction_$(date +%Y_%m).md
```
⚖ ENGINEERING — script TODO. The report aggregates:
- Total destruction events in the month
- Breakdown by trigger (retention / withdrawal / RTBF / court)
- Median time-to-destruction from trigger to completion
- Any failures / escalations
The monthly report is available to outside counsel on request.
It does NOT include candidate-identifying details — only the
counts, timings, and cryptographic attestations of the events.
---
## 6. Audit trail attestation
The per-subject HMAC chain is the cryptographic substrate that
makes destructions defensible after the fact. To produce an
attestation for a specific candidate's destruction:
1. Hit `GET /audit/subject/{candidate_id}` with legal-tier token
2. Confirm `chain_verified: true` and most-recent row has
`accessor.kind: "biometric_erasure"`
3. Cross-runtime verify: the same audit log is byte-identical
under Rust + Go (per `scripts/cutover/parity/subject_audit_parity.sh`)
4. Counsel signs an attestation referencing the audit log's
chain root hash
The chain root hash is itself a tamper-evident anchor. A motivated
insider would need the HMAC signing key (held in a separate location
from the audit logs themselves, per the spec) AND the original
log to forge a clean destruction record — and the cross-runtime
parity probe would catch a forgery that touched only one runtime's
view.
---
## 7. Operator acknowledgment
Operators with legal-tier credentials acknowledge they have read,
understood, and will follow this runbook before being granted access
to the legal_audit token.
| Operator | Date acknowledged | Signature |
|---|---|---|
| J | _____ | _______________ |
| _____ | _____ | _______________ |
⚖ COUNSEL — adopt this acknowledgment as the substrate for §3 of
Phase 1.6 (employee training acknowledgment), or specify a separate
training program.
---
## 8. Change log
- 2026-05-03 — Initial scaffold. ⚖ COUNSEL review required before
adoption.