Your local-first app, on their other device.
IndexedDB never leaves the browser it was written in. s3nd snapshots that state into a bucket you control, under a code the user carries across. Credentials stay on your server.
import { createBucket } from 's3nd'
const store = createBucket({ bucket: 'my-bucket', prefix: 'snapshots' })
// On the old device: hand the user a code.
const code = store.codes.create() // "K7QP2M4X"
await store.putSnapshot(code, state, { app: 'notes', version: 3, expiresIn: 3600 })
// On the new device: they type it in.
const snapshot = await store.getSnapshot(store.codes.normalize(typed), { maxVersion: 3 })
snapshot?.data // → the state, ready to write back into IndexedDBMove to a new device
A code on the old phone, typed into the new one. The whole database follows.
Continuous backup
One snapshot per account, rewritten as the local database changes.
End-to-end encrypted sync
Encrypt in the browser with a passphrase. Your server stores bytes it cannot read.
Attachments beside the data
The blobs an IndexedDB app holds, carried across with the records that point at them.