Stamps form a strictly total order. Given any two stamps, exactly one is greater. No ties. Ever. When two tabs write the same key concurrently:
- The higher-stamp write wins
- Every replica picks the same winner deterministically
- No conflict-resolution UI needed
Deletes are stored as tombstones, not removals. A stale write that arrives late saying "key X = 5" cannot resurrect a deleted key — the tombstone with a higher stamp always wins.
This isn't theoretical. The math is provably commutative, associative, and idempotent. I hammered it with 1,000 randomised concurrent operations across 3 simulated nodes and asserted all replicas converge byte-for-byte. The test passes 100% of the time.
Offline-first comes for free
Here's the part that genuinely felt like magic the first time it worked.
The JavaScript client maintains a localStorage cache. On every page load, it reads from cache synchronously, before the WebSocket even tries to connect. If the cache is hot, the UI renders instantly.
Stop the relay. Refresh the tab. The app still works. The browser shows your state from cache. When the relay comes back online, the WebSocket reconnects and the gateway snapshot patches any divergence.
Measured: ready() resolves in 1 ms when the cache is hot. That's the difference between "instant render" and "wait for a network round-trip that might never complete."
Real numbers (not vibes)
I shipped a benchmark harness with the repo. Run it yourself with uv run aether-benchmark. Numbers from a Linux VM, Python 3.12, fsync ON:
| Metric | Result |
|---|---|
| Gateway round-trip (loopback, p50) | 0.21 ms |
| Gateway round-trip (loopback, p99) | 0.68 ms |
| Ledger replay throughput | ~86,000 ops/sec |
| Snapshot-boot speedup vs cold replay | 1.6 – 1.8× |
| Mesh convergence (5-node ring, 100 writes) | 63 ms |
| Ledger write throughput (with fsync) | ~1,400 ops/sec |
The relay isn't your bottleneck. The network is.
What's actually in the box
Three working "killer" demo apps, all open in two tabs and sync in real time:
🗂️ Kanban board — columns, drag-drop cards between columns, priority cycling, delete-cascade
📝 Collaborative Markdown editor — paragraph-keyed CRDT, live HTML preview pane
🖧 Network topology diagrammer — drag-drop devices, typed links (1G/10G/Wi-Fi), labels
Plus the actual engine:
-
CRDT layer: Hybrid Logical Clock + LWW Map (
crdt.py) -
Mesh layer: WebSocket gossip with epidemic relay between Python nodes (
mesh.py) -
Storage: append-only JSON-lines ledger with crash recovery (
storage.py) -
Gateway: browser-facing WebSocket endpoint, rate-limited and payload-capped (
gateway.py) -
Security: token bucket, connection caps, payload validators, slow-loris guard (
_security.py) -
Browser SDK: 700 lines of vanilla JS, no build step, no framework (
aether.js) -
TypeScript declarations: full
.d.tscovering every method (aether.d.ts)
And the boring-but-important stuff:
- 26 automated tests (17 Python pytest + 9 JS scenarios)
- Protocol conformance tests that parse the README and assert the running server matches it (so the docs cannot silently drift from the code)
- Threat model documentation with every mitigation
- 8-file documentation suite: getting started, concepts, two API references, recipes, deployment guide, troubleshooting
The whole thing is MIT-licensed and depends on exactly one Python library (websockets) and zero JavaScript libraries.
What this is not for
I owe you the limits, because none of this is free magic.
❌ High-cardinality data (millions of keys). Every new browser tab gets the full snapshot.
❌ Fine-grained authorization. Every connected client sees every key.
❌ Character-level collaborative editing of the same line. LWW means one writer wins per field. (My Markdown editor sidesteps this by keying each paragraph separately.)
❌ Strong consistency. CRDTs are eventually consistent by definition.
❌ Public-facing apps with thousands of concurrent users per relay. You can shard by document/room, but you do it manually.
Where it shines: internal tools, prototypes, niche collaborative editors, multi-tab desktop-app-like web UIs, anything where 5–50 people share state. The whole class of apps where you'd normally write a 200-line Express server, a 5-table Postgres schema, and 6 REST endpoints — and where 90% of that work exists only to ferry the same data between representations.
Try it
git clone https://github.com/IronFighter23/aether-core
cd aether-core
uv sync
uv run aether-demo
Three URLs in two tabs each:
- http://localhost:8080/ — topology diagrammer
- http://localhost:8080/demos/kanban.html — Kanban
- http://localhost:8080/demos/markdown.html — Markdown
Kill the server. Refresh. Notice the state is still there.
Star, fork, tell me what to break
The repo: github.com/IronFighter23/aether-core
I'd genuinely love to know:
- What's the smallest real app you'd build on this? Drop it in the comments.
- Where does the model break for you? I want to find the edges.
- Should I add a Redis driver for the federation layer? That'd let you run this on AWS/GCP without exposing the mesh port publicly.
If you build something on top of Aether-Core, tag me — I'll happily add it to the README's "built with" list.
If this post helped you think differently about backends for a minute, leave a ❤️ or a 🦄. If it didn't, tell me why in the comments. I'm wrong about plenty of things and would rather find out now than later.
Built by Nishant Bhatte. MIT licensed. 4,000 lines of Python + JavaScript + Markdown.
Top comments (0)