whisper

FAQ

ANONYMOUS & SECURE

▶./faq.sh
[ 01 ]

WHAT IS WHISPER?

Whisper is an anonymous, end-to-end encrypted (E2EE) secret sharing tool. You paste a secret, get a one-time URL, and share it. The recipient opens the URL and sees the plaintext. No accounts, no tracking, no logs.

Secrets expire automatically (5 minutes to 30 days) and can optionally be destroyed after a single read (burn-after-reading). The server never sees the decryption key.

[ 02 ]

HOW DOES IT WORK?

Encryption happens entirely in your browser before anything leaves your device.

  YOU (BROWSER)                    SERVER
  ─────────────                    ──────
  plaintext
      │
      ▼
  AES-256-GCM encrypt ──ciphertext──▶ store(id, ciphertext)
  + random key (32B)
      │
      ▼
  share URL:
  https://host/s/<id>#<key>
                  │      │
                  │      └── fragment: NEVER sent to server
                  └───────── path: server only sees the ID

When the recipient opens the URL:

  RECIPIENT (BROWSER)              SERVER
  ───────────────────              ──────
  visit URL with #fragment
      │
      ├─── GET /api/secrets/<id> ──▶ return ciphertext
      │
      ▼
  AES-256-GCM decrypt
  (key from #fragment,
   never left browser)
      │
      ▼
  plaintext shown
  (optionally burned on server)
[ 03 ]

IS IT SAFE?

Yes, within the threat model. Whisper uses AES-256-GCM — authenticated encryption with a 256-bit key — which is the same algorithm used by banks and governments. The key never touches the server.

What you must trust: the code running in your browser. If the page itself is compromised (e.g. a supply-chain attack on the JS bundle), all bets are off. For the highest assurance, review the source and self-host.

What you do NOT need to trust: the server operator, the network, TLS certificates, or Redis storage — none of these can recover your plaintext.

[ 04 ]

WHAT DOES THE SERVER KNOW?

The server stores only:

  • ciphertext — the encrypted blob (unreadable without the key)
  • iv — the 12-byte initialization vector (public, needed for decryption)
  • expiresAt — expiration timestamp
  • burnAfterReading — boolean flag
  • maxViews — optional view limit

No IP addresses are logged. No content is readable. The decryption key is never transmitted to the server.

[ 05 ]

THE URL FRAGMENT

The shareable URL has a precise structure:

  https://whisper.example.com/s/AbCdEfGh#3mK9xQvZpL...
  ─────────────────────────── ─────────── ────────────
        domain                   path        fragment
     (reaches server)          secret ID    base58
                                (server     AES key
                                 stores    (browser
                              ciphertext)    only)

The #fragment (hash) portion of a URL is a browser-side concept. By HTTP specification, browsers never include the fragment in requests sent to a server. This is what makes Whisper zero-knowledge: the key physically cannot reach the server over normal HTTP.

[ 06 ]

BURN_AFTER_READING

When burn-after-reading is enabled, the server deletes the ciphertext immediately after the first successful retrieval. The secret can only be read once.

Note: "first retrieval" means the first HTTP GET that returns a 200 — not the first successful decryption. A network-level attacker who intercepts the ciphertext before the recipient could burn it. This is an inherent limitation of any server-side burn mechanism.

For extra protection, combine burn-after-reading with password protection.

[ 07 ]

PASSWORD PROTECTION

Optionally add a password when creating a secret. The random URL key (in the fragment) is used as the PBKDF2 salt; the password is the PBKDF2 input. Together they derive the AES-256-GCM encryption key (SHA-256, 600,000 iterations). The plaintext is encrypted directly with this derived key — there is no separate key-wrapping step.

The server never sees the password or derived key. The recipient must enter the correct password in their browser; it is combined with the URL key to rederive the encryption key. Share the password through a separate channel (e.g., a phone call) for maximum security.

Without the password, the URL key alone is insufficient to decrypt the secret.

[ 08 ]

TECHNICAL_SPEC

AlgorithmAES-256-GCM (authenticated encryption, 256-bit key, 12-byte IV)
Key generationcrypto.getRandomValues() via Web Crypto API (CSPRNG)
Key encodingBase58 (Bitcoin alphabet) in URL fragment
Key size32 bytes = 256 bits
IV size12 bytes (96 bits, GCM standard)
Password KDFPBKDF2-SHA-256, 600,000 iterations; URL key (32 bytes) used as salt
Storage backendUpstash Redis via HTTP (server-side)
CLI interopGo CLI uses identical AES-256-GCM + Base58 (cross-compatible for non-password secrets)
RuntimeWeb Crypto API — browsers, Node 20+, Cloudflare Workers, Vercel Edge

The TypeScript crypto library (@whisper/crypto) and Go implementation (internal/crypto) are cross-compatible. Both use the same AES-256-GCM algorithm, key format, and password KDF (PBKDF2-SHA-256, URL key as salt).

[ 09 ]

LIMITATIONS & TRUST MODEL

Things Whisper protects against:

  • ✓ Server operator reading your secrets
  • ✓ Database breaches exposing plaintext
  • ✓ Network interception of the decryption key
  • ✓ Passive surveillance of stored data

Things Whisper does NOT protect against:

  • ✗ A compromised browser or OS on your device
  • ✗ A malicious JavaScript bundle served from the host
  • ✗ Screen recording or shoulder surfing
  • ✗ The recipient sharing the secret after reading it
  • ✗ Timing attacks to determine when a secret was accessed

For maximum trust: review the source code, verify the deployed bundle, and self-host. The architecture is intentionally simple — there is no server-side logic that could leak keys.