Delegation & Attenuation
Hand an agent part of your authority and follow the four checks the kernel runs before a delegated child is admitted.
On this page
A coordinator can give a worker exactly the authority one task needs and keep the rest for itself. The kernel admits the worker’s delegated capability only when its scope fits inside a parent grant that permits delegation, the parent traces to authority the verifier already trusts, and the siblings together stay within the parent’s share.
Applications set the limits on the grant, as delegation: { depth, fanout } in the Quickstart. Beneath that call, each delegated token carries a signed link to its parent and a proof of the narrowing, and the kernel recomputes both before the child’s first call.
In the pricing project, fixer hands tester read-only access to /workspace to reproduce the flaky test in issue #416. The tester reaches for a write anyway, and the receipt for the refusal names both tokens in the chain:
receipt rcpt-01d2…e48a
request tester write /workspace/src/pricing.py
verdict deny: outside the grant
grant cap-01d2…6c13
chain cap-01d2…2a90 (fixer), cap-01d2…6c13 (tester)
policy sha256 61d0…a7f3
signed your kernel 4f1c…a2e9, signature verifiesThe fixer's grant covers the write; the tester's narrower grant does not, and the kernel decides against the token presented. Delegate Between Agents runs the whole exchange, including the revocation that ends the tester's token through its parent.
A delegated child is admitted only inside authority someone already holds
Delegation passes on authority a parent already holds; direct issuance creates authority from an accepted issuer. The kernel runs the delegation checks in a fixed order, and the share comes last.
Four checks pass, in order, before the child runs
The child keeps the parent’s /srv constraint, adds /srv/reports, and lowers the ceiling from ten invocations to three. The witness recomputes as a subset, the claimed parent matches the issuer’s trust root, the request matches the child’s scope and passes the guards, and only then does the registry commit the child’s share. What a child may inherit.
Delegate is a right on each grant. A parent must hold Delegate on the grant that covers the authority it passes on, so holding Invoke on one tool and Delegate on another authorizes nothing for the first. The check applies independently to tool, resource, and prompt grants.
The subset relation admits equality as well as narrowing. Swarm task graphs build on related checks through a different proof carrier, the swarm witness chain, with its own format and admission contract.
The verifier recomputes every narrowing from the signed witness
Three records describe a delegation, and the token’s signature covers all of them.
DelegationLink- Signed by the delegator: the parent capability ID, the delegator and delegatee keys, the declared reductions, a timestamp, and any scope or family bindings.
AttenuationProof- The parent and child scope hashes, plus a normalized witness.
AttenuationWitness- The normalized parent and child scope text, the subset relations, and any aggregate-budget or cumulative-approval markers.
The verifier hashes and parses the witness’s scope text and recomputes the subset relation itself; a witness that only asserts subset: true is refused. Schema validation binds the child hash to the token’s actual scope, so a witness cannot describe a smaller child than the token grants.
Witness fields and their separate checks
A relation explicitly marked false rejects the witness. restricted_predicates records mint-time diagnostic material, and the subset verifier reads it as data, never as a predicate language. Cumulative-approval markers must agree with the parsed child scope, and aggregate-budget bindings meet additional token-schema checks.
A child keeps every parent limit and may tighten any of them
ChioScope::is_subset_of requires every child grant to be covered by some parent grant, and the delegation check requires that covering grant to permit delegation. Each dimension then narrows by its own rule.
- Target and operations. The child stays within the parent’s target coverage and operation set.
- Invocation ceiling. A capped parent requires a child cap no larger than its own; an uncapped parent can delegate a finite cap.
- Cost. Per-invocation and total cost ceilings are equal or lower, in the same currency; the subset check converts no currencies.
- Proof of possession. A parent’s required DPoP stays required on the child.
- Constraints. The parent’s constraints carry over unchanged, and the child may add more.
- Cumulative approval. The budget ID, epoch, currency, and canonical root binding carry over, and the child threshold may decrease. A child cannot start a new unbound family.
A parent carrying PathPrefix("/workspace") can give a child both that constraint and PathPrefix("/workspace/tests"). Replacing the parent constraint with the narrower-looking string fails the structural preservation rule, because the parent’s constraint must survive as written.
Tool targets match an exact name or the whole * wildcard. Resource and prompt grants also accept a trailing-prefix wildcard by string comparison, which is textual: file:///workspace* covers file:///workspace-archive. Filesystem containment belongs to the filesystem guards and the host.
Inspect invocation-cap preservation
// If parent has an invocation cap, child must too and it must be <= parent
if let Some(parent_max) = parent.max_invocations {
match self.max_invocations {
Some(child_max) if child_max <= parent_max => {}
None => return false, // child is uncapped but parent is capped
Some(_) => return false, // child exceeds parent
}
}A narrowing must start from authority the verifier already accepts
A correct subset can still start from a fictional parent scope. Chain binding compares the proof’s claimed parent with authority the verifier already accepts: the issuer’s configured trust-root scope hash, reached directly or through one signed link.
Any token that carries an attenuation proof, scope attenuations, or a budget share requires the binding. An absent trust root, a required link hash that is missing, or disabled chain-binding negotiation refuses the attenuated token. A pass-through chain without attenuation fields meets the chain-shape checks only.
Portable chain-shape validation checks signatures, key connectivity, non-decreasing timestamps, and final subject continuity. Hosted admission adds the configured maximum depth and reads ancestor snapshots through the receipt store: parent IDs, subject and time continuity, delegable subsets, and declared reductions.
Multi-hop attenuation travels in swarm witness chains, which carry per-hop material and pass each subset proof to the same attenuation validator; their edge anchoring and issuer rules belong to the swarm verifier.
Siblings share their parent’s allocation, and the share is committed last
Two children can each fit their parent and still exceed it together. If the parent holds 5,000 basis points and two children each request 4,000, either fits alone; BudgetRegistry keeps a running sum of admitted shares and refuses the second.
Children divide the parent’s share
budget_share_bps gives each child a share of its parent in basis points, and 10,000 is a full share. Two admitted children hold 4,000 and 3,500, so 2,500 stays open. A grandchild’s 5,000 is half of its own parent’s share, never of the root’s. Shared capacity.
A full share is 10,000 basis points, the per-token ceiling, and a share above it fails closed. An omitted share requests the full ceiling. A grandchild’s basis points are a share of its own parent’s share, and attenuation checks each child against its own parent; the registry is what sums the siblings.
The registry admits shares for tokens with a non-empty delegation chain, keyed by the immediate parent capability ID from the last link; a root-issued token with a share field is not charged. Portable evaluation verifies authority, subject, scope, and guards before it commits the child’s share, and the hosted path separates the same two steps, so a validly signed token presented for the wrong request is refused before it consumes sibling authority.
Inspect the portable ordering
// Step 5: mutate delegated sibling budget only after every deny-capable
// local check has passed.
if let Err(error) = admit_delegated_budget(input.capability, budgets) {
let core_err = KernelCoreError::InvalidCapability(error);
return deny(core_err, Some(matched_grant_index), Some(verified));
}A witness proves containment; admission supplies the rest
Subset validation establishes that the supplied child scope fits within the supplied parent scope. Root binding, issuer trust, signed links, stored lineage, and runtime admission establish the facts needed to use that relationship. Current revocation status, available money, and completed execution come from the kernel’s state and receipts; the witness carries none of them.
Read more in the assurance model.
Delegation steps, chain shapes, and rejections
Search for ShortenExpiry, OversubscribedSiblings, PqRequired, or Lease.
Declared attenuation steps
Issuance validates both sides of a declared reduction: the parent must hold the targeted authority, and the child must reflect the reduction. Targeted steps name a server and tool pair; ShortenExpiry applies to the whole token.
RemoveTool- No child grant still covers the removed target.
RemoveOperation- No covering child grant retains the removed operation.
AddConstraint- Every covering child grant carries the added constraint.
ReduceBudget- Every covering child grant has an invocation cap at or below the declared ceiling.
ReduceCostPerInvocation·ReduceTotalCost- Every covering child grant has a same-currency cost cap at or below the declared ceiling.
ShortenExpiry- The child expiry is at or before the declared bound, which is itself at or before the parent expiry.
Overlapping grants
Parent step validation examines every covering grant and accepts when one of them justifies the declared reduction, past an unrelated or insufficient first match. Child reflection checks stop another overlapping child grant from retaining authority the declared step removes.
Inspect covering-grant validation
/// Accept a step when at least one covering parent grant satisfies the
/// variant-specific narrowing `predicate`, fail-closed otherwise.
///
/// Overlapping parent grants are order-independent: a broad `*:*` grant that
/// lacks the targeted operation (or cost cap) must not mask a later concrete
/// grant that holds it. Subset validation already accepts a child grant covered
/// by *any* parent grant, so step validation mirrors that by checking every
/// covering grant and accepting if any one of them makes the step a true
/// narrowing. When no covering grant exists at all, the step targets a tool the
/// parent never held: reject as a widening.
fn step_matches_any_covering_grant<F>(
parent_scope: &ChioScope,
server_id: &str,
tool_name: &str,
predicate: F,
reason: impl FnOnce() -> String,
) -> Result<()>
where
F: Fn(&super::scope::ToolGrant) -> bool,
{
let mut covered = false;
for grant in covering_parent_grants(parent_scope, server_id, tool_name) {
covered = true;
if predicate(grant) {
return Ok(());
}
}
if !covered {
return Err(Error::AttenuationViolation {
reason: format!(
"attenuation step targets tool {server_id}:{tool_name} not present in parent scope"
),
});
}
Err(Error::AttenuationViolation { reason: reason() })
}Chain shapes on the resolver path
- Attenuated, no links
- The claimed parent scope hash binds to the issuer’s configured trust-root scope hash.
- Attenuated, one link
- The signed link’s scope hash must match the trust root, and the leaf proof binds to that link.
- Attenuated, multiple links
- Refused: the inline token format carries no per-hop child-scope witnesses, which this verifier requires. Swarm witness chains carry them.
- Pass-through, no attenuation fields
- Chain-shape checks apply; a chain vector alone does not trigger the attenuation-root rule.
The fixed-hash verifier has a narrower contract: it checks chain shape and leaf binding, and the full resolver path supplies the per-link root checks. The full verifier sets no maximum depth for the portable chain walk; hosted admission enforces the configured depth.
Signature floor
Floor-aware verification checks the optional algorithm label against the encoded signature, applies the configured floor, then verifies schema and cryptographic material. AllowClassical accepts classical signatures, AllowHybrid accepts classical or hybrid signatures, and PqRequired requires hybrid signatures; both branches of an accepted hybrid signature must verify. The algorithm reference gives the full table.
Hybrid verification needs the post-quantum build support and signing material, and P-256 and P-384 need the FIPS support. Pair a raised floor with a build that includes the algorithms and provisioned keys.
Registry rejections
UnknownParent- The registry holds no entry for the parent authority the child names.
OversubscribedSiblings- The child plus its already-admitted siblings exceeds the parent’s share.
DuplicateChild- An existing child ID returns with a different share.
Holders and release
The registry stores one edge per parent and child pair, and several live evaluations may depend on that edge. Each evaluation that leases the edge adds a holder, and releasing one evaluation keeps the authority another is still using.
Lease- Acquires a releasable holder, including when the child already exists with the same share. Hosted cleanup releases its holder, and the last release frees the edge.
VerifyOnly- Commits a fresh child share without a releasable holder; rechecking the same share adds no holder. The share stays in the registry for the caller to account for.
Release is idempotent for a missing child, and a share mismatch rejects the release. Hosted delegated admission also checks unresolved reserved holds from a prior process and fails closed when registry health is unknown. Those monetary holds belong to the runtime budget store, a separate object from the sibling-share registry.
Source
- Attenuation validation recomputes the subset and declared reductions.
- Scope coverage defines the subset relation for each grant kind.
- Capability verification distinguishes the fixed-root and resolver paths.
- The sibling-share registry defines admission, holders, and release.