# Local-first is a security posture, not just a privacy feature Source: https://selfstore.dev/blog/local-first-is-a-security-posture 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](https://selfstore.dev/) 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](https://selfstore.dev/) 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: - **Server-side data exfiltration.** No central database, no bulk dump. - **Backup and snapshot leaks.** Backups exist, but they are encrypted on the device and stored by the user; a leak of the storage bucket yields ciphertext. - **Insider access.** No operator, contractor or support tool can read user data, because the operator never holds a key. - **Credential-store compromise.** With no accounts and no server sessions, there is no password database to steal. - **Transit interception of plaintext.** Sync and backup move ciphertext, so a compromised network sees encrypted files. 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](https://selfstore.dev/blog/encrypted-backups-browser), and the container is [specified independently of the library](https://selfstore.dev/docs/format) 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: - **Code running inside your origin.** Cross-site scripting owns the session: it sees the data already decrypted in memory and can use the device key that unseals the local cache. Client-side encryption at rest does nothing against an attacker executing as your app. The [threat model](https://github.com/selfstoredev/selfstore/blob/main/THREAT-MODEL.md) states this as a non-goal in plain terms. - **The device itself.** An unlocked, compromised laptop is a compromised working copy. Local-first moves the trust boundary to the user's device; it does not make that device invulnerable. - **The user's password.** Confidentiality of the backup rests on a password the user chooses and keeps. A weak or reused password is the weak link, as it always is. 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](https://selfstore.dev/blog/sync-without-server) 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](https://selfstore.dev/local-first) 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. Map of this site for a model: https://selfstore.dev/llms.txt Every page in one file: https://selfstore.dev/llms-full.txt