Kotlin Multiplatform libraries for CRDT-based sync, plus a collaborative todo app demonstrating them.
:crdt—com.doppio.syncdo:crdt— generic CRDT primitives (VectorClock,LwwRegister,OrSet,Delta/DeltaState,DeltaBuffer).:sync—com.doppio.syncdo:sync— generic WebSocket sync engine + Ktor server routing. Parameterized on anyDelta<D>from:crdt.
Targets: Android, iOS (arm64 + simulatorArm64), JVM.
:shared— Todo domain glue:TodoListCrdt,TodoListDelta,OfflineFirstTodoRepository, file-based persistence.:composeApp— Compose Multiplatform UI (Android, iOS, Desktop).:server— Ktor sample server wiring the genericsyncEndpointwith Todo CRDT types.
┌────────────────────────────────────────┐ ┌────────────┐
│ composeApp (Android / iOS / JVM) │ │ server │
└──────────────────┬─────────────────────┘ └─────┬──────┘
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ :shared (sample) │
│ TodoListCrdt · TodoListDelta · OfflineFirstRepository │
└─────────────────────┬──────────────────────┬─────────────┘
▼ ▼
┌──────────────────┐ ┌────────────────────┐
│ :sync (library) │──▶│ :crdt (library) │
│ SyncEngine<D> │ │ VectorClock │
│ SyncServer<S,D> │ │ LwwRegister │
│ SyncMessage<D> │ │ OrSet · Delta │
└──────────────────┘ └────────────────────┘
Architecture deep-dive: docs/crdt-implementation.md.
// settings.gradle.kts
dependencyResolutionManagement {
repositories { mavenLocal(); mavenCentral() }
}
// build.gradle.kts (KMP, commonMain)
dependencies {
implementation("com.doppio.syncdo:crdt:0.1.0-SNAPSHOT")
implementation("com.doppio.syncdo:sync:0.1.0-SNAPSHOT")
}Quickstarts: :crdt README (build a CRDT aggregate),
:sync README (client engine + server route).
Prereqs: JDK 11+, Android Studio / IntelliJ with KMP plugin, two devices or emulators for the sync demo.
./gradlew :server:run # Port 8080
./gradlew :composeApp:run # Desktop
./gradlew :composeApp:assembleDebug # Android APK
# iOS: open iosApp/ in XcodeEnter the server's LAN IP in each client (or localhost if on the same host).
- Real-time sync — Add a task on device A, it appears on B within ~1s.
- Concurrent different fields — Edit title on A and note on B simultaneously; both survive.
- Concurrent same field — Both devices converge to the later-timestamp write (LWW, node-id tiebreak).
- Offline buffering — Disable sync on A, mutate, re-enable; pending deltas push cleanly.
- Add-wins — Concurrent edit + delete → edit wins (OR-Set semantics).
./gradlew build # Everything
./gradlew :crdt:allTests :sync:allTests # Library tests (incl. iOS sim)
./gradlew :shared:jvmTest :server:test # Sample tests
./gradlew :crdt:apiCheck :sync:apiCheck # Binary-compat guard
./gradlew :crdt:publishToMavenLocal \
:sync:publishToMavenLocal # Local publish