Muru brings notes, planning, shopping, and money into one workspace. As its vault expands to protect locked note bodies as well as credential passwords, it needs a precise answer to a basic engineering question: which values leave the browser encrypted?
The Muru case study explains the wider product and its Compact Daybook design. Here I focus on the credential form, locked-note saves, key derivation, and unlock lifecycle in the current implementation.
Follow the data leaving the form#
The password field goes through browser-side encryption before submission. Service name, username, URL, category, and notes remain separate metadata fields. An encrypted password does not establish confidentiality for every field in the record.
That distinction changes interface copy. Describing the feature as password encryption gives users a boundary they can understand; claiming the entire workspace is encrypted would require a different implementation and evidence.
Extend the boundary to locked notes#
Locked notes use the same passphrase-derived encryption helpers for their body. The browser sends encrypted content, an IV, and a salt while the ordinary content field is cleared. Note titles, folders, tags, and other metadata remain outside that body-encryption boundary.
The rich-text editor, Markdown source view, and code editor still feed the note-save lifecycle. Save requests are serialized so a slow response cannot overwrite a newer edit, and pending edits are drained before the editor's own navigation or lock transition. Encryption belongs in the save path, including its final pending attempt, rather than only behind a separate lock button.
Derive the key and generate the IV separately#
The Argon2id helper derives a 32-byte key from a passphrase and a fresh 16-byte salt. The source configures 19,456 KiB of memory, two iterations, and parallelism of one. These values describe the current implementation; they are not measurements of performance across devices.
The AES-GCM helper generates a fresh 12-byte IV with Web Crypto's random-number generator. It imports the key as non-extractable and returns ciphertext with the IV needed for decryption. The salt belongs to key derivation, while the IV belongs to this encryption operation. Reusing one as the other would mix two different responsibilities.
Treat unlocking as a separate session#
Supabase authentication identifies the account. The vault passphrase unlocks protected password values and locked note bodies locally. Muru verifies an unlock by decrypting a stored verifier and keeps the passphrase in React state. It clears that state on lock, starts locked after a reload, and supports five-, ten-, thirty-, and sixty-minute idle timeouts with a ten-minute default.
Clearing state ends access through that application state; it is not a claim that JavaScript can securely erase every memory copy. The design also depends on the browser executing trustworthy application code.
Keep backups and offline pages honest#
The JSON backup carries credential passwords and locked note bodies as stored ciphertext with their IVs and salts. Ordinary notes, tasks, transactions, shopping records, and metadata remain readable JSON. The whole export is not an encrypted container, and file contents are excluded.
Import includes the vault verifier so a new account can use the original passphrase to unlock restored encrypted records. It preserves an existing account's verifier instead of replacing it. Importing ciphertext does not remove the need for the passphrase that encrypted it.
The service worker caches previously opened workspace pages for offline browsing. These pages can contain ordinary personal records and credential metadata. Personal caches are cleared on sign-out and authentication-route returns; API calls, Server Actions, admission routes, and cross-origin file bodies are excluded. An offline page is not a promise of offline saving or of encryption for every cached value.
Make the boundary reviewable#
A useful review traces the plaintext input, derived key, ciphertext submission, stored metadata, and lock behavior individually. Database ownership rules address access to records; encryption addresses the protected password value. Reviewing both keeps a security-sensitive feature explainable without implying an independent security audit.
