Skip to main content

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 in apps/

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 variable ocr_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_evidence rejections.

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 a verification.completed webhook. 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 pg and undici errors thrown inside the OCR handler used to escape Fastify's global handler as internal_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 typed EkycError envelopes:

    • Postgres errors (5-character SQLSTATE on .code) → internal_error with the pg message verbatim plus db_code in extra.
    • undici / Node network errors (UND_ERR_*, ECONNREFUSED, ENOTFOUND, ETIMEDOUT, etc.) → service_unavailable with network_code in extra.
    • Truly unknown errors still bubble to the global handler so the original stack is logged.

    Same defensive-coding gap exists in face.ts and liveness.ts — tracked as a follow-up.