diff --git a/02-architecture/BIOMETRIC_ENGINE_ARCHITECTURE.md b/02-architecture/BIOMETRIC_ENGINE_ARCHITECTURE.md index 77c7156..71e6920 100644 --- a/02-architecture/BIOMETRIC_ENGINE_ARCHITECTURE.md +++ b/02-architecture/BIOMETRIC_ENGINE_ARCHITECTURE.md @@ -145,7 +145,7 @@ After migration, all real-time detection logic moves into the engine library. Th | | P2 (Enhancement — deferrable to Phase 3+): | | | | +-------------------+ +--------------------+ +-----------------+ | | | | | PassiveLiveness | | EmbeddingComputer | | CardDetector | | | -| | | Detector | | (MobileFaceNet) | | (ONNX YOLO) | | | +| | | Detector | | (Facenet512 ONNX) | | (ONNX YOLO) | | | | | +-------------------+ +--------------------+ +-----------------+ | | | +=====================================================================+ | | | | @@ -278,7 +278,7 @@ Nice to have. Server-side alternatives exist for all of these. | Component | Responsibility | Server Fallback | |-----------|---------------|-----------------| | `PassiveLivenessDetector` | Texture/color/moire liveness scoring | Server-side liveness via `biometric-processor` is the primary authority. Client-side is supplementary. Gabor convolution is expensive; defer to Phase 3. | -| `EmbeddingComputer` | ONNX face embeddings | Server-side DeepFace embedding via REST API. **Note:** MobileFaceNet was removed per ADR 0003 (2026-04-18); the active client path is geometry-512 (landmark-based fallback). | +| `EmbeddingComputer` | ONNX face embeddings | Server-side DeepFace embedding via REST API is the legacy/default path (flag OFF). **Note:** MobileFaceNet was removed per ADR 0003 (2026-04-18) for licensing. The client-side embedding path (flag `app.auth.client-side-embedding`, default OFF) computes the **authoritative Facenet512 embedding in the browser** via onnxruntime-web and uploads only the 512-d vector — the raw face image never leaves the device; the server keeps the image→Facenet512 path as fallback and owns the pgvector match + liveness verdict + decision either way. See [CLIENT_SIDE_ML_PLAN](../plans/CLIENT_SIDE_ML_PLAN.md). | | `CardDetector` | ONNX YOLO card detection | None — client-only in production; the server-side YOLO card path was removed (web-app #111) | --- diff --git a/09-auth-flows/01-PLATFORM_CAPABILITY_MATRIX.md b/09-auth-flows/01-PLATFORM_CAPABILITY_MATRIX.md index 61764cf..a722695 100644 --- a/09-auth-flows/01-PLATFORM_CAPABILITY_MATRIX.md +++ b/09-auth-flows/01-PLATFORM_CAPABILITY_MATRIX.md @@ -105,9 +105,9 @@ FIVUCSAS supports **5 client platforms** and **10 authentication methods** acros - **Input**: Live camera feed for face capture - **Platforms**: All camera-equipped devices - **Dependencies**: Biometric Processor API (FastAPI), front-facing camera -- **Processing**: Server-side (image sent to biometric-processor for embedding extraction/matching) +- **Processing**: Server-authoritative matching + liveness verdict. Legacy/default path (flag OFF): the browser pre-filters and uploads the image; biometric-processor extracts the Facenet512 embedding and matches. Client-side embedding path (flag `app.auth.client-side-embedding`, default OFF): the browser computes the Facenet512 embedding via onnxruntime-web and uploads only the 512-d vector (raw image never leaves the device); the server matches against enrolled templates. Either way the server owns the match + liveness verdict + accept/reject decision. - **Quality Requirements**: Minimum face size 112x112px, centered, well-lit, eyes open -- **Anti-Spoofing**: Passive liveness detection (texture analysis) + optional active challenges (blink, smile) +- **Anti-Spoofing**: Passive liveness detection (texture analysis) + active Biometric Puzzle (randomized challenge, server re-scored) - **Enrollment**: 1-3 face images captured with quality validation - **Offline Capable**: No (requires biometric processor) diff --git a/adr/0004-facenet512-server-authoritative.md b/adr/0004-facenet512-server-authoritative.md index 3f4fc66..b7f7672 100644 --- a/adr/0004-facenet512-server-authoritative.md +++ b/adr/0004-facenet512-server-authoritative.md @@ -1,9 +1,25 @@ # ADR 0004: Facenet512 as the server-authoritative face embedding -**Status**: Accepted +**Status**: Accepted; **amended 2026-06-11** (see note below) **Date**: 2026-04-18 **Deciders**: Biometric processing, security, backend +> **Amendment (2026-06-11) — client-side embedding option.** The encoder choice +> (DeepFace **Facenet512**, **512-dim**, cosine match) is unchanged and remains the +> single platform-wide embedding shape. What changed: a flag-gated path +> (`app.auth.client-side-embedding`, default OFF) computes that **same Facenet512 +> embedding in the browser** (onnxruntime-web, exported from our own SHA-pinned +> weights) and uploads **only the 512-d vector** — the raw face image never leaves +> the device. When the flag is ON, the client embedding is the **authoritative** +> template/probe (re-enroll on the client pipeline so probe and template share +> preprocessing); the server still owns the pgvector match, the liveness verdict, and +> the accept/reject decision, and the legacy image→Facenet512 path is retained as +> fallback. This supersedes the §Decision claim that the client embedding is "a +> different model / shape … never compared against `face_embeddings`": the *old* +> `geometry-512` pre-filter (ADR 0003) was indeed different, but the *new* +> client-side Facenet512 is identical to the server encoder by design. See +> [`plans/CLIENT_SIDE_ML_PLAN.md`](../plans/CLIENT_SIDE_ML_PLAN.md) (v3.0). + ## Context Once we accepted that the client-side embedding cannot make auth decisions (ADR 0003), the next question was which model the *server* should run as the authoritative face encoder. The biometric-processor service stack includes DeepFace, which exposes a handful of backends (`Facenet`, `Facenet512`, `ArcFace`, `OpenFace`, `VGG-Face`, `DeepFace`, etc.). The choice determines: