> **Praktische Konsequenz für das System:** Alle personenbezogenen Gesundheitsdaten dürfen den Self-Hosted-Perimeter **niemals** verlassen. Jeder externe Service (Cloud-PDF-Renderer, CDN, externe Template-Stores) ist unzulässig, solange kein Auftragsverarbeitungsvertrag (AVV) vorliegt und keine Rechtsgrundlage nach Art. 9 Abs. 2 gegeben ist.
**Carbone.js** ist ein Open-Source-Report-Generator (AGPL-3.0 Community, kommerzielle Lizenz verfügbar), der JSON-Daten in ODT/DOCX-Templates injiziert und via LibreOffice-Integration nach PDF konvertiert. Das Backend ist ein schlanker **Express.js**-Service; das Frontend eine einfache SPA (Vue 3 oder plain HTML).
#### Architektur-Sketch
```
Vue SPA ──POST /render──▶ Express.js ──REST──▶ Directus API
│
├──carbone.render()──▶ LibreOffice (lokal im Container)
| **Frontend-Aufwand** | ⚠️ Custom SPA muss selbst entwickelt/gepflegt werden |
| **Skalierung** | ⚠️ LibreOffice-Worker ist prozessbasiert; parallele Requests erfordern mehrere Worker oder Queue |
#### Lizenz-Klarstellung Carbone
Die **On-Premise Docker Edition** ist frei nutzbar (Community-Features). Kostenpflichtige Lizenzen werden nur für Enterprise-Features (erweitertes Template-Management, API-Rate-Limits, Support) benötigt. Für ein internes Serienbriefwerkzeug reicht die Community-Edition vollständig aus.
---
### Option B: Vollwertig — Gotenberg + Budibase/Appsmith als Low-Code-Frontend
#### Beschreibung
**Gotenberg** ist ein containerisierter PDF-Konverter-Dienst (MIT-Lizenz), der über eine REST-API DOCX-, HTML- und andere Formate via LibreOffice oder Headless Chromium in PDF umwandelt. Als Frontend wird **Budibase** (Self-Hosted, GPL-3.0/Budibase-Lizenz) oder **Appsmith** (Apache 2.0, Self-Hosted) eingesetzt — beides Low-Code-Builder mit Directus-Konnektoren.
#### Architektur-Sketch
```
Budibase UI ──Query Builder──▶ Directus API (Daten holen)
| **Lizenz Gotenberg** | ✅ MIT – keine Einschränkungen |
| **Lizenz Budibase** | ⚠️ Budibase v2+ nutzt eine eigene "Budibase Personal License" für Self-Hosted; GPL nur für ältere Versionen; Enterprise-Features kostenpflichtig |
| **Lizenz Appsmith** | ✅ Apache 2.0 für Community-Edition |
| **DSGVO-Relevanz** | ✅ Vollständig self-hosted möglich; Budibase/Appsmith senden keine Daten nach außen (Telemetrie deaktivierbar) |
| **Wartbarkeit** | ⚠️ Budibase/Appsmith sind komplexe Eigenanwendungen mit eigenem Lifecycle |
| **Resource-Footprint** | ⚠️ Budibase benötigt eigene DB + Backend-Services; Appsmith ebenso |
Ab Budibase v2 gilt eine proprietäre Self-Hosted-Lizenz für kostenfreie Tier-Nutzung. Bei Änderungen am Source-Code entsteht eine Pflicht zur Veröffentlichung nur für AGPL-Teile. Für reine interne Nutzung ohne Modifikation ist das unproblematisch – dennoch Lizenztext vor Deployment genau lesen.
---
### Direktvergleich
| Kriterium | Option A (Carbone + Node.js) | Option B (Gotenberg + Budibase) |
Das Backend liest `metadata.json` und exponiert eine Template-Liste. Deployment neuer Templates via `scp` oder Git-Webhook.
**Nachteil:** Kein GUI-Upload; kein integriertes RBAC.
### Empfehlung: Option 5A (Directus als Template-Store)
Da Directus bereits läuft, ergibt sich kein Mehraufwand. Die RBAC-Integration verhindert, dass unberechtigte Nutzer Templates überschreiben – kritisch im regulierten Umfeld.
| **Gotenberg (Chromium-Modul)** | Headless Chromium | `gotenberg/gotenberg:8` (~2,5 GB) | ~1–1,5 GB | ✅ (Web-Fonts nur wenn lokal) | ✅ | ⚠️ Für HTML-Templates sinnvoll, aber schwerer |
| **WeasyPrint** | Python, CSS-basiertes PDF aus HTML | `python:3.12-slim` + apt | ~150–300 MB | ⚠️ Nur installierte Systemfonts | ✅ | ⚠️ Gut für HTML-Templates; DOCX nicht möglich |
### Sicherheitshinweis: Puppeteer/Chromium in Docker
Headless Chrome benötigt privilegierte Capabilities oder ein angepasstes **seccomp-Profil**. Der häufig verwendete `--no-sandbox`-Flag ist in einer produktionsnahen Umgebung mit nicht-vertrauenswürdigen Templates **inakzeptabel**. Korrekte Konfiguration:
| `sb_user` | Lesen: freigegebene Collections (kein Zugriff auf raw Gesundheitsfelder) |
| `sb_admin` | Lesen + Schreiben auf `sb_templates`; Einsehen von `sb_audit_log` |
| `sb_service` | Service-Account für das Backend; nur Lese-Zugriff auf Kontakt-Collections |
Das Backend-Service-Token wird als Docker Secret verwaltet (nie in `.env`-Dateien einchecken):
```yaml
secrets:
directus_token:
file:./secrets/directus_service_token.txt
services:
serienbrief-backend:
secrets:
- directus_token
environment:
DIRECTUS_TOKEN_FILE:/run/secrets/directus_token
```
#### Session-Management im Frontend
Wenn eine Custom-SPA eingesetzt wird: **keine** persistente Token-Speicherung in `localStorage`. Stattdessen `sessionStorage` oder HttpOnly-Cookie mit kurzem TTL (max. 4 Stunden für regulierten Kontext).
### 7.3 Audit-Log
Ein Audit-Log ist für den Gesundheitssektor nicht optional. Jede Serienbrief-Generierung erzeugt einen unveränderlichen Log-Eintrag:
```sql
CREATETABLEsb_audit_log(
idBIGSERIALPRIMARYKEY,
created_atTIMESTAMPTZNOTNULLDEFAULTnow(),
user_idTEXTNOTNULL,-- Directus-User-UUID
user_emailTEXTNOTNULL,-- Snapshot, da User gelöscht werden können
template_idTEXTNOTNULL,
template_versionTEXT,
recipient_countINTEGERNOTNULL,
recipient_idsTEXT[],-- Arrays: welche Kontakt-IDs betroffen
ip_addressINET,
user_agentTEXT,
actionTEXTNOTNULLDEFAULT'render_pdf',
-- Keine Nutzdaten (kein PDF-Inhalt, keine personenbezogenen Felder)
CONSTRAINTno_updateCHECK(true)-- Ergänzt durch DB-Grants: kein UPDATE/DELETE
**Wichtig:** Der Audit-Log selbst enthält `recipient_ids` (Directus-IDs), aber **keine personenbezogenen Felder** (kein Name, keine Adresse). Die Verbindung zur Person ist nur über Directus herstellbar und bleibt so auflösbar.
- Interne Container-Kommunikation: im internen Docker-Netzwerk ohne TLS akzeptabel, solange das Host-System gehärtet ist (kein Fremdzugriff auf Docker-Socket)
- **Volumes mit sensiblen Daten** (Template-Store): LUKS-verschlüsseltes Dateisystem auf Host-Ebene empfohlen
### 7.5 Härtungsmaßnahmen für Container
```yaml
# Best-Practice-Snippet für alle Serienbrief-Services
| Berufsgeheimnis (§ 54 ÄrzteG AT) | Zugriff nur für autorisiertes Personal; RBAC | ☐ Rollen definieren |
---
## 8. Docker Compose Grundstruktur
Die folgende Struktur ist **als Ausgangspunkt konzipiert**– nicht als vollständige Produktionskonfiguration. Environment-spezifische Werte (Passwörter, Tokens) gehören in `.env`-Dateien, die **nicht ins Git-Repository** eingecheckt werden.
```
/opt/serienbrief/
├── docker-compose.yml
├── docker-compose.override.yml # Lokale Dev-Overrides
├── .env # Nicht ins Repo!
├── secrets/
│ ├── directus_service_token.txt
│ └── db_password.txt
├── config/
│ ├── traefik/
│ │ └── traefik.yml
│ └── seccomp/
│ └── default.json
├── templates/ # Bind-Mount für Template-Store (Option B)
└── Makefile # Deployment-Shortcuts
```
```yaml
# docker-compose.yml
# Hinweis: Directus und PostgreSQL existieren bereits in einem separaten Compose-Projekt.
# Dieses Compose-File referenziert das externe Netzwerk über `external: true`.
name:serienbrief
networks:
serienbrief_int:
driver:bridge
internal:true# Kein Internet-Zugang für interne Services
Directus als Template-Store + Adressdatenbank (bereits vorhanden)
+
Schlanke Vue 3 SPA oder Directus-eigene Flows für UI
```
#### Begründung
**1. Kontrolle und Transparenz über die gesamte Pipeline**
Als erfahrener Admin und Data Protection Officer ist die vollständige Nachvollziehbarkeit des Datenflusses entscheidend. Eine Low-Code-Plattform wie Budibase abstrahiert Interna, die im regulierten Umfeld dokumentiert werden müssen. Ein eigenes, überschaubares Node.js-Backend (~300–500 LoC) ist vollständig auditierbar.
**2. DSGVO-Konformität by Design**
- Kein Datentransfer nach außen; alle Komponenten self-hosted
- Kein `localStorage` für Tokens; kein Telemetrie-Risiko
- Audit-Log ist fester Bestandteil, nicht nachgerüstet
- Datenfeldminimierung ist im Backend-Code erzwingbar; bei Low-Code-Lösungen oft umgehbar
**3. Lizenzrisiko ist beherrschbar**
Carbone AGPL-3.0 ist für interne, nicht weiterverteilte Nutzung unproblematisch. Gotenberg MIT hat keinerlei Einschränkungen. Eine Budibase-Proprietär-Lizenz birgt mehr langfristiges Risiko.
**4. Ressourceneffizienz**
Der vorgeschlagene Stack benötigt ~1,5–2 GB RAM. Option B mit Budibase würde allein für die Plattform ~1–2 GB zusätzlich beanspruchen – auf einem dedizierten Ubuntu-Server im Gesundheitssektor mit definierten Ressourcen kein unwesentlicher Faktor.
**5. Wartbarkeit für einen Admin ohne Frontend-Entwickler**
Das Backend ist mit Standard-Node.js-Werkzeugen wartbar. Gotenberg-Updates sind trivial (`docker pull`). Templates werden über Directus verwaltet – die Nutzer kennen diese Oberfläche bereits.
**6. Upgrade-Pfad ist klar**
Sollte das System wachsen (mehr parallele Nutzer, größere Batches), kann:
- Gotenberg horizontal skaliert werden (mehrere Replicas im Swarm)
- Eine Job-Queue (Bull/BullMQ mit Redis) vor den PDF-Renderer geschaltet werden
- Das Frontend durch Budibase/Appsmith ersetzt werden, ohne Backend-Änderungen
| Cloud-basierter PDF-Service (DocuSign, Adobe, Cloudmersive) | Drittlandtransfer von Gesundheitsdaten; DSGVO-konformer AVV meist nicht ausreichend für Art. 9-Daten |
| Puppeteer/headless Chrome selbst verwaltet | Sicherheitskomplexität (seccomp, sandbox); wartungsintensiv; Gotenberg bietet dieselbe Funktionalität mit weniger Aufwand |
| wkhtmltopdf | Keine aktive Entwicklung seit 2020; bekannte Rendering-Schwächen; sicherheitsrelevante Bugs ungepatcht |
| Budibase als primäre Datenquelle (statt Directus) | Würde bestehende Directus-Infrastruktur verdoppeln; Datenduplikation mit DSGVO-Implikationen |
---
### Abschlussbewertung
Das vorgeschlagene System hält sich an das Prinzip **minimale Komplexität bei maximaler Auditierbarkeit**. Für einen Data Protection Officer im Gesundheitssektor ist nicht die technische Eleganz entscheidend, sondern die Fähigkeit, bei einer Prüfung durch die österreichische Datenschutzbehörde (DSB) jeden Schritt der Datenverarbeitung lückenlos erklären und belegen zu können. Ein überschaubares Node.js-Backend mit explizitem Audit-Log und klarer Netzwerkisolation erfüllt diese Anforderung besser als jede Black-Box-Low-Code-Plattform.