Skip to content

an **End-to-End Encrypted, Offline-first Sync Database** designed for secure, distributed document storage and synchronization

Notifications You must be signed in to change notification settings

klehmann/MindooDB

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

106 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MindooDB - Your Data. Your Control.

Sleep well, even if your hosting service gets hacked. πŸ”’

MindooDB is an end-to-end encrypted, offline-first sync database. It lets apps collaborate and sync data without giving servers access to the contents.

Even if someone has full access to your infrastructure β€” database dumps, backups, logs β€” all they get is ciphertext.

Your data is encrypted on the client before it ever touches a server. No plaintext. No server-side keys. No trust required.

⚠️ Alpha software: This project is in early development and not yet recommended for production use. APIs may change without notice.

The Problem

Traditional databases trust the server. If your hosting provider is compromised, your data is exposed. Even "encrypted at rest" solutions decrypt data server-side for queries. MindooDB takes a different approach: encryption keys never leave your clients.

How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Your Clients                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚  β”‚   Alice's   β”‚  β”‚    Bob's    β”‚  β”‚  Charlie's  β”‚               β”‚
β”‚  β”‚   Device    β”‚  β”‚   Device    β”‚  β”‚   Device    β”‚               β”‚
β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚               β”‚
β”‚  β”‚ β”‚  Keys   β”‚ β”‚  β”‚ β”‚  Keys   β”‚ β”‚  β”‚ β”‚  Keys   β”‚ β”‚ ← Keys stay   β”‚
β”‚  β”‚ β”‚(private)β”‚ β”‚  β”‚ β”‚(private)β”‚ β”‚  β”‚ β”‚(private)β”‚ β”‚   on devices  β”‚
β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚               β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚ encrypted      β”‚ encrypted      β”‚ encrypted
          β–Ό                β–Ό                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Server (or P2P Peers)                        β”‚
β”‚                                                                  β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚   β”‚           Encrypted Blobs (unreadable)                  β”‚    β”‚
β”‚   β”‚     πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’ πŸ”’             β”‚    β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                                  β”‚
β”‚   Server can sync & store data, but CANNOT read it               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Sync happens through content-addressed stores: clients exchange only the encrypted entries they're missing. Works peer-to-peer, client-server, or any combination - for documents and attached files.

Key Features

Feature What It Means
πŸ›‘οΈ End-to-End Encrypted Data encrypted on client before sync. Servers can't decrypt.
πŸ“΄ Offline-First Create and edit documents without network. Sync when online.
✍️ Signed Changes Every change is digitally signed. Proves authorship, prevents tampering.
πŸ”— Tamperproof History Append-only, cryptographically chained. Like a blockchain for your docs.
🀝 Real-time Collaboration Built on Automerge CRDTs. Conflicts resolve automatically.
πŸ”‘ Fine-grained Access Named encryption keys for sensitive documents. Share with specific users.

Quick Start

Installation

npm install mindoodb

πŸ“± React Native / Expo? See the React Native setup guide for mobile-specific instructions with native performance.

Create a Tenant and Start Working

import { 
  BaseMindooTenantFactory, 
  InMemoryAppendOnlyStoreFactory,
  KeyBag 
} from "mindoodb";

// 1. Set up storage (in-memory for demo; use file/server-backed for production)
const storeFactory = new InMemoryAppendOnlyStoreFactory();
const factory = new BaseMindooTenantFactory(storeFactory);

// 2. Create a user (generates signing + encryption key pairs)
const user = await factory.createUserId("CN=alice/O=acme", "user-password");
const keyBag = new KeyBag(user.userEncryptionKeyPair.privateKey, "user-password");

// 3. Create admin keys (for managing users)
const adminKeyPair = await factory.createSigningKeyPair("admin-password");

// 4. Create a tenant (an organization that shares documents)
const tenant = await factory.createTenant(
  "acme-corp",
  adminKeyPair.publicKey,
  "tenant-key-password",
  user,
  "user-password",
  keyBag
);

// 5. Open a database and create documents
const db = await tenant.openDB("contacts");
const doc = await db.createDocument();

await db.changeDoc(doc, async (d) => {
  const data = d.getData();
  data.name = "John Doe";
  data.email = "john@example.com";
});

// 6. Read it back
const contacts = await db.getAllDocumentIds();
const loaded = await db.getDocument(contacts[0]);
console.log(loaded.getData()); // { name: "John Doe", email: "john@example.com" }

Sync Between Users

// Alice creates a document
const aliceDB = await aliceTenant.openDB("projects");
const project = await aliceDB.createDocument();
await aliceDB.changeDoc(project, (d) => { d.getData().title = "Secret Project"; });

// Bob pulls Alice's changes
const bobDB = await bobTenant.openDB("projects");
await bobDB.pullChangesFrom(aliceDB.getStore());

// Bob edits the document
const projectDoc = await bobDB.getDocument(project.id);
await bobDB.changeDoc(projectDoc, (d) => { d.getData().status = "In Progress"; });

// Alice pulls Bob's changes
await aliceDB.pullChangesFrom(bobDB.getStore());
// Alice now sees: { title: "Secret Project", status: "In Progress" }

Core Concepts

Tenants

An organization or team that shares access. Created client-sideβ€”no server registration needed.

  • Has a default encryption key (a regular KeyBag key shared with all members)
  • Has an admin key (for registering/revoking users)
  • Contains multiple databases

Users

Identified by cryptographic key pairs, registered by an admin:

  • Signing key (Ed25519): Proves authorship of changes
  • Encryption key (RSA-OAEP): Protects local key storage
  • Keys generated locally; only public keys shared with admin

Databases

Each tenant can have multiple databases, created on-demand:

const contacts = await tenant.openDB("contacts");
const invoices = await tenant.openDB("invoices");

A special directory database stores user registrations (admin-only).

Documents

Automerge CRDTs with full history:

  • Every change is signed and encrypted
  • Automatic conflict resolution for concurrent edits
  • Time travel: reconstruct any historical state of documents (e.g. run queries on historic data) β€” see Time Travel Documentation

Attachments

Files attached to documents:

  • Chunked (256KB) and encrypted
  • Streaming upload/download for large files
  • Deduplication across the tenant

See: Attachments Documentation

Document Indexing

MindooDB provides a flexible, incremental indexing facility:

  • Cursor-based processing: Only index documents that changed since the last runβ€”no full rescans
  • Pluggable indexers: Add any indexer you need (fulltext search, aggregations, custom queries)
  • Built-in Virtual Views: Spreadsheet-like views that categorize, sort, and aggregate documents
  • Cross-boundary queries: Virtual Views can span multiple databases, mix local and remote data, or even query across tenants
// Incremental indexing: process only what's new
let cursor = null;
while (true) {
  for await (const { doc, cursor: newCursor } of db.iterateChangesSince(cursor)) {
    if (doc.isDeleted()) {
      mySearchIndex.remove(doc.getId());
    } else {
      mySearchIndex.update(doc);  // Flexsearch, Lunr, or custom
    }
    cursor = newCursor;
  }
  await sleep(1000);
}

Encryption Model

All encryption keys are stored in the KeyBagβ€”a local, password-protected key store.

Key Type Purpose Who Has It
default key Used when no other key is specified All tenant members
Named keys Fine-grained access for sensitive docs Only users you share it with

Keys are distributed offline (e.g. password protected via email or a shared drive). The default key is typically shared during onboarding; named keys are shared as needed for specific documents.

Security

Cryptographic Guarantees

  • Signatures: Ed25519 on every change - proves who wrote it
  • Encryption: AES-256-GCM - servers see only ciphertext
  • Integrity: Changes are hash-chained - tampering breaks the chain

User Revocation

Revoked users:

  • ❌ Cannot sync with peers or servers
  • ❌ Cannot make new changes (signatures rejected)
  • ⚠️ Can currently read previously-synced local data (planned: data wipe on first connect after revocation)

MindooDB includes revocation timestamp protection to prevent backdated changes from revoked users. See: Revocation Protection

Audit Trail

Append-only storage means nothing is ever deleted:

  • Complete history of who changed what and when
  • Cryptographic proof of all operations
  • GDPR compliance via purgeDocHistory() when legally required

Supported Platforms

  • βœ… Node.js - Server-side and desktop apps
  • βœ… Web Browsers - Progressive web apps with Web Crypto API
  • βœ… React Native / Expo - iOS and Android with native Automerge (Rust via JSI)
  • βœ… Electron - Cross-platform desktop apps

Use Cases

  • Multi-Tenant SaaS: Each customer isolated with encrypted data
  • Collaborative Editing: Real-time co-editing with signed changes
  • Secure File Sharing: Named keys for need-to-know access
  • Audit-Critical Systems: Tamperproof history meets compliance requirements
  • Offline-First Apps: Full functionality without network; sync when connected
  • Mobile Apps: End-to-end encrypted sync with native performance

See: Use Cases Documentation

Documentation

Support

Need commercial support, have questions, or want to request a feature? We're here to help! :-)

License

Apache 2.0

Author

Mindoo GmbH


Your data. Your keys. Your control. With MindooDB, even a complete server breach doesn't expose your documents. Sleep well! πŸ˜΄πŸ”’

About

an **End-to-End Encrypted, Offline-first Sync Database** designed for secure, distributed document storage and synchronization

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •