Funktionsweise
Vom Start in den Orbit
Schritt für Schritt: Ein Admin richtet einen MindooDB-Tenant ein, zwei Benutzer tauschen verschlüsselte Daten aus und arbeiten zusammen — ohne dass der Server je Klartext sieht.
Start
Der Admin richtet den Tenant ein
Tenant anlegen
Der Admin legt mit einem einzigen Aufruf einen neuen Tenant an. Dabei werden alle Schlüssel erzeugt, der Tenant geöffnet und Alice als erste Benutzerin im Verzeichnis registriert.
const { tenant, adminUser, appUser, keyBag }
= await factory.createTenant({
tenantId: "acme",
adminName: "cn=admin/o=acme",
adminPassword: "••••••••",
userName: "cn=alice/o=acme",
userPassword: "••••••••",
}); Auf dem Server veröffentlichen
Der Admin registriert den Tenant auf einem MindooDB-Server. Der Server speichert nur verschlüsselte Daten und öffentliche Schlüssel.
await tenant.publishToServer(
"https://sync.acme.com",
{ registerUsers: [appUser] }
); Dokument anlegen & synchronisieren
Alice legt ein Todo-Dokument an und schiebt die verschlüsselten Änderungen auf den Server.
const db = await tenant.openDB("todos");
const doc = await db.createDocument();
await db.changeDoc(doc, (d) => {
const data = d.getData();
data.title = "Buy groceries";
data.done = false;
});
const remote = await tenant.connectToServer(
"https://sync.acme.com", "todos"
);
await db.pushChangesTo(remote); Signal
Ein neuer Benutzer beantragt den Beitritt
Beitrittsanfrage erstellen
Bob erzeugt seine Identität lokal — seine privaten Schlüssel verlassen sein Gerät nie. Er erstellt eine Beitrittsanfrage, die nur öffentliche Schlüssel enthält.
const bob = await factory.createUserId(
"cn=bob/o=acme", "••••••••"
);
const joinRequestURI = factory.createJoinRequest(
bob, { format: "uri" }
);
// → mdb://join-request/eyJ2IjoxLCJ1c2Vy... Andocken
Der Admin genehmigt die Beitrittsanfrage
Beitrittsanfrage genehmigen
Der Admin genehmigt Bobs Anfrage. Damit wird Bob im Verzeichnis registriert und die symmetrischen Schlüssel werden mit einem gemeinsamen Passwort verschlüsselt.
const joinResponseURI =
await tenant.approveJoinRequest(
joinRequestURI,
{
adminSigningKey: adminUser
.userSigningKeyPair.privateKey,
adminPassword: "••••••••",
sharePassword: "one-time-secret",
format: "uri",
}
);
// → mdb://join-response/eyJ2IjoxLCJ0ZW5h... Orbit
Die Zusammenarbeit in Echtzeit beginnt
Beitreten, Pull, Ändern, Push
Bob tritt dem Tenant bei, holt die aktuellen Daten, markiert das Todo als erledigt und schiebt die Änderung zurück.
const { tenant: bobTenant } =
await factory.joinTenant(
joinResponseURI,
{
user: bob,
password: "••••••••",
sharePassword: "one-time-secret",
}
);
const remote = await bobTenant.connectToServer(
"https://sync.acme.com", "todos"
);
const db = await bobTenant.openDB("todos");
await db.pullChangesFrom(remote);
await db.syncStoreChanges();
const todo = await db.getDocument(todoDocId);
await db.changeDoc(todo, (d) => {
d.getData().done = true;
});
await db.pushChangesTo(remote); Änderungen abrufen
Alice holt die aktuellen Änderungen und sieht, dass das Todo erledigt ist.
await db.pullChangesFrom(remote);
await db.syncStoreChanges();
const updated = await db.getDocument(todoDocId);
console.log(updated.getData().done);
// → true ✓ Todo ist erledigt ✓ Was der Server sieht — und was nicht
Der Server sieht
- Öffentliche Signaturschlüssel (Ed25519)
- Öffentliche Verschlüsselungsschlüssel (RSA-OAEP)
- Verschlüsselte Blobs (AES-256-GCM-Geheimtext)
- Metadaten der Einträge (Zeitstempel, Inhalts-Hashes)
- Hashes von Benutzernamen (SHA-256 — nicht die Namen selbst)
Der Server sieht nie
- Benutzernamen (mit dem RSA-Schlüssel des Admins verschlüsselt)
- Dokumentinhalte (Ende-zu-Ende-verschlüsselt)
- Private Schlüssel (bleiben auf dem Gerät)
- Gemeinsame Passwörter (nur out-of-band)
- Symmetrische Verschlüsselungsschlüssel