Skip to content
install & run
$ cargo install efema-server
$ efema-server serve
# listening on 127.0.0.1:2451

$ curl 127.0.0.1:2451/health
ok

efema

A sync relay for local-first apps. Your devices write their changes into a stream; the relay keeps them in order and hands each device what it missed - without being able to read any of it.

A local-first app keeps its data on the device, and works without a network. The moment it runs on a second device, the two need somewhere to leave their changes for each other. efema is that place, and nothing more.

A device appends a batch of changes to a named stream. The relay gives each entry the next position and links it into the stream’s hash chain. Every other device reads what came after its cursor - or waits until there is something to read. Batches arrive whole and in one order for everyone, and a cursor that no longer matches the relay’s history is refused, not silently read past.

  • It does not read your data. Entries are opaque bytes: the relay stores and returns them without decoding anything. The client library seals them on the device before they leave it, under a key the relay never has.
  • It does not merge. Two devices that changed the same thing both have their batch kept, in order. Deciding what the result is belongs to the app, which knows what its data means - the relay does not, by design.
  • It does not terminate TLS. Put it behind the reverse proxy that already serves your other things.

0.2.0 is the relay and the Rust client library, efema: every entry sealed on the device with a key the relay never has, cursors that survive restarts, epochs on both sides. The relay has no access control of its own yet - it arrives in 0.5.0 - so run it on loopback or a network you trust, behind a proxy for TLS. Syncing an app is where to start.