Architecture
Multi-Device Synchronization Architecture
go-oversync implements a bundle-based replication model for SQLite clients and PostgreSQL servers. This page describes the current architecture in this repository, not earlier protocol iterations.
Overview
The current architecture has four main pieces:
- your application HTTP server
oversync.SyncServiceon PostgreSQLoversqlite.Clienton SQLite- the bundle/snapshot HTTP protocol between them
How It Works
- Your server registers the PostgreSQL tables that participate in sync.
Bootstrap()validates the server schema and prepares the sync metadata/runtime topology.- The SQLite client runs
Open()on launch, thenAttach(userID)after sign-in. - The SQLite client tracks local writes in
_sync_dirty_rowswith triggers. AfterDetach(), those triggers stay active so offline anonymous writes continue to enqueue local dirty state immediately. PushPending()freezes those writes into_sync_outbox_*, uploads them through/sync/push-sessions, then replays the authoritative committed bundle returned by the server. Process restart keeps that singleton outbox durable and resumes from it instead of recollecting a new local bundle.PullToStable()reads complete committed bundles from/sync/pulland advances the local checkpoint only after durable local apply.Rebuild(ctx)rebuilds managed tables from/sync/snapshot-sessionswhen a client is new or falls behind retained history. For stale/out-of-order source recovery,oversqlitechooses the rebuild-plus-rotate path internally. The server materializes those frozen snapshots inside PostgreSQL session tables and serves chunk reads statelessly bysnapshot_id. During rotated recovery the client persists one replacement source id durably, the server reserves that replacement source explicitly, and the old source is retired fail-closed until rebuild finishes.
flowchart LR
subgraph Clients["SQLite Clients"]
DeviceA["Device A<br/>(oversqlite)"]
DeviceB["Device B<br/>(compatible client)"]
end
subgraph Server["Server"]
HTTPServer["Your HTTP Server"]
SyncService["oversync.SyncService"]
PostgreSQL["PostgreSQL"]
end
DeviceA <-->|"push / pull / snapshot"| HTTPServer
DeviceB <-->|"push / pull / snapshot"| HTTPServer
HTTPServer --> SyncService
SyncService --> PostgreSQL
Authoritative State
PostgreSQL business tables are authoritative. Sync metadata is derived from committed
business-table effects and stored under the sync schema.
Important runtime tables include:
sync.metasync.table_catalogsync.user_statesync.scope_statesync.source_statesync.row_statesync.bundle_logsync.bundle_rowssync.push_sessionssync.push_session_rowssync.snapshot_sessionssync.snapshot_session_rows
The server uses compact internal identities in hot sync tables: user_pk for users, table_id
for registered tables, key_bytes for row keys, and op_code for operations. Visible protocol
fields such as schema, table, key, op, row_version, and payload JSON are reconstructed
at the HTTP boundary.
sync.bundle_log is the durable committed-bundle metadata and accepted-push replay source. Bundle
history is retained only above each user’s retained_bundle_floor; below that floor, clients
recover through snapshot rebuild rather than best-effort historical replay.
Server-Originated Writes
If your application writes registered PostgreSQL tables outside client push handling, it should do
so through ScopeManager.ExecWrite(...) in the common case, or WithinSyncBundle(...) if your
application needs the lower-level primitive directly. Both paths ensure the committed business
transaction is captured as one sync bundle visible to other clients. See
Server-Originated Writes for the
runtime contract and examples.
Core Concepts
Core Concepts →
Read the core concepts page for the exact vocabulary used by the current contract.