I've been obsessed with the Ink & Switch "Local-First Software" vision for some time now—apps that work offline, feel instant, and give users control of their data. But every time I tried to build a local-first app, I hit the same wall: integration complexity.
The existing CRDT libraries (Yjs, Automerge) are incredible—they pioneered the algorithms that make local-first work. Kevin's optimization work on Yjs is masterful, and Automerge changed how I think about distributed systems.
But they're designed as modular primitives. You compose networking, storage, and conflict resolution yourself. That flexibility is powerful for complex systems, but it creates what I call the "Day 1 problem" for local-first: you spend 3 days wiring infrastructure before writing features.
I built SyncKit to solve this.
Different philosophy: Where Yjs gives you powerful primitives (build your own local-first stack), SyncKit gives you a pre-wired local-first database. Persistence, sync, presence—built-in, not plugins.
The technical approach:
- Rust core with zero unsafe blocks (you can grep the codebase)
- TLA+ verified (explored 6.5M states to prove correctness)
- 154KB bundle (everything compiled together)
- 1,081 tests, 80 chaos tests
Performance: Competitive within 1-2ms of Yjs for standard ops. The win is local-first DX - one package that handles offline, sync, and conflict resolution.
What's included:
- Rich text editing (Fugue algorithm for non-interleaving edits)
- Live cursors & presence
- Undo/redo (CRDT-native)
- IndexedDB persistence
- React/Vue/Svelte adapters
Built this in Nairobi, 9pm-3am. v0.1.0 got 601 stars in 3 weeks which honestly shocked me.
That's a fair point. The description does assume some familiarity with local-first patterns. I'll think about how to make the "why you'd want this" clearer for people outside that space. I appreciate the honest feedback
That's actually a really cool idea! Notes are just documents, so the sync model would work well. Hadn't thought about Obsidian specifically but I like it.
Currently I am leaning more into P2P for the zero-effort user side setup, although I was considering if hybrid P2P/client-server approach is feasible: free lite P2P vs paid, managed SaaS for user convenience and improved performance.
"Zero data loss" means system-level guarantees (no corruption from network failures, crashes, etc), not that concurrent edits to the same field are preserved. LWW definitely clobbers one of the edits.
What v0.1.0 does give you is field-level granularity (edits to different fields don't conflict) and guaranteed convergence (all devices reach the same state). But yeah, same field = last write wins.
The Rust core already has better CRDTs (Text, Counter, Set) that handle concurrent edits properly. They just need to exposed in the TypeScript SDK for v0.2.0.
I should clarify this in the docs. Thanks for pointing it out.
Thanks! For native Android, not directly yet because v0.1.0 is Rust → WASM → TypeScript (web/Node/Deno/Bun).
But since the core sync engine is pure Rust, native Android bindings are definitely possible. The main paths would be:
1. JNI bindings directly from Rust
2. Or using the existing WASM core with a JVM WASM runtime
Haven't prioritized this yet since v0.1.0 focused on web/JS ecosystem, but if there's demand for Android, I could explore it. Are you working on something that needs offline sync on Android?
Thanks! Yeah, Realm's shift in direction left a gap in the offline-first space.
I didn't build Dart bindings yet (Rust → WASM → TypeScript for v0.1.0), but since the core is in Rust, Dart bindings via dart_rust_bridge are definitely feasible. Would actually be a great addition.
Are you actively looking for a Realm replacement for a Dart project? Curious what your offline sync requirements are. It might help me prioritize Dart support
I use Loro CRDT, which is written in Rust as well, on my Dart apps with flutter_rust_bridge and it works pretty well. For syncing I'm using their Loro protocol product although I used iroh before. However, the annoying thing about how the CRDT libraries work is that they're essentially key value NoSQL type storage, not relational. I would love something like SQLite or Postgres on both the server and client that can sync, which companies like ElectricSQL have, but no Dart bindings and also I'd prefer to be in the all Rust ecosystem as well as self host.
You've hit on the core limitation because SyncKit has the same issue. Document/key-value like Loro and Yjs. The relational model problem is fundamental to CRDTs. They guarantee conflict-free convergence because documents are independent, but that breaks with foreign keys and joins. Can't have arbitrary SQL queries and guaranteed convergence without coordination.
ElectricSQL and PowerSync solve it differently (full SQL but weaker offline guarantees). For SyncKit, document-based state is an architectural choice, not something v0.2.0 will change.
I'm curious to know how you're working around this with Loro. Are you building your own query layer or are you keeping everything denormalized?
Yeah I'm essentially writing my own ORM with functions like getFoo which queries the exact substructure of the CRDT to find the info I need. Apparently Postgres is getting a CRDT extension though but we'll see how it goes, typically it's not that fast.
Not yet, but it's definitely something I'm considering.
Right now v0.1.0 is React (web) only. I'm planning Vue/Svelte for v0.2.0, and React Native could absolutely be v0.3.0 or sooner if there's demand.
The nice thing is the core sync engine is Rust → WASM, so it's framework-agnostic. The main work is just creating the React Native bindings and not rebuilding the sync logic.
Are you working on something with React Native? Curious what your offline sync needs are because it'd help me prioritize what to build next.
Hey HN, I built SyncKit after shipping two local-first projects (RestBolt and Graft) and realizing there's no simple way to add cross-device sync.
The problem: Existing solutions are either too complex (Automerge/Yjs require learning CRDTs) or too restrictive (Firebase isn't truly local-first, Supabase has no offline support - issue #357 has been open 4+ years with 350+ upvotes).
SyncKit is the middle ground: simple API, works offline-first, self-hostable.
Technical highlights:
- TLA+ formal verification: 118,711 states checked, caught bugs before implementation
- Rust → WASM core (48.9KB gzipped)
- 700+ tests including 80 chaos tests (zero data loss)
- Server: Bun/Hono WebSocket (SDK works in any JS runtime)
- Production-ready: v0.1.0 on npm and Docker Hub
Known limitations (v0.1.0):
- LWW only - advanced CRDTs (Text, Counter, Set) coming in v0.2.0
- React hooks only - Vue/Svelte adapters planned
- Reference server is Bun (Node/Deno coming v0.3.0)
Happy to answer questions about the CRDT implementation, TLA+ modeling, or WebSocket architecture.
See my response to skybrian above. You're both asking the same good question about the LWW messaging. Short version: system-level guarantees, not user-level concurrent edit preservation.
I've built my own sync engine in rust to run against supabase. It doesn't run the browser though, it's for a native app (tauri in my case). It's column level lww.
I have one place where I'm thinking I might need a crdt due to the complexity of the data and how you collaborate with it.
People take my app offline for long periods of time then reconnect with 1000s of records to sync so my sync is backgrounded and built to deal with several mb of data in sqlite files.
Anyway I'm interested in taking a further look at your thing over the holidays to see if it's worth switching or making partial use of it.
Thanks! The TLA+ modeling actually caught 3 bugs even before I wrote any code. Worth the upfront investment. It's way easier to debug a state machine model than distributed sync logic.
Happy to share more about the verification approach if you're interested!
Please do in the repo, and thank you for the wonderful contribution on multiple fronts. This is very well done work and documentation. A few tips to help others discover the value on the Readme: Add the key memory benchmark (currently just above the acknowledgments) to the top of the Readme and link to the rest of the benchmarks on the page that you already have (maybe rename it benchmarks.md in the process): https://github.com/Dancode-188/synckit/blob/main/docs/guides...
That's a great suggestion. The memory benchmark being buried is a fair point. I'll move it up top and create a proper benchmarks.md page. Thanks for the detailed feedback.
I appreciate the enthusiasm! And yeah, I'm planning to start blogging about the build process and some of the technical decisions probably after launch week settles down. I'll post about it on LinkedIn and in the GitHub Discussions when it's live. If you're following either, you'll see it!
I've been obsessed with the Ink & Switch "Local-First Software" vision for some time now—apps that work offline, feel instant, and give users control of their data. But every time I tried to build a local-first app, I hit the same wall: integration complexity.
The existing CRDT libraries (Yjs, Automerge) are incredible—they pioneered the algorithms that make local-first work. Kevin's optimization work on Yjs is masterful, and Automerge changed how I think about distributed systems.
But they're designed as modular primitives. You compose networking, storage, and conflict resolution yourself. That flexibility is powerful for complex systems, but it creates what I call the "Day 1 problem" for local-first: you spend 3 days wiring infrastructure before writing features.
I built SyncKit to solve this.
Different philosophy: Where Yjs gives you powerful primitives (build your own local-first stack), SyncKit gives you a pre-wired local-first database. Persistence, sync, presence—built-in, not plugins.
The technical approach: - Rust core with zero unsafe blocks (you can grep the codebase)
- TLA+ verified (explored 6.5M states to prove correctness)
- 154KB bundle (everything compiled together)
- 1,081 tests, 80 chaos tests
Performance: Competitive within 1-2ms of Yjs for standard ops. The win is local-first DX - one package that handles offline, sync, and conflict resolution.
What's included: - Rich text editing (Fugue algorithm for non-interleaving edits)
- Live cursors & presence
- Undo/redo (CRDT-native)
- IndexedDB persistence
- React/Vue/Svelte adapters
Built this in Nairobi, 9pm-3am. v0.1.0 got 601 stars in 3 weeks which honestly shocked me.
Try it live: https://synckit-demo.netlify.app GitHub: https://github.com/Dancode-188/synckit
Happy to answer questions about the TLA+ verification or the local-first architecture choices.