0TL;DR — die Kernentscheidungen
| Frage | Empfehlung |
|---|---|
| Homeserver | Ein gemeinsamer Synapse für alle Tenants, Föderation deaktiviert. Getrennte Homeserver nur, wenn Compliance es später erzwingt. |
| Raum-Bindung | Ein Raum pro Vorgang (requestId), nicht pro Berater+Kunde-Paar. |
| Raum-Erstellung | Der Orchestrator (Bot mit Power Level 100) erstellt Räume — nie die Nutzer selbst. |
| Orchestrator | Eigener, kleiner Service — weil mehrere Produkte den Chat nutzen sollen: ein Integrationspunkt für alle, statt Modul-Kopien in jedem Backend. Bewusst schmal gehalten (5 Endpunkte, nie im Nachrichtenpfad). |
| Login | Token-Exchange: Keycloak-Access-Token rein → Matrix-Access-Token raus (Orchestrator nutzt Synapse-Admin-API, provisioniert User beim ersten Mal). Ein Code-Pfad für Standalone und Embedded. |
| Berechtigungen | Keycloak sagt, wer jemand ist (Rolle). Matrix Power Levels sagen, was er im Raum darf. Der Orchestrator übersetzt einmalig beim Erstellen/Einladen. Durchsetzung passiert serverseitig in Synapse, nicht in der UI. |
| Kunden-Schutz | Kunde = Power Level 0, invite erfordert PL 50, Räume sind invite-only. Damit sind Einladen/Moderieren auch per direkter API unmöglich. |
| Tenant-Isolation | Tenant-Präfix in Matrix-IDs (@acme.max:…), Orchestrator prüft Tenant bei jeder Aktion, User-Directory-Suche aus, plus kleines Synapse-Modul als harte Leitplanke. |
| Spaces | Später Optional — für das MVP nicht nötig, die Raumliste kommt aus dem Sync bzw. dem Vorgangskontext. |
| E2EE | Bewusst aus im MVP: Support-/Audit-Fähigkeit, Bot-Zugriff und Einfachheit wiegen hier schwerer. Explizite Entscheidung, siehe §9. |
1Technische Komponenten
Es braucht genau vier Bausteine — drei davon existieren schon oder sind Standardsoftware. Neu gebaut wird nur die Web-Component und das Orchestrator-Modul.
flowchart TB
subgraph clients ["Clients"]
SA["Standalone-Chat
(statische Shell-Seite
+ Web-Component)"]
EMB["Bestehendes Produkt
(Web-Component
eingebettet)"]
end
subgraph products ["Produkt-Backends (mehrere)"]
P1["Produkt A
(Vorgänge / Requests)"]
P2["Produkt B
(Vorgänge / Requests)"]
end
ORCH["Chat-Orchestrator
(eigener kleiner Service, neu)"]
P1 -- "REST + Service-Token:
Request angenommen" --> ORCH
P2 -- "REST + Service-Token" --> ORCH
KC["Keycloak
(vorhanden)
Realm pro Tenant"]
SYN["Synapse Homeserver
(einer für alle Tenants,
keine Föderation)"]
PG[("PostgreSQL")]
PGO[("PostgreSQL
(Mapping + Standalone-Vorgänge)")]
LK["LiveKit
(vorhanden, Video)"]
SA -- "OIDC-Login" --> KC
EMB -. "hat bereits
Keycloak-Session" .-> KC
SA -- "Keycloak-Token" --> ORCH
EMB -- "Keycloak-Token" --> ORCH
ORCH -- "validiert Token
(JWKS)" --> KC
ORCH -- "Admin-API: User anlegen,
Räume erstellen, einladen,
Matrix-Token ausstellen" --> SYN
SA -- "Matrix Client-Server-API
(sync, senden, lesen)" --> SYN
EMB -- "Matrix Client-Server-API" --> SYN
SYN --- PG
ORCH --- PGO
ORCH -. "LiveKit-JWT
(später)" .-> LK
| Komponente | Was | Status |
|---|---|---|
| Synapse | Matrix-Homeserver, ein Deployment, Föderation aus, PostgreSQL. Erreichbar unter z. B. matrix.example.com (eine Domain für alle Tenants). | Standardsoftware, nur konfigurieren |
| Keycloak | Bleibt einzige Identitätsquelle. Pro Tenant ein Realm (passt zum Subdomain-Modell). Drei neue Realm-Rollen, sonst keine Änderung. | Vorhanden |
| Chat-Orchestrator | Eigener kleiner Service mit eigener DB. Übersetzt Produkt-Events + Keycloak-Rollen in Matrix-Operationen. Einziger Ort mit Synapse-Admin-Rechten; gemeinsamer Andockpunkt für alle Produkte. | Neu (klein) |
| Web-Component | Ein Custom Element (z. B. Lit + matrix-js-sdk). Wird standalone in einer statischen Shell-Seite und embedded im Produkt verwendet — derselbe Code. | Neu |
| LiveKit | Bleibt wie es ist. Einzige Berührung: der Orchestrator stellt LiveKit-JWTs aus und nutzt die Matrix-Room-ID als LiveKit-Raumname. | Vorhanden, Randthema |
2Die zwei Modi: Standalone & Embedded
Kernidee: Es gibt keinen Unterschied im Chat-Stack zwischen den Modi. Der einzige Unterschied ist, woher das Keycloak-Token kommt und welche Räume angezeigt werden.
Standalone-Modus
- Statische Seite pro Tenant-Subdomain (z. B.
chat.acme.example.com), die nur die Web-Component hostet. - Die Shell macht selbst den OIDC-Redirect-Login gegen das Realm des Tenants (aus der Subdomain abgeleitet).
- Danach: Keycloak-Token → Orchestrator → Matrix-Token (wie embedded).
- Raumliste = alle Räume aus dem Matrix-Sync. Kein Produkt nötig.
- Ohne bestehenden Vorgang: Button „Neue Anfrage“ → Orchestrator legt einen leichtgewichtigen Vorgang an + Raum, Berater-Seite sieht ihn in einer Eingangs-Ansicht und nimmt ihn an.
Embedded-Modus
- Produkt bindet
<chat-widget>ein und übergibt eine Token-Callback (das Produkt hat die Keycloak-Session bereits). - Widget zeigt genau den Raum zum aktuellen Vorgang (
request-id-Attribut) — oder die Raumliste des Nutzers, je nach Einbettungsort. - Kein zweiter Login, kein iframe-Redirect-Gefrickel: das Token-Exchange ist ein einfacher XHR-Call.
<chat-widget
orchestrator-url="https://chat-api.example.com"
request-id="4711">
</chat-widget>
widget.getKeycloakToken = () =>
keycloak.token; // Host-App liefert
m.room.power_levels ablesbar, kein eigenes Rechte-Modell im Frontend).
3Rollenmodell & Keycloak
Drei Realm-Rollen genügen. Der Tenant ist implizit durch das Realm (Subdomain) gegeben und wird als Claim mitgegeben — kein eigenes Tenant-Attribut pro User nötig.
| Keycloak-Rolle | Wer | Darf (fachlich) |
|---|---|---|
chat-user | Kunden / Gäste (echte Keycloak-User mit wenig Rechten) | In eigenen Vorgangs-Räumen lesen und schreiben. Sonst nichts: kein Einladen, kein Raum erstellen, keine Moderation, keine Nutzersuche. |
chat-berater | Berater | Alles von chat-user, plus: Vorgänge annehmen (→ Raum entsteht), weitere Berater in eigene Räume holen, Nachrichten löschen (redact), Kunden aus dem Raum entfernen. |
chat-berater-admin | Berater-Admins | Alles von chat-berater, plus: Berater auch in fremde Räume des Tenants setzen, Räume schließen/archivieren. |
Token-Claims
Der Orchestrator braucht aus dem Access-Token nur:
{
"sub": "8f3a…", // stabile User-ID → Basis der Matrix-ID
"preferred_username": "m.mustermann",
"iss": "https://auth.example.com/realms/acme", // → Tenant "acme"
"realm_access": { "roles": ["chat-berater"] }
}
4Übersetzung: Keycloak-Rolle → Matrix-Rechte
Die Übersetzung passiert an genau zwei Stellen: beim Token-Exchange (User-Provisionierung) und beim Erstellen/Einladen in Räume (Power Level setzen). Danach arbeitet Matrix autark.
| Keycloak | Matrix-Mechanismus | Wert |
|---|---|---|
chat-user (Kunde) | Power Level im Raum | 0 — Nachrichten senden ja, sonst nichts |
chat-berater | Power Level im Raum | 50 — invite, kick, redact |
chat-berater-admin | Kein höherer PL! Admin-Aktionen laufen über den Orchestrator-Bot | 50 im Raum; Sonderrechte nur via Orchestrator-API |
| Orchestrator-Bot | Power Level | 100 — Raum-Owner, setzt State, lädt ein, archiviert |
| Mitgliedschaft im Vorgang | m.room.member (Invite durch Bot) | Nur wer fachlich zum Vorgang gehört, wird eingeladen |
| „Wer darf rein?“ | m.room.join_rules | invite — niemand kann selbst joinen |
| Verlauf für später hinzugezogene Berater | m.room.history_visibility | shared — Übergabe/Vier-Augen funktioniert mit vollem Kontext |
| Matrix-„Guests“ | m.room.guest_access + Serverconfig | forbidden / global deaktiviert. Unsere Gäste sind echte Keycloak-User — das Matrix-Guest-Feature wird nicht benutzt. |
Rollenänderungen
Verliert ein User die Berater-Rolle, wird das beim nächsten Token-Exchange erkannt; der Orchestrator senkt dann seine Power Levels bzw. entfernt ihn aus Räumen (MVP: lazy beim Login; später: Keycloak-Event-Listener/Webhook für sofortige Wirkung).
5Raum-Template & Power-Level-Konzept
Jeder Raum hängt am Vorgang, nicht am Personen-Paar. Alias-Schema: #<tenant>.req-<requestId>:matrix.example.com. Der Orchestrator merkt sich nur das Mapping requestId ↔ roomId — mehr Schatten-Datenhaltung braucht es nicht.
requestId statt Berater+Kunde?
- Übergabe: Beraterwechsel = Membership-Änderung, der Raum (und die Historie) bleibt.
- Mehrere Berater am selben Fall sind trivial — beim Personen-Paar-Modell bräuchte man dafür neue Räume.
- Audit: Die gesamte Kommunikation zu einem Fall ist an einem Ort.
- Mehrere Fälle desselben Kunden bleiben sauber getrennt.
- Ein Kunde mit zwei Vorgängen hat zwei Räume — das ist gewollt, nicht redundant.
createRoom-Template (vom Bot ausgeführt)
{
"preset": "private_chat",
"room_alias_name": "acme.req-4711",
"name": "Baufinanzierung #4711",
"creation_content": { "m.federate": false },
"initial_state": [
{ "type": "m.room.join_rules", "content": { "join_rule": "invite" } },
{ "type": "m.room.history_visibility", "content": { "history_visibility": "shared" } },
{ "type": "m.room.guest_access", "content": { "guest_access": "forbidden" } },
{ "type": "com.example.request", "content": { "tenant": "acme", "requestId": "4711" } }
],
"power_level_content_override": {
"users": { "@chat-bot:matrix.example.com": 100,
"@acme.b.schmidt:matrix.example.com": 50 },
"users_default": 0,
"events_default": 0, // PL 0 darf Nachrichten senden → Kunde kann schreiben
"state_default": 100, // Raum-Einstellungen nur der Bot
"invite": 50, "kick": 50, "ban": 50, "redact": 50,
"events": {
"m.room.name": 100, "m.room.topic": 100,
"m.room.avatar": 100, "m.room.power_levels": 100
}
},
"invite": [ "@acme.b.schmidt:matrix.example.com",
"@acme.k.mueller:matrix.example.com" ]
}
Das Custom-State-Event com.example.request verankert den fachlichen Kontext im Raum selbst — nützlich für das Widget (Titelzeile, Deep-Link zum Vorgang) und für Betrieb/Debugging.
Relevante Matrix-Mechanismen im Überblick
| Mechanismus | Rolle im Konzept |
|---|---|
| Power Levels | Das eigentliche Berechtigungssystem im Raum. Serverseitig durchgesetzt — gilt für jede API-Nutzung, nicht nur fürs eigene Frontend. |
Join Rules (invite) | Niemand betritt einen Raum ohne Einladung. Einladungen kann nur PL ≥ 50 (Berater/Bot) aussprechen. |
| History Visibility | shared: hinzugezogene Berater sehen den bisherigen Verlauf. |
| Room Aliases | Deterministische Adresse pro Vorgang, Idempotenz bei Raum-Erstellung. |
| Custom State Events | Fachlicher Kontext (Tenant, requestId) direkt am Raum. |
| Admin-API / Login-as-User | Orchestrator provisioniert User und stellt Matrix-Tokens aus, ohne Passwörter. |
| Spam-Checker-Modul-API | Serverweite Leitplanken (wer darf Räume erstellen / einladen), siehe §9. |
| Spaces | Später Optionale Gruppierung „alle Räume eines Tenants“ für Berater-Übersichten. Kein Sicherheitsfeature — Isolation läuft nicht über Spaces. |
| Föderation | Aus Geschlossenes System, m.federate: false + Serverconfig. |
6Der Chat-Orchestrator
Der Orchestrator ist die einzige Stelle, an der Produkte, Keycloak und Matrix zusammenkommen. Er ist bewusst klein: fünf Endpunkte, ein Bot-User, zwei DB-Tabellen.
tenant + requestId + Teilnehmer. Er hält eine eigene kleine PostgreSQL-DB mit zwei Tabellen: room_mapping (requestId ↔ roomId) und standalone_requests (die leichtgewichtigen Vorgänge aus dem Standalone-Modus). Mehr nicht — sonst entsteht ein zweites Fachbackend.
Anbindung der Produkte (Machine-to-Machine)
Jedes Produkt-Backend erhält einen Keycloak-Service-Account (Client-Credentials-Flow) und ruft den Orchestrator per REST auf — z. B. beim „Request angenommen“. Der Orchestrator prüft am Service-Token, welches Produkt und welcher Tenant aufruft. Kein Event-Bus, kein Message-Broker: ein synchroner, idempotenter REST-Call reicht für diese Frequenz völlig; das Produkt kann ihn bei Fehlern einfach wiederholen.
Aufgaben & API
| Endpunkt | Auth | Funktion |
|---|---|---|
POST /api/chat/token | Keycloak-Bearer | Token-Exchange: validiert das Keycloak-Token (JWKS), provisioniert den Matrix-User beim ersten Mal (@<tenant>.<username>:…), stellt via Admin-API ein Matrix-Access-Token aus. Antwort: { mxid, accessToken, homeserverUrl }. |
POST /api/chat/rooms | Service-Token (Produkt-Backend) | Idempotent: Raum zu requestId erstellen oder zurückgeben (Alias-Lookup). Wird vom Produkt beim „Request annehmen“ aufgerufen. |
POST /api/chat/rooms/{requestId}/participants | Berater / Berater-Admin | Weiteren Berater einladen. Prüft: Aufrufer ist Berater desselben Tenants und Mitglied des Raums (oder Berater-Admin). Bot lädt ein und setzt PL 50. |
GET /api/chat/colleagues | Berater | Berater-Verzeichnis des Tenants für den Einladen-Dialog (via Keycloak-Admin-API nach Rolle chat-berater gefiltert — die Matrix-Nutzersuche bleibt aus, und es braucht kein Produkt dafür). |
POST /api/chat/rooms/{requestId}/close | Berater-Admin | Vorgang abgeschlossen: Bot kickt Teilnehmer bzw. archiviert. |
Später POST /api/chat/rooms/{requestId}/call-token — LiveKit-JWT für Videocall, Raumname = Matrix-Room-ID, Berechtigung = Matrix-Mitgliedschaft. Mehr Kopplung braucht LiveKit nicht.
Was der Orchestrator bewusst nicht tut: Nachrichten proxien (der Chat-Traffic läuft direkt Client ↔ Synapse — der Orchestrator ist nie im heißen Pfad) und Fachlogik abbilden (Vorgänge gehören den Produkten).
7Die konkreten Flows
7.1 Login / Token-Exchange (beide Modi)
sequenceDiagram
autonumber
participant U as Browser (Widget)
participant KC as Keycloak
participant O as Orchestrator
participant S as Synapse
alt Standalone
U->>KC: OIDC-Redirect-Login (Realm aus Subdomain)
KC-->>U: Access-Token
else Embedded
Note over U: Produkt hat Keycloak-Session,
liefert Token per Callback
end
U->>O: POST /api/chat/token (Bearer: Keycloak-Token)
O->>O: Token validieren (JWKS),
Tenant + Rollen extrahieren
opt Erster Login
O->>S: Admin-API: User @acme.k.mueller anlegen
end
O->>S: Admin-API: Access-Token für User ausstellen
S-->>O: Matrix-Access-Token
O-->>U: { mxid, accessToken, homeserverUrl }
U->>S: /sync — Chat läuft ab jetzt direkt
7.2 Berater nimmt Vorgang an → Raum entsteht
sequenceDiagram
autonumber
participant B as Berater (Produkt-UI)
participant P as Produkt-Backend
participant O as Orchestrator (Service)
participant S as Synapse
B->>P: Request #4711 annehmen
P->>P: Fachliche Zuweisung
P->>O: POST /api/chat/rooms (Service-Token)
{ requestId, beraterId, kundeId }
O->>S: Alias #acme.req-4711 auflösen
alt Raum existiert noch nicht
O->>S: Bot: createRoom (Template §5)
PLs: Bot 100, Berater 50, Kunde 0
O->>O: Mapping requestId ↔ roomId speichern
end
O->>S: Bot: invite Berater + Kunde
S-->>B: Raum erscheint im Sync des Beraters
Note over S: Kunde sieht den Raum beim
nächsten Öffnen des Chats
7.3 Weiterer Berater wird hinzugefügt
sequenceDiagram
autonumber
participant B as Berater A (Widget)
participant O as Orchestrator
participant S as Synapse
participant B2 as Berater B
B->>O: GET /api/chat/colleagues (Einladen-Dialog)
O-->>B: Berater-Liste des Tenants
B->>O: POST /rooms/4711/participants { userId: B2 }
O->>O: Prüfen: A ist chat-berater, gleicher Tenant,
Mitglied im Raum (oder berater-admin)
O->>S: Bot: invite @acme.b2 + Power Level 50 setzen
S-->>B2: Einladung im Sync → Auto-Join im Widget
Note over B2: Sieht dank history_visibility=shared
den bisherigen Verlauf
Der Kunde hat diesen Dialog nie: das Widget blendet ihn bei PL < 50 aus, der Orchestrator lehnt den Call ohne Berater-Rolle ab, und Synapse würde einen direkten /invite mit PL 0 ohnehin mit 403 beantworten — dreifach dicht, entscheidend sind die beiden Server-Schichten.
7.4 Standalone ohne bestehendes Produkt
sequenceDiagram
autonumber
participant K as Kunde (Standalone-Chat)
participant O as Orchestrator
participant S as Synapse
participant B as Berater (Standalone/Produkt)
K->>O: „Neue Anfrage“ (Thema optional)
O->>O: Leichtgewichtigen Vorgang anlegen
(Status: offen, kein Berater)
O->>S: Bot: Raum erstellen, Kunde einladen
Note over B: Eingangs-Ansicht zeigt offene Anfragen
(Orchestrator-Query, kein Matrix nötig)
B->>O: Anfrage annehmen
O->>S: Bot: Berater einladen, PL 50
K-->>B: Chat läuft — identisch zu 7.2
So bleibt die Regel „Räume hängen an Vorgängen“ auch ohne Produkt erhalten — der Vorgang ist dann eben minimal (ID, Tenant, Status, Thema). Wächst später ein Produkt darum, ist nichts umzubauen.
8Durchsetzung: Warum Kunden auch per API nichts können
UI-Ausblenden ist Komfort, keine Sicherheit. Die Durchsetzung hat drei Schichten — die untersten zwei sind serverseitig und gelten für jeden API-Client, auch für einen Kunden mit curl und seinem eigenen Matrix-Token.
| Schicht | Mechanismus | Verhindert |
|---|---|---|
| 1 — Widget (Komfort) | Buttons nur bei ausreichendem Power Level rendern | Verwirrung, nicht Missbrauch |
| 2 — Synapse Power Levels & Join Rules | invite/kick/ban/redact ≥ 50, State ≥ 100, Räume invite-only | Einladen, Moderieren, Raum-Manipulation, Fremd-Join — per jeder API |
| 3 — Synapse-Modul (Leitplanke) | Kleines Python-Modul über die Spam-Checker-Hooks:user_may_create_room → nur der Botuser_may_invite → nur Bot; zusätzlich: Einlader- und Eingeladenen-MXID müssen denselben Tenant-Präfix tragen | Kunden erstellen eigene Räume; Cross-Tenant-Einladungen; Umgehung des Orchestrators |
| flankierend | user_directory.search_all_users: false, Presence aus, Föderation aus, Rate-Limits, Matrix-Guest-Accounts deaktiviert | Ausspähen anderer Nutzer/Tenants |
9Multi-Tenancy & Isolation
flowchart LR
subgraph acme ["Tenant acme — acme.example.com"]
KCA["Keycloak-Realm acme"]
UA["@acme.k.mueller
@acme.b.schmidt"]
end
subgraph beta ["Tenant beta — beta.example.com"]
KCB["Keycloak-Realm beta"]
UB["@beta.k.weber
@beta.b.krause"]
end
subgraph syn ["Ein Synapse — matrix.example.com"]
RA["#acme.req-4711
#acme.req-4712"]
RB["#beta.req-0815"]
MOD["Synapse-Modul:
Invites nur innerhalb
desselben Tenant-Präfixes"]
end
UA --> RA
UB --> RB
UA -. "✗ blockiert" .-x RB
MOD --- RA
MOD --- RB
Ein Homeserver oder einer pro Tenant?
| Gemeinsamer Synapse Empfehlung | Synapse pro Tenant | |
|---|---|---|
| Betrieb | 1 × deployen, upgraden, monitoren, backuppen | n × alles; Provisionierung neuer Tenants wird ein Projekt |
| Isolation | Logisch (Präfixe + Modul + invite-only). Für B2B-SaaS üblich und ausreichend. | Physisch — nur nötig bei harten Compliance-Vorgaben (eigene DB/Keys pro Kunde) |
| Kosten | Eine kleine Instanz trägt viele Tenants | Grundlast pro Tenant |
| Später wechselbar? | Ja — weil MXIDs Tenant-Präfixe tragen und der Orchestrator die einzige Admin-Schnittstelle ist, kann ein einzelner Tenant später auf eine eigene Instanz migriert werden, ohne das Konzept zu ändern. | |
Isolation konkret:
- Identität: MXID =
@<tenant>.<user>:matrix.example.com; Tenant kommt aus dem Realm (iss-Claim), nie vom Client. - Räume: Alias-Präfix pro Tenant; Tenant zusätzlich als State-Event im Raum.
- Zugriff: invite-only + Invites nur durch Bot/Berater + Modul-Check „gleicher Präfix“ → ein Cross-Tenant-Kontakt ist strukturell nicht herstellbar.
- Sichtbarkeit: keine öffentliche Raumliste, keine globale Nutzersuche, kein Presence.
- Orchestrator: prüft bei jedem Call, dass Aufrufer-Tenant == Ziel-Tenant.
Matrix Spaces? Optional, kein Bestandteil der Isolation. Sinnvoll erst, wenn Berater viele Räume haben und eine gruppierte Übersicht brauchen („alle offenen Fälle“). Das MVP löst das über die Vorgangs-Ansicht des Produkts bzw. die Eingangs-Ansicht des Orchestrators.
10Kubernetes-Fähigkeit (ohne es jetzt zu bauen)
Nichts am Konzept setzt Kubernetes voraus — und nichts verhindert es. Diese Regeln jetzt einhalten, dann ist der Umzug später ein Deployment-Thema, kein Architektur-Thema:
- Orchestrator zustandslos halten (Zustand nur in seiner DB) — als eigener, containerisierter Service ist er später ein gewöhnliches Deployment mit n Replicas.
- Konfiguration ausschließlich über Env-Vars/Secrets, keine Pfade oder Hosts hart verdrahten.
- PostgreSQL extern betreiben (managed oder eigener Server), nicht im Container-Lifecycle.
- Synapse-Media auf S3-kompatiblen Storage statt lokales Dateisystem — der einzige echte Stolperstein für Container-Betrieb.
- Synapse als Monolith starten; das Worker-Modell für horizontale Skalierung existiert und kann später aktiviert werden — erst relevant ab vielen tausend aktiven Nutzern.
- TLS am Edge (heute Caddy/Reverse-Proxy, später Ingress) — die Dienste selbst sprechen HTTP.
- Subdomain-Tenancy ist Host-Header-Routing — funktioniert mit jedem Ingress unverändert.
- Health-Endpoints (
/health) von Anfang an vorsehen.
11MVP-Empfehlung & Roadmap
MVP — bewusst schmal
- Synapse (Monolith) + PostgreSQL, Föderation/Guests/Registrierung aus, Directory-Suche aus.
- Orchestrator als eigener kleiner Service: die 5 Endpunkte aus §6, Bot-User, eigene DB (
room_mapping,standalone_requests), Service-Accounts für die Produkt-Backends. - Token-Exchange-Login (Keycloak → Matrix) für beide Modi.
- 3 Keycloak-Rollen:
chat-user,chat-berater,chat-berater-admin. - Raum-Template aus §5, Raum pro
requestId, PLs 100/50/0. - Web-Component: Raumliste, ein Raum, Text senden, Bilder/Dateien, „Kollege hinzufügen“-Dialog (nur Berater).
- Standalone-Shell-Seite mit OIDC-Login + „Neue Anfrage“-Flow.
- Synapse-Modul: Raum-Erstellung nur Bot, Invites nur innerhalb des Tenants.
Später — wenn der Bedarf da ist
- LiveKit-Anbindung im Chat: Call-Button, Orchestrator stellt LiveKit-JWT aus, Raumname = Matrix-Room-ID.
- Spaces pro Tenant für Berater-Übersichten.
- Sofortige Rollen-Synchronisation via Keycloak-Event-Listener (statt lazy beim Login).
- Push-Benachrichtigungen / E-Mail-Nudges („Ihr Berater hat geantwortet“).
- Archivierung/Export abgeschlossener Vorgänge (Compliance).
- Matrix Authentication Service (OIDC-nativ): ersetzt den Token-Exchange, wenn das Matrix-Ökosystem dort stabil angekommen ist — der Wechsel ist auf den Orchestrator begrenzt.
- Synapse-Worker / K8s-Deployment bei Lastwachstum.
- Dedizierter Homeserver für einzelne Compliance-Tenants.
- E2EE, falls regulatorisch gefordert — als eigenes Projekt.
12Checkliste: alle Ausgangsfragen in einem Satz
- Welche Komponenten brauchen wir?
- Synapse, Keycloak (vorhanden), Orchestrator-Modul, Web-Component — LiveKit bleibt am Rand (§1).
- Wie funktioniert Standalone?
- Statische Shell mit OIDC-Login + dieselbe Web-Component; Anfragen ohne Produkt über den „Neue Anfrage“-Flow (§2, §7.4).
- Wie funktioniert Embedded?
- Produkt liefert das Keycloak-Token per Callback, Widget tauscht es beim Orchestrator gegen ein Matrix-Token und zeigt den Raum zum Vorgang (§2, §7.1).
- Wer erstellt Räume?
- Immer der Orchestrator-Bot, ausgelöst durch „Request angenommen“ (§7.2).
- Raum an requestId oder an Berater+Kunde?
- An die
requestId— wegen Übergaben, Mehrfach-Beratern und Audit (§5). - Wie kommt ein weiterer Berater in den Raum?
- Über den Orchestrator-Endpunkt mit Tenant- und Rollenprüfung; der Bot lädt ein und setzt PL 50 (§7.3).
- Rolle des Orchestrators? Als Modul im bestehenden Backend?
- Übersetzer Produkte/Keycloak → Matrix, nie im Nachrichtenpfad. Als eigener kleiner Service, weil mehrere Produkte den Chat nutzen — ein Andockpunkt statt Modul-Kopien; bewusst ohne Fachlogik gehalten (§6).
- Keycloak-Rollen sinnvoll? Geeignet für Chat-Funktionen?
- Drei Realm-Rollen; geeignet für die globale Frage „was für ein Nutzer ist das“, nicht für Raum-Laufzeitrechte — das machen Power Levels (§3, §4).
- Wie übersetzt man Keycloak → Matrix?
- Beim Token-Exchange und beim Raum-Erstellen/Einladen: Rolle → Power Level (0/50), Bot 100, invite-only, history shared (§4, §5).
- Wie sind Kunden auch API-seitig beschränkt?
- Power Levels + Join Rules serverseitig, plus Synapse-Modul gegen Raum-Erstellung/Fremd-Invites — die UI ist nur die dritte, kosmetische Schicht (§8).
- Tenant-Isolation?
- Präfix in MXIDs/Aliassen, Realm-basierte Tenant-Ermittlung, invite-only, Modul-Check, keine Suche/Presence (§9).
- Gemeinsamer oder getrennter Homeserver?
- Gemeinsam; getrennte Instanzen bleiben pro Tenant nachrüstbar (§9).
- Spaces?
- Optional, später, kein Isolationsmechanismus (§9).
- Kubernetes?
- Zustandsloser Orchestrator, Env-Config, externes Postgres, S3-Media — dann ist K8s reines Deployment (§10).
- Wie bleibt es simpel?
- Ein Homeserver, ein Modul, eine Komponente, kein Proxy, kein E2EE im MVP, alles Weitere additiv (§11).