Delete the backend: shipping v0.5 of a CRDT relay where the server can't read your data
Yjs, Automerge, and Loro are excellent CRDT libraries — but they leave the server problem to you. You still write API endpoints. You still design a persistence layer. You still bolt on auth. Aether-Core deletes that ent
Yjs, Automerge, and Loro are excellent CRDT libraries — but they leave the server problem to you. You still write API endpoints. You still design a persistence layer. You still bolt on auth.
Aether-Core deletes that entire stack.
The pitch in one code snippet
Before:
function ShoppingList() {
const [items, setItems] = useState([]);
// ... plus API endpoint, database schema, auth middleware, ...
}
After:
function ShoppingList() {
const [items, setItems] = useAether('items', []);
// that's the whole change. multiplayer, offline, persisted.
}
The Python relay treats every value as opaque bytes. It doesn't implement business logic. It doesn't have endpoints. It cannot read your data.
What's new in v0.5
End-to-end value encryption. Optional client-side AES-256-GCM wraps every value before it leaves the browser. The relay sees ciphertext only. Even someone who runs the relay for you can't read your data:
const room = await AetherCrypto.roomFromPassphrase("passphrase", "salt");
const aether = new Aether("ws://relay", { transform: room.transform });
Verified end-to-end with a wire observer: two clients round-trip plaintext through the room key, while a third socket sniffing the same relay sees only {"__ae_enc": 1, "iv": "...", "ct": "..."}.
IndexedDB per-key persistence (now default). The previous localStorage default hit two hard problems: the 5-10 MB quota ceiling, and JSON-serialize-the-whole-blob on every write. Both fixed. localStorage remains as a backward-compatible fallback for private mode.
Merkle-trie anti-entropy. When two peers reconnect after a partition, epidemic gossip alone can't recover the ops that flowed while they were split. Aether-Core now ships a Merkle-trie digest exchange. Bandwidth is O(k log N) in the number of divergent keys, not O(N) total. A 1000-key mesh with 3 divergent keys reconciles in ~16 probe messages, not 1000.
LAN peer discovery via mDNS. Two relays on the same wifi find each other with zero config. Opt-in (MeshNode(discovery=True)) because some networks treat mDNS traffic as suspicious.
One-command scaffold:
npx @nishantbhatte/aether-core init
Emits relay.py, index.html, Dockerfile, Procfile. Two commands from npx init to a working relay.
Honest positioning
I'm not competing with Yjs on CRDT math. Yjs is more mature, has a bigger ecosystem, and its sync engine is excellent. The wedge is server-side: I ship auth, rate limiting, payload caps, and optional E2E encryption on by default. Yjs leaves that to you.
| Yjs / Automerge / Loro | Aether-Core | |
|---|---|---|
| CRDT sync | ✅ | ✅ |
| Backend code required | You write it | None |
| Auth built-in | ❌ bring your own | ✅ HMAC by default |
| Rate limiting built-in | ❌ | ✅ token bucket |
| Payload caps built-in | ❌ | ✅ configurable |
| E2E crypto in recommended path | Ecosystem addon | Ships in web/aether-crypto.js |
| Anti-entropy after partition | State-vector sync (O(delta)) | Merkle-trie (O(k log N)) |
| LAN auto-discovery | ❌ | ✅ mDNS opt-in |
Try it
pip install aether-zta # Python relay
npm install @nishantbhatte/aether-core # Browser client
Or scaffold a working project:
npx @nishantbhatte/aether-core init
- Repo: https://github.com/IronFighter23/aether-core
- 73 tests green on Linux/macOS/Windows × Python 3.11/3.12
- MIT licensed
Feedback welcome. Especially interested in "here's where I'd break it" from anyone who's built collaborative apps before.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.