Nirnay Aadhaar Protection Pipeline

Purpose

Nirnay must not persist a full Aadhaar number or an unmasked Aadhaar document. For newly processed Nirnay data, the database retains only a masked display value and an opaque vault reference. Lead Collection remains unchanged as an explicit forward-only boundary for the later platform-wide remediation.

Executive summary

For an officer, Aadhaar upload and verification remain familiar: upload both sides, then enter a 12-digit number only when verification is needed. Nirnay shows XXXX XXXX 1234, never the complete number.

Behind that experience, the system now guarantees that Nirnay stores only a masked document and, where a number is needed, an opaque protected reference. OCR is helpful but not required to produce the number: a provider can return a complete number, a masked number, or no number without changing the officer workflow or weakening the storage rule.

[Diagram]

Before

The existing flow did not consistently distinguish complete Aadhaar OCR from an OCR vendor's masked/no-number result. A complete OCR value could continue into Nirnay persistence, and dual-sided uploads could merge and upload the original front/back bytes. The main escape paths were:

  1. OCR candidates used the full card_number as raw_value and normalized_value in CustomerProfileFieldValue.
  2. A dual-sided upload performed OCR/masking but merged and uploaded the original front/back bytes.
  3. A masked/no-number OCR result could be treated as an Aadhaar candidate; the full Aadhaar could also be submitted to the generic manual-field endpoint.
[Diagram]

Now

The protection implementation makes the existing validation engine the single place where vendor OCR, masking, and Aadhaar protection behaviour lives. Documents and Customer Profile use internal capabilities and never import a vendor strategy directly. The decision is based on canonical OCR data, never the OCR vendor: exactly 12 digits follows the protected-candidate path; every other OCR result follows the masked-document-only path.

[Diagram]

Data persisted by Customer Profile

Field Aadhaar value retained
raw_value XXXX XXXX 1234
normalized_value XXXX XXXX 1234
protected_reference Aadhaar vault token
protection_scheme aadhar_vault_store
candidate/event hashes Masked value and protected reference only

protected_reference and protection_scheme are deliberately not included in Customer Profile API serializers.

Outcome matrix

Situation Document upload Aadhaar profile value Next officer action
OCR returns exactly 12 digits Stores only the masked document Creates masked, protected candidate Enter the number to verify it against the protected value
OCR returns a mask or no usable number Stores only the masked document No Aadhaar candidate is created Enter the number in Verify Aadhaar; it is protected and verified then
Masking provider does not return both masked sides Nothing is persisted until another eligible provider is tried No candidate Retry automatically through the validation engine; show upload failure only if no provider can supply both sides
OCR/masking/protection fails No raw document or Aadhaar candidate is stored No change Correct the document or retry later
Historical selected value has no protected reference Existing record is not used No runtime fallback Run the one-time remediation, then verify

Processing model

The ordered document-plan registry remains the source of truth. Aadhaar now has the following plan while other document plans stay unchanged.

[Diagram]

documents.services.verification.steps is the shared handler registry used by both synchronous dual-sided upload and the asynchronous document runner. It passes a transient in-memory context between steps. The context may contain OCR data, masked replacement image bytes, and a protection token; it is sanitized before Document records or events are saved. vault is successful but skipped with no_full_aadhaar_from_ocr when canonical OCR does not contain exactly 12 digits.

Upload behaviour

Dual-sided Aadhaar upload

[Diagram]

Provider capability safeguard

The validation engine may route a masking request to more than one approved provider. A successful provider response is accepted for a dual-sided upload only when it contains replacement images for both sides. This is important because some providers can return a valid masked front image without a usable masked back image.

[Diagram]

This safeguard lives in Validations V2, not in LOS or Customer Profile. It is therefore reusable for any future document type that requires replacement artifacts from a provider.

Aadhaar is rejected from the ordinary single-file upload path because it would place raw bytes in Nirnay storage before the masking step could complete.

Orchestrated Lead Collection document

[Diagram]

The source Lead Collection record and object are not changed. The Lead Temporal workflow runs OCR and masking in parallel, then persists OCR data on the masked Lead document; Nirnay must not attempt to recover a full Aadhaar by OCRing the already-masked artifact.

Manual Aadhaar verification

The UI does not prefill the stored mask as a value. It accepts a 12-digit Aadhaar input and sends it only to verify-aadhaar.

[Diagram]

The generic single-field and batch manual-field APIs reject aadhaar_number, preventing bypass of this flow. If vaulting succeeded but a subsequent manual persistence operation fails, the implementation makes a best-effort vault-token deletion call.

What the LOS UI shows

Moment What the officer sees
Aadhaar has been uploaded or verified A masked value such as XXXX XXXX 5398
Officer clicks edit An empty numeric field; the existing mask is never copied into the input
Officer saves Aadhaar Verify Aadhaar
Submitted number is invalid A request to enter a 12-digit Aadhaar number
Submitted number does not match an existing protected candidate A normal mismatch message; neither value is exposed

The UI does not mention tokens, vaulting, providers, OCR vendors, or any other backend protection mechanism.

Persistence and audit safety

The following data is removed before Document persistence or event recording:

Documents retain only safe step status/diagnostic summaries and, for the vault step, a masked display value. Validations continues to own its existing redacted audits. Customer Profile PII redaction also treats vault-token fields as secrets.

Main implementation locations

Concern Location
Internal vault service Validations/engine/services/aadhaar_vault.py
Shared document handlers/sanitizer documents/services/verification/steps.py
Aadhaar document plan documents/services/verification/registry.py
Protected field processor registry customer_profile/services/field_processing.py
Candidate persistence customer_profile/services/bootstrap.py
Manual match/verify customer_profile/services/aadhaar_match_verify.py
Orchestration import protection documents/services/orchestration_ingest.py
LOS replacement-only edit behaviour intra-frontend/components/los/customer-profile/PDCustomerProfilePage.tsx

Verification performed

Scope boundary

This implementation is forward-only for Nirnay. Historical tokenless Nirnay rows are repaired only through the separately supplied, one-time shell remediation; runtime code does not contain a legacy fallback. Lead Collection source data and historical artifacts remain outside this scope.