Skip to content

Repository files navigation

Lockerr

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.

lockerr.chpecson.dev

How it works

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.

What you still have to trust

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.

Stack

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.

Running it

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 + WebKit

npm run dev is fast but serves no CSP, so run preview before trusting anything security-related.

Design notes

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.

Licence

MIT

About

One-time, end-to-end encrypted note sharing. Encrypted in your browser; the key never reaches the server.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages