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:
- Offline-first mobile (or multiplatform) apps with your own API
- An outbox that survives process death, with explicit
sync()/ scheduled sync - Per-entity conflict strategies without rewriting a sync stack
- Android-first delivery, with iOS / JVM desktop / macOS on the same model
- Self-hosted or BYO backend (Ktor/Spring starters and a mock server are in-repo)
Not a fit when you want:
- A managed BaaS that owns your schema and auth end-to-end (use Supabase/Firebase as a transport, or pick a full BaaS product)
- Automatic domain schema evolution for breaking JSON changes (you migrate Room + payloads; SyncForge does not version your entity model)
- Real-time collaborative editing as the primary mode (push/pull + optional realtime adapters; not a full CRDT document product)
- Drop-in Swift Package Manager / XCFramework install today (use KMP frameworks via Gradle — iOS setup)
- Production browser install from Maven Central (web is experimental and monorepo-only — Web setup)
| 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:
- Entity schema is yours — SyncForge stores opaque
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-inschemaVersionmigrate API yet. - You need a backend (or adapter) — SyncForge does not host data. Implement the push/pull contract or use a provided transport against Supabase/Firebase/GraphQL.
- Not whole-document CRDT collab — Field-level
crdt { }/gitLike { }help merge concurrent edits; they are not a Notion-style collaborative document layer. - iOS install path — KMP framework from Gradle today; SwiftPM / XCFramework packaging is still on the roadmap.
- Web is experimental — not published to Central; SQL.js worker stack; opt-in required.
- Optional modules are non-transitive — store, transport, and integration artifacts are listed in the catalog; add them explicitly when needed.
- Multi-device E2E and samples prove the engine — production hardening (auth, rate limits, observability) is documented; your ops and threat model remain yours.
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