High-Level Architecture


Domain Breakdown

The current domain set is: account, admin_narayana, auth, benefit, benefit_entity, benefit_provider, benefit_provider_order, config, consultation, daily_contribution, dashboard_user, dependant, event_log, insurance_policy, mandate, mandate_execution, mrn, order, platform, sponsor, and user. The cards below highlight the user-facing domains.

Auth

Tables: users, user_key_storeBearer JWT end to end. A platform service account (authenticated to Keycloak via client_credentials) calls POST /auth/token with `{phone_number, phone_country_code, id_proof}` and receives `{user_id, access_token}` — a stateless, self-contained app JWT (RS256).

User Profile

Tables: users (shared with Auth), dependantReads/writes profile fields (first_name, last_name, email, dob, gender, address). phone_number and id_proof are set at provisioning. PII is encrypted at rest via a per-user key in user_key_store.

Account (PBA)

Tables: accounts, ordersUser money lives in a Prepaid Bank Account (PBA) ledger, surfaced via GET /users/{user_id}/balance and GET /users/{user_id}/ledger. Contributions flow through orders; the holder column keys each account to a user:<id> or sponsor:<id>.

Insurance Policy

Tables: insurance_policies, benefitsPolicy lifecycle under /users/{user_id}/insurance_policies/*. Each policy references a benefit and covers a set of dependant_ids. Purchase/issuance is delegated to Narayana Health.

Order / Mandate

Tables: orders, mandates, mandate_executionsSelf-contribution and sponsor-funded top-ups create orders. Recurring autopay is driven by a Juspay UPI mandate; each scheduled debit lands a row in mandate_executions.

Consultation

Tables: consultations, benefitsProvider-agnostic chat-doctor pipeline (currently Narayana Health). A consultation is tied to a dependant and a consultation benefit; messages are exchanged through the provider gateway. See ADR-005.

User Journeys

Manual Flow (User-Initiated)

Automated Flow (Habit Forming — Autopay + Insurance)

This is the primary flow for drivers on a host platform. After a one-time mandate setup, the system automatically tops up the PBA account and purchases insurance on schedule — no manual intervention needed.

Scheduled Autopay (Mandate Execution)

Recurring debits are not driven by an in-process worker. An external scheduler (Kronos) fires each due mandate into the backend at POST /mandate/{mandate_id}/execute, passing a per-firing X-Mandate-Execution-Id (the job_execution_id). The handler is idempotent: it claims a mandate_executions row via INSERT ... ON CONFLICT (job_execution_id) DO NOTHING RETURNING *, so a Kronos retry of the same firing replays rather than double-charges. The MandateExecutionStatus enum is initiated | pending | success | failed. Each firing also mints an orders row (the funding ledger entry) before the provider charge, satisfying the mandate_executions.order_id foreign key.

Security Model

App JWT (mobile)

RS256-signed app token issued by POST /auth/token. Validated on every protected endpoint by the auth middleware. Stateless — verified by signature alone, with no per-request DB lookup.

Platform Service Accounts

Only a platform service account may call POST /auth/token. It authenticates to Keycloak via client_credentials and presents a bearer JWT with actor_type: "platform_service".

Admin / Dashboard OIDC

Admin, dashboard, and other service actors authenticate via Keycloak OIDC. Tokens are JWKS-verified (signature + iss + aud + exp) and the actor_type claim drives role-based access.

PII Protection

PII fields are encrypted at rest with a per-user data key in user_key_store (application-level). Secret fields (phone, tokens) are wrapped in Secret<T> and never logged in plaintext.
Token issuance is entirely service-account driven — the host platform exchanges the user’s {phone_number, phone_country_code, id_proof} for an app JWT on the user’s behalf.