Threat model
What we are defending, who we are defending it from, and what we do not claim.
This is the long version of the security page. It is written so that somebody deciding whether to trust us can check the claims rather than take them, and so that anybody who finds a gap in it knows exactly what we thought we had covered.
What is being protected
One thing: the content of a secret, from the moment it is typed until the moment it is destroyed. Everything else - who sent it, when, how large it was, how many times it was opened - is metadata we hold and can be compelled to produce. If that metadata is itself sensitive to you, this is not the right tool for that part of the problem.
Who we defend against
Somebody who obtains our database or our backups. Somebody who reads our logs. An employee of ours acting alone. A network attacker between the sender and us. A search engine or link-preview bot that follows the link. A recipient's colleague who later finds the link in a chat history.
Who we do not defend against
Somebody who has the link, because the link is the secret. Somebody who has already compromised the sender's or the recipient's device or browser. A recipient who copies the content elsewhere after opening it. An attacker who serves the recipient a modified page and can also defeat the reproducible build and integrity checks below - which is why those checks are published rather than described.
Where the trust boundary is
In the browser, at the point where plaintext becomes an envelope. On the outside of that line: the key, the plaintext, the filename, the passphrase. On the inside: ciphertext, a size, an expiry, a view counter, and the SHA-256 of a passphrase's server half. There is no configuration, flag or support action that moves the line.
The two exceptions, stated plainly
curl mode encrypts on our server, because a terminal cannot run our code; every response and every reveal page says so in the same words. Request links use a keypair generated in the requester's browser, so we hold a public key and a wrapped key we cannot unwrap. Both are labelled everywhere they appear, and the CLI exists so neither is necessary.
What an attacker actually gets
With the database: ciphertext and timings. With the backups: the same, and the restore procedure destroys anything already in a terminal state before traffic is served. With the logs: no bodies on secret paths, an allowlisted set of headers, and a test that fails the build on a leak. With one view of a link: the secret, once, after which it is gone for everybody including the sender.
Residual risks we accept
A malicious or coerced build of the client is the largest one; reproducible builds and published hashes narrow it rather than close it. Timing and size metadata leak something about content. A passphrase chosen badly is a passphrase brute-forced offline by whoever holds the link, which is why the derivation runs 600,000 iterations and the key half never reaches us.
How to check us
The client, the envelope and the CLI are open source. The build is reproducible: the same commit produces the same bytes, and a hash manifest of every asset ships with each release. Scripts in the repository compare that manifest with what this site actually serves. The envelope has published test vectors, so an independent implementation can prove it matches before trusting it.
When this document changes
Any change to what the server can see is an architecture decision record before it is a pull request, and it appears here in the same release. A security page that only ever gets more reassuring is a security page nobody should believe.
Verify a deployment
Build the client from the tagged commit, produce the manifest, and compare it with this origin:
pnpm integrity:manifest && pnpm integrity:verify https://secretpaste.com