Skip to content

Capability tokens

A capability token in Matrix is an unforgeable reference to a right. The interface is ICapabilityToken in projects/matrix-3/packages/core/src/core/security/ICapabilityToken.ts. The model follows Mark Miller's capability rules: holding a capability = having the right; capabilities can be attenuated but never amplified; delegation creates chains.

The shape (verbatim)

typescript
// projects/matrix-3/packages/core/src/core/security/ICapabilityToken.ts:40
export interface ICapabilityToken {
  /** Unique token ID (e.g., 'cap-1702500000000-abc123') */
  id: string;

  /** Who minted this capability (realm mount path) */
  issuer: string;

  /** What this capability grants access to (mount path or pattern) */
  subject: string;

  /** Rights granted - typed for safety, extensible for custom namespaces */
  rights: CapabilityRight[];

  /** Additional restrictions (e.g., { maxCalls: 100, expiresAfterUse: true }) */
  caveats: Record<string, unknown>;

  /** Timestamp when issued (ms since epoch) */
  issuedAt: number;

  /** Timestamp when expires (ms since epoch) */
  expiresAt: number;

  /** Parent token ID if delegated (for chain verification) */
  parent?: string;

  /** Cryptographic signature (Phase 2+ - not implemented in Phase 1) */
  signature?: string;
}

Rights

The rights catalog (verbatim from ICapabilityToken.ts:23):

typescript
/** Well-known capability rights (compile-time checked) */
export type WellKnownRight = 'read' | 'write' | 'spawn' | 'delegate' | 'invoke' | 'emit';

/** Custom namespace-prefixed rights (extensible) */
export type CustomRight = `${string}:${string}`;
RightWhat it allows
readread state / receive events from the subject
writemutate state / publish to the subject
spawncreate child actors under the subject
delegatemint sub-capabilities derived from this one
invokecall ops via request/reply
emitpublish events
<ns>:<verb>custom namespaced right (e.g., acme:approve-invoice)

The well-known rights are compile-time checked. Custom rights use the namespace:verb shape — "'wirte' fails. 'acme:approve' passes."

Caveats

The free-form caveats map carries restrictions:

CaveatMeaning
maxCalls: numberusable only N times before expiring
expiresAfterUse: trueone-shot
boundToCorrelation: <cid>usable only for one specific correlation id
boundToSession: <sessionId>usable only within one session
<actor-defined>: <value>actor-specific restriction

The actor enforcing the capability checks both the rights and the caveats. The framework does not enforce caveats automatically.

Delegation chains

A capability can be delegated by minting a child token whose parent: <id> points to the original. Chain rules:

  1. The child's rights are a subset of the parent's (attenuation, never amplification).
  2. The child's caveats are at least as restrictive as the parent's.
  3. The child's expiresAt is at most the parent's expiresAt.
  4. The chain is verified at use time by walking parent links.

Status: target state, partial. Phase 1 does not yet implement cryptographic signatures (signature field). The token is currently trusted by reference (the issuer's actor identity is enough). Phase 2 introduces signing for cross-process delegation.

How tokens are used today

The MatrixActor base class accepts a _factCapability: ICapabilityToken | null field used by the blackboard layer (MatrixActor.ts:413). Most ops do not use capability tokens directly; authorization today is per-actor and per-op (the actor decides whether to honor the request based on the caller's principal).

Where tokens DO appear:

  • Fact-plane emit on the blackboard requires a _factCapability.
  • Cross-actor delegation through system.security (target).
  • Marketplace-paid blackboard reads (target — payment.required emits).

For most actor code, the relevant security model is:

  1. The transport metadata identifies the principal (no payload trust).
  2. The actor decides per-op whether to honor.
  3. Cross-tenant requests are blocked by NATS account ACL on the wire, not by tokens.

Capability tokens are the layer above this for fine-grained delegation and attenuation.

Why capability semantics

The Miller model is the canonical pattern for delegation in actor systems (see Mark Miller's Robust Composition). Three properties Matrix relies on:

  • Designation = authorization. If you have the token, you have the right. There is no separate ACL table to consult.
  • Confused-deputy resistance. A capability cannot be tricked into acting beyond what it grants. Unlike an ambient-auth ACL check, the actor doesn't need to know who is asking — only what the token allows.
  • Composable least privilege. Delegating a sub-capability with attenuated rights is the natural way to give code "just enough" auth.

Anti-patterns

  • Don't put a capability in a payload. The transport-metadata principal is what authorizes. Tokens travel as actor-state references (_factCapability), not as untrusted JSON fields.
  • Don't treat id as authoritative. The full token (including rights, caveats, expiry, parent) must be verified. Just the id is not enough.
  • Don't skip expiry. A token without an expiry check is effectively bearer-permanent.
  • Don't sign-then-trust without checking the chain. Even with signatures (target state), the parent chain must be walked.

See also