04

Izvršno okruženje platforme

Sažetak

Aplikacija je modularni monolit: domene su odvojene granicama modula i lint pravilima, a ne mrežom, pa klinička transakcija ostaje jedna transakcija baze. Svaki zahtjev prolazi fiksni lanac middlewarea, a svaki AI zahtjev dodatno prolazi provjere usklađenosti, privole, budžeta, revizije i izlaza prije poziva modela. HTTP površinu opisuje generirani manifest koji CI provjerava pri svakoj promjeni.

Tehnička specifikacija vodi se na engleskom jeziku; naslovi i sažeci poglavlja prevedeni su na hrvatski.

Modular monolith

ADR 0010 keeps Faurenis a single deployable application with internal boundaries. A dental visit touches Scribe, Perio, Copilot, artifacts, facts and audit at once; splitting those into services would turn one atomic transaction into a distributed one, with no corresponding gain at this scale. Services are split out only for a different runtime (GPU, Chromium) or a hard isolation boundary (no database credentials).

  • Client
    Location
    src/ — React 19, Vite, Zustand
    Rule
    May not import server code
  • Shared
    Location
    shared/ — protocols, deterministic clinical engines, policies
    Rule
    Portable: no Express, no React, no Node-only infrastructure
  • Server
    Location
    server/ — routes, services, schema, middleware, integrations
    Rule
    May not import client code; database access only through scoped helpers
  • Satellites
    Location
    services/* — renderer, imaging, LLM worker
    Rule
    Own image, own service account; no in-process domain logic

Request pipeline

Global middleware runs on every request in this order:

  1. Request id — a correlation id carried through logs, traces and audit rows.
  2. CORS for the native shells, then a per-request CSP nonce and Helmet security headers.
  3. Cookie parsing and retirement of legacy cookies.
  4. Structured HTTP logging with PII masking.
  5. Body limits: 1 MB by default, raised only for document and imaging upload routes.
  6. OpenTelemetry tracing and a request timeout.
  7. Route-group middleware: authentication, a rate limiter per route class, no-store caching for anything carrying clinical data.

The AI gate chain

Every route under /api/ai adds a second chain between authentication and the handler. Each link can refuse the request; none of them can be skipped by a handler.

  • 1
    Gate
    Clinic AI rate limiter
    Refuses when
    A clinic exceeds its AI request rate
  • 2
    Gate
    Compliance guard
    Refuses when
    The clinic’s compliance state does not permit AI processing (HTTP 451)
  • 3
    Gate
    Consent attestation
    Refuses when
    AI-processing consent is missing, stale against the current form version, or uncertain — fails closed (HTTP 451)
  • 4
    Gate
    Plan budget guard
    Refuses when
    The plan’s token allowance is exhausted
  • 5
    Gate
    AI cost budget guard
    Refuses when
    The clinic’s monthly USD cap is reached (HTTP 429); overrides may only lower the cap
  • 6
    Gate
    Overage debit guard
    Refuses when
    Metered overage cannot be debited
  • 7
    Gate
    Audit logger
    Refuses when
    Records the request to the audit chain
  • 8
    Gate
    Output PII scanner
    Refuses when
    Inspects the model’s reply for identifiers before it leaves the server

The route manifest

architecture/manifests/route-protocol-manifest.json is generated from the router itself. For every registration it records method, path, audience, authentication, clinic scope, middleware, body limit, status codes, content types and streaming mode. CI regenerates it and fails on any difference, so an API change is always a visible, reviewed diff.

HTTP registrations
700+
Server-sent event streams
8
WebSocket paths
4
Server service modules
970+
TypeScript files under server/services

Identity and sessions

  • Sign-in: Google OAuth with server-side code exchange, Sign in with Apple, magic link, password, and a one-time token hand-off for the native apps.
  • Access tokens are JWTs signed with PS256 by a Cloud KMS key; the public keys are published as JWKS.
  • Refresh tokens are stored hashed, single-use, with a 14-day lifetime. Presenting a consumed token revokes its entire family.
  • TOTP multi-factor authentication with backup codes; MFA secrets are encrypted with AES-256-GCM.
  • Idle-session policy shared between client and server, a revocation registry checked by REST and WebSocket paths alike.
  • Identity (auth_users) is separate from authorization (users, roles, clinic membership).

Client architecture

The client is a React 19 single-page application built by Vite, with Zustand stores, route-level code splitting and a performance budget enforced by a bundle-size ratchet. The public site is prerendered to static HTML in both locales. Clinical working state that must survive a crash or a dropped connection is kept in an encrypted local recovery store, which is always subordinate to the server (REC-01) and wiped on sign-out.

  • Microphone
    Approach
    One ownership registry for the whole app; spoken output observes it and yields to clinical capture
  • 3D and imaging
    Approach
    three.js through React Three Fiber; Cornerstone for DICOM; pixels are never rewritten
  • Internationalisation
    Approach
    i18next with 17,000+ keys per language and CI-enforced parity; locale bundles split for the public site
  • Offline and skew
    Approach
    Old clients remain valid against new servers across a flag boundary; destructive assumptions require a minimum-version gate (REC-10)
SOURCE OF TRUTH
  • server.ts
  • server/middleware/
  • architecture/manifests/route-protocol-manifest.json
  • docs/adr/0010-modular-monolith-boundaries.md
  • docs/adr/0011-http-contract-ownership.md
Svaki broj u ovoj specifikaciji generira se iz repozitorija i provjerava u CI-ju.