Threat model
What it defends against, and what it cannot
This page lays out the entire security boundary of this journal. Trust should rest on boundaries you can see, not on adjectives.
Last updated: 2026-09-19
Structure: how the three layers work
Your passphrase never leaves the device. The login credential sent over the wire is a hash of the passphrase (PH1), and the server stores the value hashed once more (PH2). For the algorithm upgrade status of both layers (PH1/PH2 and the wrapping), see the "KDF upgrade status" section below; this section covers structure only. The entry text itself is encrypted with AES-GCM using a separately generated random key (noteKey); that key is wrapped with your passphrase into an encrypted package and uploaded. Opening the text requires noteKey; opening noteKey requires your passphrase. The two layers are independent.
What it defends against
Database breachIf the whole database is taken, what leaves is ciphertext, hashes, and encrypted packages. No column is a readable diary. Reading the entries requires your passphrase.
A passive serverAn operator browsing the database and request logs as a system administrator sees nothing but ciphertext. Structurally unreadable.
Our own curiosityThe same: no one on our side (currently a staff of one) can read content out of the storage layer.
AI analysisThe server never touches plaintext from start to finish, so AI analysis, summarization, and model training are structurally impossible.
WiretappingTLS everywhere. Even if the transfer were intercepted, what is caught is ciphertext.
Identity subpoenaThere is no email, no name, and no payment data on the server. The only thing that can prove "you are you" is the passphrase in your head; we cannot prove it for anyone either. After payment redemption, "this identity has paid" becomes linkable (see "The exception when you pay" in the privacy policy). Payments are open.
The honest limit: a malicious server
HONEST DISCLOSURE
The sentence "the server structurally cannot read it" holds only for a passive server. If the operator is malicious, they could in theory serve tampered code to harvest keys, or use online credentials to try guessing your passphrase; with a strong passphrase, guessing is not realistic, but the boundary itself exists. Mitigation: the encryption-layer code is public (see public verification) and anyone can verify the behavior with their own hands using the code on the verification page; this mitigates, it cannot eradicate. Writers who need complete zero trust in the operator should use pure local mode (zero uploads).
Passphrase strength is the only real gate
The upgrade status of the login credential and the key wrapping: see "KDF upgrade status" below. What follows covers only why strength is the one real gate.
At least one of three legs: A. at least 12 characters with at least 3 character classes; B. at least 16 characters with at least 2 classes; C. a natural sentence of at least 24 characters (passes an algorithmic scorer).
There is also a weak-passphrase denylist (enforced on both the client and the server, against both credential shapes). A random 5-word string or a 24-character everyday sentence is already a substantive wall against Argon2id-derivation offline guessing; a "123456"-style passphrase does not even pass the gate.
When you set a PIN, key wrapping switches to a combined key of "passphrase and PIN" (the new shape composes two Argon2id 64 MiB derivations; earlier dual-factor accounts used PBKDF2 with 2,000,000 iterations for the PIN segment and upgrade automatically after the next login): even a leaked passphrase cannot open anything without the PIN. Bound accounts always have "lock on open" enabled: every open requires the PIN, and while locked the device holds no unencrypted decryption key (the lock screen's PIN sealing deliberately uses PBKDF2 with 600,000 iterations, prioritizing instant unlock); this setting applies to a single device and cannot be turned off for bound accounts (pure-local mode is not affected).
KDF upgrade status
Both layers of key derivation on this page have gone through one algorithm upgrade; the status of both paths is collected here.
Login credential (PH1/PH2)Since 2026-09-10 it is derived with Argon2id (64 MiB, 3 passes): earlier accounts complete the upgrade automatically on their next login, and until then they remain a fast hash.
Key wrappingThe package's key derivation uses memory-hard Argon2id (64 MiB, 3 passes; new bindings and automatic upgrades since 2026-09), while earlier packages are still PBKDF2 (600,000 iterations) and upgrade automatically to Argon2id after your next successful login, with zero change to notes and keys.
ConvergenceLogin credentials created before the one-time global-expiry cutoff of 2026-09-17; after that, an account that has not finished upgrading gets credentials valid for at most 7 days on each re-login (the login itself triggers the upgrade, which then returns to 180 days; this cap is carried by the new app's two-column login. Devices still running the old single-column app cannot be distinguished by request shape, so their credentials stay at 180 days until the app auto-updates). A login state that is "still a fast hash" is carried by "the next time the app is opened", no longer relying on the 180-day natural expiry.
Once both upgrades are done, the offline-guessing surface after a database leak is protected by Argon2id all the way. While an upgrade is still pending, the transitional cost is paid by the mandatory strength policy (see the section above).
What it cannot defend against
A compromised deviceDecrypted text and held keys exist in device memory and local storage. Malware, shared devices, an unlocked phone: none of these are within the model's reach. Mitigation: bound accounts always have "lock on open" enabled: the key is sealed with your PIN, and no usable decryption key remains in device storage (except briefly, before an older account completes its one-time PIN setup); this gate stops casual snooping, not malware.
A server serving poisoned codeSee "the honest limit" above. Open source and verifiability are mitigations, not eradication. Offline support comes from a small same-origin Service Worker: it caches only the program (never any notes or passphrases), the cached code is same-origin with the page's scripts and adds no new attack surface; the source is fully readable, the browser compares and updates it at every launch, and a poisoned version is automatically replaced the next time you come online.
A weak passphraseThe strength policy blocks common passphrases, but it cannot block the extreme combination that just barely passes the rules yet is still guessed. The final strength of your passphrase is your decision.
Passphrase and recovery kit lost togetherWhen both are lost, recovery is impossible by design and there is no customer-service reset. Write the kit on paper and keep it offline.
An unbound device that breaksIn pure local mode, data exists only on the device; device lost means the words are gone. This is the price of zero uploads, stated plainly when you use it.
Clipboards and screenshotsAfter the recovery kit is shown once, clipboard sync and cloud screenshot backups are leak surfaces. Paper is the most stable container.
What we deliberately do not do
No back doors, no "lawful request decryption" procedure (it technically does not exist), no embedded analytics, no third-party scripts, no hook of any kind for the ad ecosystem. These are not omissions; they are the design.
Verify it with your own hands
Every sentence above can be checked: the public verification page lets you paste your own encrypted backup and decrypt it locally in your browser, proving "only your passphrase opens this"; the network requests it makes number zero. The code and the data format are documented on that page.