Skip to content

Commit 83aea31

Browse files
authored
Merge branch 'main' into codex/chunked-payloads-network
2 parents 5006f91 + e522f28 commit 83aea31

11 files changed

Lines changed: 137 additions & 23 deletions

File tree

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<h1>SQLite-Sync</h1>
77
<p><strong>Offline-first sync for SQLite, powered by CRDTs.</strong><br>
8-
Local writes, conflict-free merges, real-time collaboration across devices. Sync to SQLite Cloud, PostgreSQL, or Supabase — no central coordinator required.</p>
8+
Local writes, conflict-free merges, real-time collaboration across devices. Sync to SQLite Cloud, <a href="./docs/postgresql/quickstarts/postgres.md">PostgreSQL</a>, or <a href="./docs/postgresql/quickstarts/supabase-self-hosted.md">Supabase</a> — no central coordinator required.</p>
99

1010
<p>
1111
<a href="https://dashboard.sqlitecloud.io/auth/sign-in"><strong>Free managed instance →</strong></a> ·
@@ -32,15 +32,15 @@
3232

3333
<br>
3434

35-
> **Need a sync backend?** Plug into any PostgreSQL or Supabase instance, or use **[SQLite Cloud CloudSync](https://www.sqlite.ai/pricing)** — managed device sync with auth, ACL, and a free tier for up to 3 devices.
35+
> **Need a sync backend?** Plug into any [PostgreSQL](./docs/postgresql/quickstarts/postgres.md) or [self-hosted Supabase](./docs/postgresql/quickstarts/supabase-self-hosted.md) instance, or use **[SQLite Cloud CloudSync](https://www.sqlite.ai/pricing)**
3636
3737
---
3838

3939
# SQLite Sync
4040

4141
[![sqlite-sync coverage](https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fsqliteai.github.io%2Fsqlite-sync%2F&search=Functions%3A%3C%5C%2Ftd%3E%5Cs*%3Ctd%20class%3D%22headerCovTableEntry(?:Hi|Med|Lo)%22%3E(%5B%5Cd.%5D%2B)%26nbsp%3B%25&replace=%241%25&label=coverage&labelColor=rgb(85%2C%2085%2C%2085)%3B&color=rgb(167%2C%20252%2C%20157)%3B&link=https%3A%2F%2Fsqliteai.github.io%2Fsqlite-sync%2F)](https://sqliteai.github.io/sqlite-sync/)
4242

43-
**SQLite Sync** is a multi-platform extension that turns any SQLite database into a **conflict-free, offline-first replica** that syncs automatically with **[SQLite Cloud](https://sqlitecloud.io/)** nodes, **PostgreSQL** servers, and **Supabase** instances. One function call is all it takes: no backend to build, no sync protocol to implement.
43+
**SQLite Sync** is a multi-platform extension that turns any SQLite database into a **conflict-free, offline-first replica** that syncs automatically with **[SQLite Cloud](https://sqlitecloud.io/)** nodes, **[PostgreSQL](./docs/postgresql/quickstarts/postgres.md)** servers, and **[Supabase](./docs/postgresql/quickstarts/supabase-self-hosted.md)** instances. One function call is all it takes: no backend to build, no sync protocol to implement.
4444

4545
Built on **CRDT** (Conflict-free Replicated Data Types), it guarantees:
4646

@@ -91,7 +91,7 @@ Built on **CRDT** (Conflict-free Replicated Data Types), it guarantees:
9191

9292
### 1. Install
9393

94-
Download a pre-built binary from the [Releases](https://github.com/sqliteai/sqlite-sync/releases) page, or install a platform package (see [full installation guide](./docs/INSTALLATION.md) for platform-specific code examples):
94+
Download a pre-built binary from the [Releases](https://github.com/sqliteai/sqlite-sync/releases) page, or install a platform package (see [full installation guide](./docs/installation.md) for platform-specific code examples):
9595

9696
| Platform | Install |
9797
|----------|---------|
@@ -131,6 +131,8 @@ SELECT * FROM tasks;
131131

132132
### 4. Sync with the cloud
133133

134+
The example below uses SQLite Cloud CloudSync. If you are wiring up a self-hosted backend instead, use the [PostgreSQL quick start](./docs/postgresql/quickstarts/postgres.md) or the [self-hosted Supabase quick start](./docs/postgresql/quickstarts/supabase-self-hosted.md).
135+
134136
```sql
135137
-- Connect to your SQLite Cloud managed database
136138
-- (get the managed database ID from the OffSync page on the SQLite Cloud dashboard)
@@ -188,7 +190,7 @@ SELECT cloudsync_terminate();
188190

189191
Back on Device A, calling `cloudsync_network_sync()` will pull Device B's changes. The CRDT engine ensures all devices converge to the same data, automatically, with no conflicts.
190192

191-
> **Note:** every device participating in the same sync must create **the same set of tables with the same structure** and initialize each one with `cloudsync_init()`. sqlite-sync derives a schema hash from the synced tables, and the server rejects payloads whose hash it does not recognize. For multi-tenant setups where each client should see only a subset of rows, use a shared schema with a tenant/scope column and enforce isolation with [Row-Level Security](./docs/row-level-security.md) — do not give each client a different table.
193+
> **Note:** every device participating in the same sync must create **the same set of tables with the same structure** and initialize each one with `cloudsync_init()`. sqlite-sync derives a schema hash from the synced tables, and the server rejects payloads whose hash it does not recognize. For multi-tenant setups where each client should see only a subset of rows, use a shared schema with a tenant/scope column and enforce isolation with [RLS Overview](./docs/rls-overview.md) — do not give each client a different table.
192194
193195
## Block-Level LWW
194196

@@ -214,21 +216,25 @@ With SQLite Cloud's RLS, a single shared cloud database serves all users while e
214216
- One database, multiple tenants, no per-user database provisioning.
215217
- Each client syncs only authorized rows, minimal bandwidth and storage.
216218

217-
See the full guide: **[Row-Level Security Documentation](./docs/row-level-security.md)**.
219+
See the full guide: **[RLS Overview](./docs/rls-overview.md)**.
218220

219221
## Documentation
220222

221223
- **[API Reference](./API.md)**: all functions, parameters, and examples
222224
- **[Performance & Overhead](./PERFORMANCE.md)**: sync cost model, payload chunking, and large-value memory notes
223225
- **[Installation Guide](./docs/installation.md)**: platform-specific setup (Swift, Android, Expo, React Native, Flutter, WASM)
224226
- **[Block-Level LWW Guide](./docs/block-lww.md)**: line-level text merge for markdown and documents
225-
- **[Row-Level Security Guide](./docs/row-level-security.md)**: multi-tenant access control with server-enforced policies
227+
- **[RLS Overview](./docs/rls-overview.md)**: conceptual guide to multi-tenant access control with server-enforced policies
228+
- **[Self-Hosted PostgreSQL Quick Start](./docs/postgresql/quickstarts/postgres.md)**: run CloudSync against your own PostgreSQL instance
229+
- **[Self-Hosted Supabase Quick Start](./docs/postgresql/quickstarts/supabase-self-hosted.md)**: run CloudSync against your own Supabase deployment
226230
- **[Database Schema Recommendations](./docs/schema.md)**: primary keys, constraints, foreign keys, triggers
227231
- **[Custom Network Layer](./docs/internal/network.md)**: replace the built-in libcurl networking
228232
- **[Examples](./examples/)**: complete walkthroughs (todo app, sport tracker, Swift multiplatform)
229233

230234
## SQLite Cloud Setup
231235

236+
If you are not using SQLite Cloud as the sync backend, see the [self-hosted PostgreSQL quick start](./docs/postgresql/quickstarts/postgres.md) or the [self-hosted Supabase quick start](./docs/postgresql/quickstarts/supabase-self-hosted.md).
237+
232238
1. Sign up at [SQLite Cloud](https://sqlitecloud.io/) and create a project.
233239
2. Create a database and your tables in the [dashboard](https://dashboard.sqlitecloud.io/).
234240
3. Enable synchronization: click **"OffSync"** for your database and select the tables to sync.
File renamed without changes.
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
Download the appropriate pre-built binary for your platform from the official [Releases](https://github.com/sqliteai/sqlite-sync/releases) page:
44

5-
- Linux: x86 and ARM
6-
- macOS: x86 and ARM
7-
- Windows: x86
5+
- Linux: x86_64 and arm64 (glibc and musl)
6+
- macOS: x86_64 and arm64
7+
- Windows: x86_64
88
- Android
99
- iOS
1010

@@ -34,7 +34,7 @@ var stmt: OpaquePointer?
3434
sqlite3_prepare_v2(db, "SELECT cloudsync_version()", -1, &stmt, nil)
3535
defer { sqlite3_finalize(stmt) }
3636
sqlite3_step(stmt)
37-
log("cloudsync_version(): \(String(cString: sqlite3_column_text(stmt, 0)))")
37+
print("cloudsync_version(): \(String(cString: sqlite3_column_text(stmt, 0)))")
3838
sqlite3_close(db)
3939
```
4040

@@ -43,7 +43,7 @@ sqlite3_close(db)
4343
Add the [following](https://central.sonatype.com/artifact/ai.sqlite/sync) to your Gradle dependencies:
4444

4545
```gradle
46-
implementation 'ai.sqlite:sync:1.0.0'
46+
implementation 'ai.sqlite:sync:<latest-version>'
4747
```
4848

4949
```java
@@ -121,6 +121,8 @@ flutter pub add sqlite_sync # Flutter projects
121121
dart pub add sqlite_sync # Dart projects
122122
```
123123

124+
Requires Dart 3.10+ / Flutter 3.38+.
125+
124126
```dart
125127
import 'package:sqlite3/sqlite3.dart';
126128
import 'package:sqlite_sync/sqlite_sync.dart';

docs/internal/postgres-flyio.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,108 @@ docker compose pull db
836836
docker compose up -d db
837837
```
838838

839+
### Update CloudSync from a GitHub Actions PostgreSQL artifact
840+
841+
If you already run the Dockerized PostgreSQL setup from this guide and want to test a newer CloudSync PostgreSQL build without rebuilding and pushing a custom image, you can copy the packaged extension files into the running `db` container.
842+
843+
1. Confirm the running PostgreSQL major version and current CloudSync version:
844+
845+
```bash
846+
cd /data/cloudsync-postgres
847+
docker compose exec db pg_config --version
848+
docker compose exec db psql -U postgres -d postgres -c "SELECT cloudsync_version();"
849+
docker compose exec db psql -U postgres -d postgres -c "SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'cloudsync';"
850+
uname -m
851+
```
852+
853+
For a standard Fly Ubuntu VM running PostgreSQL 17 on x86_64, download the artifact named:
854+
855+
```text
856+
cloudsync-postgresql17-linux-x86_64
857+
```
858+
859+
2. Download the artifact from the GitHub Actions run on your local machine while signed into GitHub, then upload the zip to the VM:
860+
861+
```bash
862+
fly ssh sftp shell --app <your-app-name>
863+
```
864+
865+
At the SFTP prompt:
866+
867+
```text
868+
cd /data
869+
put /absolute/path/to/cloudsync-postgresql17-linux-x86_64.zip cloudsync-postgresql17-linux-x86_64.zip
870+
```
871+
872+
3. SSH back into the VM, extract the package, and verify the files:
873+
874+
```bash
875+
fly ssh console --app <your-app-name>
876+
877+
apt-get update
878+
apt-get install -y unzip
879+
880+
cd /data
881+
mkdir -p cloudsync-package
882+
cd cloudsync-package
883+
cp ../cloudsync-postgresql17-linux-x86_64.zip .
884+
unzip -o cloudsync-postgresql17-linux-x86_64.zip
885+
ls -1
886+
```
887+
888+
The extracted package should contain:
889+
890+
- `cloudsync.so`
891+
- `cloudsync.control`
892+
- `cloudsync--<version>.sql`
893+
- any `cloudsync--<from>--<to>.sql` upgrade scripts
894+
895+
4. Copy those files into the running PostgreSQL container:
896+
897+
```bash
898+
docker cp cloudsync.so cloudsync-postgres:/tmp/
899+
docker cp cloudsync.control cloudsync-postgres:/tmp/
900+
for f in cloudsync--*.sql; do
901+
docker cp "$f" cloudsync-postgres:/tmp/
902+
done
903+
```
904+
905+
5. Install the files into PostgreSQL's extension directories inside the container and restart it:
906+
907+
```bash
908+
docker exec cloudsync-postgres sh -lc 'cp /tmp/cloudsync.so "$(pg_config --pkglibdir)/"'
909+
docker exec cloudsync-postgres sh -lc 'cp /tmp/cloudsync.control /tmp/cloudsync--*.sql "$(pg_config --sharedir)/extension/"'
910+
docker restart cloudsync-postgres
911+
```
912+
913+
6. Verify the new binary version:
914+
915+
```bash
916+
docker exec cloudsync-postgres psql -U postgres -d postgres -c "SELECT cloudsync_version();"
917+
docker exec cloudsync-postgres psql -U postgres -d postgres -c "SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'cloudsync';"
918+
```
919+
920+
If `installed_version` is behind `default_version`, apply the PostgreSQL extension upgrade:
921+
922+
```bash
923+
docker exec cloudsync-postgres psql -U postgres -d postgres -c "ALTER EXTENSION cloudsync UPDATE;"
924+
```
925+
926+
Then verify again:
927+
928+
```bash
929+
docker exec cloudsync-postgres psql -U postgres -d postgres -c "SELECT cloudsync_version();"
930+
docker exec cloudsync-postgres psql -U postgres -d postgres -c "SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'cloudsync';"
931+
```
932+
933+
For example, after a successful `1.0.16 -> 1.1.0` upgrade you should see:
934+
935+
- `cloudsync_version()` returns `1.1.0`
936+
- `default_version = 1.1`
937+
- `installed_version = 1.1`
938+
939+
> **Important:** this is an in-place container modification. It survives a container restart, but it does **not** survive recreating the container from the original image. If you later run `docker compose pull`, change the image tag, or recreate `db`, you will lose the manually copied files unless the image itself already includes that newer CloudSync build.
940+
839941
### View logs
840942

841943
```bash
@@ -852,10 +954,12 @@ docker compose logs -f auth # Auth server only
852954
|---------|----------|
853955
| `fractional_indexing.h: No such file or directory` | Run `git submodule update --init --recursive` before building |
854956
| `cloudsync_version()` not found | Init scripts only run on first start. Run `CREATE EXTENSION IF NOT EXISTS cloudsync;` manually |
957+
| `unzip: command not found` | Install it on the Fly VM first: `apt-get update && apt-get install -y unzip` |
855958
| Auth server won't start | Check `docker compose logs auth`. Ensure `npm install` was run in `auth-server/` |
856959
| Token verification fails (HS256) | Ensure `JWT_SECRET` matches exactly — CloudSync uses the raw string, not base64-decoded |
857960
| Token verification fails (JWKS) | Ensure CloudSync can reach the JWKS endpoint and `JWT_ISSUER` matches the `ISSUER` env var |
858961
| JWKS keys lost after restart | The JWKS server generates new keys on each start. For production, persist keys to a volume |
859962
| Docker commands not found after VM restart | Run `/data/startup.sh` — Fly VM root filesystem resets on stop/start |
860963
| `fuse-overlayfs` not working | Install it: `apt-get install -y fuse-overlayfs` |
964+
| Manual artifact upgrade disappears after `docker compose up` recreation | This method only patches the running container. Rebuild or switch to an image that already contains the new CloudSync build for a durable upgrade. |
861965
| Can't connect to Postgres from outside Fly | Use `fly proxy 5432:5432 -a <your-app-name>` |

docs/postgresql/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,6 @@ The PostgreSQL integration is actively evolving. Current limitations include:
9898

9999
# Next
100100
* [CLIENT](client.md) installation and setup
101-
* [SUPABASE](integrations/supabase.md) configuration and setup
102-
* [SPORT-TRACKER APP](examples/sport-app-supabase.md) demo web app based on SQLite Sync WASM
101+
* [POSTGRES QUICK START](quickstarts/postgres.md) configuration and setup
102+
* [SUPABASE QUICK START](quickstarts/supabase-self-hosted.md) configuration and setup
103+
* [SPORT-TRACKER APP](examples/sport-tracker-app-supabase.md) demo web app based on SQLite Sync WASM
File renamed without changes.

docs/postgresql/examples/sport-tracker-app-supabase.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A Vite/React demonstration app showcasing [**SQLite Sync**](https://github.com/s
44

55
> This app uses the packed WASM version of SQLite with the [SQLite Sync extension enabled](https://www.npmjs.com/package/@sqliteai/sqlite-wasm).
66
7-
**The source code is located in [examples/sport-tracker-app](../../examples/sport-tracker-app/)**
7+
**The source code is located in [examples/sport-tracker-app](../../../examples/sport-tracker-app/)**
88

99
## Setup Instructions
1010

@@ -13,7 +13,7 @@ A Vite/React demonstration app showcasing [**SQLite Sync**](https://github.com/s
1313

1414
### 2. Database Setup
1515
1. Create database
16-
2. Execute the schema with [sport-tracker-schema-postgres.sql](../../examples/sport-tracker-app/sport-tracker-schema-postgres.sql).
16+
2. Execute the schema with [sport-tracker-schema-postgres.sql](../../../examples/sport-tracker-app/sport-tracker-schema-postgres.sql).
1717
3. Enable CloudSync for all tables on the remote database with:
1818
```sql
1919
CREATE EXTENSION IF NOT EXISTS cloudsync;
@@ -40,4 +40,4 @@ npm run dev
4040
4141
### Demo
4242
43-
Continue reading on the official [README](https://github.com/sqliteai/sqlite-sync/blob/main/examples/sport-tracker-app/README.md#demo-use-case-multi-user-sync-scenario).
43+
Continue reading on the official [README](https://github.com/sqliteai/sqlite-sync/blob/main/examples/sport-tracker-app/README.md#demo-use-case-multi-user-sync-scenario).

docs/postgresql/examples/todo-app-postgres.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ cd MyApp
1616

1717
### 2. Setup
1818

19-
1. Execute the exact schema from [`to-do-app.sql`](../../examples/to-do-app/to-do-app.sql).
19+
1. Execute the exact schema from [`to-do-app.sql`](../../../examples/to-do-app/to-do-app.sql).
2020
2. Enable CloudSync for all tables on the remote database with:
2121
```sql
2222
CREATE EXTENSION IF NOT EXISTS cloudsync;
@@ -61,4 +61,3 @@ npm run ios # or android
6161
- **Dropdown Menu** - Select categories for tasks from a predefined list.
6262
- **Cross-Platform** - Works on iOS and Android
6363
- **Offline Support** - Works offline, syncs when connection returns
64-
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
# Row-Level Security
1+
# RLS Overview
22

33
SQLite Sync supports **Row-Level Security (RLS)** through the underlying [SQLite Cloud](https://sqlitecloud.io/) infrastructure. RLS allows you to use a **single shared cloud database** while each client only sees and modifies its own data. Policies are enforced on the server, so the security boundary is at the database level, not in application code.
44

5+
> **Using self-hosted PostgreSQL or Supabase?** Start with the backend-specific [RLS Reference](./postgresql/reference/rls.md) for CloudSync apply behavior, PostgreSQL roles and grants, Supabase notes, and troubleshooting.
6+
57
## How It Works
68

79
- Control not just who can read or write a table, but **which specific rows** they can access.
@@ -44,6 +46,6 @@ CREATE TABLE users (
4446
);
4547
```
4648

47-
For more schema guidelines, see [Database Schema Recommendations](./schema.md).
49+
For more schema guidelines, see [Database Schema Recommendations](./schema.md). If you are running a self-hosted backend, also see the PostgreSQL/Supabase [RLS Reference](./postgresql/reference/rls.md).
4850

4951
For full RLS documentation, see the [SQLite Cloud RLS documentation](https://docs.sqlitecloud.io/docs/rls).

docs/SCHEMA.md renamed to docs/schema.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ sqlite-sync computes a **schema hash** from the synced tables and includes it in
1616
cloudsync operation failed: Cannot apply the received payload because the schema hash is unknown <hash>
1717
```
1818

19-
If you need different clients to see different subsets of data (for example, per-tenant or per-workspace isolation), do **not** give each client a different table. Instead, use a single shared schema and scope the data with a column such as `tenant_id` or `workspace_id`, then enforce isolation server-side with [Row-Level Security](./row-level-security.md).
19+
If you need different clients to see different subsets of data (for example, per-tenant or per-workspace isolation), do **not** give each client a different table. Instead, use a single shared schema and scope the data with a column such as `tenant_id` or `workspace_id`, then enforce isolation server-side with [RLS Overview](./rls-overview.md).
2020

2121
## Primary Key Requirements
2222

0 commit comments

Comments
 (0)