ABDM Health Locker implementation validation
Audit date: 18 September 2026
Repositories: orvo-abha and orvo-phr
Scope: Static implementation and test review against the published ABDM Health Locker,
PHR, HIU, HIP, consent, subscription, FHIR, and encrypted data-transfer flows. This is not a
substitute for the current ABDM certification test pack or a sandbox trace captured with Orvo's
production-bound credentials.
Executive summary
Orvo has the main Health Locker protocol chain in place. The platform is represented as both HIP
and HIU, can set itself up as the patient's locker, receives LINK and DATA subscription events,
creates PATRQT consent requests, receives encrypted FHIR data as an HIU, serves stored documents
as a HIP, and creates/link care contexts for patient uploads. orvo-phr exposes setup, status,
pause/resume, and self-uploaded document management.
The implementation is functionally substantial but not yet complete enough to treat “Active” as guaranteed automatic synchronization. The most important gaps are:
- A fallback auto-approval policy created after locker setup is not durably associated with the locker subscription, so it may not be disabled later.
- Re-enabling a subscription does not re-enable or recreate the auto-approval policy disabled by the pause operation.
- The
DATAhandler reuses a granted consent based only on patient, HIP, grant status, and expiry; it does not prove that purpose, HI types, care contexts, or data period cover the notification. LINK/DATAfollow-up operations are process-local fire-and-forget work after the ABDM event is acknowledged. A crash can lose the work while preventing ABDM redelivery.- The PHR presents a single active/paused boolean and promises automatic synchronization even when auto-approval is off, failed, expired, or unknown.
- The deletion lifecycle for an already-linked self-uploaded care context is not defined.
Remediation update — 18 September 2026
The first implementation pass after this audit resolved or materially mitigated the following:
- HL-01: fallback auto-approval IDs are captured and persisted.
- HL-02: resume also attempts to resume the recorded policy and returns partial success.
- HL-03: DATA consent reuse now fails closed through a shared full-scope evaluator.
- HL-04: LINK/DATA work is awaited, so the durable callback row is not marked processed early.
- HL-05: future event categories reach the durable log, are ACKed, and remain processing errors.
- HL-06: setup and resume return independent subscription/policy outcomes.
- HL-07: authenticated status reconciliation cleans up a policy left by an external revoke.
- HL-08–11: the PHR defaults auto-approval off, distinguishes manual/automatic/unknown states, surfaces normalized API errors, warns on partial success, and refetches authoritative state.
- HL-12: deletion is refused after its care context is linked, preventing an orphaned ABDM context until NHA provides a supported unlink/delete lifecycle.
- HL-13: active PDF actions are rejected as defense in depth. Production still requires an infrastructure malware/CDR scanner; token inspection is not antivirus scanning.
- HL-14–15: duplicate live subscriptions and unknown policy status are explicit states.
The remaining external/operational work is a production malware/CDR integration, current-sandbox execution of the scenarios below, and NHA confirmation of the linked-care-context deletion rule.
Source baseline
The validation uses these published sources:
- Building Health Locker
- ABHA Mobile (PHR) Application
- FHR components and roles
- Building HIU
- Building HIP
- Data request and transfer
- Requester-HIU encryption and decryption
- APIs and data standards
- NHA ABDM Wrapper
- ABDM Health Data Management Policy
Some public pages retain older NDHM/v0.5 terminology while Orvo targets HIE-CM v3. Architectural requirements are stable, but exact payloads, callbacks, error codes, and status values must also be checked against the current NHA collection supplied during Orvo onboarding and real sandbox traces.
Canonical flow and validation matrix
| Stage | ABDM expectation | Backend | PHR | Result |
|---|---|---|---|---|
| Entity onboarding | Locker registered as HEALTH_LOCKER, HIP, and HIU with callback and launch URLs |
Configuration support exists; NHA-side registration cannot be proven from code | Not applicable | Operational evidence required |
| Patient setup | Authenticated patient selects locker and grants subscription | POST /v1/phr/health-locker/setup; system locker ID resolved server-side |
Setup modal exists | Implemented |
| Subscription scope | LINK + DATA, HIP scope, HI types, purpose, validity period | Generic approval/edit schemas support the scope | Generic subscription UI supports selections; locker setup hides actual resulting scope | Partial |
| Auto-approval | Separate, explicit patient choice with bounded policy | Optional policy setup exists | Explicit checkbox exists, but defaults on and does not display scope/expiry | Partial |
| Subscription callback | Correlate, persist, acknowledge, handle duplicates | Request/subscription correlation and ACK logic exist | Status is refreshed by API | Implemented with durability risk |
| LINK event | Request matching PATRQT consent for new contexts | Implemented with care-context parsing and deduplication | Consent pages can show/manage grants | Implemented with durability risk |
| DATA event | Reuse only a consent whose complete scope covers the event, otherwise request consent | Existing lookup checks only HIP/status/expiry | Not visible to patient | Gap |
| HIU receive | Request under granted artefact, decrypt, normalize/store FHIR, notify CM | Fidelius request/receive and recovery services exist | My Records renders normalized FHIR | Implemented; sandbox E2E evidence required |
| Self upload | Validate, wrap as FHIR, create/link locker-owned care context | Implemented for PDF/JPEG/PNG with byte-signature check | Upload/view/delete UI exists | Mostly implemented |
| HIP onward share | Validate consent scope, encrypt eligible FHIR, push to requesting HIU, notify CM | HIP health-information request/push pipeline exists and uses stored documents | Consent UI exists | Implemented; locker-specific E2E test required |
| Pause/resume | Stop/resume events; manage auto-approval separately and consistently | Pause disables both best-effort; resume enables subscription only | One switch implies one state | Gap |
| External revoke | Reconcile subscription and auto-approval changed outside Orvo | Subscription callback updates local state; policy cannot be disabled without patient token | Refresh can show remote state | Partial |
| Delete | Enforce ownership, storage deletion, retention/audit, and linked-context behavior | Ownership and object deletion exist | Delete has no confirmation or lifecycle explanation | Partial |
| Observability | Durable idempotency, retry/replay, correlation and dead-letter handling | Rich logs and sync recovery exist; event branches are fire-and-forget | Generic error toasts only | Partial |
What is correctly implemented
1. Orvo's own locker identity is resolved on the server
The PHR never supplies an arbitrary locker ID for the one-click flow. The backend resolves the configured system HIU/locker and binds both subscription setup and self-uploaded records to it. This prevents client-side locker impersonation and supports the ABDM model where one approved Health Locker identity carries HIP and HIU responsibilities.
Evidence:
src/modules/abdm/phr/health-locker/health-locker.service.tssrc/modules/abdm/core/credential-source.resolver.tssrc/modules/abdm/hip/health-locker-admin/health-locker-admin.service.ts
2. Locker setup is correlated with asynchronous ABDM callbacks
Before calling the ABDM locker endpoint, Orvo creates a pending subscription request. The callback service stores both subscription-request and durable subscription identifiers, acknowledges events, and has a locker-specific fallback correlation by patient and HIU for the callback shape that does not echo Orvo's request ID.
Evidence:
src/modules/abdm/shared/subscription-request.store.tssrc/modules/abdm/hip/hiu-subscription/subscription.service.tssrc/modules/abdm/phr/health-locker/health-locker.service.ts
3. LINK and DATA are treated as distinct triggers
The subscription event handler parses ABDM's grouped care-context shape. A LINK event creates a
context-scoped, self-requested (PATRQT) consent and avoids known duplicate requests. A DATA event
tries to use an existing grant and otherwise creates a PATRQT request. Empty or unparseable LINK
events do not create blind blanket requests.
Evidence:
src/modules/abdm/hip/hiu-subscription/subscription.service.tssrc/modules/abdm/hip/hiu-subscription/__tests__/subscription.service.test.ts
4. The encrypted health-information path exists in both directions
As HIU, Orvo generates and retains ephemeral key material, requests health information, accepts the data push, decrypts Fidelius payloads, and normalizes FHIR bundles. Recovery logic looks for granted consents that did not progress to a health-information request and for stalled transfers.
As HIP, Orvo accepts a health-information request, finds eligible linked documents, produces FHIR, encrypts the response, pushes it to the requesting HIU, and records transfer status.
Evidence:
src/modules/abdm/hip/consent/operations/health-information.operations.tssrc/modules/abdm/hip/consent/operations/consent-callback.operations.tssrc/modules/abdm/core/crypto/src/modules/abdm/sync/sync.service.ts
5. Self-uploaded records enter the HIP document pipeline
The backend validates declared PDF/JPEG/PNG signatures, maps the document to an ABDM HI type, generates a unique care-context reference, builds/stores a FHIR bundle, associates it with the locker facility, and attempts care-context linking. Patient reads and deletes are constrained by both ABHA address and uploader identity.
Evidence:
src/modules/abdm/phr/health-locker/health-locker.service.tssrc/modules/abdm/hip/health-document/health-document.service.tssrc/modules/abdm/hip/hip-linking/hip-linking.service.ts
6. Embedded ABDM failures and live statuses are normalized
ABDM can return HTTP 200 with an embedded error. Enable/disable now converts that body to a real
application error. The locker considers ACTIVE, ENABLED, and GRANTED live, case-insensitively,
so a live subscription is not incorrectly offered an enable transition.
Evidence:
src/modules/abdm/core/abdm.errors.tssrc/modules/abdm/phr/subscription/subscription.service.tssrc/modules/abdm/phr/subscription/__tests__/subscription.enable-disable.test.tssrc/modules/abdm/phr/subscription/__tests__/subscription.get-orvo-subscription.test.ts
Findings requiring changes
HL-01 — Fallback auto-approval ID is not persisted
Severity: High
The locker first records an auto-approval ID returned by setup-locker. If none is returned and the
patient requested auto-approval, Orvo calls setupAutoApproval, but ignores the returned policy and
does not call recordLockerAutoApprovalId again. disableLockerAutoApproval later has no handle for
that policy and silently returns.
Risk: pausing the locker may leave a standing auto-approval policy active at the CM.
Required change:
- Capture the fallback setup response.
- Extract and persist its auto-approval ID on the subscription request.
- Return separate subscription and policy outcomes.
- Add a regression test covering setup fallback followed by disable.
HL-02 — Resume does not restore auto-approval
Severity: High
Pause enables two transitions: disable subscription, then best-effort disable policy. Resume only enables the subscription. The status can therefore be “Active” with auto-approval off, while the PHR states that records synchronize automatically.
Required change: make resume an explicit patient choice:
- “Resume notifications only”; or
- “Resume notifications and auto-approval,” which re-enables or creates a bounded policy.
The response must expose both outcomes and must not claim automatic sync if only the subscription is active.
HL-03 — DATA consent selection does not validate complete scope
Severity: Critical
The DATA branch selects the newest granted, unexpired consent for the patient and HIP. It does not check:
- Requesting HIU is Orvo's Health Locker identity.
- Purpose is
PATRQT. - Not-before/effective dates.
- Consent data period.
- Allowed HI types.
- Allowed care contexts from the event.
- Frequency/access limits, if present.
- Paused state represented inside the artefact.
Expiry and HIP equality are necessary but not sufficient. A consent for one diagnostic report must not authorize requesting every record from the HIP.
Required change: create one shared consentCoversSubscriptionEvent() scope evaluator used by DATA
events and by the health-information request guard. If the event lacks enough scope information to
prove coverage, request fresh consent instead of broadening an existing grant.
HL-04 — Event work can be lost after acknowledgement
Severity: High
The LINK/DATA handler acknowledges the ABDM event and then invokes consent/health-information work
with .catch(...) without awaiting or durably enqueueing it. If the process exits after ACK and
before completion, ABDM will not redeliver the event. Logs do not provide guaranteed replay.
Required change:
- Persist the raw event keyed by
event.idbefore ACK. - Upsert idempotently on redelivery.
- ACK once durable acceptance succeeds.
- Process from a retryable worker with attempt count, backoff, last error, and dead-letter state.
- Provide an operator replay action.
The existing sync recovery worker reduces some health-information stalls but does not replace a durable LINK/DATA event inbox.
HL-05 — Unknown category is acknowledged and dropped
Severity: Medium
An event category other than LINK/DATA reaches neither branch after ACK. Persisting unknown events would make future ABDM additions observable and replayable instead of silently discarded.
HL-06 — Auto-approval setup has an invisible partial-success state
Severity: High
Policy setup is best-effort. The API can report locker setup success even if automatic approval failed or was disabled administratively. The PHR always shows “Health locker set up successfully.”
Required change: return and render a structured state such as:
{
"subscription": { "state": "ACTIVE" },
"autoApproval": { "state": "FAILED", "retryable": true },
"syncMode": "MANUAL_CONSENT"
}
HL-07 — Remote revoke cannot reconcile its policy
Severity: High
The code records that a revoke performed in another ABHA application cannot disable the policy because the callback has no patient token. This leaves two remote resources that can diverge.
Required change: on the patient's next authenticated status refresh, reconcile the subscription and locker policy. Clearly show any active policy attached to a revoked subscription and offer a patient-authorized cleanup action. Record reconciliation time and outcome.
HL-08 — PHR overstates automatic behavior and hides scope
Severity: High (consent UX)
The setup modal and status card say records from every linked facility synchronize automatically. That is only true when subscription scope includes the HIP/category/HI type, auto-approval matches, consent is granted, and transfer succeeds. The UI does not show:
- Subscription scope or validity.
- Auto-approval HI types or expiry.
- Manual-consent mode.
- Partial setup or reconciliation failure.
- Last successful sync or pending sync.
Required change: distinguish “notifications active” from “automatic consent active,” show a concise scope summary, and link to full subscription/consent details.
HL-09 — Default-on auto-approval needs stronger consent presentation
Severity: Medium
The checkbox is explicit but defaults to checked and describes “future records” without showing HIP scope, HI types, purpose, or policy duration. Before certification/privacy review, confirm that this meets the current NHA consent UX requirements. A safer presentation is unchecked by default or a separate confirmation showing the exact bounded policy.
HL-10 — Generic error toasts discard actionable ABDM errors
Severity: Medium
Setup, toggle, upload, and deletion all show generic failure messages. The backend now supplies useful normalized errors, but the PHR does not display them. This hid the earlier “not in revoked state” mismatch.
Required change: show safe server messages and an error/reference code, then refetch authoritative status after every failed transition.
HL-11 — Optimistic switch behavior is not reconciled immediately
Severity: Medium
The switch is controlled by cached status and invalidated only on success. On failure, the UI does
not force a status refetch; on eventual ABDM state changes, invalidation may still read a transitional
state. Add an onSettled authoritative refetch and render transition states.
HL-12 — Delete does not define linked care-context behavior
Severity: High
The patient can delete the stored file, but the audit found no explicit tombstone/unavailability strategy for an already-linked ABDM care context. A later consent request could refer to a context whose document no longer exists.
Required change: decide and test the product rule with ABDM/NHA:
- retain a tombstone and omit unavailable content with a defined transfer status;
- unlink/deactivate through an approved ABDM mechanism, if supported; or
- restrict deletion and apply the documented retention workflow.
The PHR also needs a confirmation explaining whether deletion affects future sharing and whether it is recoverable.
HL-13 — File safety is format validation, not malware scanning
Severity: High (security)
The backend verifies magic bytes, size, and supported MIME types. That prevents simple MIME spoofing but does not detect malicious PDFs/images or parser exploits. Add quarantine, malware scanning, safe previewing, scan state, and rejection/audit behavior before making uploads available for HIP sharing.
HL-14 — Multiple active locker subscriptions are silently collapsed
Severity: Medium
getOrvoSubscription prefers a non-terminal record and then the newest. If ABDM contains duplicate
live subscriptions, the patient sees one without a reconciliation warning. Detect multiple live
matches, log/metric the condition, and expose an operator/patient-safe recovery path.
HL-15 — Auto-approval status failure is represented as false
Severity: Medium
If list-lockers fails, status defaults autoApprove to false. This conflates “confirmed off” with
“unknown.” Use a tri-state (ACTIVE, INACTIVE, UNKNOWN) and surface last successful
reconciliation time.
HL-16 — Locker-specific onward-sharing proof is missing
Severity: Medium
The generic HIP pipeline exists, but static review alone does not prove that both imported locker records and self-uploaded records can be selected, packaged, encrypted, and pushed under an external HIU consent. Add an end-to-end test fixture for each provenance type.
Recommended state contract
Do not derive the whole locker from one enabled boolean. A compatible response should separate the
remote resources and sync process:
interface HealthLockerState {
registration: 'UNAVAILABLE' | 'REGISTERED'
subscription: {
state:
| 'NOT_SETUP'
| 'REQUESTED'
| 'ACTIVE'
| 'DISABLED'
| 'REVOKED'
| 'DENIED'
| 'EXPIRED'
| 'ERROR'
id: string | null
categories: Array<'LINK' | 'DATA'>
validUntil: string | null
}
autoApproval: {
state: 'ACTIVE' | 'INACTIVE' | 'EXPIRED' | 'FAILED' | 'UNKNOWN' | 'NOT_REQUESTED'
id: string | null
hiTypes: string[]
validUntil: string | null
}
sync: {
state: 'IDLE' | 'WAITING_FOR_CONSENT' | 'FETCHING' | 'PARTIAL_FAILURE' | 'ERROR'
lastSuccessfulAt: string | null
}
reconciledAt: string | null
}
The simple switch can remain, but it should be a capability-driven control: canEnable,
canDisable, and canRepair, based on this state.
Required validation scenarios
These should run against the current ABDM sandbox in addition to unit/integration tests.
- Fresh patient setup without auto-approval; LINK produces a manually approvable PATRQT consent.
- Fresh setup with auto-approval; verify exactly one bounded policy exists.
- Repeated setup click/network retry; verify no duplicate subscription or policy.
- LINK notification with multiple HI-type groups and care contexts.
- Duplicate LINK event; verify idempotent inbox and one consent request.
- DATA event covered by a matching consent; verify one health-information request.
- DATA event with wrong HI type/context/purpose/period; verify fresh consent instead of reuse.
- Encrypted multi-entry FHIR push; verify decryption, normalization, provenance, and CM notify.
- Process crash after event persistence and before processing; verify worker retry.
- Pause; verify subscription and policy independently become inactive.
- Resume with and without auto-approval; verify displayed mode matches behavior.
- Revoke from the reference ABHA app; sign into Orvo and verify reconciliation/cleanup UX.
- Policy expiry while subscription remains active; verify “manual consent” rather than “automatic.”
- Self-upload PDF/image; verify FHIR profile, care-context link, discovery, and patient display.
- Delete linked self-upload; verify the approved care-context/tombstone behavior.
- External HIU consent for a self-uploaded record; verify Orvo HIP encryption and transfer.
- External HIU consent for a record imported from another HIP; verify provenance and permitted onward sharing behavior.
- Duplicate live subscriptions and unknown status/category values; verify safe degraded state.
- ABDM HTTP 200 embedded error; verify backend failure and informative PHR recovery.
- Unsupported/malicious upload; verify quarantine/rejection and no ABDM linking.
Delivery order
Phase 1 — correctness and consent safety
- HL-01 fallback policy persistence.
- HL-02 resume semantics.
- HL-03 complete consent-scope evaluator.
- HL-04 durable event inbox/worker.
- HL-06 partial-success contract.
- HL-07 authenticated reconciliation.
Phase 2 — truthful patient experience
- HL-08 state/scope UI.
- HL-09 consent presentation.
- HL-10 actionable errors.
- HL-11 authoritative transition reconciliation.
- HL-15 unknown auto-approval state.
Phase 3 — document lifecycle and assurance
- HL-12 deletion/care-context rule.
- HL-13 malware-safe upload pipeline.
- HL-14 duplicate-subscription repair.
- HL-16 locker-specific onward-sharing E2E tests.
Certification evidence to retain
For each sandbox scenario, retain redacted evidence containing:
- Orvo request/correlation ID and ABDM request/event/transaction IDs.
- Callback order and idempotency result.
- Subscription, policy, consent, and transfer state transitions.
- Consent scope summary without patient health content.
- FHIR profile validation result.
- Encryption/decryption success metadata without keys or plaintext.
- HIP and HIU completion notifications.
- Patient-visible state before and after the operation.
- Retry/replay evidence for injected failures.
Never place patient tokens, ephemeral private keys, decrypted clinical content, uploaded document bytes, or full consent artefacts in routine logs or certification screenshots.