Documentation · doc.s3nd.sh
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.
lib/store.tsyour bucket
import { createBucket } from '@s3nd/core'
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 IndexedDB40 bits · expires 1h
Use cases
Worked, end to end.
Move to a new deviceA code on the old phone, typed into the new one. The whole database follows.Continuous backupOne snapshot per account, rewritten as the local database changes.End-to-end encrypted syncEncrypt in the browser with a passphrase. Your server stores bytes it cannot read.Attachments beside the dataThe blobs an IndexedDB app holds, carried across with the records that point at them.
Reference
API referencecreateBucket(), the file API, snapshots, the handler.The CLIput, get, rm, doctor, init and config.React hooksSend, receive, and a code input that repairs typos.ErrorsEvery stable code, and what to do about it.
The product, the use cases and the comparisons live on s3nd.sh ↗