You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: apps/web/src/app/(landing)/blog/scaling-local-first-software/page.mdx
+11-11Lines changed: 11 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,7 @@ Building local-first apps is already a challenge, and making them scalable is an
21
21
22
22
---
23
23
24
-
# Where it all began: a mission to restore ownership
24
+
##Where it all began: a mission to restore ownership
25
25
26
26
When I discovered the concept of local-first software, I immediately knew I didn't want to build apps any other way. Ownership is the foundation of a free society—without it, there is no freedom. Outside programming, I often speak about the state, anarchism, Bitcoin, and economics. I'm currently writing a book called [Deconstruction of the State](https://www.startovac.cz/projekty/kniha-dekonstrukce-statu), which aims to return the state to the people.
27
27
@@ -37,7 +37,7 @@ To reason about when events happened and how they relate, distributed systems us
37
37
38
38
But here’s the catch: knowing when something happened isn’t enough. To sync two peers, we don’t just need causality—we need to know which exact events the other peer has already seen. Clocks describe order and causality, but not which specific events a peer has seen. This becomes a serious limitation in peer-to-peer systems, where events (messages) might take different paths to different nodes. Without a way to compare sets of events directly, we risk syncing inefficiencies: unnecessary retransmissions, duplicated events, or bloated metadata. These issues limit scalability.
39
39
40
-
# Set reconciliation
40
+
##Set reconciliation
41
41
42
42
> Imagine two computers connected over a network, and each computer holds a set of values. Set reconciliation is the problem of efficiently exchanging messages between them such that in the end both hold the union of the two sets. If there is a total order on the items, and if items can be hashed to smaller fingerprints with negligible collision probability, this can be done in logarithmic time in a fairly simple manner.
43
43
@@ -55,7 +55,7 @@ By pure luck, one of them turned out to be Aljoscha Meyer. I didn’t fully gras
55
55
56
56
Back in Prague, I began diving deep into tree structures. As Doug [explains](https://logperiodic.com/rbsr.html#tree-friendly-functions), a tree structure is essential for incremental fingerprint computation. Compared to B+ trees, skiplists have a key advantage: because they’re probabilistic, they avoid the complexity of node splitting, merging, and balancing. But the big question remained—how do you make that work efficiently in SQLite? Traversing a tree can require up to 20 queries. Surely that must be painfully slow, especially if done in JavaScript.
57
57
58
-
## RBSR with(in) SQLite
58
+
###RBSR with(in) SQLite
59
59
60
60
Long story short, I bet on something pretty unconventional: I decided to write the entire logic in SQL. No C extensions—because I wanted it to be portable across SQL databases. With skiplists, all we really need is data traversal and minimal manipulation—no need for node splitting, merging, or balancing.
61
61
@@ -69,7 +69,7 @@ It may not be ideal—ideally, we’d have a key-value WASM storage—but it’s
69
69
70
70
Does this fully solve the challenge of handling large amounts of data? No—and I’ll write more about that later. But for now, let’s shift focus to something else: the Evolu code itself.
71
71
72
-
# Rewriting Evolu: fp-ts → Effect → Evolu Library
72
+
##Rewriting Evolu: fp-ts → Effect → Evolu Library
73
73
74
74
I've always been a fan of functional programming—but it took me a while to figure out exactly why. It definitely wasn’t because of the cryptic code or the heavy terminology. What I like the most are typed errors (like the Result type), dependency injection, and immutability. These patterns make code more composable, testable, and easier to reason about. i.e., scalable.
75
75
@@ -164,7 +164,7 @@ It turns out we don’t need pipe or chaining at all—just `Result` and plain f
164
164
165
165
Now let’s return to the new Evolu—its local-first architecture and the ongoing challenge of scaling. That brings us to the Evolu Protocol.
166
166
167
-
# Evolu Protocol
167
+
##Evolu Protocol
168
168
169
169
Evolu Protocol is a local-first, end-to-end encrypted binary synchronization
170
170
protocol optimized for minimal size and maximum speed. It enables data sync
@@ -173,7 +173,7 @@ relays with each other. Evolu Protocol is designed for SQLite but can be extende
173
173
implements [Range-Based Set Reconciliation](https://arxiv.org/abs/2212.13567)
174
174
by Aljoscha Meyer.
175
175
176
-
## Why binary?
176
+
###Why binary?
177
177
178
178
The protocol avoids JSON because:
179
179
@@ -239,7 +239,7 @@ That's **272 bytes** for **16 fingerprints**, representing the entire database (
239
239
240
240
Again, to understand how **Range-Based Set Reconciliation works**, I highly recommend [this great article](https://logperiodic.com/rbsr.html) by Doug. Now let’s take a closer look at the protocol message structure:
@@ -257,7 +257,7 @@ Again, to understand how **Range-Based Set Reconciliation works**, I highly reco
257
257
258
258
It’s simple—but is it too simple? No. Let’s look at how it can support **authentication**, **collaboration**, and **partial sync**.
259
259
260
-
# Auth, collaboration, and partial sync
260
+
##Auth, collaboration, and partial sync
261
261
262
262
When I started thinking about local-first auth and collaboration for Evolu, I struggled to find a model that could **scale to all use cases**. That’s why the first version of Evolu didn’t support collaboration. Rather than implement something half-baked, I decided to postpone it.
263
263
@@ -297,7 +297,7 @@ It’s important to understand that while these owners are _default_ in Evolu, f
297
297
The goal was to make Evolu Protocol and Relay as **generic and application-agnostic** as possible. They should **know nothing** about what applications are using them.
298
298
This is the only way to guarantee **maximum privacy** and **true scalability**—as we’ll explore next.
299
299
300
-
## Partial sync
300
+
###Partial sync
301
301
302
302
It’s clear that no single device can hold all the data. You can’t download the whole of Facebook to your phone. Traditional client-server applications handle this by selectively downloading parts of the data using some form of
303
303
fine-grained "select"—via REST, RPC, GraphQL, and so on.
@@ -313,7 +313,7 @@ So how does Evolu scale in practice? In two complementary ways:
313
313
314
314
It's up to the Evolu app to know which owner should be synced for which data and when. Relays must know nothing.
315
315
316
-
# From local-first apps to local-first clouds
316
+
##From local-first apps to local-first clouds
317
317
318
318
Evolu’s strict design has a surprising consequence: the **limitations** of local-first apps are exactly what allow them to **scale better** than traditional client-server applications.
319
319
@@ -365,7 +365,7 @@ Let’s summarize why I believe Evolu can scale:
365
365
366
366
-**Peer scalability through stateless sync**: Evolu scales to virtually unlimited peers by using content-based synchronization instead of shared state. With range-based set reconciliation, peers compare data fingerprints, not sync histories. Relays remain stateless, remembering nothing about past syncs. This enables massive horizontal scaling with no centralized bottlenecks.
367
367
368
-
# Conclusion
368
+
##Conclusion
369
369
370
370
Today, I’m open-sourcing what I have. The tests and benchmarks say that Evolu is ready. It’s not a production release yet—just a few details remain, like managing owners and a few other tasks tracked in [GitHub Issues](https://github.com/evoluhq/evolu/issues). I also still need to write the free, open-source Evolu Relay.
Copy file name to clipboardExpand all lines: apps/web/src/app/(landing)/blog/why-the-world-needs-local-first-apps/page.mdx
+9-13Lines changed: 9 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,7 @@ In this post, we'll explore why local-first apps are essential, the challenges o
22
22
23
23
---
24
24
25
-
###Why local-first apps matter
25
+
## Why local-first apps matter
26
26
27
27
**Local-first apps** shift the power dynamic by ensuring that data is stored and processed primarily on the user's devices. This isn't just a technical distinction; it fundamentally changes the relationship between users and software. Here's why this matters:
28
28
@@ -40,7 +40,7 @@ In this post, we'll explore why local-first apps are essential, the challenges o
40
40
41
41
---
42
42
43
-
###The trade-offs of client-server architecture
43
+
## The trade-offs of client-server architecture
44
44
45
45
The client-server model has dominated app development for decades, primarily because it simplifies synchronization and backups. However, this convenience comes with significant trade-offs:
46
46
@@ -56,29 +56,29 @@ These challenges highlight the need for an alternative approach—one that prior
56
56
57
57
---
58
58
59
-
###How Evolu redefines local-first apps
59
+
## How Evolu redefines local-first apps
60
60
61
61
**Evolu** is a library built with the local-first philosophy at its core. It solves the challenges of building local-first apps while retaining the benefits of synchronization and backup through a minimal, optional server.
62
62
63
-
####1. **Full Data Ownership**
63
+
### 1. **Full Data Ownership**
64
64
65
65
Evolu stores all app data in a local SQLite database. Unlike caching or partial storage solutions, this ensures that users have complete control over their data at all times.
66
66
67
-
####2. **Built-in Privacy with Encryption**
67
+
### 2. **Built-in Privacy with Encryption**
68
68
69
69
Data privacy is non-negotiable in Evolu. All local data is encrypted by default, ensuring robust security without requiring developers to configure it manually.
70
70
71
-
####3. **Lightweight, Open-Source Server**
71
+
### 3. **Lightweight, Open-Source Server**
72
72
73
73
Evolu offers a simple, open-sourced **Evolu Server** for synchronization. This server is purely a message buffer with no app-specific logic, ensuring that developers aren’t tied to a single provider or platform.
74
74
75
-
####4. **No Vendor Lock-In**
75
+
### 4. **No Vendor Lock-In**
76
76
77
77
Evolu's design allows developers to leverage cloud services without becoming dependent on them. This approach balances flexibility with independence, giving users and developers peace of mind.
78
78
79
79
---
80
80
81
-
###What local-first means to Evolu
81
+
## What local-first means to Evolu
82
82
83
83
The term "local-first" has become an umbrella for various strategies. At Evolu, it specifically means:
84
84
@@ -90,12 +90,8 @@ By focusing on these principles, Evolu empowers developers to create apps that p
90
90
91
91
---
92
92
93
-
###Why the future is local-first
93
+
## Why the future is local-first
94
94
95
95
The internet is not infallible. Servers go down, companies fail, and errors occur. These realities underline the need for software that doesn't rely solely on centralized systems. **Local-first apps** offer a compelling alternative, combining the best of both worlds: the autonomy of local data with the convenience of optional cloud features.
96
96
97
97
Evolu makes it easier than ever to build local-first apps, providing developers with the tools they need to create resilient, user-centric software. With Evolu, the future of app development looks brighter, more private, and entirely in the hands of users.
98
-
99
-
---
100
-
101
-
Ready to take control of your app's data? Explore Evolu and start building the future of local-first apps today.
0 commit comments