konzept vertraulich owner: matus
Isolationsstufe für Gesundheitsdaten benennen (`GG-GOV-SECURITY-0008`)
Isolationsstufe für Gesundheitsdaten benennen (GG-GOV-SECURITY-0008)
Status: offen · Priorität: hoch · Fällig: mit dem ersten echten Patientenfall, dann blockierend
Blockiert durch: zwei fehlende Bauteile (siehe unten) und E-1 (Betriebsmodus des Pilots, extern)
Regel: GG-GOV-SECURITY-0008
(Zwirn, neu 2026-08-10, ADR-0029) —
lokal über guidelines/INDEX.md erreichbar.
Worum es geht
Zwirn kennt seit 2026-08-10 drei Isolationsstufen, und jedes Produkt muss je Datenklasse benennen, auf welcher es fährt:
- I1 — Query-Isolation: eine DB, ein Schema,
tenant_key-Spalte, Filter in jeder Query. - I2 — Schema-/DB-Isolation: ein Schema oder eine DB je Mandant.
- I3 — Instanz-Isolation: eigener Stack je Mandant.
Dieses Produkt fährt heute I1 — und zwar nicht als Entscheidung, sondern weil das die Bauform des
Zwirn-Samples ist. Das ist genau der Zustand, den die neue Regel abstellen soll. Es geht nicht darum,
dass hier schlecht gearbeitet worden wäre: GG-GOV-SECURITY-0005 ist vollständig erfüllt (10/10 Entities
mit tenant_key, keine tenant-lose Repository-Methode, 17/17 Schreib-Endpunkte gegated, alle fünf
Baseline-Regeln aktiv mit leerer Baseline). Die Regel ist erfüllt — die Frage ist eine andere: reicht
diese Stufe für diese Daten?
Die Daten sind: Laborwerte, Medikation auf Wirkstoffebene, Kontraindikationen und dauerhafte Sicherheitsmerkmale, Befundinterpretationen, Therapieempfehlungen mit Dosierung, Anamnese, Einwilligung. Die Mandanten sind: viele kleine, voneinander unabhängige Praxen — jede davon vermutlich eigene Verantwortliche im datenschutzrechtlichen Sinn. Das ist die Konstellation, für die die Regel I2 als Erwägung nennt.
Diese Notiz beantwortet die Frage nicht. Sie kann es nicht: das Kundenmaterial sagt zu Datenschutz,
Verschlüsselung, Löschkonzept, Hosting und technischer Mandantentrennung nichts — ausdrücklich
ausgeschlossen im Backlog-Kopf („Ausdrücklich nicht enthalten: … Security-Implementierungsdetails …
Infrastruktur"). Die Einordnung ist im Material als extern zu vergebende Entscheidung geführt
(E-1, FACH-121, E-20). Diese Notiz sorgt dafür, dass die Frage einen Ort hat und dass klar
ist, was mit welcher Antwort fällig wird.
Was mit I2 fällig würde — ehrliche Umfangsschätzung
Nicht klein. Ein Projektschritt, kein Patch. Zwei Bauteile sind Vorbedingung; eines davon steht seit dem 2026-08-10, das andere fehlt weiterhin — und es ist das blockierende:
| Vorbedingung | Stand hier | Warum sie zwingend ist |
|---|---|---|
| Migrationswerkzeug (Flyway/Liquibase) | fehlt — quarkus.hibernate-orm.database.generation=update, migrations/ enthält nur eine README.md |
update kann kein Schema je Mandant anlegen und N Schemas nicht synchron halten. Solange das so bleibt, ist I2 nicht offen, sondern verbaut. |
Tenant-Registry |
✅ vorhanden seit 2026-08-10 — tuxametrics-platform-service, Aggregat Tenant (ADR-0005) |
Bei I2 wird „Praxis anlegen" zu einem Vorgang mit Infrastrukturfolge (Schema erzeugen, migrieren, Verbindung registrieren). Der Vorgang braucht einen Ort, und das ist die Tenant-Registry. Der Ort existiert jetzt — TenantService.create ist heute reine CRUD-Fachlogik, die Infrastrukturfolge käme erst mit I2 dazu (so steht es auch im Javadoc dort). |
Was dagegen nicht anfällt, und das ist die gute Nachricht: die Anwendungsschicht ist bereits
I2-freundlich gebaut. Jede Repository-Methode nimmt tenantKey als ersten Parameter, jeder Service
reicht ihn durch, jeder Pfad trägt ihn. Ein Schema-Routing würde oberhalb ansetzen (Datasource- bzw.
@TenantId-Auflösung je Request); die 10 Entities, 10 Repositories und 7 Resources blieben unverändert.
Der Query-Filter bleibt ohnehin — bei I2 ist er zweite Linie, nicht Ersatz.
Grobe Reihenfolge, falls I2 entschieden wird: (1) Flyway einführen und das bestehende Schema als
Baseline erfassen, (2) Authority + erledigt, (3) Schema-Provisionierung beim
Anlegen einer Praxis, (4) Datasource-/Schema-Auflösung je Request, (5) Migrationslauf über N Schemas im
Deploy.Tenant-Registry
Was Schritt 2 nicht mitgebracht hat, und das ist wichtig: die Authority hat jetzt drei eigene
Datenbanken mehr im Stack (platform, vid, Keycloak). Sie sind alle I1 — auch die
Grant-Tabelle, in der steht, wer in welcher Praxis was darf. Bei einer I2-Entscheidung wäre zu klären,
ob die Autorisierungsdaten mitwandern müssen oder ob sie als Steuerdaten bewusst plattformweit bleiben.
Das ist keine rhetorische Frage: platform_capability_grant enthält keine Gesundheitsdaten, wohl aber
die vollständige Liste, welche Person in welcher Praxis arbeitet.
Zusätzlich offen: -0008.6 — praxisübergreifende Kennzahlen
Das ist kein Nebenpunkt, sondern eine fachliche P1-Anforderung, die dem heutigen Zwirn-Modell
widerspricht. GG-GOV-SECURITY-0005 sagt: „Es gibt keine API-Endpunkte, die Daten mehrerer Tenants
zusammenführen." Das Material fordert genau das:
FACH-083: der klinische Fachexperte sieht Übersteuerungen „praxisübergreifend, aggregiert und ohne Personenbezug"; „praxisübergreifend personenbezogen" ist „nicht vorgesehen".FACH-083weiter: „Eine Auswertung, die auf eine einzelne Praxis zurückführbar wäre, wird nicht angezeigt. Unterhalb einer festzulegenden Mindestfallzahl bleibt eine Zelle leer."FACH-100/FACH-102: Kennzahlen sind „nach Praxis und Zeitraum" auswertbar.FACH-107: „Je Kandidat sind Fallzahl und Anzahl beteiligter Praxen sichtbar."
-0008.6 macht daraus keine Ausnahme vom Datenschutz, sondern eine deklarierte Ausnahme mit vier
Pflichtangaben: Zweckbindung, Aggregationsgrad, Mindestfallzahl (die Zahl fehlt im Material →
OF-68), und Durchsetzung im Datenmodell statt nur über eine Capability. Der letzte Punkt ist im
Produkt bereits als OF-62 erkannt: „Die Zweckbindung der Kennzahlen muss im Datenmodell erzwungen
werden, nicht nur über Rechte." -0008.6 bestätigt das als allgemeine Regel.
Außerdem: ein solcher Endpunkt liegt nie unter /tenants/{tenantKey}/… — der Pfad würde einen
Mandanten behaupten, den die Antwort nicht hat. Alle sieben heutigen Resources tun das; der
Kennzahl-Endpunkt braucht einen eigenen Pfadraum.
Erledigt in diesem Durchgang
-
✅
tenant_key-Spaltenlänge 16 → 8 (alle 10 Entities). Vorher speicherbar, aber unerreichbar: der geteilteUserContextResolverauszwirn-securitylehnt jeden Pfad-Tenant über 8 Zeichen mit400 tenant_key_too_longab, undGG-GOV-SECURITY-0005schreibtVARCHAR(8)fest. Ein Mandantenschlüssel mit 9–16 Zeichen wäre dauerhaft nicht ansprechbar gewesen — keine Regel und kein Test hätte das gemeldet (TenantKeyOnEntitiesRuleprüft nur die Anwesenheit des Feldes, nie die Länge).mvn testdanach 40/40 grün. Wirkungslos auf Bestandsdaten:generation=updateverkleinert keine Spalte, und der einzige Mandantpraxis01hat 8 Zeichen.Folgefrage, bewusst nicht hier entschieden: 8 Zeichen sind für „viele kleine Praxen" knapp —
praxis01…praxis99passt,praxis100nicht. Die Namenskonvention für Mandantenschlüssel muss das vor der zweiten Praxis berücksichtigen (z. B.prx-a1b2). Die Alternative — die 8-Zeichen-Grenze im Zwirn-Kern anheben — ist eine Shared-Kernel-Änderung mit ADR-Pflicht und wäre hier nicht am richtigen Ort entschieden worden.
Der eine Schritt, der ohne die externe Entscheidung schon gemacht werden kann (2026-08-10)
Der Rest dieser Notiz wartet auf E-1. Ein Punkt wartet nicht: das Migrationswerkzeug. Es ist in
der Tabelle oben als Vorbedingung geführt, aber es ist mehr als das — es ist der einzige der beiden
Blocker, der unabhängig von jeder Isolationsentscheidung fällig ist. generation=update ist auch
für I1 keine tragfähige Dauerlösung, sobald echte Daten im Spiel sind: es löscht keine Spalte, es
verkleinert keine Spalte (siehe „Erledigt in diesem Durchgang"), es kennt keine Datenmigration und es
hinterlässt keine nachvollziehbare Historie. Für ein Produkt mit Gesundheitsdaten ist „wir wissen
nicht, welche Schemaversion in der Praxis läuft" eine eigene Klasse von Problem.
Deshalb: Flyway einführen, bevor E-1 beantwortet ist — nicht danach. Wenn die Antwort I2 lautet,
ist es die Vorbedingung; wenn sie I1 lautet, ist es trotzdem richtig. Es gibt keinen Ausgang, bei dem
dieser Schritt verworfen wird. Konkret, in dieser Reihenfolge:
quarkus-flywayals Dependency,quarkus.flyway.migrate-at-start=true.- Baseline aus dem Ist-Schema erzeugen, nicht von Hand schreiben. Der Stack einmal mit
generation=updategegen eine leere DB hochfahren, dann das erzeugte Schema alsV1__baseline.sqlablegen (pg_dump --schema-only). Von Hand geschrieben weicht es garantiert an einer Stelle ab, und genau diese Stelle fällt erst in der Praxis auf. quarkus.hibernate-orm.database.generationaufvalidateumstellen. Das ist der eigentliche Gewinn: ab hier meldet der Start einen Drift zwischen Entities und Schema, statt ihn still zu reparieren. Solangeupdatesteht, kann der Unterschied zwischen „Entity geändert" und „Schema geändert" gar nicht auffallen.migrations/README.md(heute der einzige Inhalt des Verzeichnisses) durch die tatsächliche Konvention ersetzen.- Einen Test, der gegen die migrierte DB läuft statt gegen ein von Hibernate erzeugtes H2-Schema —
sonst prüft die Testsuite weiterhin ein Schema, das es in keiner Umgebung gibt. Heute:
quarkus.devservices.enabled=false+ H2 in-memory (src/test/resources/application.properties). Dieser Punkt ist der aufwendigste und der wichtigste; er ist auch der einzige, der die bestehenden 40 Tests anfassen könnte.
Aufwand: Schritte 1-4 sind ein überschaubarer Tag, Schritt 5 ist eine eigene Entscheidung (Testcontainers ja/nein). Nicht angefangen am 2026-08-10 — der Umfang überschreitet, was ohne Rücksprache sinnvoll in einem fremden Zug erledigt wird, und Schritt 5 berührt die Teststrategie.
Zu tun
- Flyway einführen (Schritte 1-5 oben) — unabhängig von
E-1, kann sofort beginnen. -
E-1/FACH-121abwarten bzw. anstoßen: läuft der Pilot mit echten Fällen? - Bei „ja": Isolationsstufe entscheiden und in
guidelines/project/benennen (-0008.1). - Bei I2: erst Flyway, dann Authority — in dieser Reihenfolge, sonst läuft man in die Sackgasse.
(Stand 2026-08-10: an der Authority wird gearbeitet, siehe
capability-authority.mdbzw. das Modultuxametrics-platform-service. Die Reihenfolge-Warnung bleibt trotzdem gültig: eine Authority ohne Migrationswerkzeug kann Mandanten registrieren, aber keine Schemas bereitstellen.) - Unabhängig davon: Mindestfallzahl für
-0008.6klären (OF-68) und den Kennzahl-Endpunkt außerhalb von/tenants/{tenantKey}/…planen. - Namenskonvention für Mandantenschlüssel festlegen, bevor die zweite Praxis angelegt wird (8 Zeichen, siehe „Erledigt in diesem Durchgang").