Four threads landing together — all driven by the audit J asked for before
production cutover.
(1) Gate 3b DECIDED: Option C (defer classifications). `BiometricCollection.classifications`
stays `Option<JSON> = None` in v1. `docs/specs/GATE_3B_DEEPFACE_DESIGN.md` status
flipped from "draft / awaits product" to DECIDED. Consent template + retention
schedule revised to remove all "automated facial-classification" / "deepface"
language so disclosed scope matches implemented scope.
(2) Endpoint-path drift reconciled across 3 docs. `PHASE_1_6_BIPA_GATES.md`,
`BIPA_DESTRUCTION_RUNBOOK.md`, and `biometric_retention_schedule_v1.md` had
references to legacy `/v1/identity/subjects/*` paths (proposed under a separate
identityd daemon, never shipped) — corrected to actual shipped routes
`/biometric/subject/*` (catalogd-local). Schema block in PHASE_1_6_BIPA_GATES
rewritten to reflect JSON `SubjectManifest.biometric_collection` substrate
(not the proposed Postgres `subjects` table).
(3) New operational artifacts:
- `scripts/staffing/verify_biometric_erasure.sh` — checks 4 things post-erasure
(manifest cleared, uploads dir empty, audit row matches, chain verified).
Smoke-tested live against WORKER-2.
- `scripts/staffing/biometric_destruction_report.sh` — monthly anonymized
destruction-event aggregation. Smoke-tested clean.
- `scripts/staffing/bundle_counsel_packet.sh` — tarballs the counsel-review
packet with per-file SHA-256 manifest.
- `docs/runbooks/LEGAL_AUDIT_KEY_ROTATION.md` — formal rotation procedure
operationalized after the 2026-05-05 /tmp wipe incident.
- `docs/counsel/COUNSEL_REVIEW_PACKET_2026-05-05.md` — cover note bundling
all eng-staged BIPA docs for counsel review with per-doc questions, sign-off
checklist, recommended review sequence.
(4) Double-upload file leak fixed in `crates/catalogd/src/biometric_endpoint.rs`.
`verify_biometric_erasure.sh` smoked WORKER-2 and surfaced a stranded photo
file. Investigation showed the file was 13-byte test-fixture bytes (zero PII,
no biometric content); audit timeline showed two consecutive uploads followed
by one erasure — the second upload had silently overwritten manifest.data_path,
orphaning the first file. Patched `process_upload` to refuse a second upload
with HTTP 409 + `error: "biometric_already_collected"` when
`biometric_collection.is_some()` on the manifest. Operator must explicitly
POST `/biometric/subject/{id}/erase` first.
Tests: new `second_upload_without_erase_returns_409` (asserts 409 + manifest
pointer unchanged + first file untouched on disk). Replaced
`repeated_uploads_grow_the_chain` with `upload_erase_upload_grows_the_chain_cleanly`
(covers the legitimate re-collection cycle: chain grows to 3 rows). Updated
`content_type_with_parameters_accepted` to use 2 distinct subjects (was
using 1 subject with 2 uploads to test ct parsing — would now 409).
22/22 biometric_endpoint tests + 59/59 catalogd lib tests green post-patch.
Production posture: gateway needs `cargo build --release -p gateway` +
`systemctl restart lakehouse.service` to pick up the new 409 in live traffic.
Counsel calendar is now the only remaining blocker for first real-photo intake.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.6 KiB
Biometric Information Consent — v1
Spec: docs/PHASE_1_6_BIPA_GATES.md §1 Gate 2 (BIPA §15(b)(1)-(3))
Status: Engineering scaffold — ⚖ COUNSEL must author the binding text before deployment
Version: v1 (initial; supersession requires a new version + new hash)
Updated 2026-05-05: Disclosure 1 + plain-language summary revised to match the Gate 3b deferral recommendation in docs/specs/GATE_3B_DEEPFACE_DESIGN.md (Option C — defer classifications). Pending J's product confirmation of Gate 3b; if Gate 3b chooses Option A or B, this template needs counsel re-authoring.
This is the consent template a candidate signs (electronically or on paper) BEFORE Lakehouse collects, stores, or processes any biometric identifier or biometric information from that candidate.
Without an executed consent under this template (or a counsel- approved successor), the system MUST NOT accept a photograph from the candidate. Enforcement lives at the photo-upload endpoint (Gate 3) and at the SubjectManifest writer, which refuses biometric writes when
consent.biometric.status != "given".
Required disclosures (BIPA §15(b)(1)-(3))
The disclosures below are MANDATORY content per 740 ILCS 14/15(b). ⚖ COUNSEL — render this content into binding language appropriate for the candidate-facing UI. Engineering provides the structural content; counsel provides the legally-sufficient wording.
Disclosure 1 — Notice of collection (§15(b)(1))
Lakehouse will collect and store my biometric identifier (a photograph of me from which facial geometry is implicit). The photograph itself is the data we keep — we do NOT run automated facial-classification (gender / race / age inference) against it in v1. If at a later date we add automated classification, we will re-collect consent under a superseding template before doing so.
Disclosure 2 — Specific purpose and length of term (§15(b)(2))
The biometric data will be used for:
- Identity verification at staffing job sites
- Internal record-keeping so coordinators can recognize me across placements
The biometric data will be retained for a maximum of 18 months from my most recent interaction with the staffing platform, after which it will be permanently destroyed per the Biometric Retention Schedule v1.
I may withdraw this consent at any time by contacting the operator (see §3 below). Withdrawal triggers permanent destruction of my biometric data.
Disclosure 3 — Written release (§15(b)(3))
I provide a written release authorizing Lakehouse to collect, store, and use my biometric identifier and biometric information for the purposes stated above and for the term stated above.
1. Plain-language summary (non-binding)
⚖ COUNSEL — the section above is the binding legal disclosure. The summary below is provided for candidate comprehension and is NOT a substitute for the binding disclosure. Both should appear together in the consent UI; counsel determines whether this summary is appropriate to include or whether a different plain-language section is preferred.
What you're agreeing to: if you upload a photo of yourself, we'll keep that photo so your staffing coordinator can recognize you when you arrive at job sites. We don't run automated guesses about your age, gender, or race against the photo.
How long we keep it: at most 18 months after your last placement or interaction with us, then it's permanently destroyed.
What we DON'T do with it: we don't sell it, we don't share it with anyone outside the staffing operation unless legally compelled, and we don't use it to decide what jobs to recommend to you.
How to take it back: contact us (§3 below) at any time to withdraw your consent. We will permanently destroy your biometric data within 30 days of receiving your request.
2. Withdrawal procedure
I may withdraw biometric consent at any time. Withdrawal:
- Is free of charge
- Does not affect my ability to remain on the staffing platform (only my biometric data is removed)
- Triggers permanent destruction of all biometric data within 30 days, per the destruction runbook
- Is recorded as an append-only audit row in my per-subject audit log, providing me with tamper-evident proof of withdrawal if I subsequently exercise my BIPA right of action
⚖ COUNSEL — confirm 30 days is the right destruction SLA. Some deployments use 7 or 14 days. The runbook (Gate 5) currently references this template's number, so changing it here updates both.
3. Contact for withdrawal / questions
⚖ COUNSEL — supply the candidate-facing contact channel for
biometric-consent withdrawal. Examples: a dedicated email
(biometric-consent@<deployment-domain>), a postal address, a
named operator. The contact must be functional from day one of
deployment.
4. Consent acknowledgment
By signing below (electronically or on paper), I acknowledge that:
- I have read and understood the disclosures in §1-3 above
- I am providing this consent voluntarily and free of coercion
- I have received a copy of this consent template (or have been provided a means to retrieve a copy at any time)
| Field | Value |
|---|---|
| Candidate name | _______________________________ |
| Date | __________ |
| Signature | _______________________________ |
| Consent template version | v1 (SHA-256: generated at deployment time) |
5. Operational integration
The structured fields the consent UI must capture and post to identityd:
{
"candidate_id": "<token>",
"consent_version_hash": "<sha256 of this file at deployment>",
"consent_given_at": "<ISO-8601 timestamp>",
"consent_collection_method": "<electronic_signature|paper|click_acceptance>",
"consent_collection_evidence_path": "<path to signed artifact, if applicable>"
}
These fields write to SubjectManifest.consent.biometric.status='given'
and the corresponding SubjectAuditRow (see
crates/catalogd/src/subject_audit.rs).
6. Versioning
This consent template is version v1. Per Gate 1's versioning rules, any change to the binding disclosure language requires a new version, and existing subjects retain their original consent_version reference unless they re-consent under the new version.
⚖ COUNSEL — confirm whether existing consent under v1 carries forward when the schedule is updated, or whether re-consent is required. This affects the deployment workflow.
7. Authority
| Role | Name | Signature | Date |
|---|---|---|---|
| Operator | J | _______________ | _____ |
| Outside counsel | _____________ | _______________ | _____ |