🌀 SwirlDB
A modular-first, CRDT-based embedded database built from composable adapters
Browser and server are equivalent nodes. No primary platform, no privileged environment. Just pure, swappable components.
Design Philosophy
Everything is an Adapter
SwirlDB is not a monolith with configuration options. It's a composition of swappable adapters:
- Storage Adapters: localStorage, IndexedDB, redb, SQLite, sharded files, or your own
- Sync Adapters: WebSocket, HTTP, WebRTC, custom protocols
- Auth Adapters: JWT, OAuth, ABAC, custom policies
- Encryption Adapters: AES-GCM, field-level, custom crypto
No feature flags. No conditional compilation. Each adapter is an independent implementation of a shared trait.
Core Principles
🔌 Adapter-First Architecture
Every subsystem is a pluggable adapter. Compose your database from the exact components you need. Runtime swappable with zero recompilation.
⚖️ Equivalent Nodes
Browser and server are peers implementing identical traits. Same CRDT engine, same storage interface, just different adapter implementations.
🎯 Path-Level Policies
Per-path control over storage, sync, and auth. Different data, different rules. Configure at runtime without rebuilding.
🧩 Shared Implementations
Pure Rust core with unified traits. Browser WASM and native servers share the same CRDT logic, differ only in platform adapters.
🔄 CRDT-Based Sync
Built on Automerge for conflict-free replicated data. Multi-user by default, with incremental delta sync and merge semantics.
⚡ Natural APIs
Native property access in JavaScript via Proxies. Idiomatic Rust APIs. No learning curve, just write code naturally.
Quick Example
import { SwirlDB } from '@swirldb/js';
// Create database with LocalStorage persistence
const db = await SwirlDB.withLocalStorage('my-app');
// Native property access via Proxies
db.data.user.name = 'Alice';
db.data.user.email = 'alice@example.com';
// Get values with .$value
console.log(db.data.user.name.$value); // 'Alice'
// Observe changes
db.data.user.name.$observe((newValue) => {
console.log('Name changed:', newValue);
});
// Enable auto-persistence
db.enableAutoPersist(500); // Debounced 500ms
db.data.settings.theme = 'dark'; // Auto-saves Architecture: The 3-Crate System
SwirlDB uses complete separation of concerns with no feature flags:
swirldb-core
Platform-agnostic Rust library
- CRDT engine (Automerge-based)
- Storage trait definitions (DocumentStorage + ChangeLog)
- Policy engine (path-based authorization)
- Auth providers (extensible)
- Zero platform dependencies
swirldb-browser
WASM bindings for browsers
- JavaScript bindings via wasm-bindgen
- localStorage adapter (5-10MB)
- IndexedDB adapter (50MB-1GB)
- Browser-specific storage implementations
- ~489KB total (gzipped)
swirldb-server
Pure Rust binary for servers
- HTTP/WebSocket sync server (axum + tokio)
- redb adapter (embedded, persistent)
- Memory adapter (volatile, fast)
- No Node.js dependency
- Native I/O performance
Unified Traits, Different Implementations
Both browser and server implement the same storage traits:
trait DocumentStorage {
async fn save(&self, key: &str, data: &[u8]) -> Result<()>;
async fn load(&self, key: &str) -> Result<Option<Vec<u8>>>;
// ...
}
trait ChangeLog {
async fn append_change(&self, namespace_id: &str, change: Change) -> Result<()>;
async fn get_changes_since(&self, namespace_id: &str, since: i64) -> Result<Vec<Change>>;
// ...
} Browser: localStorage + IndexedDB implementations
Server: redb + memory implementations
Same workflows. Same APIs. Just different backends.