Interest Article
December 18, 2025
"The web hosts most of the world’s new crypto functionality. A significant portion of that crypto has been implemented in Javascript, and is thus doomed.”
In the coming weeks, we are preparing for the testing release for WEBCAT, our previously announced framework for distributing signed, integrity-verified web applications in the browser. WEBCAT aims to address [the long-standing “chicken-and-egg” problem of verifying web…
Interest Article
December 18, 2025
"The web hosts most of the world’s new crypto functionality. A significant portion of that crypto has been implemented in Javascript, and is thus doomed.”
In the coming weeks, we are preparing for the testing release for WEBCAT, our previously announced framework for distributing signed, integrity-verified web applications in the browser. WEBCAT aims to address the long-standing “chicken-and-egg” problem of verifying web applications that perform, among other things, client-side cryptography. We recently presented its design at Transparency.dev 2025 and gave a demo at Tor’s State of the Onion 2025.
Now that we are participating in broader community efforts to bring some of WEBCAT’s guarantees to new web standards, we frame these efforts in this post by explaining the main challenges in designing an integrity mechanism for the web, focusing on reproducibility and auditability. Some are explained in more detail in this document, though parts of the architecture have since evolved.
What is a web application, anyway?
WEBCAT wants to bring package-manager or app-store-like security to some web applications. But which ones? Not all web applications are suitable for this model. Some mix client and server code by dynamically generating markup such as HTML, or code such as JavaScript, depending on the user session or queries. These applications (imagine your legacy home banking) do not have a clear division between a client and a server: The server itself generates parts of the client upon every execution. Thus, it is impossible to sign and commit strictly to the client’s code. WEBCAT cannot be used in this context.
A well-known historical example is JSONP. At a time when browsers did not allow cross-origin data requests, developers could bypass this restriction by loading data as executable JavaScript. The downside was that if a JSONP endpoint injected any additional code — whether intentionally or via a reflected cross-site scripting vulnerability — that code would be executed directly in the user’s browser. The classic mix of PHP and HTML, or more generally any form of server-side templating, incurs the same limitations.
In contrast, when a clear split between frontend and backend is defined, for instance by using a REST API or a WebSocket for data exchange, then we have what is often referred to as a single-page application. SPAs generally load all their code from an HTML page, and then dynamically load, route, and populate their views using only JavaScript. As a consequence, the whole web application can be pre-generated, and its code is not expected to mutate across requests or during execution. Many modern web applications follow this model, and some of them were compatibility targets while developing WEBCAT, such as Jitsi and Cryptpad.
While WEBCAT aims to be compatible with SPAs, it is not restricted to them. Anything that can be pre-generated (such as websites built using static site generators) can easily be signed and transparency logged. As such, one could have a static website that has signed pages, for instance, for authenticated contact information, warrant canaries, or other security-sensitive content.
Let’s talk bundles and manifests
WebBundles, an earlier proposal from Google, shared a similar high-level requirement: Package a web application and sign it, so that the same bundle could be reloaded either locally or from other origins while preserving integrity guarantees. Although Google’s goal at the time was different, and WebBundles never really gained third-party adoption, their structure remains instructive. They include a special archive of web application assets, with associated metadata, such as all the necessary HTTP headers, including, but not limited to, the Content Security Policy and a content-type for each resource.
For several reasons, we have chosen not to adopt an archive-based approach. First, avoiding archives allows for greater flexibility in progressive loading; only the resources required for a given functionality need to be fetched, rather than downloading a monolithic bundle containing the entire application. Second, this design is more feasible to implement and verify within the constraints of a WebExtension. Finally, we want to preserve zero-effort backward compatibility; by introducing an additional metadata file instead of changing existing resource formats, browsers that do not support WEBCAT can simply ignore it and continue to operate unchanged. Thus, we rely on a single metadata file to describe and verify application resources.
Accordingly, WEBCAT — like related proposals — relies on a metadata file called a manifest (not to be confused with PWA Web Manifests). This manifest describes the web application, its assets, and its execution environment. It is the only object that needs to be signed and transparency-logged, but because it commits to all of the assets that make up the application, they inherit those security properties.
A reproducible runtime is the only auditable runtime
Let’s start by stating the obvious: No mechanism described here can prevent vulnerabilities in web application code, and nothing can stop intentionally malicious code. If a developer signs and distributes a messaging application that sends your private keys to third parties, this cannot be prevented. If someone prepares a browser exploit and signs it, it will still run. If one of the API endpoints contains an SQL injection or a similar vulnerability, it will be exploitable. What can be done, by designing a solid sandbox, is to prevent some classes of bugs and some known bad paradigms.
WEBCAT aims for two very ambitious goals:
- The execution environment must be reproducible.
- Only JavaScript that has been committed to may execute within the same-origin policy** of a WEBCAT application.**
To restate it differently: In a web application verified using a given manifest, no JavaScript that is not present in a manifest should reach execution in the same context. The practical consequence of it is that an auditor (somebody wanting to verify what is being executed and reproduce the application behavior) must be able to access all the corresponding JavaScript.
To show concrete examples, the following code snippets would violate these constraints:
*eval(await (await* *fetch("/someimage.jpg")).text());*
In this case, eval allows the execution of arbitrary JavaScript strings that can either be constructed at runtime or fetched from anywhere, without meaningful restrictions.
*document.write(\