Profile
Back to NewsBack
GitHub Trending 3 min
Reader Mode
syncular/syncular: Offline-first SQL sync with Typescript and Rust cores

syncular/syncular: Offline-first SQL sync with Typescript and Rust cores

8 hours ago

Animated syncular ASCII singularity

syncular

Syncular keeps an SQLite database on each client and synchronizes it through a server-owned commit log. Apps read from local SQLite and continue to work offline. A write updates local state immediately, enters an outbox, and is checked by the server when it syncs.

The browser client runs SQLite on OPFS. Native integrations share the Rust core and native SQLite across Rust, Swift, Kotlin, Flutter, React Native, and Tauri. Servers run on Bun, Node, or Cloudflare Workers with SQLite, Postgres, or D1. Backend services and workers can run a server-side SyncClient with persistent SQLite (often called a headless client). A database-less SyncRemoteClient submits commits and calls registered server operations. Applications can store domain actions as event rows alongside their related writes.

Documentation · Quickstart · Live demos · Benchmarks · Blog

Try it

bun create syncular-app my-app --template minimal

The minimal template creates a server, a schema, two independent clients, and a smoke test. The five-minute quickstart walks through the generated project and runs the two clients against each other.

Project status

Syncular is pre-1.0 and currently maintained by me, Benjamin Kniffler. The wire protocol is written down and checked across two implementations. Public APIs and protocol details can still change before 1.0. Changes are recorded in docs/RELEASE.md.

How behavior is checked

docs/SPEC.md defines the sync wire protocol, docs/REMOTE.md defines registered remote operations, and spec/vectors/ contains its byte-level fixtures. The TypeScript and Rust cores run the same implementation-independent conformance catalog. A behavior change that affects both cores has to update the spec and add a conformance scenario.

Integration tests use an in-memory transport with deterministic fault injection. They wait for explicit readiness signals; sleeps are banned. A small set of adapter tests exercises real sockets. The details live in packages/conformance.

Repository map

| Path | What it contains | |---|---| | packages/core | Protocol codecs, shared types, vector round-trips | | packages/server | Server protocol handler plus storage and authorization interfaces | | packages/server-hono, packages/server-workers | Hono and Cloudflare Workers bindings | | packages/web-client | @syncular/client, the TypeScript client core on SQLite | | packages/react | React hooks over the client | | packages/typegen | Schema and query compiler with TypeScript, Swift, Kotlin, Dart, and Rust output | | packages/crypto, packages/crdt-yjs | Column encryption and Yjs CRDT integration | | packages/testing | @syncular/testkit, an in-process server and clients | | packages/conformance | The scenario catalog both cores run | | rust/ | Rust client core, command surface, and C FFI | | bindings/ | Tauri, React Native, Swift, Kotlin, and Flutter bindings | | apps/docs | The syncular.dev source |

Development

bun install
bun run check   # typecheck + lint + test

Read AGENTS.md before contributing. It contains the rules that apply to maintainer work and external contributions.

LLMs

This README has had plenty of LLM help. So has the rest of the project: docs, tests, benchmarks, production code. Syncular itself wasn't prompted into existence though; I've spent years on offline-first problems, built debe back in 2019, and studied PowerSync, Zero, Electric, Replicache, Turso, LiveStore, and Jazz closely before writing this engine. The concepts and their first implementations are hand-written, and the checks above apply to every diff no matter where it came from. The full story is at syncular.dev/llms.

Contributions with LLM help are welcome. If a model drafted or rewrote something that's still in your pull request, say so in the description. Read your own diff and be ready to explain it; pull requests pasted straight out of a model are closed without comment.

Chat with me