Plattform · Governance & Zugriffskontrolle

Governance, fest im Kern verankert

In MindooDB ist Governance eine Eigenschaft der Datenschicht selbst – kein Aufsatz auf einem vertrauenswürdigen Server oder einer Anwendungsschicht. Zugriffs-Policy, Identität und ein kryptografisch beweisbarer, zeitpunktgenau reproduzierbarer Audit-Trail werden im Datenbankkern gespeichert und durchgesetzt. Der Gewinn: Du kannst nicht nur beweisen, was sich geändert hat, sondern auch, wer es ändern durfte – zu dem Zeitpunkt, an dem die Änderung tatsächlich in den Tenant gelangt ist.

Governance besteht aus zwei Hälften: einer Schreib-Seite, die steuert, wer Dokumente anlegen, ändern oder löschen darf – auch dann durchgesetzt, wenn Benutzer offline sind – und einer Lese-Seite, die steuert, wer Daten sehen darf, indem sie ein Access-Gate auf Sync-Ebene mit admin-blinder Schlüsselverteilung kombiniert. Beide bauen auf der Ende-zu-Ende-Verschlüsselung von MindooDB auf.

Governance im Kern verankert: Client-Geräte senden signierte Schreibanfragen durch ein Policy-Gate aus admin-signierten Rules; erlaubte Schreibvorgänge erreichen die verschlüsselte Datenbank, abgelehnte werden blockiert, während eine Audit-Zeitleiste die Autorisierung zum jeweiligen Zeitpunkt zeigt
Warum das Governance ist und nicht bloß Berechtigungen

Governance ist eine Eigenschaft der Datenschicht

Die meisten Stacks schieben Governance nach oben – zum Server oder in die Anwendung: Die Datenbank speichert Zeilen, und etwas anderes entscheidet, wer sie anfassen darf, und führt das Log. MindooDB dreht das um. Policy, Identität, vollständige Historie und Audit-Trail liegen in denselben Ende-zu-Ende-verschlüsselten, append-only geführten Speichern – so wandern die Garantien mit den Daten über Server, Peers und Offline-Geräte hinweg und überstehen auch einen vollständig kompromittierten Server.

Policy als versionierte Daten

Policies und Rules sind admin-signierte Dokumente in der Directory-Datenbank und selbst append-only. Der genaue Moment, in dem Governance eingeschaltet – oder vorübergehend deaktiviert – wurde, ist Teil des dauerhaften Protokolls und keine spurlose Konfigurationsänderung.

Reproduzierbare Autorisierung

Jeder Eintrag trägt eine vertrauenswürdige Zeit, und das Directory führt eine Time-Travel-Kette seiner eigenen Historie. So kann wasAllowedAt(op, user, dbid, at) nachspielen, welche Rules galten und wer autorisiert war – zu jedem beliebigen früheren Zeitpunkt, mit einem deterministischen Urteil, dem jede ehrliche Replica zustimmt.

Beweisbare Nachvollziehbarkeit

Jede Änderung ist Ed25519-signiert – das macht sie unabstreitbar – und wird gegen eine vertrauenswürdige Uhr bezeugt. Verstöße verschwinden nicht stillschweigend: Sie landen in einem Quarantäne- und Audit-Log pro Tenant, das Haven anzeigt. So bleibt selbst ein abgelehnter Versuch nachvollziehbar.

Identität, Widerruf & Löschung

Grants enthalten Schlüssel-Arrays pro Gerät; ein Widerruf bedeutet schlicht, Schlüssel zu entfernen. Eine ausdrückliche Remote-Löschung entfernt beim nächsten Verbinden den gesamten Tenant von einem gestohlenen oder ausgeschiedenen Gerät – der Identitäts-Lebenszyklus wird im selben signierten Directory verwaltet.

Die Grundidee

Das Zwei-Tier-Modell

Jede Rule fällt in eines von zwei Tiers – je nachdem, was der Sync-Server sehen kann. Der Server verarbeitet ausschließlich Geheimtext plus eine kleine Menge Klartext-Metadaten; Dokumentinhalte kann er nie lesen. Diese eine Unterscheidung ist die gesamte Architektur: Sie erlaubt MindooDB ein ehrliches Versprechen darüber, was genau kryptografisch garantiert ist und was per Policy unter kooperierenden Clients durchgesetzt wird.

Tier 1 vs. Tier 2
Tier-1-Identitäts-Rules prüfen Autor, Datenbank und Operationstyp und werden von Server und Clients durchgesetzt; Tier-2-Inhalts-Rules prüfen withfields und werden nur von Clients durchgesetzt
Eine Rule gehört genau dann zu Tier 2, wenn sie eine withfields-Klausel hat. Alles andere ist Tier 1.
Die beiden Tiers im Vergleich
Tier Prüft Durchgesetzt von Stärke
Tier 1 – IdentitätAutoridentität, Ziel-Datenbank, Operationstyp Server und ClientsKryptografisch – der Server weigert sich, einen verletzenden Eintrag zu bezeugen, also kann er sich nicht verbreiten
Tier 2 – InhaltDer tatsächliche Dokumentinhalt (withfields)Nur Clients Policy – begrenzt ehrliche Clients und formt die UX. Ein manipulierter Client kann sie nur lokal umgehen; jede ehrliche Replica prüft withfields beim Empfang erneut und stellt eine verletzende Änderung unter Quarantäne, sodass sie für niemanden sonst sichtbar wird
Das Problem der Offline-Uhr

Witness-Receipts: eine vertrauenswürdige Uhr, für die du dem Client nicht vertrauen musst

Die schwierigste Frage in einem local-first-System lautet: "Welcher Uhr vertrauen wir?" Wer offline arbeitet, könnte eigene Einträge zurückdatieren, um eine Änderung an einer Policy vorbeizuschmuggeln. MindooDB beantwortet das mit einem Witness-Receipt: einer Bestätigung, signiert von einem vertrauenswürdigen Witness (deinem Sync-Server), dass ein Eintrag zu einem bestimmten Zeitpunkt angenommen wurde. Jeder Durchsetzungspunkt nutzt damit genau eine klar definierte Uhr.

Der Lebenszyklus eines Schreibvorgangs
Ein Autorengerät erstellt einen Eintrag und pusht ihn; der Server als Witness prüft Tier 1, stempelt receivedAt und signiert ein Receipt; andere Replicas pullen und vertrauen dem Receipt. Ein abgelehnter Eintrag bleibt lokal und kann nicht synchronisiert werden.
Wer ein Recht verloren hat, kann die betreffende Änderung schlicht nicht synchronisieren – die Offline-Uhr lässt sich nie nutzen, um eine Policy per Rückdatierung zu umgehen.
Szenario A
Lokal schreiben

Das SDK wertet Tier 1 + Tier 2 gegen den lokalen Directory-Stand des Benutzers zur aktuellen lokalen Zeit aus. Ist der Vorgang erlaubt, wird der Eintrag ohne Witness-Felder lokal gespeichert und ist auf diesem Gerät sofort sichtbar. Über die rein lokale Sicht entscheidet die eigene Uhr – das ist unkritisch, denn die Änderung ist noch nicht in den gemeinsamen Tenant gelangt.

Szenario B
An einen Server pushen

Der Server wertet Tier 1 gegen seinen eigenen Stand zur Serverzeit aus. Ist der Vorgang erlaubt, stempelt er receivedAt, hält den Witness-Schlüssel fest, signiert das Receipt und gibt die Witness-Felder zurück, sodass sie zum Absender und weiter fließen. Lehnt er ab, liefert er ein strukturiertes AccessDenied und der Eintrag bleibt lokal – er kann sich nicht verbreiten.

Szenario C
Von einem Server pullen

Der Empfänger prüft die Receipt-Signatur gegen seine Liste vertrauenswürdiger Witnesses. Eine gültige Signatur bedeutet, dass Tier 1 zum Zeitpunkt receivedAt erfüllt war; standardmäßig wird das deshalb nicht erneut ausgewertet. Anschließend prüft der Empfänger Tier 2 lokal und materialisiert die Änderung entweder oder leitet sie in ein lokales Quarantäne- und Audit-Log.

Rules, Policies & Identitäten

Wie Entscheidungen konfiguriert werden

Der gesamte Zustand der Zugriffskontrolle liegt in der nur für Admins zugänglichen directory-Datenbank und synchronisiert zu allen Beteiligten. Alles, was der Server für Tier 1 braucht, ist mit dem Schlüssel $publicinfos verschlüsselt und damit lesbar, ohne den Standard-Tenant-Schlüssel zu besitzen; withfields-Inhalte kann der Server nie lesen. Jeder verändernde Aufruf ist admin-signiert.

Policies setzen die Baseline

Eine Standard-Policy für den Tenant und optionale Policies pro Datenbank legen fest, welche Operationen verboten sind, solange keine Allow-Rule greift:

  • Create, Change, Delete & Undelete – standardmäßig erlaubt, bis du sie verbietest
  • Snapshot & Purge – standardmäßig nur für Admins
  • Read – die Baseline für das Lese-/Sync-Gate auf Datenbankebene, verfeinert durch Read-Rules
  • Erlaubte Datenbank-IDs – die Allowlist des Tenants, welche Datenbanken existieren dürfen; der Server weigert sich, eine Datenbank außerhalb davon anzulegen oder zu synchronisieren
  • Standard-Verschlüsselungsschlüssel – welchen Schlüssel ein neues Dokument nutzt, wenn keiner angegeben ist; eine Bequemlichkeit, keine Sicherheitsmaßnahme
  • Globaler Ausschalter – ein Flag, das sämtliche Zugriffsprüfungen auf einmal deaktiviert

Ein brandneuer Tenant hat überhaupt kein Policy-Dokument, also ist alles erlaubt, bis ein Admin eines schreibt. Jede Revision wird an die Historie angehängt, sodass das genaue Fenster von Aktivierung und Deaktivierung prüfbar bleibt. Entscheidend: Jede Änderung wird an den Policies gemessen, die zu ihrer eigenen vertrauenswürdigen Zeit (receivedAt) aktiv waren. Änderungen, die nach der Aktivierung in den Tenant gelangt sind, fallen darunter; frühere werden gegen die implizite Alles-erlaubt-Vorgabe aufgelöst – Bestandsdaten haben damit automatisch Bestandsschutz, ganz ohne Migrationsschritt.

Rules entscheiden nach deny-overrides-allow

Jede Rule zielt auf einen Operationstyp und eine Datenbank ("*" = alle) und nennt die Benutzer oder Gruppen, für die sie gilt. Die Auswertung ist mengenbasiert und unabhängig von der Reihenfolge:

  • greift eine deny-Rule → verboten
  • sonst greift eine allow-Rule → erlaubt
  • sonst entscheidet die Baseline-Policy

Die Unabhängigkeit von der Reihenfolge ist wichtig, weil Rule-Dokumente über CRDTs zwischen Replicas zusammengeführt werden – es gibt keine Rule-Reihenfolge, über die man sich uneinig werden könnte.

Ein Server kann nur zu einem Tier-1-Urteil kommen. Wenn allein eine Tier-2-Inhalts-Rule zwischen Erlauben und Verbieten steht, behandelt er den Eintrag als auf Tier 1 erlaubt und überlässt die withfields-Prüfung den Clients. Jede Entscheidung liefert ein strukturiertes Ergebnis – allowed, eine lesbare reason, die matchedRuleId und das tier –, das das SDK nutzt, um Aktionen auszugrauen, die ein Benutzer nicht ausführen darf.

withfields: Inhaltsbedingungen

Eine withfields-Klausel prüft einen Punkt-Pfad im Dokument mit einem geschlossenen Satz von Operatoren (equals, contains, gt, …) und Platzhaltern wie ${user.usernames}. Jede Klausel wird gegen einen gewählten Dokumentzustand ausgewertet:

  • before – das bestehende Dokument (Standard für Change/Delete): "Du musst bereits Editor sein". Eine Auswertung mit after würde es erlauben, sich selbst hinzuzufügen und die eigene Bearbeitung zu autorisieren.
  • after – das Dokument mit angewandter Änderung (Standard für Create): "Wer anlegt, muss sich selbst in myeditors eintragen".
Identitäten, Gruppen & Widerruf

Rules werden gegen den gehashten Benutzernamen eines Benutzers, seine Gruppen-Hashes (inklusive verschachtelter Gruppen) und reservierte Pseudo-Token abgeglichen:

  • $everyone – alle registrierten Benutzer
  • $admin – nur Admins
  • $author – der ursprüngliche Ersteller des Dokuments (Tier 1, Eigentümermodell)

Der Widerruf erfolgt durch Entfernen von Schlüsseln aus dem admin-signierten Grant-Dokument – separate Widerrufsdokumente gibt es nicht. Ein Admin kann zusätzlich eine ausdrückliche, aktiv zu aktivierende Remote-Löschung signieren, die den Tenant beim nächsten Verbinden von einem gestohlenen oder ausgeschiedenen Gerät entfernt.

Praxisbeispiel

Ein CRM mit Editoren pro Datensatz

Hier läuft eine realistische Policy einmal komplett durch, damit die Einzelteile zusammenpassen. Der Tenant hat eine crm-Datenbank, und wir wollen vier Rules: Alle dürfen Kontakte anlegen, müssen sich dabei aber selbst in myeditors eintragen; ändern darf einen Kontakt nur, wer bereits eingetragener Editor ist; löschen darf ihn nur der ursprüngliche Ersteller; und die Gruppe HR darf als Eskalationsweg alles ändern.

Alice legt einen Kontakt an

Die Baseline ist deny. Rule 1 greift über $everyone, und ihr withfields geht durch, weil der after-Zustand von myeditors Alice enthält → erlaubt. Der Server bestätigt nur Tier 1 und bezeugt den Eintrag anschließend.

Bob (kein Editor) versucht eine Änderung

Rule 2 greift über $everyone, aber ihr withfields scheitert: Der before-Zustand von myeditors enthält Bob nicht → lokal verboten, selbst wenn seine Änderung ihn selbst hinzufügen will. Ein manipulierter Client könnte sie pushen, aber jeder ehrliche Empfänger stellt sie beim Materialisieren unter Quarantäne.

Alice löscht, HR übersteuert

Rule 3 greift über $author – der Erstellerschlüssel und der Signierende der Löschung lösen sich zum selben Benutzer auf → erlaubt und bezeugt (Tier 1, übersteht einen bösartigen Client). Rule 4 lässt jedes Mitglied der Gruppe HR jeden Kontakt ändern, unabhängig von myeditors.

Das zeigt die Arbeitsteilung: Tier-1-Rules ($author, Gruppe hr) werden am Server durchgesetzt und überstehen einen bösartigen Client; Tier-2-Rules (die myeditors-Inhaltsprüfungen) setzt jeder ehrliche Client durch und stellt sie beim Empfang unter Quarantäne, wenn sie verletzt werden.

Die Leseseite

Lese-Zugriffskontrolle

Schreib-Rules entscheiden, wer Daten ändern darf; der Lesezugriff entscheidet, wer sie sehen darf – und MindooDB setzt das auf zwei Ebenen durch, beide admin-signiert und mit $publicinfos verschlüsselt, damit der Zero-Trust-Server mit den nötigen Metadaten arbeiten kann, ohne je den Tenant-Schlüssel zu halten.

1. Ein Lese-/Sync-Gate auf Datenbankebene. Eine denyDocRead-Baseline plus doc_read-Rules (dieselbe deny-overrides-allow-Auswertung, gerichtet auf Benutzer, Gruppen, $everyone oder $admin) entscheiden, wer eine Datenbank überhaupt öffnen und synchronisieren darf. Das ist direkt im Sync-System verankert: Wer den Lesezugriff verliert, erhält keine Updates mehr und kann nicht einmal die bereits synchronisierte lokale Kopie öffnen. Weil das Gate vor jeder Sync-Operation sitzt, ist Lesen das übergeordnete Gate pro Datenbank – es steuert auch Schreibvorgänge: Wer eine Datenbank nicht lesen kann, kann darin auch keine Daten anlegen (und würde sonst Dokumente verfassen, die er nie zu sehen bekäme).

2. Die Vertraulichkeit von Dokumenten bleibt kryptografisch. Ein Dokument kann nur lesen, wer in seinem KeyBag (dem persönlichen, passwortgeschützten Schlüsselspeicher) einen Schlüssel hat, der es entschlüsselt – der Server trifft hier nie eine Leseentscheidung. Die Schlüsselverteilung bringt diese Schlüssel sicher zu den richtigen Leuten: Eine admin-signierte Policy legt fest, welche Benutzer und Gruppen welchen Schlüssel halten sollen, und jeder Client hält seinen KeyBag automatisch damit im Einklang. Das ist opt-in – mit dem gemeinsamen Standardschlüssel und ohne Lese-Gate kann weiterhin jeder alles lesen, genau wie zuvor.

Lese-Gate, fest im Sync verankert

Am Engpass im Server wertet derselbe Hook, der die Allowlist der Datenbank-IDs prüft, auch doc_read für das authentifizierte Principal gegen die vertrauenswürdige Serverzeit aus und weigert sich, eine verbotene Datenbank auszuliefern oder Pushes dafür anzunehmen – unerlaubte Updates werden schlicht nie zugestellt. Im Gleichschritt weigert sich der Client, eine Datenbank zu öffnen, für die der Benutzer den Zugriff verloren hat, sodass ein Benutzer mit widerrufenem Zugriff nicht einmal seine lokal synchronisierte Kopie lesen kann. Die directory-Datenbank unterliegt nie dem Gate (sie trägt genau die Policies, von denen das Gate abhängt); der Admin ist ausgenommen.

Schlüssel reisen für jeden Empfänger verschlüsselt

Wird ein Schlüssel an einen Benutzer gepusht, wird er mit dessen persönlichem öffentlichem (RSA-)Schlüssel verschlüsselt, sodass nur dieser ihn auspacken kann – kein anderer Benutzer und nicht einmal der Admin kann den Schlüssel lesen. Deshalb ist die Verteilung admin-blind: Wer den Schlüssel bereits hat, verpackt ihn für die Empfänger, und der Admin signiert und veröffentlicht nur das Ergebnis. Auch ein normaler Benutzer kann eine Verteilung vorbereiten und sie einem Admin zum Signieren geben – selbst einem Admin, der den Schlüssel gar nicht besitzt.

Gepushte Schlüssel zeigen Daten, zurückgezogene verbergen sie

Jeder Client gleicht seinen KeyBag beim Start und nach jedem Sync mit der Policy ab. Pusht man einem Benutzer einen Schlüssel, zieht sein nächster Sync die Dokumente nach, die dieser Schlüssel aufschließt – sie erscheinen einfach in der Datenbank und sind nun lesbar. Zieht man den Schlüssel zurück, verschwinden diese Dokumente: Die lokalen Kopien, die sich nicht mehr entschlüsseln lassen, werden aus Datenbank, Caches und Views entfernt. Beförderungen, Rotationen und Abteilungswechsel laufen automatisch darüber.

Die echte Grenze ist die Rotation

Als Bandbreitenschutz liefert der Server auch keine Daten mehr aus, die mit einem Schlüssel verschlüsselt sind, den ein Benutzer verloren hat. Wer entfernt wurde und eine alte Kopie behalten hat, kann aber weiterhin lesen, was er schon hatte – die echte Grenze ist deshalb die Rotation: Gib eine frische Version des Schlüssels nur an die verbleibenden Empfänger aus, dann nutzt jede künftige Änderung eine Version, die der entfernte Benutzer nie erhalten hat.

Apps verteilen

Policy-basierte App-Verteilung

Dieselbe admin-signierte Mechanik, die Schlüssel verteilt, verteilt auch Haven-Anwendungen. Ein Admin veröffentlicht eine Policy, die benennt, welche Apps ein Tenant anbietet und wer sie erhalten soll, und jeder Haven-Client gleicht seine lokale Anwendungsliste nach jedem Directory-Sync mit dieser Policy ab – so installiert das Onboarding eines Benutzers automatisch die Apps, die dieser Tenant veröffentlicht, und ein Widerruf entfernt sie wieder.

Anders als ein Schlüssel trägt eine App kein Geheimnis, es geht also schlicht darum, wer was bekommt. Die App-Definition und ihre Metadaten sind mit dem Tenant-Schlüssel verschlüsselt, der Zero-Trust-Server sieht also nur Geheimtext und stellt die Policy trotzdem an die richtigen Empfänger zu.

An Benutzer und Gruppen pushen (und zurückziehen)

Eine Policy adressiert sowohl einzelne Benutzer als auch ganze Gruppen. Eine Push-Liste sagt, wer eine App erhalten soll; eine Pull-Liste nimmt sie zurück. Die Gruppenzugehörigkeit wird zum Verteilzeitpunkt aufgelöst, das Hinzufügen oder Entfernen einer Person ändert also, wer die App bekommt, ohne die Policy anzufassen, und bei Überschneidung gewinnt ein Pull immer gegen einen Push.

Von Benutzern vorbereitet, vom Admin signiert

Ein normaler Benutzer kann eine neue Verteilung – oder eine Änderung an einer bestehenden – vorbereiten und sie einem Admin als fertige Signieranfrage übergeben. Der Admin prüft sie und signiert das Policy-Dokument; erst diese Admin-Signatur macht sie verbindlich. Das entspricht exakt der Schlüsselverteilung, dieselben Leute, die schon Schlüssel verwalten, verwalten also auch Apps.

Automatisch installieren, aktualisieren & entfernen

Nach jedem Directory-Sync vergleicht jeder Client die Policy mit seinem lokalen Stand: Er installiert Apps, die er nun haben sollte, aktualisiert eine App, wenn sich veröffentlichte Version oder Details ändern, und entfernt jede App, für die keine Berechtigung mehr besteht – alles, ohne dass der Benutzer etwas tun muss.

Klar als Tenant-verwaltet erkennbar

Eine verteilte App ist als Tenant-verwaltet gekennzeichnet und zeigt, aus welchem Tenant sie stammt. Verwaltete Apps kann der Benutzer weder bearbeiten noch entfernen – nur als private Kopie duplizieren, um lokal zu bauen und zu testen –, damit die veröffentlichte Version des Tenants die maßgebliche Quelle bleibt.

Audit & Geltungsbereich

Prüfbar by design, ehrlich über die Grenzen

Für jeden Zeitpunkt reproduzierbar

Weil jeder Eintrag eine vertrauenswürdige Zeit trägt (receivedAt, ersatzweise createdAt bei rein lokalen Einträgen) und das Directory eine Time-Travel-Kette seiner eigenen Historie führt, lässt sich die Frage beantworten: "Durfte Benutzer X dieses Dokument ändern, als die Änderung tatsächlich in den Tenant gelangt ist?" – und exakt rekonstruieren, wie sich der Zugriff eines Benutzers über die Zeit verändert hat. Die Abfrage wasAllowedAt(op, user, dbid, at) macht daraus eine erstklassige API. Tier-2-Verstöße verschwinden nicht stillschweigend; sie werden in einem Quarantäne-Log pro Tenant festgehalten, das Haven in seiner Audit-Ansicht zeigt.

Umfang und Nicht-Ziele von v1

Die Schicht regelt sowohl Schreibzugriffe als auch Lesezugriffe (auf Dokument- und Schlüsselebene). Sie kann einen manipulierten Client nicht daran hindern, einen Eintrag lokal zu verfassen, verhindert aber, dass dieser Eintrag in den Tenant übernommen wird, und das Lese-Gate des Servers hält unberechtigte Daten davon ab, je bei einem Client anzukommen. v1 setzt auf serververmittelten Sync als Witness. Sync von Gerät zu Gerät über Iroh gibt es inzwischen, aber ein Peer stellt keinen Witness-Receipt aus: Das empfangende Gerät prüft die Identitätsregeln selbst, und der Server beurteilt den Eintrag erneut, wenn er ankommt. Von Peers ausgestellte Witness-Receipts und echte Lesekontrolle auf Feld-Ebene (Schlüssel pro Feld) bleiben künftige Arbeiten. Historie wird nie nachträglich neu verschlüsselt, da Autorensignaturen über dem Geheimtext liegen.

In Aktion sehen
Haven – der Arbeitsbereich auf Basis dieses Zugriffsmodells

MindooDB Haven bringt dieselbe Ende-zu-Ende-Verschlüsselung, signierte Historie und eingebaute Governance in einen ruhigen, plattformübergreifenden Arbeitsbereich – inklusive der Audit-Ansicht, die Änderungen in Quarantäne sichtbar macht. Teste die kostenlose Beta oder lies dich in die Details ein.