A one-off secret,
sent without a server.
Type a message, type a secret phrase, get an encrypted vault file. Send the file through one channel and the phrase through another. No account. No server. Plaintext never leaves this tab — open DevTools and watch: after this page loads, there are zero network requests. XChaCha20-Poly1305 + Argon2id, running in your browser via libsodium.
If any of these are you, this is built for you
You need to send one secret, once
A credential during offboarding. A recovery phrase to a new employee. A document access token to a contractor for a single job. You do not want to create an account anywhere, invite anyone to a platform, or leave a copy sitting on a vendor's server. You want to hand over one thing and be done.
You want to verify nothing leaks
Signal requires trusting Signal. Email with a passphrase leaks metadata. Cloud password managers put ciphertext on someone else's hardware. Here you can open DevTools, perform an encrypt, perform a decrypt, and see zero network requests. That is not just a promise about our code: this page is served with a Content-Security-Policy HTTP response header that includes connect-src 'self', so the browser refuses any connection to a host other than this one — no third-party fetch, XMLHttpRequest, WebSocket or beacon can leave this page. Same-origin requests are still permitted, and are needed: libsodium loads its WebAssembly module that way, and blocking it stops the cryptography working at all. Check both yourself in DevTools: after load you should see no request to any other host, and this page's own entry shows the policy under Response Headers. What that header cannot do is stop a compromised server from serving you a different page in the first place — for that, see the integrity check below.
You share the receiving end with someone non-technical
They need to open the box on their phone, on a laptop they borrowed, on a machine with no software installed. This page runs in any modern browser. Drop the file, type the phrase, read the message. No install. No signup. No QR scan with a second device.
You want to verify the page itself wasn't tampered with
The JavaScript is SRI-locked inside this HTML, so the browser will not run a script whose bytes don't match the hash written here. That check cannot cover the HTML itself — a server that rewrote this page would rewrite the hashes with it. The HTML is covered separately: its SHA-256 hash is listed in a manifest that is signed with Ed25519, published at /verify. Compare the manifest hash for vault/index.html against the bytes your browser loaded. If they match, the page you just ran is the published, signed version.
Encrypt a note for yourself
Locked! Save the box file and remember your secret phrase.
To open this box later you need two things: the box file (download below) and the secret phrase you typed (only in your head).
Open a vault
Decrypted message
What this protects, and what it does not
What this does protect
- Confidentiality of your message against anyone without the secret phrase
- Integrity of every byte of the vault — flipping any bit breaks decryption with a clear error
- Every byte of metadata (salt, both nonces, KDF parameters, schema, version, mode, created_at, both AEAD algorithms) is bound into the authentication tag
- A random data key encrypts your message; the secret phrase only unwraps that data key. The phrase is never the sole root of secrecy
- The JavaScript on this page is integrity-protected via Subresource Integrity — the browser refuses to execute a script whose bytes don't match the SHA-384 hash written in this HTML. That binds the scripts to this page; it does not prove this page is the published one (see the column on the right)
- Plaintext exists only in this browser tab. Zero network requests after page load, enforced by a
connect-src 'self'Content-Security-Policy header, not just by our code — no third-party host is reachable from this page. Verify both in DevTools. Nothing is cached for offline use — there is no service worker — so the page keeps working while this tab stays open, and reloading it needs the network again
What this does not protect
- Against anyone who has the secret phrase — protect it like you would a house key
- Against weak phrases — Argon2id raises the cost but does not make a one-word phrase strong. The generator gives you 4 random words from a 7,776-word list plus 2 digits, about 58 bits; a phrase you invent yourself has no measurable number attached to it
- Against a compromised host server that ships a malicious page at serve time — SRI cannot catch this, because SRI hashes live in the HTML and a rewritten page carries rewritten hashes. The
connect-src 'self'header cannot catch it either, since a substituted page would come with its own headers. Only the separately signed integrity manifest covers the HTML. This is the real weakest link; if it matters, verify the signed integrity record on /verify - Against someone who walks up to the screen after you decrypt — the message stays visible in this tab until you press Clear or close it
- Against endpoint compromise — keyloggers, malware, screen recorders, shoulder-surfers
- Against forgetting the phrase — if you lose it, the vault is permanently unrecoverable. There is no backdoor, no reset, no recovery. There is also no recovery keyslot (today)
- This is not end-to-end messaging like Signal — there is no key exchange, no forward secrecy, no identity verification. It is a vault you share the phrase to
Standard primitives. Standard library. No magic.
Argon2id
Your phrase is stretched by Argon2id. Strong lock (the default) uses opslimit 4 and memlimit 96 MiB — about 4 seconds per guess on modern hardware, so offline brute-force is expensive. The 16-byte salt is generated fresh per vault and stored alongside the ciphertext. Quick lock and Vault lock presets are also available.
XChaCha20-Poly1305
A fresh random 32-byte vault key encrypts the message with XChaCha20-Poly1305 (24-byte content nonce, 16-byte Poly1305 tag). The vault key is then wrapped a second time with XChaCha20-Poly1305 under the Argon2id output (24-byte wrap nonce, 16-byte tag). Tampering with any byte of either ciphertext, either nonce, the salt, or the metadata breaks at least one of the two authentication tags on decrypt.
libsodium.js
All cryptographic operations are delegated to libsodium.js, the same audited library used by Signal and many other security products. The page loads it once, runs a self-test, and refuses to enable encryption if the self-test fails.
Don't trust us. Check the page yourself.
Three things to check
- SRI — and what it can't tell you. View the source of this page and look at the
integrityattribute on the three<script>tags:/vault/sodium.js,/vault/chat.js, and/site-verify.js. The browser refuses to execute any script whose bytes don't match its SHA-384 hash, so if the page rendered and the form is enabled, all three scripts match the hashes written in this HTML. That is the whole of what SRI proves. It binds the scripts to this HTML file and nothing more — it cannot detect a rewritten HTML page, because a server that rewrote the page would rewrite the hashes in the same edit. Step 2 is the layer that covers the HTML. - Site integrity — this is the one that covers the page. Visit /verify and download the integrity record.
vault/index.html,vault/chat.js, andvault/sodium.jsare listed there with their SHA-256 hashes, and the record itself is signed with Ed25519. Hash the bytes your browser actually loaded and compare. Cross-checking here is what turns "the scripts match this page" into "this page is the published one". - Network silence. Open DevTools, switch to the Network tab, perform an encrypt and a decrypt, and confirm there are no requests after the initial page load. That silence is enforced, not merely observed: this page is served with a
Content-Security-PolicyHTTP response header that includesconnect-src 'self', so the browser blocks any call to a third-party host. Same-origin requests are still allowed, which is what lets libsodium load its WebAssembly. Read it for yourself in the same tab — click this page's own entry in the Network list, open Headers, and look under Response Headers forcontent-security-policy. If that header is not there, nothing is being enforced, so check rather than take our word for it. And note the limit: the policy stops this page from calling out; it cannot stop a compromised server from serving you a different page with different headers. That is what step 2 is for.
One phrase. One vault. No accounts. No servers.
Encryption that fits on one page. Verifiable. Deletable. Yours.