bongsmoke.cloud

Two halves,
kept apart.

Most “self-destructing note” services encrypt your message and then store the result on their own servers. You are trusting them not to keep it. This one is built so that there is nothing to keep.

Your browser256-bit client shareAES-256-GCMcompress · pad · encryptkey = HKDF(A ‖ B)plaintext never leavesthis boxThe link/n/<id>#A · iv · ciphertextfragment — never sentto any serverThe serverB = HMAC(secret, id)released exactly onceSET burned NX EXstores: one expiring flagholds: no ciphertext, evercarries Aasks only for the id · receives B

The ciphertext lives in the part of the URL after #, which browsers never send to a server. The other half of the key is derived on demand and released once.

The short version

The key that opens your note is assembled from two pieces that never sit in the same place. One piece rides inside the link. The other is computed by our server, handed out a single time, and then refused forever. Neither piece is any use alone, and we only ever hold one of them.

Writing a note

Your browser asks the server for an identifier. The server invents one — 128 random bits plus the moment the note should die — signs it with a secret only it knows, and returns both the identifier and that signature. It writes nothing down. There is no record that a note was created, because there is nothing worth recording.

Your browser then generates 256 random bits of its own, combines them with the server’s signature through HKDF to produce an AES-256 key, compresses and pads your message, encrypts it, and throws the server’s half away. What is left is a link:

bongsmoke.cloud/n/<id>#<your half>.<iv>.<ciphertext>

Everything after the # is a URL fragment. It is the one part of a web address that a browser keeps to itself — it is not in the request, not in the Referer header, not in any server log anywhere. The encrypted message travels entirely inside the link you send.

Reading a note

Opening the link fetches nothing. That is deliberate: chat apps and mail servers follow links to build previews, and a page that phoned home on load would be burned by a bot before the recipient ever saw it. Nothing happens until someone presses Reveal.

At that point the browser sends one thing — the identifier — and asks for the missing half. The server checks the expiry, then performs a single atomic write: mark this note burned, but only if it is not already marked. If that write succeeds, this is the first reader, and the server returns its half of the key. If it fails, the answer is simply gone, and no amount of asking changes it.

The browser reassembles the key, decrypts, shows you the note, and clears the fragment out of the address bar. At no point did the ciphertext or the plaintext cross the network.

What we actually store

Burn markers
A fixed-size flag under a hash of the note identifier, set the first time a note is opened. It expires on its own. It cannot be turned back into a note, a link, or a message.
Rate-limit counters
A number per connection per ten minutes, keyed by a hash of the IP address rather than the address itself. Also expires on its own.
That is the entire database
No messages. No ciphertext. No key shares. No account, because there is nothing to attach one to.

A passphrase, if you want one

Adding a passphrase stretches it with PBKDF2-SHA256 at 600,000 iterations and mixes the result into the key alongside the other two shares. It is a third factor, not a replacement: someone would need the link and the first reveal and the passphrase. Send it through a different channel than the link, or it is decoration.

What this does not protect you from

Anyone who sees the full link before your recipient does can read the note, and your recipient will find it already burned. The fragment may linger in your own browser history and in whatever app you pasted it into. And once someone has read a note, nothing stops them keeping it.

The threat model is written out in full, including the parts that are inconvenient.

Write a note