Local-first data

How do you delete data in a local-first app?

You delete it from every place a copy lives, not from one server. That means recording the deletion in the replicated data so other devices apply it, removing the bytes from local storage, and accounting for backups and devices you cannot reach. In a CRDT, a delete can be kept as a marker, called a tombstone, so the deleted item cannot quietly come back.

Learning objectives

After reading this article you will be able to:

  • Distinguish hiding data, forgetting it on one device and removing it everywhere
  • Explain why tombstones and storage engines can leave deleted data behind
  • List the copies an erasure or account deletion plan has to cover

A delete is a change that has to sync

In a cloud app, deleting can be a single call to the server that holds the authoritative copy. In a local-first app every device holds a primary copy, so a delete is a change like any other: it is made on one device and has to reach the others.

It helps to separate three things people mean by “delete”:

  1. Hide it. The item disappears from the interface but the data stays.
  2. Forget it here. This device drops its copy, but other devices keep theirs.
  3. Remove it everywhere. Every replica applies the deletion and, eventually, discards the content.

The Ink & Switch essay on local-first software raises the hard version of the third case as an open question: if we can’t remove documents from others’ computers, what does it mean to “stop sharing” with someone?

Why deleted data lingers in merging data structures

Conflict-free replicated data types have to cope with a delete arriving before, after or alongside other changes. Shapiro and colleagues’ study of CRDTs shows the basic technique with a two-phase set: removed elements go into a second set, colloquially the tombstone set. The tombstone is what makes a remove still take precedence if a replica receives it before the matching add.

Tombstones have a cost. The same report notes that CRDTs tend to become inefficient over time as tombstones accumulate, and that safely removing tombstones needs agreement among all replicas: the set of replicas must be known and all of them reachable.

Libraries handle this differently:

  • Yjs flags a deleted item rather than recording when or by whom it was deleted. With garbage collection on, the content of a deleted object is discarded and replaced by a lightweight structure that stores only its length. With doc.gc set to false, old content is kept so it can be restored.
  • Automerge was the library behind the Ink & Switch prototypes. Their essay reported that because the CRDTs stored all history, including character-by-character edits, that history could not easily be truncated: a collaborator might reconnect months later and need to merge.

So “removed from the document” and “gone from the bytes on disk” are not the same thing. Check which one your library gives you.

Local storage keeps traces too

Below the document format sits the storage engine. SQLite’s documentation says that when content is deleted it is not usually erased; the space is marked for reuse, which can allow deleted content to be recovered by forensic analysis. Running VACUUM rebuilds the database and removes all traces of deleted content, and setting PRAGMA secure_delete=ON is the documented alternative.

Backups are another copy. Apple says iCloud Backup includes app data for downloaded apps unless the app stores it in iCloud Drive, and that if a user turns off iCloud Backup for a device, backups already stored are kept for 180 days before being deleted. A deletion plan has to account for copies like these that the app does not control directly.

Evicting a copy versus removing a document

Sync libraries can offer separate operations for “forget it here” and “remove it everywhere”, and they are easy to confuse. Offline Protocol’s shared state guide is a concrete example. deleteDoc evicts this device’s copy, and a peer can restore it. removeDoc propagates removal, but a concurrent edit can preserve and restore the document. The guide’s advice is to use a fresh document name for new content rather than relying on removal as a permanent tombstone.

The lesson carries over to other libraries. If a removal can race with an edit made offline, do not reuse an identifier and expect the old content to stay gone.

Erasure requests and the account lifecycle

Some deletions are legal obligations. Article 17 of the EU General Data Protection Regulation gives people the right to have personal data erased without undue delay on certain grounds, such as withdrawing consent. Where a controller has made the data public, it must take reasonable steps, considering available technology and cost, to inform other controllers processing it that erasure of any links, copies or replications has been requested. This is not legal advice; check what applies to your app.

In a local-first app, an erasure or account deletion plan should cover:

  • server-side copies, relay queues and logs;
  • a delete or removal that syncs to the user’s other devices;
  • local databases, compacted or vacuumed so the content is gone from disk;
  • keys and identity material in the platform key store (see where an app’s data lives on a phone);
  • backups, and how long they are retained.

Devices that are offline, or belong to other people, may never apply the deletion. Say so honestly in your product, and limit what spreads in the first place by keeping each shared document to the people who need it.

Frequently asked questions

Is uninstalling the app enough to delete a user's data?

Not on its own. Uninstalling affects one device. Copies on the user's other devices, on servers and relays, and in backups remain until they are deleted too.

Can I take back data I already shared with someone?

Not from their device. You can stop sending them new changes, but a copy they already received stays with them. The Ink & Switch essay on local-first software lists this as an open problem for sharing.

Sources

Build it with Offline Protocol

Offline Protocol's shared state guide explains the difference between evicting a document from one device and propagating its removal to peers, and why new content should get a fresh document name.

Read the shared state guide