Skip to content

Sync

bae’s sync is coven’s sync: bae declares its tables and blobs, and coven does the rest. This page is the mechanics as they apply to bae; coven’s own docs cover each piece in full depth.

coven owns the SQLite connection, so every write bae commits passes through it. When a transaction that changes synced tables commits, coven records each row change, with the columns it touched, in the same transaction, so a committed change is never lost, even if the app crashes a moment later. No diffing, no dirty flags: capture is a property of the connection, and a write cannot be missed. The change waits on the device and is encrypted and signed only when it is sent, so writing needs no key and no network.

All of a library’s history in the cloud home is one numbered sequence of objects. Each holds one device’s batch of changes, an admin entry, a key sealed to one member, or a snapshot announcement. Every device applies them in order and never skips one, so every device agrees on what happened and when. A device adds the next object only after it has applied every one before it.

A sync pass reads forward from the last object the device applied until one is missing, checks each one’s signature and applies it, sends this device’s queued changes as one new object, then does the work the new state calls for: sharing or unsharing access, taking snapshots, deleting old history. A pass over a quiet library costs two requests, whatever its size.

Passes run after local changes, on an idle timer, and immediately on Sync Now. Failures retry on one backoff schedule, and anything that can’t proceed names what it is waiting for, which bae shows in its sync status line.

Two devices editing the same library while apart is the normal case, not the exception. When their changes meet:

  • Edits to different rows or different columns both apply. Editing a release’s year on the laptop and its label on the desktop yields both.
  • Edits to the same column of the same row are ordered by timestamp: the later edit wins, deterministically and identically on every device.
  • Deletes win over concurrent edits: an edit made to a row that has since been deleted loses, whenever it arrives, so a release deleted on one device stays deleted even if another device edited it in the same interval.

coven keeps each replaced value as a lost value; bae shows none and dismisses none, so there is no conflict UI. Every device converges to the same result from any arrival order.

A device joining or restoring doesn’t replay history from the beginning. Devices take snapshots, the complete state of the library as of one point in the sequence, and once the library and every private circle have one, the history before it is deleted. A new device loads the latest snapshot, then applies only what came after it. A device that has been away longer than the kept history does the same, and keeps its unsent edits.

bae’s schema is one migration, and every change a device sends records the schema version it was made with. A device that reaches a change from a newer schema than its own stops there, applying nothing past it, until the app updates; nothing is lost and nothing misapplies.

Coarser than the schema version is the compatibility era: the pinned coven revision baked into each binary. Two builds sync only within an era; a coven wire-format break is a new era and a major version bump across every app. Pre-1.0, every build is era zero and formats change without migration.

The provider stores encrypted changes, snapshots, membership records, and files; it can count objects, observe sizes and timing, and see which account writes each object, and read none of the contents. Every object a device pulls is verified, signature and membership, before anything touches the database: the storage is a dumb, untrusted mailbox. See Encryption and Identity and membership.