Technical Requirements: Offline-First and Client Synchronization (Platform)
Domain: Platform cross-cutting
Version: 1.0
Date: 2026-03-28
Service surfaces: Client applications (e.g. ehr-web, future native), Kong, owning NestJS services
References:
- SPEC.md
- SOLUTION_DESIGN.md
- OFFLINE_FIRST_AND_CLIENT_SYNC.md
- ARCHITECTURE_BASELINE.md
- TESTING_STANDARDS.md
1. Technology assumptions
| Area | Choice / note |
|---|---|
| Transport | HTTPS only to Kong-exposed APIs for browser and mobile clients |
| Auth | Bearer JWT (Keycloak / IAM); session refresh before sync burst |
| Server idempotency store | Redis or equivalent cache manager where implemented (e.g. registration patient create TTL 24h) |
| Client persistence | Technology-agnostic; MUST be encrypted at rest when PHI is stored per COMPLIANCE_SECURITY.md |
2. Technical requirements (TR-OFF-###)
| ID | Requirement |
|---|---|
| TR-OFF-001 | Every queued mutation intended for replay SHALL include a stable clientMutationId (UUID v4 or ULID recommended) generated once per logical user action. |
| TR-OFF-002 | Server APIs that adopt offline replay SHALL accept Idempotency-Key (HTTP header) and/or clientMutationId in the request body where documented; dedupe key SHALL be scoped by tenant (and user where required). |
| TR-OFF-003 | Idempotent success responses SHALL be byte-stable for the same key (same JSON shape and domain ids) to simplify client reconciliation. |
| TR-OFF-004 | Ordering: mutations affecting the same aggregate (e.g. same patientId) SHOULD be sent in causal order; parallel races are resolved via optimistic versioning or module-specific rules. |
| TR-OFF-005 | BFF routes (e.g. Next.js app/api/**) SHALL forward Idempotency-Key from client to upstream service when the owning API supports it. |
| TR-OFF-006 | Kong routes SHALL not strip Idempotency-Key or Authorization headers needed for sync; rate limits apply as for online traffic. |
| TR-OFF-007 | On 401/403 during sync, clients SHALL NOT infinitely retry; refresh token or prompt re-login per IAM policy. |
| TR-OFF-008 | On 429, clients SHALL use exponential backoff with jitter. |
| TR-OFF-009 | Structured error bodies from services SHALL include code where possible so clients can branch (conflict, duplicate, validation) without string matching. |
| TR-OFF-010 | NATS publishing remains server-only; clients never connect to the broker from the public internet. |
3. Reference implementation: patient registration
Owning service: registration
| Item | Implementation |
|---|---|
| Endpoint | POST /v1/patients |
| Idempotency | Header Idempotency-Key or body clientMutationId; duplicate returns cached 201 payload |
| Storage | Redis-backed cache (see service PatientsController + CACHE_MANAGER) |
| Module gate | @ModuleKey('ehr.registration') + ModuleEntitlementGuard |
Documentation: registration API_DOCS.md.
Gap: Client-side outbox in ehr-web not yet wired to send clientMutationId on every registration submit; platform FR-OFF-002 is satisfied at the API layer first.
4. Client outbox (target architecture)
When implemented for a shell (e.g. ehr-web):
| Field | Purpose |
|---|---|
id | Local primary key |
clientMutationId | Idempotency token |
tenantId | From session; never trust local queue without session binding |
operation | e.g. POST /v1/patients |
payload | Serialized JSON |
status | pending / syncing / completed / failed / conflict |
serverResponse | Last 2xx snapshot or error code for UX |
createdAt / updatedAt | For retention and ordering |
Retention: PHI in the queue SHALL follow device wipe and session policies.
5. Security
| Topic | Requirement |
|---|---|
| Encryption at rest | Local DB + secure storage for tokens; see COMPLIANCE_SECURITY.md § offline. |
| Integrity | Optional signed envelopes for offline audit buffers when audit module requires. |
| Tenant isolation | Queue rows filtered by tenant; no shared cache across tenants on device. |
6. Observability
| Topic | Requirement |
|---|---|
| Correlation | Propagate correlationId from client (if provided) through BFF to service for support. |
| Logging | No raw PHI in application logs; use ids and codes. |
7. Appendix: HTTP headers (convention)
| Header | Use |
|---|---|
Authorization | Bearer access token |
Idempotency-Key | Opaque string; same semantics as Stripe-style idempotency for POST |
X-Correlation-Id | Optional; trace across sync attempts |
8. Appendix: Error codes (client handling)
Clients SHOULD handle at least:
| Code | Typical HTTP | Action |
|---|---|---|
OPTIMISTIC_LOCK_CONFLICT | 409 | Refresh + merge |
DUPLICATE_DETECTED | 409 | User review (registration) |
MODULE_NOT_ACTIVE / licensing | 403 | Disable offline queue for module or show upgrade |
PATIENT_SEARCH_INSUFFICIENT_CRITERIA | 400 | Broaden search inputs |
Exact codes are defined per module TECHNICAL_REQUIREMENTS.md.