Security, precisely. And where it stops.
The promise, precisely, and where it stops. This page is a shortened version of the design document that ships with the code; it is kept honest by that document, not by this one.
The promise
Nobody without your passphrase or recovery code can read your note titles, note bodies or folder names, including whoever runs the server.
How the keys work
One random data key encrypts everything. Your passphrase and your recovery code each unlock a sealed copy of it.
passphrase ──Argon2id(m=64 MiB, t=3, p=1, salt)──► KEK_p ──┐ ├──► wraps the data key recovery code ──HKDF-SHA-256(salt)────────────────► KEK_r ──┘ data key (256-bit, random) ──AES-256-GCM──► note bodies, note metadata, folder names
- The data key is random, never derived from the passphrase. Changing your passphrase re-wraps 32 bytes and re-encrypts nothing. That also means it revokes nothing: a device that is already unlocked stays unlocked. For a lost device, sign out and lock where you can, then change the passphrase.
- Every encryption uses a fresh random nonce, and each envelope is bound to its own note or folder, so a stored note cannot be silently altered, downgraded, or swapped for another. Replaying an older copy of the same note is caught by the server’s revision checks, not by the encryption.
- “Stay unlocked on this device” keeps the data key in the browser as a non-extractable key: the browser will not hand its raw bytes to any script. Code running in the unlocked app can still decrypt with it, and it does not replace disk encryption or a locked computer.
- The passphrase, the recovery code, and any key derived from them are never stored anywhere by us.
What is stored, and where
| Where | Encrypted | Not encrypted |
|---|---|---|
| Your device (IndexedDB) | Titles, bodies, folder names, pins, creation dates, versions, the crash journal | Note ids, last-modified time, a revision number, a deleted flag |
| Our server (Firestore) | The same ciphertext, byte for byte, plus 30 days of versions and any shared snapshots | The same reconciliation fields and a server timestamp; for versions, when and why each was saved (edit, delete, restore) and when it expires; for shared links, when each was made and expires, and which note it came from; plus your account email, your plan, a usage meter (encrypted bytes stored, this month’s history and link counts) and which version of the terms you agreed to |
| The search index | Held in memory in a worker, never written to disk. It holds word tokens, never text or positions, so it cannot rebuild a note. | |
Our server is Google Cloud Firestore in the United States (us-central1); see the privacy policy. HushJot is made in Portugal, but your ciphertext is stored in the US. Because the server cannot read a title, it cannot index or sort by one. Sorting and filtering ten thousand notes in memory costs about three milliseconds, so nothing was lost by refusing to.
Nothing vanishes by surprise. Nothing is merged behind your back.
What “delete” means
There is no “delete forever” button, on purpose. Deleting a note drops its body from the current record and keeps the sealed title as a tombstone, so the deletion reaches your other devices. The version history keeps the body ciphertext for up to thirty days on each device and in your account, so Recently deleted can offer it back. On the server, expired versions are removed by a scheduled policy. In every case the bytes are unreadable without your key, but “deleted” means “unreadable and expiring”, not “gone this second”.
Deleting your account removes everything under it from the server. How to ask is on the support page.
Backups. The database keeps seven days of point-in-time recovery and weekly backups, and once a week a copy of it and of the sign-in records goes to a separate project, also in the United States. They hold the same ciphertext, plus emails and hashed account passwords, which are not your vault passphrase. A backup lives at most 60 days, so deleted data can outlast the delete by that long, still unreadable. They exist to recover the whole service, not to restore one account.
Conflicts are kept, never merged
If two devices edit the same note while apart, the second one to arrive is kept as a separate conflict copy next to the first. Nothing is auto-merged and nothing is discarded. You are told, and you choose.
What it defends against. What it does not, and why.
Defended
9 threats- The server operator reads notes
- The server only ever receives ciphertext
- A database dump or backup leaks
- Titles, bodies and folder names are AES-256-GCM
- Stored notes are tampered with or swapped
- The GCM tag fails, and each envelope is bound to its own note; nothing is silently corrupted
- Downgrade to a weaker algorithm
- The header is authenticated data
- Reading the key out of the browser
- The device key is non-extractable: usable in place, never readable as bytes
- Offline passphrase guessing
- Argon2id, 64 MiB memory-hard, over a passphrase the app can generate for you at about 62 bits
- Correlating identical notes
- A random nonce per encryption
- Accidental overwrite or delete
- Encrypted versions for 30 days; a restore keeps the replaced text too
- A stolen session erasing history
- Versions are create-only in the server rules; only expiry removes them
Not defended, and why
- Metadata.
- Note count, per-note ciphertext size, timestamps, edit frequency and the shape of your folder tree are visible to anyone holding the database. Hiding them needs padding and dummy traffic, which is disproportionate for a notepad. Stated rather than fixed.
- A compromised device.
- Malware, or a hostile browser extension with access to an unlocked session, can read plaintext. No browser app can defend this.
- A weak passphrase.
- Argon2id raises the cost of each guess; it cannot rescue
password123. The create screen offers a generated phrase, estimates what a typed one is worth, enforces a ten-character minimum and refuses known passwords. Someone who declines all of that gets the vault they chose. - A shared link, once sent.
- Whoever holds the link can read that note until it expires or is revoked, the chat it was pasted into included. The key is in the link; that is what keeps it away from the server.
- Forgotten passphrase and lost recovery code.
- The synced notes are gone. Permanently. By design: a reset link that restored access would mean the server could decrypt them. What you can do is start over: signed in, with your account password, you can erase the synced copy and create a new vault on the same account and plan.
- An agent you let in.
- An agent scoped to a folder reads that folder in the clear and sends what it reads to its model provider. Choose the folder, or use a local model.
A malicious or coerced host.
The server delivers the app on every load, so it could serve one targeted user a build that reads the passphrase as it is typed. This is the structural limit of any web app’s encryption promise, and it is not closed, only made harder to do quietly: a new build never activates without your consent (the app asks, it does not swap itself in on a reload), the shell carries integrity hashes, and the content security policy allows no third-party script origin at all and only a short list of places to connect to. That narrows where injected code could send what it reads; it does not close it, because a hostile host also controls the policy it serves.
Against a database breach, a subpoena of stored data or an honest-but-curious operator, the encryption does exactly what this page says. Against a host willing to attack one named user, a web app is the wrong tool, and you should want a signed, installed application instead. We would rather tell you that than reassure you.
Recovery, sharing, agents, logs.
Recovery, and why there is no reset link
Recovery is a code: 160 random bits shown exactly once when you create your vault, behind an “I have written this down” confirmation. It wraps a second, independent copy of the same data key. Lost the passphrase but have the code: set a new passphrase from the unlock screen. Lost the code but have the passphrase: re-issue it from the account panel, and the old one stops working the moment the new one is shown. Lost both: nobody can read those notes again, and the setup screen says so in those words. What remains is Start over: signed in, after re-entering your account password and typing erase, the synced notes, folders, versions, links and key record are deleted, and you create a new vault. Your sign-in and your plan stay.
Sharing a note by link
Publishing seals a snapshot of the note under a fresh random key that is never your vault key, and puts that key in the URL fragment, which browsers never send to any server. The public record holds ciphertext and two timestamps and deliberately no owner id, so someone holding two of your links cannot tell they came from the same person. Your own list of links, which is what lets you revoke them, is kept under your account: the server can see which link ids are yours and which note each came from, never what they say. The page that opens a link is a separate document of under 60 KB with no vault, no sign-in form and no SDK, so a link from a stranger cannot present a fake passphrase prompt on our origin.
The CLI, MCP server and Rust SDK
The command-line client signs in, fetches your sealed key record, runs Argon2id on your machine and decrypts there. The cached data key lives in your operating system’s keychain; the search index is rebuilt in memory and never written to disk. The MCP server is off until you enable it, can be confined to chosen folders, and has no verb that can lose text: anything it rewrites is kept in the note’s History. A note an agent reads is sent to that agent’s model provider, under its policies, so scope the agent to the folder you are happy to share.
Other apps can offer HushJot Sync through the Rust SDK, a developer preview. It works like the CLI and only unlocks, never changing your passphrase or recovery code, but it cannot stop the app around it from mishandling your text. Signing in to another app means trusting that app with your notes.
Details →Logging and telemetry
- No analytics, no third-party scripts, no network calls beyond the app’s own assets and the sync backend.
- Errors report a shape, never a value: the key-derivation and search workers return an error name, never the passphrase, salt, query or note content.
- This website sets no cookies and loads no third-party script on any page except the account page, which loads Paddle’s checkout.
What is not done yet.
Stated because a security page that lists none is not being straight with you.
- No independent security review yet.
- No data-key rotation. Changing a secret re-wraps the key; re-encrypting every note is not built.
- Rate limits on writes are by size and shape, not frequency.
- The 64 MiB unlock has not been tested on a 4 GB Android phone.
- Published asset hashes and tagged releases are the next step. They would turn the integrity hashes into a check rather than a visibility aid.
Using HushJot in your country.
Some countries restrict or license encryption software, or can require you to disclose a passphrase. You are responsible for following the law where you are. If HushJot is not permitted there, do not use it. See section 20 of the terms.
Questions about any of this?
Reports of a vulnerability go to the same address and are answered first.