Local-First · Software

Sovereign Software: The Case for Local-First

Your data should live on your own devices, not in a landlord's cloud. A field guide to software that still works when the network doesn't.

Every app you use today is a lease. The documents you write, the notes you keep, the plans you make: they sit on a server owned by someone else, and the moment that someone changes their terms, raises their price, or simply goes out of business, you discover what you actually owned. Which was a login.

Local-first software is the old and sensible alternative. The primary copy of your data lives on your own device. The network is a convenience, a road between devices, not the foundation under them.

The Seven Ideals

The research lab Ink & Switch articulated the movement in their 2019 essay Local-First Software, naming seven ideals. In my own words, a local-first application should:

  1. Feel instant, because it never waits on a distant server.
  2. Work across all your devices.
  3. Work offline, fully, without a degraded mode.
  4. Make collaboration a first-class citizen.
  5. Outlive the company that made it.
  6. Keep your data private by default.
  7. Leave final control with the user.

The cloud is somebody else's castle. You are a guest in it, and guests may be asked to leave.

A working maxim

How the Pieces Fit

The technical key is the CRDT, a conflict-free replicated data type. A CRDT is a data structure designed so that any two replicas, edited independently and in any order, can be merged into the same result without a central referee. Libraries such as Automerge and Yjs make this practical for text, lists and maps.

Pair a CRDT with a sync layer, whether a relay you host, a peer-to-peer connection, or a simple dumb file store, and you obtain collaboration without a server that must be trusted with the meaning of your data. The server becomes a postal courier. It carries sealed letters and cannot read them.

// the shape of a local-first write
doc = change(doc, d => { d.notes.push({ text: "Ship it" }) })
await saveToDisk(doc)        // instant, always works
peers.forEach(p => p.sync(doc)) // whenever the road allows

The Honest Costs

This is not free. Schema migrations are harder when you cannot upgrade every client at once. Access control is subtler when no central server enforces it. Storage grows with history unless you plan for compaction. And the product experience of "your data is a file on your machine" demands good design to avoid feeling like a regression to 1998.

Note None of these are reasons to abandon the idea. They are the price of freehold, and freehold has always cost more than tenancy.

Where to Begin

Start small. Choose one tool you rely on, and ask what would happen if its servers vanished tomorrow. Then build, or demand, the version that survives that morning.