Why Browsers Lag Behind on Trust
The web is the most powerful application platform in existence. As long as you have the right API, you can safely run anything you want in a browser. Well… anything but cryptography.
It is as true today as it was in 2011 that Javascript cryptography is Considered Harmful. The main problem is code distribution. Consider an end-to-end-encrypted messaging web application. The application generates cryptographic keys in the client’s browser that lets users view and send end-to-end encrypted messages to each other. If the application is compromised, what would stop the malicious actor from simply modifying their Javascript to exfiltrate messages?
It is interesting to note that smartphone apps don’t have this issue. This is because app stores do a lot of heavy lifting to provide security for the app ecosystem. Specifically, they provide integrity, ensuring that apps being delivered are not tampered with, consistency, ensuring all users get the same app, and transparency, ensuring that the record of versions of an app is truthful and publicly visible.
It would be nice if we could get these properties for our end-to-end encrypted web application, and the web as a whole, without requiring a single central authority like an app store. Further, such a system would benefit all in-browser uses of cryptography, not just end-to-end-encrypted apps. For example, many web-based confidential LLMs, cryptocurrency wallets, and voting systems use in-browser Javascript cryptography for the last step of their verification chains.
Here we provide an early look at such a system, called Web Application Integrity, Consistency, and Transparency (WAICT), a W3C-backed effort among browser vendors, cloud providers, and encrypted communication developers to bring stronger security guarantees to the entire web. We will discuss the problem we need to solve, and build up to a solution resembling the current transparency specification draft.
Defining the Web Application
In order to talk about security guarantees of a web application, it is first necessary to define precisely what the application is. A smartphone application is essentially just a zip file. But a website is made up of interlinked assets, including HTML, Javascript, WASM, and CSS, that can each be locally or externally hosted. Further, if any asset changes, it could drastically change the functioning of the application. A coherent definition of an application thus requires the application to commit to precisely the assets it loads. This is done using integrity features, which we describe now.
Subresource Integrity
An important building block for defining a single coherent application is subresource integrity (SRI). SRI is a feature built into most browsers that permits a website to specify the cryptographic hash of external resources, e.g.,
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.7/underscore-min.js" integrity="sha512-dvWGkLATSdw5qWb2qozZBRKJ80Omy2YN/aF3wTUVC5+D1eqbA+TjWpPpoj8vorK5xGLMa2ZqIeWCpDZP/+pQGQ=="></script>
This causes the browser to fetch underscore.js from cdnjs.cloudflare.com and verify that its SHA-512 hash matches the given hash in the tag. If they match, the script is loaded. If not, an error is thrown and nothing is executed.
If every external script, stylesheet, etc. on a page comes with an SRI integrity attribute, then the whole page is defined by just its HTML. This is close to what we want, but a web application can consist of many pages, and there is no way for a page to enforce the hash of the pages it links to.
Integrity Manifest
We would like to have a way of enforcing integrity on an entire site, i.e., every asset under a domain. For this, WAICT defines an integrity manifest, a configuration file that websites can provide to clients. One important item in the manifest is the asset hashes dictionary, mapping a hash belonging to an asset that the browser might load from that domain, to the path of that asset. Assets that may occur at any path, e.g., an error page, map to the empty string:
"hashes": {
"81db308d0df59b74d4a9bd25c546f25ec0fdb15a8d6d530c07a89344ae8eeb02": "/assets/js/main.js",
"fbd1d07879e672fd4557a2fa1bb2e435d88eac072f8903020a18672d5eddfb7c": "/index.html",
"5e737a67c38189a01f73040b06b4a0393b7ea71c86cf73744914bbb0cf0062eb": "/vendored/main.css",
"684ad58287ff2d085927cb1544c7d685ace897b6b25d33e46d2ec46a355b1f0e": "",
"f802517f1b2406e308599ca6f4c02d2ae28bb53ff2a5dbcddb538391cb6ad56a": ""
}
The other main component of the manifest is the integrity policy, which tells the browser which data types are being enforced and how strictly. For example, the policy in the manifest below will:
- Reject any script before running it, if it’s missing an SRI tag and doesn’t appear in the hashes
- Reject any WASM possibly after running it, if it’s missing an SRI tag and doesn’t appear in hashes
"integrity-policy": "blocked-destinations=(script), checked-destinations=(wasm)"
Put together, these make up the integrity manifest:
"manifest": {
"version": 1,
"integrity-policy": ...,
"hashes": ...,
}
Thus, when both SRI and integrity manifests are used, the entire site and its interpretation by the browser is uniquely determined by the hash of the integrity manifest. This is exactly what we wanted. We have distilled the problem of endowing authenticity, consistent distribution, etc. to a web application to one of endowing the same properties to a single hash.
Designing for transparency without breaking the web
A transparent web application keeps its code in a publicly accessible, append-only log. That gives users two forms of protection: if they are served malicious code and later find out, they can prove to third parties exactly what ran; and if they never find out, an independent auditor can still scan historical logs and catch the attack. Transparency doesn't prevent malicious code from being served—it makes the serving of it publicly auditable.
With a single hash committing to a website’s full contents, the next step is getting that hash into a public log. That requires a system meeting a demanding set of constraints:
- No breakage. Participation must be strictly opt-in and must not interfere with how existing sites work.
- No added round trips. Transparency must not introduce extra client-server network latency.
- Privacy. Users must not identify themselves to any party they wouldn’t already identify themselves to—no new third-party connections, no identifying data sent to the site.
- Statelessness. Users must not store per-site cryptographic data.
- No centralization. No single point of failure or trust; the system must keep making progress if any party goes down, and remain secure if any single party is distrusted.
- Low barrier to entry. Site operators should be able to enroll cheaply, without deep expertise.
- Easy opt-out. Sites must be able to leave, even if all cryptographic material is lost (e.g., domain seizure or sale), avoiding the lock-in that killed HPKP.
- Transparent opt-out. Since transparency is optional, an attacker could disable it, serve malicious content, and re-enable it. Disabling must itself be logged so this attack is detectable.
- Monitorability. Operators must be able to watch their site’s transparency data efficiently, without running an always-on, high-load monitor.
Hash chains as the building block
At the core of nearly every transparency mechanism is an append-only log that can produce two kinds of proofs: an inclusion proof that an element appears at a given index, and a consistency proof that one log version is an extension of another—proving nothing was modified or deleted, only added.
The simplest such log is a hash chain, where each element is hashed with the running chain hash. The final chain hash concisely represents the whole list.

Inclusion proofs and consistency proofs have simple structures. For inclusion at index i, the prover gives the chain hash before i plus all later elements; the verifier recomputes and checks the final hash.

For consistency between chains of sizes i and j, the prover supplies the elements between those points.

Building a transparency scheme
Per-site logs and witnesses
Give each site its own log as a hash chain whose items are the hashes of the site’s manifests at each point in time. The log stores manifest hashes, not manifests themselves; a separate asset host maps hashes to content using content-addressable, strongly cached static storage.

A raw log isn’t trustworthy—whoever runs it can rewrite history and recompute the chain. To enforce append-only behavior, a trusted third party called a witness verifies a consistency proof against its stored chain hash, and if valid, signs the new chain hash with a timestamp.
When a user visits a transparency-enabled site:
- The site serves its manifest, an inclusion proof that the manifest is in the log, and the signatures from all witnesses that validated the chain hash.
- The browser verifies the signatures from witnesses it trusts.
- The browser verifies the inclusion proof, confirming the manifest is the newest entry.
- The browser proceeds with the usual manifest and SRI integrity checks.
The user can now trust that the manifest is recorded in a log whose chain hash a witness has saved, so it can’t be deleted from history—and, assuming the asset host works, a copy of the served code is publicly available.
The transparency service
There’s a problem: if an attacker takes over a site, they can simply stop serving transparency data, silently disabling it. The system needs an explicit record of every site enrolled in transparency.
That record is a global data structure mapping each site domain to its log’s chain hash. A prefix tree (trie) fits well. Each leaf represents a domain and stores the site log’s chain hash, the log’s current size, and the asset host URL. A site proves its transparency status by presenting an inclusion proof for its leaf—efficient for tries.

The party running the trie, the transparency service, requires a site to prove domain possession before enrollment. Updates are handled by sending the service a new entry, which computes the new chain hash. Unenrollment is simply a request to remove the leaf—an adversary could also do this, which is why the removal mechanism matters (discussed below).
Witnesses verify whole-tree updates
Witnesses now check the prefix tree rather than individual site logs. For each tree update, the system must produce a proof covering every added, deleted, or modified entry, plus a consistency proof for each site log showing it was properly appended to. After verifying, the witness signs the tree root.

Client-side verification remains largely the same, with two changes: the client verifies two inclusion proofs—one for the integrity policy in the site log, and one for that log in the prefix tree—and it verifies the witness signature over the prefix tree root.
Signaling enrollment without extra round trips
Knowing whether a site is enrolled could mean fetching its tree entry—but that adds a round trip. Instead, browsers ship with the list of enrolled sites, the transparency preload list. If a site is on the preload list, the browser expects it to present an inclusion proof in the prefix tree, or a proof of non-inclusion in a newer tree version indicating unenrollment. A site must supply one of these until the last preload list containing it has expired. Because nothing enforces that the preload list matches the tree, the preload list itself must be published transparently.
Completing the property set
Cheap monitoring with timestamps
Monitoring without this design would mean constantly polling transparency services and verifying a domain hasn’t been tampered with—better than Certificate Transparency’s roughly 500k events per hour, but still a standing burden on both monitor and service.
The fix adds a “created” timestamp to each leaf, set at enrollment. Witnesses enforce that this field stays constant across all leaf updates and is deleted when the leaf is deleted. A site operator only needs to remember the last observed “created” and “log size” values. If both are unchanged on the next fetch, nothing happened since the last check.
Transparent opt-out via tombstones
Leaf deletion must also be auditable. Rather than removing a leaf outright, the transparency service replaces it with a tombstone—a value containing only a “created” timestamp. Witnesses ensure the field is unchanged until the leaf is either permanently deleted after a visibility period or re-enrolled.
Multiple services, no single point of trust
To avoid a single point of failure or trust, the design anticipates a small set of non-colluding transparency service providers, each running its own prefix tree. Like Certificate Transparency, the set must be small enough for reasonable trust assumptions and for independent auditors to handle the load of verifying all of them.
Why Consistency Matters
Transparency alone is not enough. If a site can serve any version from its entire history, an auditor would have to review every version ever published to ensure no user was ever served malware. Even a site that releases just one version per week would accumulate hundreds of servable versions over the years, including old, vulnerable ones. For transparency to be useful, we need consistency: the property that all browsers see the same version of a site at a given time.
Perfect consistency is unattainable, but weaker forms suffice. If a site had eight valid versions at any moment, an auditor could manage that. Users might not all see identical versions, but they would still all benefit from transparency.
We distinguish two types of inconsistency:
Tree Inconsistency
Tree inconsistency arises when transparency services' prefix trees disagree on the chain hash of a site, meaning they disagree on the site's history. Consensus mechanisms could eliminate this entirely—for example, majority voting across five transparency services would require a site to present three tree inclusion proofs. But that triples proof sizes and reduces fault tolerance: if three log operators go down, no transparent site can publish updates.
Instead, we limit the number of transparency services. Chrome trusts eight Certificate Transparency logs in 2025, and a similar number would work here. Inconsistencies between trees remain detectable and provable, since witnesses sign roots. If sites are expected to use the same version across all trees, violations become subject to social pressure.
Temporal Inconsistency
Temporal inconsistency occurs when a user receives an older or newer version of a site (both still unexpired) based on external factors like geography or cookies. If a signed prefix root remains valid for ten years, a site could serve any version from that decade.
Consensus mechanisms could also solve this—publishing the latest manifest on a blockchain would let users fetch the current head and verify they have the latest version. But that adds a network round trip per client, forces sites to wait for on-chain publication before updating, and dramatically increases specification complexity. We're aiming for v1.0.
Our mitigation is to require reasonably short validity periods for witness signatures. If prefix root signatures expire weekly, the number of simultaneously servable versions shrinks dramatically. The cost: site operators must query the transparency service at least weekly for a fresh signed root and inclusion proof, even with no changes. The transparency service must handle that load, so this parameter needs careful tuning.
Beyond the Core Properties
Integrity, consistency, and transparency are substantial, but additional app store-like features can be layered on with modest effort.
Code Signing
WAICT doesn't address provenance: precisely where did the code originate? For heavily audited code, third-party review fills the gap. But small self-hosted deployments of open-source software lack that coverage. If Alice hosts her own Cryptpad instance for her friend Bob, how can Bob verify the code matches the upstream repository?
WEBCAT, built by the Freedom of the Press Foundation (FPF), solves this. The protocol lets site owners announce which developers signed the site's integrity manifest—meaning all code and assets served to users. Users running the WEBCAT plugin can inspect those developers' Sigstore signatures and decide whether to trust the code.
WAICT is extensible enough to accommodate WEBCAT. Manifests may carry additional metadata we call extensions; here, the extension holds a list of developers' Sigstore identities. For this to work, browsers must expose an API so plugins can read these extension values, enabling independent parties to build features on top of WAICT.
Cooldowns as a Safety Net
Nothing described so far stops an attack in progress. An attacker who compromises a website can strip code-signing extensions or remove the site from transparency entirely, continuing their attack undisturbed. The removal will be logged, but the malicious code won't be, and discovery may come too late.
Unenrollment cooldown prevents spontaneous removal. With a 24-hour cooldown, the client requires that any site on the preload list either has transparency enabled or has a tombstone entry at least 24 hours old. An attacker must then either serve a transparency-compliant version or run a broken site for a full day.
Extension cooldown similarly blocks sudden modifications, using code signing as the example. The dev-ids extension gets its own preload list identifying sites that opted into code signing—without one, any site could delete the extension at will. The client enforces two rules: dev-ids must exist in the manifest, and dev-ids-inclusion must contain an inclusion proof showing the current value sat in a prefix tree at least 24 hours old. Clients reject newer values. To remove dev-ids, a site must first request removal from the preload list, then set dev-ids to an empty string and update dev-ids-inclusion accordingly.
Deployment Roles
This ecosystem has several distinct actors with different trust and resource profiles.
Transparency services store metadata for every transparency-enabled site. With 100 million domains at 256 bytes per entry (a few hashes plus a URL), a single tree totals roughly 26GB, excluding intermediate hashes. Pruning rules would likely unenroll inactive sites to control growth. Services need moderate storage, high availability, and uncorrelated downtime, since all services going down would block updates for every transparent site.
Their trust requirements are bounded by witnesses. A service could theoretically swap any leaf's chain hash and obtain witness validation if the consistency proof is valid, but such changes are detectable by anyone monitoring that leaf.
Witnesses verify prefix tree updates and sign resulting roots. They hold a full copy of each tree they witness, giving them storage costs similar to transparency services, plus high uptime demands. They must also protect their signing keys for extended periods—long enough for browser trust stores to update during key rotation.
Asset hosts carry little trust. They cannot serve bad data because query responses are hashed and compared against known hashes. Their only misbehavior is refusing to respond, which can also happen accidentally through downtime.
Clients—the web browsers themselves—are the most trust-sensitive component, performing all transparency and integrity checks.
At Cloudflare, we intend to run both a transparency service and a witness. Crucially, our witness should not monitor our own service. Instead, we witness other organizations' services, and they witness ours.
Supporting Alternate Ecosystems
WAICT should work where large centralized players are absent. We are collaborating with the FPF to define transparency for networks with different trust environments, primarily the Tor ecosystem.
Paranoid Tor users may distrust existing transparency services and witnesses, and may lack the resources to self-host them. For such cases, placing the prefix tree on a blockchain may be appropriate. Standard domain validation becomes impossible without a validator server, but that's acceptable for onion services: an onion address is a public key, so a signature alone proves domain ownership.
A consensus-backed tree eliminates the need for witnesses and leaves a single canonical transparency service. Tree inconsistency largely disappears, at the cost of update latency.
What Comes Next
The standardization process is just beginning. Near-term work includes extending subresource integrity to WASM and images, followed by standardizing the integrity manifest format and then the remaining features. We intend to develop this specification with browsers and the IETF, with betas expected soon.
You can follow the transparency specification draft, examine open problems, and contribute ideas. Pull requests and issues are welcome.
Acknowledgements
Thanks to Dennis Jackson of Mozilla for extensive design discussions, to Giulio B and Cory Myers of the FPF for feedback and influence, and to Richard Hansen for valuable input.



