Skip to main content
Version: v5.7.0

12.2 Enrol, own, assign, grant, ceiling

packs:agentspacks:agent-governance

After this you can: enrol Felix with a one-use token, give it an owner, scope it with assignments, empower it with tiered grants, and read the type ceiling nothing exceeds.

Before you start — tick each one:

  • Lesson 12.1 — What an agent is; the two kinds (opschain learn 12.1)
  • Lesson 7.2 — Users, groups, rules, policies (opschain learn 7.2)

In the GUI: the agent sheet on Operations → Agents assigns the owner in place; Governance → Agents → Trust lists the grants with their approval state, the assignments and the ceilings, and an administrator writes grants (the agent's own sheet pre-addresses one), assigns, and sets ceilings there — the same verbs as this lesson's CLI; a held trust raise waits on Inbox → Approvals.

This is the hire. By the end Felix is enrolled in limebank, owned by release_managers, assigned the prod environment, trusted to request patching there under supervision, and bounded by a ceiling no grant can exceed — every step with the output you should see and the event it lands. You need a server target and a superuser login (lesson 0.1), packs:agents for the enrolment, and packs:agent-governance for the grants and ceilings (the entitlement lines are opschain docs packs-agents and opschain docs packs-agent-governance; the kill switches of lesson 12.5 stay base).

1. Mint a one-use token​

Enrolment is token-first, like a runner's: mint a short-lived registration token scoped to the project and the agent code, and hand it to the agent process. One use, one hour:

opschain admin agent-token create --project limebank --code felix --ttl 1h --max-uses 1

The token value prints alone on stdout, the expiry on stderr — capture it now, because the server never returns it again:

<token>
expires at 2026-10-14T04:12:09Z

opschain admin agent-token list shows the row by fingerprint with USES 0/1 and status active; possession of the token cannot enrol outside its project, group or code.

2. Start Felix​

Start the agent process with the token. The quickest live instance is the platform's own runtime, which serves the five minimum surfaces (healthz, readyz, info, metrics, logs) through the tunnel; the demo kit's Felix is an SDK agent that declares type: service and serves its real capabilities the same way (lesson 12.7):

opschain agent --server https://opschain.example.com --token <token> --code felix --dir ./felix

The process logs its registration and then channel open (stream) (or (long-poll)), and stays in the foreground. From another shell:

opschain admin agents list --project limebank
PROJECT CODE NAME ID GROUP ONLINE LAST SEEN CHANNEL STATE RELEASE LANE PROVIDER
/limebank felix felix 01999a7e-… default ✓ 2026-10-14T03:12:09Z stream ready - stable -

The ID column is the member id every opschain admin agent verb takes; felix works as a bare code because exactly one project holds it. The server landed info:agent:registered on the agent's own path /limebank/agents/felix, carrying the instance id and lane. A second start with the same token is refused: the token is exhausted (the list now reads USES 1/1, status exhausted).

Now opschain admin agent show felix answers: ID, CODE, PROJECT, GROUP, STATE ready, ONLINE ✓, the key fingerprint, and SERVICE ACCOUNT svc-agent-limebank-felix. TYPE reads service when the manifest declared it (the SDKs and opschain agent dev --type do; the bare runtime declares none and reads -); OWNED BY reads -, which is the next step.

3. Name the owner​

Enrolment is not finished until the agent names its owner — the accountable human or team its escalations route to. An agent may declare it in its manifest (opschain agent dev --owned-by, the SDKs' owner option); assign it administratively when it did not, or when custody moves:

opschain admin agent owner felix --owned-by release_managers

The answer is a machine document with the agent id and the stored owned_by; the event is info:agent:owner_changed. The owner must be a known user or group in the identity catalog — an unknown name refuses by name. Ownership is accountability and routing only: it never derives a privilege, and it rides the agent's warn events (warn:agent:halted, warn:agent:request_refused) so the owning team hears first. An unowned agent still works, but the fleet views flag it, and step 5's supervised grant would refuse until an owner is set.

4. Enrolled is not empowered — the assignment​

Felix can serve its surfaces and nothing more: a request for work with no matching grant is refused with No trust grant covers <path> for action "<action>": this agent is not trusted to request work here, and the refusal lands warn:agent:request_refused.

Capability arrives in two steps. The assignment records Felix's inventory — the subtree it is the expert for — and confers only the configured floor (agents.assignment_floor, advise by default):

opschain admin trust assign --agent felix --project limebank \
--scope /projects/limebank/environments/prod

You get the stored assignment back as a machine document, including the ID that opschain admin trust unassign takes; the event is info:trust:assigned. Felix may now recommend within prod — and read what its service account's policy assignments allow — nothing more.

5. Real power — the trust grant​

A trust grant is subtree-scoped, action-patterned, tiered and capped:

opschain admin trust grant --agent felix --project limebank \
--scope /projects/limebank/environments/prod \
--action 'patch_*' --tier supervised \
--rate 6 --budget 20 \
--note "BAU patching, sized for the quarterly cycle"

The answer is the stored grant — id, subject, scope, actions, tier, rate, budget, tokens, status, note. What the fields mean:

  • Tier is the autonomy boundary: advise (recommend only), supervised (its changes gate in the approval funnel), autonomous (runs unattended within scope).
  • --rate / --budget bound the loop: governed work requests per minute and invocations per UTC day under this grant. An over-cap request refuses loudly, naming the grant, the cap and the window reset, and lands warn:trust:cap_refused. --tokens is the third cap, model tokens per UTC day, for the AI job in lesson 12.6.
  • The most specific scope wins; ties resolve least-permissive. "Autonomous in dev, supervised in prod" is two rows, not a flag.
  • supervised and autonomous presume an owner. A grant at either tier to an unowned agent refuses by name with the one-verb fix in the refusal (step 3). advise has no owner requirement.
  • --release-stage prod would additionally admit only instances whose signed release sits at or above that rung — lesson 12.4.

Raising a tier is a ceremony, not an edit. On an estate with the approval fabric, every grant write naming supervised or autonomous is a trust raise: the grant lands with status: pending, the event is info:trust:raise_held, and a strict checklist opens in the one approvals inbox — approvers from the grant's own --approver list, else agents.approvers in the server configuration (with neither, the write refuses: a governed raise is never approver-less). Release it there:

opschain approvals list
opschain approvals approve <grant-id>
opschain admin trust list

approve activates the grant (info:trust:grant_approved); reject revokes it (info:trust:grant_rejected); an unanswered quorum TTL lapses it. The list now shows Felix's row with TIER supervised and STATUS active. Without the fabric composed, the grant lands active directly and the event is info:trust:granted. Either way an unapproved raise never takes effect.

6. The ceiling — the boundary that stays​

Grants compose with a per-type tier ceiling: effective tier = min(grant tier, type ceiling). The ai type ships ceilinged at supervised:

opschain admin trust ceilings
opschain admin trust ceiling ai --ceiling supervised --note "shipped default reaffirmed"

ceilings lists the rows; the write answers the stored row (type, ceiling, note, updated_by, updated_at) and lands info:trust:ceiling_set with the previous value. Ceiling writes are admin-gated and step-up floored (trust.ceiling_write); lowering one clamps every covered agent on its very next request, with grant_tier and tier_ceiling stamped beside applied_tier in the request event and the run's metadata, so the audit explanation is always spelled out.

Ceilings also key on node classification (lesson 11.1): a rule caps every agent at nodes whose effective metadata matches its predicates, so "critical systems never see autonomous agents" is one row rather than per-agent bookkeeping — effective = min(grant tier, type ceiling, every matching node ceiling):

opschain admin trust node-ceiling set --where 'core.criticality=critical' \
--ceiling supervised --note "critical estate stays supervised"
opschain admin trust node-ceilings

The write lands info:trust:node_ceiling_set with the rule; matching reads the compiled metadata snapshot on each request, so classifying a node moves the clamp on the next request, with node_ceiling stamped beside applied_tier.

Felix is hired: enrolled, owned, assigned, trusted under supervision in prod, and ceilinged. Lesson 12.3 reads what it does with that.

Check yourself​

  • What can Felix do straight after enrolment, and what refuses when a grant exceeds the type ceiling?
  • In order, which events does this walk land on Felix's path: the enrolment, the owner, the assignment, the grant (held, then approved — or granted directly without the fabric) and the ceiling?
  • Does opschain admin agent show felix name the owner, and do opschain admin trust assignments and opschain admin trust list show the inventory and an advise-tier grant scoped to your project? The Academy's enrol-and-grant exercise reads exactly that sequence — info:agent:registered, info:agent:owner_changed, info:trust:assigned, info:trust:granted on your project. Remember: agent owner takes the member's ocr_ id, and a raise to supervised or above needs approvers.
  • Verified version: opschain academy runs the enrol-and-grant exercise against this lesson.

What you learned​

  • Enrolment is a one-use, project-and-code-scoped token redeemed by the agent process; the fleet lists one row per instance and info:agent:registered records it.
  • An owner is accountability and routing, never privilege — and a supervised or autonomous grant refuses until one is set.
  • An assignment confers only the advise floor; a tiered, capped, subtree-scoped grant confers power, and raising a tier is held in the approvals inbox.
  • The type ceiling and the node-classification ceilings cap every grant; the effective tier is the minimum, stamped into every decision's audit.

Next: lesson 12.3 — Evidence: activity, trust review, transcripts (opschain learn 12.3), or back to your track (opschain learn).