Design: Capability Patterns

Prerequisites

Scope

This document defines Trellis capability naming, contract-authored capability metadata, and role/capability usage patterns.

Capability Model

Contracts declare capability requirements on RPCs, operations, events, and feeds. The owning contract may also declare human-facing metadata for each owned capability so planning and approval UIs can explain the requested authority without inventing a separate scope catalog. Accepted deployment authority stores the runtime capability view projected from those contracts. Deployments grant capabilities through roles, groups, grant overrides, or external identity mappings.

Rules:

  • contracts declare required capabilities on owned and used surfaces
  • event subscription capabilities authorize the logical event surface. Durable service event consumers require an additional eventConsumers resource binding and receive least-privilege JetStream consumer permissions from that binding rather than from broader capability grants.
  • contracts SHOULD declare top-level metadata for every capability they own
  • deployments assign capability bundles to users and services
  • deployments MAY also assign auth-owned dynamic capability overlays through grant overrides keyed by contractId + origin for web grants or contractId + sessionPublicKey for session-keyed grants
  • services receive deployment policy through deployment authority materialization and current materialized authority
  • authorization changes take effect after accepted authority is materialized or stored grants change; runtime auth derives transport permissions from current materialized authority and stored grants, not from active contracts
  • auth-owned self-service RPCs may intentionally require zero granted capabilities when ordinary authenticated user context is sufficient, such as Auth.Sessions.Me and Auth.Sessions.Logout
  • user, service, session, and grant projections store capability keys as strings; approval payloads carry capability metadata objects keyed by those strings

Grant overrides are deployment policy, not user-owned grants. They must not be copied onto the user projection, and they may be revoked dynamically so affected delegated sessions must reconnect and re-evaluate current policy.

Capability Naming

Capability names have two forms:

  • local capability names are authored inside the owning contract, for example users.read or admin.read
  • global capability keys are emitted into canonical manifests and grant records as <contract namespace>::<local capability>, for example trellis.jobs::admin.read

The contract namespace is the contract id with a trailing major-version suffix removed. For example, both trellis.jobs@v1 and trellis.jobs@v2 map to the capability namespace trellis.jobs. This keeps grants stable across intentional major contract-version upgrades when the capability meaning is preserved.

Rules:

  • contract authors SHOULD write local capability names in source contract files and let authoring helpers emit global keys
  • local capability names MUST NOT start with the owning contract namespace plus ., so trellis.core@v1 declares catalog.read rather than trellis.core.catalog.read
  • direct manifest authors SHOULD write global keys in canonical trellis.contract.v1 manifests
  • if a capability reference matches a declared top-level capability, tooling projects it to the global key in the emitted manifest
  • undeclared platform or external capability strings such as service and admin remain raw strings and are not rewritten
  • capability metadata belongs to the owning contract; other contracts reference used APIs by logical uses selections, not by redeclaring another contract’s capability metadata
  • admin capability catalogs come from Trellis platform capabilities plus authority-owned projected capability definitions, not from the active catalog alone
  • changing capability metadata changes what users are asked to approve and therefore changes the contract digest
PatternExampleMeaningWho Can Claim
<namespace>::<domain>.<action>trellis.auth::users.readCan read usersUsers, Services
<namespace>::<domain>.<action>graph::partners.writeCan mutate partnersUsers, Services
serviceBackend service principalServices only
adminAdministrative accessUsers, Services
<namespace>::<domain>.<action>trellis.jobs::admin.readRead jobs admin dataUsers, Services
<namespace>::<domain>.<action>trellis.jobs::admin.mutateMutate jobs admin stateUsers, Services
<namespace>::<domain>.<action>trellis.jobs::admin.streamObserve jobs admin streamsUsers, Services

Deployments may still encounter role-shaped strings such as users:read, but the architectural model is capability-oriented. New Trellis-owned contract capabilities should use dotted local names and global :: projection rather than colon-shaped role names.

Service-Only Requirements

Some operations require both:

  • the needed capabilities
  • a registered service identity

Auth enforces this using service identity plus a presented contract compatible with the materialized authority.

Future Direction

Richer capability bundles and role composition remain deployment policy concerns, not protocol surface.