Architektur
kumbuka ist ein einzelner Docker-Compose-Stack. Ein Backend bedient sowohl die KI-gerichtete MCP-Oberfläche als auch die team-gerichtete Admin-API; es ist die einzige Komponente, die mit dem Identity-Provider kommuniziert. Alles andere ist ein gut verstandener Baustein: PostgreSQL als Speicher, Keycloak für Identität, Caddy am Rand und eine Next.js-Konsole für das Team.
Komponenten
Abschnitt betitelt „Komponenten“| Komponente | Rolle |
|---|---|
| Backend — Quarkus / Java 21 | Bedient /mcp (Streamable HTTP) und die Admin-REST-API. Die einzige Komponente, die mit Keycloak kommuniziert. Nutzt Hibernate ORM + Panache über JDBC, mit Flyway-Migrationen und SmallRye-Health. |
| PostgreSQL | System of Record für Memory-Einträge und Scopes. Das Schema wird von Flyway verwaltet. |
| Keycloak | Identity-Provider, headless betrieben — der Realm wird beim Start importiert und Benutzer werden über die Admin-API bereitgestellt. OAuth 2.1 ist für die entfernte MCP-Oberfläche verpflichtend. |
| Next.js-Admin-Konsole | Die team-gerichtete UI. Ein BFF-Client — sie hält niemals Tokens (siehe unten). |
| Caddy | Edge-Router und automatisches TLS. Leitet die Konsole, den /mcp-Endpunkt und den Auth-Host weiter. |
Topologie
Abschnitt betitelt „Topologie“flowchart TD
subgraph clients[AI clients]
A["claude.ai · Desktop · Code · Mobile"]
end
B[Browser · admin console]
A -- "OAuth 2.1 + bearer token" --> E
B --> E
E[Caddy edge]
E -- "/mcp (Streamable HTTP)" --> S
E -- "/api/* (admin REST)" --> S
E -- "/ (console UI)" --> N
E -- "/auth/* (redirect to IdP)" --> K
S["Quarkus backend"]
N["Next.js console (BFF client)"]
K["Keycloak (headless)"]
P["PostgreSQL"]
N -- "HttpOnly session cookie" --> S
S -- "OIDC: bearer + confidential" --> K
S -- "JDBC + Flyway" --> P
Die zwei OIDC-Rollen
Abschnitt betitelt „Die zwei OIDC-Rollen“Das Backend spielt zwei verschiedene OIDC-Rollen gegenüber dem Keycloak-Realm
kumbuka. Das ist der Teil, der es wert ist, verstanden zu werden.
-
Resource Server (Bearer) für
/mcp. KI-Clients entdecken den Autorisierungsserver über Protected Resource Metadata (/.well-known/oauth-protected-resource→ den Keycloak-kumbuka-Realm) und präsentieren audience-gebundene Bearer-Tokens. Das Subject des Tokens ist der handelnde Benutzer; die Realm-Rolle (member/admin) steuert die Autorisierung. Jeder/mcp-Aufruf läuft daher als ein bestimmter authentifizierter Benutzer, was es ermöglicht, dass derselbe Endpunkt den privaten Scope dieses Benutzers sicher bedient. -
Confidential-Web-App-Client (BFF) für die Konsole (
kumbuka-admin). Der Browser wird zu Keycloak und zurück zu einem Backend-Callback umgeleitet; das Backend hält die OIDC-Session und stellt ein HttpOnly-Cookie aus. Das Frontend hält niemals Tokens und ruft Keycloak niemals direkt auf (außer der Umleitung dorthin zur Anmeldung). Jeder privilegierte Aufruf der Konsole wird vom Backend vermittelt.
Der claude.ai-Connector
Abschnitt betitelt „Der claude.ai-Connector“Der Connector-Client (kumbuka-connector) ist confidential + PKCE. PKCE wird
unabhängig vom Client-Typ gesendet; das Client-Secret bietet einen echten
Connector-weiten Kill-Switch (durch Rotieren wird der Zugriff widerrufen) und
entspricht dem von claude.ai vorregistrierten Pfad aus ID plus Secret. Siehe
Einen Assistenten verbinden.
Datenfluss
Abschnitt betitelt „Datenfluss“- Ein Assistent liest/schreibt Memory: KI-Client → Caddy →
/mcpauf dem Backend (Bearer-Token) → PostgreSQL. Die Identität des Benutzers im Token bestimmt, welche Scopes sichtbar sind, einschließlich seines eigenen privaten Scopes. - Das Team kuratiert geteiltes Memory: Browser → Caddy → Konsole → Backend (Session-Cookie) → Admin-REST-API → PostgreSQL. Die Lesepfade der Konsole geben niemals private Einträge zurück — es gibt keinen Codepfad von einer Admin-/Team-Oberfläche zu den privaten Zeilen einer Person.
- Identitätsoperationen (Einladen, Aktivieren/Deaktivieren, Rollen, Rotation des Connector-Secrets) laufen über den vertraulichen Service-Account-Client des Backends zu Keycloak. Das Frontend hat null Keycloak-Wissen.
Zugriffskontrolle, in einer Zeile
Abschnitt betitelt „Zugriffskontrolle, in einer Zeile“private ist nur von seinem Eigentümer über die MCP-Oberfläche lesbar/schreibbar;
global und project sind team-geteilt (Members lesen, Admins verwalten); die
Urheberschaft wird serverseitig aus dem Schreibkanal abgeleitet. Die
private Regel wird auf der Datenzugriffsebene durchgesetzt, nicht in der UI —
siehe Sicherheit & Datenschutz.
Benennung
Abschnitt betitelt „Benennung“Alles, was das Produkt, die Organisation oder ein Deployable benennt, trägt die
Marke kumbuka: groupId ai.kumbuka, Artefakt kumbuka-server, npm
@kumbuka-ai/console, Images ghcr.io/kumbuka-ai/*, Compose-Services
kumbuka-backend / kumbuka-console, Keycloak-Realm kumbuka. Die eine
bewusste Ausnahme sind die MCP-Tool-Verben, die funktional bleiben (memory_*).