Witbitz docs HomeTrustAll docs

Trust model

Witbitz makes two separate trust claims. Keep them separate:

  1. Privacy is verifiable — the double blind. A Space is sealed to a key the platform never holds; the app has no backend of its own; the browser egress footprint is locked; and the deployed render can be rebuilt from published source and checked against a signed certificate. Neither the operator nor the app's own owner can read the users' data — and that's a property you check, not a promise.
  2. Humans keep authority — delegated authority. An AI agent can propose work, but the runtime requires a human approval before any high-stakes action executes. No action runs without an approval.

This page states both guarantees in full. It is intentionally explicit about what is live today and what is not — start with Status if you need the shipped / preview / designed map, and Verify it yourself to run the checks in your own terminal.


What is protected today

Claim Production status
A gated Space refuses reads and writes without an allow-listed identity Live and checkable
The stored Space ledger is ciphertext sealed to the room key Live and checkable
There is no operator recovery recipient in the production envelope Live and checkable
The app's browser egress is locked to a published allowlist Live and checkable
The deployed render publishes a signed build certificate Live and checkable
The render source can be rebuilt to the deployed hash Live and checkable
High-stakes agent actions require approval Live and tested

Run the checks: Verify it yourself.

What is not hidden

Witbitz does not hide everything, and the docs should never imply that it does.


Guarantee 1 — The double blind

Witbitz's core privacy claim: for an app built on it, neither the platform operator nor the app's own owner can read the users' data — and, the part that makes it a property not a promise, that claim is meant to be checkable from the code, not taken on faith. What holds today, exactly where it stops, and how the remaining "readable in use" gap is closed by the attested tier, now in production.

Sealed to a key we never hold → the platform is blind

A Space's data is a snapshot sealed under the room key mk and stored as ciphertext; the platform never holds mk (agent/envelope.mjs seal/open; the key is committed only as commit(mk)). To the operator, a Space is opaque bytes — storage and transport are content-blind, because the platform holds no key. There is no operator recovery key: every record is sealed to the room key alone, so there is nothing on the platform that can read it. The double blind is the only configuration that runs in production.

The decrypt-once render → the one plaintext door

Plaintext opens in exactly one code path — the certified render — but that path runs on two triggers: an AI turn (open the ledger with a present member's mk, run the declared program, re-seal, drop mk) and every changed read (the poll decrypts the new entries to return them, because the client is thin and decrypts nothing itself — async Spaces). Reads outnumber turns, so the honest exposure surface is the render, not "only when the AI acts." The key reaches that render only from a present member's client, per active window — never from storage — so the operator can act on the data only while a member is present, and learns nothing when no one is.

The honest boundary, stated plainly. Because the client is thin, mk travels to the render on every read and write, and the server opens the ledger in place — a decryption oracle for the moments a member is active. So content-blind means at rest and against a passive (honest-but-curious) operator, not one that captures mk in flight; and because the whole ledger is sealed under one long-lived room key, an operator that logged mk once could read that Space's history past and future — there is no per-Space forward secrecy yet. That boundary is the reason the attested tier exists (below). Keeping it one small, auditable path is exactly what makes attestation tractable.

Backendless → the app owner is blind too

The second blind is the one nobody else offers. A Witbitz app is a static page with no backend of its own that connects only to the audited Witbitz API. So the app's developer/owner has no backend through which to read its users either: the users' content is sealed to a key the owner doesn't hold, and the app has nowhere else to send it. Data is encrypted from the owner, not merely to them.

To be exact — "backendless" is a claim about the app, not the platform. Witbitz is still a backend (one shared, constrained, inspectable platform). What's removed is each developer's own, opaque, app-specific server. So the trust surface isn't smaller in a hand-wavy "no infrastructure" sense — it's consolidated: one auditable platform to check, instead of N unknown app backends. That consolidation is exactly what makes the footprint declarable.

Verifiable → check the code, not the operator

Because the app is a static page with a declared egress (it can only talk to the Witbitz API), its entire data footprint is inspectable and declarable. You verify the code and a signature, not a "trust us" policy. Four of those checks run in your own terminal today — the admission gate enforcing, the egress lock, the content-blind store, and the deployed render being a reproducible build of published source. Verify it yourself is the commands. What is not yet checkable about your own traffic is the render being unobservable in use; that gap is closed cryptographically only in principle (a never-receives-mk, append-only tier whose sealed client→actor channel is unbuilt and not planned) — the shipped way to close it is the attested tier.

Status — what holds today

Property Status
Sealed ledger, platform holds no key (nothing on the platform can read it) shipped
No operator recovery key (sealed to the room key alone) shipped & CHECKABLEPOST {"op":"sealed"}recipients:["room"] (verify Test 5)
Admission gate enforces (reads and writes refused without an allow-listed identity) shipped & CHECKABLE (verify Tests 1–3)
Decrypt-once render, mk from the client only, dropped after shipped
Backendless static app (no backend of its own) shipped
Egress-lock (CSP connect-src/script-src allowlist) shipped & ENFORCED (block-mode) (verify Test 4)
Client E2EE primitive (the sealed-blob envelope) shipped
Signed build certificate (deployed build hash + egress, signed from ground truth) shipped & CHECKABLE/cert.json (verify Test 6)
Reproducible build of published source (rebuild it → the deployed hash) shipped & CHECKABLE/source.tar.gz (verify Test 7)
Attested tier (operator provably blind in use) Production — verifiable on real Nitro hardware, scale-to-zero; the app builder enables the enclave per room (attested tier)
Attested client (a hardware-attested native shell carrying the audited bundle) Design — native shells not shipped; the deployed web app already verifies the enclave's attestation, which is part of the attested tier above (verified client)

The moat — honestly

The moat is not "incumbents can't do this." They can: Apple's Private Cloud Compute ships attested confidential inference today, and a competent team could match everything Witbitz has shipped in a quarter. Two things are actually defensible:

Code map

Concern Where
At-rest ledger seal (symmetric, under mk only) + key commitment agent/envelope.mjs (seal, open, commit, newRoomKey); agent/sessionStore.mjs encodeMemory — one recipient, mk. No operator/beta/admin recipient in prod.
Recipient sealing (ECDH sealTo/openBox) — seals to a member/agent public key, never an operator scoped agent reads (agent/spaceScopedView.mjs), Bridge epoch keys (agent/bridgeMembership.mjs), the collection-blob — a separate mechanism from the at-rest ledger above
Sealed ledger + decrypt-once render agent/sessionStore.mjs, agent/asyncTurn.mjs
Content-blind poll (etag over ciphertext) agent/spacePoll.mjs
Egress-lock CSP (block-mode, enforced on prod Spaces) spaces/tools/gen-space-csp.mjs
The render, published and readable (~600 lines) the published render source

The double blind is not "trust us less." It is "you don't have to" — sealed to your key by default, and provably unobservable in use for any Space that turns on the attested tier.


Guarantee 2 — Delegated authority

The second guarantee. An agent never owns authority — it receives it, limited and revocable, and a human approves anything high-stakes before it happens. The runtime enforces one invariant: no action runs without an approval.

Built + tested (async Spaces M2 Cut 4); runs inside the deployed /space render. Code: agent/delegatedAuthority.mjs.

Propose → approve → execute

An effectful action moves through three recorded steps, asynchronously (the human may approve on a later turn, not in-the-moment):

  1. propose — on its turn, the agent proposes an action; it is recorded pending, not run (propose(room, { action, by })).
  2. approve / deny — a human decides on a later turn (decide(room, { id, approve, by })). A decision on a non-pending proposal is refused; a decided proposal can't be flipped.
  3. execute — only an approved proposal runs, through the injected executor (execute(room, { id, run })), and only once.

The invariant

Enforced and unit-tested: an un-approved proposal never runs, and no proposal double-runs. Approval is a precondition to execution, checked in the runtime — not left to the model's discretion. The model reasons, the human authorizes, the runtime gates.

Capabilities are just tools

A Space's effectful capabilities are ordinary tools in its config (send_summary, schedule_checkin, …) — there is no separate "actions" concept. The turn runs the normal brain tool-loop; the only async-specific twist is that a high-stakes tool call is deferred to a human approval instead of running in-room. On approve, execute runs the same proposal through the same tool executor a live call would use, keyed on the tool name — so approval routes to the real implementation.

The audit trail

Every step — propose / approve / deny / execute (with result or error) — is appended to a single per-room authority doc, an append-only log. It is mk-sealed like the ledger, so it is content-blind (the platform can't read it) and it persists across weeks. A wrong mk yields nothing — the trail is readable only to a present member, in the decrypt-once render.

Richer policy — spend caps, per-capability authority tiers, org-level delegation — builds on this primitive. The one-method-call SDK surface for it is Delegated authority in the SDK.


Identity and admission

A Space starts with key possession: holding the room key proves membership. Gated Spaces add signed identity on top — Google/OIDC identity, signed invites, agent keys, owner policies. The link is the admission capability; the owner decides who may join and at what tier; and members join, rotate devices, and are revoked over a Space's life. The full mechanism — identity, the verified room, admission tiers, the owner rule, member lifecycle, and recovery — is one doc: Identity and admission.

Stronger tiers

Tier Status What it changes
Owner-governed rooms Implemented The app owner can mandate admission tiers and capabilities; built and tested, not yet enabled in production (owner rule).
Attested server tier Production A Space's turns run inside measured Nitro hardware, key sealed to the image; launches on demand, scales to zero when idle. The platform can run any room in it; enabling it is the app builder's choice (attested tier).
Attested client Design (native shells) The deployed web app already verifies the enclave's attestation and pins its measurement before sealing your key — that half is live, part of the attested tier; a native binary carrying the audited bundle is still future work (verified client).

For the full mechanism-by-mechanism breakdown — the canonical maximum, the orthogonal privacy axes, the named modes, and the feature × mode matrix — see the privacy spine: Privacy tiers.

Where to go next

Machine-readable source: trust-model.md · Generated 2026-08-29T18:46:30Z · build f3ff88cc · every doc in one fetch: llms-full.txt (HTML) · ← async-spaces · privacy-tiers