Kayle ID verifies a person’s identity by reading the cryptographic chip embedded in their ID document, checking the document’s signatures against the issuing country’s public key infrastructure, and matching the on-chip portrait against a live selfie. The underlying biometric and chip data never leaves the verification pipeline.Documentation Index
Fetch the complete documentation index at: https://docs.kayle.id/llms.txt
Use this file to discover all available pages before exploring further.
Components
A complete Kayle ID deployment is a small set of services that talk to each other and to the user’s phone:| Component | Role |
|---|---|
apps/api | The Cloudflare Worker that exposes the public API and orchestrates verifications. |
apps/verify | The web app users land on (verify.kayle.id). Renders consent and produces a handoff payload for the mobile app. |
apps/ios, apps/android | Native apps that read the passport chip over NFC and capture the selfie. |
infra/face-matcher | A separate Worker plus container that compares the on-chip portrait (DG2) to the user’s selfies. |
database/trust-store | A Cloudflare D1 database seeded from the ICAO PKD. Holds CSCA certificates and CRLs used to verify document signatures. |
database/kayle-id | Postgres. Stores sessions, attempts, events, webhook deliveries — metadata only, never document data. |
A typical verification
The API is the only component you talk to directly. Everything pastverification_url is the user’s experience, driven by the verify app and the mobile app.
The trust model
Three independent checks run before a verification is accepted:- Passive authentication — The Security Object (SOD) on the chip is a CMS-signed manifest of every data group. Kayle verifies the SOD signature against the Document Signer Certificate (DSC), then walks the chain to a Country Signing CA (CSCA) trusted by the ICAO PKD. Each data group’s hash must match the SOD declaration. Recent Certificate Revocation Lists are consulted; missing or stale CRL coverage is reported as
revocation_unknownand tightens the policy decision. - Active authentication — If the document carries a DG15, the chip signs a freshly derived challenge with its private key. A successful signature proves the chip you’re talking to is the same chip that was personalised at issuance, not a clone holding the same data.
- Chip authentication — If the document carries a DG14 (most modern passports), the chip and the reader run an authenticated key agreement. The transcript binds the rest of the session to the genuine chip and frustrates man-in-the-middle attempts.
The verification protocol
Mobile clients drive the verification over a single WebSocket. The wire format is Cap’n Proto, defined inpackages/capnp/verify.capnp. The protocol has a small message vocabulary: a hello, a handful of phase updates, chunked binary data payloads (DG1, DG2, DG14, DG15, SOD, selfie frames, active-auth signatures), and a server verdict.
You don’t implement this yourself. The mobile apps implement it; the verify web app coordinates the handoff.
Data the API stores
The Postgres database holds:- Verification sessions — status, expiry, requested share fields, optional
redirect_url, hashedcancel_token. - Verification attempts — status, failure code if any, risk score, hashed mobile write token, hashed device ID, app version, current phase.
- Events — domain events (
verification.attempt.succeeded, etc.) with a trigger ID, but no PII. - Webhook deliveries — endpoint ID, status, attempt count, payload (encrypted in transit when an encryption key is registered).