Files
2026-03-09 08:42:22 +01:00

11 KiB
Raw Permalink Blame History

Overall Architecture Open Reception

Project Overview

Open Reception is an end-to-end encrypted appointment booking platform, funded by the German Federal Ministry of Research, Technology and Space and the PrototypeFund. License: AGPL-3.0.

Tech Stack

  • Framework: SvelteKit 5 (SSR + SPA) with TypeScript
  • UI: Tailwind CSS 4, shadcn/svelte (bits-ui), Lucide Icons
  • Database: PostgreSQL 16 via Drizzle ORM
  • Authentication: JWT (jose), WebAuthn/Passkeys (@simplewebauthn/server)
  • Cryptography: ML-KEM-768/Kyber (@noble/post-quantum), AES-256-GCM, Argon2, Shamir Secret Sharing
  • Email: Nodemailer with Svelte-rendered templates
  • i18n: Paraglide (German, English)
  • Logging: Winston (UniversalLogger)
  • Validation: Zod, sveltekit-superforms + formsnap
  • Testing: Vitest (unit), Playwright (E2E)
  • Infrastructure: Docker Compose, Caddy (reverse proxy + HTTPS)

System Architecture

┌─────────────────────────────────────────────────────┐
│                     Clients                         │
│            (Browser  Clients / Staff)              │
└──────────────────────┬──────────────────────────────┘
                       │ HTTPS
┌──────────────────────▼──────────────────────────────┐
│                Caddy Reverse Proxy                  │
│         (TLS Termination, Security Headers,         │
│          On-Demand TLS, Gzip Compression)           │
└──────────────────────┬──────────────────────────────┘
                       │ HTTP :3000
┌──────────────────────▼──────────────────────────────┐
│              SvelteKit Application                  │
│  ┌────────────────────────────────────────────────┐ │
│  │            Server Hooks Pipeline               │ │
│  │  startup → logging → i18n → rateLimit →        │ │
│  │  cors → secHeaders → apiAuth → authGuard       │ │
│  └────────────────────────────────────────────────┘ │
│  ┌──────────────────┐  ┌────────────────────────┐   │
│  │   API Routes     │  │   Page Routes (SSR)    │   │
│  │   /api/...       │  │   /(pages)/...         │   │
│  └────────┬─────────┘  └───────────┬────────────┘   │
│  ┌────────▼─────────────────────────▼────────────┐   │
│  │              Service Layer                    │   │
│  │  AppointmentService, AgentService,            │   │
│  │  ChannelService, ScheduleService,             │   │
│  │  StaffCryptoService, NotificationService,     │   │
│  │  SessionService, AuthorizationService,        │   │
│  │  EmailService, InviteService, ...             │   │
│  └────────┬──────────────────────────────────────┘   │
│  ┌────────▼──────────────────────────────────────┐   │
│  │            Database Layer (Drizzle ORM)        │   │
│  └────────┬──────────────────────────────────────┘   │
└───────────┼──────────────────────────────────────────┘
            │
┌───────────▼──────────────────────────────────────────┐
│                  PostgreSQL 16                        │
│  ┌──────────────────┐  ┌──────────────────────────┐  │
│  │  Central DB      │  │  Tenant DB (per tenant)  │  │
│  │  - tenant        │  │  - agent                 │  │
│  │  - user          │  │  - channel               │  │
│  │  - user_passkey  │  │  - slot_template         │  │
│  │  - user_session  │  │  - appointment           │  │
│  │  - user_invite   │  │  - notification          │  │
│  │  - tenant_config │  │  - staff_crypto          │  │
│  │  - challenge_    │  │  - client_appointment_   │  │
│  │    throttle      │  │    tunnel                │  │
│  └──────────────────┘  │  - appointment_key_share │  │
│                        │  - agent_absence         │  │
│                        │  - auth_challenge        │  │
│                        │  - client_pin_reset_     │  │
│                        │    token                 │  │
│                        └──────────────────────────┘  │
└──────────────────────────────────────────────────────┘

Multi-Tenant Architecture

The system uses database-per-tenant isolation: each tenant receives its own PostgreSQL database. The central database manages tenant metadata, users, and sessions.

  • Central DB: Contains tenant, user, user_passkey, user_session, user_invite, tenant_config, challenge_throttle
  • Tenant DB (per tenant): Contains all business-specific data (appointments, channels, agents, cryptographic material)
  • Connection management: getTenantDb() resolves the database connection at runtime by tenantId and caches connections in a Map

Tenant Lifecycle

Each tenant passes through five states during onboarding: SETTINGSAGENTSCHANNELSSTAFFREADY

User Roles & Authorization

Three roles with hierarchical access:

  1. GLOBAL_ADMIN Full access to all tenants, can create/delete tenants
  2. TENANT_ADMIN Management of a single tenant (channels, agents, staff, settings)
  3. STAFF Operational access to the calendar and appointments of their own tenant

Authorization is enforced via AuthorizationService and checkPermission(), each with tenant-scope verification.

Authentication

Staff/Admin Authentication

  • WebAuthn/Passkeys as the primary method
  • Passphrase as an alternative method
  • JWT Access Tokens (15 min) + Refresh Tokens (7 days)
  • Sessions stored in the central database
  • Token validation against the DB on every API call

Client Authentication (Appointment Bookers)

  • PIN-based with challenge-response protocol
  • Email hash for privacy-preserving identification
  • Argon2 for PIN derivation
  • Challenge throttling against brute-force attacks

End-to-End Encryption

Core Principle: Zero-Knowledge Server

The server never has access to unencrypted personal data at any point.

Cryptographic Primitives

  • ML-KEM-768 (Kyber): Post-quantum secure key exchange
  • AES-256-GCM: Symmetric encryption of appointment data
  • Shamir Secret Sharing (2-of-2): Private key splitting
  • Argon2: PIN-based key derivation (client-side)

Staff Key Management

  • Kyber key pair is generated in the browser
  • Private key is split into two shards via XOR:
    • Passkey shard: Derived from WebAuthn authenticatorData
    • DB shard: Stored in the tenant database (staff_crypto)
  • Reconstruction requires both shards (during login)

Client Tunnel Concept

  • Each client receives an encrypted "tunnel" per tenant
  • Tunnel key (AES-256) encrypts all of the client's appointments
  • Tunnel key is encrypted separately for each staff member using their Kyber public key
  • Client private key: Shamir split (PIN shard + server shard)

Server Hooks Pipeline

The request processing chain (hooks.server.ts) in execution order:

  1. startupHandle DB migrations, housekeeping scheduler (every 12h)
  2. loggingHandle Request/response logging
  3. i18nHandle Locale detection and setting
  4. rateLimitHandle IP-based rate limiting (20 req/2s)
  5. corsHandle CORS headers
  6. secHeaderHandle Security headers (CSP, HSTS, etc.)
  7. apiAuthHandle JWT validation for API routes
  8. authGuard Page-based access control (dashboard protection)

Frontend Structure

Route Groups

  • /(pages)/(clients)/ Public client pages (appointment booking, client login)
  • /(pages)/dashboard/ Protected admin/staff area
  • /(pages)/login/, /logout/, /setup/, /confirm/ Authentication flows
  • /api/ REST API with OpenAPI documentation

Dashboard Areas

  • Calendar Day view with appointments and slots
  • Channels Management of bookable resources
  • Agents Management of persons/contacts
  • Staff User management with roles
  • Absences Agent absences
  • Tenants Tenant management (GLOBAL_ADMIN only)
  • Settings Tenant configuration

UI Component Library

Based on shadcn/svelte with custom extensions:

  • Layouts: sidebar-layout, centered-card, empty-layout, max-page-width
  • Templates: empty-state, form-grid, list, loading, language-switch
  • UI Primitives: button, card, dialog, form, input, calendar, combobox, etc.

Email System

  • Svelte components as email templates (server-side rendered)
  • Supported email types: confirmation, invitation, appointment booking, appointment reminder, appointment rejection, appointment cancellation, PIN reset
  • HTML + plaintext output
  • Language-dependent templates (de/en)

Docker Infrastructure

Development

  • Only PostgreSQL as a Docker container
  • SvelteKit runs locally via npm run dev

Production

Three containers in an internal network:

  1. PostgreSQL Read-only filesystem, Docker secrets, SCRAM-SHA-256
  2. SvelteKit App Rootless (UID 1001), read-only, healthcheck at /api/health
  3. Caddy HTTPS with on-demand TLS (Let's Encrypt), security headers, gzip

Container Security Measures

  • no-new-privileges, cap_drop: ALL
  • Read-only root filesystems with tmpfs
  • Docker secrets instead of environment variables (production)
  • Internal networks, only Caddy exposes ports

Startup & Housekeeping

On application start (StartupService):

  1. Migrate central database
  2. Migrate all tenant databases in parallel
  3. Start housekeeping scheduler (every 12 hours):
    • Clean up expired invitations
    • Delete expired sessions
    • Remove expired PIN reset tokens per tenant