Interactive Demo

Rotate your device to landscape mode to view the interactive demo

Built withNext.js, React, TypeScript, Upstash Redis, Web Crypto API
RoleDesign Engineering, Product Strategy, Security Architecture

Ghostnote started as a tool I needed. I was coordinating reporting trips and wanted something I could hand to a source that wouldn't leave a trail. A link, a short code, or a QR. They open the same encrypted space, exchange what they need to, and walk away. The conversation doesn't exist anywhere it isn't needed.

The Gap

Burner note tools exist. Write something, get a link, it self-destructs after one view. That's fine for a password. It's useless for a conversation.

Signal is excellent. It also requires both people to install an app, create an account, and hand over a phone number. That's a lot of friction for a one-time handoff with someone you don't know, or can't risk knowing.

Ghostnote is the thing between them: two-way, account-free, ephemeral by default. No installs. No identities. No record.

How it Works

Creating a drop generates a symmetric AES-256-GCM key in the browser via the Web Crypto API. That key goes into the URL fragment, which means it never touches the network. The server sees ciphertext and a TTL. That's all it ever sees.

Once a message is read or the timer expires, it's deleted from Redis. The key never re-enters the server. There's nothing to recover because there's nothing to find.

The drop creator gets a separate owner key to destroy the conversation instantly. Or they can let it expire on its own. TTL policies purge idle drops regardless.

Safety by Design

Abuse prevention was the hardest design problem, because the system can't see content. Real-time moderation isn't possible. It was never on the table.

The reporting system keeps privacy intact by default. Filing a report sends only metadata. If someone wants to include message content, that's their choice. Ghostnote pulls from a local buffer of recently read messages, so even burn-after-reading messages can be included without decrypted content ever leaving the browser.

Reported drops get flagged with a count and timestamp. The architecture makes sustained abuse impractical without making the tool into a surveillance system. The two constraints don't have to be in conflict if you design around both from the start.