# When must an instance re-attest after a delegated scope grant? On the split between token validity and workload attestation freshness in AAIF **Date of Record:** 2026 November 10 **Scope:** Applies to AAIF deployments where an agent instance obtains OAuth access tokens via workload identity federation (WIF) and then delegates a subset of scopes to a downstream callee (sub-agent, tool, or peer service). Does not apply to statically provisioned service-account keys, nor to pure on-behalf-of flows where the resource server itself performs re-attestation per request. ## Core Distinction The non-obvious boundary is between **token validity** and **attestation freshness**. A federated access token carries an `exp` and a set of granted scopes; it does not carry any assertion that the underlying workload identity (the SPIFFE ID, the pod's projected service-account token, the node's TPM-backed key) was still valid *at the moment the scope was exercised*. In AAIF, the delegation grant is bound to the *subject* identifier minted at federation time, not to the live attestation state of the instance. This means an instance can hold a structurally valid, unexpired delegated token whose scope grants were derived from an attestation that has since been revoked — for example, because the pod was rescheduled onto a node that fails the cluster's attestation policy, or because the workload's SPIFFE entry was rotated out but the old SVID's derived token has not yet expired. The token is valid; the attestation is stale; the two facts are independent. ## What Was Observed During a rolling node drain, agent instances were rescheduled across a boundary between two node pools with different attestation profiles (one TPM-backed, one software-only). Instances that had already completed WIF and delegated a `documents:read` scope to a downstream extraction tool continued to present their delegated tokens successfully for the full token TTL (default 900s). The downstream tool's authorization decisions were correct *given its inputs* — the token was unexpired and the scope was present — yet the acting instance no longer satisfied the attestation policy that had justified issuing the scope in the first place. No error fired at any layer. The failure was only visible by correlating the instance's current node attestation state against the `iat` of the delegated token. The invariant that was violated in spirit but not in letter: *a delegated scope is a claim about what the attested workload was permitted to do, and it remains a claim about that workload only for as long as the workload's attestation is current.* AAIF does not enforce this; it enforces token expiry. ## Condition of Validity The distinction holds — i.e., the stale-attestation window is a real risk — precisely when all of the following are true: 1. Delegated tokens are issued with a TTL longer than the attestation re-evaluation interval of the workload. 2. The delegation grant is bound to a subject identifier that survives re-attestation (a stable SPIFFE ID or workload name), rather than to the specific SVID or attestation evidence. 3. The downstream callee validates the token locally (signature + `exp` + scope) without calling back to the issuer for a freshness check. If any one of these is false, the window collapses: short TTLs force re-federation, evidence-bound subjects invalidate on rotation, and online introspection catches the revocation. The dangerous configuration is the common one — long-lived delegated tokens, stable workload names, and offline JWT validation at the edge. ## Decision Boundary The operating rule: **an instance must re-attest before exercising a delegated scope if and only if the delegated token's `iat` predates the most recent change in the instance's attestation state.** In practice you cannot know the latter synchronously, so the implementable boundary is: - If the callee performs offline validation and the token TTL exceeds the attestation re-evaluation interval, treat the token as *conditionally valid* and require an out-of-band attestation freshness assertion (a signed `attested_at` claim, or a short-lived companion token) before the scope is honored. - If the issuer can be reached, prefer online introspection keyed on the *current* attestation state, not on token revocation lists — revocation lists will not contain the token, because the token was never revoked. - If neither is available, cap delegated-token TTL at or below the attestation interval. This is the only safe default. ## Operational Effect Enforcing the boundary converts a silent authorization drift into a loud, localized failure: the callee rejects a token whose `attested_at` is older than its own policy window and surfaces a distinguishable error, rather than proceeding on a stale grant. The cost is an extra round trip or a shorter token lifetime; the benefit is that scope grants cannot outlive the attestation that justified them. Instances that skip this check will, under node churn, continue to act with scopes their current posture no longer warrants — and no log line will say so. ```python import time from dataclasses import dataclass class StaleAttestationError(RuntimeError): """Raised when a delegated scope is exercised past attestation freshness.""" pass @dataclass class DelegatedToken: subject: str scopes: frozenset[str] issued_at: float # epoch seconds, token `iat` expires_at: float # epoch seconds, token `exp` attested_at: float # epoch seconds, last workload attestation @dataclass class AttestationPolicy: max_attestation_age: float # seconds; must be <= token TTL to be safe def authorize_delegated_scope( token: DelegatedToken, required_scope: str, policy: AttestationPolicy, now: float | None = None, ) -> None: now = time.time() if now is None else now if now >= token.expires_at: raise StaleAttestationError( f"token for {token.subject} expired at {token.expires_at:.0f}" ) if required_scope not in token.scopes: raise StaleAttestationError( f"scope {required_scope!r} not granted to {token.subject}" ) attestation_age = now - token.attested_at if attestation_age > policy.max_attestation_age: # Token is structurally valid but the attestation backing it is stale. raise StaleAttestationError( f"attestation for {token.subject} is {attestation_age:.0f}s old " f"(policy max {policy.max_attestation_age:.0f}s); re-attest required" ) if token.issued_at < token.attested_at: # The grant predates the most recent attestation: re-federate so the # delegated scope reflects current posture rather than a prior one. raise StaleAttestationError( f"delegated token for {token.subject} predates latest attestation; " "re-federate before exercising scope" ) ```