Zero‑knowledge note sharing. Encryption happens in the browser (AES‑GCM); the server stores ciphertext only. The URL looks like https://host/n/{id}#k=base64url(key) — the key stays in the URL fragment.
- Live demo: https://sn.1ccc.de/
- Install dependencies
npm i- Run in dev mode
npm run dev- Production build
npm run build
npm start-
Client
- Generates a random 256‑bit
noteKey(WebCrypto). - Optional user password → PBKDF2(SHA‑256, 310k, 16‑byte salt) →
passKey. - Final CEK = SHA‑256(noteKey || passKey), or just
noteKeyif no password. - Encrypts the note with AES‑GCM (12‑byte IV). Sends to server:
ciphertext,nonce, optionalsalt, and metadata. - Shareable link:
/n/{id}#k=base64url(noteKey). Password must be shared separately.
- Generates a random 256‑bit
-
Server
- Stores
notestable (SQLite via better‑sqlite3). - Endpoints:
POST /api/notes— create a note.GET /api/notes/:id/meta— read metadata (does not consume a view).POST /api/notes/:id/consume— get ciphertext and decrement views/burn.
- Cleans up expired notes every 10 minutes.
- Security headers: CSP, HSTS, Referrer‑Policy: no-referrer.
- Rate limiting: 60 requests/min per IP for
/api/*.
- Stores
- Expiration: default 24 hours, up to 30 days.
- Burn after read: delete on the first
consume. - View limit: if reached, note is deleted on consume.
- Extra password: client‑side only; server stores salt only.
- The server never sees the key or the password.
- URL fragment (
#k=...) is not sent to the server; Referrer‑Policy prevents leaks. - AES‑GCM with 96‑bit IV; key length is 256‑bit.
- PBKDF2 is used temporarily for the MVP. Argon2id is recommended long term.
- Add Argon2id in the browser (e.g.,
argon2-browseror@noble/argon2when stable). - In deriveCombinedKey:
passKey = Argon2id(password, salt, { t=3, m=64MB, p=1, hashLen=32 })combined = HKDF‑SHA256(ikm = noteKey || passKey, salt = '' , info = 'secret-notes', len = 32)
- Mind CSP: you may need
wasm-unsafe-evaland/or a worker.
- “Burn after reading” happens at
consume, not after successful decryption (the server cannot know decryption outcome). - If the password/key is wrong, a view is still consumed. The user is shown an error.
Feel free to submit a pull request or open an issue if you have any suggestions or improvements.