Platform changelog
The platform's per-component changelogs live in their respective repositories:
- api-gateway — currently published as commits; aggregator coming
- control-plane
- ML services (
svc-ocr,svc-face,svc-liveness) — published as commits inapps/
For high-level rollout milestones, see the rebuild roadmap and per-sub-project design specs.
Recent notable changes
2026-09-07 — liveness
- Shorter replay window for test keys. Frames scored under a test key stay in the replay guard for 60 seconds instead of 24 hours, so integration tests can reuse a fixture image. Live keys are unchanged. See Liveness checks.
2026-09-07 — OCR
- Black-and-white captures are rejected. A grayscale scan or photocopy fails with
monochrome_image; the floor is the tenant variableocr_min_chroma. - Resolution floor lowered to 550 × 350 px (was 800 px on the long side), and the floors became tenant variables.
- Decision thresholds are tenant-editable on the dashboard, with a change history. The OCR side-matching and resolution controls joined the liveness, fraud and face thresholds there.
- CID side matching works on phone photos with the card small in the frame: the card is located and cropped before the front and back ghost portraits are compared, and the crops are kept for review when source images are stored.
- Faster two-sided CID scans. Typical processing time fell from 16 to 18 seconds to 7 to 9 seconds on production hardware.
- Bilingual title lines read correctly. Dzongkha text the recogniser cannot read no longer masks the Latin document title, which removed a class of false
missing_document_evidencerejections.
2026-09-03 — platform
- Verification sessions (
vf_objects) bind a document scan, a liveness check and a face match into one decision under a tenant policy, with averification.completedwebhook. See Verification sessions. - Liveness frame replay guard. Frames are single use per tenant; a reused frame returns
frame_replayed. The challenge action is now always chosen by the server. - Fraud cross-submission memory. Repeated documents, portraits and numbers across submissions surface as review evidence.
2026-08-18 — OCR
- CID ghost binding. The front and back of a citizenship card must carry the same ghost portrait; mismatched pairs are rejected as
side_mismatch.
2026-05-13 — api-gateway
-
OCR route: defensive IO-error mapping. Raw
pgandundicierrors thrown inside the OCR handler used to escape Fastify's global handler asinternal_error / unexpected, hiding the actual cause (database error, upstream unreachable, etc.) from the caller and the audit log. The route now maps known IO shapes to typedEkycErrorenvelopes:- Postgres errors (5-character SQLSTATE on
.code) →internal_errorwith the pg message verbatim plusdb_codeinextra. - undici / Node network errors (
UND_ERR_*,ECONNREFUSED,ENOTFOUND,ETIMEDOUT, etc.) →service_unavailablewithnetwork_codeinextra. - Truly unknown errors still bubble to the global handler so the original stack is logged.
Same defensive-coding gap exists in
face.tsandliveness.ts— tracked as a follow-up. - Postgres errors (5-character SQLSTATE on