the mechanics of the burn
How it works
- Encrypt. Your browser generates a random 256-bit key and encrypts the secret with AES-256-GCM. Only the encrypted blob is uploaded.
- Share. The key rides in the link after the
#— browsers never send that part to any server. Send the link to one person. - Burn. The first open destroys the secret on our server. And if nobody ever opens it, it self-destructs on its own after 1 day — or on the date the sender picked, 30 days at most. Nothing to hack, nothing to subpoena.
Add a passphrase and the link alone is not enough — tell it to the recipient through a different channel (a call, in person). The passphrase is checked before anything burns: a typo never destroys the secret, and after 5 wrong attempts the secret locks for 5 minutes. The passphrase itself never travels to the server — only a derived proof does.
The flow, on one napkin
YOU · your browser
┌─────────────────────────────────────┐
│ secret ── AES-256-GCM ──► blob │
│ random key ──► link /s/slug#key │
└────┬───────────────────────┬────────┘
│ blob (encrypted) │ link — you send it
▼ ▼
SERVER · sqlite RECIPIENT · their browser
stores the blob opens the link
never sees keys ◄── fetches the blob
│ (#key never travels)
│ first read
▼
DELETE ── burned 🔥 decrypts locally ──► secret
What this actually guarantees
- The URL never carries your data. The link holds only a random identifier and the key. And the part after
#is never sent by browsers — it can't end up in our logs, in Cloudflare, or in any proxy in between. - One read, then it's physically gone. The moment the recipient clicks reveal, the server deletes the encrypted blob — atomically, before decryption even happens. Expired secrets are swept hourly and the database file is compacted, so deleted means deleted.
- Stealing our database gets you nothing. It holds encrypted blobs and timestamps — no keys, no plaintext, no IPs. Without the key from the link, an AES-256-GCM blob is unreadable. For an attacker, for a court order, and for us too.
The honest fine print: whoever holds the full link holds the secret — choosing a safe channel to send it is on you (that's what the passphrase is for), and a stolen database still shows how many secrets exist and when they were created. Content, never. The whole link — identifier and key — is generated in your browser with its cryptographic random generator; the server validates it and enforces its uniqueness, but never creates it.
Don't trust us — audit it
The entire service is open source. These five files are everything that touches your secret:
static/js/crypto.js— the heart: encrypt, decrypt and key derivation, ~60 readable lines of WebCrypto. Your browser runs exactly this.static/js/create.js— the create flow: generates the key, encrypts, uploads only the blob.static/js/reveal.js— the read flow: burns the server copy, then decrypts locally with the key from the #.app/store.py— the atomic burn-on-read, the 5-attempt passphrase lock and the expiry purge, in SQL.app/crypto.py— a Python mirror of the same scheme — pinned to the JavaScript by a shared test vector, so neither side can drift.