
Offline-first sync engine with durable outbox, optimistic local writes, push/pull loop, per-entity conflict strategies (LWW, merge, git-like, CRDT), and pluggable transports/stores.
Offline-first sync engine for Kotlin Multiplatform — outbox, push/pull, and conflict policy on top of your entity store.
Latest on Maven Central: 2.0.0 · Changelog · Upgrade from 1.1.0
Monorepo (main): 2.0.1 — Integration DX (BYO store + transport APIs stable); Changelog § 2.0.1. Not on Central until published — install pins stay at 2.0.0.
SyncForge is a client-side sync engine, not a hosted backend or a replacement for Room/SQLDelight/Realm.
| You own | SyncForge owns |
|---|---|
| Entity tables and schema | Durable outbox + conflict store (SQLDelight) |
Domain JSON (payloadJson shape) |
Push/pull loop, retries, pull cursor |
| UI and product rules | Per-entity conflict policy (LWW, merge, git-like, CRDT fields, defer-to-user) |
| Server that speaks the REST push/pull contract (or an adapter) | Optional transports (REST/Ktor, GraphQL, Supabase, Firebase, custom) |
Local writes go to your store first (optimistic). Mutations enqueue; sync drains the outbox when the network is available. Offline edits and app restarts are first-class.
UI / navigation: SyncForge is not a navigation or MVI framework. For Compose multiplatform navigation and sync-aware chrome, see ForgeNav (
studio.forgenav).
Good fit when you need:
sync() / scheduled syncNot a fit when you want:
| Area | Status |
|---|---|
| Maven Central |
2.0.0 published under studio.syncforge — core, optional modules, version catalog, Android Gradle plugin |
Monorepo (main) |
2.0.1 — same artifact set + Integration DX (see below); publish via tag when ready |
| Android | Stable primary path — Room (or BYO store), WorkManager, Compose helpers, KSP handlers |
| iOS / JVM desktop / macOS | Stable platform DSLs — integrate as KMP framework (iOS); SPM/XCFramework still planned |
| Conflicts |
Stable — LWW, always local/remote, merge, gitLike, crdt, deferToUser
|
| Transports / BYO store |
Stable on main (2.0.1) — REST, GraphQL, Supabase, Firebase, EntityStore (no @OptIn) |
| Auth DSL | Built-in register/login/refresh helpers when your API matches the expected shape |
| Debug / tracing / web |
Experimental — @OptIn(ExperimentalSyncForgeApi::class); shapes may change |
Browser js |
Sources + :sample-web in this monorepo only — not on Maven Central |
| Backend | Contract + starters (:mock-server, :backend-starter*) — you run and operate the server |
Wire format: REST v1 (POST /sync/push, GET /sync/pull) is frozen for the 2.x line. Entity payload schemas are your domain, not SyncForge semver. Details: REST_API.md, MODULES.md (stability table).
./gradlew :mock-server:run # Terminal 1 — reference API on :8080
./gradlew :sample:installDebug # Terminal 2 — emulator uses http://10.0.2.2:8080In the sample: Sync, force a conflict, or clear local data and pull. Debug builds expose an SF overlay for outbox depth and sync health.
Import the version catalog once (2.0.0 pins every artifact and the Gradle plugin):
// settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("syncforge") {
from("studio.syncforge:syncforge-catalog:2.0.0")
}
}
}// app/build.gradle.kts
plugins {
alias(syncforge.plugins.syncforge.android)
}
dependencies {
implementation(syncforge.core)
}// Application
syncManager = SyncForge.android(this) {
baseUrl("https://api.example.com")
registry(SyncForgeHandlers.registry(taskDao))
schedulePeriodicSyncOnStart()
}
// Repository — write your DB, then enqueue
syncManager.enqueueChange(Change.create("tasks", task))
syncManager.sync()Full walkthrough (~10 min): Getting Started
iOS / desktop: iOS setup · Desktop setup
| Platform | Entry point | Distribution | Sample |
|---|---|---|---|
| Android | SyncForge.android { } |
Maven Central | :sample |
| iOS | SyncForge.ios { } |
Maven Central (KMP framework; not SPM yet) | :ios-sample |
| JVM desktop | SyncForge.desktop { } |
Maven Central | :sample-desktop |
| macOS native | SyncForge.macos { } |
Maven Central | — |
| Browser |
SyncForge.web { } (experimental)
|
Monorepo / composite / publishToMavenLocal only |
:sample-web |
Gradle snippets and DSL options live in the setup guides — not duplicated here.
These are intentional product boundaries, not temporary bugs:
payloadJson. Additive JSON changes are straightforward; breaking renames/refactors need your Room migration and a plan for pending outbox / server history. There is no built-in schemaVersion migrate API yet.crdt { } / gitLike { } help merge concurrent edits; they are not a Notion-style collaborative document layer.Deeper guidance: Best practices · Conflict resolution · Roadmap.
| All features + samples | Feature catalog |
| Copy-paste recipes | Recipes |
| API / stability | Modules |
| Backend contract | REST API |
| Conflicts | Conflict resolution |
| Navigation + MVI (Compose) | ForgeNav |
| Doc index | docs/README.md |
| Module | Role |
|---|---|
:sample |
Android multi-entity sync, gitLike conflicts, debug overlay |
:mock-server |
Reference REST backend + conflict simulation |
:backend-starter / :backend-starter-spring
|
Ktor / Spring starters |
:sample-ios-shared + :ios-sample
|
iOS path |
:sample-desktop |
JVM desktop |
:sample-web |
Browser (monorepo-only) |
| Concern | Library |
|---|---|
| Outbox, transports, conflict store, push/pull | SyncForge (this repo) |
| Routes, backstack, MVI, optimistic UI, sync banners | ForgeNav |
Contributing and local verification: CONTRIBUTING.md.
git clone https://github.com/Arsenoal/syncforge.git
cd syncforge
./gradlew verifyReleaseSignOffOffline-first sync engine for Kotlin Multiplatform — outbox, push/pull, and conflict policy on top of your entity store.
Latest on Maven Central: 2.0.0 · Changelog · Upgrade from 1.1.0
Monorepo (main): 2.0.1 — Integration DX (BYO store + transport APIs stable); Changelog § 2.0.1. Not on Central until published — install pins stay at 2.0.0.
SyncForge is a client-side sync engine, not a hosted backend or a replacement for Room/SQLDelight/Realm.
| You own | SyncForge owns |
|---|---|
| Entity tables and schema | Durable outbox + conflict store (SQLDelight) |
Domain JSON (payloadJson shape) |
Push/pull loop, retries, pull cursor |
| UI and product rules | Per-entity conflict policy (LWW, merge, git-like, CRDT fields, defer-to-user) |
| Server that speaks the REST push/pull contract (or an adapter) | Optional transports (REST/Ktor, GraphQL, Supabase, Firebase, custom) |
Local writes go to your store first (optimistic). Mutations enqueue; sync drains the outbox when the network is available. Offline edits and app restarts are first-class.
UI / navigation: SyncForge is not a navigation or MVI framework. For Compose multiplatform navigation and sync-aware chrome, see ForgeNav (
studio.forgenav).
Good fit when you need:
sync() / scheduled syncNot a fit when you want:
| Area | Status |
|---|---|
| Maven Central |
2.0.0 published under studio.syncforge — core, optional modules, version catalog, Android Gradle plugin |
Monorepo (main) |
2.0.1 — same artifact set + Integration DX (see below); publish via tag when ready |
| Android | Stable primary path — Room (or BYO store), WorkManager, Compose helpers, KSP handlers |
| iOS / JVM desktop / macOS | Stable platform DSLs — integrate as KMP framework (iOS); SPM/XCFramework still planned |
| Conflicts |
Stable — LWW, always local/remote, merge, gitLike, crdt, deferToUser
|
| Transports / BYO store |
Stable on main (2.0.1) — REST, GraphQL, Supabase, Firebase, EntityStore (no @OptIn) |
| Auth DSL | Built-in register/login/refresh helpers when your API matches the expected shape |
| Debug / tracing / web |
Experimental — @OptIn(ExperimentalSyncForgeApi::class); shapes may change |
Browser js |
Sources + :sample-web in this monorepo only — not on Maven Central |
| Backend | Contract + starters (:mock-server, :backend-starter*) — you run and operate the server |
Wire format: REST v1 (POST /sync/push, GET /sync/pull) is frozen for the 2.x line. Entity payload schemas are your domain, not SyncForge semver. Details: REST_API.md, MODULES.md (stability table).
./gradlew :mock-server:run # Terminal 1 — reference API on :8080
./gradlew :sample:installDebug # Terminal 2 — emulator uses http://10.0.2.2:8080In the sample: Sync, force a conflict, or clear local data and pull. Debug builds expose an SF overlay for outbox depth and sync health.
Import the version catalog once (2.0.0 pins every artifact and the Gradle plugin):
// settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("syncforge") {
from("studio.syncforge:syncforge-catalog:2.0.0")
}
}
}// app/build.gradle.kts
plugins {
alias(syncforge.plugins.syncforge.android)
}
dependencies {
implementation(syncforge.core)
}// Application
syncManager = SyncForge.android(this) {
baseUrl("https://api.example.com")
registry(SyncForgeHandlers.registry(taskDao))
schedulePeriodicSyncOnStart()
}
// Repository — write your DB, then enqueue
syncManager.enqueueChange(Change.create("tasks", task))
syncManager.sync()Full walkthrough (~10 min): Getting Started
iOS / desktop: iOS setup · Desktop setup
| Platform | Entry point | Distribution | Sample |
|---|---|---|---|
| Android | SyncForge.android { } |
Maven Central | :sample |
| iOS | SyncForge.ios { } |
Maven Central (KMP framework; not SPM yet) | :ios-sample |
| JVM desktop | SyncForge.desktop { } |
Maven Central | :sample-desktop |
| macOS native | SyncForge.macos { } |
Maven Central | — |
| Browser |
SyncForge.web { } (experimental)
|
Monorepo / composite / publishToMavenLocal only |
:sample-web |
Gradle snippets and DSL options live in the setup guides — not duplicated here.
These are intentional product boundaries, not temporary bugs:
payloadJson. Additive JSON changes are straightforward; breaking renames/refactors need your Room migration and a plan for pending outbox / server history. There is no built-in schemaVersion migrate API yet.crdt { } / gitLike { } help merge concurrent edits; they are not a Notion-style collaborative document layer.Deeper guidance: Best practices · Conflict resolution · Roadmap.
| All features + samples | Feature catalog |
| Copy-paste recipes | Recipes |
| API / stability | Modules |
| Backend contract | REST API |
| Conflicts | Conflict resolution |
| Navigation + MVI (Compose) | ForgeNav |
| Doc index | docs/README.md |
| Module | Role |
|---|---|
:sample |
Android multi-entity sync, gitLike conflicts, debug overlay |
:mock-server |
Reference REST backend + conflict simulation |
:backend-starter / :backend-starter-spring
|
Ktor / Spring starters |
:sample-ios-shared + :ios-sample
|
iOS path |
:sample-desktop |
JVM desktop |
:sample-web |
Browser (monorepo-only) |
| Concern | Library |
|---|---|
| Outbox, transports, conflict store, push/pull | SyncForge (this repo) |
| Routes, backstack, MVI, optimistic UI, sync banners | ForgeNav |
Contributing and local verification: CONTRIBUTING.md.
git clone https://github.com/Arsenoal/syncforge.git
cd syncforge
./gradlew verifyReleaseSignOff