Skip to main content

Concepts · 6 of 7

Capabilities and bindings

Protocol actions are tool-agnostic. Capabilities carry authority; bindings reach real systems.

Decision record 0083, with each status checked against Canon and engineering protocols code

An action such as metrics.inspect must reach one concrete system (one cluster, one namespace, through one tool) without the protocol ever naming that tool. Decision record 0083 splits the path into four layers, each named once.

Shipped · runs today

  • ShippedTool-agnostic action idsincident.response/1 declares metrics.inspect, logs.search, release.rollback; no tool appears in it.
  • ShippedAn action declares the capabilities it needsCanon evaluation admits an action only when every capability it requires is granted.

Decided · recorded, not built

  • DecidedScoped capabilities and grantsmetrics.read{cluster, namespace}. Canon's capability requirement holds no scope yet.
  • DecidedPrecedence deny > require_approval > allow, default denyCanon's authority input decides granted or denied today.
  • DecidedConnection catalog in ConnectorsThe catalog entry format is open.
  • DecidedRuntime binding in CommissionResolved per invocation, narrowed by the case's current grant.
  • Decidedscope.widen as an action that needs authorityThe case records every widening.

Planned · roadmap only

  • PlannedNarrowed credentialsA per-case credential that cannot reach beyond the scope.

Four layers​

LayerExampleOwnerChanges per deployment?
Action (protocol, tool-agnostic)metrics.inspect, logs.searchengineering protocolsno
Capability (semantic, scoped)metrics.read{cluster: prod-eu, namespace: foo}the protocol declares it; the case grants itno
Data contractseries/v1alpha1, logs/v1alpha1Connectorsno
Connectionone metrics store, one log storethe operator, in the Connectors catalogyes

The rule: no layer above the connection names a tool.

Capabilities​

A protocol action declares the capability it needs. Today that is a name:

protocols/incident-response/1.yaml (excerpt)
actions:
release.rollback:
effect: write
requires:
- capability: release.rollback

Canon decides nothing about who holds it: whoever runs an evaluation passes in which capabilities are granted or denied, and an action is admissible only when every capability it requires is granted. Decided but not yet built: capabilities carry a scope, a case holds scoped grants derived from its subject, and the precedence is deny over require-approval over allow, with nothing granted by default.

Bindings​

For each invocation, right before it acts, Commission resolves capability → data contract → connection from the operator's catalog, narrowed by the case's current grant. The catalog entry format is still open; a sketch:

catalog sketch (illustrative, format open)
deployment: prod-eu
capabilities:
metrics.read:
contract: series/v1alpha1
connection: metrics-prod-eu
scope_keys: [cluster, namespace] # what a grant may narrow on
logs.read:
contract: logs/v1alpha1
connection: logs-prod # another deployment binds another log store
scope_keys: [cluster, namespace]
topology.read:
contract: resource_discovery/v1alpha1
connection: cluster-prod-eu
scope_keys: [cluster]

Where scope comes from​

  1. Subjectthe request

    The intent router proposes a subject: cluster, namespace, symptoms.

  2. Grantthe case

    The case grants metrics.read and logs.read on that subject only.

  3. Discovertopology.read

    Names of services, no metrics or logs, to find what serves the symptom.

  4. Widenscope.widen

    Reading another namespace is an action that needs authority, and is recorded.

Enforcement, top to bottom​

CheckWhere
The action is admissible nowCanon
The granted capability covers the requested scopeCommission, before it invokes
The query stays inside the scopethe Connectors adapter, by native query containment
The credential cannot reach beyond the scopenarrowed credentials, later

The model never sees connection names or credentials. It sees metrics.inspect with arguments whose schema carries no scope fields it could change.