Offline Editing Needs More Than a Retry Button
How Muninn combines encrypted local drafts, repeatable requests and visible conflicts to preserve journal work.
A draft and a synchronized entry are different states
Muninn stores encrypted drafts and cached history in IndexedDB. When an entry change cannot reach the API, the application queues an encrypted request and retries it after connectivity returns.
That gives local work a life beyond a single network attempt. It also introduces a distinction the system must maintain: content can exist in the browser before the server has accepted the corresponding change. Offline behavior is therefore a data and synchronization concern, not merely a message saying that the connection was lost.
Retrying must preserve the meaning of the operation
Muninn’s synchronization uses stable identifiers and idempotent mutations. Together, these address a basic ambiguity: a client may lose the response to a request without knowing whether the server accepted it.
A repeatable operation needs to remain the same logical operation when sent again. Otherwise, retrying could create an additional entry or apply the same intent twice. Stable identifiers help connect the queued request to the record it concerns; idempotent handling governs what repetition means.
Version checks address a different question: is the client changing the version it believes it is changing? Incremental change feeds help it learn about changes beyond its local view.
Conflicts are a product decision
When clients edit the same entry, Muninn exposes the conflict and preserves local work for intentional reconciliation. It does not describe concurrent edits as automatically and perfectly merged.
For a personal journal, that is an important distinction. A technically deterministic choice between two edits could still discard something meaningful to the writer. Preserving the alternative gives the user a chance to decide what the resulting entry should contain.
Offline access has a boundary
The application can only offer offline access to content and keys already available in that browser. Local full-text search builds an in-memory index from decrypted entries, so it follows the same availability constraint. An encrypted cache is not a copy of every record on every device.
These mechanisms address separate failure cases: local storage preserves work, a queue retains intent, idempotence handles repetition, and version checks reveal competing changes. A useful offline experience depends on their interaction, and on an interface that can make the resulting state understandable.
