Public Beta Pro is included for every account during the public beta — 5 GB. No payment details yet.
Deeperguard Logo Deeperguard Zero-Knowledge Notes
Independent check

Verify zero-knowledge

You do not have to trust our marketing copy. During sign-in and sync you can watch the real HTTP requests in your browser and confirm that passwords, vault keys, and note plaintext never appear on the wire.

1. Open DevTools Network

Chrome / Edge / Brave

  1. Open Sign in or the app in a normal (non-incognito) window if you already have an account.
  2. Press Ctrl+Shift+I (Windows/Linux) or ⌘+Option+I (macOS), or use the menu → More tools → Developer tools.
  3. Open the Network tab. Enable Preserve log so navigations do not clear the list.
  4. Filter by Fetch/XHR (or type api in the filter box).

Firefox

  1. Press Ctrl+Shift+E (Windows/Linux) or ⌘+Option+E (macOS), or menu → More tools → Web Developer Tools → Network.
  2. Enable Persist Logs. Filter to XHR / Fetch.

2. What to look for during login (SRP-6a)

Account sign-in uses Thinbus-compatible SRP-6a. The password is stretched in the browser (SHA-256 for legacy accounts, Argon2id for v2) and never posted as a field named password on the SRP routes.

  1. Reload /login, open Network, then sign in.
  2. Find POST /api/auth/srp/challenge. Request JSON is only {"email":"…"}. Response includes srp_salt, server public value B, and srp_kdf_version — not your password.
  3. Find POST /api/auth/srp/verify. Request JSON contains email, client public value A, and proof M1. Response includes server proof M2 plus vault metadata such as kdf_salt / vault_kdf_version for local unlock. There is still no password and no AES vault key.

Related proofs use the same shape: /api/auth/repair-login/challenge + /api/auth/repair-login (A/M1 only), and empty-vault unlock via /api/account/unlock/challenge + /api/account/unlock.

Redacted — POST /api/auth/srp/verify request
{
  "email": "[email protected]",
  "A": "a1b2c3…(hex public value)…",
  "M1": "d4e5f6…(hex proof)…"
}
Redacted — POST /api/auth/srp/verify response (shape)
{
  "M2": "…",
  "kdf_salt": "…",
  "vault_kdf_version": 2,
  "csrf": "…"
}

3. What to look for during sync

After unlock, create or edit a note and wait for sync (or pull to refresh). Filter Network for sync.

  • POST /api/sync/pull — body has cursors such as since, optional known_hashes, include_blobs, limit. Each returned item is ciphertext plus plaintext metadata only (see below).
  • POST /api/sync/items — push body is {"items":[…],"partial":1}. Each item carries item_uuid, ciphertext, optional blob_ciphertext, content_hash, deleted, updated_at, content_version, and often base_rev.
  • POST /api/sync/blobs / POST /api/sync/refetch — same ciphertext envelopes, still no note text.
  • GET /api/sync/item/<uuid> — the in-app “View server copy” action; returns one owned row exactly as stored.

The encrypted envelope is a JSON string (or object) with AES-GCM fields v, iv (base64 nonce), and data (base64 ciphertext). That is what leaves the device — not the note title or body.

Redacted — one pushed item (POST /api/sync/items)
{
  "item_uuid": "9f3c…-…",
  "content_version": 1,
  "ciphertext": "{\"v\":1,\"iv\":\"<base64>\",\"data\":\"<base64>\"}",
  "blob_ciphertext": "",
  "content_hash": "<sha-256 of ciphertext string>",
  "deleted": false,
  "updated_at": 1760000000.123,
  "base_rev": 3
}

Plaintext metadata the server can see without your vault password: item_uuid, content_version, content_hash, deleted, updated_at, synced_at, rev, and byte sizes of ciphertext fields. Account fields such as email live on the user row, not inside note ciphertext.

In the unlocked app, open any note → Note info → View server copy (also on files, 2FA entries, folders, and tags) to see the raw server JSON beside the decrypted local plaintext.

4. Optional: mitmproxy (and why Wireshark is not enough)

To inspect HTTPS as cleartext HTTP, point the browser at a local TLS-intercepting proxy such as mitmproxy, install its CA in the OS/browser trust store, and filter for deeperguard.com. You should still see only SRP proofs and ciphertext envelopes.

Wireshark on the wire without TLS keys shows only TLS records — encrypted blobs. That proves transport encryption, not that the application payload is ciphertext. Use DevTools or mitmproxy for application JSON.

What these tests can’t prove

  • Server-side retention of old copies — your browser only sees what the API returns now. Historical backups or DB snapshots are outside this check.
  • Metadata — email, IP address, request timing, ciphertext sizes, and item IDs are visible to the server and to anyone who can read logs or the database. Zero-knowledge covers vault contents, not traffic analysis.
  • Integrity of the delivered client code — if a compromised origin shipped a malicious app.js, it could exfiltrate secrets before encryption. Checking the code you run is a separate step.

How to check the code you’re running

  1. In the unlocked app open Settings → About. Note the full client code hash (also in the page’s notes-client-hash meta tag and in GET /api/health as client_hash).
  2. Open the matching GitHub Release v0.61.0 (same link as Settings → About). Download client-manifest.json and client-manifest.json.sha256. A Sigstore signature bundle is published once release signing is active; until then verify by hash and by rebuilding from source (below).
  3. Confirm the in-app hash equals manifest_sha256 inside the release manifest (and the .sha256 sidecar).
  4. Rebuild from source at the release tag and compare hashes:
    reproducible build
    git checkout v0.61.0
    export SOURCE_DATE_EPOCH=0
    python3 scripts/build-client-manifest.py --print-hash
    # must equal the release manifest_sha256
  5. When a release is Sigstore-signed (the release assets include client-manifest.sigstore.json), also verify with Cosign (keyless OIDC, logged in Rekor). Skip this step for unsigned releases such as the first v0.60.0 cut before the release workflow was installed:
    cosign verify-blob (signed releases)
    cosign verify-blob \
      --bundle client-manifest.sigstore.json \
      --certificate-identity-regexp 'https://github.com/deeperguard/Deeperguard/' \
      --certificate-oidc-issuer https://token.actions.githubusercontent.com \
      client-manifest.json

Every script and stylesheet tag on the origin carries a Subresource Integrity digest. A pinned build (extension or desktop app) remains the strongest option if one is published later.

Our claim fails if…

  • A login or sync request body contains your vault password, account password, or raw AES key material.
  • A sync item’s ciphertext / blob_ciphertext is readable plaintext (title, body, TOTP secret, file bytes) instead of a v/iv/data envelope.
  • Note contents appear in server access logs or error responses.
  • “View server copy” shows decrypted fields in the server JSON column (those belong only in the local plaintext column after unlock).

If you find any of the above, please email [email protected] with subject “Vulnerability report”.