Witbitz docs PlatformTrustAll docs

The attested client — knowing the code that runs on the user's side

Status: the server-side seam is BUILT (opt-in, behavior-neutral); the native clients are not. A room's owner-signed policy can carry requireAttestation, and the render verifies a freshness-bound platform attestation against it — that half is implemented and tested (with a mock verifier), bound to the member's signing key so it can't be replayed. What still needs the native side: the reproducible native shells and the real App Attest / Play Integrity / Keystore / TPM verifiers (today they're stubbed and fail-closed, so a requireAttestation room refuses every turn — the seam is ready for the verifiers to drop in). This is the missing symmetric half of the server attestation the rest of this site already does (reproducible build, egress-lock, signed /cert.json).

Every proof on this site is about code we run on our side. The client — the page in the user's browser — has been trusted on faith. And that faith is misplaced by construction: you cannot verify the code a browser is running. The user owns the runtime; any integrity check the page performs on itself can be hooked, patched, or spoofed, and nothing stops the user from ignoring the app entirely and calling the API with their own client. So a modified client can interact with a room in ways the audited app never would.

The response is not to try to attest the browser — that's unwinnable. It's to offer, opt-in, a client whose code can be attested, without giving up the web app.

The idea: a thin native shell around the same reproducible bundle

The app you already ship is a static, egress-locked, reproducibly-built web bundle. The attested client is that exact bundle, wrapped in a thin native shell whose only jobs are narrow and auditable:

  1. present a hardware attestation — prove a genuine, unmodified build of the app is running on a genuine device;
  2. hold keys in hardware — the Secure Enclave / StrongBox / TPM, so the room key never sits in JS;
  3. load the bundle locally into a locked web view;
  4. freshness-bind the attestation to each action.

You don't rewrite the app natively. There is one source of truth across web and attested-native, the privacy model is preserved — in fact strengthened, since keys move into hardware — and the audit surface is a thin generic shell plus the web code reviewers already read. It is the Companion promoted from a convenience into a trust primitive.

The one rule that makes it real, not theater

The web bundle must ship inside the attested binary and load locally — never off the network.

The Trusted Web Activity is the tempting trap here precisely because it is the easiest to build. Two locks keep the bundled version honest: the native↔JS bridge must be minimal and audited (no general script-injection surface), and the existing egress-lock CSP already forbids the bundle from pulling in remote script — so a bundled page can't quietly turn itself back into a network loader.

And the attestation must bind to every action, not once at admission — otherwise a member attests with the genuine app, then switches to a patched client for the writes that follow. The room's existing freshness challenge is the hook: each signed entry carries a fresh, attestation-bound value.

Platform support — and an honest split

The attested client is a web-view shell on each platform. The strength of the guarantee differs, and it's worth being exact about two distinct properties: integrity ("the running bundle is unmodified") and reproducibility ("the shipped binary rebuilds from published source").

Platform Shell Attestation Keys Notes
Android Android WebView Play Integrity, or hardware Keystore attestation (X.509 rooted in Google, carries app-signing digest + Verified Boot) StrongBox / TEE Cleanest full chain. Self-distributed + reproducible APK + Keystore attestation gives source → binary → attested. (Play Integrity is easier but ties you to Play distribution, which fights reproducibility — a real tradeoff.)
iOS / iPadOS / macOS WKWebView App Attest (hardware-backed, iOS 14+ / macOS 11+) Secure Enclave Integrity strong; reproducibility partial. Apple re-signs and encrypts the shipped binary, so you can't rebuild the App-Store artifact from source — you lean on a transparency-logged build pipeline for provenance.
Windows WebView2 Assembled — TPM 2.0 measured boot + Azure Attestation (device) · TPM key attestation (key) · Authenticode/MSIX + optional WDAC (app) TPM 2.0 No single "App Attest for Win32"; you compose device + key + binary-measurement attestation. Strongest on a managed enterprise fleet (MDM + WDAC), which is exactly the enterprise buyer.

So integrity of the running bundle is attestable on every platform; end-to-end reproducibility of the shipped binary is Android-clean, iOS-partial, and Windows-DIY. State that split plainly rather than implying one uniform guarantee.

The egress lock, generalized

Today the egress lock is the CSP header. With attested native clients it splits into two things a verifier keeps separate:

Platform Enforces the allowlist via How a skeptic checks it
Web the CSP header (connect-src …) read the live header — self-verifiable, trust nothing
iOS / macOS CSP inside WKWebView + App Transport Security; a Network Extension / per-app VPN under MDM reproduce + attest the binary and its Info.plist (ATS is TLS policy, not a host allowlist)
Android CSP inside WebView + network_security_config.xml; always-on VPN under MDM reproduce + attest the APK and its network config
Windows CSP inside WebView2 + AppContainer network capabilities (MSIX) + Firewall / WDAC attest the binary + its manifest (capabilities are coarse — internet vs private, not per-host)

The honest inversion. Every native OS control here is coarser than connect-src — none is a clean per-host allowlist you can read off a live response. So on native, the fine-grained guarantee leans more on the reproducible, attested binary ("this app provably contains no network code beyond the declared endpoints") than on a readable OS policy, and the verification method flips: on the web you read a header; on native you reproduce and attest the binary and inspect its (coarser) declared config. The web is not the weak link in the egress story — it is the platform with the cleanest, self-verifiable lock. Native buys code attestation and partly pays it back in egress-verifiability, making up the difference with the reproducible build.

Purpose-scoping is mandatory. The shell needs egress the web bundle doesn't — the attestation call to Apple / Google / Microsoft, and push. So the canonical allowlist scopes by purpose: the room-data path stays same-origin and locked exactly as today; the shell adds only a small, fixed, declared set of platform endpoints. That is a mild new trust surface (the platform attestor sees metadata), and it is stated, not hidden — the same honesty discipline as the rest of the cert.

How a room requires it

"Only attested clients may join" is not a client-asserted flag — it is an admission condition set by the room/app owner and verified on the server, in the same slot that carries "only my registered users" (see The Space link is the auth). At join and on each write, the server checks the platform attestation, bound to the member's signing key. Other members can be shown that a participant is on an attested client — the assurance is visible peer-to-peer, not just to the server. Admission and client-integrity become one policy: { authenticated identity } AND { attested client }.

What it buys — and what it doesn't

It shrinks the hardest residual: content injection between members. If only the audited, unmodified app can write, and that app is audited to seal only well-formed payloads, arbitrary injection is off the table for compliant members.

It does not make the client omnipotent-proof. A jailbroken or rooted device (attestation fights this, but it's an arms race), a vulnerability in the genuine app, and the fact that even an attested client still processes untrusted content from others all remain — so receiver hardening (XSS-safe rendering, sandboxed widgets, validated tool inputs) still matters; it just matters less. And because requiring an install trades away the open-a-link reach that makes the web app spread, this is opt-in and additive: web rooms keep the untrusted-client architecture (accountable identity + server-side enforcement); attested-native rooms get this stronger tier on top.

Why it fits

This is the natural apex of the attestation story the attested tier started on the server side. Today the render is attested and reproducible while the client is trusted on faith — an asymmetry. Closing it with a bundled, attested, hardware-keyed client makes the system attestable end to end for the rooms that ask for it, without abandoning the static-web-page model that makes any of it verifiable in the first place.

Machine-readable source: the-attested-client.md · every doc in one fetch: llms-full.txt · ← the-attested-tier · room-link-auth