Expand description
The efema client: sync a local-first app’s changes between devices through a relay that cannot read them.
An app pushes its changes into a stream as opaque items; every other device pulls what came after its cursor, in the same order for everyone. What an item means and how two changes are merged stay with the app: this crate moves bytes, sealed.
Every entry is sealed on the device. A Client cannot be made
without a Secret - the stream’s passphrase or its key - and has no
method that sends an item as it is. The relay stores ciphertext, and so
does every backup of it.
§The pieces
State- what a device remembers between runs: its identity, and for each stream the cursor and the locked key. One file per device.Relay- the relay to talk to; it implementsTransport.Client- one device’s view of one stream:push,pull,ack,wait,change_passphrase, anddoctorto report on all of it.
§Keys
The first device to open a stream creates its key - 32 random bytes - and
writes it to the stream locked under the passphrase (Argon2id, then
XChaCha20-Poly1305: lacodda_seal). Every other device finds that lock,
unlocks the key with the same passphrase and keeps the lock in its state,
so it opens without the network after that. The key itself is never
written anywhere unless the app asks for it with Client::key.
Changing the passphrase locks the same key again and writes the new lock to the stream; nothing is sealed again, and every device moves to the new lock as it reads it.
§Compression
Items are compressed with zstd before they are sealed, each one only if it
gets shorter - text usually to a third. Compression::Off turns it off
for an app that mixes secrets with text someone else chose in one item.
§Delivery
At least once. Client::pull reads from the device’s acknowledged
cursor and Client::ack moves it, once the app has applied what it
pulled - so a crash in between hands the same entries over again, and an
app applies an entry twice without harm.
§Epochs
A client writes in its app’s epoch and reads up to it. When a newer
version of the app moves a stream to a newer epoch, an older one stops in
front of the first newer entry (Error::NewerEpoch) and can no longer
write (Error::EpochBehind) - instead of misreading the newer format or
mixing the older one in.
Modules§
- chain
- The wire format and the hash chain, for writing a
Transport. The hash chain that links every entry of a stream to the one before it. - wire
- The wire format and the hash chain, for writing a
Transport. The bodies of requests and responses, and their CBOR encoding.
Structs§
- Client
- One device’s view of one stream: it seals what it pushes, opens what it pulls, and remembers how far it has read.
- Cursor
- A reader’s place in a stream: which stream, how far, and what the history up to there was.
- Device
Id - Which device this is: sixteen random bytes drawn the first time its state is opened.
- Epoch
- The version of the format an app writes into a stream.
- Invalid
Url - Why a text is not the address of a relay.
- Key
- A symmetric key: 32 bytes, wiped from memory when dropped.
- KeyId
- The identity of a key: eight bytes of a hash over it.
- KeyReport
- The stream’s key, as far as the device can tell without the passphrase.
- Limits
- What one request may carry and one page may hold.
- Locked
Key - A key locked under a passphrase.
- Pulled
- What one
Client::pullread. - Pushed
- Where a push landed.
- Received
- An entry as the app receives it.
- Relay
- An efema relay, reached over HTTP or HTTPS.
- Relay
Report - The stream as the relay has it.
- Report
- A device’s sync, as
Client::doctorfinds it. - State
- A device’s sync state: one file, opened by one process at a time.
- Stream
Id - Which incarnation of a stream name this is.
- Stream
Name - The name of a stream: what goes into the URL, and what a person reads in the relay’s listing.
Enums§
- Compression
- Whether a client compresses items before it seals them.
- Error
- Why a client operation failed.
- Secret
- What a client opens a stream with. Required: there is no client without one, and so no way to send an entry unsealed.
- Transport
Error - Why a transport could not do what it was asked.
Constants§
- ENTRY_
OVERHEAD - How many bytes sealing adds to an item it does not compress: the sealed blob’s own and the entry’s header inside it.
Traits§
- Transport
- A way to reach streams.