Authorization
JWT-claim driven. Per-twin sovereignty by KC Organization isolation. The grant model that makes the mesh permissioned by construction.
Authorization in IOBOXX is decided from JWT claims minted by Keycloak. There is no request-time database round-trip, no separate authorization service, no per-layer permission cache to drift. One trust boundary: the signed token. Every layer that consumes an IOBOXX request verifies the same JWT and reads the same claims.
This is the component that makes the XX of IOBOXX real. The third letter pair stands for the mesh of sovereign nodes. From an invoice to a catalogue to a sensitive date of birth, every datum has a sovereign owner, every share is explicit, every grant is auditable. It is a mesh, not a platform — no central control plane owns the data, no tenant becomes another tenant's product. Authorization is the layer that turns that framing into code.
CLAIMSJWT-claim driven
Identity (see Identity) runs a single realm forus-identity on Keycloak. The realm mints a signed JWT at sign-in. The session callback in each app decodes the access-token claims into a typed shape, and reducers in the SpacetimeDB data plane read the same claims from the verified JWT and enforce there. No reducer consults a separate authorization service.
| Decision | Claim consulted | Decoded as |
|---|---|---|
| Platform sysadmin? | resource_access['realm-management'].roles ⊇ ['realm-admin'] | isSysadmin |
| Network-admin for active Org? | org_permissions[activeOrganizationId] ⊇ ['manage-organization'] | isNetadmin |
| Member-admin for active Org? | org_permissions[activeOrganizationId] ⊇ ['manage-members'] | isOrgAdmin |
| KYB operator? | resource_access.activate.roles ⊇ ['kyb-operator'] | kybOperator |
| Active tenant | active_organization | activeOrganizationId |
| Per-app role grants | resource_access.<client>.roles | role list on session |
The JWT signature is the only thing each layer needs to verify. Claims are cacheable for the token's lifetime, auditable at one point (the KC event log plus the SpacetimeDB projection), and free of drift between cache and source. DB-backed permission flags drift; claims minted at sign-in don't.
SOVEREIGNTYPer-twin sovereignty by Organization isolation
Each twin is a Keycloak Organization. Membership in Organization X grants access to twin X's substrate. The sovereignty guarantee follows directly:
- Network-admin and Org-admin grants are scoped to the active
org_permissions[orgId]claim — they only apply inside their home Organization. - A user with
realm-adminis a platform sysadmin; they are not automatically an admin of any tenant Organization. Sovereignty wins over admin. - Sysadmin elevation into a specific Org requires a just-in-time membership add (KC's
org_idauthorization parameter requires Organization membership;realm-admindoes not bypass it). - KC Organizations are independent of one another — no cascade, no implicit power flow. Compromise of one Organization's admin does not extend to another.
Sovereignty is not a checkbox in an admin UI. It is the structural consequence of deciding authorization from claims that are scoped to a single Organization, and of refusing to let realm-level admin grants leak into tenant scope.
MESHWhat real businesses share — the mesh in practice
Permissioned sharing is what real businesses actually do. Not "give us your data and we'll give you back insight." Real businesses share specific information, with specific counterparties, under specific contractual terms, for specific durations, with specific revocation rights. The XX of IOBOXX delivers this by construction: a per-twin grant model lets any sovereign node permission any other sovereign node to read any subset of its working memory, and revoke that grant at any time.
- A supplier twin shares its catalogue with a buyer twin under one grant.
- Its compliance certificates with a regulator twin under another.
- Its margin structure with no one.
- A clinical twin shares de-identified outcomes with a research twin while keeping patient identity inside its own sovereign boundary.
- A founder's personal twin shares a credential with a business twin without copying the credential.
None of this requires a central database, a federated query engine, or anyone's "data lake." Just a grant, a typed reference, and a commitlog. The grant primitives themselves live one layer deeper, in tenancy & federation; the philosophical framing lives in the name.
The mesh is not a data lake. No central store accumulates every twin's working memory.
It is not a federated query engine. No coordinator plans cross-tenant joins on someone else's behalf.
It is not a central control plane that owns the data. The grant is between two sovereign nodes; the platform records the grant and audits the access, but the data stays where it was written.
SCOPECoarse today, fine-grained later
The role + Organization-membership model handles coarse authorization cleanly: "is this user a member of this twin? are they an admin? do they hold this app-level role?" That is every authorization decision the platform makes today.
A future ReBAC layer in the Zanzibar style — OpenFGA or SpiceDB — is the reserved primitive for fine-grained per-object authorization: "can this specific user view this specific document under these specific conditions?" It would sit above Keycloak, consume the KC user / org / role state as bootstrap tuples, and enforce per-object policies the coarse model can't express. It is not currently implemented, not currently needed, and not blocking. The decision is gated on a concrete requirement — a specific customer demanding per-document gating, a specific regulator demanding attribute-based policies. Until then, the doctrine is: JWT claims from Keycloak, no separate authorization service.
INVARIANTThe cookie and middleware invariant
One operational invariant follows from "JWT is the only trust boundary." The iobox_auth cookie is never written from client code. There is no cookie-based middleware gating on the observation surface — the legacy HMAC cookie helper is dead code, retained only so that nothing accidentally wires it back up. Auth state is NextAuth's session cookie, mediated by the server-side middleware against Keycloak's JWT. If you need server-side auth state, read auth() directly. The JWT is the only trust boundary the platform recognises.
SEE ALSOWhere to go next
- identity — the Keycloak realm and projection that produces the JWTs read here.
- tenancy & federation — the federation grant primitives that permit cross-Organization access.
- the name — XX — the philosophical framing of the mesh and why permissioned sharing is the shape real businesses use.