- Automerge is the CRDT; Peritext is the rich-text half of it, which is what makes a caret survive someone else's edit - Keyhive is the encryption and access layer: devices, groups, documents, roles - The relay is stateless and never holds plaintext — it introduces peers and forwards opaque bytes
- Peers upgrade to a direct WebRTC channel when they can, and fall back to the relay when they cannot - A user is a group of their devices, so sharing with a person shares with every device they own - Five document types, each with a schema — calendar, tasks, counters, spreadsheet, prose
- A QR code holds a few hundred bytes; the keyhive material to link a device is tens of kilobytes, hence the rendezvous channel - Keyhive gaps that had to be filled: freshly rotated keys lived only in memory so a reload lost them; groups as members of groups, with access capped to the minimum along each path; an event per change so the app can persist and refresh - Roles are live delegations, so revoking one changes what the *other* device can do without asking it anything
- Each device declares its own user group plus the groups it knows (friends, doc co-members); the relay only introduces same-group devices and mutual watchers - Un-friending withdraws the match and the relay dissolves the pair — strangers sharing a relay never learn of each other - Group ids are self-asserted routing hints today; the HMAC daily token is designed and deferred
- The main thread is only UI; a worker owns local and remote updates (automerge, keyhive, transports) - The UI subscribes to small jq query slices, so it never holds a whole document in memory - Presence is a path into the document (e.g. ['events', uid, 'title']), never screen coordinates — so any view can render it, and it is encrypted with the document's own key - The relay never stores messages for later delivery, so the always-online peer cycles through documents looking for updates
- Every doc type has a schema covering structure *and* data dependencies — due dates, formula references, cell keys must all resolve - Shapes are chosen *for* the CRDT: id-keyed maps instead of arrays, fractional indices for order - Formulas reference row/column ids, so reordering never rewrites them — that is what the second form buys - Validation runs on local and remote changes alike, and errors are advisory rather than a block
- The relay routes rendezvous by channel id and never sees plaintext; this is the one path a device with no group yet can use, since it has nothing to be introduced by - Both directions are sealed under the same key — the bundle crossing back is the phone's own card - The highlighted row is the moment the new device has access to anything - Then it is just a peer: the phone re-announces its group, the relay's same-group rule pairs the sockets, and the document list is whatever keyhive says it can reach
- Once the exchange is done both sides are ordinary peers, which is what this diagram is: announce, get paired, sync keyhive, and the reachable documents follow - Document ids were never in the QR payload — the new peer asks keyhive what it can reach - Content only flows because keyhive says it may; the relay is forwarding ciphertext it cannot read either way