Multi-device sync
Copy page
Multi-device sync is scoped bucket convergence — not full-database replication.
Shared tenancy model
Section titled “Shared tenancy model”- Authenticate each device with the same user (or shared shop membership)
- Resolve buckets from sync-rule
parameters(membership table or JWT claims) - Each device calls
bucketsForSyncRulewith the buckets it cares about - All devices pull/push against the same server Postgres
Web + mobile
Section titled “Web + mobile”| Layer | Web | React Native |
|---|---|---|
| Persistence | waSqlitePersistence | opSqlitePersistence |
| HTTP | fetch | installNitroFetch / createNizhalNitroClient |
| Realtime | PartySocket default | nitroWebSocketSource |
| Online detection | navigator.onLine | reactNativeOnlineDetector (NetInfo) |
Use the same syncRules and mutators on server; generated contract types on both clients.
Convergence expectations
Section titled “Convergence expectations”- Local write < 50ms with network down (TanStack optimistic path)
- After reconnect, remote device sees changes in < 5s with realtime (ping → pull)
- Without realtime,
pull.intervalMsor manualecho.pull()still converges
Client ID reconciliation
Section titled “Client ID reconciliation”When the server assigns a different PK than the client’s optimistic ID, push ack returns the mapping. Collections rebase local rows via TanStack DB.
Testing multi-device
Section titled “Testing multi-device”apps/credit-ledger/examples/multi-device-host.ts— scripted two-client checkapps/emulationchaos harness — N clients with partition/restart injectorsapps/op-sqlite-probe— real device persistence smoke
Revocation
Section titled “Revocation”Removing a device user’s membership must shrink their buckets. Verify pull returns removedBuckets and local purge — not just 403 on push.