Sendant
Security · the hard question first

Can you trust encryption that runs in a browser?

Updated 11 July 2026

Every browser-based encrypted messenger gets asked this, and most dodge it. Here is the objection at full strength, what Sendant actually does about each part of it, and the honest answer about where the browser's limits are.

Short answer: browser-based end-to-end encryption is genuinely weaker than an installed app in one specific way — the code is re-fetched from a server every visit, so you re-trust the operator every visit. Sendant doesn't pretend otherwise. We treat the browser as the low-friction tier (start chatting in seconds, nothing installed) and the installed Android app as the high-assurance tier (a signed binary that doesn't change under you). Same cryptography in both; different delivery trust.

The case against browser crypto, steelmanned

The classic argument — argued for years on Hacker News and in the "JavaScript cryptography considered harmful" literature — is not a strawman, and we'd rather state it better than our critics do:

1. The server serves the code

An installed app is a fixed, signed artifact. A web app is re-delivered on every page load. If the operator turns malicious, is compromised, or is legally compelled, it can ship a modified script to one specific user, capture their keys or plaintext for that session, and leave no trace on the client. End-to-end encryption where one end's code comes from the adversary's server collapses to trust-the-server.

2. The browser is a hostile runtime

Any cross-site-scripting flaw means attacker code runs inside the security boundary, next to your keys. The DOM, extensions, and third-party scripts are a much larger attack surface than a native process.

3. Extensions see everything

A browser extension with page access can read what you type before it is ever encrypted. No web app can defend against a runtime the user has already extended with untrusted code.

All three are real. Any browser messenger that claims to have "solved" them is overclaiming. What follows is what we do about each — and what we explicitly do not claim.

What Sendant does about it

One crypto implementation, not twoShipped

Sendant's cryptographic core — X3DH key agreement and the Double Ratchet, the same primitives Signal uses — is written once, in Go. The Android app ships it as a compiled native library; the browser runs the same code compiled to WebAssembly. There is no second, hand-written JavaScript crypto implementation to drift, diverge, or quietly weaken. The classic "JavaScript crypto is amateur crypto" objection targets hand-rolled JS primitives; Sendant's browser client contains none.

A strict Content-Security-Policy, live todayShipped

The web app at app.sendant.io serves this policy on every response — you can verify it right now with curl -sI https://app.sendant.io:

content-security-policy: default-src 'self'; script-src 'self' 'wasm-unsafe-eval';
  style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';
  connect-src 'self' https://mbx.sendant.io; worker-src 'self';
  object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'

In plain terms: the page can only run scripts from its own origin — no inline script, no third-party scripts, no CDNs, no analytics, no tag managers. It can only talk to itself and the mailbox endpoint. It cannot be framed (no clickjacking), cannot load plugins, cannot submit forms anywhere. Alongside it: HSTS with a one-year preload policy, nosniff, and a no-referrer policy. This is the front-line defense against injected or smuggled script — attack #2 above.

Keys wrapped with a non-extractable keyShipped

Your identity key and session state are not stored as readable plaintext in the browser. They are encrypted at rest with an AES-256-GCM key generated non-extractable inside the browser's WebCrypto engine and held in IndexedDB — a key that, by browser design, can never be exported, even by code running on the same page. Honest scope: this protects against a stolen storage blob (a copied profile directory, a leaked backup, storage-reading malware). It does not protect against malicious code already executing in the page — for that, the CSP above is the defense, and the residual risk is attack #1, which no web app can fully close.

Key-substitution is detectableShipped

Every conversation has a safety number — a SHA-256-based commitment to both parties' full identity key bundles, shown as twelve 5-digit groups in both the app and the web client. If any server ever substituted keys to insert itself into a conversation, the numbers stop matching. Compare them over any channel the server doesn't control (in person, on a call) and you have verified the encryption end to end, past every piece of our infrastructure.

What we deliberately don't do

The two-tier trust model

This is the part most browser messengers won't say out loud. The browser and the app run the same cryptography, but they are not the same trust level, and picking between them should be a threat-model decision:

Browser — the low-friction tier

For starting in seconds: no phone number, no email, no account, no install — on a work laptop, a library PC, an iPhone. Strong against network snooping, server data breaches, and mass surveillance of message content: the mailbox only ever holds ciphertext.

Trust assumption you accept: the code is re-served each visit, so a compromised or compelled operator could target a future session.

Installed app — the high-assurance tier

The Android app is a signed binary with the crypto core compiled in. It does not re-download its code from us each time it runs. The direct download page publishes the APK's SHA-256 and the signing certificate fingerprint, so you can verify what you installed — with or without Google Play in the loop.

Who should use it: anyone whose threat model includes the operator itself being compromised or compelled. That's not a marketing dodge; it's the actual answer.

If the stakes are life-and-safety: use the installed app, verify the safety number with your contact out-of-band, and treat any browser session as the convenience tier. We would rather lose a user to our own Android app than overstate what a browser can promise.

Known limitations, in writing

Roadmap: shrinking the trust gapPlanned

Each of these moves an item from "trust us" to "check us" — without publishing source. The order of operations is deliberate: make the served build checkable against a signed manifest, get the CSP as tight as it goes, then have someone qualified try to break it.

Corrections welcome. This page is meant to survive expert scrutiny. If any claim on it is wrong or has drifted from what ships, email privacy@sendant.io with "SECURITY" in the subject — we'll fix the page or the product, whichever is at fault.

Judge it yourself

Open the web app and inspect the headers, the storage, and the network tab — or skip straight to the high-assurance tier.

Open web app Get the verifiable APK Security overview