Built-in Self-Test — Fixture
Runs automatically on load, against the same localStorage demo store as the panels below: create a test note → open it once (must succeed) → open it again (must show burned) → confirm the stored value never contained the plaintext. This is the demo of the service logic, running in this browser.
▼ DEMO OF THE SERVICE LOGIC — in-browser, against localStorage. The real service (below) uses the identical protocol against burn_server.py. ▼
✎ Create a Burn Note (Demo)
Your note is encrypted in this page before anything is stored. Only ciphertext goes into the demo store. The key goes only into the link.
◐ Open a Burn Note (Demo)
Paste a burn link, or open one directly in the address bar — if this page loads with a burn link in its address, it tries to open it below. Opening deletes first, decrypts second: there is no second open.
The real service — burn_server.py
The demo above and the real service speak the same protocol. The difference is only where the ciphertext waits: localStorage in the demo, a small JSON store file on the home server in the real service. In both, the server/store never sees plaintext and never sees the key.
Run it on the home server (Python 3, nothing to install — standard library only). From the folder holding burn_server.py:
python3 burn_server.py --host 127.0.0.1 --port 8765
That binds to localhost only. To reach it from another device on the trusted home LAN only, bind the server's LAN address instead (for example --host 192.168.1.241). Never expose it to the internet. Stop it with Ctrl+C. Its whole state is one file, burn_store.json, beside the script (or pass --store /path/to/store.json).
How the protocol works, step by step:
1. Create: this page generates a fresh random AES-GCM 256-bit key, encrypts the note in the browser, and sends only the ciphertext plus a time-to-live to POST /new. The server files the ciphertext under a random ID with an expiry and returns the ID.
2. The link: the page builds /n/<id>#<key>. The key sits after the # — the URL fragment. Browsers never send fragments to a server, in any request, ever. That is not a promise by this code; it is how URLs work.
3. Open (once): whoever opens the link asks the server for GET /n/<id>. The server returns the ciphertext exactly once, deletes the record, and rewrites the store file without it — then the page decrypts locally with the fragment key.
4. Gone: a second GET /n/<id> returns 410 Gone. So do expired notes: records past their timer are purged on access and on a periodic sweep, and after a burn the ciphertext is gone from the store file itself, not just flagged.
5. What the server can see: ciphertext, an ID, and an expiry time. Nothing else. A stolen store file is a pile of ciphertext with no keys anywhere near it.
Honest limits: anyone holding the full link can open the note (once) — the link is the credential, so carry it like one. Screenshots, copying the opened note, or the reader's own memory are outside any burn system's reach. Metadata (that a note existed, its size, its expiry) is visible to the server operator.