Brainversum · tuxametrics Graph Admin

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:

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.
Capability-Authority mit Tenant-Registry vorhanden seit 2026-08-10tuxametrics-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 jetztTenantService.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 + Tenant-Registry erledigt, (3) Schema-Provisionierung beim Anlegen einer Praxis, (4) Datasource-/Schema-Auflösung je Request, (5) Migrationslauf über N Schemas im Deploy.

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:

-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


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:

  1. quarkus-flyway als Dependency, quarkus.flyway.migrate-at-start=true.
  2. Baseline aus dem Ist-Schema erzeugen, nicht von Hand schreiben. Der Stack einmal mit generation=update gegen eine leere DB hochfahren, dann das erzeugte Schema als V1__baseline.sql ablegen (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.
  3. quarkus.hibernate-orm.database.generation auf validate umstellen. Das ist der eigentliche Gewinn: ab hier meldet der Start einen Drift zwischen Entities und Schema, statt ihn still zu reparieren. Solange update steht, kann der Unterschied zwischen „Entity geändert" und „Schema geändert" gar nicht auffallen.
  4. migrations/README.md (heute der einzige Inhalt des Verzeichnisses) durch die tatsächliche Konvention ersetzen.
  5. 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