5-minute audit

Verify Vaulted's zero-knowledge encryption yourself

Don't trust security tools — verify them. Four browser tests, no installs, five minutes.

The 60-second checklist

Skim this first. The detailed walkthrough below covers each step in depth.

  1. 1

    Open DevTools → Network tab

    In Vaulted, press Cmd+Option+I (macOS) or F12 (Windows/Linux). Switch to the Network tab and filter by Fetch/XHR.

  2. 2

    Create a secret and inspect the POST

    Type a recognisable canary string (e.g. CANARY-12345) and click Create. Find the POST /api/secrets request. Open the Payload tab — you will see only ciphertext (base64) and iv. Search the body for your canary; it is not there.

  3. 3

    Inspect the share link

    Look at the generated link. The decryption key sits after the # (the URL fragment). Per RFC 3986, browsers never send fragments to the server — neither in request lines, headers, nor referrers.

  4. 4

    Confirm decryption runs client-side

    Open the link in a new tab. The GET response from /api/secrets/[id] returns only ciphertext. In the Sources panel, search for crypto.subtle.decrypt — that call runs in your browser, not on our server.

What you're verifying

Vaulted's claim is specific: the server never sees your plaintext or your encryption key. That breaks down into four observable properties:

  1. The plaintext never appears in any network request.
  2. The encryption key never appears in any network request.
  3. The URL fragment (where the key lives) never reaches the server.
  4. The server-side data alone cannot reconstruct the secret.

If all four hold, the architecture is zero-knowledge by construction — no marketing language required.

Test 1Plaintext never crosses the wire

  1. Open vaulted.fyi.
  2. Open DevTools and switch to the Network tab. Filter by Fetch/XHR.
  3. Type a recognisable string into the secret field — something like CANARY-12345.
  4. Click Create.

You'll see a single POST to /api/secrets. Click it and look at the Payload tab.

The body contains a ciphertext field and an iv field. Both are base64url-encoded blobs. Search the payload for CANARY-12345— it isn't there. The plaintext was encrypted in the browser before the request fired.

If you want to be thorough, switch network throttling to “Slow 3G” and repeat. The ciphertext still looks like noise; the plaintext is still nowhere.

Test 2The key never crosses the wire either

After creating the secret, Vaulted shows you a share link. It looks like:

https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
                              ^^^^^^^^^^^^^^^^^^^^
                              encryption key (base64url)

The part after # is the URL fragment. Per RFC 3986, browsers process fragments locally — they never appear in HTTP request lines, headers, or referrers.

To prove this:

  1. Copy the share link.
  2. Open it in a new tab with DevTools' Network tab open.
  3. Find the GET /s/abc123 request.
  4. Look at the Headerstab — full request URL, no fragment.
  5. Look at the Requesttab — no fragment.
  6. Check document.referreron the next page — fragment stripped.

The key the recipient needs to decrypt the secret was in the URL, but it stayed in their browser. The server saw GET /s/abc123 and nothing more.

Test 3The server alone cannot decrypt

Open a terminal and curl the secret directly:

curl https://vaulted.fyi/api/secrets/abc123

You'll get back JSON containing ciphertext and iv. That's the entire server-side state for your secret.

Now try to make sense of it. Without the key from the URL fragment, you can't. AES-256-GCM with a properly generated random key is, for all practical purposes, indistinguishable from random noise. A full database dump from Vaulted's servers would yield exactly this — encrypted blobs and metadata.

This is the literal definition of zero-knowledge: the server stores data it cannot read.

Test 4Encryption happens in JavaScript you can see

Vaulted uses the Web Crypto API, which is a browser primitive — there's no obfuscated WASM or native module hiding the work. You can watch the encryption happen.

  1. In DevTools, open the Sources tab.
  2. Search for crypto.subtle.encrypt (use Cmd+Option+F for global search).
  3. Set a breakpoint on the line that calls crypto.subtle.encrypt.
  4. Trigger a new secret creation.
  5. The breakpoint hits. Inspect the arguments: you'll see your plaintext as a Uint8Array, the AES-GCM key, and a fresh random IV.
  6. Step over the call. The result is the ciphertext that gets posted.

You're now watching plaintext become ciphertext, in your browser, before any network request. There's no other path the data could take.

What this proves

  • No plaintext leaves the browser. Test 1.
  • No key leaves the browser. Test 2.
  • The server cannot decrypt with what it stores. Test 3.
  • The encryption is real and happens client-side. Test 4.

That's the entire zero-knowledge claim, demonstrated against a running production system in five minutes.

What this doesn't prove

Verification is honest work — it's worth being clear about its limits.

  • The tests prove behaviour at this moment. A future code change could break the property. If you need ongoing assurance, subscribe to the changelog and re-run the tests periodically.
  • They don't protect against your device. A keylogger or malicious browser extension can read plaintext after decryption. That's outside any web app's control.
  • They assume the JavaScript you ran is the JavaScript Vaulted serves. A targeted attacker who compromised the CDN could serve a different bundle to a single user. Subresource Integrity and reproducible builds mitigate this; see our threat model for full detail.

Going further

If you want to dig deeper:

The strongest argument for a security tool isn't its marketing. It's that you can take it apart and confirm it does what it says. Vaulted is built so that you can.

Frequently asked questions

Have questions or found something off? Email [email protected] — we publish our responsible disclosure policy and respond within 48 hours.