pub fn delegate(
parent: &CapabilityToken,
child_scope: &ChioScope,
delegator_keypair: &Keypair,
delegatee: &PublicKey,
attenuation: ScopeAttenuation,
signed_at: u64,
nonce: [u8; 16],
) -> Result<DelegationReceipt, Error>Expand description
Recursive-delegation mint helper.
delegate wraps DelegationLink::sign with fail-closed attenuation
enforcement and emits a [DelegationReceipt] alongside the signed
link. Returns Err (denying the mint) when any of:
- The parent token’s scope does not explicitly authorize
Operation::Delegate. - The proposed
child_scopeis not a subset of the parent token’s scope (rejected byvalidate_attenuation). - Any
attenuation.stepsentry is not a true narrowing of the parent (raises a cap, targets a tool outside the parent scope, or extends expiry). Steps are validated reduce-only rather than copied verbatim, and each is checked against every covering parent grant so an overlapping*:*grant cannot mask a concrete grant that holds the targeted operation or cost cap. - Any declared step is not actually reflected in
child_scope/child_expires_at(for example anAddConstraintstep whose constraint the child grant omits). This mirrors chio-kernel’s declared-attenuation validation so the helper never emits a receipt whose signed link and child scope disagree. attenuation.budget_share_bpsexceeds the parent’s share. The share is parent-relative: a child can never claim a larger fraction of the budget than the parent holds (an absent parent share is treated as 100%). When the parent holds a share strictly below the 100% ceiling, the child MUST state an explicitbudget_share_bps: omission is rejected fail-closed because downstream admission treats a missing share as the full budget (a widening). A parent at the full ceiling (NoneorSome(10_000)) accepts an omitted child share as a no-op.- The requested
child_expires_atis greater than the parent’sexpires_at(rejected as anError::AttenuationViolation). delegator_keypair.public_key() != parent.subject(the mint helper is fail-closed: only the parent capability’s bound subject may delegate further).
The helper is intentionally pure with respect to the local clock:
callers pass signed_at and nonce explicitly so unit tests, replay
proofs, and proptest-driven invariants stay deterministic.
This function is gated behind the delegation feature flag. Callers
must opt in explicitly.