1 Commits

Author SHA1 Message Date
root
7f0f500050 phase 1.6: candidate intake UI — operator-driven consent + photo capture
Adds the Gate 2 frontend that operators use to capture candidate
biometric consent + photo at intake. Without this, the
/biometric/subject/{id}/consent endpoint shipped in 76cb5ac has
no caller — operators would have to build curl scripts by hand.

Surfaces:
- mcp-server serves /biometric/intake as static HTML (mounted at
  http://localhost:3700/biometric/intake; externally accessible via
  nginx at https://devop.live/lakehouse/biometric/intake)
- URL must include ?candidate_id=WORKER-XXX

Page flow (4 screens):
- Step 1 — operator authentication. Pastes legal-tier audit token
  into a password field; stored in sessionStorage (cleared on tab
  close), never localStorage, never cookies.
- Step 2 — consent. Renders the v1 consent template text inline
  (Disclosures 1/2/3 + plain-language summary, all matching
  docs/policies/consent/biometric_consent_template_v1.md as of
  this commit). Captures candidate printed name + checkbox accept;
  computes SHA-256 of the rendered consent block as
  consent_version_hash; computes SHA-256 of the click-acceptance
  evidence (method + name + ts + user_agent + page_origin) and
  records it as inline:sha256=<hash> evidence path. POSTs to
  gateway /biometric/subject/{id}/consent.
- Step 3 — photo. Two paths: file upload OR getUserMedia camera
  capture. Preview before submit. Skip-photo button for
  consent-only intake (e.g. consent collected before equipment
  available). POSTs to gateway /biometric/subject/{id}/photo.
- Step 4 — confirmation. Displays the audit row HMACs from both
  endpoint responses + the verify_biometric_erasure.sh command
  the operator can run for chain attestation.

Design choices:
- No framework, no build step. Single self-contained HTML file
  ~22KB. Matches the existing mcp-server precedent
  (onboard.html, console.html, etc.).
- Neo-brutalist dark style matching mcp-server's other pages
  (tracked dark surfaces, monospace technical metadata, sharp
  borders). Consistent with j's UI preferences.
- Server-authoritative timestamps. The page sends its own UA +
  click ts as evidence, but the canonical given_at on the
  manifest comes from the gateway's Utc::now() (per
  process_consent). Page displays whatever the gateway returns.
- Gateway URL configurable via ?gw= query param; defaults to
  http://localhost:3100 for the same-box workstation pattern.
  External-access deployment requires a separate nginx route
  for the gateway (out of scope for v1 to avoid touching the
  devop.live nginx config).

Verified live:
- GET http://localhost:3700/biometric/intake?candidate_id=WORKER-100
  returns 200 + 22KB HTML body
- GET https://devop.live/lakehouse/biometric/intake?candidate_id=WORKER-100
  returns 200 (nginx /lakehouse/ route proxies to :3700)
- Gateway CORS preflight for POST /biometric/subject/{id}/consent
  from origin http://localhost:3700: 200 with allow-methods/headers/origin: *

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:12:43 -05:00