# KodeMed System Architecture > Version 14 (2026.3) | KodeMed GmbH ## Table of Contents 1. [Overview and Components](#1-overview-and-components) 2. [System Architecture](#2-system-architecture) 3. [KodeMed.Server](#3-kodemedserver) 4. [KodeMed.DataServer](#4-kodemeddataserver) 5. [KodeMed.CodingUI](#5-kodemedcodingui) 6. [KodeMed Interface (DLL)](#6-kodemed-interface-dll) 7. [KodeMed.CodingClient](#7-kodemedcodingclient) 8. [Authentication and Security](#8-authentication-and-security) 9. [Data Model](#9-data-model) 10. [Configuration](#10-configuration) 11. [Deployment](#11-deployment) 12. [Cloud & Orchestration](#12-cloud--orchestration) --- ## 1. Overview and Components KodeMed is a clinical case coding platform for Swiss hospitals. It provides interactive coding of ICD-10, CHOP, and DRG classifications in the SpiGes format defined by the Swiss Federal Statistics Office (BFS). ### Platform Components | Component | Technology | Description | |-----------|-----------|-------------| | **KodeMed.Server** | Java / Spring Boot | REST API, WebSocket, session management, persistence, audit | | **KodeMed.DataServer** | Java | Classification catalog API (ICD-10, CHOP, ATC, SwissDRG) | | **KodeMed.GrouperServer** | Java | DRG grouping service (SwissDRG, TARPSY, ST Reha) | | **KodeMed.CodingUI** | React / Vite / TypeScript | Interactive coding web frontend (14+ data blocks) | | **KodeMed.Interface** | C# / .NET 9.0 | COM DLL for HIS integration, WebView2 browser | | **KodeMed.CodingClient** | C# / .NET 9.0 WinExe | Portable system tray app, WebSocket listener, webhook | ### Key Facts - **Output assembly:** `KodeMed.dll` (AssemblyName: KodeMed) - **COM ProgId:** `KodeMed.Coding` - **COM CLSID:** `{F6E73053-8D49-480F-AC06-4E0A4882373F}` - **Target:** .NET 9.0-windows with native COM hosting (`KodeMed.comhost.dll`) - **Languages:** DE (default), FR, IT, EN — auto-detected from system culture or JWT `locale` claim - **Formats:** SpiGes XML (primary), BFS pipe-delimited, BFS XML, Custom (plugin) --- ## 2. System Architecture ### Integration Flows Two primary integration paths exist: ``` Flow A (Direct COM): HIS / .NET App --> KodeMed.dll (COM) --> Server --> CodingUI (WebView2) Flow B (REST / WebSocket via CodingClient): 3rd Party App --> Server (REST/WS) --> CodingClient (WS) --> DLL --> CodingUI (WebView2) ``` ### Component Diagram ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Hospital Network │ │ │ │ ┌──────────────┐ ┌──────────────────┐ ┌────────────────┐ │ │ │ HIS / 3rd │ │ KodeMed.Server │ │ KodeMed │ │ │ │ Party App │────>│ (Spring Boot) │────>│ .DataServer │ │ │ │ │REST │ REST + WebSocket │ │ (Catalogs) │ │ │ └──────────────┘ │ JPA/Hibernate │ └────────────────┘ │ │ │ Audit Trail │ ┌────────────────┐ │ │ └───────┬──────────┘────>│ KodeMed │ │ │ │ │ .GrouperServer │ │ │ │ WebSocket │ (DRG Grouping) │ │ │ │ └────────────────┘ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ User Workstation │ │ │ │ ┌─────────────────┐ ┌─────────────┐ │ │ │ │ │ CodingClient │──>│ KodeMed.dll │ │ │ │ │ │ (System Tray) │ │ (COM DLL) │ │ │ │ │ │ WebSocket │ │ WebView2 │ │ │ │ │ │ Webhook │ │ OAuth2 │ │ │ │ │ │ Language Select │ └──────┬──────┘ │ │ │ │ └─────────────────┘ │ │ │ │ │ ▼ │ │ │ │ ┌──────────────────┐ │ │ │ │ │ KodeMed.CodingUI │ │ │ │ │ │ (React/Vite) │ │ │ │ │ │ 14+ data blocks │ │ │ │ │ └──────────────────┘ │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │ OIDC Provider │ OAuth2 / OIDC Identity Provider (external, │ │ │ (Keycloak, │ not bundled — managed by hospital IT) │ │ │ Azure AD, …)│ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 3. KodeMed.Server **Technology:** Java, Spring Boot ### Responsibilities - REST API for session CRUD, configuration, audit, instances, layouts, and health - WebSocket endpoints (`/ws/dll` for CodingClient, `/ws/app` for 3rd-party apps) - Session persistence via JPA/Hibernate (H2 for development, PostgreSQL for production) - Audit trail — logs all session events, user actions, and state transitions - Layout management — save, load, and share custom UI layouts across users - OAuth2 token validation (JWT) - Multi-tenant support via realms - Grouper integration via GrouperServer (SwissDRG, TARPSY, ST Reha) - Post-coding webhook configuration (centralized env vars) ### REST API Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | `GET` | `/api/v1/config` | Server configuration (OAuth2, UI URLs) | | `POST` | `/api/v1/coding/session` | Create coding session | | `GET` | `/api/v1/coding/session/{id}` | Get session metadata | | `GET` | `/api/v1/coding/session/{id}/data` | Get session input data | | `POST` | `/api/v1/coding/session/{id}/complete` | Complete session with result | | `POST` | `/api/v1/coding/session/{id}/cancel` | Cancel session | | `GET` | `/api/v1/coding/session/{id}/result` | Get result metadata | | `GET` | `/api/v1/coding/session/{id}/result/data` | Get result data (raw XML/JSON) | | `GET` | `/api/v1/coding/active` | Check if user has active session | | `GET` | `/api/v1/coding/sessions` | Recent sessions | | `GET` | `/api/v1/coding/history` | Session history for current user | | `GET` | `/api/v1/coding/clients` | List connected CodingClients | | `GET` | `/api/v1/coding/health` | Health check | ### WebSocket Endpoints | Endpoint | Client | Purpose | |----------|--------|---------| | `/ws/dll` | CodingClient | Receives `CODING_SESSION_LAUNCH`, sends results | | `/ws/app` | 3rd-party apps | Send sessions, receive real-time events | ### Persistence - **JPA/Hibernate** with H2 (embedded, development) or PostgreSQL (production) - Session entities, audit events, user layouts stored in relational DB - No Redis dependency — all persistence is database-backed --- ## 4. KodeMed.DataServer **Technology:** Java, REST API ### Responsibilities - Classification data API for code lookup and validation - ICD-10-GM codes with descriptions (German, French, Italian) - CHOP procedure codes - ATC medication codes - SwissDRG grouper catalog - Full-text search with autocomplete - Versioned catalogs (yearly updates from BFS) - REST API with HTTP caching for performance --- ## 5. KodeMed.CodingUI **Technology:** React 18, TypeScript, Vite ### Architecture - **State management:** Zustand (lightweight, no Redux boilerplate) - **Layout system:** react-grid-layout (draggable, resizable blocks) - **14+ data blocks:** Administrative data, diagnoses (ICD-10), procedures (CHOP), medications (ATC), grouper results, cost data, invoices, patient movements, cost centers (Standort/Unternehmen), and more - **Code search:** Autocomplete search for ICD-10, CHOP, ATC codes - **Undo history:** Visual undo bar for step-through of all changes - **i18n:** Multi-language support (DE/FR/IT/EN) ### Features | Feature | Description | |---------|-------------| | Editable code tables | ICD-10, CHOP, ATC with inline editing | | SpiGes attribute editing | Per-row attribute editing for all entities | | Real-time grouper results | SwissDRG grouping with cost weights, PCCL | | Drag-and-drop layouts | react-grid-layout, save/load/share | | Undo history bar | Visual step-through of all coding actions | | Code search | Full-text autocomplete for classification codes | | WebSocket live updates | Real-time session state synchronization | | Keyboard navigation | Full keyboard support for efficient coding | | Dark/light theme | User-selectable theme | | Export/import | Session data export and import | ### Embedding Modes 1. **WebView2 (DLL):** Embedded browser in the .NET DLL — primary mode for HIS integration 2. **Standalone browser:** Direct browser access via OAuth2/OIDC — for browser-based workflows --- ## 6. KodeMed Interface (DLL) **Technology:** C# / .NET 9.0 (net9.0-windows) ### Assembly Details - **Output:** `KodeMed.dll` (AssemblyName: KodeMed) - **COM ProgId:** `KodeMed.Coding` - **COM Host:** `KodeMed.comhost.dll` - **COM CLSID:** `{F6E73053-8D49-480F-AC06-4E0A4882373F}` ### API Methods #### COM Interface (IKodeMed) | Method | Description | |--------|-------------| | `SendConfig(string)` | Load XML or JSON configuration (auto-detected) | | `DoCoding(string, FormatType)` | Headless processing — no browser UI | | `DoCodingWithFormat(string, FormatType)` | Full UI flow: OAuth2 auth + WebView2 browser | | `GetResults()` | Get results as XML or JSON | | `GetConfigFromServer()` | Fetch config from server API | | `SetConfigFromServer()` | Fetch + apply server config | | `DisposeClient()` | Release all resources | #### Extended Methods (Coding class) | Method | Description | |--------|-------------| | `GetResultData()` | Get modified SpiGes XML | | `GetOriginalData()` | Get original (unmodified) data | | `ConnectToServer(...)` | Connect WebSocket to server | | `Logout()` | Clear OAuth2 tokens and disconnect | | `GetAccessToken()` | Get current OAuth2 access token | ### Token Flow 1. DLL performs OAuth2 Authorization Code + PKCE flow (embedded browser) 2. Injects token into WebView2 via `window.KODEMED_TOKEN` + `localStorage` 3. Auto-refreshes access token 120 seconds before expiry 4. Sends `SESSION_CLOSE` WebSocket message when the DLL closes the session ### Events - `StatusChanged` — Processing status transitions - `Error` — Error messages - `Progress` — Progress percentage (0-100%) - `AuthenticationStateChanged` — Sign in/out, token refresh - `ConnectionStateChanged` — WebSocket connection state - `CodingSessionCompleted` — Session result with action and data --- ## 7. KodeMed.CodingClient **Technology:** C# / .NET 9.0, WinExe, self-contained ### Overview Portable system tray application that bridges 3rd-party apps to the DLL: ``` 3rd Party App --> Server (REST) --> CodingClient (WebSocket) --> DLL --> WebView2 UI --> Post-Coding Webhook ``` ### Features | Feature | Description | |---------|-------------| | **WebSocket listener** | Receives `CODING_SESSION_LAUNCH` from server | | **Post-coding webhook** | Fire-and-forget HTTP POST to HIS after each session | | **Language selector** | Tray menu for DE/FR/IT/EN with fallback chain | | **OAuth2 sign in/out** | Tray menu authentication | | **Open from file** | Load SpiGes/BFS/Custom files from local disk | | **Auto-start** | Windows login auto-start via `HKCU\...\Run` | | **Single instance** | Global mutex prevents duplicate instances | | **Auto-setup** | First-run: sets env vars, registers COM DLL | | **Server settings** | Configurable server URL via tray menu or `--server-url` CLI | ### Tray Menu ``` KodeMed Coding Client v2026.3 (username) ───────────────────────────── Sign In / Sign Out (OAuth2) ───────────────────────────── Open Session from File... > SpiGes (XML) BFS (DAT/XML) Custom... ───────────────────────────── Language > Deutsch Français Italiano English ───────────────────────────── Server Settings... Auto-start on Login ✓ ───────────────────────────── Setup / Repair ───────────────────────────── Exit ``` ### Citrix / VDI Compatibility - Self-contained EXE: no .NET runtime dependency - User-level only: all registry writes to HKCU, env vars to User scope - No admin rights required - COM registration via `HKCU\Software\Classes` (no admin) - Portable: copy folder anywhere, run EXE - Non-persistent VDI: re-setup runs silently on each login ### Configuration The CodingClient only needs `serverUrl`. All other settings are fetched from `/api/v1/config`: ```json { "serverUrl": "https://kodemed.example.com", "language": "de" } ``` - `--server-url` CLI argument overwrites local config (allows multiple instances for test/prod) - Language can be set via tray menu or `language` field in config JSON ### Environment Variables (auto-set) | Variable | Description | |----------|-------------| | `KODEMED_HOME` | Working directory | | `KODEMED_DLL_PATH` | Full path to KodeMed.dll | | `KODEMED_EXE_PATH` | Full path to CodingClient.exe | --- ## 8. Authentication and Security ### OAuth2 / OIDC - **Flow:** Authorization Code + PKCE - **Compatible providers:** Keycloak, Azure AD / Entra ID, Auth0, Okta - **Clients:** `kodemed-dll` (DLL), `kodemed-ui` (browser) - **Realm:** Configurable (default: `kodemed`) - **Token auto-refresh:** Access token refreshed 120s before expiry; retry every 15s on failure ### Group-Based Authorization | Role | Description | |------|-------------| | `kodemed-admin` | Full access: edit, manage users, approve sessions | | `kodemed-coder` | Code cases, edit diagnoses/procedures | | `kodemed-approve` | Approve coding sessions | | `kodemed-viewer` | Read-only access | Roles are extracted from JWT in this order: 1. `realm_access.roles` (Keycloak, and compatible providers) 2. `resource_access..roles` (Keycloak client-specific roles) 3. Standard OIDC `roles` claim 4. Auth0-style custom namespace (`https://kodemed.com/roles`) > **Note:** Full role enforcement in the UI is planned. Currently `canEdit()` checks authentication only. ### Security Measures | Measure | Description | |---------|-------------| | HTTPS | TLS 1.2+ everywhere | | WSS | Encrypted WebSocket connections | | JWT validation | Server validates all tokens; WebSocket handshake **requires** valid JWT | | CORS | Explicit origins required — wildcard `*` **blocked at startup** (`CORS_ALLOWED_ORIGINS`) | | Encryption at rest (server) | AES-256-GCM for patient data fields in database (`KODEMED_ENCRYPTION_KEY`) | | Encryption at rest (browser) | AES-256-GCM for sensitive data in sessionStorage (GDPR Article 32) | | Medical data redaction | 25+ patterns redacted from logs when `AllowMedicalDataInLogs=false` (GDPR Article 5.1.f) | | Session expiry | Configurable timeout (default 60 min) | | Audit logging | All session events and user actions | | No secrets in config | Client config contains no credentials | | Path traversal protection | File operations validate paths against traversal attacks | | COM visibility | Sensitive authentication data hidden from COM clients | ### WebSocket Authentication WebSocket connections **require** JWT Bearer authentication. The server rejects unauthenticated connections. Two methods are supported: 1. **Authorization header** (preferred): `Authorization: Bearer ` 2. **Query parameter** (fallback): `?token=` — for WebSocket clients that cannot set headers When security is disabled (`KODEMED_AUTH_ENABLED=false`), all connections are allowed. ### Encryption at Rest Patient data is encrypted using AES-256-GCM (GDPR Article 32, nDSG Article 21). #### Server-Side Encryption (Database) Encrypted database fields when `KODEMED_ENCRYPTION_KEY` is set: | Entity | Fields | |--------|--------| | CodingSessionEntity | `originalData`, `resultData` | | CaseChangeEntity | `oldValue`, `newValue`, `details` | | UndoHistoryEntity | `itemData`, `previousValue` | - **Key:** Set `KODEMED_ENCRYPTION_KEY` (Base64-encoded, 32 bytes AES-256 key) - **Generate:** `openssl rand -base64 32` - **Disabled:** When key is empty, data is stored unencrypted (passthrough mode) - **Legacy:** Unencrypted data (without `ENC:` prefix) is read transparently — no migration needed #### Browser-Side Encryption (SessionStorage) The Coding UI encrypts sensitive medical data in sessionStorage using Web Crypto API (GDPR Article 32): - **Algorithm:** AES-GCM 256-bit with unique IV per encryption - **Key lifecycle:** Generated on page load, cleared on logout, lost on refresh (by design for security) - **Encrypted data:** Case data, enterprise info, session state (partitioned via Zustand persist middleware) - **Automatic:** No configuration needed — encryption is always active when browser supports Web Crypto API - **Key cleared on:** User logout, browser close, page refresh **Security properties:** - Non-extractable encryption key (cannot be exported) - Session-bound key (not persisted to disk) - Automatic key regeneration on each page load - Graceful degradation: If decryption fails (key lost after refresh), session starts fresh This ensures medical data stored temporarily in browser sessionStorage is encrypted at rest, preventing unauthorized access if an attacker gains access to browser storage. ### GDPR / nDSG Compliance - **Encryption at rest (server):** Patient data encrypted with AES-256-GCM when `KODEMED_ENCRYPTION_KEY` is configured - **Encryption at rest (browser):** SessionStorage encrypted with AES-GCM Web Crypto API (always active) - **Medical data redaction in logs:** 25+ medical data patterns automatically redacted from DLL logs (GDPR Article 5.1.f) - Configurable via `AllowMedicalDataInLogs` flag (default: `true` for debugging, `false` for production) - Patterns: patient ID, insurance number, AHV, birthdate, diagnosis codes, procedure codes, insurance provider, etc. - All sensitive tokens (JWT, Bearer) are always redacted regardless of flag - **Data minimization defaults:** Webhook `includeResultData` and `includeGrouperResults` default to `false` (opt-in) - **SSRF protection:** Webhook target URLs validated against private/internal ranges - **Auth credentials local only:** Webhook auth tokens configured locally per CodingClient, never transmitted from server - **Audit trail:** All data access events logged with encrypted sensitive fields --- ## 9. Data Model ### SpiGes XML Structure Primary data format defined by the Swiss Federal Statistics Office (BFS), version 1.5. ``` Unternehmen (Enterprise) ├── ent_id, version ├── Standort[] (Sites) │ ├── burnr (BUR number) │ ├── Fall[] (Cases) │ │ ├── Administratives │ │ │ (demographics, dates, insurance, canton...) │ │ ├── Diagnose[] │ │ │ (ICD-10 codes, POA indicator) │ │ ├── Behandlung[] │ │ │ (CHOP procedures) │ │ ├── Medikament[] │ │ │ (ATC medications) │ │ ├── Rechnung[] (invoices) │ │ └── Patientenbewegung[] │ └── KostentraegerStandort[] └── KostentraegerUnternehmen[] ``` ### Key Entities | Entity | Key Fields | |--------|------------| | **Administratives** | `abc_fall`, `alter`, `geschlecht`, `eintrittsdatum`, `austrittsdatum` | | **Diagnose** | `diagnose_kode` (ICD-10), `diagnose_poa` | | **Behandlung** | `behandlung_chop`, `behandlung_beginn` | | **Medikament** | `medi_atc`, `medi_dosis`, `medi_einheit` | | **GrouperResult** | DRG code, MDC, PCCL, cost weight, LOS | ### Supported Formats | Format | FormatType | Description | |--------|-----------|-------------| | SpiGes XML | `0` (SpiGes) | Swiss BFS SpiGes format v1.5 (default) | | BFS pipe-delimited | `1` (BFS) | Legacy `.dat` format (auto-detected: starts with `MX\|`) | | BFS XML | `1` (BFS) | BFS XML, transformed to SpiGes via XSLT | | Custom | `2` (Custom) | Proprietary formats via plugin system | --- ## 10. Configuration ### DLL / Client Configuration (XML or JSON) Configuration is provided to the DLL via `SendConfig()`. Format is auto-detected. #### XML Example ```xml https://kodemed.example.com https://kodemed.example.com https://sso.example.com/auth kodemed kodemed-dll de XML true ``` #### JSON Example ```json { "serverUrl": "https://kodemed.example.com", "oauth2Url": "https://sso.example.com/auth", "oauth2Realm": "kodemed", "oauth2ClientId": "kodemed-dll", "language": "de", "outputFormat": "JSON" } ``` ### Key Settings | Setting | Default | Description | |---------|---------|-------------| | `serverUrl` | `http://localhost:8080` | Main server URL | | `dataServerUrl` | `http://localhost:8081` | Data catalog server | | `grouperServerUrl` | `http://localhost:8082` | DRG grouping server | | `codingUIUrl` | `http://localhost:3000` | React coding UI URL | | `oauth2Url` | | OIDC server URL | | `oauth2Realm` | `kodemed` | OAuth2 realm/tenant | | `oauth2ClientId` | `kodemed-dll` | OAuth2 client ID | | `language` | auto | UI language: `de`, `fr`, `it`, `en` | | `outputFormat` | `XML` | Results format: `XML` or `JSON` | | `validateSchema` | `true` | XSD validation | | `timeout` | `300` | HTTP timeout (seconds) | | `sessionExpiryMinutes` | `60` | Session expiry | | `offlineTimeoutMinutes` | `5` | Offline heartbeat timeout | | `zoomFactor` | auto | Coding UI zoom (0.25 - 5.0) | ### CodingClient Configuration The CodingClient only needs `serverUrl` — rest fetched from `/api/v1/config`: ```json { "serverUrl": "https://kodemed.example.com", "language": "de", "webSocketAutoReconnect": true, "webSocketReconnectIntervalSeconds": 60, "webSocketHeartbeatIntervalSeconds": 30 } ``` ### Server Environment Variables | Variable | Default | Description | |----------|---------|-------------| | **Security** | | | | `KODEMED_AUTH_ENABLED` | `true` | Enable OAuth2/JWT authentication | | `OIDC_ISSUER_URI` | — | OAuth2 issuer URI (OIDC realm/tenant) | | `OIDC_JWK_URI` | — | JSON Web Key Set URI for token validation | | `CORS_ALLOWED_ORIGINS` | (empty, **required**) | Comma-separated allowed origins — wildcard `*` **blocked** | | `WEBSOCKET_ALLOWED_ORIGINS` | (empty) | WebSocket CORS origins (defaults to CORS_ALLOWED_ORIGINS) | | `KODEMED_ENCRYPTION_KEY` | (empty) | Base64-encoded AES-256 key (32 bytes). Generate: `openssl rand -base64 32` | | **Server** | | | | `SERVER_PORT` | `8080` | Server HTTP port | | `SPRING_DATASOURCE_URL` | H2 in-memory | Database JDBC URL | | `SPRING_DATASOURCE_DRIVER_CLASS_NAME` | H2 | Database driver | | **Instance Management** | | | | `INSTANCE_DISCONNECT_TIMEOUT` | `60` | Minutes before disconnected instance is terminated | | `HEARTBEAT_INTERVAL` | `30` | Heartbeat interval in seconds | | `RECONNECT_INTERVAL` | `60` | DLL reconnect attempt interval in seconds | | **Webhook** | | | | `KODEMED_HOOK_ENABLED` | `false` | Enable post-coding webhook | | `KODEMED_HOOK_URL` | (empty) | Webhook target URL | | `KODEMED_HOOK_AUTH_TYPE` | `none` | Webhook auth: `none`, `bearer`, `header` | | `KODEMED_HOOK_TIMEOUT_SECONDS` | `30` | Webhook HTTP timeout | | `KODEMED_HOOK_RETRY_COUNT` | `3` | Webhook retry count | | `KODEMED_HOOK_INCLUDE_RESULT_DATA` | `false` | Include result data (opt-in) | | `KODEMED_HOOK_INCLUDE_ORIGINAL_DATA` | `false` | Include original data (opt-in) | | `KODEMED_HOOK_INCLUDE_GROUPER_RESULTS` | `false` | Include grouper results (opt-in) | | `KODEMED_HOOK_EVENTS` | `applied` | Events to fire on (comma-separated) | ### Advanced Configuration #### Public URL Overrides For production deployments behind proxies, load balancers, or with custom DNS: | Variable | Default | Description | |----------|---------|-------------| | `KODEMED_PUBLIC_SERVER_URL` | (auto-detected) | Override server URL returned by `/api/v1/config` | | `KODEMED_PUBLIC_DATASERVER_URL` | (auto-detected) | Override DataServer URL returned by `/api/v1/config` | | `KODEMED_PUBLIC_WEBSOCKET_URL` | (auto-detected) | Override WebSocket URL (ws:// or wss://) | | `KODEMED_PUBLIC_UI_URL` | (auto-detected) | Override Coding UI URL for session redirects | | `KODEMED_UI_URL` | (empty) | Internal UI URL for session creation (fallback if PUBLIC not set) | **When to use:** When KodeMed is deployed behind a reverse proxy (Nginx, Apache, Traefik) with TLS termination, or when the server needs to advertise different URLs than its bind address. **Example:** ```bash # Behind proxy at https://kodemed.hospital.ch KODEMED_PUBLIC_SERVER_URL=https://kodemed.hospital.ch KODEMED_PUBLIC_DATASERVER_URL=https://kodemed.hospital.ch/data KODEMED_PUBLIC_WEBSOCKET_URL=wss://kodemed.hospital.ch/ws KODEMED_PUBLIC_UI_URL=https://kodemed.hospital.ch ``` **Auto-detection:** If not set, URLs are derived from request headers (`X-Forwarded-Proto`, `X-Forwarded-Host`, `Host`). #### Database Configuration | Variable | Default | Description | |----------|---------|-------------| | `TABLE_PREFIX` | `km_app_` | Prefix for all database tables (useful for shared databases) | | `SPRING_DATASOURCE_URL` | `jdbc:h2:mem:kodemed` | Database JDBC URL | | `SPRING_DATASOURCE_USERNAME` | `sa` | Database username | | `SPRING_DATASOURCE_PASSWORD` | (empty) | Database password | | `SPRING_DATASOURCE_DRIVER_CLASS_NAME` | `org.h2.Driver` | Database driver class | | `SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE` | `15` | Max database connections | | `SPRING_DATASOURCE_HIKARI_MINIMUM_IDLE` | `5` | Minimum idle connections | | `SPRING_DATASOURCE_HIKARI_IDLE_TIMEOUT` | `300000` | Idle timeout (ms) | | `SPRING_DATASOURCE_HIKARI_CONNECTION_TIMEOUT` | `20000` | Connection timeout (ms) | | `SPRING_JPA_HIBERNATE_DDL_AUTO` | `update` | Hibernate DDL mode: `none`, `validate`, `update`, `create`, `create-drop` | | `H2_CONSOLE_ENABLED` | `false` | Enable H2 web console at `/h2-console` (dev only) | **Table prefix examples:** - `TABLE_PREFIX=` (empty) → `coding_session_entity` - `TABLE_PREFIX=km_app_` → `km_app_coding_session_entity` - `TABLE_PREFIX=kodemed_` → `kodemed_coding_session_entity` **Production database example (PostgreSQL):** ```bash SPRING_DATASOURCE_URL=jdbc:postgresql://db.hospital.ch:5432/kodemed SPRING_DATASOURCE_USERNAME=kodemed_app SPRING_DATASOURCE_PASSWORD=secret SPRING_DATASOURCE_DRIVER_CLASS_NAME=org.postgresql.Driver TABLE_PREFIX=km_ ``` #### Persistence and Audit Trail Controls session history storage and change tracking for compliance: | Variable | Default | Description | |----------|---------|-------------| | `KODEMED_PERSISTENCE_ENABLED` | `true` | Enable persistent storage of sessions and changes | | `KODEMED_AUDIT_ENABLED` | `true` | Enable audit trail for all changes (requires persistence) | | `KODEMED_RETENTION_DAYS` | `90` | Retention period for completed sessions (0 = keep forever) | | `KODEMED_STORE_ORIGINAL` | `true` | Store original case data along with modifications | | `KODEMED_PERSISTENCE_SESSION_EXPIRY_MINUTES` | `60` | Session expiry timeout (for session cleanup) | | `KODEMED_PERSISTENCE_OFFLINE_TIMEOUT_MINUTES` | `15` | Mark sessions as expired if offline for N minutes | **When to enable:** - **Persistence:** Required for session history, last-session views, and audit compliance - **Audit:** Required for GDPR/Swiss law compliance, change tracking, and regulatory reporting **When to disable:** - **Persistence OFF:** For ephemeral/demo environments where sessions don't need to be stored - **Audit OFF:** If change tracking is not required (reduces database writes) **Compliance notes:** - Audit trail records: user ID, timestamp, case ID, change type, old/new values, IP address - Retention period applies to completed sessions only (active sessions never expire by retention policy) - Original data storage allows reverting to pre-coded state **Example (Swiss hospital - 10 year retention):** ```bash KODEMED_PERSISTENCE_ENABLED=true KODEMED_AUDIT_ENABLED=true KODEMED_RETENTION_DAYS=3650 # 10 years KODEMED_STORE_ORIGINAL=true ``` #### OAuth2 Configuration OAuth2/OIDC settings returned to DLL clients via `/api/v1/config`: | Variable | Default | Description | |----------|---------|-------------| | `KODEMED_OAUTH2_REALM` | `kodemed` | OAuth2 realm/tenant name | | `KODEMED_OAUTH2_CLIENT_ID` | `kodemed-dll` | OAuth2 client ID for DLL authentication | | `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI` | — | OAuth2 issuer URI (OIDC realm/tenant) | | `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI` | — | JSON Web Key Set URI for token validation | **OIDC example (Keycloak-style URLs):** ```bash KODEMED_OAUTH2_REALM=kodemed KODEMED_OAUTH2_CLIENT_ID=kodemed-dll SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=https://sso.hospital.ch/auth/realms/kodemed SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=https://sso.hospital.ch/auth/realms/kodemed/protocol/openid-connect/certs ``` **Azure AD example:** ```bash KODEMED_OAUTH2_REALM=kodemed KODEMED_OAUTH2_CLIENT_ID=12345-6789-abcd-ef01 SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI=https://login.microsoftonline.com/{tenant-id}/v2.0 ``` #### Swagger/OpenAPI Documentation Configuration for Swagger UI and API documentation: | Variable | Default | Description | |----------|---------|-------------| | `SWAGGER_SERVER_URL` | (auto-detected) | Server URL shown in Swagger UI (for proxy deployments) | | `SWAGGER_OAUTH2_CLIENT_ID` | `kodemed-server` | OAuth2 client ID for Swagger UI authentication | **Example (behind proxy):** ```bash SWAGGER_SERVER_URL=https://kodemed.hospital.ch SWAGGER_OAUTH2_CLIENT_ID=kodemed-swagger-ui ``` **Access Swagger UI:** `https://kodemed.hospital.ch/swagger-ui/index.html` #### Server Performance Tuning | Variable | Default | Description | |----------|---------|-------------| | `SERVER_CONNECTION_TIMEOUT` | `20000` | Tomcat connection timeout (ms) | | `SERVER_MAX_CONNECTIONS` | `8192` | Max concurrent connections | | `ASYNC_REQUEST_TIMEOUT` | `60000` | Async request timeout (ms) | | `CONTEXT_PATH` | (empty) | Server context path (e.g., `/kodemed`) | **High-load example:** ```bash SERVER_MAX_CONNECTIONS=16384 ASYNC_REQUEST_TIMEOUT=120000 ``` #### Logging Configuration | Variable | Default | Description | |----------|---------|-------------| | `LOGGING_LEVEL_ROOT` | `INFO` | Root logger level | | `LOGGING_LEVEL_COM_MIERESIT_KODEMED` | `INFO` | KodeMed logger level | | `LOGGING_LEVEL_SECURITY` | `WARN` | Spring Security logger level | | `LOGGING_LEVEL_OAUTH2` | `WARN` | OAuth2 logger level | **Debug OAuth2 issues:** ```bash LOGGING_LEVEL_OAUTH2=DEBUG LOGGING_LEVEL_SECURITY=DEBUG ``` #### DataServer Configuration KodeMed.DataServer port used for URL derivation when accessing via localhost: | Variable | Default | Description | |----------|---------|-------------| | `KODEMED_DATASERVER_PORT` | `8081` | DataServer port for auto-detected URLs | #### Management Endpoints (Actuator) | Variable | Default | Description | |----------|---------|-------------| | `MANAGEMENT_ENDPOINTS` | `health,info,metrics,prometheus` | Enabled actuator endpoints | **Access:** `http://localhost:8080/actuator/health` --- ## 11. Deployment ### Server (Docker) ```yaml # Docker Compose — Starts Server, DataServer, GrouperServer, CodingUI # OIDC Provider (external IdP, not bundled) services: kodemed-server: image: kodemed/kodemed-server:latest ports: ["8080:8080"] environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/kodemed kodemed-dataserver: image: kodemed/kodemed-dataserver:latest ports: ["8081:8081"] kodemed-grouperserver: image: kodemed/kodemed-grouper-server:latest ports: ["8082:8082"] kodemed-ui: image: kodemed/kodemed-ui:latest ports: ["3000:3000"] db: image: postgres:16 environment: POSTGRES_DB: kodemed ``` ### Linux Server Installer - Automated deployment script for on-premise Linux servers - Installs Docker containers, configures reverse proxy, sets up TLS ### Client (Windows) | Method | Description | |--------|-------------| | **MSI Installer (recommended)** | `KodeMed.msi` — per-user or per-machine, silent GPO/SCCM/Intune, COM registration included | | **Manual / Xcopy** | Copy DLL + deps, manual COM and env var setup | ### Client Deployment Notes - MSI interactive: `msiexec /i KodeMed.msi` — prompts for server URL, language, autostart - MSI silent (GPO/SCCM): `msiexec /i KodeMed.msi /quiet /norestart SERVERURL="https://..." LANGUAGE="de"` - MSI per-machine (Citrix): `msiexec /i KodeMed.msi /quiet ALLUSERS=1 SERVERURL="https://..."` - Citrix/VDI: per-machine install recommended, publish as RemoteApp or include in golden image - `kodemed-client-config.json` for config overrides (written by MSI installer) - Upgrades: MajorUpgrade auto-removes previous version, preserves config if SERVERURL not re-specified --- ## 12. Cloud & Orchestration ### Deployment Topology All KodeMed server components are delivered as **OCI-compliant Docker images**, enabling deployment on any container orchestration platform. ``` +---------- Harbor Registry ----------+ +------ Kubernetes / OpenShift Cluster ------+ | kodemed/kodemed-server:latest | pull | Ingress / Route | | kodemed/kodemed-dataserver:latest |------->| +-- kodemed-server [HPA] :8080 | | kodemed/kodemed-grouper-server:latest| | +-- kodemed-dataserver [HPA] :8081 | | kodemed/kodemed-ui:latest | | +-- kodemed-grouper :8082 | | | | +-- kodemed-ui :3000 | | Trivy + Cosign/Notary | | PostgreSQL (StatefulSet / external) | +-------------------------------------+ +--------------------------------------------+ ``` ### Kubernetes / OpenShift | Feature | Description | |---------|-------------| | **Helm charts** | Deploy all services with `helm install kodemed ./charts/kodemed` | | **Plain manifests** | Alternative K8s YAML manifests for manual deployment | | **HPA** | Horizontal Pod Autoscaling for Server & DataServer based on CPU/memory | | **Health checks** | `/actuator/health` for liveness and readiness probes | | **OpenShift** | Native support via Routes, DeploymentConfigs & integrated registry | | **Namespaces** | Recommended: separate namespace per environment (dev/staging/prod) | ### Harbor Registry | Feature | Description | |---------|-------------| | **Private registry** | Centralized image storage and distribution | | **Vulnerability scanning** | Trivy scans on every push — blocks deployment of vulnerable images | | **Image signing** | Cosign / Notary content trust for supply chain security | | **Replication** | Multi-site replication policies for distributed deployments | | **RBAC** | Role-based access control per project (dev, staging, prod) | | **Retention policies** | Automatic cleanup of old image tags | ### Service Ports | Service | Port | HPA | Health Endpoint | |---------|------|-----|-----------------| | kodemed-server | 8080 | Yes | `/actuator/health` | | kodemed-dataserver | 8081 | Yes | `/actuator/health` | | kodemed-grouper | 8082 | No | `/actuator/health` | | kodemed-ui | 3000 | No | `/` | | PostgreSQL | 5432 | No | — | --- *KodeMed System Architecture v14 (2026.2) — Updated 2026-02-24 (Cloud & Orchestration)*