RippleDB
RippleDB

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
Your Application
@rippledb/server
Db interface
@rippledb/client
Store interface
db-sqlitedb-tursodb-drizzledb-memory
adapters implement the interfaces
@rippledb/core
HLC, Change, pure merge logic

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

Choose Your Path

Packages

Core (interfaces & pure logic):

PackageDescription
@rippledb/coreHLC, Change types, pure merge logic
@rippledb/serverDb interface, append/pull contracts
@rippledb/clientStore interface, sync orchestration

Database Adapters (server-side persistence):

PackageBest For
@rippledb/db-sqliteLocal servers, Electron, tests
@rippledb/db-tursoEdge deployments, serverless
@rippledb/db-drizzleExisting Drizzle projects (any DB)
@rippledb/db-memoryUnit tests, prototyping

Materializers (project changes → queryable tables):

PackageBest For
@rippledb/materialize-dbRaw SQL projects
@rippledb/materialize-drizzleDrizzle projects (type-safe)

On this page