# Verify It Yourself Witbitz's claim is **verifiable, not promised** — so this page is where you *check* it, in your own terminal, instead of taking our word. It's the honest counterpart to every "enforcement-proven" line in the other docs: here are the exact commands, and here — just as plainly — is what they do **not** prove yet. > See also: [The Verified Room](./identity-and-admission.md) (the model you're checking) · [the Space link is the auth](./identity-and-admission.md) (how a real member passes) · [the double blind](./trust-model.md#the-double-blind) (the privacy claim + its north-star). --- ## What this page proves — and what it doesn't Be exact about the scope, because "verify it yourself" is itself a claim that can overreach: | | Status | |---|---| | **The admission gate enforces** — reads *and* writes are refused without an allow-listed identity, even when you hold the room key | ✅ **checkable now** (Tests 1–3) | | **The app's egress is locked** — the app you loaded can reach only a published allowlist of hosts; the browser blocks everything else | ✅ **checkable now** (Test 4) | | The store holds only ciphertext, **sealed to a key the platform never holds** (it can't read what it stores) | ✅ **checkable now** (Test 5) | | The deployed render is a **faithful build of published source** — rebuild it and get the same hash AWS runs | ✅ **checkable now** (Tests 6–7) | | The render is unobservable **in use** (operator can't read the plaintext during the decrypt instant) | 🔶 **Verifiable once the enclave is on** — the tier itself is **Production**; turn it on and this becomes checkable on your own traffic ([the attested tier](./the-attested-tier.md)); enabling it is the app builder's per-room choice | So today you can verify **that the gate enforces** — the load-bearing "who may read/write" layer — **and** that the front-end's data footprint is locked to a published allowlist (Test 4). You can **also** verify the store holds only ciphertext sealed to a key the platform never holds (Test 5) and that the deployed render is a **faithful, reproducible build of published source** (Tests 6–7). The one thing you **cannot** yet verify **about your own traffic** is that the render is **unobservable in use** — that during the transient decrypt-and-render instant the operator can't read the plaintext out of memory; that needs the attested/TEE tier ([the double blind §4–6](./trust-model.md#the-double-blind)). That tier is now **in production**, and independently verifiable — the room key is generated inside AWS Nitro hardware, the image measurement is reproducible from published source, the whole check runs in your browser ([the attested tier](./the-attested-tier.md)) — turn on "Run this Space inside the enclave" and its key is sealed to a key born inside the measured image, verified in your browser against the running enclave's published measurement (`/enclave-claim.json`) with a fresh nonce. Enabling it is the app builder's per-room choice — the platform can run any room in the enclave — and the model call leaves the measured image only through an attested no-log gateway into confidential-compute inference on an open-weights model — the frontier-vendor residual applies only to rooms that choose the frontier trade. Honest position: **the code is exactly this — readable, reproducible, and what AWS runs — and "it can't be watched while it runs" is now buildable, checkable, and in production for any Space the app builder runs in the enclave.** --- ## Read the render — the code that touches your plaintext Verification is only worth as much as your ability to *read* what you're verifying. Plaintext opens in one place — the server-side render — and that surface is **small**: ~600 lines for the seal / turn / read core. It's published, readable, Apache-2.0: **→ [the published render source](https://github.com/kibitz-chat/witbitz-render)** | File | Lines | What it does | |---|---:|---| | `envelope.mjs` | 287 | seal/open — a per-write key wrapped **per recipient** (`room` under `HKDF(mk)`); **no** operator recipient | | `asyncTurn.mjs` | 228 | the decrypt-once turn: open ledger → append → model → append → re-seal → drop `mk` | | `spacePoll.mjs` | 79 | the read: the sealed blob never leaves the server; unchanged = no decrypt; changed = new entries as plaintext | | `sessionStore.mjs` | 243 | session memory, sealed with `mk`, fail-safe | That repo is a **readable mirror**; the hash-verified canonical is `/source.tar.gz` (Test 7), which rebuilds byte-for-byte to the deployed hash. Read the logic there; confirm the bytes here. --- ## Setup (30 seconds) 1. In the [Spaces app](https://witbitz-spaces.pages.dev), create a Space with **"Who can post?" → Only specific Google accounts**, and add any email to the allow-list. This is an `admission:'email'` Space. 2. From its link `…/space?room=sp-XXXX#mk=YYYY&gate=email`, copy the **`room`** (`sp-XXXX`) and the **`mk`** (`YYYY`) — the room key that *everyone with the link holds*. 3. Discover the API endpoint (it's public; the app fetches it the same way): ```bash SPACE=$(curl -s https://witbitz-spaces.pages.dev/space-config \ | python3 -c 'import sys,json; print(json.load(sys.stdin)["spaceEndpoint"])') echo "$SPACE" # the /space Lambda URL the client posts to ``` You now hold exactly what a link-holder holds: the room and its key. The tests below show that **is not enough**. --- ## Test 1 — the read gate: the link alone reveals nothing A token-less `poll` — the read op — with a *valid* room key: ```bash curl -sX POST "$SPACE" -H 'content-type: application/json' -w '\n%{http_code}\n' \ -d '{"op":"poll","room":"","mk":"","sinceEtag":"","sinceCount":0}' # → {"error":"not_authorized","reason":"unsigned"} # 403 ``` Holding `mk` returns **no history**. Reads require an allow-listed Google sign-in — the server won't decrypt-and-return without one. ## Test 2 — the write gate: you can't post without an allowed identity A token-less `turn` — the write op: ```bash curl -sX POST "$SPACE" -H 'content-type: application/json' -w '\n%{http_code}\n' \ -d '{"op":"turn","room":"","mk":"","message":{"text":"hi"}}' # → {"error":"not_authorized","reason":"unsigned"} # 403 ``` Rejected before anything is written — nothing lands in the Space. ## Test 3 — positive control: the key is valid; the 403 is the *gate*, not a bad key Create a second Space with **"Who can post?" → Anyone with the link** (an `open` Space), and `poll` it the same way with **its** room/key: ```bash curl -sX POST "$SPACE" -H 'content-type: application/json' -w '\n%{http_code}\n' \ -d '{"op":"poll","room":"","mk":"","sinceEtag":"","sinceCount":0}' # → 200 (returns entries) ``` Same request shape, a valid key — but **no admission policy**, so it reads. This is the control that proves Tests 1–2 are the **admission gate** refusing you, not a malformed request or a wrong key. An open Space reads by the link; an email Space does not. ## Test 4 — the egress footprint: the app can only talk to a published allowlist The other three tests check the *server*. This one checks the *app itself*: a static page can `fetch()` anywhere, so "the app doesn't leak your data to third parties" is a real claim — and it's now **browser-enforced**, not promised. Read the enforced Content-Security-Policy straight off the response header — it is the *exhaustive* list of hosts the loaded app is permitted to reach: ```bash curl -sI https://witbitz-spaces.pages.dev/space | grep -i '^content-security-policy:' # default-src 'none'; script-src 'self' 'sha256-…' 'wasm-unsafe-eval' https://accounts.google.com; … # connect-src 'self' blob: https://api.witbitz.chat https://accounts.google.com; … frame-ancestors 'self' ``` Read it directly: - **`connect-src`** is the data path — where the app may send anything. It is exactly **`'self'`** (the same-origin app + its map/tile proxy), **`https://api.witbitz.chat`** (the Space backend), and **`https://accounts.google.com`** (Sign-in). No analytics host, no CDN, no third party. Your data has nowhere else to go — the browser refuses it. - **`script-src`** allows only same-origin files **and each inline block by its `sha256`** — **no `'unsafe-inline'`**. So even injected script can't run, and can't exfiltrate. - **`default-src 'none'`** means anything not named above is denied by default; `form-action` and `object-src` are shut; `frame-ancestors 'self'` blocks cross-origin framing. This is the **egress-allowlist half of the signed certificate** ([the double blind §4–6](./trust-model.md#the-double-blind)) — now shipped and enforced. You are reading the app's true data footprint off ground truth. (What it does **not** prove is that the render is *unobservable while it runs* — that's the attested tier, now live as a per-room option ([the attested tier](./the-attested-tier.md)). That the render *binary* is the published, reproducible build is proven separately by Tests 6–7. This test bounds the **front-end**; the gate tests bound **access**.) ## Test 5 — the store holds only ciphertext, sealed to a key we never hold The other tests check access and egress. This one checks *what the operator actually holds*. The `sealed` op returns the **exact bytes the server stores** for a room's chat — no key required, because reading ciphertext reveals nothing: ```bash curl -sX POST "$SPACE" -H 'content-type: application/json' -d '{"op":"sealed","room":""}' | python3 -m json.tool # { # "alg": "AES-256-GCM · multi-recipient envelope v1", # "bytes": 14554, # "sha256": "ad7c09…", ← hash of the exact stored bytes # "recipients": ["room"], ← WHO can decrypt: only the room key. NOT the operator. # "sealed": "{\"v\":1,\"iv\":\"…\",\"ct\":\"…\",\"recipients\":{\"room\":{…}}}" ← the opaque envelope itself # } ``` Two things to read straight off it: - **`recipients` is `["room"]`.** The envelope is sealed to exactly one key — the room key (`mk`), which lives in your link's `#fragment` and *never reached the server* (you didn't send it in the request above). The operator would be a recipient **only if** `"beta"` or `"admin"` appeared in that list. It doesn't. So the operator holds no key that opens this blob — an `aws s3 cp`, a backup, or a subpoena returns exactly these bytes: noise. - **`sealed` is opaque.** It's an AES-256-GCM ciphertext; without `mk` it is indistinguishable from random. Confirm it's genuinely *your* chat, not a decoy, by decrypting it locally with the `mk` from your link — the same key the server never saw. This is **content-blindness, made checkable**: what the operator stores is ciphertext sealed to a key it does not have. (What it still can't prove: that the **render** — the one transient moment plaintext exists to run the model — doesn't secretly keep `mk`. That's declared-program-in-use, addressed next.) ## Test 6 — the signed build certificate: what the platform DEPLOYED, committed in public The last surface is the render code itself — where plaintext opens (to run the model on a turn, and to return the new entries on a changed read). Full proof (that the running binary is a faithful, un-tampered build) needs an attested tier (below). But the **first step** ships today: the platform publishes a **signed certificate** at `/cert.json` binding what it deployed to ground truth — a build identifier (git commit), the running Lambda's AWS `CodeSha256`, and the egress allowlist — and the running service reports the same identifier via `op:attest`. ```bash curl -s https://witbitz-spaces.pages.dev/cert.json | python3 -m json.tool # { "signed": "{\"v\":1,\"service\":\"witbitz-space\",\"gitCommit\":\"…\",\"lambdaCodeSha256\":\"…\",\"egress\":\"…\"}", # "sig": "…", "alg": "ES256", "pubkey": {"kty":"EC","crv":"P-256","x":"…","y":"…"} } curl -sX POST "$SPACE" -H 'content-type: application/json' -d '{"op":"attest"}' # → { "service":"witbitz-space", "gitCommit":"", "runtime":"aws-lambda" } ``` Verify the signature yourself — the platform's **public key** is published here, so compare it to the `pubkey` in the cert and check the signature over the exact `signed` string (ES256 / ECDSA P-256): ``` {"kty":"EC","crv":"P-256","x":"TFXrR5DFL3Oo3rBwa6clwJnrfvw0TN2AewDDEcy8hBM","y":"MgLLzEq7g8B86qS7NNxkIW64ytp296d1kkK1wSKBoec"} ``` ```js // node — verifies the cert's signature against the published key (WebCrypto) const c = await (await fetch('https://witbitz-spaces.pages.dev/cert.json')).json() const ub = (s) => Uint8Array.from(Buffer.from(s.replace(/-/g,'+').replace(/_/g,'/'),'base64')) const k = await crypto.subtle.importKey('jwk', { ...c.pubkey, ext:true, key_ops:['verify'] }, { name:'ECDSA', namedCurve:'P-256' }, false, ['verify']) console.log(await crypto.subtle.verify({ name:'ECDSA', hash:'SHA-256' }, k, ub(c.sig), new TextEncoder().encode(c.signed))) // true ``` **What it proves:** the platform has publicly, cryptographically **committed** to a specific deployed build (a named build id + the Lambda's exact code hash) with a specific egress. The commitment is tamper-evident (a changed cert fails the signature), and the running service reports the same build id — so the operator can't quietly point the certificate at one build while running another without it showing. On its own, Test 6 is a *tamper-evident commitment to a build*. **Test 7 turns it into a check of the actual code** — the cert also carries `sourceSha256`, the hash of the *published, readable source* that reproduces `lambdaCodeSha256`. ## Test 7 — the faithful build: rebuild the published source, get the deployed hash The cert commits to a code hash. Test 7 lets you confirm that hash is a **faithful build of source you can read** — not a black box. The exact render source is published at `/source.tar.gz`; the cert's `sourceSha256` pins it, and it rebuilds **byte-for-byte** to `lambdaCodeSha256`. Needs only **Node + Python 3** — no Docker. The zip is **STORED** (uncompressed), so the bytes don't depend on a zlib version; `npm ci` is deterministic for this pure-JS graph (no native binaries). ```bash # 1) download the published render source and confirm it's the one the cert names curl -s -o source.tar.gz https://witbitz-spaces.pages.dev/source.tar.gz python3 -c 'import hashlib,base64;print(base64.b64encode(hashlib.sha256(open("source.tar.gz","rb").read()).digest()).decode())' curl -s https://witbitz-spaces.pages.dev/cert.json | python3 -c 'import sys,json;print(json.loads(json.load(sys.stdin)["signed"])["sourceSha256"])' # ↑ these two must match — the source you downloaded IS the source the cert commits to. # 2) rebuild it and compare to the deployed hash tar xzf source.tar.gz && bash tools/reproduce-space-build.sh # → reproduced CodeSha256: curl -s https://witbitz-spaces.pages.dev/cert.json | python3 -c 'import sys,json;print(json.loads(json.load(sys.stdin)["signed"])["lambdaCodeSha256"])' # ↑ these two must match — you just rebuilt the exact bytes AWS is running (its CodeSha256). ``` Both match ⇒ **the deployed render is a faithful build of code you just read.** No trust in the build server: you reproduced it. **Honest scope — the one thing left.** This proves the deployed *code* is a faithful build of *readable* source. It does **not** prove the running *process* is unobservable **in use** — that during the transient decrypt-and-render instant, the operator (or the cloud) can't read the plaintext out of memory. That is the **attested/TEE tier** (a confidential-compute enclave whose remote attestation binds the running measurement to this reproducible build). **Production, and independently verifiable**: see [the attested tier](./the-attested-tier.md). Everything up to and including *"the code is exactly this, and it's readable and reproducible"* is checkable today; *"and it can't be watched while it runs"* is now in production for any Space the app builder runs in the enclave — its per-room choice. --- ## Test 8 — drive the real browser: prove the locks are *enforced* (the CDP verifier) Tests 1–7 read *a response* — a header, a stored blob, a cert. That proves what the server **declares**, but not what your **browser** actually enforces on the live app — and a passive fetch is **blind to a Service Worker**, which can serve tampered bytes from its own cache with no network request at all. To close that, *drive* a real browser and check the app's claims from the browser's **ground truth** — below the page, below any Service Worker: ```bash node tools/lumen-verifier.mjs # drives https://witbitz.chat/lumen (pass a URL to point elsewhere) ``` It proves, from the running app rather than from a fetch: - **the egress-lock is *enforced*** — it runs a forbidden `fetch()` *inside the room* and confirms the browser **blocks it** with a `connect-src` violation (not merely that the header *says* so); - **cross-origin isolation holds** — the embedding site gets a `SecurityError` trying to read the room's DOM ("can't read a word"); - **no rogue Service Worker** — it enumerates the **browser-owned** registration list, which a Service Worker cannot forge; the one thing a load-time or `curl` check can't see; - **byte identity** — the inline shell's SHA-256 is surfaced and confirmed to be one of the CSP hashes the browser pinned, so you can compare it to the reproducible source (Test 7). **Honest scope.** It verifies the *browser it drives* — so **run it yourself**, or it's just checking someone else's browser (the "whose fetch" caveat, now at the browser level). It's a desktop tool; a stock **mobile** browser can't be driven this way — which is exactly what the **native attested client** ([the attested client](./client-architecture.md#the-attested-client)) is for: it *is* a controlled, bundled-local browser, so there's no rogue-worker surface and no external driver needed. --- ## Reading the result codes The gate is fail-closed; every rejection is a `403 not_authorized` with a precise `reason`: | `reason` | Meaning | |---|---| | `unsigned` | no identity presented at all (no `idToken` + `spk`) | | `token: <…>` | a Google token was presented but failed — e.g. `token: expired`, `token: bad_aud`, `token: bad_signature` (RS256-vs-JWKS) | | `nonce_mismatch` | a valid token, but its `nonce` isn't bound to the signing key it was presented with | | `not_on_list` | a valid, key-bound token, but the email isn't on the Space's allow-list | | `revoked` | a valid, allowed identity that has since been removed from the Space | | *(none)* → `200` | accepted | ## How a real member passes (the signed path) An allow-listed member's client sends, with each request, an **`idToken`** (a fresh Google ID token) and **`spk`** (its device signing public key). The server checks, in order: the token verifies **RS256 against Google's JWKS**; its `nonce` equals **`spkNonce(spk) = base64url(SHA-256(canonical-JWK(spk)))`** (so the token is bound to *this* device's key); and the email is on the **sealed allow-list**. Only then does it decrypt-and-return, or accept the write. Full mechanism: [the Space link is the auth §4](./identity-and-admission.md). This is the async analog of the live call binding an OIDC token to a live DTLS cert — "signed by the keyholder who authenticated as this account, now." --- ## What you can't check here yet — and why Being explicit, so this page doesn't become the overclaim it's meant to prevent: - **Unobservable-in-use — the attested/TEE tier.** The render decrypts with `mk` for a transient instant — on an AI turn and on every changed read, the one door plaintext opens. Tests 6–7 now prove the deployed *code* is a **faithful, reproducible build of readable source** — you can rebuild it and read it. The last thing they can't prove is that the running *process* can't be watched while it runs: that during that instant the operator (or the cloud) can't read the plaintext out of memory. That needs a **TEE** — a confidential-compute enclave whose remote attestation binds the running measurement to this reproducible build (and thereby excludes even the operator + AWS from the render). **Live on real AWS Nitro hardware as a per-Space option — launch-on-demand, scale-to-zero** ([the attested tier](./the-attested-tier.md)); enabling it is the app builder's per-room choice. On that tier the model call also closes the last plaintext reader, the model vendor: it leaves the measured image only through an attested no-log gateway into **confidential-compute inference on an open-weights model** (fail-closed if that gateway does not attest) — shipped, with the frontier model kept as a per-room choice; your enclave check transitively covers that gateway's attestation. This page grew **"verify the gate" → "footprint" → "store" → "the build."** The gate is live (Tests 1–3), the footprint is live (Test 4), the store is sealed to a key we never hold (Test 5), the signed build commitment is live (Test 6), and the **faithful, reproducible build of published source** is live (Test 7). Everything up to *"the code is exactly this — readable, reproducible, and what AWS runs"* is now checkable. What remains is only *"and it can't be watched while it runs"* — the attested/TEE tier, **now live as a per-room option**: turn it on and verify your Space against the running enclave's published measurement (above).