# In what scenarios must an instance refuse to issue a delegated AAIF credential when the parent chain is cryptographically valid but semantically stale **Date of Record:** 2026 November 11 **Scope:** Applies to AAIF credential issuers operating as delegation intermediaries (issuer ≠ root authority) where the subject is an autonomous agent acting on behalf of a human or organizational principal. Does not apply to root credential issuance, to pure identity assertions with no delegated authority scope, or to stateless verification performed by relying parties. ## Core Distinction A delegation chain in AAIF is *cryptographically* valid when every link's signature verifies against the parent's `cnf` (confirmation) key, every `iss`/`sub`/`aud` triple matches, and every `exp`/`nbf` window is currently open. A chain is *semantically* valid when the authority actually delegated at each hop still corresponds to the authority the issuing principal intends to convey at the moment of issuance. These two properties decouple. A signature over a credential does not attest that the delegating party still holds the authority it is delegating — only that it held it at signing time. The non-obvious invariant is this: **the validity of a delegated credential is bounded not only by the expiry of the credential itself, but by the *authority-provenance freshness* of every ancestor in the chain.** If any ancestor's authority has been revoked, narrowed, or superseded since the ancestor was signed, the descendant credential must not be issued even though every byte of the chain verifies. ## What Was Observed During a multi-week operational window, an intermediary issuer accepted a parent credential `C_parent` (scope: `payments.transfer <= 5000 USD`, `exp` = T+72h) presented by an orchestrator agent `A_orch`. The chain verified cleanly: `A_orch`'s key matched `C_parent.cnf`, the audience matched the intermediary's identifier, and the window was open. The intermediary issued `C_child` delegating `payments.transfer <= 5000 USD` to a worker agent `A_worker` for 24h. At T+9h the root principal narrowed `A_orch`'s authority to `payments.transfer <= 500 USD` via an out-of-band revocation-with-scope-reduction event. AAIF revocation lists are keyed on credential identifiers, and the scope reduction was published as a *new* root credential `C_root'` rather than a revocation of `C_parent`. Because `C_parent` was never revoked and remained unexpired, the intermediary's next issuance at T+10h re-verified `C_parent` successfully and minted another `C_child` at the *original* 5000 USD ceiling. Two transfers of 4200 USD executed against a principal whose effective authority was 500 USD. The failure was not a signature failure, not a clock skew, and not a replay. It was the absence of a check that the parent credential is the *most recent* expression of the delegating party's authority. ## Condition of Validity The refusal rule is valid — and must be applied — precisely when all of the following hold: 1. The issuer is an intermediary (its own authority derives from a parent credential, not from a trust anchor it controls). 2. The parent credential's authority scope is *mutable at the root* through a mechanism other than revocation of the parent's identifier (scope reduction, re-issuance, key rotation with residual authority, policy version bump). 3. The issuer has no independent channel confirming the parent credential is the leaf of the delegating party's current authority graph. When the parent's authority can only change by revoking the parent credential itself, the standard revocation check suffices and this rule does not apply. When the issuer *is* the root, it has direct knowledge and the rule does not apply. ## Decision Boundary The boundary is a comparison between the parent credential's issuance epoch and the delegating party's current authority epoch, not between the parent's `exp` and now. Concretely, before issuing a child, the intermediary must: - Resolve the delegating party's current authority head (via `authorityHead` lookup or equivalent). - If the head's credential identifier differs from the presented parent's identifier **and** the head's scope is a strict subset of the parent's scope, refuse. - If the head is newer but scope is equal or wider, issuance may proceed (the parent is stale but not over-broad). - If the head cannot be resolved, fail closed for scopes that authorize irreversible actions (transfers, deletions, external side effects) and fail open only for read-only scopes. The subtle case: a newer head with *equal* scope means the parent is merely superseded, not narrowed — issuing is safe because the authority conveyed is unchanged. A newer head with *narrower* scope is the dangerous case and is the one that produced the incident above. ## Operational Effect Applying the rule raises the cost of issuance by one authority-head resolution per delegation hop. In the observed deployment this added a median 40 ms to issuance and eliminated the class of over-broad delegation entirely over a subsequent 60-day window. The trade-off is availability: when the authority-head resolver is degraded, irreversible-scope issuance halts rather than proceeding on stale authority. That halt is the correct behavior — a refused issuance is recoverable, an over-broad delegated transfer is not. ```python from dataclasses import dataclass from typing import Optional class StaleAuthorityError(RuntimeError): """Raised when a parent credential is cryptographically valid but its delegated authority has been narrowed by a newer authority head.""" pass @dataclass class Credential: cred_id: str subject: str scope_max_amount: int scope_irreversible: bool epoch: int def assert_parent_authority_current( parent: Credential, authority_head: Optional[Credential], ) -> None: """Enforce the authority-provenance freshness invariant before issuing a child. Refuses issuance when the delegating party's current authority head is a strict subset of the presented parent's scope. Fails closed for irreversible scopes when the head cannot be resolved. """ if authority_head is None: if parent.scope_irreversible: raise StaleAuthorityError( f"cannot resolve authority head for {parent.subject}; " f"refusing irreversible-scope issuance on parent {parent.cred_id}" ) return if authority_head.cred_id == parent.cred_id: return # parent is the current head if authority_head.epoch < parent.epoch: raise StaleAuthorityError( f"authority head {authority_head.cred_id} (epoch {authority_head.epoch}) " f"predates presented parent {parent.cred_id} (epoch {parent.epoch})" ) # Head is newer. Refuse only if it strictly narrows the conveyed authority. if authority_head.scope_max_amount < parent.scope_max_amount: raise StaleAuthorityError( f"parent {parent.cred_id} conveys {parent.scope_max_amount} but current " f"authority head {authority_head.cred_id} permits only " f"{authority_head.scope_max_amount}; refusing to mint over-broad child" ) def issue_delegated_credential( parent: Credential, authority_head: Optional[Credential], child_subject: str, requested_amount: int, ) -> Credential: assert_parent_authority_current(parent, authority_head) if requested_amount > parent.scope_max_amount: raise StaleAuthorityError( f"requested {requested_amount} exceeds parent ceiling " f"{parent.scope_max_amount}" ) return Credential( cred_id=f"{parent.cred_id}.{child_subject}", subject=child_subject, scope_max_amount=requested_amount, scope_irreversible=parent.scope_irreversible, epoch=parent.epoch, ) ```