One-time, end-to-end encrypted note sharing. Paste a password or a secret, get a single link that destroys itself once it has been read.
Your note is encrypted in the browser before anything is sent. The key is 32 random bytes that live in the URL fragment, which browsers never transmit, so the server only ever stores ciphertext it has no way to open.
A password is required on top of that, so every note carries two independent secrets. Send the password separately from the link, or you have posted the key next to the lock.
pwStretch = PBKDF2-SHA256(password, saltB, 600_000)
ikm = fragmentKey || pwStretch
K = HKDF-SHA256(ikm, salt = saltA, info = "lockerr-v1|" + id)
ct = AES-GCM(K, iv, plaintext, aad = id)
One combined key rather than two nested layers. Nesting leaves the outer GCM tag as an offline password oracle: someone holding only a database dump could confirm a password guess without ever having the link. Combining removes that.
Reads are burned by a single conditional statement, which is the whole correctness guarantee:
UPDATE notes SET view_count = view_count + 1
WHERE id = ?1 AND expires_at > ?2 AND view_count < max_views
RETURNING ...D1 serialises writers, so two concurrent reads of a one-view note cannot both succeed. There is a test that fires eight at once and asserts exactly one wins.
The note is encrypted in your browser and the key never reaches the server. That part is arithmetic. But the page doing the encrypting is served by the same origin, so a compromised deploy could change it. This is the boundary condition of all browser-delivered end-to-end encryption, and the reason this repo is public.
Next.js (static export) and Hono in a single Cloudflare Worker, with D1 for storage and an hourly cron sweep. No framework runs on the server; nothing is rendered there, because nothing there can be decrypted.
npm install
npx wrangler d1 create lockerr # put the id in wrangler.toml
npm run db:init # local schema
npm run dev # next dev + wrangler dev
npm run preview # built output, real CSP, real routing
npm test # 38 unit + integration
npm run e2e # 14 browser, Chromium + WebKitnpm run dev is fast but serves no CSP, so run preview before trusting
anything security-related.
SPEC.md records the architecture and, more usefully, the
reasoning: why the key is combined instead of nested, why the burn is one
statement, why the sweep cap is derived from the cron cadence, and why a static
Next export needs its inline script hashes pinned or it renders a blank page.
MIT