Skip to content
Security6 min read

The web client problem: checking the code that decrypts

A browser vault runs code the server sends on every visit. What a tampered page could see, which checks Crunu builds in, and what you can verify today.

Crunu teamPublished 6 min read

Example. Kiteline is a fictional company.
On this page

Any vault that decrypts in the browser has the same weak point: the code that does the decrypting is sent by the server, fresh, every time you open the page. If that server is compromised, it can send a page that looks the same and quietly keeps what you type. In Crunu, such a page would see your password and Secret Key when you sign in, which is why the product names a tampered web client as its main remaining risk. The defenses are a strict content security policy with integrity checksums, signed releases, a reproducible build, and a single-file client, crunu.html, that runs from your disk and checks the site against the signed release. Some of these you can check yourself today. The ones that make the check independent of the server, a downloadable crunu.html and a release key published through a separate channel, aren't published yet.

This post explains the problem, what each defense covers, and exactly where things stand. The guide on who can decrypt your secrets puts this risk next to the others.

Why the web is different

A desktop or mobile app is installed once. Its updates arrive through a store or an updater, and they're signed by a key that isn't on the server you talk to every day. A web app has no install step. Meta's engineering team put it plainly when they built a verifier for WhatsApp Web: "a web app is usually served directly to users, without a third party reviewing and auditing the code" (Engineering at Meta: Code Verify).

For a vault, that matters more than for most sites. End-to-end encryption moves trust from the server to the code on your device. In a browser, the code on your device came from the server a second ago. A server that can't read your secrets in storage could, if compromised, send code that reads them on arrival.

What a tampered page could and couldn't do

Crunu's server stores ciphertext, public keys, signed documents and metadata such as names and email addresses, and nothing it holds opens a value. Your account key is derived on your device from your password and your Secret Key, and neither is sent over the network.

A modified page changes the "on your device" part. Running in your browser, it would see your password and Secret Key as you type them, and anything you decrypt while it's open. It could send them somewhere.

What it couldn't do, even then:

  • Sign as someone it didn't capture. Access rules and versions are signed by the people who make them. A page that captured one person's keys could act as that person, but not as anyone else, and its changes would be signed and recorded under that person's name.
  • Reach the agent's machines directly. Agents on servers and CI check every signature they receive and refuse anything that doesn't verify. They don't run code from the web page.
  • Undo what other devices have seen. Clients remember the newest state they've seen and refuse older ones.

That doesn't make the risk small. Capturing an admin's keys is close to the worst case. It does mean the risk sits in one place, the page you sign in on, and the defenses can concentrate there.

Layer 1: the browser refuses code from anywhere else

Crunu's pages send a content security policy that allows scripts only from the page's own origin. You can see it yourself:

Shell
curl -sI https://app.crunu.com | grep -i content-security-policy

On October 30, 2026, that printed a policy starting default-src 'none'; script-src 'self', which blocks third-party and inline scripts. The page also loads its own code with integrity checksums. Subresource Integrity, in MDN's words, lets "browsers verify that resources they fetch … are delivered without unexpected manipulation": if a file doesn't match its hash, the browser refuses to load it (MDN: Subresource Integrity).

What this stops: injected scripts from a third party, a tampered file on a cache or CDN, and an attacker who can change one file but not the page that names it. What it doesn't stop: a server that changes the page itself. The policy and the checksums are in the response from that server, so whoever controls it controls them too.

Layer 2: a signed list of what the release contains

Each web release comes with a manifest, release.json, listing every file of the site and of crunu.html with its SHA-256, and a signature, release.sig, made with the release signing key. That key isn't on any server; deploy scripts carry the signed files, not the key.

You can check that the cloud serves files that match its manifest:

Shell
curl -s https://app.crunu.com/release.json | grep -o '"name":"app.js","sha256":"[0-9a-f]*"'
curl -s https://app.crunu.com/app.js | shasum -a 256

On October 30, 2026, the two hashes matched. Be precise about what that proves: the server's files agree with the server's manifest. A compromised server could change both. The check that matters is whether the manifest's signature verifies against the real release key, and that key must come from somewhere other than the server. Today the page carries the key in a <meta> tag, which the same server sends. The release key isn't yet published through a separate channel, so an outside user can't yet make this check independent of the server.

Example. Kiteline is a fictional company.

Layer 3: a build anyone can repeat

The web build is reproducible: the same commit produces the same bytes. That means a release can be checked without trusting the signer at all, by rebuilding it from source and comparing hashes with the manifest. Reproducible builds turn "trust the key" into "trust the source code you read". The glossary entries on reproducible builds and code signing explain both ideas.

The honest status: the source code isn't public, so today only people with access to it can do this.

Layer 4: a client that comes from your disk

crunu.html is the same web client as a single file. Opened from your disk, its code doesn't come from the server at all; it talks to the server only for data. On start, it fetches the site's release.json and release.sig, verifies the signature with the key built into the file, and compares the site's files with itself. The product recommends that administrators work from this file, because their keys are the most valuable.

This is the layer that changes the trust model. With the file, a compromised server can't replace the code you run; it can only refuse to serve you or serve data your client will reject.

Where it stands today: the file is built and signed with every release, and it's listed in the cloud's manifest. A public download of crunu.html, with its release key published separately, isn't available yet. Until it is, everyone, administrators included, uses the web app as served.

Things that aren't about the server

Two risks sit next to this one and no web defense helps with them:

  • Browser extensions with access to all sites can read any page, including a vault's. Use a browser profile without extensions for vault work.
  • Malware on the device. If the computer where you decrypt is compromised, a keylogger or a root process sees what you see. Crunu's limitations say so directly: there's no protection against malicious code on a device where keys are decrypted.

What you can do now

  • Check the headers and the manifest with the commands above, and notice if they ever change in ways you didn't expect.
  • Use the command line for routine work. crunu is a binary installed once; crunu self-update checks the release manifest's signature with a key built into the binary and the SHA-256 of the file. Day-to-day use doesn't depend on code the server sends.
  • Keep admin sessions short and on a clean browser profile. Fewer people with owner or admin roles means fewer keys worth capturing.
  • Watch the audit log and alerts. A sign-in from a new device raises an alert, and every change is signed by its author.

The security limitations page lists this risk with the others, and what "the server can't read it" actually means covers what the server can and can't do to stored data.

Sources