How Whisp Works

Zero-knowledge encrypted secret sharing - the server never sees your data

1 Creating a Secret
Browser

You type your secret

Plaintext lives only in your browser's memory. It is never transmitted.

Browser

Generate a random encryption key

A 256-bit AES key is generated using Web Crypto API. This key never leaves the browser.

Browser

Encrypt with AES-256-GCM

Your secret is encrypted with the random key + a unique 12-byte IV. If you set a password, a second encryption layer is added using PBKDF2-derived key (600k iterations).

Browser

Send only ciphertext to server

Only the encrypted blob, IV, salt, TTL, and burn flag are sent. The encryption key stays in the browser.

Server

Store encrypted data

Server generates a unique ID, stores the ciphertext in SQLite, and returns the ID. It cannot decrypt anything.

Browser

Build the share link

The key is placed in the URL fragment (after #), which browsers never send to servers.

https://whisp.dev/s/abc123def456#9fK2x...encryption-key...bQ7w=
↑ sent to server (just the ID) ↑ NEVER sent to server (decryption key)
Why the # matters: Everything after # in a URL is called the "fragment." By HTTP specification, browsers never include fragments in network requests. The server literally cannot see it - not in logs, not in headers, nowhere.

2 Viewing a Secret
Browser

Open the share link

Browser extracts the secret ID from the path and the decryption key from the # fragment.

Server

Fetch encrypted data

Server returns the ciphertext, IV, and salt. If burn-after-reading is enabled, the secret is marked as viewed (and deleted on next access).

Browser

Decrypt locally

If password-protected, you enter the password → PBKDF2 derives the outer key → decrypt outer layer. Then the URL key decrypts the inner layer. All in your browser.

Browser

Display plaintext

The decrypted secret is shown. It was never sent unencrypted over the network.


3 Encryption Layers
Outer Layer (optional)
AES-256-GCM with password-derived key (PBKDF2, 600k iterations)
Inner Layer (always)
AES-256-GCM with random URL key
Your Secret
Plaintext - only exists in sender's and recipient's browser
Without a password: one layer of AES-256-GCM (URL key).
With a password: two layers. Even if someone intercepts the URL, they still need the password.

4 What's Stored Where

In the URL Fragment

  • Decryption key (AES-256)
  • Only the link holder has it
  • Never touches the server

On the Server (SQLite)

  • Encrypted ciphertext
  • IV (initialization vector)
  • Salt (if password set)
  • Expiry time & burn flag

Never Stored Anywhere

  • Your plaintext secret
  • Your password
  • Decryption key on server

5 Self-Destruction

Time-Based Expiry

  • Choose: 5 min, 1 hour, 24 hours, or 7 days
  • Server deletes expired secrets automatically
  • Checked on every access + periodic cleanup

Burn After Reading

  • Secret can only be viewed once
  • First view: data returned, marked as read
  • Second view: permanently deleted, returns 404

AES-256-GCM PBKDF2 (600k iterations) Web Crypto API Zero Knowledge Client-Side Encryption Rate Limited

The server is a blind courier. It stores what it cannot read, and delivers what it cannot understand.