Skip to content

Synchro

Offline-first sync between PostgreSQL and native client SDKs for Swift, Kotlin, and React Native. Your tables. Minimal changes.

import { Card, CardGrid } from ‘@astrojs/starlight/components’;

`query()`, `execute()`, transactions, batch writes, prepared statements, and reactive observation. Plain SQL with parameter binding. No ORM, no proprietary query language. Clients read and write locally. Changes push to the server and pull to other devices automatically. Works offline, syncs when connectivity returns. PostgreSQL logical replication captures changes at the database level. No triggers on your tables, no polling, no audit tables, no shadow tables. Push writes execute under `SET LOCAL app.user_id`. PostgreSQL row-level security enforces access. Authorization lives in the database, not application code.
flowchart TB
    subgraph Client["Client Device"]
        direction LR
        APP[Your App] -- "query / execute" --> SDK
        subgraph SDK["Native SDK"]
            direction TB
            DB[(SQLite)]
            CDC[CDC Triggers]
            PQ[Pending Queue]
            DB --> CDC --> PQ
        end
    end

    subgraph Server["Go Server"]
        direction TB
        PG[("PostgreSQL")]
        WAL[WAL Consumer]
        CL[Changelog]
        PG --> WAL --> CL
    end

    PQ -- "push" --> PG
    CL -- "pull" --> DB

Your app writes standard SQL to a local SQLite database. CDC triggers detect changes and queue them for push. The Go server uses PostgreSQL WAL to detect changes and serves them to clients via pull. Conflicts are resolved automatically using last-writer-wins with configurable strategies.


// Swift
try client.execute("INSERT INTO tasks (id, user_id, title) VALUES (?, ?, ?)",
params: [uuid, userId, "Ship v1"])
let tasks = try client.query("SELECT * FROM tasks WHERE completed = 0")
// Kotlin
client.execute("INSERT INTO tasks (id, user_id, title) VALUES (?, ?, ?)",
arrayOf(uuid, userId, "Ship v1"))
val tasks = client.query("SELECT * FROM tasks WHERE completed = 0")
// React Native (bridges to native Swift and Kotlin SDKs)
await client.execute('INSERT INTO tasks (id, user_id, title) VALUES (?, ?, ?)',
[uuid, userId, 'Ship v1']);
const tasks = await client.query('SELECT * FROM tasks WHERE completed = 0');

Import as a Go library into your existing server. Add sync routes to your router. No extra processes. Run `synchrod` as a dedicated binary. Or split the WAL consumer and HTTP handlers across services for scale.

Same library. Same protocol. Same SDKs. Moving between modes is a configuration change, not a rewrite.


Follow the [quick start guide](/synchro/getting-started/quickstart/) to set up sync in minutes. Read the [architecture docs](/synchro/server/architecture/) to understand the sync protocol in depth.