RippleDB
A local-first sync engine with field-level conflict resolution
RippleDB
Early Development: RippleDB is currently in early stages of development. APIs may change, and some features are still being refined. Use at your own discretion and feel free to contribute feedback!
A local-first sync engine that treats local storage as the source of truth. Changes replicate via an append-only log, and conflicts resolve deterministically at the field level using hybrid logical clocks.
Why RippleDB?
- Local-first: Your app works offline. Local writes are instant.
- Field-level LWW: Conflicts resolve per-field, not per-row. No data loss.
- Append-only log: Simple, auditable replication primitive.
- Modular & headless: Core logic has zero dependencies — bring your own database.
- Framework agnostic: Works with any UI framework or backend.
Architecture
RippleDB follows a headless, adapter-based architecture:
- Core packages define interfaces and pure logic — no I/O, no dependencies
- Adapter packages provide battle-tested implementations for specific databases and ORMs
- You pick what you need — use our adapters or write your own
This means:
- Swap databases without changing application code
- Test with in-memory adapters, deploy with SQLite or Turso
- Integrate with existing ORMs like Drizzle seamlessly
Quick Example
import { defineSchema, s } from "@rippledb/core";
import { SqliteDb } from "@rippledb/db-sqlite";
import { createSyncMaterializer } from "@rippledb/materialize-db";
import { makeUpsert } from "@rippledb/core";
import { tickHlc, createHlcState } from "@rippledb/core";
const schema = defineSchema({
todos: { id: s.string(), title: s.string(), done: s.boolean() },
});
const db = new SqliteDb({
filename: "./data.db",
schema,
materializer: ({ db, schema }) =>
createSyncMaterializer({
schema,
db,
dialect: "sqlite",
tableMap: { todos: "todos" },
fieldMap: { todos: { id: "id", title: "title", done: "done" } },
}),
});
// Append a change
const hlc = tickHlc(createHlcState("node-1"), Date.now());
await db.append({
stream: "my-stream",
changes: [
makeUpsert({
stream: "my-stream",
entity: "todos",
entityId: "todo-1",
patch: { id: "todo-1", title: "Buy milk", done: false },
hlc,
}),
],
});Get Started
Installation
Install RippleDB and set up your first project
Quick Start
Build a sync-enabled app in 5 minutes
Core Concepts
Understand HLCs, changes, streams, and materialization
Choose Your Path
Adapters
Pick the right database and materializer for your use case
API Reference
Full API documentation for all packages
Packages
Core (interfaces & pure logic):
| Package | Description |
|---|---|
@rippledb/core | HLC, Change types, pure merge logic |
@rippledb/server | Db interface, append/pull contracts |
@rippledb/client | Store interface, sync orchestration |
Database Adapters (server-side persistence):
| Package | Best For |
|---|---|
@rippledb/db-sqlite | Local servers, Electron, tests |
@rippledb/db-turso | Edge deployments, serverless |
@rippledb/db-drizzle | Existing Drizzle projects (any DB) |
@rippledb/db-memory | Unit tests, prototyping |
Materializers (project changes → queryable tables):
| Package | Best For |
|---|---|
@rippledb/materialize-db | Raw SQL projects |
@rippledb/materialize-drizzle | Drizzle projects (type-safe) |