Appearance
Authorization
Authentication tells the platform who a request is from. Authorization tells the platform what they can do. This page documents the present-state model (single check: principal ownership) and the target capability/role model.
Present model: principal ownership
Every Space has an ownerPrincipalId. Every op that touches a Space verifies the calling principal matches.
Examples in code:
ts
// auth.hostLink.create — system-auth/src/index.ts:736
if (!space || space.ownerPrincipalId !== principalId) {
return { ok: false, error: 'Host link requires a Space owned by the principal' };
}
// auth.space.default — system-auth/src/index.ts:696
if (!space || space.ownerPrincipalId !== principalId) {
return { ok: false, error: `Space not found for principal: ${nextSpaceId}` };
}This is the entire authorization model today. A principal:
- Can read their own data.
- Can mutate their own data.
- Can read public data.
- Cannot read other principals' private data.
There is no:
- Read-only delegation.
- Time-bounded grants.
- Per-op permission ("this principal can read but not revoke").
- Org-level role.
- Admin role distinct from "principal that owns the platform Host."
Anonymous and local-client
Two identities are special-cased:
- Anonymous (
authenticated: false): can read/healthz,/api/apps, public namespace claims, and not much else. No NATS bus token. - Local-client (
localClient: true, principalId: 'host-service'): bypasses every check. Can do anything. This is the local-CLI / supervisor identity.
The local-client identity is the only "operator-like" path today. There is no separate platform-admin role distinct from local-client.
What an admin needs (target)
A platform admin needs to:
- List all principals.
- Revoke any Host Link, not just their own.
- Suspend any Space.
- Read audit logs across principals.
- Apply rollouts to fleets.
Today these are reachable only from a local-client connection on the platform Host. There is no "admin: true" flag on a principal record. Target state.
Capability-based vs role-based
The architectural target is capability-based authorization (per ARCHITECTURE-SCOPED-CAPABILITY-RESOLUTION.md in ARCHITECTURE/), not RBAC. The intuition:
- A capability is a token that says "the bearer may invoke
<op>on<resource>for<duration>." - Granting capability = minting and delivering a token.
- Revoking capability = adding the token id to a revocation list.
- Capabilities compose (you can hold multiple).
- Capabilities can be delegated (with appropriate parent capability).
The architecture document is mature; the implementation is not.
Why not RBAC
RBAC ("admin," "member," "viewer") is simpler to implement but harder to scope. A capability "read-only access to Space spc_xxx for 24 hours" cannot be expressed cleanly in RBAC without proliferating roles. Matrix's federation model (cross-realm published actors) is naturally capability-shaped.
That said, a small RBAC-flavored layer for the launch is acceptable as long as it doesn't preclude the capability target. The launch admin model could be:
principal.role: 'user' | 'platform-admin'.- All ops gate on
role === 'platform-admin'for cross-principal access.
This is implementable in 200 lines of code and unblocks the platform-admin dashboard. Target state, but tractable.
What enforcement looks like today
- At the gateway:
auth.identity.resolveruns on every HTTP request, attaching identity to the actor invocation. - In the actor: each op decides for itself. There is no central authorization middleware. The pattern is
if (calling.principalId !== resource.ownerPrincipalId) reject.
Decentralized authorization is correct for an actor system, but it means every op author must remember the check. A test seam (scripts/audit-device-identity-schema.ts and similar) keeps this honest for Device-related ops. A more general "authorization audit" tool is a target.
Cross-Space federation
A principal's actors can publish to system.registry. Other principals can discover and invoke published mounts. This is federation, not authorization — the published actor decides for itself whether to honor a cross-realm invocation. The platform does not enforce a cross-realm authorization layer.
A legitimate target use case: an org Space publishes a chat.support actor. Anyone can invoke chat.support and get rate-limited responses. The actor decides who gets full responses based on its own internal policy.
See also
- Authentication — who is calling.
- Capability tokens — token primitives that exist today.
- Devices / Device grants — per-Device scope strings.
- ARCHITECTURE/ARCHITECTURE-SCOPED-CAPABILITY-RESOLUTION.md — target capability model (architecture document).
Source:
projects/matrix-3/packages/system-auth/src/index.ts:719-757is the canonical example of the present-state ownership check.ARCHITECTURE-SCOPED-CAPABILITY-RESOLUTION.mdis the design target.