I built a zero-knowledge secret sharing tool. Text and files are encrypted in the browser with AES-GCM 256 before upload - the server only ever sees ciphertext.

The decryption key lives exclusively in the URL fragment (#k=...). URL fragments are never sent in HTTP requests (RFC 9110 §4.2.3), so Cloudflare Workers, D1, and R2 never see it - even in logs.

A few things I tried to do right:

Single-use by default: Durable Objects handle atomic read→decrement→delete with blockConcurrencyWhile, no race conditions on concurrent requests

Paranoid mode: returns not_found instead of expired/burned, no timing oracle