Extensions and Capability-Gating#

Alongside the message, every operation carries typed extensions: the contextual state policy reasons about. Identity is an extension. So are security labels, the delegation chain, request headers, agent session context, and more. Each extension is bridged into the flat attribute bag APL reads, under a well-known namespace. Capability-gating controls which plugins may read or write each one.

This is a supporting concern, not the headline. You rarely configure it directly. It matters because it is what makes least privilege real for the plugins that execute policy effects, and because the namespaces below are the exact keys an APL predicate or plugin can read.

The extensions#

Each extension flattens into bag attributes under its namespace, gated by a read capability. A prefix ending in . matches any key beneath it (role. matches role.hr); a bare name is an exact key.

ExtensionCarriesBag namespaceRead capability
Security (subject)subject id and type, roles, permissions, teams, claims, authentication statussubject.id, subject.type, authenticated, role.*, perm.*, subject.teams, team.*, claim.*read_subject, read_roles, read_permissions, read_teams, read_claims
Security (client)OAuth application identity: client id, trust level, roles, permissions, scopes, audiences, teams, claimsclient.*read_client
Security (workload)attested workload identity (SPIFFE / mTLS) for this host and the inbound callerworkload.*, caller_workload.*read_workload
Security (labels)taint / classification labels for information-flow controlread directly from the extension (not materialized into bag keys)read_labels, append_labels
Delegationdelegation depth, delegated flag, origin and actor subjects, chain agedelegation.*, delegatedread_delegation, append_delegation
Agentsession, conversation, turn, and lineage contextagent.*read_agent
Metaentity metadata: type, name, tags, scope, propertiesmeta.*read_meta
Requestenvironment, request id, timestamp, trace and span idsrequest.*read_request
HTTPrequest and response headers (lowercased)http.request_headers.*, http.response_headers.*read_headers, write_headers
LLMmodel id, provider, capabilitiesllm.*read_llm
MCPtool, resource, or prompt metadatamcp.* (mcp.tool.*, mcp.resource.*, mcp.prompt.*)read_mcp
Completionstop reason, token counts, model, latencycompletion.*read_completion
Provenancesource, message id, parent idprovenance.*read_provenance
Frameworkagentic framework name and version, node and graph ids, metadataframework.*read_framework
Customfree-form host-defined namespacecustom.*read_custom
Raw credentialsinbound tokens and minted delegated tokensflow through plugin payloads, not the bagread_inbound_credentials, read_delegated_tokens

The request arguments and response body are also flattened, under args.* and result.*, and the route name is available as route.key. APL field pipelines (args: / result:) operate on those.

Capabilities#

A plugin declares the capabilities it needs. CPEX filters the extensions before handing them to the plugin, so a plugin sees only what it declared. The default is no access; capabilities are additive grants.

plugins:
  - name: audit-log
    kind: audit/logger
    hooks: [cmf.tool_pre_invoke]
    capabilities:
      - read_subject
      - read_client
      - read_delegation

Read capabilities and the bag keys they unlock#

CapabilityUnlocks
read_subjectsubject.id, subject.type, authenticated
read_rolesrole.* (plus the read_subject baseline)
read_permissionsperm.* (plus baseline)
read_teamssubject.teams (plus baseline; team.* mirrors teams)
read_claimsclaim.* (plus baseline)
read_clientclient.*
read_workloadworkload.*, caller_workload.*
read_delegationdelegation.*, delegated
read_agentagent.*
read_metameta.*
read_requestrequest.*
read_headershttp.request_headers.*, http.response_headers.*
read_llmllm.*
read_mcpmcp.*
read_completioncompletion.*
read_provenanceprovenance.*
read_frameworkframework.*
read_customcustom.*
read_labelsno bag keys; the plugin reads labels from the security extension directly
read_inbound_credentialsno bag keys; gates raw inbound tokens in the plugin payload
read_delegated_tokensno bag keys; gates minted tokens in the plugin payload

read_roles, read_permissions, read_teams, and read_claims each imply the read_subject baseline (subject.id, subject.type, authenticated). The last three capabilities gate state that is not materialized into bag keys: labels are read from the extension, and credential material flows through plugin payloads rather than the bag, so granting them does not widen what an APL predicate can read.

Write capabilities#

Three capabilities grant write tokens rather than read access:

CapabilityGrants
append_labelsadd a taint label (monotonic; cannot remove)
append_delegationextend the delegation chain (monotonic)
write_headersrewrite request and response headers (implies read_headers)

Mutability tiers#

Extensions differ in how they may change during a request, and the runtime enforces the tier:

  • Immutable: fixed once resolved. The verified subject identity, client, workload, agent, meta, request, LLM, MCP, completion, provenance, and framework extensions.
  • Monotonic: may only grow. Security labels (added via append_labels, never removed) and the delegation chain (extended via append_delegation).
  • Mutable: may be rewritten. HTTP headers (via write_headers) and the custom namespace.

So a plugin cannot clear a taint label or rewrite a verified identity even if it holds the corresponding read capability. This is what keeps the state APL depends on trustworthy: the model is untrusted, and so is any plugin beyond the context and mutations it was explicitly granted.

How it connects to policy#

Capability-gating runs at the boundary between the manager and each plugin (filter_extensions in cpex-core decides which extension slots a plugin sees; the CMF extractors then flatten those slots into the bag). The same filtered, tier-enforced view feeds the attribute bag APL evaluates, so a policy and the plugins it invokes operate on a consistent, least-privilege picture of the request. See Identity for how the subject is populated and Session Tainting for the monotonic label tier in action.