Identity and admission
Identity in Witbitz is not "there is an account in our database." A Space is self-authenticated: its link carries the public material needed to verify who belongs, and members hold the corresponding private credentials.
This page is the short reader path. The canonical deep dive remains The Verified Room.
Base membership: hold the room key
Every Space has a room key mk. The client mints it and stores only a commitment on the server:
commit(mk) = base64url(sha256(mk))
When a member acts, the render checks that the presented key matches the stored commitment. A right key proves the caller holds the Space link. A wrong key opens nothing.
This proves "a member is present." It does not by itself prove which member.
Per-member identity: signed entries
To attribute entries to a specific person, each member can hold a signing key. The room's public invite key verifies the member grant, and the member's public signing key verifies the entry.
The signed core is conceptually:
{
"room": "sp-example",
"author": "ada@example.com",
"ts": 1785350400000,
"kind": "text",
"id": "c:...",
"text": "I approve this."
}
Because the author is inside the signed core, a rename or forged attribution fails verification.
Admission families
Witbitz inherits the credential model from Kibitz and carries it to async Spaces.
| Family | What it proves |
|---|---|
| Key possession | The caller holds the Space key. |
| Signed invite | A creator or room authority granted this member access. |
| OIDC / Google | The member authenticated as an allow-listed identity. |
| Agent key | This agent key is allowed by the room. |
| Capability grant | This participant may perceive or act in specific ways. |
| Owner policy | The app owner mandated admission tiers for the room. |
Gated Spaces
On an email-gated Space, the link alone is not enough. Reads and writes both require an allow-listed sign-in. That is
why the verification page starts with token-less poll and turn requests that return 403.
The allow-list itself lives in the sealed config, so the platform does not read the membership list at rest.
Run the check: Verify it yourself
Who sets admission
There are two layers:
| Layer | Decides |
|---|---|
| The Space creator | Ordinary open, invite, or email-gated rooms. |
| The app owner | Owner-governed rooms where every room must obey a signed App Policy. |
The owner rule is the enterprise path: an organization can require its own identity provider and tiered capabilities without giving itself access to room content at rest.
Read: Owner-governed rooms
Live vs designed
| Piece | Status |
|---|---|
| Key-possession membership | Shipped |
| Solo signed identity | Live and verified |
| Email-gated reads and writes | Live and checkable |
| Agent-key admission | Deployed |
| Scoped agent reads | Deployed |
| 2-of-2 per-member signed path | Not yet |
| Crypto-strong single-Space read revocation by epoch re-key | Designed; Bridge has epoch re-key |
For the complete status table, see The Verified Room.
Code map
| Concern | File |
|---|---|
| Room key commit and envelope | agent/envelope.mjs |
| Client key/link creation | spaces/public/spaceConfig.js |
| Signed entry/grant | agent/spaceEntrySig.mjs, spaces/public/entrySig.js |
| OIDC verification | agent/spaceOidc.mjs |
| Admission enforcement | agent/spaceService.mjs, agent/spaceHandler.mjs |
Machine-readable source: identity-and-admission.md · every doc in one fetch: llms-full.txt (HTML) · ← verify · member-lifecycle →