Security Whitepaper
How Dim Notes Protects Your Data
TL;DR
Notes are encrypted on your device — we only see scrambled text and have no way to read them. If you lose your PIN, your recovery codes unlock everything; without them, the data stays scrambled.
This document explains the technical mechanisms behind Dim Notes' encryption and security model. It is written for users who want to verify our claims rather than take them on faith. Last updated: June 2026 · v1.1
1. Architecture Overview
Dim Notes uses a client-server architecture where encryption and decryption happen exclusively in your browser or app — never on our servers. The server stores only ciphertext it cannot read.
The data flow for a note looks like this:
Encryption flow. On your device: your plaintext note is encrypted with AES-256-GCM using your content encryption key. That key is itself sealed with a second key derived from your PIN, which never leaves your device. The resulting ciphertext crosses an HTTPS connection to our server. On our server: only the ciphertext, its initialisation vectors, the sealed copy of your content key, and unencrypted metadata (timestamps, color labels, pinned and archived flags) are stored. We cannot unseal your key without your PIN, so we cannot decrypt your data.
The Content Encryption Key (CEK) lives only on your device while your session is unlocked. It is never sent to our servers in raw form. The server stores a copy of your CEK that has itself been encrypted with a key derived from your PIN — so even that stored copy is opaque to us.
2. Encryption Implementation
All encryption runs on your device, never on our servers. Note content is encrypted with AES-256-GCM using @noble/ciphers, and keys are derived with Argon2id using @noble/hashes. Both are audited, dependency-free implementations. We use Argon2id rather than the Web Crypto API's PBKDF2 because it is memory-hard, which makes brute-forcing a short PIN far more expensive. Randomness comes from the platform CSPRNG (crypto.getRandomValues()). Share links are a separate path and use the browser's built-in crypto.subtle for AES-GCM.
- —Algorithm: AES-256-GCM (256-bit key, authenticated encryption with additional data). GCM provides both confidentiality and integrity — a tampered ciphertext will fail to decrypt rather than produce garbage output.
- —IV (Initialization Vector): 96-bit (12 bytes), generated fresh from
crypto.getRandomValues()for every encrypt call. IVs are never reused. - —What is encrypted: Note title, note body, and file attachments (both contents and filenames). These fields leave your device only as ciphertext. Attachments uploaded before end-to-end filename encryption shipped may still have their original filename stored in plaintext; their contents were always encrypted.
- —What is not encrypted: Color labels, timestamps, and whether a note is pinned or archived. See Section 6 for the full breakdown.
File attachments are encrypted client-side with the same AES-256-GCM scheme as notes, using the same per-account CEK, before being uploaded to cloud storage — both the file contents and the original filename. The server stores only the ciphertext bytes, an IV, and the encrypted filename; it can read neither the file nor its name.
3. Key Derivation & Storage
Dim Notes uses a two-key system: a Content Encryption Key (CEK) that encrypts your data, and a Key Encryption Key (KEK) that protects the CEK.
CEK — Content Encryption Key
A 256-bit random AES key generated once when you first set up encryption. This key encrypts all your notes and attachments. It is stored on our server only in wrapped (encrypted) form. We never see the raw CEK.
KEK — Key Encryption Key
Derived from your PIN on your device using Argon2id with m=65,536 (64 MiB), t=3, p=1 and a 256-bit random salt. Argon2id is the OWASP-recommended algorithm for password-based key derivation — its memory-hard design makes offline brute-force attacks using GPUs or ASICs significantly more expensive than PBKDF2 or bcrypt. The KEK itself never leaves your device and is never stored anywhere.
When you enter your PIN to unlock, your device derives the KEK locally, uses it to unwrap the CEK from the server, and holds the CEK in memory for the duration of your unlock session. When the session expires or you lock manually, the CEK is discarded from memory.
Your PIN never leaves your device — not even on unlock. To prove you know it, the client sends only a one-way proof (a SHA-256 hash of your derived key), which the server checks against a stored scrypt hash. The proof cannot be reversed to your PIN or your key, so a server that logged every request would still be unable to unwrap the CEK.
4. Authentication Layers
Dim Notes has two separate authentication layers that operate independently.
Layer 1 — Account Login
Standard account authentication. Supports email + password, Google OAuth, and Microsoft OAuth. This layer controls who can access your account. Session tokens are managed via httpOnly cookies. Dim Notes never handles your OAuth provider credentials.
Layer 2 — Note Unlock (Encryption Gate)
A second gate that controls access to note content. Being logged in is not enough — you must also pass the unlock step before your notes are decrypted. This layer is independent of Layer 1; even if your account session is valid, a locked Dim Notes session hides all note content.
Unlock methods (you choose one at signup):
- —TOTP: Time-based one-time password via Google Authenticator or any compatible app. First unlock per session only.
- —Email OTP: Email verified once during setup; all subsequent unlocks use your PIN.
- —PIN: 4–6 digit PIN set by you. Required alongside TOTP/Email OTP to derive the encryption key. Always available as a fallback.
Auto-lock: Your session automatically locks after a configurable period of inactivity (5 minutes, 15 minutes, 30 minutes, 1 hour, or Never). The default is 15 minutes. After the initial unlock, re-locking always requires your PIN or biometrics — not a new TOTP or email code — to avoid constant interruptions.
The unlock session is tracked as an HS256 JWT stored in an httpOnly, SameSite=Strict cookie. The cookie is inaccessible to JavaScript running on the page, which prevents XSS from stealing the unlock session.
5. Recovery Codes
When you enable encryption, Dim Notes generates 10 single-use recovery codes in the format XXXX-XXXX-XXXX. These are the only way to recover your encrypted notes if you lose access to your PIN and your primary unlock method.
- —Each code independently encrypts a backup copy of your CEK using Argon2id-derived keys (same parameters as the PIN: m=65,536, t=3, p=1) with unique salts. The 10 backups are stored on our server.
- —Using a code decrypts its CEK backup and immediately marks the code as consumed. It cannot be reused.
- —We store a scrypt hash of each code for verification. We cannot reverse the hash to recover the original code.
- —Recovery codes are presented to you once at setup. If you lose them without saving copies, they are gone.
- —You can regenerate your recovery codes at any time. Regeneration creates a new set of 10 codes and invalidates all previous ones.
- —We cannot reset your PIN, restore your recovery codes, or recover your encryption key. This is intentional — it means a server breach cannot compromise your data.
6. Zero-Knowledge Architecture
"Zero-knowledge" means we designed the system so that our servers cannot read your note content, even if compelled to by law enforcement, even if our database is breached.
We cannot read
- ✓Note titles
- ✓Note body content
- ✓File attachment contents
- ✓Folder names
- ✓Tag names
- ✓Your raw PIN or recovery codes
- ✓Your encryption key (CEK)
We can see
- —Your account email
- —Note count and storage usage
- —Timestamps (created/updated)
- —Color labels
- —Whether notes are pinned or archived
Folder and tag names are encrypted using the same AES-256-GCM scheme as note content. They are decrypted client-side after you unlock your notes. While locked, folder and tag names are not visible — even on your own device.
7. What We Don't Do
- —No cloud AI. Your notes are never sent to a third-party AI service or used to train models. Any AI-assisted feature runs entirely on your device — the content never leaves it.
- —No analytics on note content. Usage analytics (PostHog) track app-level events like "note created" — never what is inside a note.
- —No advertising, no ad networks, no tracking pixels.
- —No selling data. We are a subscription product. Our revenue comes from you, not from your data.
- —No third-party access to note content. Our infrastructure vendors (Neon, Cloudflare, Hetzner) receive only the encrypted ciphertext stored on their hardware — the same blobs we cannot read.
8. Known Limitations
We believe honesty about limitations is more useful than pretending they don't exist.
- —Metadata is visible. Note count, storage usage, timestamps, color labels, and whether notes are pinned or archived are stored in plaintext. This reveals usage patterns even without access to content.
- —Not open source (currently). Dim Notes is built and maintained by a solo developer. The codebase is not yet public. The encryption implementation in
lib/crypto.tsuses standard Web Crypto API calls that can be independently verified by inspecting the JavaScript bundle in your browser's DevTools. We plan to open-source the repository after launch stabilizes. - —PIN strength matters. The security of your encryption depends on the strength of your PIN. A 4-digit PIN has 10,000 possible values. Argon2id (m=65,536 MiB, t=3) makes brute-force slow, but a longer PIN or using a random one is stronger. We recommend at least 6 digits.
- —Biometrics are mobile-only. Biometric unlock (fingerprint / Face ID) is available on the mobile app only. It does not change the underlying cryptography — it is a convenience gate that uses a stored PIN to derive the KEK. The PIN still underpins the encryption.
9. Threat Model
A threat model is a statement of what a security system does and does not protect against. Being specific about this is more useful than vague assurances.
Protected against
- ✓Server breach / database dump: Attackers who steal our database get note ciphertext and a PIN-wrapped copy of your key. Neither can be opened without your PIN, which never reaches our servers. We store only a scrypt hash of a value derived from your PIN — it cannot be reversed to the PIN or the key.
- ✓Live capture of unlock requests: Your PIN is never transmitted, not even on unlock. The client derives your key locally and sends only a one-way proof (a hash of the key) that we verify against a stored scrypt hash. A server that logs every request sees only proofs, which cannot be reversed to your PIN or used to unwrap your key.
- ✓Insider access to stored data: Dim Notes staff, contractors, or infrastructure providers cannot read note content from what we store. The server never holds the unwrapped CEK. (An insider who could deploy malicious client code is a different case — see "actively compromised server" below.)
- ✓Legal requests for note content: A court order served on Dim Notes or our infrastructure providers cannot produce plaintext note content — we do not have it.
- ✓Network interception: All client-server communication uses TLS. Note content additionally travels as ciphertext, so an attacker who broke TLS would still see only encrypted notes. Your PIN never travels at all — the unlock request carries only a one-way proof derived from it, not the PIN itself.
- ✓Cross-site scripting stealing note content: Note content is decrypted only when the unlock session is active. Even if an XSS attack runs on the page, it cannot access the unlock JWT (httpOnly cookie) and the CEK is held in memory only.
Not protected against
- ✗An actively compromised Dim Notes server (tampered client code): Your PIN is never transmitted — the app derives your key on your device and sends only a one-way proof, so a compromised backend cannot capture PINs from unlock traffic or unwrap your key from what it stores. The residual risk is the browser: because the web app’s JavaScript is served by us, an attacker with live control of our servers could serve tampered code that captures your PIN in your browser before it is used. This is inherent to any web app that delivers code from a server. The mobile app narrows this window because its code is a signed binary installed from the app store, not fetched on each visit. A database dump still cannot read your notes.
- ✗Compromised device: If an attacker has physical or remote access to your unlocked device, they can read your notes — the same way they could read anything else on your screen. Device security is outside the scope of Dim Notes.
- ✗Keyloggers: A keylogger on your device captures your PIN before it reaches the crypto layer. Dim Notes cannot detect or prevent this.
- ✗User error: Sharing your PIN, losing all recovery codes, or being socially engineered into providing credentials. We cannot protect against voluntary disclosure.
- ✗Malicious browser extensions: A malicious extension with broad page access could read DOM content or intercept decrypted text. Use a trusted browser environment.
- ✗Account takeover: If an attacker gains access to your Layer 1 account (email + password), they can initiate an unlock attempt. They still need your PIN, but account security is the first line of defense.
10. Account & Data Deletion
You can permanently delete your Dim Notes account and all associated data at any time. No data is retained after deletion.
Deletion removes the following immediately and irreversibly:
- —All notes, note versions, and drafts
- —All folders and tags
- —All file attachments (deleted from cloud storage)
- —Your unlock settings, PIN hash, TOTP secret, and recovery codes
- —Your active subscription (cancelled immediately with Paddle)
- —Your account credentials and session data
Verification before deletion: To prevent unauthorised deletion, you must verify ownership of your account email with a one-time code before the deletion is processed. The code expires after 10 minutes.
Deletion process
- 1.Go to Settings → Account → Delete account, or visit dimnotes.com/delete-account
- 2.Type DELETE (all caps) to confirm intent
- 3.A 6-digit verification code is sent to your account email
- 4.Enter the code to authorise the deletion
- 5.All data is permanently removed and a confirmation email is sent
To delete your account: dimnotes.com/delete-account
Questions about our security model? [email protected]
This document is updated when the security implementation changes. v1.0 — May 2026.