pediatric-ai-scribe-v3/docs/architecture.md
Daniel b97c5f146b v6.1: Turnstile bot protection, LiteLLM provider, PPTX tables, audio backup fixes, docs
- Add Cloudflare Turnstile to login, register, and password reset forms
- Switch AI provider to LiteLLM, transcription to OpenAI Whisper
- Change domain to scribe.pedshub.com
- Fix PPTX export: add tables, bold/italic, numbered lists, code blocks, blockquotes
- Fix announcement banner close button (CSP was blocking inline onclick)
- Fix auth middleware: empty Bearer token now falls through to cookie auth
- Fix audio backups: only save on transcription failure, stop auto-deleting on success
- Soften AI correction injection to prevent model hallucination from correction history
- Fix LiteLLM TTS model name handling (no incorrect openai/ prefix)
- Expand AI instructions textarea in Learning Hub CMS
- Update README for v6 with all features and providers
- Add comprehensive docs/: architecture, API reference, database schema,
  authentication, AI providers, speech, learning hub, configuration, deployment
2026-04-04 22:56:24 +02:00

6.5 KiB

Pediatric AI Scribe - Architecture Overview

System Overview

The Pediatric AI Scribe is a self-hosted, Dockerized clinical documentation assistant built on the following stack:

  • Runtime: Node.js 20 (Alpine) with Express
  • Database: PostgreSQL 16 with the pgvector extension for embedding-based similarity search
  • Containerization: Docker Compose with two services (app + database)
  • Frontend: Vanilla JavaScript single-page application (no framework)

The application provides AI-powered transcription, note generation, and learning tools for pediatric clinicians. It runs entirely behind a reverse proxy and is designed for single-institution or personal deployment.


File Structure

/
├── server.js                        # Application entry point
├── package.json
├── Dockerfile
├── docker-compose.yml
├── sw.js                            # Service worker (copied into public/)
│
├── src/
│   ├── routes/                      # 27 route files (Express routers)
│   │   ├── encounters.js
│   │   ├── auth.js
│   │   ├── admin.js
│   │   ├── learning.js
│   │   ├── ...                      # (27 total)
│   │
│   ├── utils/
│   │   ├── ai.js                    # LLM client abstraction (OpenAI-compatible)
│   │   ├── models.js                # Model registry and selection
│   │   ├── prompts.js               # System/user prompt templates
│   │   ├── config.js                # App settings helpers (DB-backed)
│   │   ├── logger.js                # Winston logger setup
│   │   ├── embeddings.js            # pgvector embedding generation
│   │   ├── transcribeAWS.js         # AWS Transcribe integration
│   │   ├── transcribeGoogle.js      # Google Cloud Speech-to-Text
│   │   ├── transcribeLocal.js       # Local Whisper WASM transcription
│   │   └── ttsGoogle.js             # Google Cloud Text-to-Speech
│   │
│   ├── middleware/
│   │   ├── auth.js                  # JWT + session authentication
│   │   └── logging.js               # Request/response logging middleware
│   │
│   └── db/
│       └── database.js              # PostgreSQL connection pool + query helpers
│
├── public/                          # Static frontend assets
│   ├── index.html                   # SPA shell
│   ├── app.js                       # Tab/navigation manager
│   ├── components/                  # HTML partials loaded via fetch
│   ├── js/                          # 20+ JS modules
│   └── css/                         # Stylesheets
│
└── scripts/                         # Utility and migration scripts

Request Flow

Every incoming HTTP request passes through the following middleware chain in order:

Client Request
  |
  v
Helmet (CSP headers, security hardening)
  |
  v
CORS (origin validation)
  |
  v
Cookie Parser (signed cookies for sessions)
  |
  v
Rate Limiting (per-IP and per-route limits)
  |
  v
Static File Serving (public/ directory)
  |
  v
Route Matching (src/routes/*.js)
  |
  v
Auth Middleware (JWT verification, role checks)
  |
  v
Route Handler (business logic, DB queries, AI calls)
  |
  v
JSON Response

Static assets are served before route matching, so unauthenticated users can load the SPA shell and login page. All API routes under /api/ require authentication unless explicitly excluded (e.g., /api/auth/login, /api/auth/register).


Frontend Architecture

The frontend is a vanilla JavaScript SPA with no build step and no framework.

Loading

index.html serves as the application shell. It contains a <div class="app-body"> placeholder and loads 20+ JS modules via <script defer> tags. On startup, app.js initializes the tab system and fetches HTML partials from components/ into the .app-body container.

Module Communication

Because there is no framework or module bundler, frontend modules communicate through two mechanisms:

  1. Window globals -- Shared state and utility functions are attached to window (e.g., window.currentUser, window.apiCall).
  2. CustomEvents -- Modules dispatch and listen for CustomEvent instances on document to coordinate loosely-coupled updates (e.g., when an encounter is saved, other tabs refresh their data).

Tab Navigation

Tabs are managed by app.js. Clicking a tab fetches the corresponding HTML partial from components/, injects it into .app-body, and invokes the module's initialization function. Only one tab is active at a time; previous tab content is replaced.


Docker Configuration

Application Container

  • Base image: node:20-alpine
  • System dependencies: ffmpeg (audio processing for transcription)
  • Bundled models: Self-hosted Whisper WASM models for browser-side and server-side local transcription
  • Internal port: 3000

Database Container

  • Image: pgvector/pgvector:pg16
  • Extension: pgvector is loaded automatically for vector similarity search on learning content embeddings

Port Mapping

The application binds to the loopback interface only:

127.0.0.1:3552 -> container:3000

This means the app is not directly accessible from the network. A reverse proxy (e.g., Nginx, Caddy) should terminate TLS and forward traffic to 127.0.0.1:3552.

Volumes

Volume Purpose
pgdata PostgreSQL data directory (persistent)
scribe-logs Application file logs written by Winston

Service Worker

The file sw.js is registered by the frontend and implements a two-strategy caching model:

Static Assets (Cache-First)

Requests for CSS, JS, images, fonts, and HTML partials are served from the cache first. If the cache misses, the network is used and the response is cached for future requests. This enables fast repeat loads and basic offline shell rendering.

API Requests (Network-First)

Requests to /api/ endpoints always attempt the network first. If the network fails (e.g., offline or timeout), the service worker falls back to a cached response if one exists. This ensures users always see the freshest data when connected.

Precaching

On installation, the service worker precaches the application shell: index.html, app.js, core CSS, and critical component partials. This set of assets is enough to render the login screen and basic UI skeleton without any network requests.