Skip to content
04Source available

ephemera

End to end encrypted secrets that self-destruct.

The ephemera landing page, white on black, with the product name over four panels covering zero-knowledge encryption, burning after reading, retention, and self-hosting.
An overview of encryption, expiry, view limits, and self-hosting.
Role
Design and build
Status
Source available
Stack
  • Next.js 16
  • tRPC 11
  • Drizzle
  • PostgreSQL
  • Zod 4
  • WebCrypto (AES-256-GCM, PBKDF2)
  • Vitest against PGlite
  • Playwright

Where the key lives

In link mode a random 256-bit key is generated in the browser and carried in the URL fragment. Browsers never send a fragment over the network, so the server sees a token and ciphertext. In passphrase mode the key comes from PBKDF2-SHA256 at 600,000 iterations and only the salt is stored.

The reveal gate

Opening a link does not consume a view. Link previews, mail scanners, and prefetching bots reach a gate page; the recipient must choose to reveal the secret. A wrong passphrase does not cost another view, because decryption is retried locally against the fetched ciphertext.

What it does not protect against

Encryption protects the stored content from someone reading the database. A malicious server operator could still deliver JavaScript that captures a secret or its key. The source is available to inspect, and the project recommends self-hosting.

Screenshots

  • The ephemera dashboard. Counters for active drops and views sit above a composer with text, link, and file tabs, an expiry control, a view count, and a list of existing drops.
    Expiry and view count are chosen before the browser encrypts anything.
  • A gate page reading A secret awaits, with a note that revealing will use one of three remaining views, and a Reveal secret button.
    The reveal gate keeps link previews from consuming a view.
  • A revealed secret rendered as markdown, an incident runbook with two numbered steps and a shell command, above a line reading decrypted locally in your browser.
    The recipient decrypts the secret locally before its Markdown is rendered.