# 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](./verified-rooms.md) (the model you're checking) · [the Space link is the auth](./room-link-auth.md) (how a real member passes) · [the double blind](./the-double-blind.md) (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 platform is **content-blind at rest** — it stores only ciphertext, sealed to *your* key, not the operator's | ✅ **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) | 🔶 **not yet** — needs the attested/TEE tier |

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 **cannot** yet independently verify content-blindness-at-rest or declared-program-in-use in full; those need the *signed* certificate (build hash) and the attested tier ([the double blind §4–6](./the-double-blind.md)) — the certificate's **egress-allowlist half is now live**, its build-hash half is not. Honest position: **the gate and the egress are verifiable; the render is still trusted in use.**

---

## 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 | grep -oE 'https[^"]+/space')
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":"<ROOM>","mk":"<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":"<ROOM>","mk":"<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":"<OPEN_ROOM>","mk":"<OPEN_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](./the-double-blind.md)) — now shipped and enforced. You are reading the app's true data footprint off ground truth. (What it does **not** yet prove: that the *server-side* render binary is the published one — that's the build-hash + attested tier, still pending. This test bounds the **front-end**; the gate tests bound **access**; the render remains trusted in use.)

## Test 5 — content-blind at rest: the store holds only ciphertext, sealed to YOUR key

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":"<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 at rest, 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 — the one moment plaintext exists, to run the model. 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":"<same sha>", "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: <base64>
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). Designed; the north-star. Everything up to and including *"the code is exactly this, and it's readable and reproducible"* is now checkable — only *"and it can't be watched while it runs"* remains.

---

## 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](./room-link-auth.md). This is the async analog of Kibitz 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 — the single 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). Designed; the strongest rung, and the north-star.

This page grew **"verify the gate" → "footprint" → "store" → "the build."** The gate is live (Tests 1–3), the footprint is live (Test 4), content-blindness at rest is live (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. When it ships, its check lands **here**.
