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/--budgetbound 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 landswarn:trust:cap_refused.--tokensis 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.
supervisedandautonomouspresume an owner. A grant at either tier to an unowned agent refuses by name with the one-verb fix in the refusal (step 3).advisehas no owner requirement.--release-stage prodwould 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 felixname the owner, and doopschain admin trust assignmentsandopschain admin trust listshow the inventory and an advise-tier grant scoped to your project? The Academy'senrol-and-grantexercise reads exactly that sequence —info:agent:registered,info:agent:owner_changed,info:trust:assigned,info:trust:grantedon your project. Remember:agent ownertakes the member'socr_id, and a raise to supervised or above needs approvers. - Verified version:
opschain academyruns theenrol-and-grantexercise 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:registeredrecords it. - An owner is accountability and routing, never privilege — and a
supervisedorautonomousgrant 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).