Search selfstore
v1.8.21

Local-first is a security posture, not just a privacy feature

The largest breaches are server-side. Keeping one person's data on their own device removes a whole class of them - and here is exactly which class, and which threats it does not touch.

Most data breaches you read about share one shape: an attacker, or a misconfiguration, or a rogue insider reaches a central store and walks off with everyone’s records at once. The database was the prize because the database held everyone. That concentration is not incidental to the breach; it is the precondition for it.

Local-first architecture attacks that precondition directly. When one person’s data lives on that person’s device and never lands in a store you operate, there is no central pile to steal. This is worth stating plainly, because it is often filed under “privacy” and sold as a nicety. It is a security property, and it is checkable.

The breach you cannot have

A vendor who never receives the plaintext cannot leak it. Not through a SQL injection, not through a leaked backup, not through an employee with too much access, not through a subpoena. You cannot lose what you never held.

selfstore is built on that inversion. The data lives in an IndexedDB working copy on the device, and the only thing that ever leaves is an encrypted backup written to storage the user already owns: a file on disk, their Google Drive, a WebDAV server, an S3 bucket. The bytes that go out are ciphertext (AES-256-GCM, under a key derived from the user’s own password). The place they land holds opaque blobs and learns the file’s size and date, nothing more.

Note what this does to responsibility. If you ship a browser app with no server in the data path, you are not the custodian of a breachable store, because there is no store of yours to breach. The canonical definition says it in one line; this article is what that line means for your threat model.

What the architecture removes from the attack surface

It is easy to overstate. So here is the precise list of breach classes a serverless, local-first design eliminates outright, because the thing they target does not exist:

Each of these is a real, common failure mode in server-backed apps. Removing the server does not mitigate them; it deletes the category.

The encryption claim, stated narrowly

“Encrypted” is a word that hides architectures. The specific claim here is narrow and therefore useful: the backup is encrypted before it leaves the device, under a key the vendor never sees, so confidentiality does not depend on trusting the storage provider. The primitives and their reasoning have their own detailed write-up, and the container is specified independently of the library so the claim can be verified against an artifact rather than a promise. The point for this article is only that the encryption boundary sits at the device edge, not at a server you would otherwise have to secure.

Where the boundary honestly sits

A security posture that oversells itself is a liability, so here is what local-first does not protect:

These are the same boundaries any honest client-side system has. What changes is that the giant, remote, always-on target - the one an attacker on the other side of the world can reach without touching your users - is gone.

Why this is a posture and not a checkbox

Security features get added; security postures get designed in. You cannot bolt “no central breach surface” onto an app that already funnels every write through your database. It comes from the architecture: static files, a local working copy, encrypted backups the user owns, and sync that runs through the user’s own storage instead of a server you operate.

Adopt that shape and the most damaging, most common breach class simply has no target on your side. The local-first overview walks the rest of the trade-offs. This one - the breach that cannot happen because there is nothing central to break into - is the one worth leading with.


Try selfstore in five minutes, or read the other notes.