Architektur & Sicherheit

Aufbau von Customermates, Datenisolation je Workspace, Anmeldung per API-Key und MCP-OAuth, Inhalt des Aktivitätsprotokolls, Meldeweg für Schwachstellen.

Jedes Unternehmen ist ein isolierter Mandant in einer gemeinsamen PostgreSQL-Datenbank. Jeder Datensatz gehört zu genau einem Unternehmen, API-Keys und OAuth-Verbindungen handeln als der Nutzer, der sie erstellt oder autorisiert hat, und KI-Agents sehen nur, was dieser Nutzer sehen darf. Ein Login kann zu mehreren Workspaces gehören, und jede Sitzung, jeder API-Key und jede OAuth-Verbindung handelt in genau einem davon; siehe Workspaces.

Was ist der Stack?

  • Next.js 16 (App Router, Turbopack): Web-App, REST-API und MCP-Endpunkt.
  • PostgreSQL: primärer Datenspeicher. JSONB enthält Feldoptionen und -formate, Notizen, Ansichts- und Widget-Einstellungen, Webhook-Header, Payloads von Webhook-Zustellungen und Audit-Einträge.
  • Prisma: ORM und Migrationen.
  • better-auth: Sitzungen, Anmeldung mit Google und Microsoft, API-Keys, MCP-OAuth.
  • mcp-handler: Endpunkt für das Model Context Protocol.
  • Workflow: Hintergrund-Jobs wie Webhook-Zustellungen; auf einer Self-Hosted-Instanz liegt die Warteschlange in derselben PostgreSQL-Datenbank.
  • TypeScript durchgängig, mit Zod-Validierung an jeder Grenze.

Wie funktioniert die Mandantentrennung (Multi-Tenancy)?

Alle Unternehmen teilen sich eine Datenbank, und jeder Datensatz trägt die ID seines Unternehmens. Die Trennung wird auf drei Ebenen erzwungen:

  1. Interactors: Jede fachliche Operation läuft als angemeldeter Nutzer und filtert nach dessen Unternehmen.
  2. Query-Guard: Eine Prisma-Erweiterung bricht mit einem Fehler ab, wenn eine Abfrage auf eine Workspace-Tabelle die aktuelle Unternehmens-ID nicht im Filter hat oder ein anderes Unternehmen nennt, und wenn eine neue Zeile die ID eines anderen Unternehmens trägt. Nur die Authentifizierungstabellen sind ausgenommen (Anmeldeidentitäten, Anmeldekonten, Sitzungen, Bestätigungstokens, API-Keys sowie die MCP-OAuth-Clients, -Tokens und -Zustimmungen), und einige ausdrücklich markierte Systemoperationen umgehen den Guard.
  3. @TenantInteractor: ermittelt den aktiven Nutzer, weist inaktive Mitglieder ab, prüft die Rollenberechtigung und öffnet den Mandantenkontext, den der Query-Guard liest.

Eine Self-Hosted-Instanz kann mehrere Workspaces nebeneinander mit derselben Trennung enthalten. Mandantenübergreifendes Lesen ist über die öffentliche Oberfläche nicht möglich. In der gehosteten Cloud kann eine Operator-Konsole des Anbieters für den Support mandantenübergreifend lesen; sie ist auf ein freigeschaltetes Operator-Konto beschränkt, verweigert den Dienst außerhalb des Cloud-Modus und schreibt jeden Lese- und Schreibzugriff in ein separates internes Log. Eine Self-Hosted-Installation hat keine solche Konsole. Der gehostete Assistent Mate läuft im selben Rahmen: Er kann nur lesen und ändern, was der anfragende Nutzer darf.

Wie authentifizieren sich Clients?

Drei Wege, die alle auf denselben Nutzer- und Unternehmenskontext auflösen:

  • Session-Cookie (Browser): signiert, http-only, Secure unter HTTPS. Angemeldet wird mit E-Mail und Passwort oder mit Google oder Microsoft, sofern die Instanz diese Anbieter eingerichtet hat. Ein neues Konto ist 24 Stunden nutzbar, bevor die E-Mail-Adresse bestätigt werden muss. Jede Anfrage prüft die Session in der Datenbank, daher wirkt das Cookie nach dem Abmelden oder einer widerrufenen Session sofort nicht mehr, und der Status Inaktiv sperrt sofort den Workspace, in dem er gesetzt ist. Die Session hält auch den Workspace fest, in den Sie gewechselt haben, sodass ein anderer Browser in einem anderen Workspace arbeiten kann.
  • API-Key: 64 Zeichen langes Token aus ASCII-Buchstaben (a-z, A-Z), ausgestellt vom better-auth-API-Key-Plugin, nur als Hash gespeichert, an einen Nutzer und den Workspace gebunden, in dem er erstellt wurde, optional mit Ablaufdatum. Wird im Header x-api-key gesendet. Siehe API-Keys.
  • OAuth 2.1 (MCP): Remote-MCP-Clients autorisieren sich über better-auth mit PKCE und senden statt eines API-Keys ein Bearer-Token. Access-Tokens gelten eine Stunde, Refresh-Tokens dreißig Tage. Eine Verbindung ist an den Workspace gebunden, der auf dem Zustimmungsbildschirm gewählt wurde, ebenso die Tokens, die sie erneuert.

Unterstützt Customermates SSO oder SAML?

Nein. Enterprise-Single-Sign-on mit Ihrem eigenen Identity-Provider über SAML oder OIDC ist nicht implementiert, weder in der Cloud noch beim Self-Hosting. Mitglieder melden sich mit E-Mail und Passwort an oder mit einem Google- oder Microsoft-Konto, sofern die Instanz diese Anbieter eingerichtet hat; auf einer Self-Hosted-Instanz richten Sie sie selbst ein, siehe Self-Hosting. Um den Zugang eines Mitglieds zu beenden, öffnen Sie es unter Mitglieder und setzen Status auf Inaktiv.

Wie wird die Autorisierung entschieden?

Eine Rolle unter Rollen vergibt Erstellen, Bearbeiten, Löschen und Lesen Alle, Zugewiesen oder Keine getrennt für jede Liste, einschließlich selbst angelegter Listen. Bearbeiten und Löschen erfordern die Aktionsberechtigung und Zugriff auf den Datensatz. Workspace-Berechtigungen nutzen dieselben Spalten, mit einem Strich, wo eine Aktion nicht gilt. Bearbeiten bei Datenmodell ist von Datensatzzugriff und Rollenverwaltung getrennt: Ohne Bearbeiten bei Benutzer & Rollen dürfen Datenmodell-Verwalter keine zusätzlichen Rechte vergeben. Die eingebaute Rolle Admin hat alle Rechte. Datensatz- und Konfigurationsänderungen prüfen Rechte und Revisionen erneut innerhalb der Workspace-Transaktion. API-Keys erben die aktuellen Rechte ihres Besitzers; ein Scoping pro Key gibt es nicht.

Wie stoppe ich die API-Keys eines ehemaligen Mitglieds?

Die Berechtigung API & Webhooks steuert die Seiten API-Schlüssel, Webhooks und Zustellungen und die Key- und Webhook-Operationen dahinter. MCP-Verbindungen und REST-Aufrufe, per Key oder OAuth-Token, brauchen außerdem ihr Lesen Alle; die angemeldete Nutzung der Web-App nicht. Jeder Aufruf wird gegen die Berechtigungen des Besitzers auf den betroffenen Datensätzen geprüft. Entziehen Sie einer Rolle API & Webhooks, sehen ihre Mitglieder ihre Keys also nicht mehr, und die Keys funktionieren weder für MCP noch für REST.

Um die Keys eines Mitglieds zu stoppen, öffnen Sie das Mitglied unter Mitglieder, setzen Status auf Inaktiv und klicken auf Speichern. Dafür braucht Ihre Rolle Bearbeiten und Lesen Alle bei Benutzer & Rollen; sich selbst können Sie nicht deaktivieren, und das letzte aktive Mitglied mit der Rolle Admin lässt sich nicht deaktivieren. Jeder Aufruf mit den Keys eines inaktiven Mitglieds, der Workspace-Daten liest oder ändert, wird abgelehnt. Niemand kann die Keys eines anderen Mitglieds auflisten oder löschen.

Wie funktioniert externer KI-Zugriff?

MCP-Anfragen nutzen denselben Authentifizierungs- und Autorisierungspfad:

  • Der Agent ruft <BASE_URL>/api/v1/mcp mit einem API-Key oder einem OAuth-Bearer-Token auf.
  • Der Server ermittelt Nutzer und Unternehmen aus dem Credential.
  • Jeder Tool-Aufruf läuft in einem mandantenbezogenen Kontext.
  • Validierungsfehler liefern strukturierte Meldungen mit Hinweisen zur Behebung und verraten keine internen Schema-Details. Unerwartete Fehler liefern nur eine allgemeine Meldung.

Die MCP-Oberfläche ergänzt Eingabe-Schutzregeln:

  • Ein Schreibvorgang ändert einen typisierten Datensatz oder eine Verknüpfung, oder über updateMany (eine gemeinsame Änderung) und deleteMany bis zu 100 benannte Datensätze auf einmal, jeweils atomar. Jede Referenz enthält Typ-ID und Datensatz-ID; Verknüpfen und Lösen nennen zusätzlich die Beziehungsdefinition. Ausgelassene Felder bleiben unverändert, null leert ein optionales Eingabefeld ausdrücklich. Berechnete Felder sind nicht direkt beschreibbar.
  • Das Backend prüft Typ, Workspace, Datensatzrechte, Version und Konfigurationsrevision während der Schreibtransaktion. Eine Referenz auf einen anderen Typ oder Workspace kann ein Update nicht umleiten.
  • Auch beim Löschen, einschließlich deleteMany, wird jeder Datensatz per Referenz und aktueller Version benannt; ein Löschen per Filter gibt es nicht. Ein optionaler Hash der Auswirkungs-Vorschau lässt das Löschen scheitern, wenn sich die Kaskadenwirkung seit der Vorschau geändert hat.

Die vollständige Liste finden Sie im MCP-Tool-Katalog.

Wie werden Daten im Ruhezustand und bei der Übertragung geschützt, wie werden Passwörter gespeichert, und wo werden die Daten gehostet?

  • Die Verschlüsselung der Postgres-Daten im Ruhezustand (at rest) hängt von Ihrem Anbieter ab. In der gehosteten Cloud liegen die Daten in einer EU-Region und sind im Ruhezustand auf Datenträgerebene verschlüsselt.
  • Passwörter und API-Keys werden nur als Hash gespeichert.
  • HTTPS überall in der gehosteten Cloud. Beim Self-Hosting stellen Sie das TLS selbst bereit; das Caddy-Beispiel finden Sie unter Self-Hosting. Anmelde-Cookies sind als Secure markiert, wenn die BASE_URL der Instanz mit https:// beginnt.
  • Webhook-Secrets und die Werte eigener Header sind abrufbar gespeichert, weil ausgehende Anfragen sie brauchen; siehe Wie sind Webhook-Secrets und Webhook-Ziele abgesichert?.

Enthalten Logs oder Fehlerberichte Secrets?

Die Anwendung schreibt einfache Logs in ihre Standardausgabe. Umgebungswerte wie die Secrets aus .env werden nicht geloggt, einen Filter, der Secrets in Logzeilen maskiert, gibt es aber nicht. Ist ein Dienst für Fehlerberichte eingerichtet, wie in der gehosteten Cloud, werden Ereignisse ohne Cookies, Header und Bodys der Anfrage gesendet. Unerwartete Fehler in MCP- und Assistenten-Tools erreichen den Client und den Fehlerbericht nur als allgemeine Meldung wie „The operation could not be completed“.

Das veröffentlichte Self-Hosted-Image setzt keine Adresse für Fehlerberichte und schaltet die Telemetrie von Next.js und Prisma ab. Auf einer Self-Hosted-Instanz erscheinen Fehler, die die App in ihre Ausgabe schreibt, nur in docker compose logs app; Fehler, die die App nur an einen Dienst für Fehlerberichte meldet, werden nirgends festgehalten. Eine E-Mail, die der Anbieter abgelehnt oder nicht rechtzeitig beantwortet hat, wird mit Betreff, Domain des Empfängers und dem Grund des Anbieters geloggt, nie mit der vollständigen Adresse.

Wie sind Webhook-Secrets und Webhook-Ziele abgesichert?

  • Webhook-Secrets und die Werte eigener Header liegen ungehasht in Postgres, weil sie abrufbar sein müssen, um ausgehende Anfragen zu signieren und zu senden. REST-API und MCP geben sie nie zurück, das Aktivitätsprotokoll schwärzt sie, und ein geöffneter Webhook zeigt sie nur der Rolle Admin und Rollen mit Bearbeiten bei API & Webhooks; eine Rolle mit nur Lesen Alle sieht an ihrer Stelle ********. Wenn Sie sie woanders halten wollen, betreiben Sie Customermates selbst mit Ihrem eigenen Secret-Manager.
  • Eine Webhook-URL darf für jeden Host reines http:// verwenden, solange der Webhook keine eigenen Header hat. Ein Webhook mit eigenen Headern muss https:// verwenden, ausgenommen localhost, 127.0.0.1 und ::1; das wird beim Speichern des Webhooks und erneut vor jeder Zustellung geprüft.
  • Webhook-URLs sind nicht auf öffentliche Adressen beschränkt: Der Server sendet Anfragen an jeden Host, den er erreicht, auch an interne Dienste in seinem Netz. Beschränken Sie beim Self-Hosting den ausgehenden Netzverkehr des App-Containers, wenn interne Dienste unerreichbar bleiben müssen, und geben Sie Erstellen und Bearbeiten bei API & Webhooks nur vertrauenswürdigen Rollen.

Webhooks verwalten Sie unter Webhooks; siehe Webhooks.

Was erfasst das Aktivitätsprotokoll?

Aktivität ist das Aktivitätsprotokoll des Workspace. Jede Änderung wird dort mit Verursacher, Art, betroffenem Element und Payload geschrieben, in derselben Transaktion wie die Änderung selbst. Datensatzänderungen tragen feldweise vorherige und aktuelle Werte; Aktualisierungen an Rollen, Webhooks, Routinen und verbundenen Konten enthalten ein feldweises Diff, Erstellungen, Löschungen sowie das Speichern von Mitgliedern eine Momentaufnahme. Konfigurationsänderungen am Datenmodell und am Datensatzzugriff je Rolle bleiben mit der Konfigurationsrevision erhalten, die sie erzeugt haben. Registrierungen von Nutzern, Datensatz-Exporte und akzeptierte Rechtsdokumente werden ebenfalls erfasst. Der Verursacher bleibt auch erhalten, wenn die Person entfernt wird, und automatische Änderungen werden als System oder als Eigentümer der Routine erfasst. Ein fehlgeschlagener Schreibvorgang hinterlässt keinen Eintrag. Der Aktivitätsprotokoll ist in jedem Cloud-Tarif und ebenso in der Self-Hosted-Edition enthalten.

Was steht nicht im Aktivitätsprotokoll?

Messaging-Ereignisse gehören nicht zum Aktivitätsprotokoll; sie werden nur als Zustellungseinträge gespeichert, solange ein Webhook oder eine Event-Routine sie abonniert. Anmeldeereignisse, der Lebenszyklus von API-Keys, Abo-Änderungen, gespeicherte Ansichten, Widgets, Agent-Konversationen und Operator-Aktionen des Anbieters werden ebenfalls nicht protokolliert. Webhook-Secrets und Header-Werte werden in Webhook-Einträgen geschwärzt. Ein Eintrag aus einem automatisierten Prozess, etwa das Entfernen eines verbundenen Kontos nach Ablauf der Testphase, nennt die betroffene Person, nicht eine ausführende.

Wer kann das Aktivitätsprotokoll sehen, und wo?

Öffnen Sie Aktivität; siehe Aktivität. Der Zugriff braucht Lesen Alle in der Zeile Aktivitätsprotokoll Ihrer Rolle, die weder Erstellen noch Bearbeiten noch Löschen anbietet; die Rolle Admin hat ihn immer. Die Seite zeigt Datensatzänderungen, Verwaltungs- und Systemereignisse sowie Konfigurationsänderungen und lässt sich nach Art filtern. Datensatzänderungen folgen den Datensatzrechten der lesenden Person. Dieselbe Berechtigung blendet das Aktivitätsprotokoll in der Ansicht Aktivitäten eines Datensatzes ein.

Wie lange bleiben Einträge im Aktivitätsprotokoll erhalten, und kann ich sie exportieren?

Einträge werden nicht automatisch gelöscht; sie bleiben erhalten, solange der Workspace besteht. Aktivität bietet keinen Export. Der Aktivitätsverlauf, sein REST-Endpunkt und sein MCP-Tool liefern dieselben Einträge mit derselben Berechtigung.

Wo melde ich Sicherheitslücken (Vulnerabilities)?

Schreiben Sie an mail@customermates.com. Wir bemühen uns um eine Eingangsbestätigung innerhalb eines Werktags. Ein Bug-Bounty-Programm gibt es nicht.

Für Agents

Schreibvorgänge an Datensätzen laufen über MCP mit mutate_crm_record, Webhook-Operationen mit manage_webhooks, und Audit-Einträge liest get_activities.

Weiter

  • Self-Hosting: Customermates selbst betreiben.
  • API-Keys: Regeln zur Key-Hygiene.
  • MCP: wie die KI-Oberfläche geformt ist.