Technical Requirements: Desktop Electron EHR Client
Domain: Platform client (Electron)
Version: 1.0
Date: 2026-03-31
Companion: SPEC.md, SOLUTION_DESIGN.md
References:
1. Technology stack
Canonical theme stack: ../../common/THEME.md §3 defines the single base theme shared by desktop, web, and mobile. The table below summarises desktop-specific choices aligned to that canonical source.
| Area | Requirement |
|---|
| Shell | Electron (supported LTS track; version pinned in repo). |
| Renderer | React + MUI (foundation + complex components) + Tailwind CSS (utility styling, Preflight disabled — MUI CssBaseline is the reset) + Emotion (MUI internals + sx escape hatch) + Motion (motion.dev) for animation. Major versions aligned with apps/ehr-web. |
| Tokens & theme | @ghasi/design-tokens + @ghasi/theme-runtime — tokens drive MUI theme, Tailwind preset, CSS variables, and motion tokens. Per-tenant overrides resolved via config-resolver per ../../common/THEME.md §5. |
| Icons | @ghasi/icons-react — shared SVG source with RTL-mirror metadata. |
| Build | Vite for renderer and preload bundles; avoid running Next.js inside Electron for the primary UI. |
| Language | TypeScript strict mode. |
| HTTP | HTTPS to Kong-exposed APIs only for sync; no NATS from client (offline-first SPEC). |
| Shared UI | Workspace packages: @ghasi/design-tokens, @ghasi/theme-runtime, @ghasi/design-system, @ghasi/widgets, and other shared clients as applicable. |
2. Process architecture (TR-DSK-###)
| ID | Requirement |
|---|
| TR-DSK-001 | Main process SHALL manage window lifecycle, optional auto-update, and privileged IPC; it SHALL NOT expose Node APIs directly to the renderer. |
| TR-DSK-002 | Preload script SHALL be the only bridge; expose a minimal, typed API (contextBridge) for storage, sync, and secure token operations. |
| TR-DSK-003 | Renderer SHALL be treated as untrusted; assume XSS attempts; no secrets in renderer bundle. |
| TR-DSK-004 | Content-Security-Policy and webPreferences (e.g. contextIsolation: true, nodeIntegration: false) SHALL follow Electron security best practices. |
3. Local persistence and encryption
| ID | Requirement |
|---|
| TR-DSK-010 | Primary local store SHALL be SQLite (e.g. better-sqlite3 in main, or equivalent) with a documented schema version and migrations. |
| TR-DSK-011 | PHI SHALL be encrypted at rest at the file level or via SQLCipher-compatible extension per deployment policy; document key derivation (e.g. OS keychain + tenant-scoped salt). |
| TR-DSK-012 | Local database files SHALL live in user-data paths appropriate per OS; paths SHALL NOT be world-readable. |
| TR-DSK-013 | Tenant isolation: all persisted rows SHALL be scoped by tenantId (and user/session where required); queries SHALL enforce the same. |
4. Outbound queue (outbox)
Aligned with offline-first TECHNICAL_REQUIREMENTS.md §4:
| Field | Purpose |
|---|
id | Local primary key |
clientMutationId | Stable idempotency token (UUID v4 or ULID) |
tenantId | From session; queue entries MUST NOT be replayed under wrong tenant |
operation | Target operation (e.g. POST /v1/patients, POST /v1/orders) |
payload | Serialized JSON |
status | pending / syncing / completed / failed / conflict |
serverResponse | Last snapshot or structured error for UX |
createdAt / updatedAt | Ordering and retention |
| ID | Requirement |
|---|
| TR-DSK-020 | Every queued mutation SHALL include clientMutationId; HTTP SHALL send Idempotency-Key and/or body token per service API docs. |
| TR-DSK-021 | Mutations affecting the same aggregate (e.g. same patientId) SHOULD be sent in causal order per module rules. |
| TR-DSK-022 | Idempotent success responses SHALL be reconciled by stable server ids in local store (same semantics as web). |
5. Sync and HTTP behavior
| ID | Requirement |
|---|
| TR-DSK-030 | Base URL SHALL target Kong public routes per API_PATH_CONVENTIONS.md (/api/, /v1/, /fhir/R4/ as applicable). |
| TR-DSK-031 | Authorization: Bearer JWT; refresh tokens handled per IAM policy; on 401/403 do not infinite-retry sync (TR-OFF-007). |
| TR-DSK-032 | On 429, exponential backoff with jitter (TR-OFF-008). |
| TR-DSK-033 | Structured error bodies SHALL use code for branching (TR-OFF-009); map to user-visible conflict and retry flows. |
| TR-DSK-034 | Correlation: optional X-Correlation-Id on sync requests (TR-OFF-010). |
| TR-DSK-035 | There is no Next.js BFF in the desktop stack; the client MUST attach idempotency headers directly to upstream-appropriate calls (equivalent to BFF forwarding in TR-OFF-005). |
6. Authentication and token storage
| ID | Requirement |
|---|
| TR-DSK-040 | Use OIDC/OAuth2 flows compatible with platform IAM (Keycloak or as deployed); PKCE for public desktop clients where applicable. |
| TR-DSK-041 | Store refresh tokens in OS credential storage (e.g. keytar or Electron safeStorage) where available; avoid plain-text localStorage for long-lived secrets. |
| TR-DSK-042 | Session timeout and logout SHALL clear local sensitive state per policy (tokens, cached PHI as required). |
7. Localization and direction (TR-DSK-070–076)
| ID | Requirement |
|---|
| TR-DSK-070 | Renderer SHALL use react-intl (IntlProvider) with message catalogs aligned to @ghasi/i18n / ehr-web keys; avoid divergent copy for the same clinical concept. |
| TR-DSK-071 | For RTL locales, SHALL apply MUI direction: 'rtl' and Emotion CacheProvider with stylis-plugin-rtl (same pattern as apps/ehr-web); ensure both LTR and RTL are tested. |
| TR-DSK-072 | On locale or direction change, SHALL update document.documentElement.dir and lang without a full app reload where possible; keep theme direction in sync. |
| TR-DSK-073 | Main process native menus, OS dialogs, and window titles SHALL follow a single documented strategy: either app-selected locale (preferred for parity with in-app language) or OS app.getLocale() with explicit product decision; avoid mixed-language chrome without intent. |
| TR-DSK-074 | Dates, times, numbers, and calendars SHALL use dayjs (or equivalent) with the correct locale plugin and REGIONAL_PROFILE.md rules (Gregorian + optional Hijri display where required). |
| TR-DSK-075 | E2E tests SHALL include at least one RTL run: assert dir="rtl" (or equivalent) on the shell and one clinical surface; optional screenshot baseline for visual regression. |
| TR-DSK-076 | Persist locale/direction preference in local settings store; if the server exposes user preferences, reconcile on login per product rules. |
8. Updates and distribution
| ID | Requirement |
|---|
| TR-DSK-050 | Built artifacts SHALL be signed; auto-update SHALL verify signatures before apply. |
| TR-DSK-051 | Document channels (beta/stable) and tenant-specific pinning if required. |
9. Testing obligations
| ID | Requirement |
|---|
| TR-DSK-060 | Unit tests for outbox state machine, idempotency reconciliation, and backoff. |
| TR-DSK-061 | Integration tests against mock Kong or test stack for sync paths. |
| TR-DSK-062 | E2E (e.g. Playwright) for login → offline edit → online sync smoke paths. |
| TR-DSK-063 | E2E or component tests for RTL shell and mixed-direction identifier display (see TR-DSK-075). |
10. AI integration (TR-DSK-080–083)
| ID | Requirement |
|---|
| TR-DSK-080 | Cloud AI calls SHALL use only Kong-exposed orchestrator routes (e.g. /v1/ai/... per AI_PLATFORM.md §7) with Bearer JWT; no third-party model API keys or SDKs embedded in the renderer bundle. |
| TR-DSK-081 | Optional local inference SHALL run in the main process or a dedicated worker subprocess with a typed IPC contract (request/response size limits, timeouts, optional redaction); the renderer MUST NOT load arbitrary native inference stacks directly. |
| TR-DSK-082 | Feature toggles for ai.cloud and ai.local (or equivalent keys) SHALL come from config-resolver / tenant policy and licensing; default deny until explicitly enabled. |
| TR-DSK-083 | When both cloud and local are enabled, document routing policy (e.g. prefer local when offline or for latency; cloud when policy requires centralized audit only) — product-owned; SHALL remain consistent with AI_PLATFORM.md §8. |
11. Appendix: Server-side idempotency (current baseline)
Non-exhaustive; verify in code and module API_DOCS.md:
| Flow | Notes |
|---|
POST /v1/patients (registration) | Idempotency-Key / clientMutationId — reference implementation in offline-first pack. |
POST /v1/orders (orders) | Idempotency-Key supported (see orders). |
Additional modules MUST be verified before marking offline write as complete.