Skip to main content

efema_proto/
lib.rs

1//! The wire format of [efema](https://github.com/lacodda/efema): what a client
2//! and a relay say to each other.
3//!
4//! efema is a sync relay for local-first apps. A device writes its changes as
5//! a *batch* of opaque entries into a named *stream*; the relay gives each
6//! entry the next *position* and keeps it; every other device reads what came
7//! after its *cursor*. The relay never looks inside an entry - it cannot tell
8//! a note from a password, and merging is left to the app.
9//!
10//! This crate is that conversation and nothing else: the messages, their CBOR
11//! encoding, the stream's names and identities, the cursor and the hash chain
12//! that makes a cursor checkable. The relay (`efema-server`) and the client
13//! library both build on it, so the two cannot drift apart: there is one
14//! definition of each message, and it lives here.
15//!
16//! # The pieces
17//!
18//! - [`StreamName`] - the name a stream is addressed by, in the URL.
19//! - [`StreamId`] - which *incarnation* of that name: a fresh random value
20//!   when the stream is created, so a cursor from a stream that was wiped and
21//!   started again is recognised as foreign instead of silently reused.
22//! - [`Epoch`] - a number the app raises when it changes the format of what
23//!   it writes. The relay only compares epochs; it never knows what they mean.
24//! - [`Hash`](struct@Hash) and [`chain`] - every entry is linked to the one before it, so a
25//!   cursor names not just a position but the history up to it.
26//! - [`Cursor`] - where a reader stands: stream, position and the hash there.
27//! - [`wire`] - the request and response bodies, encoded as CBOR.
28//!
29//! ```
30//! use efema_proto::{chain, Epoch, StreamId};
31//!
32//! let stream = StreamId::from_bytes([7; 16]);
33//! let start = chain::genesis(&stream);
34//! let first = chain::link(&start, 1, Epoch(1), b"sealed bytes");
35//! // The same history always gives the same hash...
36//! assert_eq!(first, chain::link(&start, 1, Epoch(1), b"sealed bytes"));
37//! // ...and a different one never does.
38//! assert_ne!(first, chain::link(&start, 1, Epoch(2), b"sealed bytes"));
39//! ```
40
41pub mod chain;
42mod cursor;
43mod hex;
44mod ids;
45pub mod limits;
46mod name;
47pub mod wire;
48
49pub use cursor::{Cursor, ParseCursorError};
50pub use ids::{Epoch, Hash, ParseIdError, StreamId};
51pub use name::{InvalidName, MAX_NAME_LEN, StreamName};
52
53/// The media type of every request and response body in protocol version 1.
54pub const MEDIA_TYPE: &str = "application/cbor";
55
56/// The path every endpoint of protocol version 1 lives under.
57///
58/// A later major version of the protocol gets a path of its own, so a relay can
59/// serve two versions while its clients move from one to the other.
60pub const API_PREFIX: &str = "/v1";