Browser-only encrypted envelope

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.

Loading crypto module...
Who this is for

If any of these are you, this is built for you

Not everyone needs browser-only secret sharing. These are the specific cases where everything else is worse.

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

Encrypt a note for yourself

Type a message and a password. Save the vault file. Remember the password. You can decrypt it later on any device.
1
The text you want to lock up. Anything you type. Up to 256 KiB. The text only exists in this browser tab. It is never sent to a server.
2
This phrase is the only thing that opens the box. It is never sent anywhere — it stays in your head. You need the exact same phrase to open the box later.

Pick a phrase, not a word. A 6-character password is crackable in hours on a single GPU. The generator below does better: it picks 4 words at random from the standard EFF long wordlist (7,776 words) and appends 2 random digits, which is about 58 bits of entropy (77764 × 100 ≈ 258.3). An attacker who could test a million Argon2id guesses per second — far more than a single GPU manages against the Strong lock setting — would still average thousands of years. That figure is arithmetic about the generator, not a guarantee about hardware you can't see, and it drops as attack hardware improves. A phrase you invented yourself has no such number: it is only as strong as it is unpredictable.

If you forget the phrase, the box CANNOT be opened. There is no reset, no recovery, no backdoor.
(empty)

What the bar measures: length, character variety, and how many words you typed. It cannot tell whether you picked those words at random, so read it as a floor, not a score. The only phrase here with a known strength is a generated one — about 58 bits.

3
How hard the box is to break open. Stronger locks take longer to lock and unlock but are harder for a thief to break. The math is called Argon2id. The numbers it uses are stored inside the box so you don't need to remember them.
4
"Just for me" means you are saving a private note. "Share with someone" means you will send the box to a friend and tell them the secret phrase a different way (call them, text them). The math is the same; the label tells future-you what kind of box this was.
5
A random 24-byte number we pick for every box. It makes every lock unique even if you use the same secret phrase twice. You don't need to remember it — it's stored inside the box. Crypto people call this a "nonce" (number used once).
(generating...)
6
Another random number that mixes with your secret phrase to make the lock harder to pick. Different from the secret number above. Also stored inside the box. Crypto people call this a "salt".
(generating...)
7
The kind of math we use to lock the box. We use XChaCha20-Poly1305 (encryption) and Argon2id (password mixing). These are the same math used by Signal and other security tools. We don't change them.
XChaCha20-Poly1305 + Argon2id (cannot be changed)
8
The version of the locked box format. Version 2 is what we make today — it uses a random data key that is itself locked by your phrase. Older Version 1 boxes still open on this page. If we ever change the format again, future versions will get a new number.
Vault format V2 — schema BRY-NFET-SX-VAULT-V2 (cannot be changed)

The build that made a box is recorded inside the box itself, in its version field — read it in the box JSON after you lock one, or on the details line when you open one. This page does not print a build number here, because the number would only be a copy that can drift out of step with the code actually running.

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).

Box name (envelope ID): (computing...)

        
Important: If you are sharing this box with someone, send the box file and the secret phrase through different channels. Email the file, text the password. Never send both in the same place.

Decrypt

Open a vault

Drop or paste a vault file, type the password, get the message back.
Drop the .vault.json file or the vault-recovery-...txt recovery sheet here
Step 2 — the password. Type it in the field below. Do not paste it from the same place as the vault. Do not paste it from a chat window where the vault was sent.

Nothing here was ever sent anywhere, but a decrypted message stays on screen until you clear it or close the tab. On a borrowed or shared computer, clear it before you walk away.

Decrypted message


        
Honest Threat Model

What this protects, and what it does not

Every security tool needs honest framing. Here is the one for this one.

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
How It Works

Standard primitives. Standard library. No magic.

If you know cryptography, you can read the source. If you don't, here is the short version.
KDF

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.

AEAD

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.

Library

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.

Verify This Page

Don't trust us. Check the page yourself.

The integrity story is layered, and each layer covers something the others can't: Subresource Integrity binds the scripts to this HTML, and the HTML itself is covered by a separately signed integrity manifest.

Three things to check

  1. SRI — and what it can't tell you. View the source of this page and look at the integrity attribute 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.
  2. Site integrity — this is the one that covers the page. Visit /verify and download the integrity record. vault/index.html, vault/chat.js, and vault/sodium.js are 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".
  3. 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-Policy HTTP response header that includes connect-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 for content-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.