Appearance
Capability tokens
A capability token is a credential that says "the bearer is authorized to do X in scope Y." It is distinct from an identity token, which says "the bearer is principal Z." Identity is verified once; capability is enforced on every operation.
The reference implementation: the C# bridge realm
The substrate's working capability realm lives in BridgeSecurityRealm.cs. Both com-matrix-bridge and win32-matrix-bridge use it to gate every COM and Win32 invocation. The shape mints, attenuates, revokes, and audits — Mark Miller's full discipline:
text
mint(target, rights, ttl, caveats) -> CapabilityToken
attenuate(parent, removeRights, expiresAt, addCaveats) -> CapabilityToken (strict subset)
verify(token, action, target) -> ok | denied(reason)
revoke(tokenId)
audit(every step)Mint requires either an admin secret or an existing admin capability — minting is itself a privileged operation. Attenuate is monotonic: rights can only be removed, expiry can only shrink, caveats can only be added. Verify checks revocation, expiry, target match, and required-right presence on every call. Revoke is effective immediately because verify hits the revocation set on every call. Audit records principal, action, target, sourceRealm, targetRealm, outcome, and (on denial) a denialReason.
This is the substrate's mental model. The TypeScript-side expansion is filed as target state, and the bridge realm is what it follows.
TypeScript side — what's enforced on the bus today
The codebase has a partial capability model on the TypeScript side. The structure is in place but per-op enforcement is not. Specifically:
IHostLinkRecord.scopes: readonly string[]records capabilities attached at pairing time.HostPrincipalStore.listGrants(principalId)returns a fixed list (identity,bus,prompt,install,invoke) for active principals.HostPrincipalStore.hasCapability(principalId, _scope, _capability)exists but only checksprincipal.status === 'active'. Both_scopeand_capabilityarguments are ignored.- The policy engine (
PolicyPresets.ts) op-gates with thestrict/standard/lockdownpresets — this is op-name gating, not identity-and-capability gating, but it is the live per-op enforcement seam today.
There is no separate "capability token" wire format on the bus today. The closest thing is the bus token (HostSessionService.createBusToken()), which carries purpose: 'bus' but no capability list.
Bus token (today)
ts
// projects/matrix-3/packages/system-auth/src/host-auth.ts:219-221
createBusToken(principalId, extra): IHostSessionToken {
return this._createToken(principalId, BUS_TOKEN_DURATION_MS, extra, 'bus');
}The bus token has:
ts
{
sub: principalId,
email?: string,
displayName?: string,
iss: string, // configured issuer
iat: number,
exp: number, // iat + 5 minutes
jti: string,
purpose: 'bus',
}It authenticates to NATS but does not carry capabilities. NATS account permissions (per the operator/JWT account configuration) provide the authorization layer: subject permissions are scoped to the principal's account.
Target state — substrate-side capability tokens
A future capability token would look like:
ts
{
sub: principalId,
iss: '<issuer>',
iat: number,
exp: number,
jti: string,
purpose: 'bus' | 'invoke' | 'install',
caps: [
{ scope: 'space.alt.stories', capability: 'invoke' },
{ scope: 'host.hostlink_abc', capability: 'install' },
{ scope: 'mount.system.inference.openai', capability: 'prompt' },
],
attenuated_for?: 'hostlink_abc', // links the token to a Device
}Tokens of this shape would be attenuable: a parent token with broad caps could mint a child with a strict subset, and the child holder could not exceed the parent's caps. The bus or HTTP gateway would enforce on each op. Revocation would be effective immediately because the token's jti is checked against a revocation set on each verify.
This is the C# bridge realm shape, lifted onto the bus.
Why not today
Two reasons capability tokens haven't been implemented on the bus side yet:
- Status-based gates plus NATS account isolation plus policy presets cover most of the threat model. Cross-tenant leakage and "anyone can act as anyone" are blocked. The remaining gaps (per-op fine-grained authorization, intra-tenant attenuation) are not the highest-priority security risks today.
- Schema choice matters. Picking the wrong cap-token shape early creates compatibility debt. The current design space includes:
- macaroons (caveat-based attenuation)
- bearer JWTs with
capsclaims - NATS-account-permissioning extension
- explicit capability handles (lookup table on the cloud)
Each has different revocation, offline-verification, and issuance properties. A target-state spec needs to commit before code lands.
Status: target state — v1+. Per-op capability gates and the substrate-side capability token wire format are not yet implemented. Tracking item TBD; the C# bridge realm is the working precedent.
What is enforced today
| Authorization check | Where | Mechanism |
|---|---|---|
| "Is this principal allowed to bus-connect?" | NATS server | account JWT permissions (operator mode) |
| "Is this principal active vs suspended?" | system.auth ops | principal.status === 'active' |
| "Is this Device's bearer token valid?" | HTTP heartbeat endpoint | verifyHeartbeatToken SHA-256 + status-active check |
| "Does this Host Link own this hostId?" | HTTP heartbeat endpoint | Host Link record's hostId field |
| "Is this actor mount visible to this caller?" | NATS subject permissions | account JWT permissions (operator mode) |
| "Is this op declared on this actor?" | actor framework | static accepts declares accepted ops; framework rejects unknown ops |
| "Is this op allowed by the operating-posture policy?" | policy engine | DeclarativePolicyEngine evaluates the loaded preset (standard / strict / lockdown) |
| "Is this COM/Win32 right held by the caller?" | BridgeSecurityRealm.Verify | full mint/attenuate/revoke/audit cycle (C#) |
The "policy engine" gate is the closest thing to per-op authorization on the bus today, but it is preset-defined op-name gating, not identity-and-capability gating. The C# bridge realm is the substrate's only true capability gate today.
Cap-token interaction with refresh
For long-running browser sessions, the browser fetches a fresh bus token before each NATS reconnect. The bus token's 5-minute TTL caps the window of validity if the principal is suspended mid-session. Capability tokens (target state) would inherit the same short-TTL pattern.
See also
- Capability model — conceptual framing and the C# bridge reference.
- Topic claims — NATS subject permissions, the layer that carries capabilities today.
- Reference / Token schema — wire shapes.
- Security model — policy presets and trust-surface doctrine.
Source:
BridgeSecurityRealm.cs(reference capability realm);projects/matrix-3/packages/system-auth/src/host-auth.ts(HostSessionService,HostPrincipalStore,IHostLinkRecord);PolicyPresets.ts(live policy engine).