konzept vertraulich owner: matus
Autorisierungs-Authority und IdP entscheiden
Autorisierungs-Authority und IdP entscheiden
Status: ✅ ENTSCHIEDEN UND UMGESETZT (2026-08-10) — Entscheidung: ADR-0005
Dieses Dokument wird nicht gelöscht. Es beschreibt, warum die Lücke bestand und was fehlte — und genau das ist die Begründung für den Umfang dessen, was jetzt gebaut wurde. Der ursprüngliche Befund steht unverändert darunter; erledigt ist er ab dem Abschnitt „Was am 2026-08-10 gebaut wurde". Muster wie in
v12-nachzug-scope.md: Historie bleibt lesbar.
Was am 2026-08-10 gebaut wurde
Auslöser: es sollen Ärztinnen und Ärzte mit echten Patientendaten arbeiten. Damit ist die Frist aus
GG-ARCH-SERVICES-0005.5 („fällig vor dem ersten Nutzer, der nicht der Entwickler ist") abgelaufen —
ab da ist ein Platzhalter kein „bewusst offen" mehr, sondern ein Regelverstoß.
| Was | Wo |
|---|---|
Eigene Authority (Form „eigen" aus -0005.1) |
tuxametrics-platform-service, Port 8125, eigene Postgres 5524. Umfang exakt -0005.2, Vorbild prozesswerkstatt-platform-service |
| IdP | Keycloak, Realm digital-labs, Port 8180, eigene Postgres 5526. Zwei Clients, Claim-Mapper für user_key/actor_type/tenant_key (keycloak/README.md) |
| Virtual Identity | tuxametrics-virtual-identity-service, Port 8126, eigene Postgres 5525. Kein Pflichtbaustein — fachlich begründet über OF-12 |
Registrar im Data-Service (-0005.3) |
tuxametrics-laborauswertung-service/capability/CapabilityRegistrar + PlatformCapabilityAdminClient + ServiceTokenClient + KeycloakTokenRestClient. Nicht startblockierend, Retry im Daemon-Thread |
allowedActors je Capability |
Alle 22 Schlüssel (18 txm.* + 4 vid.*) tragen eine nicht-leere Liste. Die drei GM-7-Schlüssel sind [USER], durchgesetzt an vier Stellen (util/Gm7Schutz, Gm7SchutzTest) |
| UI | AuthShell — gebautes Artefakt immer OIDC, Dev-Stub nur noch als Abkürzung ohne Infrastruktur |
Zwei Dinge, die über den Referenz-Bauplan hinausgehen und begründet sind:
- Der Bootstrap-Grant wird angelegt, nicht dokumentiert.
prozesswerkstatt-platform-serviceführt das Henne-Ei-Problem (der Upsert-Endpunkt ist selbst gegatet) als Operator-Vorgang. Hier legtboot/CapabilityRegistrargenau einen Grant an:SERVICE svc-tuxametrics-servicesdarfplatform.capability.createim Sentinel-Mandantenplatform. Bei Gesundheitsdaten ist „der Operator macht das schon" kein Betriebszustand. CapabilityRegistrarTestbindet die registrierten Schlüssel maschinell an die tatsächlichencapabilityGuard.require(...)-Aufrufe. Beide Driftrichtungen brechen jetzt den Build.
Korrektur einer Zahl, die dieses Dokument selbst noch falsch hat
Der Absatz unten sagt „14 schreibende Endpunkte" und „die 14 txm.*-Capabilities". Es sind 18
(17 schreibende plus txm.auswertung.read). Die 14 stand seit der Erstanlage in mehreren Dokumenten;
korrigiert wurde sie am 2026-08-10 in
implementation-gaps.md — dieses Dokument war
eine der Stellen, die dabei stehen blieben. Der Satz oben schließt das nach.
Neu ist nicht die Zahl, sondern dass sie jetzt nicht mehr driften kann.
CapabilityRegistrarTest vergleicht die registrierte Schlüsselmenge maschinell mit den tatsächlichen
capabilityGuard.require(...)-Aufrufen im Quelltext. Vorher waren das zwei Listen, die nichts
aneinander band außer Disziplin — und genau daran ist die 14 vier Dokumente weit mitgelaufen.
Was davon nicht berührt ist
tuxametrics.regulatorik.einstufung und tuxametrics.pilot.betriebsmodus stehen unverändert auf
UNGEKLAERT, RegulatorikGuard greift unverändert beim Start (ADR-0002, ADR-0003). Das sind Fragen an
Dritte, keine Infrastrukturfragen — dass die Autorisierung entschieden ist, sagt über sie nichts.
Der ursprüngliche Befund (Stand bis 2026-08-09)
Der Service ruft CapabilityGuard.require("txm.…") an allen 14 schreibenden Endpunkten (korrekt: 17,
siehe oben). Der PlatformCapabilityCheckClient zeigt auf
quarkus.rest-client.platform-service.url — einen lokalen Platzhalter. Im Test ersetzt
AllowAllCapabilityCheckClient den echten Client; geprüft wird der Pfad durch das Gate, nicht der
Remote-Check.
Ebenso offen: welcher IdP für eine Pilotpraxis gilt. Der Compose-Stack enthält keinen Keycloak.
Von drei Schritten existiert nur der dritte — es fehlt ein CapabilityRegistrar (2026-08-09)
GG-GOV-SECURITY-0003 nennt die Reihenfolge ausdrücklich bindend: registrieren → granten →
erzwingen. Dieses Repo macht nur das Erzwingen. Es gibt keinen CapabilityRegistrar, keinen
PlatformCapabilityAdminClient und keinen ServiceTokenClient — die 14 txm.*-Capabilities sind
nirgends deklariert, sie existieren ausschließlich als Zeichenketten in require(...)-Aufrufen.
Kein Katalog kennt sie, also kann sie auch niemand granten.
Das ist der Unterschied, der beim Vergleich mit einem vollständig aufgesetzten Zwirn-Produkt sofort
auffällt (dkfz-prozesswerkstatt/eventmanagement, dl-controlling/dot und Zwirns eigene
Docker-Services haben alle drei Klassen). Es ist aber keine übersehene Lücke, sondern eine Folge
genau dieser offenen Entscheidung: ein Registrar registriert bei einer Authority, und welche das
ist, ist nicht entschieden. Ohne Ziel gibt es nichts zu registrieren.
Mit der Entscheidung fällig, als Paket und nicht einzeln:
CapabilityRegistrar(@Observes StartupEvent, Retry im Daemon-Thread — der Start darf nicht blockieren, weil die Authority später hochkommen kann), idempotenter Upsert je Capability,PlatformCapabilityAdminClient(Upsert-Endpunkt) +ServiceTokenClient/KeycloakTokenRestClient(client-credentials — der Registrar hat kein User-JWT zum Durchreichen),allowedActorsje Capability. Für dieses Produkt die inhaltlich wichtigste Zeile: Freigabe, Übersteuerung und Therapeutenentscheidung sindUSER-only.GM-7steht und fällt damit, dass diese drei nie an einenAGENTgrantbar sind — heute ist das nur dadurch garantiert, dass die MCP-Tools ausnahmslos lesend sind, nicht durch eine deklarierte Actor-Einschränkung.
Das fehlende Bauteil heißt Capability-Authority — und ist seit 2026-08-09 eine Zwirn-Regel
Der Abschnitt darüber beschreibt richtig, was fehlt (Registrar, Admin-Client, Service-Token-Client),
sagt aber nicht, wogegen das registrieren würde. Genau diese Lücke hat Zwirn aus diesem Befund heraus
geschlossen: GG-ARCH-SERVICES-0005
(Zwirn-ADR-0027, Herkunft
ist ausdrücklich dieser tuxametrics-Befund).
Die Regel verlangt je Produkt genau eine benannte Authority, in einer von zwei Formen:
| Form | Was zu bauen/tun wäre | Vorbild |
|---|---|---|
eigen — ein tuxametrics-platform-service |
Tenant + Capability + CapabilityGrant, GET /api/platform/v1/capabilities/{key}/check, in-process-Registrar. Ausdrücklich nicht DOTs Umfang (72 Dateien mit Agent-Sessions, Keycloak-Client-Provisioning, Profilen, OPA, Outbox) |
prozesswerkstatt-platform-service — 34 Java-Dateien, der belegte Minimalbauplan |
| fremd — die Authority eines Plattform-Repos desselben Kunden konsumieren | Kein eigener Service; nur Registrar + Check-Client, dafür ein schriftlicher Check-Vertrag auf der Gegenseite | eventmanagement-anmeldung-service → prozesswerkstatt-platform-service, Vertrag in CAPABILITY_CHECK_CONTRACT.md |
Ein dritter Fall — Platzhalter-URL auf einen Dienst, den es nicht gibt — ist keine Form, sondern ein offener Punkt. Genau dafür existiert dieses Dokument.
Für ein Produkt mit Gesundheitsdaten hat die Formwahl eine fachliche Folge, die über Infrastruktur
hinausgeht: -0005.2 verlangt allowedActors je Capability im Katalog. Das ist der einzige Ort, an dem
sich „Freigabe, Übersteuerung und Therapeutenentscheidung sind USER-only" deklarieren und damit
gegen einen AGENT-Grant absichern lässt. Heute ist das nur dadurch garantiert, dass die MCP-Tools
ausnahmslos lesend sind — eine Eigenschaft des Codes, keine Zusage. Bei einer fremden Authority
bedeutet das: der Katalog dort muss allowedActors führen, sonst ist GM-7 nicht durchsetzbar und die
Form scheidet aus. Das gehört in die Entscheidung, nicht danach.
Warum das trotzdem kein Regelverstoß ist: -0005.5 macht die Authority erst vor dem ersten Nutzer,
der nicht der Entwickler ist fällig. Bis dahin genügt ein auf das dev-Profil begrenzter Stub plus ein
benannter Backlog-Punkt — beides ist gegeben (siehe nächster Abschnitt; dev/dev-auth-stub.mjs existiert
im gebauten Artefakt und im Compose-Stack nicht). Mit der ersten Pilotpraxis ist die Frist abgelaufen.
Warum es passiert ist — nicht Nachlässigkeit dieses Repos: Zwirns
samples/sample-data-service-quarkus/.../capability/ liefert nur die Konsumentenseite
(PlatformCapabilityCheckClient, PlatformCapabilityRestClient) — keinen CapabilityRegistrar, keinen
Admin-Client, keinen ServiceTokenClient. Wer das Sample korrekt kopiert, bekommt genau Schritt 3 von
dreien und hat keinen Anlass zu merken, dass zwei fehlen. Verstärkt dadurch, dass GG-GOV-SECURITY-0003
durchgehend dot-platform-service beim Namen nennt — das liest sich ohne DOT-Kenntnis wie ein
plattformweit vorhandener Dienst statt wie DOTs lokale Implementierung. Zwirn-ADR-0027 führt das
Sample-Nachziehen als Folgearbeit mit dem höchsten Hebel.
Was seit dem Frontend-Start daran hängt (2026-08-09)
Damit tuxametrics-ui im Browser bedienbar ist, gibt es jetzt einen lokalen Stub für genau diese
beiden offenen Punkte: dev/dev-auth-stub.mjs. Er signiert ein JWT mit
dem Claim-Satz aus TestToken.java, veröffentlicht den passenden öffentlichen Schlüssel als JWKS und
antwortet auf jeden Capability-Check mit allowed: true.
Er greift ausschließlich im Quarkus-dev-Profil (%dev-Block in application.properties) und im
Vite-Dev-Server; im gebauten Artefakt und im Compose-Stack existiert er nicht. Er ist kein
Keycloak-Realm und keine Vorentscheidung — er spricht kein OIDC-Protokoll, kennt keinen Realm und
kein Rechtemodell.
Was mit dieser Entscheidung zu löschen ist: dev/, der %dev-Block in application.properties,
der /dev-auth-Proxy in vite.config.ts und tuxametrics-ui/src/shell/DevAuthProvider.tsx
(ersetzt durch AuthProvider + createOidcConfig aus @zwirn/web, eine Zeile in main.tsx).
Nachtrag 2026-08-10 — bewusst anders gemacht als hier vorgesehen. Der Tausch in
main.tsxist tatsächlich eine Zeile geworden (AuthShell), und ein gebautes Artefakt nimmt immer OIDC — genau das verlangt-0005.5. Gelöscht wurde der Stub trotzdem nicht. Begründung: seine Bedeutung hat sich geändert, nicht sein Nutzen. Er war der Ersatz für eine offene Entscheidung; jetzt ist er die Abkürzung für einen Dev-Loop ohne laufende Infrastruktur — der Unterschied zwischennpm run devin fünf Sekunden und einem Kaltstart von 15 Containern. Das ist ein echter Nutzen, und-0005.5verbietet ihn nicht: verboten ist der Stub „im gebauten Artefakt oder im Compose-Stack", und dort ist er nach wie vor nicht.Damit die Abkürzung keine zweite Wahrheit wird, hängt der Default an
import.meta.env.PRODund nicht an einer Variable, die jemand vergisst — undnpm run dev:oidcfährt denselben Dev-Server gegen die echte Kette. Ebenso neu:%dev.tuxametrics.capability.registrar.aktiv=false, weil der Stub keinen Token-Endpunkt hat und der Registrar sonst zwei Minuten lang vergeblich anklopfen würde.
Warum das nicht nebenbei entschieden werden sollte
Bei einem Produkt mit Gesundheitsdaten ist die Autorisierung keine Infrastrukturfrage. Zwei Punkte aus dem Fachmaterial hängen direkt daran:
OF-12— ist der Mandant die Praxis oder der Therapeut? Davon hängt ab, ob Therapeut B die Patienten von Therapeut A sieht. Ein Prototyp mit einem Therapeuten umgeht die Frage; die erste Praxis mit zwei Therapeuten nicht.OF-62— die Zweckbindung der Kennzahlen muss im Datenmodell erzwungen werden, nicht nur über Rechte. Die Auswertung von Übersteuerungen dient der Regelverbesserung, „nicht der Bewertung einzelner Therapeuten". Eine Capability allein setzt das nicht durch.
Nächster Schritt — erledigt am 2026-08-10
Die drei Punkte, die hier standen, sind abgearbeitet:
- ✅ Wahl eigen, mit Namen und Port in
service-landscape.mdeingetragen — die Zeile „Autorisierungs-Authority: Platzhalter" ist ersetzt, nicht ergänzt. - ✅ Klassen-Paket gebaut (Registrar + Admin-Client + Service-Token-Client) in beiden Konsumenten. Ein
Check-Vertrag der Gegenseite entfällt bei eigener Authority — stattdessen ist der eigene Vertrag
schriftlich, weil zwei Zwirn-Fremd-Images (
zwirn-audit-service,zwirn-feedback-service) ohne Einblick in diesen Code dagegen bauen:CAPABILITY_CHECK_CONTRACT.md. - ⚠️
docker compose buildnicht verifiziert — siehe „Was offen bleibt".
Es gibt keine „Zwirn-Plattform-Authority" zum Mitbenutzen — das war eine Fehlannahme einer früheren
Fassung dieses Absatzes. Zwirn liefert die Konsumentenseite (zwirn-securitys CapabilityGuard) und
das Muster, aber keinen betriebenen Dienst; jede Authority gehört einem Produkt.
Was offen bleibt
-
docker compose build/ Vollstart nicht ausgeführt. Compose-Datei, Dockerfiles und Realm-Import sind geschrieben und in sich konsistent, aber ein Kaltstart der jetzt 15 Container ist nicht gelaufen. Das ist eine Verifikationslücke, keine bekannte Fehlfunktion — sie gehört vor den ersten Einsatz geschlossen. - Erste echte Nutzer und Grants. Der Stack ist bewusst fail-closed: kein Keycloak-Nutzer, kein
Grant für einen Menschen. Der manuelle Weg steht in
tuxametrics-platform-service/README.md, Abschnitt „Erste Grants". Ein Self-Service-Bootstrap für Menschen gibt es bewusst nicht. - Ein Service-Account statt drei.
tuxametrics-serviceswird von drei Services genutzt; alle teilenactorKeyund damit Grants. DEV-Vereinfachung, für echten Betrieb nach Zwirn-ADR-0039 (dedizierte Service-Actors je Tenant) aufzuteilen. -
IdentityStatusGuardnicht verdrahtet. Eine deaktivierte agentische Identität verliert ihre Grants nicht automatisch (Zwirn-ADR-0008, strikt opt-in, in keinem Produkt verdrahtet). - OPA-Container ist Altbestand. Er ist an der Capability-Entscheidung nicht beteiligt
(
GG-ARCH-SERVICES-0005: „Kein OPA-Zwang"). Ihn zu entfernen ist eine eigene kleine Entscheidung inkl..env-Variable und Bundle-Pipeline. -
OF-62bleibt unberührt. Die Zweckbindung der Kennzahlen muss im Datenmodell erzwungen werden, nicht über Rechte — eine Capability allein setzt das nicht durch. Sieheisolationsstufe-gesundheitsdaten.md,-0008.6.