06 runtime view - smart-village-solutions/sva-studio GitHub Wiki
Dieser Abschnitt beschreibt kritische Laufzeitszenarien und Interaktionen.
- Mindestens 3 kritische End-to-End-Szenarien
- Sequenz der beteiligten Bausteine pro Szenario
- Fehler- und Ausnahmeverhalten für kritische Flows
- App lädt
getRouter()inapps/sva-studio-react/src/router.tsx - Core-Route-Factories werden client- oder serverseitig geladen
- Der Host liest die statische Plugin-Liste und materialisiert Plugin-Routen aus
PluginDefinition - Core-/Auth-Runtime-Routen und Plugin-Routen werden zu einem gemeinsamen Route-Tree kombiniert
- Router wird mit RouteTree und SSR-Kontext erstellt
Fehlerpfad:
- Fehlerhafte Route-Factory oder server-only Import im Client kann Build/Runtime brechen.
- Plugin-Routen außerhalb
/plugins/<pluginNamespace>oder mit unbekanntem Guard werden vor Veröffentlichung des Route-Trees mit deterministischem Guardrail-Code abgewiesen.
- Die App übergibt statische Plugin-Packages an
createBuildTimeRegistry(). - Das Plugin-SDK führt die bestehende Registry-Erzeugung in festen Phasen aus: Preflight, Content, Admin, Audit, Permissions, Routing und Publish.
- Jede Phase erzeugt die bisherigen
BuildTimeRegistry-Outputs; bestehende Consumer müssen keinen neuen Snapshot-Typ verwenden. - Erlaubte UI-Komponenten und host-invoked Payload-Validatoren bleiben im Snapshot erhalten.
- Verbotene Felder wie eigene Route-Handler, Autorisierungsresolver, Audit-Sinks, Persistenzhandler oder dynamische Registrierung brechen die Initialisierung fail-fast ab.
Fehlerpfad:
- Der Host veröffentlicht keinen teilweise materialisierten Plugin-Snapshot.
- Die Fehlermeldung folgt
<guardrailCode>:<pluginNamespace>:<contributionId>:<fieldOrReason>. - Plugin-Routen, Navigation oder Actions mit produktiven
content.*-Guards, fremden Namespaces oder nicht registrierten Permission-IDs brechen den Snapshot vor der Route-Materialisierung ab.
- Die App lädt neben Seiten-Bindings auch die statische Liste
appAdminResources. -
@sva/routingvalidiert die Admin-Ressourcen gegen den Plugin-SDK-Vertrag und materialisiert daraus Listen-, Create- und Detailrouten. - Der Host wendet den deklarativ referenzierten Guard auf alle Teilrouten der Ressource an.
- Legacy-Pfade wie
/content,/content/newund/content/$contentIdwerden im Routing-Layer auf/admin/content*umgeleitet.
Fehlerpfad:
- Doppelte Ressourcen-IDs oder kollidierende Basispfade brechen die Registrierungsphase fail-fast ab.
- Ohne gültige Ressourcendefinition wird kein teilweise inkonsistenter Admin-Route-Baum veröffentlicht.
- Die App initialisiert
studioPluginsund merged Plugin-Übersetzungen in die i18n-Ressourcen. - Der Router materialisiert die Plugin-Routen für News, Events und POI, zum Beispiel
/plugins/news,/plugins/eventsund/plugins/poi. - Beim Aufruf der Route wendet der Host den registrierten Plugin-Guard an, zum Beispiel
news.read,events.readoderpoi.read. - Die Fachlisten rufen ihre Host-Fassaden auf:
/api/v1/mainserver/news,/api/v1/mainserver/eventsoder/api/v1/mainserver/poi; lokale IAM-Contents werden nicht mehr produktiv gelesen. - Die Editoren senden Create-, Update- und Delete-Requests an die jeweilige Fassade und Detailroute.
- Die App-Fassade prüft Session,
instanceId, plugin-spezifische IAM-Permission und Mainserver-Credentials serverseitig. -
@sva/sva-mainserver/serverführt typisierte GraphQL-Operationen für News, Events und POI mit Benutzer-Credentials aus. - News nutzt das vollständige Mainserver-Modell mit dedizierten Feldern; Events und POI nutzen eigene Mapping-Adapter für Termine, Adressen, Kontakte, URLs, Medien, Preise, Barrierefreiheit, Tags und POI-Bezug.
- Es gibt keinen Dual-Write und keine Legacy-Migration in lokale IAM-Contents.
- Nach erfolgreichem Speichern oder Löschen zeigt das Plugin Statusfeedback und navigiert zurück zur News-Liste.
Fehlerpfad:
- fehlt die Berechtigung, blendet die Shell Plugin-Navigation fail-closed aus, blockiert der Host die Plugin-Route vor dem Rendern oder verweigert die serverseitige Mutation mit
capability_authorization_deniedim Diagnosekontext. - ist das News-Input-Modell ungültig, enthält schreibgeschützte Felder oder fehlt
publishedAt, antwortet die Mainserver-News-Fassade mit HTTP400. - schlägt ein API-Call fehl, zeigt das Plugin eine verständliche Fehlermeldung und behält den Formzustand.
- Die App lädt das statisch registrierte Plugin und validiert dessen Routen, Admin-Ressourcen und Guard-Metadaten über
@sva/plugin-sdk. - Der Host materialisiert die Plugin-Route unter
/plugins/<pluginNamespace>und bettet sie in die normale App-Shell ein. - Die Plugin-Komponente rendert ihre fachliche Oberfläche mit
@sva/studio-ui-react-Bausteinen für Seitenstruktur, Formularfelder, Aktionen, Tabellen und Lade-/Fehlerzustände. - Fachliche Datenzugriffe laufen über hostkontrollierte HTTP- oder Server-Funktionsverträge; die Custom-View erhält keine eigenen Host-Handler, Audit-Sinks oder Persistenzpfade.
- Die App- und Plugin-Lint-/Boundary-Checks verhindern App-interne UI-Imports und lokale Basis-Control-Duplikate in Plugin-Packages.
Fehlerpfad:
- Importiert ein Plugin App-interne Komponenten, bricht ESLint oder der Plugin-UI-Boundary-Check mit Verweis auf
@sva/studio-ui-reactab. - Definiert ein Plugin eigene wiederverwendbare Basiscontrols für Button, Input, Tabelle, Tabs, Dialog oder Alert, wird der Beitrag als UI-Drift behandelt und muss in einen fachlichen Wrapper um Studio-Primitives geändert werden.
- Versucht eine Custom-View Shell, Guard, Route-Materialisierung oder Persistenz zu übernehmen, greift der bestehende Plugin-Guardrail-Pfad fail-fast.
- Request trifft mit Host-Header auf die Runtime.
- Middleware klassifiziert Root-Host, Tenant-Host oder ungültigen Host.
- Tenant-Hosts werden über die Instanz-Registry aufgelöst.
- Nur
active-Instanzen erhalten Traffic. - Unbekannte, suspendierte und archivierte Hosts werden identisch fail-closed beantwortet.
Fehlerpfad:
- Registry-Eintrag fehlt oder ist nicht traffic-fähig -> identische fail-closed-Antwort.
- Browser ruft
/auth/loginauf -
loginHandler()erstellt PKCE-LoginState, setzt signiertes State-Cookie und redirectet zum IdP - IdP redirectet nach
/auth/callback?code=...&state=... -
callbackHandler()validiert State, tauscht Code gegen Tokens und erstellt eine versionierte Session mitissuedAt,expiresAtundsessionVersion- Bei Tenant-Hosts wird
instanceIdaus dem zuvor aufgelösten Auth-Scope aus Host, Registry und Realm in den Session-User übernommen. - Ein fehlender
instanceId-Claim blockiert den Tenant-Login nicht; ein widersprüchlicher Claim beendet den Callback fail-closed als Scope-Konflikt.
- Bei Tenant-Hosts wird
- Session-Cookie wird mit expliziter Laufzeit aus
expiresAtgesetzt; Redis-TTL wird technisch aus der Restlaufzeit plus Puffer abgeleitet - App ruft
/auth/mefuer minimalen Auth-Kontext (id,instanceId, Rollen) - Falls UI Profildaten wie Name oder E-Mail braucht, laedt sie diese ueber dedizierte Profil-Endpunkte getrennt nach
Fehlerpfad:
- Fehlender/abgelaufener State -> Redirect mit Fehlerstatus
- Token-/Refresh-Fehler -> Session invalidiert oder unauthorized Antwort
- Profilfehler beruehren die Session-Hydration nicht; die App behaelt ihren minimalen Auth-State
- Host-/Realm-/Claim-Konflikte erzeugen keinen tenant-losen Fallback, sondern bleiben als Auth-Fehler sichtbar.
- Admin öffnet
/admin/instancesauf dem Root-Host. - UI lädt
GET /iam/instances. - Das Detail lädt zusaetzlich Preflight, Plan, Status und vorhandene Provisioning-Runs.
-
Instanzdaten speichernsendet CSRF-Header, Idempotency-Key und Reauth-Bestaetigung und schreibt nur Registry-Daten. -
Provisioning ausfuehrenoderReconcilestartet einen expliziten Run mit Realm-Modusnewoderexisting; der validierteIdempotency-Keywird zusammen mit Mutation und stabilem Payload-Fingerprint persistent dedupliziert. -
packages/auth-runtimedelegiert an die gemeinsame Provisioning-Fassade inpackages/instance-registry. - Die Fassade provisioniert getrennt Login-Client (
authClientId) und Tenant-Admin-Client (tenantAdminClient.clientId) inklusive separater Secret-Aufloesung. - Die Fassade persistiert Run, Schritte und Audit-Event und invalidiert anschliessend betroffene Host-Caches.
Fehlerpfad:
- Tenant-Host statt Root-Host ->
403 forbidden. - fehlende Re-Authentisierung ->
403 reauth_required. - blockierter Preflight oder Plan -> kein Keycloak-Mutationslauf.
- wiederholter Keycloak-Request mit identischem
Idempotency-Keyund identischer stabiler Payload -> kein zweiter Run; abweichende Payload im selben Scope ->409 idempotency_key_reuse. - fehlt nur der Tenant-Admin-Client, darf Reconcile gezielt
provision_admin_clientnachziehen, ohne den Login-Pfad zu veraendern.
-
AuthProviderruft/auth/meauf und erhält401. - Das Frontend startet genau einen stillen Recovery-Versuch über
/auth/login?silent=1in einem versteckten iframe. -
loginHandler()setztprompt=noneund verwendet weiterhinstate,nonceund PKCE. -
callbackHandler()antwortet im Silent-Fall mit einer iframe-sicheren HTML-Response statt mit einem normalen Redirect. - Bei Erfolg lädt das Frontend
/auth/meerneut und übernimmt den aktualisierten Sessionzustand. - Bei Fehlschlag bleibt der Benutzer ausgeloggt und muss aktiv den regulären Login starten.
Fehlerpfad:
- Browser-/IdP-Cookies verhindern Silent SSO -> Recovery endet ohne Schleife im ausgeloggten Zustand.
- Ein expliziter Logout blockiert den automatischen Silent-Recovery-Pfad zeitlich begrenzt.
- Ein Request trifft auf Tenant-Host oder Root-Host ein.
- Hostvalidierung und Registry-Auflösung entscheiden, ob der Request fail-closed abgewiesen oder weiterverarbeitet wird.
- Auth- und Session-Schicht prüfen Cookie, Session-Store, Session-Hydration und optional Token-Refresh.
- IAM-nahe Handler klassifizieren Actor-, Membership-, Keycloak-, DB- oder Schema-Probleme und erzeugen allowlist-basierte Details.
- Browserpfade lesen Fehlercode,
requestIdund freigegebene Detailfelder. - UI und Betrieb sollen daraus künftig denselben Diagnosekern ableiten, auch wenn die konkrete Formulierung kontextabhängig bleibt.
Fehlerpfad:
- Recovery-Pfade wie Silent-Recovery, Session-Hydration oder Host-Fallbacks können Symptome kurzfristig überdecken; der degradierte Zustand muss daher für Diagnose und Folgeentscheidungen erhalten bleiben.
- Runtime-IAM-Fehler und Instanz-/Provisioning-Drift dürfen nicht in getrennten Diagnosewelten landen.
- Ein Administrator startet in
/admin/usersden Keycloak-User-Sync oder in/admin/rolesden Rollen-Reconcile. - Der Server unterscheidet Root-Host-Platform-Scope und Tenant-Instance-Scope. Im Platform-Scope nutzt er den Plattform-Realm ohne
instanceId; im Tenant-Scope lädt er den Instanzkontext und prüft vor jeder tenantlokalen Admin-Mutation blockerrelevanten Drift aus Registry, Preflight und Provisioning-Plan. - Beim Keycloak-User-Sync ist der aktive Tenant-Realm die führende Benutzergrenze; fehlende
instanceId-Attribute blockieren den Import nicht. - Liegt ein Blocker vor, endet der Lauf sofort fail-closed mit technischem Fehlervertrag inklusive
classification,requestIdund freigegebenen Safe-Details. - Ohne Blocker führt
packages/iam-adminden Sync oder Reconcile deterministisch aus und trennt pro Eintrag zwischen korrigiert, fehlgeschlagen und fachlichem Restzustandmanual_review. - Die Handler antworten immer mit genau einem Abschlusszustand
success,partial_failure,blockedoderfailedsowie aggregierten Zählwerten. - Read-Pfade für Profil, User-Liste und Rollenansicht laden anschließend denselben kanonischen Projektionskern nach, damit UI und Fachzustand übereinstimmen.
Fehlerpfad:
- fehlender Tenant-Admin-Client, Secret-Drift oder blockierter Provisioning-Plan verhindern den Start des Laufs vollständig.
-
IDP_FORBIDDENundIDP_UNAVAILABLEbleiben als technische oder Berechtigungsfehler sichtbar und werden nicht alsmanual_reviewkaschiert. - einzelne fachlich mehrdeutige Fälle können in
manual_reviewenden, ohne dass der Gesamt-Request hängen bleibt.
-
/admin/usersund/admin/rolesladen Listen über den aktiven Keycloak-Admin-Pfad. - Im Platform-Scope wird nur der Platform-Admin-Keycloak-Client verwendet.
- Im Tenant-Scope wird nur der Tenant-Admin-Keycloak-Client der Instanz verwendet; fehlt dieser, endet der Request mit
tenant_admin_client_not_configured. - Tenant-Userlisten lesen den vollständigen Realm-Ausschnitt aus Keycloak und verbinden ihn anschließend mit Studio-Read-Models.
- Keycloak-Objekte ohne Studio-Zuordnung bleiben als
unmappedodermanual_reviewsichtbar. - Mutierende Aktionen schreiben zuerst Keycloak, synchronisieren anschließend Studio-Read-Models und erzeugen Audit-Events.
- Read-only- oder blockierte Objekte werden in der UI mit Diagnosecode angezeigt und serverseitig erneut vor der Mutation geprüft.
Fehlerpfad:
- Keycloak
403wird alsIDP_FORBIDDENbeziehungsweiseidp_forbiddeneingeordnet. - föderierte oder profilrichtliniengeschützte Felder werden als
read_only_federated_fieldsichtbar und nicht überschrieben. - verbotene Rollenzuordnungen werden als
forbidden_role_mappingsichtbar. - Built-in-Rollen bleiben als Rollenobjekt read-only, dürfen aber abhängig von der aktiven Rechte-Matrix zugewiesen oder entfernt werden.
- Ein interner Serverpfad ruft
forceReauthUser({ userId, mode, reason })auf. - Der Auth-Server erhöht
minimumSessionVersion, setztforcedReauthAtund invalidiert bekannte Studio-Sessions des Benutzers. - Bei
app_and_idpbeendet der Keycloak-Admin-Client zusätzlich aktive IdP-Sessions des Benutzers. - Nachfolgende Requests mit älteren Sessions schlagen bei der Session-Auflösung fehl.
- Das Frontend erhält dadurch spätestens beim nächsten
/auth/meoder geschützten Request einen unauthentifizierten Zustand.
Fehlerpfad:
- Bei
app_onlykann eine vorhandene Keycloak-Session einen nachfolgenden interaktiven Login ohne Passwort erlauben. - Bei
app_and_idpist eine echte Re-Authentifizierung erforderlich.
- Server-Code loggt via
createSdkLogger(...) - Context (workspace/request) wird über AsyncLocalStorage injiziert
- In Development schreiben Console- und Dev-UI-Transport die redaktierten Logs sofort lokal aus
- Sobald OTEL bereit ist, werden bestehende Logger um den Direct-OTEL-Transport erweitert
- OTEL Processor redacted und filtert Labels
- Export via OTLP an Collector -> Loki/Prometheus
Fehlerpfad:
- Development ohne OTEL-Readiness: Console und Dev-Konsole bleiben aktiv, die App bleibt lauffähig
- Production ohne OTEL-Readiness: der Start gilt als Fehlerzustand und wird fail-closed behandelt
- Eine Auth- oder IAM-Route wirft in
packages/routing/src/auth.routes.server.tseinen unerwarteten Fehler. - Die äußere JSON-Error-Boundary liest
X-Request-Idundtraceparentbest effort aus den Request-Headern. - Der SDK-Logger schreibt einen strukturierten Fehler mit
request_id,trace_id,route,method,error_typeunderror_message. - Die Response wird über
toJsonErrorResponse()als JSON mit flachem Fehlervertrag und HeaderX-Request-Idzurückgegeben.
Fehlerpfad:
- Sind Header ungültig oder fehlen sie, bleiben
request_idundtrace_idleer; die Response bleibt trotzdem JSON. - Schlägt der Logger selbst fehl, schreibt die Routing-Schicht einen sanitisierten Minimal-Eintrag auf
stderr.
- Ein Operator startet
pnpm env:release:studio:localfuer einen konkreten Digest. -
environment-precheckliest den Live-Stack bevorzugt ueber die Portainer-API und vergleicht Soll-/Ist-Drift fuerapp. -
image-smokeprueft Root-Host, Tenant-Hosts und OIDC-Verhalten prod-nah gegen das Zielartefakt. - Wenn derselbe Digest bereits live laeuft, darf der Gate-Schritt die Live-Paritaet nur wiederverwenden, wenn Ingress-Konsistenz, Tenant-Auth-Proof, Runtime-Flags und
app-db-principalfuer genau dieses Digest gruen sind. - Erst danach folgen optional
migrateundbootstrap, dann der eigentliche Live-Rollout. -
internal-verify,smokeundprecheckbestaetigen den Zustand erneut aus Sicht der laufenden App.
Fehlerpfad:
- Weicht der Root-/Tenant-/OIDC-Vertrag ab, blockiert der Rollout vor jeder Live-Mutation.
- Ist
/health/readyaus Sicht vonAPP_DB_USERnicht stabil, gilt der Stack auch bei gruener Superuser-Sicht als nicht freigegeben. - Manueller Incident-Recovery ueber Portainer oder Quantum bleibt temporaer; abgeschlossen ist der Fall erst nach kanonischem
app-only-Reconcile und erneut gruener Verifikation.
- Root-Shell rendert initial in einem kurzen Loading-Zustand
-
Headerzeigt Skeleton für Auth-Aktion in der Kopfzeile -
Sidebarzeigt Skeleton-Navigation -
AppShellzeigt Skeleton-Platzhalter im Contentbereich - Nach Abschluss des initialen Zustands wird auf regulären Inhalt gewechselt
Fehlerpfad:
- Falls Route-/Inhaltsdaten verzögert verfügbar sind, bleibt die Shell strukturell stabil (kein Layout-Springen), bis regulärer Inhalt rendert.
- Client ruft
POST /iam/authorizemitinstanceId,action,resourceund optionalem ABAC-Kontext auf;GET /iam/me/permissionsnutzt denselben Snapshot-Pfad optional mitorganizationId,geoUnitIdundgeoHierarchy. - Server erzwingt Instanzgrenze und wertet Hard-Deny-Regeln zuerst aus.
- Permission-Snapshot wird zuerst im lokalen L1-Cache und danach in Redis über User-/Instanz-/Org-/Geo-Kontext gesucht.
- Bei Cache-Hit wertet die Engine die Entscheidung in fester Reihenfolge aus: RBAC-Basis, danach ABAC-Regeln und Hierarchie-Restriktionen.
- Bei Miss, Stale oder Integritätsfehler erfolgt Recompute aus Postgres als fachlicher Quelle; ein erfolgreicher Recompute schreibt zuerst Redis und danach den L1-Cache.
- Bei Redis- oder Recompute-Fehler im sicherheitskritischen Pfad greift Fail-Closed mit HTTP
503und Fehlercodedatabase_unavailable.
Fehlerpfad:
- Eventverlust bei Invalidation: TTL begrenzt die Stale-Dauer; ein stale Snapshot darf bei technischem Fehler nicht fachlich weiterverwendet werden.
- DB-Ausfall ohne nutzbaren Snapshot:
503 database_unavailable.
- Client ruft
POST /iam/governance/workflowsmitoperation,instanceIdundpayloadauf. - Server validiert Instanzscope, Ticketstatus und Vier-Augen-Regeln.
- Workflow-Status wird in Governance-Tabellen persistiert (Request, Delegation, Impersonation, Legal-Text-Akzeptanz).
- Sicherheitsrelevante Schritte erzeugen Dual-Write-Audit-Events (
iam.activity_logs+ SDK-Logger/OTEL). - Bei Acting-As-Zugriff prüft
POST /iam/authorizeaktive, nicht abgelaufene Impersonation. - Compliance-Nachweis wird über
GET /iam/governance/compliance/exportin CSV/JSON/SIEM exportiert.
Fehlerpfad:
- Ticket fehlt oder ist ungültig: Denial mit Governance-Reason-Code.
- Self-Approval: Aktion wird fail-closed abgewiesen.
- Impersonation abgelaufen: Session wird als
expiredmarkiert, Acting-As wird verweigert.
- Request trifft auf dem Root-Host ein und wird als
scope_kind=platformklassifiziert. - Der Auth-Resolver lädt den Plattform-Auth-Kontext ohne Tenant-Fallback-Instanz.
- Login, Logout und Silent-Reauth emittieren operative Logs mit
workspace_id=platform,reason_code,request_idundtrace_id. - DB-Audit wird in
iam.platform_activity_logspersistiert. - Optionale Audit-Fehler bleiben non-blocking; die Auth-Antwort wird nur bei fachlichem Scope- oder Provider-Fehler fail-closed.
- Änderung an Rollen, Permission-Zuordnung oder Policy wird in Postgres persistiert.
- Writer emittiert ein Invalidation-Ereignis über
NOTIFYmiteventId,instanceIdund betroffenem Scope. - Der Autorisierungspfad prüft zuerst den lokalen L1-Snapshot und danach Redis als Shared-Read-Path.
- Cache-Worker in
packages/auth-runtimeempfängt das Event, dedupliziert pereventIdund invalidiert passende Redis-Snapshots gezielt perkeycloakSubjectoder instanzweit. - Nachfolgende
POST /iam/authorize-Aufrufe erzwingen Recompute für invalidierte Einträge und schreiben zuerst Redis, danach den L1-Cache. - Invalidation, Recompute, Cold-Start und Degraded-State werden mit
request_id/trace_idstrukturiert geloggt.
Fehlerpfad:
- Event kommt verspätet oder gar nicht an: TTL begrenzt die Stale-Dauer, ein stale Snapshot darf nach Recompute-Fehler aber nicht fachlich weiterverwendet werden.
- Redis-Lookup, Snapshot-Write oder Recompute schlagen fehl: der Entscheidungspfad bleibt fail-closed mit HTTP 503.
- Invalidation schlägt fehl:
cache_invalidate_failedwird geloggt; der Readiness-Status kann aufdegradedoderfailedkippen.
Referenzen:
apps/sva-studio-react/src/router.tsxapps/sva-studio-react/src/routes/__root.tsxpackages/auth-runtime/src/runtime-routes.tspackages/iam-core/src/index.tspackages/iam-admin/src/index.tspackages/iam-governance/src/index.tspackages/server-runtime/src/index.tspackages/monitoring-client/src/otel.server.tsdocs/architecture/iam-service-architektur.md
- User meldet sich über
/auth/loginund/auth/callbackan. -
handleCallback()erstellt Session und triggertjitProvisionAccount(...). - Account wird per
INSERT ... ON CONFLICT (keycloak_subject, instance_id)idempotent angelegt/aktualisiert. - Erstanlage wird als
user.jit_provisionedauditierbar protokolliert. - User oeffnet
/account, Profil wird ueberGET /api/v1/iam/users/me/profilegeladen. - Aenderungen werden ueber
PATCH /api/v1/iam/users/me/profilegespeichert.
Fehlerpfad:
- JIT-Fehler blockiert den Login nicht, wird aber strukturiert geloggt.
- Profil-Update ohne gueltigen CSRF-Header wird serverseitig abgewiesen.
- Session und Autorisierung bleiben auch bei temporaer nicht verfuegbaren Profildaten stabil, da Name/E-Mail nicht Teil des Session-Kerns sind.
-
system_admin/app_manageröffnet/admin/users. - Liste wird paginiert über
GET /api/v1/iam/usersgeladen. - Bearbeitung erfolgt in
/admin/users/$userIdper Tabs undPATCH /api/v1/iam/users/$userId. - Rollen-Änderungen triggern Permission-Invalidierung über
pg_notify. -
system_adminverwaltet Custom-Rollen auf/admin/rolesmitPOST/PATCH/DELETE /api/v1/iam/roles. - Auf Tenant-Hosts löst der Backend-Service den Adminpfad strikt aus
iam.instances.authRealmplustenantAdminClient.clientIdund tenantlokalem Admin-Secret auf und führt Rollen- und Nutzer-CRUD Keycloak-First innerhalb desselben Tenant-Realms aus. - Root-/Plattform-Pfade verwenden einen separaten Plattform-Admin-Client nur für Instanz-Provisioning, Reconcile und explizites Break-Glass.
- Nach erfolgreichem Tenant-Sync schreibt der Service das lokale IAM-Mapping.
- Bei Erfolg werden
role.sync_succeededundrole.created|updated|deletedauditierbar protokolliert. - Bei Fehlern werden
sync_state,last_error_code, Metriken undrole.sync_failedaktualisiert.
Fehlerpfad:
- Nicht autorisierte Rollen werden via Route-Guard umgeleitet.
- Last-Admin-/Self-Protection wird serverseitig mit Konfliktantwort geschützt.
- Fehlen tenantlokaler Admin-Client oder tenantlokales Secret, schlagen Tenant-Mutationen fail-closed mit
tenant_admin_client_not_configuredodertenant_admin_client_secret_missingfehl. - Schlägt der DB-Schritt nach erfolgreichem Keycloak-Write fehl, läuft eine Compensation; misslingt auch diese, bleibt der Vorgang als
COMPENSATION_FAILEDsichtbar.
- Ein Admin ruft
POST /api/v1/iam/users/sync-keycloakauf. - Der Service löst den fachlichen Ziel-Realm pro Instanz aus
iam.instances.authRealmauf und verwendet fuer normale Tenant-Syncs ausschliesslich den tenantlokalen Adminpfad austenantAdminClient. - Der Import lädt Keycloak-Benutzer seitenweise und projiziert sie deterministisch nach
iam.accountsundiam.instance_memberships. - Läuft der Import bereits gegen einen instanzspezifischen Realm, werden Benutzer ohne explizites
instanceId-Attribut trotzdem dem aktiven Instanzkontext zugeordnet. - Nicht passende Benutzer werden nur bei aktivem Debug-Level begrenzt geloggt; das Log enthält
subject_ref,user_instance_idundexpected_instance_id. - Die API-Antwort und das Summary-Log enthalten knappe Diagnostik zum verwendeten Realm, zur Provider-Quelle, zum
executionModeund zu übersprungenen Instanz-IDs.
Fehlerpfad:
- Bei großen Batches bleiben Detail-Logs gecappt; die Diagnose erfolgt dann primär über das Summary-Log und die additiven Sync-Diagnosefelder.
- Ein fehlender tenantlokaler Adminpfad wird nicht mehr durch einen globalen Fallback kaschiert.
-
system_admintriggertPOST /api/v1/iam/admin/reconcileoder der Scheduler startet den Lauf überIAM_ROLE_RECONCILE_INTERVAL_MS. - Der Service lädt studio-verwaltete Rollen aus
iam.rolesund den aktuellen Realm-Rollenbestand aus Keycloak. - Fehlende Keycloak-Rollen werden erstellt, abweichende Beschreibungen oder Anzeigenamen werden aktualisiert.
- Orphaned, studio-markierte Keycloak-Rollen werden nur als
requires_manual_actiongemeldet. - Das Ergebnis wird als Report zurückgegeben, über Audit-Events geschrieben und über
iam_role_drift_backlogmessbar gemacht.
Fehlerpfad:
- Fehlt die Keycloak-Verbindung oder der Service-Account hat zu wenige Rechte, endet der Lauf mit
keycloak_unavailable. - Einzelne Rollen können im Report als
failedauftauchen, ohne den gesamten Drift-Kontext zu verlieren.
- Routing oder ein externer Konsument importiert eine stabile Zielpackage-Fassade wie
@sva/auth-runtime/server,@sva/iam-admin,@sva/iam-governanceoder@sva/instance-registry. - Die Fassade delegiert in fachliche Bausteine des jeweiligen Zielpackages.
- Der Fachbaustein orchestriert Request-Handling, Authentifizierungskontext, Autorisierung und Response-Mapping über injizierte Runtime-Dependencies.
- Alte Sammelpackage-Fassaden bleiben nur als Kompatibilitätsadapter bestehen und begründen keine neue fachliche Ownership.
Fehlerpfad:
- Bleibt Restkomplexität im
core.tsbestehen, wird sie überQUAL-*-Tickets im Complexity-Gate nachverfolgt und nicht stillschweigend toleriert.
-
system_adminoder berechtigterapp_manageröffnet/admin/organizations. - Die UI lädt
GET /api/v1/iam/organizationsund erhält ein instanzgebundenes Read-Model mit Parent-, Typ- und Zählerdaten. - Beim Anlegen oder Bearbeiten sendet die UI
POSToderPATCH /api/v1/iam/organizations/:organizationId. - Der Server validiert Instanzscope, Parent-Bezug, Zyklusfreiheit, CSRF-Contract und Deaktivierungsregeln.
- Bei Erfolg schreibt der Service Organisationsdaten, emittiert Audit- und Betriebslogs und liefert das aktualisierte Read-Model zurück.
- Membership-Änderungen laufen über die dedizierten Membership-Endpunkte und aktualisieren anschließend die Detailansicht.
Fehlerpfad:
- Parent aus fremder Instanz oder Zyklusversuch führt zu einer deterministischen Konflikt- oder Validierungsantwort.
- Deaktivierung mit aktiven Children oder Memberships wird fail-closed abgewiesen.
- Die Shell lädt
GET /api/v1/iam/me/contextund erhält aktiven Kontext plus zulässige Organisationsoptionen. - Der Org-Switcher rendert die Optionen nur für aktive Mitgliedschaften und kündigt den aktuellen Zustand über eine Live-Region an.
- Beim Wechsel sendet die UI
PUT /api/v1/iam/me/contextmit der gewähltenorganizationId. - Der Server validiert CSRF-Contract, Session, Instanzscope, Membership und Aktivstatus der Zielorganisation.
- Bei Erfolg wird der aktive Kontext serverseitig in der Session aktualisiert und ein Audit-/Betriebsereignis für
organization_context_switchederzeugt.
- Ein Release-Workflow baut genau ein
linux/amd64-Image und ermittelt den Manifest-Digest. -
Studio Image Verifystartet exakt dieses Image isoliert im Runner und prüft/health/live,/health/readyund/. - Der lokale Operator-Einstieg
env:release:studio:localfuehrt danachenv:precheck:studio,env:deploy:studioundenv:smoke:studiogegen denselben Digest aus;env:precheck:studiodokumentiert zusätzlich, ob ein passendesStudio Image Verify-Artefakt fuer diesen Digest vorliegt. - Nach optionaler Migration wird der Stack aktualisiert.
-
internal-verifykombiniert interne HTTP-Probes gegen den App-Service mitdoctor-Diagnostik. -
external-smokeprüft öffentliche URL, Health-Pfade, Auth-Entry und IAM-Kontext. - Erst danach wird eine technische
release-decisionerzeugt und als Artefakt persistiert.
Fehlerpfad:
- Fehlschlag vor dem Rollout bleibt auf
config,imageodermigrationklassifiziert. - Fehlschlag nach erfolgreichem Stack-Update, aber vor öffentlicher Verifikation, bleibt als
healthoderingresssichtbar und wird nicht als erfolgreicher Release bewertet. - Fehlende Image-Verify-Evidenz fuer einen expliziten Digest ist mindestens ein Warnsignal im Precheck und darf nicht als stiller Erfolgsfall verschwinden.
- Nachgelagerte UI- und Backend-Pfade lesen den aktiven Organisationskontext aus dem kanonischen Sessionzustand.
-
system_adminöffnet/admin/groups. - Die UI lädt
GET /api/v1/iam/groupsund erhält instanzgebundene Gruppen inklusive Rollenbündeln und Mitgliederzahl. - Beim Anlegen oder Bearbeiten sendet die UI
POSToderPATCH /api/v1/iam/groups/:groupIdmitgroupKey,displayName, optionaler Beschreibung undroleIds. - Der Server validiert Instanzscope, CSRF, Idempotency und dass alle referenzierten Rollen in derselben Instanz existieren.
- Bei Erfolg persistiert der Service
iam.groupsundiam.group_roles, schreibt Audit-/Betriebslogs und invalidiert Permission-Snapshots überpg_notify. -
DELETE /api/v1/iam/groups/:groupIddeaktiviert die Gruppe fail-closed statt sie physisch zu löschen.
Fehlerpfad:
- Unbekannte oder instanzfremde Rollen führen zu
invalid_request. - Fehlende Admin-Rolle oder deaktiviertes IAM-Admin-Feature führt zu
forbiddenoderfeature_disabled. - Datenbankfehler werden als
database_unavailablebzw.internal_errornach außen stabilisiert.
- Ein Admin öffnet
/admin/users/:userIdund lädtGET /api/v1/iam/users/:userId. - Die Detailansicht zeigt direkte Rollen, Gruppenmitgliedschaften, deren Herkunft (
manual|seed|sync) und Gültigkeitsfenster. - Beim Speichern sendet die UI
PATCH /api/v1/iam/users/:userIdadditiv mitgroupIds. - Der Backend-Service validiert alle Gruppen im aktiven
instanceId-Scope und ersetzt die aktiven Einträge iniam.account_groups. - Anschließend wird ein
user_group_changed-Invalidation-Event emittiert; der nächsteGET /iam/me/permissions- oderPOST /iam/authorize-Aufruf recomputet den Snapshot. - Transparenzansichten zeigen die daraus abgeleiteten Rechte mit
sourceRoleIds,sourceGroupIdsund Provenance der Quelle an.
Fehlerpfad:
- Nicht existente Gruppen oder instanzfremde IDs werden mit
invalid_requestabgewiesen. - Läuft die Invalidation nicht sofort durch, begrenzen TTL und Recompute den Stale-Zeitraum fail-closed.
- Ein Admin öffnet
/admin/users/:userIdund wechselt in den TabBerechtigungen. - Die UI lädt den globalen Permission-Katalog sowie die direkten Nutzerrechte aus
GET /api/v1/iam/users/:userId. - Pro Permission wird eine direkte Wirkung
nicht gesetzt,allowoderdenygewählt und mitPATCH /api/v1/iam/users/:userIdgespeichert. - Der Server validiert die referenzierten
permissionIds instanzgebunden und ersetzt die aktiven Einträge iniam.account_permissions. - Anschließend wird ein
user_permission_changed-Invalidation-Event emittiert; der nächsteGET /iam/me/permissions- oderPOST /iam/authorize-Aufruf recomputet den Snapshot. -
me/permissionsundauthorizeliefern die Quelle alsdirect_user; direktedeny-Einträge schlagen konfliktäre Allows aus Rollen oder Gruppen deterministisch.
Fehlerpfad:
- Unbekannte Permissions oder doppelte Zuordnungen im Payload werden mit
invalid_requestabgewiesen. - Fehlt der Admin-Kontext oder ist die Zielperson außerhalb des zulässigen Manage-Scope, endet der Vorgang fail-closed mit
forbidden. - Reine Nutzerrechte-Änderungen schreiben nur Studio-IAM-Daten und lösen keinen Keycloak-Write aus.
- Client oder interne Serverlogik ruft
POST /iam/authorizemitinstanceId,action,resourceund optionalcontext.attributes.geoHierarchybzw.resource.attributes.geoUnitIdauf. - Der Server lädt effektive Permissions aus direkten Rollen und gruppenvermittelten Rollen und normalisiert
sourceKinds. - Die Engine prüft zuerst Instanzscope und Hard-Deny-Regeln, danach passende RBAC-Kandidaten für
action,resourceType,resourceIdund Organisationshierarchie. - Für Geo-Scopes wertet sie
allowedGeoUnitIdsgegengeoHierarchybzw.geoUnitIdaus; Parent-Allows dürfen auf Children vererben. -
restrictedGeoUnitIdswerden mit derselben Hierarchie aufgelöst; ein spezifischerer Child-Deny schlägt den Parent-Allow deterministisch. - Die Antwort enthält neben
allowedundreasonauchdiagnostics.stagesowie Provenance-Felder wieinheritedFromGeoUnitIdoderrestrictedByGeoUnitId.
Fehlerpfad:
- Fehlen erforderliche Geo-Attribute trotz
requireGeoScope, wird der Kandidat verworfen und die Entscheidung endet fail-closed. -
instanceId-Mismatch führt immer zuinstance_scope_mismatch, bevor weitere Scope- oder Rollenregeln ausgewertet werden.
Fehlerpfad:
- Ungültige oder deaktivierte Zielorganisationen liefern einen stabilen Fehlercode; der bisherige Kontext bleibt unverändert.
- Technische Fehler werden im Org-Switcher verständlich, internationalisiert und ohne inkonsistenten Zwischenzustand angezeigt.
- Ein dedizierter Runner validiert Pflicht-Env, Testrealm und Testbenutzer gegen Keycloak.
- Vor dem Lauf werden Acceptance-spezifische IAM-Datensätze und Organisationsartefakte in der Testumgebung kontrolliert zurückgesetzt.
- Der Runner prüft
GET /health/readyfail-closed auf Datenbank, Redis und Keycloak. - Browsergestützte OIDC-Logins validieren
/auth/me, Claims und JIT-Provisioning. - API- und UI-Smokes prüfen Organisations-CRUD, Membership-Zuweisung und Sichtbarkeit in den Admin-Oberflächen.
- Der Lauf schreibt einen versionierten JSON-/Markdown-Bericht nach
docs/reports/.
Fehlerpfad:
- Fehlende Pflicht-Env oder fehlende Testbenutzer beenden den Lauf vor dem Browserstart.
- Nicht bereite Dependencies oder fehlerhafte Laufzeitnachweise erzeugen deterministische Failure-Codes im Bericht.
- Ein berechtigter Studio-Benutzer löst eine serverseitige Mainserver-Funktion aus.
- Die App prüft lokal Rollen und aktiven
instanceId-Kontext, bevor ein Upstream-Call gestartet wird. -
@sva/sva-mainserver/serverlädt die aktive Endpunktkonfiguration für die Instanz ausiam.instance_integrations. -
@sva/auth-runtime/serverliestmainserverUserApplicationIdundmainserverUserApplicationSecretaus Keycloak-User-Attributen des aktuellen Benutzers; Legacy-Attribute werden nur noch als Fallback berücksichtigt. - Die Integrationsschicht fordert per OAuth2-Client-Credentials ein Access-Token an und cached es kurzlebig pro
(instanceId, keycloakSubject, apiKey). - Danach wird der GraphQL-Request serverseitig an den SVA-Mainserver gesendet;
request_idundtrace_idwerden als Korrelation weitergereicht. - Die Server-Funktion gibt ein kuratiertes Diagnose-Read-Model an die App zurück; Credentials oder rohe Upstream-Fehlerdetails verlassen den Server nicht.
Fehlerpfad:
- Fehlende lokale Studio-Berechtigung blockiert den Aufruf vor dem Upstream-Zugriff.
- Fehlende Keycloak-Attribute liefern einen stabilen Fehlerzustand
missing_credentials. -
401/403vom Mainserver werden in deterministische Integrationsfehler übersetzt; Netzwerk- oder Tokenfehler bleiben fail-closed.
-
POST /iam/authorizeoderGET /iam/me/permissionslöst bei Cache-Miss den Permission-Store aus. - Der Store lädt strukturierte Permission-Felder (
action,resource_type,resource_id,effect,scope) zusammen mit Rollen- und Membership-Kontext. - Bei org-spezifischen Anfragen werden Parent-Mitgliedschaften über
hierarchy_pathdes Zielkontexts aufgelöst. - Die Engine prüft zuerst Matching von
action,resource_typeund optionalerresource_id. - Danach werden
deny-Permissions vorallow-Permissions ausgewertet; lokale Restriktionen können vererbte Parent-Freigaben blockieren. - Anschließend werden ABAC-Attribute wie Geo-Scope, Acting-As und Restriktionslisten gegen den Requestkontext ausgewertet.
- Das Ergebnis wird als effektiver Permission-Snapshot mit Scope-Daten gecacht.
Fehlerpfad:
- Fehlen strukturierte Felder noch in Alt-Daten, greift der Kompatibilitätspfad über
permission_key. - Widersprechen
allowunddeny, gewinnt deterministisch die restriktivere Regel.
- Admin öffnet
/admin/iam?tab=rights|governance|dsroder Benutzer/account/privacy. - Die Route validiert und kanonisiert den Tab über Search-Parameter; unzulässige Tabs werden per
replaceauf den ersten erlaubten Tab umgelenkt. - Nur der aktive Tab lädt Daten: Rights über
GET /iam/me/permissions, Governance überGET /iam/governance/workflows, DSR überGET /iam/admin/data-subject-rights/cases, Self-Service überGET /iam/me/data-subject-rights/requests. - User-Historie unter
/admin/users/:userIdlädt die vereinte Actor+Target-Timeline überGET /api/v1/iam/users/:userId/timeline. - Die UI rendert nur normalisierte Read-Modelle; Rohstatus oder Diagnosefelder bleiben sekundär und allowlist-basiert.
Fehlerpfad:
- Fehlende Rollen blockieren die Route oder den Tab fail-closed.
- Bei 403 auf Transparenz-Reads invalidiert die UI den Session-/Permission-Kontext.
Hinweis: Dieser Abschnitt beschreibt den Soll-Zustand. Die vollständige Verdrahtung als zentraler Request-Guard (403 + Kontext-Propagation) ist als Folgearbeit geplant.
- Eingehende Anfrage trifft Traefik, wird über
HostRegexpan den App-Service geroutet. - App extrahiert den Host-Header und normalisiert ihn (Lowercase, Port-Stripping, Trailing-Dot).
- Host wird gegen die Parent-Domain und die zentrale Instanz-Registry geprüft:
- Root-Domain → Kanonischer Auth-Host,
instanceId = null - Gültige Instanz-Subdomain →
instanceIdaus Subdomain abgeleitet - Ungültiger oder unbekannter Host →
403mit identischem Body ({ error, message }+X-Request-Id)
- Root-Domain → Kanonischer Auth-Host,
- Bei Auth-Endpunkten auf Instanz-Hosts: fail-closed, Redirect zum kanonischen Auth-Host.
- Gültige
instanceIdwird im Request-Kontext propagiert (analog zuworkspace_idin AsyncLocalStorage), sobald der zentrale Request-Guard verdrahtet ist.
Fehlerpfad:
- Bei fehlender
SVA_PARENT_DOMAIN(Entwicklungsmodus) wird die Host-Validierung übersprungen. - Bei lokalen oder migrationsbezogenen Fallback-Pfaden bricht die App bei ungültigen Einträgen in
SVA_ALLOWED_INSTANCE_IDSweiterhin fail-fast ab.