Don't trust us.
Verify.

AshNote is designed so that trusting us is unnecessary: our servers physically cannot read what you share. Here is exactly how, and where the limits are.

The anatomy of a drop link

  • #key: the AES-256 key, generated in your browser. URL fragments (everything after #) are never sent to servers by any browser. The key exists only in the link itself.
  • token: an access token our server stores only as a SHA-256 hash. Even a full database leak grants access to nothing.
  • id: a random UUID locating the encrypted blob.

The life of a secret

Creating a drop

  1. Your browser generates a random 32-byte key and a 16-byte access token via WebCrypto.
  2. The AES-256-GCM key is derived from the raw key with HKDF-SHA256. An optional passphrase is mixed into that derivation: it changes the key itself and never leaves your browser.
  3. The secret (text or file, including its name) is wrapped in an envelope and encrypted with a fresh random IV.
  4. Only the ciphertext, the IV, the SHA-256 hash of the token and the expiry/read settings are uploaded.
  5. The link is assembled locally: id and token go into the path, the key goes into the #fragment.

Opening a drop

  1. The reader's browser presents the token; the server compares its hash, hands out the ciphertext and decrements the read counter. The final read deletes the drop in the same transaction.
  2. The key from the fragment (plus the passphrase, if one was set) goes through the same derivation, and the envelope is decrypted locally.
  3. A wrong key or passphrase never produces garbage output: GCM is authenticated encryption, so decryption simply fails.

What our servers see

  • Ciphertext (AES-256-GCM, encrypted client-side via the browser's WebCrypto API)
  • An initialization vector and a token hash
  • An expiry time and a read counter

That's all. No plaintext, no keys, no filenames. File metadata lives inside the encrypted envelope. Optional passphrases are mixed into the key via HKDF in your browser; a wrong passphrase simply fails cryptographic authentication.

The app also makes no third-party requests: while you encrypt, your browser talks to ashnote.io and nothing else. Open the network tab and watch.

Deletion is real deletion

The final read hard-deletes the row in the same database transaction that serves it: atomically, so two simultaneous readers can never both claim a burn-after-read drop. Expired drops are purged every 60 seconds. A burned link returns the same response as a link that never existed, so nobody can probe which drops ever were.

Preview bots can't burn your drops

Slack, Teams, Outlook and security scanners fetch links to render previews. AshNote reveals a secret only on an explicit reader action (a POST request): automated GET fetches never consume a read.

Honest limits

  • Whoever holds the link holds the secret. Treat drop links like the secret itself, just with an expiry date and a read receipt.
  • Secrets sent through our Slack command transit our server once (Slack offers no client-side encryption path) and are encrypted immediately, never stored or logged. The web app and the Teams dialog are fully zero-knowledge.
  • The recipient's device is beyond any sharing tool's control.
  • The trust anchor is the JavaScript we serve: a compromised server could ship code that leaks keys. Zero-knowledge protects against passive breaches (database leaks, curious operators, requests for stored data), not against actively hostile code delivery. That is true of every end-to-end encrypted web app; our open-source client code is the counterweight.
  • URL fragments never reach servers, but they do land in browser history on both ends. Burn-after-read and an optional passphrase (shared over a second channel) are the countermeasures.

The client-side crypto is open source on GitHub, both the web app and the CLI: read it, audit it, run it yourself. The full wire protocol is documented in PROTOCOL.md; the CLI installs with brew tap arrowhacks-org/ashnote && brew install ashnote.