# Control Plane by Karl McGuinness
> Thoughts on identity and security as a foundational control plane for modern software systems. Drawing on decades of experience building internet-scale identity platforms, the blog explores how authentication, authorization, access management, policy, and trust move upstream into shared infrastructure that governs who can act, what they can do, and how execution unfolds across systems.
This file contains the full Markdown text of every published article. Canonical article URLs and individual Markdown URLs are listed before each document.
---
# Mission-Bound Authorization: The Glossary
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-glossary/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-glossary.md
This is the handbook's vocabulary: one table, A to Z. Each entry is
one to three sentences and links the part that defines it, because
the entries are written to be quoted and the canonical homes carry
the argument. If you are meeting the model for the first time, do not
start here. Start at [the cover](/mission-handbook/), whose reading
order teaches these terms in the order the architecture runs, and
come back when you need one.
| Term | Meaning |
| --- | --- |
| **`act` chain** | The RFC 8693 actor chain. A delegated token carries the *same* `mission` claim with subset authority. The chain is actor lineage, not authority lineage: it records who acted through whom, and it does not prove what task was approved, how authority narrowed at each derivation, whether the task is still `active`, or which parameter constraints bind. Those travel in the Mission's own constructs, the anchors, the Authority Set, the lifecycle state, and Child Mission lineage. Reading an actor chain as authorization provenance is the gap Mission lineage exists to close. |
| **Action-bound approval** | A fresh human decision demanded for one concrete action with its final parameters, independent of the Mission's original approval ([the high-assurance level](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level)). |
| **Agent Deployment** | The approved behavioral version of an agent (code, model, system prompt, tool allowlist, data scope, runtime configuration), owned by change governance and pinnable through the core's OPTIONAL `controls.agent_deployment`. The Deployment (the `client_id`, in the OAuth binding) is the class-grain authorization subject and the instance is the attribution subject: a Mission pinned to the class may be executed by many attested instances at once, bounded by `max_derivations` and Mission-grain consumption, with per-instance keys keeping the record attributable. Distinct from the Mission Deployment Profile, the estate's published claims manifest. Not to be confused with the Mission Deployment Profile, the spec family's composition document naming which profiles a deployment runs. |
| **Agent instance** | The concrete running process, identified by its own key and, where deployed, an attester-minted identifier that survives rotation. The attribution grain, never the authorization grain. |
| **Agent Registry** | The enterprise record of logical Agents and instances: owner, status, risk tier, and approved Deployment association. A derivation input, never a grant. |
| **Approval event** | The AS validates the Intent and derives the Authority Set, the Approver consents to the rendered Intent + derived Authority Set, and the AS commits the anchors and creates the Mission `active`, atomically. |
| **Attestation** | Runtime evidence about the instance and its key, consumed by the registry rather than replacing it. Strength runs in tiers, from measured environments to contractual assertion, and a deployment names which tier it relies on. |
| **Authority** | The capacity to cause consequential effects, held as typed, bounded, resource-specific entries rather than as ambient permission. In this model authority is always derived from an approved task, never asserted by a client or inferred by a model. |
| **Authority ceiling** | The standing charter a human consents to once and renews on a governance cadence, under which each unit of work draws its own short-lived Mission. The renewal default forces narrowing: a ceiling renews at its evidence-derived usage envelope, and anything wider is a new approval ([the standing agent at scale](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)). |
| **Authority Set** | The maximum authority committed by `authority_hash`, as `mission_resource_access` entries (`resource`, `actions`, `constraints`, per-entry `delegation`), plus any other RFC 9396 types a deployment registers. |
| **`authorization_details`** | The RFC 9396 wire shape for derived authority. |
| **Binding security architectures** | Credential-carried (the Mission rides the credential), PDP-joined (the decision point joins ordinary credentials to the record), and authority-native (the substrate's own authority object is the record): the three security systems the four bindings realize, named beside the level and the binding ([the deployment ladder](/notes/mission-based-authorization-field-reference/#the-deployment-ladder)). |
| **Child Mission** | A strict-subset Mission a parent authorizes for a sub-agent, with cascade revocation. **Offline attenuation:** minting a narrower child token off the AS hot path, kept safe by the runtime re-checking Mission state. |
| **Claim gate / litmus test** | The six properties, split four and two: the four substrate properties admit the category at issuance strength, and two more back the action-time defense claim, [expanded above](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test). |
| **Class-guard classes** | Irreversible actions, external commitments, and privileged administration. The class guard keeps a human in the approval for them whatever approves everything else, and the runtime prices the same classes as high-consequence, where the strictest requirements attach ([who may approve](/notes/from-a-request-to-an-approved-mission/#who-may-approve), [which actions are consequential](/notes/mission-bound-runtime-enforcement/#which-actions-are-consequential)). |
| **Completion / discharge / `terminal_when`** | Authority that retires itself as work finishes, entry by entry. `terminal_when` is the one member a consumer must understand rather than ignore, because ignoring it fails open ([Complete](/notes/mission-lifecycle-and-change/#complete-discharge-narrows-one-entry-at-a-time)). |
| **Consent Evidence / `consent_rendering_hash`** | A companion artifact committing the structured disclosure the AS recorded as rendered (not the pixels, not comprehension). |
| **Consequential action** | An action with external visibility or effect, the unit the runtime gate evaluates. The boundary is deployment policy above a floor the profile fixes, and the classes are floors across reversibility, exposure, privilege, commitment, and value ([which actions are consequential](/notes/mission-bound-runtime-enforcement/#which-actions-are-consequential)). |
| **Constraints, two layers** | The Intent's free-text `constraints` bind at disclosure: they are what the Approver reads, never what the derivation function parses. An Authority Set entry's structured `constraints` bind at enforcement: machine-actionable, with Common Constraints where shared semantics exist. |
| **Containment matrix** | The six kills of incident response (Mission, agent, Agent Deployment, credential, workload, egress), each with its own blast radius and owner, [in the runtime part](/notes/the-agent-runtime-and-audit/#the-containment-matrix). |
| **Control plane for delegated authority** | The operational reading of the layer: the Mission record is desired state, issuance and the runtime gate are the data plane consulting it, the freshness dial is propagation, and the discovery loop is reconciliation for authority. The strategic case is in the [architecture chapter](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority), and the structural mapping is the [Authority Control Plane part](/notes/the-authority-control-plane/#the-authority-control-plane). |
| **Decision Evidence / Execution Evidence** | Per-decision and per-outcome audit records. |
| **Deferred Approval / Revision** | The approval event made asynchronous. Submission returns a deferral code, the review rides the workflow the organization already staffs, and derivation happens once, at the approval event, under the policy in force. Revision is the narrowing channel: a reviewer trims bounds through the same deferral, and a narrowing is never an edit in place ([deferred and revisable approval](/notes/from-a-request-to-an-approved-mission/#deferred-and-revisable-approval)). |
| **Delegated authority** | Authority exercised by an actor on the strength of someone else's approval. The layer's whole subject matter, and the handbook's claim is that managing it is a [layer with no standard form](/series/designing-mission-bound-authorization/#the-missing-layer). |
| **Delegation** | The explicit grant of same-or-narrower authority to another actor. Spawning is not delegation, a handle is not a credential, and ancestry grants nothing ([Mission-Bound Authority](/notes/mission-bound-authority/)). |
| **Derivation / derivation policy** | The mechanical step that turns a validated Intent and registry facts into an Authority Set, performed once at the approval event under the policy in force. The derivation policy is that step's deployment-authored artifact: deterministic, no model in the loop, fixture-tested, and versioned by `policy_version` ([the derivation policy, concretely](/notes/adopting-mission-bound-authorization/#the-derivation-policy-concretely)). |
| **Discovery loop** | Deny, request, approve, expand, retry: how the open world arrives under governance, named in [Adopting](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem). |
| **Enforcement-scope statement (the honest deployment claim)** | The deployment's published claim: the level, the mediated paths, the issuance-gated paths, the exclusions, and the freshness bound each path actually runs. The [honest deployment claim](/notes/mission-based-authorization-field-reference/#the-honest-deployment-claim) in the Reference's citation kit is its reusable template: a claim that cannot be written in that form is not a conformance claim. |
| **Evidence family** | Shaping, Consent, Decision, and Execution Evidence plus the lifecycle records, each emitted by its own authority and joined on the Mission. The join is what makes continuity verifiable. |
| **Expansion / successor Mission** | Widening as a fresh approval that creates a successor with lineage, never an edit in place. Activation atomically supersedes the predecessor, because two active records for one undertaking are a widening wearing a lifecycle ([Grow](/notes/mission-lifecycle-and-change/#grow-expansion-creates-a-successor)). |
| **Fail closed** | The uniform failure posture. For consequential actions, when the answer is in doubt, refuse: unknown states are non-active, an unrecognized narrowing member refuses the entry, and missing state is a denial, never a default. |
| **Fatigue budget** | Approval attention treated as a security boundary with a spend plan: route decisions where attention already lives, narrow instead of resubmitting, and spend action-bound approval only at maximum consequence ([the fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget)). |
| **Five laws** | Durability, Attribution, Narrowing, Termination, and Containment, the layer's substrate-neutral invariants, [stated in full](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority). |
| **Freshness source / staleness bound** | The published maximum age of the Mission state a path relies on, and the mechanism that supplies it: issuer introspection, Mission Status, the Status List, or Signals. Only `active` permits reliance, within the bound ([fail-closed and active freshness](/notes/mission-bound-runtime-enforcement/#fail-closed-and-active-freshness)). |
| **Harness** | The execution-continuity owner: sessions, task graphs, queues, retries, and sub-agent handles. It binds every durable work item to Mission state and stops when the Mission does. Cooperation, never containment ([the agent runtime and audit](/notes/the-agent-runtime-and-audit/)). |
| **Integrity anchors (`intent_hash`, `authority_hash`)** | The commitments that bind what was approved to what is enforced: `intent_hash` is the canonical hash of the approved Mission Intent, and `authority_hash` is the canonical hash of the Authority Set, both committed at the approval event and carried with the `mission` claim. |
| **Issuance gating** | The token-layer chokepoint. A non-active Mission derives and refreshes nothing, so credential issuance itself consults the record. |
| **Issuance grant** | The middle path that restores the token-layer chokepoint to a MAS estate: estate Authorization Servers redeem MAS-minted grants for Mission-bound, state-gated tokens without moving approval into the AS. |
| **Least exposure** | Bound what the agent may see as deliberately as what it may do, [the exposure discipline](/notes/least-exposure-is-broader-than-least-privilege/) whose enforceable slice today is the edges the trifecta-containment claim names. |
| **Lethal trifecta** | Private-data access, untrusted-content exposure, and external side effects in one execution loop. The handbook treats it as a first-order design constraint: the legs stay separately typed, separately evaluated, and separately auditable, and trifecta containment is a claim with named conditions ([Splitting the Lethal Trifecta](/notes/splitting-the-lethal-trifecta/)). |
| **Lifecycle states** | `active` / `revoked` / `expired`. **Companion states** `suspended` / `completed` (Status), `superseded` (Expansion), and `cascaded` (Child Delegation). Unknown states are treated as non-active. |
| **Logical Agent** | The durable agent identity in the registry, with an accountable owner, status, and risk tier. It outlives deployments, instances, and Missions. |
| **Mandate** | A signed, portable, independently verifiable statement of a Mission's committed facts, minted by its issuer, with optional selective disclosure. Evidence, never a credential: presenting one authorizes nothing ([the control plane part](/notes/the-authority-control-plane/)). |
| **Mediated custody** | The High-Assurance discipline in which the agent never holds the sender-constraint key for a protected class. A mediating handler holds it, obtains the parameter-bound permit, and acts ([the high-assurance level](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level)). |
| **Mission** | The durable, approval-backed, integrity-anchored record of one approved undertaking: what was approved, by whom, within what bounds, until when, and whether it is still in force. Identified everywhere by `mission.id` and `mission.issuer`, committed by `intent_hash` and `authority_hash`, and authoritative for reliance, where only `active` permits it ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)). |
| **Mission Assurance Levels** | Baseline Issuance, Runtime-Enforced, Governed Agent, and High-Assurance Agent, with the binding (OAuth AS, standalone Mission Authority Server, AAuth Person Server, or the experimental UMA sketch) as the orthogonal axis, staged as crawl, walk, run in [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/), and read as an unlock ladder: each level makes a broader class of write authority defensible. |
| **Mission Authority Server (MAS)** | The standalone binding: a dedicated Mission Issuer that derives no tokens, with the PDP joining each ordinary OAuth token to its Mission at the point of use ([the control plane part](/notes/the-authority-control-plane/)). |
| **`mission` claim** | The object on every issued token: `id`, `issuer`, `authority_hash`, plus the OPTIONAL `expires_at` member the core defines as a bounding commitment with no liveness. |
| **Mission Intent** | The structured proposal: `goal`, `resources`, and `expires_at` (required), with optional `constraints`, `proposed_authority`, `success_criteria`, `purpose`, and `controls`. Closed at the top level: unknown members are rejected, and machine-actionable extensions ride in `controls`. Submitted via the `mission_intent` parameter through PAR. |
| **Mission Issuer (the Authorization Server)** | Holds the Mission record, derives authority, runs the approval event, gates issuance. |
| **Mission Shaper** | A client-side component that turns a prompt or trigger into a candidate Mission Intent. Proposes only. Grants no authority. |
| **Mission Status** | (pull, signed, `mission_id`-keyed) and **Lifecycle Signals** (SET events, delivered push or poll). **Expansion** widens via a fresh approval that supersedes the predecessor. **Completion / `terminal_when`** is monotonic, per-entry discharge. |
| **Offline attenuation** | Minting a strictly narrower token without an issuer round-trip, the narrowing proven on the chain, for fan-out at scale at the cost of issuer visibility ([mechanism 2](/notes/mission-bound-authority/#mechanism-2-offline-attenuation)). |
| **Open world** | The deployment condition the architecture assumes: tools and resources discovered at runtime rather than pre-registered, trust relationships that form after authorization time, delegation to actors unknown at approval, untrusted content in the working set, and authorization decisions made with incomplete knowledge of what the task will need. The [Open-World OAuth series](/series/open-world-oauth/) is the published treatment, and the discovery loop is how the open world arrives under governance. |
| **Orchestrator** | The runtime role that records, before dispatch, how each step will be unwound: reversibility classes and unwind plans, exercised when a Mission stops mid-flight. |
| **`parameter_digest`** | Binds a permit to concrete request parameters, closing the time-of-check-to-time-of-use gap. |
| **PEP / PDP** | The Policy Enforcement Point obtains a permit from the Policy Decision Point before each consequential action. The PDP evaluates against the live Mission. |
| **Permit (the lease)** | The PDP's short-lived, audience-specific, parameter-bound decision artifact. It binds the request, not the world, and the high-consequence classes consume it exactly once ([runtime enforcement](/notes/mission-bound-runtime-enforcement/)). |
| **Policy approver** | An authorized policy that approves at machine speed inside a previously human-consented ceiling, with committed inputs and `policy_version` standing where the disclosure stood ([who may approve](/notes/from-a-request-to-an-approved-mission/#who-may-approve)). |
| **Projection** | An audience-scoped, subset-ruled view of a Mission's authority, carried in a credential or loaded as a policy view. Projections never exceed their source and expire no later than it. |
| **Read-only ceiling** | The posture most estates start from (read-only agents, humans approving or executing the writes, permanent pilots), what it costs, and the graduation path off it, named in [Adopting](/notes/adopting-mission-bound-authorization/#the-read-only-ceiling). |
| **Reference security architecture** | The Runtime-Enforced level as [a formula](/notes/mission-based-authorization-field-reference/#the-reference-security-architecture): issuance core, runtime enforcement, AuthZEN binding, and a freshness source. Ratified dependencies, sized as a substantial build. |
| **Revocation** | Ending authority, not merely tokens. It stops new derivation immediately and stops reliance within each path's published freshness bound, and completed effects need compensation, never time travel. |
| **Runtime enforcement** | The action-layer chokepoint. Each consequential action is checked against the live Mission and current resource policy at the point of use ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)). |
| **Sender constraint** | The binding that makes a credential unusable without its key (`cnf` with DPoP or mutual TLS). Mediated custody moves that key out of the agent entirely for the highest classes. |
| **Session** | Execution continuity, never authority. A session proves where work can resume. The Mission decides whether it may ([the agent runtime and audit](/notes/the-agent-runtime-and-audit/)). |
| **Shaping Evidence** | An optional record of how the proposal was produced. Audit material, not authority. |
| **Standing agent** | The agent that never finishes. The agent stands, the authority cycles: a ceiling carries the meaning, and each unit of work draws its own Mission with its own expiry and discharge ([the standing agent at scale](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)). |
| **Standing authority** | Authority that persists between tasks because a credential, account, or grant persists. The blank check, and the thing the layer exists to retire. |
| **Status List** | The fleet-scale freshness surface: one signed, TTL-bounded bit array covering many Missions, where a set bit is the only thing that permits reliance ([observe and revoke](/notes/mission-lifecycle-and-change/#observe-and-revoke-pull-and-push)). |
| **Subject / Approver** | `{iss, sub}` principals: the user the task is for, and the principal who approved it. They may differ. The Approver may be a human or an authorized policy authority, and a non-human approval traces to a human-consented ceiling or policy ([who may approve](/notes/from-a-request-to-an-approved-mission/#who-may-approve)). |
| **Subset rule** | Every derivation, projection, and delegation yields the same or narrower authority than its source, validated at the issuer or proven on the chain. Widening is never derivation. It is a successor Mission. |
| **Survivable incorrectness** | The design stance beneath the laws, inherited from the [Mission Shaping series](/series/mission-shaping/): assume the agent will sometimes be wrong and keep the system governable when it is, with least exposure as the input arm and the laws and runtime gate as the action arm. |
| **Swarm** | Many attested instances of one Agent Deployment executing one Mission: multiplication, not delegation. The class is an authorization subject, never an attribution subject ([the swarm](/notes/mission-bound-authority/#the-swarm-multiplication-not-delegation)). |
| **Taint rule** | The harness discipline that downgrades a session once untrusted content enters it, under default-taint polarity: a parameter that cannot be affirmatively traced to a trusted source stays tainted, so paraphrase sheds nothing ([the trifecta at execution time](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time)). |
| **Three objects, three lifecycles** | Agent identity (who is acting), Agent Deployment (what is running), and the Mission (why the authority exists), each with its own owner, lifecycle, and revocation, [defined in the architecture chapter](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles) with the [working integration above](/notes/mission-based-authorization-field-reference/#agent-iam-and-the-agent-registry). |
| **Token classes** | The core's taxonomy. A *Mission-referenced* token carries a Mission reference without derived authority or any gating guarantee, and a reference is never authority. A *Mission-derived* token carries Mission-derived authority as `authorization_details`. A *Mission-bound* token is a Mission-derived token whose issuance and refresh are gated on `active` state, and the core reserves "Mission-bound" for that gated class alone. |
| **Undertaking** | The work itself, the durable task that spans tokens, calls, tools, sub-agents, and time. The Mission is its record. The undertaking is what the record governs. |
| **Vendor test** | The same six properties as [questions to ask a vendor](/notes/mission-based-authorization-vendor-test/), with what failing answers sound like. |
| **Verifiable continuity** | The property that the approval, the decisions including denials, and the executions can be shown to belong to one undertaking, joined deterministically on the Mission rather than stitched from timestamps. Distinct from lifecycle continuity, the workflow surviving time and disconnects ([the agent runtime and audit](/notes/the-agent-runtime-and-audit/)). |
---
# Mission-Bound Authorization: The Standards Map
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-standards-map/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-standards-map.md
The handbook's design rule is that the architecture introduces one
primitive, the governed record of the approved task, and composes the
rest from existing work. That rule is only checkable against a map.
This appendix is that map: the OAuth specifications and drafts, WIMSE
documents, and OpenID Foundation work with a concrete relationship to
the architecture. Each entry names the relationship, the use case, and, where one exists, the important delta. Where substituting a neighboring object for
the approved task would break the architecture's claim, the hazard is
stated plainly.
Statuses follow the public record as of **July 15, 2026**, the same
reconciliation date the [Reference](/notes/mission-based-authorization-field-reference/)
tracks. The map is exhaustive for the active
[OAuth](https://datatracker.ietf.org/wg/oauth/documents/) and
[WIMSE](https://datatracker.ietf.org/wg/wimse/documents/) working-group
queues on that date. It is intentionally selective for
ratified RFCs, individual Internet-Drafts, and OpenID specifications:
those sections include documents with a concrete architectural join,
a material overlap, or a common substitution hazard. This is a design
map, not a registry dump.
Statuses move, so the links are the authority for current publication
state. Relationships move more slowly. They describe how the Mission
draft family uses or compares with a neighboring document. They do
not imply endorsement or adoption by that document's authors or
working group.
# How to read this map
The tables use one primary relationship for each entry:
| Relationship | Meaning |
| --- | --- |
| **Required substrate** | At least one family profile depends on it normatively |
| **Defined composition** | A family profile or this handbook defines a concrete join to it |
| **Optional composition** | It can supply a layer or rail, but the architecture does not require it |
| **Peer binding** | It is an alternate substrate for which the family defines a Mission binding |
| **Adjacent** | It works in overlapping territory, but no concrete join is defined |
| **Not a substitute** | It remains useful for its own object, but cannot stand in for the governed task |
Two conventions carry the honesty. A **delta** states what the
neighboring document does differently or leaves to deployment policy,
so composition claims stay falsifiable. A **conflict if substituted**
flag marks the places where asking that neighboring object to stand
in for the governed task would fail the
[litmus test](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test).
The [comparison table](#key-deltas-and-substitution-hazards) collects
the highest-impact cases.
# OAuth: the ratified substrate
OAuth is the flagship binding because it supplies the most complete
deployed substrate. The required dependencies of the
[reference security architecture](/notes/mission-based-authorization-field-reference/#the-reference-security-architecture)
are ratified RFCs or finalized OpenID specifications. Experimental and
advanced companions may additionally depend on Internet-Drafts, and
those dependencies are identified below.
| Spec | Relationship | How it composes, and the deltas |
| --- | --- | --- |
| [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749) / [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750) OAuth 2.0 core and bearer usage | Required substrate | The deployment base and the reason OAuth is the first binding. Delta: OAuth defines credential and grant lifecycles, not a cross-audience lifecycle for the approved work |
| [RFC 9126](https://www.rfc-editor.org/rfc/rfc9126) Pushed Authorization Requests | Required substrate | The submission channel. The `mission_intent` parameter rides PAR, keeping the proposal off the browser-visible front channel and binding the resulting `request_uri` to the client before approval |
| [RFC 9396](https://www.rfc-editor.org/rfc/rfc9396) Rich Authorization Requests | Required substrate | The Authority Set serialization. Derived entries are `authorization_details`, never free text. Delta: RAR defines extensible authorization-detail types but not who owns their ontology. The [RAR-type metadata draft](#oauth-the-individual-drafts-in-the-familys-orbit) and AAuth's R3 explore resource-declared semantics |
| [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519) / [RFC 7515](https://www.rfc-editor.org/rfc/rfc7515) JWT and JWS, with [RFC 9068](https://www.rfc-editor.org/rfc/rfc9068) JWT access tokens | Required substrate | The carrier. The `mission` claim, integrity-anchor envelopes, Mandate, and Consent Evidence use JWT or JWS artifacts |
| [RFC 8785](https://www.rfc-editor.org/rfc/rfc8785) JSON Canonicalization | Required substrate | `intent_hash`, `authority_hash`, and the permit's `parameter_digest` are SHA-256 over JCS-canonical bytes, making the commitments reproducible |
| [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693) Token Exchange | Required substrate | The delegation plumbing. **Conflict if substituted:** RFC 8693 leaves output-token scope and linkage to authorization-server policy; it does not require a narrow-only subset proof. Child Missions and offline attenuation add that invariant and its evidence |
| [RFC 7662](https://www.rfc-editor.org/rfc/rfc7662) Token Introspection, with [RFC 9701](https://www.rfc-editor.org/rfc/rfc9701) JWT responses | Required substrate | One accepted freshness source. The Mission profile extends the introspection result with current Mission state alongside token validity |
| [RFC 7009](https://www.rfc-editor.org/rfc/rfc7009) Token Revocation | Defined composition | Revokes a token and, where the server supports it, related tokens or the underlying grant. Delta: its subject is still one authorization server's token or grant, not the approved task projected across issuers and audiences |
| [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414) AS metadata / [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) protected resource metadata | Required substrate | Discovery. Mission support is advertised in AS metadata, and `mission_constraints_supported` lives in protected-resource metadata as one ontology-supply mechanism |
| [RFC 9449](https://www.rfc-editor.org/rfc/rfc9449) DPoP / [RFC 8705](https://www.rfc-editor.org/rfc/rfc8705) mTLS, with [RFC 7800](https://www.rfc-editor.org/rfc/rfc7800) confirmation | Required substrate | Sender constraint. Mission-bound tokens are sender-constrained, and the runtime permit binds the same `cnf` key, so stealing the token alone is insufficient |
| [RFC 8707](https://www.rfc-editor.org/rfc/rfc8707) Resource Indicators | Required substrate | Audience scoping. The family profiles single-audience narrowing when gated and ungated authority must be split across tokens |
| [RFC 9470](https://www.rfc-editor.org/rfc/rfc9470) Step-Up Authentication | Defined composition | The authentication-strength grain of the graduated challenge family, raised for an action inside an approved Mission without widening authority |
| [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) JWT client assertions | Required substrate | Client authentication and the JWT assertion rail used by the instance-identity substrate |
| [RFC 7591](https://www.rfc-editor.org/rfc/rfc7591) Dynamic Client Registration | Optional composition | Registers clients. Delta: registration identifies and configures a client; the Agent Deployment registry separately governs what may run |
| [RFC 7636](https://www.rfc-editor.org/rfc/rfc7636) PKCE and [RFC 9700](https://www.rfc-editor.org/rfc/rfc9700) Security BCP | Required substrate | The hardening baseline for the applicable OAuth flows |
Beyond the OAuth registry, four wider IETF rails carry family
capabilities:
| Rail | Relationship | How it composes |
| --- | --- | --- |
| [RFC 8417](https://www.rfc-editor.org/rfc/rfc8417) Security Event Tokens, [RFC 8935](https://www.rfc-editor.org/rfc/rfc8935)/[RFC 8936](https://www.rfc-editor.org/rfc/rfc8936) delivery, [RFC 9493](https://www.rfc-editor.org/rfc/rfc9493) subject identifiers | Required substrate | Lifecycle Signals are SETs with Mission subjects, pushed and polled on the standard delivery rails |
| [RFC 9943](https://www.rfc-editor.org/rfc/rfc9943) SCITT architecture, with the [SCRAPI draft](https://datatracker.ietf.org/doc/draft-ietf-scitt-scrapi/) | Required substrate | The audit profile registers Mission evidence as Signed Statements in a transparency service, making inclusion independently verifiable |
| [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) HTTP Message Signatures | Required substrate (AAuth binding) | The request-integrity rail used by the AAuth substrate |
| [RFC 9635](https://www.rfc-editor.org/rfc/rfc9635) GNAP | Adjacent | The clean-slate delegation protocol. Its grant negotiation and grant-management continuation are close analogs to shaping and lifecycle. Delta: GNAP does not define the Mission-specific integrity anchors, narrow-only child-task derivation, or cross-substrate task lifecycle. No GNAP binding has been authored; the substrate contract states what one would have to supply |
# OAuth: the working-group drafts
The OAuth working group's sixteen active or IESG-stage documents as of
July 15, 2026, mapped in full, plus the recently published SD-JWT RFC:
| Draft | Status | Relationship | How it composes, and the deltas |
| --- | --- | --- | --- |
| [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) | WG document | Optional composition | Consolidates the substrate: PKCE is required for authorization-code clients, while the implicit and password grants are omitted. The family's PAR-first and sender-constraint requirements remain additional profile rules |
| [Identity chaining across domains](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/) | RFC Editor queue | Required substrate for an advanced companion | Cross-Domain Projection profiles its single-hop, audience-scoped grant so another trust domain can honor a Mission. That companion should not be treated as stable ahead of this dependency |
| [Identity Assertion Authorization Grant (ID-JAG)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) | WG document | Required substrate for an advanced companion | The IdP-brokered leg of Cross-Domain Projection. The family gates ID-JAG issuance on current Mission state so the cross-domain credential inherits the derivation kill switch |
| [Transaction Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-transaction-tokens/) | In WG last call | Optional composition | Carries seconds-scale identity and authorization context through an intra-domain call chain. A hop may carry both a Txn-Token for transaction context and a Mission-bound grant for the durable approved task. **Conflict if substituted:** a Txn-Token does not define the approval, integrity anchors, or lifecycle of that task, because those are outside its job |
| [Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/) | WG document | Defined composition | The attestation rail beneath Agent Deployment: how a client instance proves platform and posture when authenticating |
| [Token Status List](https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/) | RFC Editor queue | Optional composition | Mission Status defines an optional Mission Status List using the status-list shape. Delta: a token's referenced status describes that token; a Mission status entry describes whether the work remains authorized |
| SD-JWT selective disclosure ([RFC 9901](https://www.rfc-editor.org/rfc/rfc9901)) | Published RFC | Required substrate for an optional feature | The Mandate's minimization tool: a holder can disclose only the committed Mission facts a verifier needs |
| [SD-JWT VC](https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/) | With the IESG | Adjacent | A credential-shaped use of the same disclosure mechanics. It may carry evidence about a Mission, but presenting it is not by itself a Mission authorization decision |
| [Refresh Token and Authorization Expiration](https://datatracker.ietf.org/doc/draft-ietf-oauth-refresh-token-expiration/) | WG document | Optional composition | Defines interoperable expiration semantics for refresh tokens and authorizations. The family separately caps derived lifetimes with `expires_at` and gates refresh on current Mission state |
| [OAuth SPIFFE Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-spiffe-client-auth/) | WG document | Optional composition | The OAuth-to-workload-identity join: a SPIFFE credential authenticates the client to which a Mission-bound token is issued |
| [Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) | WG document | Optional composition | Provides fetchable client metadata without prior registration. It can be an identity and metadata input to Agent Deployment; the registry still makes the governance decision |
| [Cross-device flows BCP](https://datatracker.ietf.org/doc/draft-ietf-oauth-cross-device-security/) | RFC Editor queue | Optional composition | Supplies the threat model for a decoupled approval interaction when the Approver uses another device |
| [First-party applications](https://datatracker.ietf.org/doc/draft-ietf-oauth-first-party-apps/) | In WG last call | Adjacent | A first-party application interaction model with no Mission-specific join |
| [Browser-based apps BCP](https://datatracker.ietf.org/doc/draft-ietf-oauth-browser-based-apps/), [Security BCP updates](https://datatracker.ietf.org/doc/draft-ietf-oauth-security-topics-update/), [JWT BCP update](https://datatracker.ietf.org/doc/draft-ietf-oauth-rfc8725bis/), and the [RFC 7523 update](https://datatracker.ietf.org/doc/draft-ietf-oauth-rfc7523bis/) | RFC Editor queue or active review | Defined composition | The substrate's maintenance train. Applicable updates flow through because the family profiles the base mechanisms rather than forks them |
# OAuth: the individual drafts in the family's orbit
The proposals the family answers, profiles, or supplies an object to.
This section is selective, not a census of individual submissions.
The family's own twenty-eight documents are cataloged in the
[Reference's draft-family table](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance)
and are not repeated here. McGuinness drafts listed below are separate
identity and delegation prerequisites, not members of that Mission
document set.
| Draft | Relationship | How it composes, and the deltas |
| --- | --- | --- |
| [AI agent authorization best practices](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) (draft-klrc-aiagent-auth) | Adjacent | Its Agent Mission section expects a mission to be translated into authorization requirements but leaves that process out of scope. The Mission family proposes one such process; it is an answer to the gap, not a dependency of this draft |
| [AAuth Protocol](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/) (Hardt) | Peer binding | This proposed clean-slate agent protocol has a first-class mission layer. The family's AAuth binding maps the Mission model to the Person Server while preserving issuance gating |
| [AAuth Rich Resource Requests (R3)](https://dickhardt.github.io/AAuth/draft-hardt-aauth-r3.html) | Defined composition | The resource-declared direction: a resource publishes operation semantics and requests commit to them. The AAuth binding carries that shape. Delta: OAuth RAR does not prescribe the same ontology direction |
| [RAR-type metadata](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/) (Zehavi) | Defined composition | An OAuth-side discovery mechanism for machine-readable RAR types. The family can use those declarations during derivation, disclosure, and `insufficient_authorization_details` remediation |
| [Transaction-specific challenges](https://datatracker.ietf.org/doc/draft-rosomakho-oauth-txn-challenge/) (Rosomakho) | Defined composition | The single-transaction grain of the graduated challenge family, beside scope, authorization-detail, and authentication-strength challenges |
| [Deferred token response](https://datatracker.ietf.org/doc/draft-gerber-oauth-deferred-token-response/) (Gerber) | Required substrate for an advanced companion | Deferred Approval profiles its pending response and deferral code so approval can complete asynchronously |
| [Attenuating agent tokens](https://datatracker.ietf.org/doc/draft-niyikiza-oauth-attenuating-agent-tokens/) (Niyikiza) | Required substrate for an experimental companion | Supplies self-proving offline delegation chains; the Mission attenuation profile additionally binds each chain to approved authority and the narrow-only invariant |
| [Transaction token chaining](https://datatracker.ietf.org/doc/draft-fletcher-transaction-token-chaining-profile/) (Fletcher) | Optional composition | Carries context across transaction-token hops, including Mission context within one trust domain |
| [RAR evaluation with Cedar](https://datatracker.ietf.org/doc/draft-cecchetti-oauth-rar-cedar/) (Cecchetti) | Optional composition | Defines Cedar evaluation of `authorization_details`, a policy-engine-specific companion to the engine-neutral AuthZEN binding |
| [Credential brokers for agents (CB4A)](https://datatracker.ietf.org/doc/draft-hartman-credential-broker-4-agents/) (Hartman) | Optional composition | Supplies the custody pattern: the broker holds credentials and the agent does not. A Mission-aware deployment additionally gates broker issuance and action requests on current Mission authority |
| [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/), with [Actor-Signed Hop Proofs](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-proofs/) and [Actor Receipts](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-receipts/) | Required substrate for optional delegation | Defines `act`-chain discipline and optional per-hop proof and provenance. The issuance core's only Internet-Draft dependency is confined to its optional delegation capability |
| [Identity Assertion Trust Framework](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-assertion-framework/) | Required substrate for an advanced companion | Defines which assertions an issuer accepts, from whom, and under what proof for assertion-based cross-domain legs |
| [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) and [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/) | Required substrate for optional instance identity | Identifies which instance runs, on what attested platform, and under which Agent Deployment. The [authority part](/notes/mission-bound-authority/) consumes that identity instead of redefining it |
| [UMA 2.0](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html) (Kantara Initiative) | Peer binding | The family's experimental UMA binding carries Mission Intent through claims pushing, records the resource owner's decision in the authorization assessment, and gates RPT issuance on Mission state. Delta: UMA's permission ticket and persisted claims provide protocol continuity, not the Mission's approved-task lifecycle |
# OAuth: the emerging agent wave
A wave of individual drafts arrived through 2026, most still at
revision 00 or 01, all addressing aspects of agent authorization.
Early drafts move too fast for per-document verdicts to stay useful,
so this selected table maps the wave by territory. Omission means only
that no distinct architectural delta was identified in this snapshot.
The evaluation frame never changes: the four substrate properties
admit a design to the category, and all six back a defense claim.
| Territory | The drafts | The family's read |
| --- | --- | --- |
| Use cases and gaps | [Agent authorization use cases and gap analysis](https://datatracker.ietf.org/doc/draft-chen-oauth-agent-authz-use-cases/) (Chen) | The catalog [Part 5 answers line by line](/notes/closing-the-agent-authorization-gaps/): ten answered, one partial, one delegated |
| Delegation chains | [Delegated authorization](https://datatracker.ietf.org/doc/draft-li-oauth-delegated-authorization/) (Li), [delegation chains](https://datatracker.ietf.org/doc/draft-liu-oauth-chain-delegation/) (Liu), [verifiable actor chains](https://datatracker.ietf.org/doc/draft-mw-oauth-actor-chain/), [DAAP](https://datatracker.ietf.org/doc/draft-mishra-oauth-agent-grants/) (Mishra), [async delegation handles](https://datatracker.ietf.org/doc/draft-zhu-oauth-async-delegation/) (Zhu) | The territory of the Actor Profile, Child Missions, and offline attenuation. The evaluation is the narrow-only law: a chain must prove its narrowing, not just its custody |
| Agent revocation | [Agent authorization explicit revocation](https://datatracker.ietf.org/doc/draft-chen-oauth-agent-revocation/) (Chen) | The kill-switch territory. The family's answer is possession-independent: revoke the task, and every future derivation dies with it |
| Intent, consent, and approval | [Intent admission assertions](https://datatracker.ietf.org/doc/draft-jiang-oauth-intent-admission/) (Jiang), [native authorization via structured elicitation](https://datatracker.ietf.org/doc/draft-embesozzi-oauth-agent-native-authorization/) (Embesozzi), [PACT](https://datatracker.ietf.org/doc/draft-valverde-oauth-pact/) (Valverde), [user-mediated credential delivery](https://datatracker.ietf.org/doc/draft-emerson-oauth-user-mediated-delivery/) (Emerson) | The shaping and approval territory: proposals as untrusted input, committed disclosures, an accountable Approver. Intent admission is the shaping profile's exact question |
| Evidence and audit | [Authorization evidence and audit trail](https://datatracker.ietf.org/doc/draft-liu-oauth-authorization-evidence/) (Liu), [per-transaction posture consistency](https://datatracker.ietf.org/doc/draft-vicente-oauth-apm/) (Vicente) | The evidence territory: Decision and Execution Evidence joined on the Mission and registered into SCITT. The join key is the difference between an audit trail and a pile of logs |
| Scopes for AI workloads | [AI model access scopes](https://datatracker.ietf.org/doc/draft-hemanth-oauth-ai-scopes/) (Hemanth), [scope aggregation for agent workflows](https://datatracker.ietf.org/doc/draft-jia-oauth-scope-aggregation/) (Jia) | The scope-explosion diagnosis, answered at task grain: consent to one Mission's derived authority, not a thousand scopes |
| Transaction context for agents | [Transaction tokens for agents](https://datatracker.ietf.org/doc/draft-araut-oauth-transaction-tokens-for-agents/) (Araut), [cross-domain transaction tokens](https://datatracker.ietf.org/doc/draft-liu-oauth-cross-domain-txn-token/) (Liu) | The transaction-token row applies unchanged: context objects compose, and none of them is the approved task |
| Multi-agent collaboration | [Multi-agent collaboration extension](https://datatracker.ietf.org/doc/draft-song-oauth-ai-agent-collaborate-authz/) (Song) | The fan-out territory: Child Missions with lineage, explicit breadth and depth bounds, and cascade termination backed by lineage checks |
| Attestation for agents | [ACAP](https://datatracker.ietf.org/doc/draft-yakung-oauth-agent-attestation/) (Yakung), [attestation-based native-app authorization](https://datatracker.ietf.org/doc/draft-ekahraman-oauth-attestation-authz-native-app/) (Kahraman) | The Agent Deployment territory: attestation strengthens instance identity and deployment admission, but attestation alone authorizes no task |
None of these is dismissed by its placement here. Collectively, the
drafts show pressure at the same seams: intent, delegation, lifecycle,
runtime context, and evidence. That is evidence of shared problem
pressure, not proof that the authors agree on one object or solution.
The [convergence argument](/notes/the-convergence-and-the-wagers/)
makes the narrower claim.
# WIMSE and the workload identity substrate
The [three objects](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles)
divide the estate: agent identity answers who, Agent Deployment
answers what runs, and the Mission answers why. WIMSE and SPIFFE
supply workload identity and authentication beneath the first two
answers. The Mission family consumes that identity. It does not treat
identity as task authority.
The WIMSE working group's six active documents and one IESG-stage
document are mapped first. The final three rows group all eleven
related individual drafts listed by the working group on the snapshot
date.
| Document | Status | Relationship | How it composes, and the deltas |
| --- | --- | --- | --- |
| [WIMSE architecture](https://datatracker.ietf.org/doc/draft-ietf-wimse-arch/) | WG document | Optional composition | The workload identity model beneath the agent: how workloads obtain and use identities across systems |
| [Workload Identifier](https://datatracker.ietf.org/doc/draft-ietf-wimse-identifier/), [Workload Proof Token](https://datatracker.ietf.org/doc/draft-ietf-wimse-wpt/), and [Workload Credentials](https://datatracker.ietf.org/doc/draft-ietf-wimse-workload-creds/) | WG documents | Optional composition | The identity artifacts: how a workload is named, how it proves possession, and what carries the identity. They can supply client identity and proof beneath Mission-bound calls |
| Workload-to-workload authentication with [HTTP signatures](https://datatracker.ietf.org/doc/draft-ietf-wimse-http-signature/) or [mutual TLS](https://datatracker.ietf.org/doc/draft-ietf-wimse-mutual-tls/) | WG documents | Optional composition | Call-level authentication rails between services, beneath the policy enforcement point |
| [Workload Identity Practices](https://datatracker.ietf.org/doc/draft-ietf-wimse-workload-identity-practices/) | With the IESG (Informational) | Adjacent | A survey of deployed workload-identity patterns that informs the layer beneath the architecture |
| [SPIFFE](https://spiffe.io/) (CNCF) | Deployed external specification | Optional composition | A deployed workload-identity system. A SPIFFE ID can identify an agent workload, and the [OAuth SPIFFE client authentication draft](https://datatracker.ietf.org/doc/draft-ietf-oauth-spiffe-client-auth/) joins that identity to OAuth client authentication |
| Agent applicability | Early individual draft | Adjacent | [WIMSE applicability for AI agents](https://datatracker.ietf.org/doc/draft-ni-wimse-ai-agent-identity/) maps workload-identity concepts into agent deployments. The Mission supplies task authority above that identity layer |
| Context, delegation, evidence, and bounded credentials | Early individual drafts | Adjacent | [Execution Context Tokens](https://datatracker.ietf.org/doc/draft-nennemann-wimse-ect/), [authorization-evidence records](https://datatracker.ietf.org/doc/draft-munoz-wimse-authorization-evidence/), [cross-organizational delegation requirements](https://datatracker.ietf.org/doc/draft-reece-wimse-cross-org-delegation/), and [condition-bounded credentials](https://datatracker.ietf.org/doc/draft-winmagic-wimse-condition-bounded-credentials/) overlap the runtime, projection, and evidence layers. A Mission reference can provide a common approved-task join key, but no join is defined yet |
| Attestation, credential verification, and trust discovery | Early individual drafts | Adjacent | [Trustworthy workload-identity extensions](https://datatracker.ietf.org/doc/draft-ccc-wimse-twi-extensions/), [heterogeneous credential verification](https://datatracker.ietf.org/doc/draft-jiang-wimse-heterogeneous-credential/), [attestation in workload identity tokens](https://datatracker.ietf.org/doc/draft-liu-wimse-wit-attestation/), [transitive attestation](https://datatracker.ietf.org/doc/draft-mw-wimse-transitive-attestation/), [workload attestation](https://datatracker.ietf.org/doc/draft-reddy-wimse-workload-attestation/), and [trust-domain discovery](https://datatracker.ietf.org/doc/draft-schwenkschuster-wimse-trust-domain-discovery/) can strengthen who or what is running. They do not by themselves say which approved task authorizes an action |
The delta is the whole point: workload identity answers who or what is
calling, not which approved task authorizes the call. **Conflict if
substituted:** an estate that treats successful workload
authentication as sufficient authorization has rebuilt a credential
perimeter with stronger credentials. The Mission makes authority
derive from approved work, and the
[AIdentity crosswalk](/notes/answering-the-laws-of-aidentity/) carries
the long form.
# The OpenID Foundation
| Specification | Status | Relationship | How it composes, and the deltas |
| --- | --- | --- | --- |
| [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html) and [Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) | Final | Optional composition | Common sources of Approver and Subject identity. The Mission records principals as issuer-scoped subject identifiers; OIDC is one way to establish them |
| [AuthZEN Authorization API 1.0](https://openid.net/specs/authorization-api-1_0.html) | Final, January 2026 | Required substrate for the AuthZEN binding | The interoperable PEP-to-PDP decision wire. The family profile maps Mission state, capability identity, parameters, and evidence obligations into that wire while the runtime contract remains protocol-neutral |
| [ARAP, Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html) | AuthZEN WG draft | Optional composition | Turns a requestable denial into a governed request: the discovery loop's ask leg. Delta: ARAP deliberately allows multiple completion modes; a Mission is one possible form of durable authorization state, not a requirement of ARAP |
| [COAZ, the MCP authorization profile](https://openid.github.io/authzen/authzen-mcp-profile-1_0.html) | AuthZEN WG draft | Optional composition | Maps MCP `tools/call` inputs into the AuthZEN decision shape so tool-boundary PEPs can construct the same policy request |
| [AROP, the proposed OAuth binding of ARAP](https://github.com/openid/authzen/pull/531) | Proposal, not a WG draft | Adjacent | Proposes to bind ARAP completion to OAuth issuance for token-resident authorization state. It is a pull request, not an adopted AuthZEN profile |
| [Shared Signals Framework 1.0](https://openid.net/specs/openid-sharedsignals-framework-1_0-final.html) with [CAEP](https://openid.net/specs/openid-caep-1_0-final.html) | Final | Required substrate for Lifecycle Signals | The push framework. Lifecycle Signals profiles an SSF transmitter and defines Mission lifecycle events. **Conflict if substituted:** identity, session, or credential-risk events do not by themselves terminate an approved task across every projection |
| [CIBA Core 1.0](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) | Final | Optional composition | A decoupled interaction rail for reaching a person on another device. **Conflict if substituted:** CIBA completes an authentication and token-grant flow; it does not define the durable approved-task record, committed disclosure, or revision semantics. A Mission approval can use a CIBA-style interaction without confusing the two objects |
| [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0-final.html) | Final | Optional composition | A hardened OAuth profile with PAR and sender-constrained access tokens. A FAPI 2.0 estate already supplies much of the OAuth binding's security baseline |
| [OpenID Federation 1.0](https://openid.net/specs/openid-federation-1_0.html) | Final, February 2026 | Optional composition | Establishes multilateral trust and resolves trust chains to anchors. It can authenticate issuers and resolve keys for Mandate and evidence verification across domains |
| [IPSIE](https://openid.net/wg/ipsie/) | Working group, drafting | Adjacent | Develops enterprise identity interoperability for SSO, lifecycle, sessions, and signals. The Mission authority layer can ride above that fabric; IPSIE does not define task authorization |
| [OpenID for Verifiable Credentials](https://openid.net/sg/openid4vc/) (OpenID4VCI and OpenID4VP) | Specification family | Adjacent | Defines credential issuance and presentation flows. The family shares SD-JWT disclosure mechanics but currently defines no OpenID4VC binding. If a deployment carries Mission facts on these rails, presentation alone must not authorize consequential action; current Mission state still gates authority, and the Mandate remains evidence rather than an access credential |
| [OIDC Session Management](https://openid.net/specs/openid-connect-session-1_0.html), [Front-Channel Logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html), and [Back-Channel Logout](https://openid.net/specs/openid-connect-backchannel-1_0.html) | Final | Not a substitute | Manage or end login sessions. Delta: [sessions are not Missions](/notes/sessions-are-not-missions/). Logout does not require every projected credential to be revoked or the approved work to end |
# Key deltas and substitution hazards
The high-impact comparisons in one table. The pattern is consistent:
the neighboring document is useful for its own object. A conflict
appears only when a deployment asks that object to stand in for the
approved task.
| Question | The neighboring answer | The family's answer | The delta |
| --- | --- | --- | --- |
| Asynchronous approval | CIBA authenticates a person out-of-band and completes a grant | Deferred Approval defers the Mission approval event, with the disclosure committed and interrogation recorded | CIBA has no durable task object and no revision channel. The two compose when CIBA is the interaction rail and the Mission is the record |
| Call-chain context | Transaction Tokens carry intra-domain, seconds-scale transaction context | Mission-bound tokens carry the durable approved task | A Txn-Token does not aim to satisfy the four Mission substrate properties. The two can compose on one hop as facts about different objects |
| Delegation | Token Exchange (RFC 8693) re-issues tokens and can represent actor relationships | Child Missions and offline attenuation enforce narrow-only delegation with lineage | RFC 8693 leaves output scope and token linkage to AS policy; the Mission profiles add a subset invariant and proof |
| Revocation events | SSF and CAEP carry identity, session, and security-condition events | Lifecycle Signals define Mission lifecycle events that gate every future derivation | Same event framework, different event subject and semantics. Session or credential events do not necessarily end the approved task |
| Status | Token Status List answers whether a credential is good | Mission Status answers whether the work is still authorized | The family profiles the same wire shape with the Mission as subject |
| Ending things | Logout ends the session | Termination ends the task within a published freshness bound | Sessions are not Missions, and logout does not require every projected credential to stop |
| Portable proof | A deployment may treat presentation of a verifiable credential as sufficient authorization | The Mandate proves committed facts and authorizes nothing by presentation | The restriction belongs to the Mission profile, not OpenID4VC: authority remains gated on current Mission state |
| Task lifecycle in-protocol | GNAP grant management can continue or update a grant | The Mission lifecycle governs an approved task with anchors and derivation | GNAP does not define the Mission's integrity anchors, narrow-only child-task rule, or cross-substrate lifecycle; a future binding could add them |
| Workload authentication | WIMSE or SPIFFE proves which workload is calling | The Mission says which approved work authorizes the call | Authentication can be a policy input, but successful possession proof is not task authority |
| Who supplies the ontology | RAR lets a client request typed authorization details but leaves type definition and discovery outside the core | R3-shaped resource declaration, with RAR-type metadata as an OAuth discovery mechanism | The direction is substrate-specific. The invariant is that meaning is bound at approval and enforced at use |
# What has no row, and why
Three absences are deliberate. **XACML** and policy languages such as
Cedar and Rego have no language-level row because the family binds to
the decision wire, AuthZEN, and stays agnostic about the PDP engine.
The Cedar RAR draft appears only because it defines a concrete join at
the `authorization_details` layer. **SAML** has no direct Mission
binding. A SAML assertion can still supply principal or client
identity, including through the OAuth assertion framework and
[RFC 7522](https://www.rfc-editor.org/rfc/rfc7522), without becoming
the approved task. **DIDs** likewise can supply identifiers and key
resolution. Whether those identifiers are useful is deployment
specific. Neither a DID nor its control proof grants Mission
authority.
The family's own Mission documents are deliberately absent too. This
map is the outside-in view. The separate prerequisite identity and
delegation drafts are included only where the Mission documents
depend on them. The inside-out view, which document supplies which
capability at which assurance level, is the
[Reference's draft-family table](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance)
and the [adoption part's build order](/notes/adopting-mission-bound-authorization/).
If a row here drifts from the public record, the fastest correction
path is an issue on the
[draft repository](https://github.com/mcguinness/mission-bound-authorization/issues),
where the family's references are reconciled first.
---
# The Mission-Based Authorization Vendor Test
Canonical URL: https://notes.karlmcguinness.com/notes/mission-based-authorization-vendor-test/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-based-authorization-vendor-test.md
> This is the [handbook's](/mission-handbook/) evaluation tool, its
> Appendix D: built to be linked, pasted into an RFP, and asked in a
> vendor call. The
> [blueprint](/notes/adopting-mission-bound-authorization/#walk-the-runtime-enforced-level) is
> the build order, and the
> [Field Reference](/notes/mission-based-authorization-field-reference/)
> carries the definitions behind every question.
Agent auth today can prove who is acting and what credential they
hold. It cannot prove the work is still authorized. So when a vendor
says they support agent authorization, the evaluation is six
questions. Each probes one property of the
[six-property litmus](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test),
and each has a recognizable failing answer.
The count is deliberate. The five laws are the invariants of delegated
authority. These six questions are the vendor-verifiable surfaces that
prove those invariants are actually implemented.
Use this as a hard gate, not a maturity survey. If question 1 does not
produce an approved task object, the claim is not mission-based
authorization. If question 3 is a shared parent token, there is no
delegation claim. If question 4 is only token validation, there is no
runtime-enforcement claim. If question 5 is token expiry, there is no
revocation claim.
| # | Ask | What it probes | A failing answer sounds like |
| --- | --- | --- | --- |
| 1 | What is the approved task object, and where is it stored? | An approved task object | "The prompt", "the session", "the trace ID" |
| 2 | What derives the agent's authority from that object? | Authority derived from the task | "Admins assign scopes at integration time" |
| 3 | When the agent fans out or delegates, what guarantees the child's authority is strictly narrower? | Narrow-only delegation | "Sub-agents reuse the parent's token" |
| 4 | What checks each consequential action against it at the moment of use? | Per-action runtime enforcement | "The token is validated on every call" |
| 5 | What happens, and how fast, when the task is revoked? | Observable lifecycle state | "Tokens expire within an hour" |
| 6 | Can an auditor pull one identifier and see the whole task? | Evidence joins on the task's identity | "We have comprehensive logs" |
Notice what every failing answer has in common. Each one names a
credential artifact, a runtime artifact, or a log where the question
asked for a governed task object. That substitution is the whole category error, and hearing it is the point of the test. One failing answer has an acceptable floor form: token expiry alone fails, but state-gated issuance against a live task record with a published staleness bound is the category's own coarse end, the Baseline level honestly claimed. The differentiator is the state gate and the record, never the lifetime.
The test is conjunctive for the action-time defense claim, and it
splits where the family's own conformance splits. Questions 1, 2, 3,
and 5 are the substrate bar: fail one and the product is not
mission-based at all, just a different product wearing the category's
name. Questions 4 and 6 are what the defense claim adds: pass the
substrate four without them and the honest claim is Baseline
Issuance, governance rather than defense. Either way the missing
property tells you what you are looking at: no task object is a
policy engine, no runtime enforcement is a governance dashboard, no
revocation reach is a token issuer with labels, and no narrowing is a
shared service account with extra steps.
A passing answer has a different sound:
| Property | A passing answer says |
| --- | --- |
| Approved task object | There is a durable Mission record with an identifier, issuer, approved purpose, actor binding, authority, constraints, lifecycle state, and evidence links |
| Derived authority | The agent's usable authority is computed from the Mission, not only from an integration-time role, scope, or admin setting |
| Narrowing | Delegated and derived authority is computed as a strict subset of the parent's: a child task, sub-agent, or downstream token only narrows, and widening requires a fresh approval |
| Runtime enforcement | A PEP checks each consequential action against action, parameters, actor, and current Mission state before the effect happens |
| Revocation | Revoking or expiring the Mission reaches enforcement within a named freshness bound and fails closed when freshness cannot be established |
| Evidence | An auditor can start with one Mission identifier and reconstruct approvals, derived authority, decisions, denials, lifecycle changes, and the consequential actions taken under it, joined by the mission identifier and integrity signal every token and decision carries |
And ask for the demonstration, because it is the cleanest proof that
the Mission is enforced rather than decorative:
> **Show me one denied action where the token was valid but the
> Mission's state, bounds, parameters, or delegation chain made the
> action impermissible.**
A system that cannot produce a valid-token denial is validating
credentials, not enforcing a task.
The bar is deliberately ordinary. These are the questions any finance
team could answer about a corporate card program without preparation:
the approved purpose, who derived the limits, what authorizes each
swipe, what the freeze reaches, and what the statement joins. The
[corporate-card test](/series/what-the-corporate-card-already-solved/#the-corporate-card-test)
is this same instrument in card language, and if the answers would be
unacceptable for a card program, they are unacceptable for an agent
that moves faster and can be talked into things by the documents it
reads.
A vendor that passes all six should be able to write the
[honest deployment claim](/notes/mission-based-authorization-field-reference/#the-honest-deployment-claim):
the level, the enforcement scope, the freshness bound, the evidence,
and the exclusions, in writing. Two follow-ups keep the pass honest. A
mission-bound token without runtime enforcement is governance metadata,
not agent safety, so question 4 is the one a claim most often fails in
practice. And the
[what-not-to-claim list](/notes/mission-based-authorization-field-reference/#what-not-to-claim)
names the six overclaims to listen for on the way out.
For an RFP or architecture review, the reusable clause is simple. Describe the approved task object. Identify where it is stored. Show how authority, tokens, decisions, enforcement, revocation, and evidence join to it. Name the enforcement scope and freshness bound. And list every path where the claim does not apply.
For the depth behind each question: the
[litmus test](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test)
expands the six properties and names near misses, the
[implementation checklist](/notes/mission-based-authorization-field-reference/#the-implementation-checklist)
is how you verify a claimed pass, and the
[competitive landscape](/notes/mission-based-authorization-field-reference/#competitive-landscape)
covers the alternatives a failing answer is usually reaching
for, row by row, with the law each one breaks.
---
# Common Objections to Mission-Based Authorization
Canonical URL: https://notes.karlmcguinness.com/notes/common-objections-to-mission-based-authorization/
Markdown URL: https://notes.karlmcguinness.com/notes/common-objections-to-mission-based-authorization.md
This page is for the people who run today's control planes: an IdP for
human access, workload identity for the fleet, an Authorization Server
for credentials, a PDP for decisions, PAM for privileged access, IGA
for entitlements, and workflow systems around all of them. The recurring
question, "isn't this something we already operate?", deserves its
strongest answer rather than a category boundary drawn for convenience.
The answer is sometimes **yes**. Existing products can keep task state,
carry purpose attributes, issue task-specific credentials, and gate
actions. A system that makes an approval-backed task record the root of
authority and enforcement may already implement mission-based
authorization within its declared scope, whatever it calls the object.
The standards case begins where that private state must cross a boundary
the platform does not own.
Use four questions throughout the page:
| Question | What a sufficient answer must show |
| --- | --- |
| What is the root object? | A durable record of approved work, not only a credential, session, or correlation ID |
| What projects from it? | Authority is derived from the approved bounds, including delegation where supported |
| What enforces it? | Claimed consequential paths check current task state within published freshness bounds |
| What remains outside the claim? | Unmediated paths, semantic misuse, completed effects, and privacy risks are named |
The handbook calls that root object the Mission and the surrounding
function [the control plane for delegated authority](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority).
It composes with the systems above rather than requiring their removal.
The
[Field Reference](/notes/mission-based-authorization-field-reference/)
carries the definitions and the
[competitive landscape](/notes/mission-based-authorization-field-reference/#competitive-landscape)
these answers lean on, and the
[vendor test](/notes/mission-based-authorization-vendor-test/) turns
the category into six questions to ask anyone claiming it.
The test is behavioral, not nominal. A workflow record that passes it
can be Mission-shaped. A product labeled "mission-aware" that merely
adds a token claim does not pass. Most objections below therefore turn
on whether an existing artifact supplies one input, or actually owns the
approval, derivation, enforcement, lifecycle, and evidence relationship.
Jump by the plane you run:
[identity and workload](#the-identity-and-workload-plane),
[credential](#the-credential-plane),
[decision](#the-decision-plane),
[privileged and entitlement](#the-privileged-and-entitlement-planes),
[workflow](#the-workflow-plane), or go straight to
[the hard questions](#the-hard-questions).
# The identity and workload plane
**"Our IdP is our control plane. Why is this not just an IdP
feature?"** The IdP is the control plane for authentication and human
access: identities, authentication events, sessions, and federation.
OIDC and SAML do not standardize an approved-work object or its
lifecycle, but an IdP product can add one. If that feature owns the
approval-backed task record, derives authority from it, and makes its
state available to the claimed enforcement paths, it is a valid local
implementation of the category. On the OAuth binding, the Authorization
Server, often part of the same platform, can host the Mission record and
gate issuance on its state. Where that product cannot change, the
standalone
[Mission Authority Server](/notes/the-authority-control-plane/) carries
the record with a PDP join at the point of use. The distinction is a
responsibility boundary, not a requirement to buy another control plane.
**"We run workload identity and a non-human identity program:
SPIFFE, attested instances, inventoried and rotated credentials."**
Keep it. Mission-Bound Authorization composes with that stack: instance
attestation and sender constraint are inputs, not alternatives. Custody
governs the credential, inventory knows what
exists, and attestation supports a claim about which workload is
speaking. Those identity functions do not by themselves identify the
approved task. An attested workload with freshly rotated keys can still
resume work whose approval ended. Deployment policy can bind workload
identity to private task state. The Mission standardizes that binding
when it must cross systems. An NHI program answers what non-humans exist
and what credentials or entitlements they hold. The Mission answers what
approved task governs a particular use of that authority, and
[Mission-Bound Authority](/notes/mission-bound-authority/) is where
the two bind: the `mission` claim rides tokens that are already
instance-bound and sender-constrained. The same analysis applies to
existing automation: a CI pipeline, Terraform apply, or reconciling
controller becomes a candidate for task-bound authority when its work
outlives one request or crosses enforcement domains.
**"Isn't this just a session?"** A session primarily preserves runtime
continuity and may also cache authorization state. A server-side session
record can even carry a task identifier. What sessions do not
standardize is an approval-backed task lifecycle from which authority is
derived across credentials, actors, and domains. If a session record
does own those semantics and every claimed action is gated on its current
state, it may be a sufficient local implementation. [Sessions Are Not
Missions](/notes/sessions-are-not-missions/) gives the general
distinction: resume state must remain subordinate to the governing task,
not become evidence that the task still stands.
# The credential plane
**"Isn't this just RAR?"** [Rich Authorization
Requests](https://www.rfc-editor.org/rfc/rfc9396.html) can express
fine-grained, resource-specific authorization requirements and can be
the subject of an OAuth approval flow. An API-defined RAR type can also
carry a task identifier. That makes RAR a natural serialization for an
Authority Set, not evidence that RAR itself defines the governing task.
RFC 9396 does not standardize a cross-domain task identity, lifecycle,
current-state surface, evidence model, or comparison rule for arbitrary
authorization-detail types. A deployment may define all of those around
RAR. When it does, the surrounding object and behavior, not the JSON
parameter alone, are the Mission-shaped part.
**"Isn't this just UMA?"** UMA is closer than a token-format analogy.
[UMA 2.0](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html)
lets a requesting party's client use a permission ticket to seek a
requesting party token (RPT) for protected-resource access asynchronously
from the resource owner's authorization. Its authorization server
evaluates resource-owner policy conditions and requesting-party claims
and can manage grants over time. UMA's companion [federated-authorization
specification](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-federated-authz-2.0.html)
also allows authorization and resource servers to be loosely coupled. It
directly disproves any claim that OAuth-family authorization always
requires a synchronously present resource owner.
The remaining distinction is the governed object and its scope. UMA
standardizes party-to-party access to protected resources. It does not
standardize a cross-system undertaking whose approved Authority Set is
the source of credential projection and, where supported, delegation,
whose current state gates claimed consequential paths within a declared
scope, and whose evidence joins across the work. A UMA deployment can
define that task object and bind permission tickets, RPTs, policy
changes, and enforcement to it. If it does, it may implement
mission-based authorization under UMA rather than compete with it.
Mission-based authorization should reuse UMA's asynchronous and
claims-gathering patterns where they fit.
**"Why not just short-lived tokens?"** Short lifetimes bound staleness.
They do not represent the task, and they make the wrong thing the
clock. A revoked task keeps deriving fresh short tokens unless issuance
is gated on task state. Token lifetime alone also supplies no common
task identifier for cross-system evidence.
Mission-bound deployments use short-lived tokens too. They are a
control inside the design, not a substitute for the object. And the
bridge form is different from tokens alone: short-lived tokens minted
under state-gated issuance are the Baseline pattern for estates whose
resources cannot check state, a conforming freshness source with
revocation explicitly bounded by the token lifetime ([the Reference's revocation matrix](/notes/mission-based-authorization-field-reference/#when-revocation-bites) prices it).
**"This drags authorization back to stateful. The industry spent
twenty years going stateless."** Stateless *validation* was real
progress, and Mission-bound tokens can still verify offline. But offline
validation cannot provide fresh revocation without some state signal,
introspection, revocation list, or bounded credential lifetime. Short
lifetimes choose the last option, which is a freshness source with
[the dial](/notes/mission-bound-runtime-enforcement/#fail-closed-and-active-freshness)
set to the token lifetime, the state tax paid at the issuer on every
refresh. This architecture makes the availability and staleness trade
explicit: each action class chooses its bound, permits can amortize
round-trips, the coarse end
([bounded staleness at the token lifetime](/notes/mission-based-authorization-field-reference/#when-revocation-bites))
adds no new state dependency at all, and only the classes whose
consequences justify it pay for per-action freshness. The objection
still lands on availability: a state service or PDP becomes a tier-0
dependency for each path that fails closed on it. Leases, caches,
replication, and scoped degradation reduce that cost but do not erase
it. The
[revocation bet](/notes/the-convergence-and-the-wagers/#where-this-could-be-wrong)
names the wager and the deployment evidence needed to evaluate it.
# The decision plane
**"Our PDP already evaluates every request, and our Zero Trust
architecture verifies continuously."** A PDP can evaluate purpose or
task state if policy receives those attributes, and Zero Trust does not
forbid doing so. [NIST's Zero Trust
Architecture](https://csrc.nist.gov/pubs/sp/800/207/final) already
places a PDP and PEP around access to enterprise resources. The question
is where trustworthy approved-task state comes from and who owns its
lifecycle. Without that input, repeated evaluation can keep permitting a
cancelled task because actor, device, and resource policy remain valid.
Mission-based authorization does not replace the PDP or the doctrine. It defines one governed input and its state semantics. The AuthZEN
binding carries that input to the decision point. The
[control-plane reading](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority)
states where that input lives, and the
[runtime profile](/notes/mission-bound-runtime-enforcement/) is the
per-action check, with its enforcement scope and freshness bound made
part of the deployment claim.
**"Isn't this just CAEP and Shared Signals?"** The transport
pattern is right, and the family reuses it. The [Shared Signals
Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html)
is intentionally extensible: cooperating parties can define event types
and subject identifiers beyond those in
[CAEP](https://openid.net/specs/openid-caep-1_0.html). That means a
Mission event can ride this substrate. It does not mean the substrate
defines the Mission object or its lifecycle semantics. Mission Lifecycle
Signals are Security Event Tokens used as the push side of the
[freshness dial](/notes/mission-bound-runtime-enforcement/#fail-closed-and-active-freshness).
Existing Shared Signals plumbing keeps its job. The Mission profile
adds an agreed subject, transition vocabulary, and state authority, with
[Status as the fail-closed pull](/notes/mission-lifecycle-and-change/)
underneath push delivery.
# The privileged and entitlement planes
**"Isn't this just PAM, or an IGA request?"** Closest cousins, and the
differences are the point. IGA often produces standing entitlements,
and PAM often binds elevation to an identity, ticket, session, and time
window. Mature deployments can already make those grants task-aware.
The category test asks whether the task record is merely approval
context or the continuing authorization root: is authority derived from
it, are claimed consequential paths checked against current state, and
does termination propagate within a published bound? A PAM or IGA
product that does all of that may already be Mission-shaped within its
scope. Deferred Approval is designed so an existing request workflow can
drive the approval rather than be replaced. The same machinery can
produce task-scoped human access today, which is
[the better IGA outcome](/notes/adopting-mission-bound-authorization/#you-do-not-need-an-agent-to-start)
in one sentence.
**"Our agents are standing. The work never ends."** Then the authority
should cycle even though the agent does not. The standing charter
becomes a consented ceiling, each unit of work draws a bounded Mission
from it under policy, discharge retires what each unit used, and the
ceiling's renewal is a governance review with the last cycle's
evidence in front of the reviewer, not a habit.
[The standing agent at scale](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)
carries the pattern, and the tell that it has decayed into a blank
check with a calendar: renewals that never narrow.
**"Everything still hangs on a human meaningfully consenting. That is
the consent screen, again."** The approval event requires
accountability and delegated authority, not a human click for every
Mission. An
[accountable approver](/notes/from-a-request-to-an-approved-mission/#who-may-approve)
decides against committed inputs before authority exists: a person for
decisions that require individual judgment, or an authorized policy
operating within an organizationally approved ceiling for repeatable
work. Nor is the disclosure take-it-or-leave-it. The approver can
interrogate it before deciding, with answers drawn from recorded
shaping material, and the interrogation lands in the record. That does
not prove meaningful understanding. Consent Evidence
records the disclosure and decision. It cannot prove the approver read,
understood, or wisely accepted them. Mission-grain fatigue is therefore
a security constraint rather than a UX footnote. The
[fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget)
spends human attention on the ceilings and the guard exceptions
instead of per Mission, and shaping keeps the disclosure readable
enough to decide on. Generated judgment is not accepted as the sole
granting authority for attacker-influenced proposals, because the
approver would share the agent's injection surface. The residual is
governance quality: a badly designed ceiling or rubber-stamped approval
still grants bad authority.
# The workflow plane
**"Isn't this just a workflow instance, a case record, or a durable
execution?"** Those systems already hold durable task state, and some
also drive access decisions. Run the behavioral test: is authority
derived from the record, is each claimed consequential action enforced
against its current state, and do lifecycle and evidence join on its
identity? If yes, the workflow may be a valid Mission implementation
inside that platform. If it only schedules steps, carries a ticket ID,
or replays durable execution while authorization remains independent,
it is a neighboring system. The name and storage location are not the
distinction. The authority relationship is. The
[looks-like table](/notes/mission-based-authorization-field-reference/#what-looks-like-a-mission-but-is-not)
runs the full lineup.
**"We built this internally in a quarter: a task table, a PDP, and
scoped tokens. Why standardize a new object?"** That may be enough, and
the
[landscape](/notes/mission-based-authorization-field-reference/#competitive-landscape)
concedes it: inside one platform, careful composition can implement the
category. Custom engineering can also produce revocation across known
audiences, joined evidence, integrity anchors, and lifecycle-aware
issuance and enforcement
([what becomes possible only with a Mission](/notes/mission-based-authorization-field-reference/#what-becomes-possible-only-with-a-mission)
itemizes the target properties). Standardization is justified only when
multiple implementations need common semantics and wire behavior: the
SaaS API that cannot read your private task table, a third-party tool
server, a partner domain, or an auditor joining evidence across them. If
one platform owns every relevant boundary, keep the private design. The
handbook treats the interoperability case as a bet rather than a
theorem: the
[composition bet](/notes/the-convergence-and-the-wagers/#where-this-could-be-wrong)
names the deployment evidence that would prove composition enough.
An internal implementation is useful evidence for the
[venue conversation](/notes/adopting-mission-bound-authorization/#what-the-community-still-has-to-standardize),
especially where proprietary integrations repeat across products.
# The hard questions
**"An agent discovers tools and resources at runtime. You cannot
approve what you cannot enumerate."** The Mission approves the task,
not the toolset, and discovery is a proposal event, not an authority
event. A runtime-discovered tool or resource acquires authority in one
of two governed ways. Either it was already inside the approved bounds
(the Intent's constraints and the derivation policy govern resources
the Approver bounded without enumerating), or it arrives through the
discovery loop: the PDP denies with a requestable `out_of_authority`,
the current
[ARAP draft](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html)
turns the denial into a governed request, and the widening lands as a
separately approved successor Mission through
[Expansion](/notes/mission-lifecycle-and-change/#grow-expansion-creates-a-successor).
The experimental
[Mission Open-World Discovery](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-discovery.html)
profile names this end to end: each encounter is adjudicated against a
ceiling the Approver pre-consented, a resource's self-declaration is
never classification authority, and a session that has ingested
untrusted content cannot bind a communication- or commitment-capable
resource without the additional approval required by deployment policy.
High-consequence deployments can require that approval to be human.
The alternative, broad standing grants to cover the unknown, is the
blank check this handbook exists to retire. What a Mission cannot
do is make a discovered counterparty trustworthy: whether to trust a
runtime-discovered issuer, tool server, or its metadata is the
substrate problem, and the
[Open-World OAuth series](/series/open-world-oauth/) carries it.
**"How does the Mission know the meeting was cancelled?"** It does not
infer business truth. An authorized source must change authoritative
state: the subject or approver, an administrator, a policy process, or an
orchestrator reacting to a trusted business event. If no transition
arrives, the Mission remains active until expiry or an entry discharges.
That is why ownership of lifecycle operations, default expiries, and
source integration are operational requirements rather than protocol
decoration. The guarantee begins after the authoritative transition:
new derivation stops immediately at the state authority, while reliance
stops on each claimed path within its published freshness bound. The
architecture makes termination enforceable. It does not make the system
omniscient.
**"An agent can stay inside every parameter bound and still do damage
with the content."** Correct, and the layer never claims otherwise.
Sharpened, the objection says the model stops unauthorized access but
not the malicious execution of authorized access, and the handbook
states exactly that as its own residual, because
[survivable incorrectness](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority)
assumes the agent will be steered. Structure still bites on the shape
of in-bounds misuse: quotas meter the thousand-comment spam run,
parameter bounds pin the fields and destinations, and reversibility
classes route what cannot be undone to a human before it happens.
What structure cannot do is judge content, and the bound is stated as
canon: Mission compliance is evaluated against the approved
representation of purpose, not against an independent oracle of human
intent. Parameter binding, metering, and state checks are structural
enforcement: they bound the blast radius, and they cannot judge whether
an in-bounds email body leaks intellectual property. The architecture's
answers to semantic risk are deliberately structural too: keep the
envelope small (scope, and
[exposure](/notes/least-exposure-is-broader-than-least-privilege/)),
keep the
[lethal trifecta split at execution time](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time),
and escalate the classes where content is the harm to action-bound
human approval under mediated custody. Content-aware controls (DLP
verdicts, classifiers, an LLM judge) compose as decision inputs through
the AuthZEN binding's `context`, and they raise the bar without
becoming a guarantee, because a judge model reading attacker-influenced
content is itself injectable. The [adversary model](/notes/mission-based-authorization-field-reference/#adversary-model)
carries the residual in writing: within the approved scope, a steered
agent can still misuse what was granted, which is why scope stays
tight.
**"The agent can bypass the PEP through a shell, direct network access,
or another tool."** Then that path is not runtime-enforced. The protocol
cannot prove that a deployment enumerated every execution channel. A
claim must name its mediated paths, issuance-gated-only paths, and
unmediated exclusions. The harness, network policy, gateway, or resource
server must close the paths included in the claim. An action observed in
logs but not forced through a gate is evidence, not containment. This is
the reason the [deployment claim](/notes/mission-based-authorization-field-reference/#the-honest-deployment-claim)
publishes enforcement coverage instead of saying "all agent actions."
**"Your subset rule cannot prove the child is really narrower."** For
the representation, it can: exact-or-prefix resources, subset actions,
tighter constraints, capped expiry, delegation policy no broader, all
mechanically checkable on every derivation. What representation alone
cannot prove is semantic narrowing, that the child cannot produce
effects outside the parent's approved purpose, because contextual and
quantitative constraints can compare as narrower while permitting
purpose-inconsistent effects. The
[Reference names the distinction](/notes/mission-based-authorization-field-reference/#the-mission-object-model)
and the required posture where the comparison relation cannot decide:
conservative refusal or a separately approved projection, never an
optimistic mapping.
**"Who decides what 'prepare the board packet' permits?"** Not the
sentence, and not the agent. The Authorization Server derives a
concrete Authority Set from the validated Intent under deployment
[derivation policy](/notes/adopting-mission-bound-authorization/#what-you-will-operate),
the approver consents to that derived authority with the summary as
context (never the summary alone), and enforcement checks each
action's concrete parameters against the committed set. Natural
language never becomes executable authority by interpretation at run
time: the Mission authorizes parameterized operations, not free-form
intent, and the classes where a wrong reading costs most carry
[action-bound approval](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level)
on top. A mis-derived set an approver accepts is still approved. The
disclosure therefore renders the derived
authority and the fatigue budget keeps the reviewer able to read it.
**"Why not just keep agents read-only?"** That is the control most
estates run today, and it is a legitimate containment strategy for
work that is genuinely read-only. It also sets a capability ceiling:
work that requires mutation still moves to another actor or process.
Read-only access does not make exposure harmless, because an agent can be
steered by anything it was allowed to see and data it holds can
leak, which is
[least exposure's](/notes/least-exposure-is-broader-than-least-privilege/)
whole argument. Where a human executes agent-drafted writes, that human
is the enforcement point and needs a bounded, intelligible approval
surface. The
[adoption path](/notes/adopting-mission-bound-authorization/#the-read-only-ceiling)
keeps read-only as a valid starting posture and stages broader authority
only where the added controls justify it.
**"Isn't this overkill?"** For any flow where the credential lifetime is the task, yes. Skip it. The layer is for authorization that must stay bound to approved work across a credential, session, actor, or domain boundary, and the Reference's [when it is the wrong tool](/notes/mission-based-authorization-field-reference/#competitive-landscape) draws the full line: what disqualifies is never size or the actor, it is the absence of a durable task.
**"The Mission record is itself a disclosure risk. You have built a
registry of business intent."** Yes, and the design treats it that
way. A Mission store is a high-value catalog of organizational activity,
and a reference projected across domains is a durable correlation
handle. The token-side claim (`id`, `issuer`, `authority_hash`) does not
directly carry the task description, but it still reveals that a shared
undertaking exists and who issued it. Deployments must minimize stored
intent, partition access, encrypt records, audit reads, and set retention
deliberately. Selective disclosure is the
[Mandate's](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html)
job when a third party needs some committed facts and not all of them.
The [adversary model](/notes/mission-based-authorization-field-reference/#the-mission-record-is-itself-sensitive)
carries the residual in full: evidence stores require access control
commensurate with the systems they describe, the evidence chain may
outlive the task, and no protocol feature makes cross-domain correlation
disappear.
An objection this page does not answer belongs in the
[issues on the draft repository](https://github.com/mcguinness/mission-bound-authorization/issues),
where the drafts move with the argument.
---
# Mission-Bound Authorization on the Wire
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-on-the-wire/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-authorization-on-the-wire.md
The [architecture chapter](/series/designing-mission-bound-authorization/)
carries the argument, the
[Building Mission-Bound Authorization](/series/building-mission-bound-authorization/)
chapter carries the controls this appendix accompanies, and the
[Field Reference](/notes/mission-based-authorization-field-reference/)
carries the definitions. This appendix carries the bytes: the
[running example](/notes/mission-based-authorization-field-reference/#the-running-example-end-to-end),
Alice's Q3 board packet, as the protocol exhibits an implementer would
actually see. Exhibits follow the draft family's editor's copies as of
July 15, 2026, and the drafts are the normative text. Standard OAuth
parameters that carry no Mission semantics are elided. Identifiers,
keys, and digests are illustrative, with two exceptions: `intent_hash`
and `authority_hash` reproduce byte for byte from the
[test vector](/notes/mission-based-authorization-field-reference/#reproducible-test-vector),
and the two `parameter_digest` values are real SHA-256 digests over the
JCS form of the parameter objects the exhibits name.
# 1. The proposal: Mission Intent via PAR
The shaper turned Alice's request into a candidate Mission Intent
([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)),
and the client submits it through a Pushed Authorization Request. The
`mission_intent` parameter value is the UTF-8 JSON serialization of the
Intent object, form-encoded on the wire. Decoded:
```json
{
"goal": "Prepare the Q3 board packet for the audit committee",
"purpose": "urn:example:mission:board-packet",
"resources": ["https://finance.example.com",
"https://docs.example.com",
"https://workflow.example.com"],
"constraints": ["Q3 2026", "Example Corp", "confidential"],
"expires_at": "2026-10-15T18:00:00Z"
}
```
The client never sends `authorization_details` alongside
`mission_intent`. It proposes a task. The Authorization Server derives
the authority.
# 2. The pivot: the approved record
The Authorization Server validates the Intent, derives the Authority
Set (`query_financials`, `create_doc`, `notify_reviewer`), renders the
disclosure, and on Alice's approval commits the Mission atomically. The
[concrete record](/notes/mission-based-authorization-field-reference/#a-concrete-mission-record)
lives in the Reference. The two anchors it commits are the ones every
later exhibit carries:
| Anchor | Value |
| --- | --- |
| `intent_hash` | `sha-256:jjx06KDh_TpWYhzSAvzEBH_lMz32eRj1tjgjNvt-crE` |
| `authority_hash` | `sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY` |
# 3. The projection: a Mission-bound token
The agent asks for authority to read the financials. Issuance is a
derivation, gated on the Mission being `active`, and the response
echoes the narrowed grant and the `mission_id` reference:
```json
{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
"token_type": "DPoP",
"expires_in": 300,
"mission_id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"authorization_details": [
{ "type": "mission_resource_access",
"resource": "https://finance.example.com",
"actions": ["query_financials"],
"constraints": { "period": "Q3 2026" } }
]
}
```
The decoded access-token claims carry the projection: a subset of the
Authority Set, a sender-constraint key, and the `mission` claim that
binds the credential back to the governance record. The token's `exp`
is minutes away. The Mission's `expires_at` is weeks away. That
asymmetry is the Durability law.
```json
{
"iss": "https://as.example.com",
"sub": "alice@example.com",
"aud": "https://finance.example.com",
"client_id": "s6BhdRkqt3",
"iat": 1790787780,
"exp": 1790788080,
"jti": "at_3Vq8nK2xT6mR9sY1pZ4d",
"authorization_details": [
{ "type": "mission_resource_access",
"resource": "https://finance.example.com",
"actions": ["query_financials"],
"constraints": { "period": "Q3 2026" } }
],
"cnf": { "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" },
"mission": {
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com",
"authority_hash":
"sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY"
}
}
```
# 4. The gate: an AuthZEN permit, bound to parameters
The packet is drafted and the agent notifies the audit committee. The
notify is externally visible, so the PEP at `workflow.example.com`
asks the PDP before acting ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/),
[AuthZEN binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html)).
The Mission, actor, credential, parameters, and freshness ride in
`context`, and because the PEP supplies `state`, the `freshness`
object is required with it:
```json
{
"subject": {
"type": "user",
"id": "alice@example.com",
"properties": { "iss": "https://login.example.com" }
},
"resource": {
"type": "notification",
"id": "notif_board_packet_q3"
},
"action": { "name": "notify_reviewer" },
"context": {
"mission": {
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com",
"authority_hash":
"sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY",
"state": "active",
"policy_version": "deploy-policy:v17"
},
"actor": { "client_id": "s6BhdRkqt3" },
"credential": {
"issuer": "https://as.example.com",
"expires_at": "2026-09-30T18:09:00Z"
},
"parameters": {
"group": "audit-committee",
"message": "Q3 board packet is ready for review"
},
"parameter_digest":
"sha-256:DCTAlrBUhQwW_t1EDxHCjdNkLbrT_Bc6nCLYkQubRU4",
"audience": "https://workflow.example.com",
"freshness": {
"mode": "fresh",
"freshness_at": "2026-09-30T18:05:00Z"
}
}
}
```
The permit comes back bound to exactly those parameters, with a
single-use decision identifier because the action is externally
visible:
```json
{
"decision": true,
"context": {
"decision_id": "dec_2FpQ8kV5nR1tX7mB4sJ9eL6wYc",
"action_class": "external_commitment",
"class_source": "deployment",
"parameter_digest":
"sha-256:DCTAlrBUhQwW_t1EDxHCjdNkLbrT_Bc6nCLYkQubRU4",
"policy_view_id":
"sha-256:CMcisP1vVPsdDxWs336YmmKoBLFgMro1SYVJJEK4sV8",
"permit_expires_at": "2026-09-30T18:06:00Z",
"single_use": true
}
}
```
The executing PEP recomputes the digest against the parameters it is
about to use. Change the message after the permit and the digest
mismatches. That is the TOCTOU gap, closed.
# 5. The refusal: `parameter_violation`
Steered by a prompt-injected document, the agent tries the same notify
(same `message`) against a different audience, the `press-list` group.
The Authority Set's constraint names `audit-committee`, so the
evaluation succeeds and the decision is no:
```json
{
"decision": false,
"context": {
"decision_id": "dec_6JwN3xT9rQ4mV8kP1sB5eZ2yLd",
"denial_reason": "parameter_violation",
"action_class": "external_commitment",
"class_source": "deployment",
"parameter_digest":
"sha-256:_0sG_Vm6EFHvqL5MMNYp-iL1KXXW5CGMr3NLXcXbKrc",
"policy_view_id":
"sha-256:CMcisP1vVPsdDxWs336YmmKoBLFgMro1SYVJJEK4sV8"
}
}
```
A denial is a successful evaluation, not a transport error, and it
lands in the evidence trail with the same `decision_id` discipline as a
permit.
# 6. The stop: revocation by `mission_id`
At 23:00 the board meeting is cancelled and an administrator ends the
task at the lifecycle endpoint ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)).
One authenticated call, keyed by the Mission, touching no token:
```http
POST /as/mission/lifecycle HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: DPoP eyJhbGciOiJFUzI1NiIsImtpZCI6...
mission_id=msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1
&operation=revoke
&reason=Board+meeting+cancelled
&nonce=nonce_8Y3vN0sM6tP1xR9bQ5
```
# 7. The announcement: a lifecycle event
Subscribed consumers learn the transition through the
Signals push, a Security Event Token whose strictly monotonic
`version` makes replayed or reordered events harmless. Decoded:
```json
{
"iss": "https://as.example.com",
"aud": "https://workflow.example.com",
"jti": "set_5R8wQ2kN7mX4vT1pB6zJ3e",
"iat": 1790809200,
"sub_id": {
"format": "opaque",
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1"
},
"events": {
"https://schemas.karlmcguinness.com/mission/lifecycle-change": {
"mission": {
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com"
},
"state": "revoked",
"prior_state": "active",
"version": 2,
"committed_at": "2026-09-30T23:00:00Z"
}
}
}
```
# 8. The check that stops the resume
At 02:00 the harness wakes to continue the draft and, before
dispatching anything, establishes current Mission state
([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)).
It needs state, not audience-scoped authority, so it omits `audience`
and the AS addresses the state-only response to the requester. The
signed Status response says the one thing no credential in the
session can: the approved task no longer exists.
```json
{
"iss": "https://as.example.com",
"aud": "client_board-packet-agent",
"sub": "client_board-packet-agent",
"nonce": "nonce_K9pV4nT2sR7mB1xQ",
"iat": 1790820005,
"exp": 1790820065,
"mission": {
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com",
"authority_hash":
"sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY",
"state": "revoked",
"expires_at": "2026-10-15T18:00:00Z",
"fresh_until": "2026-10-01T02:01:05Z"
}
}
```
The harness suppresses the resume and emits the
[evidence record](/notes/the-agent-runtime-and-audit/#sessions-are-not-authority)
the agent runtime part shows. Session continuity was recoverable. The
authority was not, and the Mission, not the session, decided.
# The signals beside the exhibits
The core's -01 revision named signals for the paths these exhibits
cross. No exhibit above changes, and a reader wiring this up should
know they exist:
| Signal | Where it rides | What it says |
| --- | --- | --- |
| `mission_derivation` | Token response | The derivation was partial, and how it narrowed |
| `mission_error` | Token endpoint error | Which non-active state refused the request, so a client can tell revoked from expired from superseded, or that derivations are exhausted |
| `derivations_remaining` | Introspection | The issuer-reported remaining derivations under `max_derivations` |
| `mission_denial` | `WWW-Authenticate` | Whether a resource denial is insufficient authority, a step-up requirement, or an unrecognized constraint |
| `mission_constraints_supported` | Protected-resource metadata | Which Common Constraints the resource can enforce |
Two smaller promotions ride along: the token response's `mission_id`
echo (exhibit 3) is now a SHOULD, and the `mission` claim can carry an
OPTIONAL `expires_at` member, a bounding commitment with no liveness,
so it never extends reliance and current state still comes from a
freshness source.
# Where each exhibit is normative
The [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html)
owns exhibits 1 through 3 and the integrity anchors. The
[runtime profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html)
and its [AuthZEN binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html)
own 4 and 5. [Status and Lifecycle](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html)
owns 6 and 8, and [Lifecycle Signals](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html)
owns 7. Where an exhibit and a draft disagree, the draft wins, and the
[Field Reference](/notes/mission-based-authorization-field-reference/) is the
citable summary of the whole model.
---
# Mission-Based Authorization: The Field Reference
Canonical URL: https://notes.karlmcguinness.com/notes/mission-based-authorization-field-reference/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-based-authorization-field-reference.md
This is the standing reference for the
[Mission-Bound Authorization handbook](/mission-handbook/).
It is written to be linked, quoted, and shared. The parts carry the
argument. This page carries the definition, the litmus test, the
landscape, and the vocabulary. The top of the page is the citation kit, each item written to be copied whole, with
[Mission-Bound Authorization on the Wire](/notes/mission-bound-authorization-on-the-wire/)
as the companion exhibits. The [litmus test](#what-counts-the-litmus-test)
and everything deeper follow. Everything here tracks the draft family's editor's copies as of July 15, 2026.
| Use this page to | Start at |
| --- | --- |
| Get the mental model first | [What the Corporate Card Already Solved](/series/what-the-corporate-card-already-solved/), then [the bridge into the architecture](/notes/from-the-card-to-the-architecture/) |
| Define the category | [The bottom line](#the-bottom-line) and the [litmus test](#what-counts-the-litmus-test) |
| Evaluate a vendor or deployment claim | [The vendor test](/notes/mission-based-authorization-vendor-test/), then [the implementation checklist](#the-implementation-checklist) and [what not to claim](#what-not-to-claim) |
| Map the neighboring standards | [The standards map](/notes/mission-bound-authorization-standards-map/): OAuth, WIMSE, and OpenID, with deltas and substitution hazards |
| Implement the reference security architecture | [The formula](#the-reference-security-architecture) and the [wire exhibits](/notes/mission-bound-authorization-on-the-wire/) |
| Define the primitive, its roles, and its name | [The object model](#the-mission-object-model), [trust boundaries and roles](#trust-boundaries-and-roles), and [why "Mission"](#why-mission) |
| See what changes and what never does | [The object model's aggregate and mutability rules](#the-mission-object-model) |
| Price revocation by path | [The statement](#revocation-in-one-statement) and [the matrix](#when-revocation-bites) |
| Place the record in the estate | [Where the Mission record lives](#where-the-mission-record-lives) and [the division of labor](#trust-boundaries-and-roles) |
| Draw the line with agent IAM | [Agent IAM and the Agent Registry](#agent-iam-and-the-agent-registry), with [three objects, three lifecycles](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles) behind it |
| Pick the right kill in an incident | [The revocation matrix](#when-revocation-bites), then [the containment matrix](/notes/the-agent-runtime-and-audit/#the-containment-matrix) |
| Check the record's own privacy | [The Mission record is itself sensitive](#the-mission-record-is-itself-sensitive) |
| Compare to scopes, sessions, PDPs, IGA, PAM | [The landscape](#competitive-landscape) and the [objections](/notes/common-objections-to-mission-based-authorization/) |
| Cite the draft family | [The catalog](#the-draft-family-at-a-glance) and [how to cite](#how-to-cite-this-handbook) |
# Mission-based authorization in brief
> **Mission-based authorization governs the approved *task*, not just the
> credential, session, or individual request.**
It is a *category*, not one product. **Mission-Bound Authorization**,
the [draft family](https://github.com/mcguinness/mission-bound-authorization)
this handbook explains, is one concrete instance of it, carried on
four substrate bindings with OAuth 2.0 as the flagship.
The vocabulary has a strict hierarchy:
| Term | What it names |
| --- | --- |
| **Delegated authority management** | The missing layer of the stack |
| **Mission-based authorization** | One design pattern for that layer, and the category this page defines |
| **Mission-Bound Authorization** | This draft family: one instance of the category, with OAuth 2.0 as the flagship binding, the standalone Mission Authority Server and AAuth beside it, and an experimental UMA 2.0 sketch as the fourth |
| **Mission** | The concrete approved-task object in this draft family |
| **Mandate** | A portable, verifiable statement about a Mission. Evidence, not a second object |
> A mission-based authorization system has a **durable approved task
> object**, **derives authority** from it, **checks consequential
> actions** against it, and **binds audit evidence** back to it.
Why existing objects are not enough: a **token** authorizes a request, a
**session** preserves runtime continuity, a **scope** names requested
authority, a **task/trace ID** correlates activity. None of them is the
*approved task* with a lifecycle that authority is derived from and gated
on. That object is the **Mission**.
The triad that makes it work:
> **Tokens carry authority. Missions govern purpose. PDPs enforce
> actions.**
And the hard truth:
> **A mission-bound token without runtime enforcement is governance
> metadata, not agent safety.**
## The bottom line
> Mission-based authorization is the missing layer between user intent
> and per-request authorization. It makes the approved task a first-class
> governance object, then binds tokens, runtime decisions, delegation,
> lifecycle, and audit back to that object.
And the positioning line, for the slide:
> **Identity says who. Credentials say what may be accessed. The
> Mission says what the work is, who approved it, and when it ends.**
## The Mission, in one sentence
> **The Mission is the durable, integrity-anchored, lifecycle-governed
> governance object for a user-approved task. In a Mission-Bound
> deployment, credential issuance, runtime decisions, and audit records
> project from it.**
And the questions, laddered, because each generation of the stack
answered one and the last never became first-class:
| Layer | The question it answers |
| --- | --- |
| Identity | Who? |
| OAuth | Who delegated? |
| Scopes | Roughly what? |
| Policy | Whether, right now. |
| **Mission** | **Why does this authority exist, and does it still?** |
And the shorthand contrasts, for the same slide:
| Concept | Answers |
| --- | --- |
| Prompt | *What was requested?* |
| Workflow | *How will it execute?* |
| Token | *Is this credential currently valid?* |
| Policy | *Is this request permitted right now?* |
| **Mission** | ***What authority exists, why, for how long, on whose approval?*** |
## The five laws of delegated authority
The layer's invariants, stated to be quoted. They hold on any
substrate, and the [handbook](/mission-handbook/) is an
enforcement mechanism for all five.
1. **Durability.** Authority must outlive credentials.
2. **Attribution.** Every action must remain attributable, and the approval record commits exactly what the approver was shown.
3. **Narrowing.** Authority can only narrow as work fans out.
4. **Termination.** Revocation must end authority, not merely tokens.
5. **Containment.** Execution must continuously remain inside approved purpose.
Each law is easiest to remember by its violation:
| Law | The failure when violated |
| --- | --- |
| Durability | The 02:00 resume: every credential valid, the approved task gone |
| Attribution | The blurred principal: nobody can say who acted under whose authority, through what chain |
| Narrowing | The borrowed card: the delegate inherits everything the delegator had |
| Termination | The gym that bills the replacement card: the ending that does not end |
| Containment | The deleted production database: nothing the agent did was outside what its credentials allowed |
The names are stable across the handbook: Durability is always Law
1, Containment is always Law 5. The specification family
operationalizes them as the Architecture document's seven Mission Invariants: the five laws plus two wire-level mechanics (enforcement fails closed, and the integrity anchors commit what was approved rather than prove its semantics).
And the design stance beneath all five, inherited from the
[Mission Shaping series](/series/mission-shaping/):
> **Survivable incorrectness: the system remains governable and limits
> damage even when the agent's semantics are partial or wrong. Perfect
> fidelity is not achievable at agent scale. Survivable failure is.**
## The numbers
The handbook counts several things, and each count has one job:
| Artifact | Job | Count | Relation |
| --- | --- | --- | --- |
| The five laws | Architectural invariants | 5 | What must remain true on any substrate |
| The litmus properties | The category gate | 4 + 2 | Four substrate properties claim the category, two more back an action-time defense claim |
| The vendor questions | The gate as an interview | 6 | One question per property |
| The Mission Assurance Levels | Deployment claims | 4 | How much of the architecture is deployed |
| The adoption stages | The build order | 3 | Crawl, walk, run |
| The three objects | Estate separation of duties | 3 | Agent identity (who), Agent Deployment (what runs), Mission (why) |
| The containment kills | Incident blast radii | 6 | Mission, agent, Agent Deployment, credential, workload, egress |
| The bindings | Substrates | 4 | Where the Mission record lives: OAuth AS, standalone MAS, AAuth, and the experimental UMA sketch |
| The binding security architectures | How enforcement composes | 3 | Credential-carried, PDP-joined, authority-native |
| The Five Packages | The deployable decomposition | 5 | Mission Control, Authority Distribution, Runtime Enforcement, Agent Execution Governance, Evidence and Accountability |
The six questions are the six properties in interview form, and the
laws are what the properties enforce.
## The layer vocabulary
Four functions any implementation of the layer must supply, whatever it
names its governance object:
- **Authority compilation:** approved intent becomes bounded,
integrity-anchored authority.
- **Authority projection:** that authority reaches instances,
credentials, domains, and delegates without ever exceeding its
source.
- **Authority containment:** every consequential action is checked
against the approved purpose at the point of use.
- **Authority continuity:** reliance stays conditioned on the current
state of the task, across time and across the runtime.
## The reference security architecture
> **Reference security architecture = issuance core + runtime
> enforcement + AuthZEN binding + a freshness source** (Status, or
> issuer introspection)
This is the Runtime-Enforced level as a formula, and the sizing is real: a substantial build, not a wedge, measured from the runtime
profile's conformance section rather than from this one line. Every
dependency in it is a ratified OAuth RFC or a finalized OpenID
specification, with one tracked exception scoped out of the formula: the issuance core's single Internet-Draft reference, the Actor
Profile, is confined to its OPTIONAL delegation capability. Lifecycle
Signals, the stable push complement, sits outside the formula.
The [architecture chapter](/series/designing-mission-bound-authorization/#the-reference-security-architecture)
carries the sizing argument, and
[Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/)
carries the staged build order.
## The honest deployment claim
A conformance claim names its assurance level and its enforcement
scope, and every line can be verified against the
[implementation checklist](#the-implementation-checklist) below. The
level names the dependency bundle a deployment runs. The claims name what it can prove: proof obligations become available at a level, and a relying party compares claims, not level names. The architecture's Mission Deployment Profile
is the spec-level form of this claim: the composition of the
per-layer statements the profiles themselves demand, covering level,
binding, state sources, PEP coverage, custody, evidence, and
`residual_risks`, with the machine-readable manifest schema named as
deferred family work. The coverage
split is the load-bearing part: which paths are mediated per action,
which are issuance-gated only with revocation bounded by the token
lifetime, and which are unmediated and named as exclusions.
| Claim line | Example |
| --- | --- |
| Claim | Runtime-Enforced |
| Scope | Finance, docs, and workflow APIs |
| Enforcement | PEP at MCP `tools/call` and at the resource APIs |
| Mediated paths | Finance and docs APIs, and every `tools/call` |
| Issuance-gated only | Workflow API: revocation bounded by a 10-minute token lifetime |
| Unmediated paths | None claimed |
| Freshness | Mission Status within 30 seconds on mediated paths |
| Signals | Workflow-domain push revocation |
| Evidence | Decision Evidence for all consequential calls, denials included |
| Exclusions | No runtime-enforcement claim for direct shell egress |
## What not to claim
The negative space of the claim, stated to be quoted:
- **A mission-bound token alone is not agent safety.** It is governance
metadata until a PEP checks each consequential action against it.
And the core's taxonomy polices the word itself: a token that merely
references or carries Mission data without state-gated issuance is
not Mission-bound, only Mission-referenced or Mission-derived.
- **Status without PEP coverage is not runtime enforcement.** Freshness
feeds a gate. It does not replace one.
- **Signals without a fail-closed state source is not revocation
safety.** A missed event must read as stale state, never as still
active.
- **Harness logs without mediated execution paths are not
containment.** A record of the resume is not a boundary on it.
- **Audit transparency is evidence, not prevention.** It makes a false
record permanent and attributable. It stops nothing.
- **A renewed charter is not a governed standing agent.** Renewal is a
review only when the prior cycle's record is in front of the
reviewer, and a ceiling whose renewals carry no evidence review is a
blank check with a calendar.
## Revocation, in one statement
> **Revocation changes the Mission's authoritative state immediately.
> Enforcement latency is path-dependent: runtime-gated actions stop
> within the published freshness bound, new derivation stops when the
> issuer observes state, and outstanding offline-valid tokens run to
> expiry unless the path checks Mission state.**
## When revocation bites
Termination is a law, and its implementation burden lives in this
table. Revocation ends authority only where enforcement consults live
Mission state within a published bound. What a deployment runs
determines what it may claim:
| Enforcement path | Worst-case revocation latency | What you may claim |
| --- | --- | --- |
| PEP + Status polling (or issuer introspection, or the swarm-scale [Status List](/notes/mission-lifecycle-and-change/#observe-and-revoke-pull-and-push)) | Staleness bound + permit validity window + the class's execution bound | Runtime revocation within a published freshness bound |
| PEP + Signals push, with Status fallback | Seconds, degrading to the polling bound when the stream goes quiet | Prompt revocation that never fails open |
| Issuance gating only, no PEP | The outstanding token lifetime | Bounded-staleness revocation at the token lifetime: the legacy-estate bridge, a conforming freshness source when the bound is published, for classes below high-consequence |
| Short-lived tokens alone | The token lifetime, renewed forever | Nothing: a revoked task keeps deriving fresh tokens unless issuance is gated on task state |
| An unmediated path | Never | Nothing. Name it in the enforcement scope |
Revocation is also only one kill among six. [The containment matrix](/notes/the-agent-runtime-and-audit/#the-containment-matrix) places Mission termination beside the agent, Agent Deployment, credential, workload, and egress kills, each with its own blast radius and owner, because revoking a Mission terminates no process and closes no network path.
## The vendor test
The test is its own page, built to be linked and pasted into an
evaluation:
[The Mission-Based Authorization Vendor Test](/notes/mission-based-authorization-vendor-test/).
Six questions, the [litmus property](#what-counts-the-litmus-test) each
one probes, and what failing answers sound like. A vendor that passes
all six should be able to write the
[honest deployment claim](#the-honest-deployment-claim) above, and the
[implementation checklist](#the-implementation-checklist) is how you
verify it.
## The canonical picture
One diagram for the whole model: the six stages, and the actors that
own each.
```mermaid
flowchart LR
subgraph S1["Intent"]
U([User])
SH[Shaper]
end
subgraph S2["Mission"]
MI[Mission Issuer
OAuth AS]
M[("Mission record
intent_hash, authority_hash,
state")]
end
subgraph S3["Authority"]
AG[Agent instance
+ act chain]
end
subgraph S4["Enforcement"]
PEP[PEP]
PDP[PDP]
RS[Resource Server]
end
subgraph S5["Lifecycle"]
ST[Status pull /
Signals push]
H[Harness]
end
subgraph S6["Evidence"]
AUD([Auditor])
end
U --> SH
SH -->|Mission Intent via PAR| MI
MI -->|renders derived authority| U
U -->|approves| MI
MI --> M
M -->|state-gated issuance,
mission-bound token| AG
AG -->|action + parameters| PEP
PEP -->|evaluate| PDP
PDP -->|permit / deny| PEP
PEP --> RS
PDP -.->|current state| ST
ST -.-> M
ST --> H
H -.->|stop on non-active| AG
M -->|lifecycle events| AUD
PDP -->|decision evidence| AUD
PEP -->|execution evidence| AUD
```
Everything below is the detail behind the bottom line: what qualifies as
mission-based ([litmus](#what-counts-the-litmus-test)), how it differs
from what you already run ([landscape](#competitive-landscape)), the
smallest useful deployment ([the ladder](#the-deployment-ladder)),
the checkable claim ([checklist](#the-implementation-checklist)),
what it does *not* solve ([non-goals](#threats-and-non-goals)), the
[glossary](/notes/mission-bound-authorization-glossary/) (Appendix F), and one [worked example](#the-running-example-end-to-end).
# Mission-based authorization as a category
The field has converged on the same gap from several directions:
agent IAM, intent-based access control, capability systems, the lethal
trifecta. The shared answer is to elevate the *approved task* to a
first-class object. That move is the category. A system is in the
category whether it calls the object a Mission, a charter, or a governed
task, and whether it rides on OAuth, on a clean-slate agent protocol, or
on something else. The decisive distinction from every task-shaped
record nearby is not the fields: the object is the authorization root,
with authority derived from it, execution enforced against it, and
lifecycle and evidence joined on it. In infrastructure terms the category is
[the control plane for delegated authority](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority):
the layer that holds the desired state of the work, with credentials
and enforcement as the data plane reconciled against it.
> **Identity was the control plane for human access. The Mission layer
> is the control plane for delegated authority.**
The category is not enterprise-shaped either, even though this
handbook's examples deliberately are. A vacation-planning agent's
Mission has the same anatomy as the board packet's (an approved task,
authority derived from it, an expiry, and evidence that joins), and
the same is true for the smart-home agent or the assistant booking a
dinner. The handbook's examples stay in the enterprise because that is
where the adoption pressure, the estates, and the approval workflows
live, not because the object knows the difference.
This handbook is about one instance: **Mission-Bound
Authorization**, the draft family where the object is the **Mission**
and enforcement uses a PEP/PDP contract. Its flagship binding is OAuth
2.0, where authority is derived as Rich Authorization Requests and the
token binding is the `mission` claim. The standalone Mission Authority
Server, AAuth, and (as an experimental sketch) UMA 2.0 bindings carry
the same object on other substrates.
Where this page says "mission-based," it means the category. Where it
says "the Mission" or names a draft, it means this instance.
**The category does not depend on OAuth.** OAuth is the substrate used
here because it is the dominant deployment reality, and it already
supplies the derivation, exchange, sender-constraint, and revocation
machinery a Mission binds to. The family itself now demonstrates the
independence: the
[AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html)
hosts the same governance object at the AAuth Person Server,
[Mission Substrate Requirements](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-substrate.html)
consolidates what any further binding must provide, and the
experimental [UMA 2.0 sketch](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-uma.html)
is the first binding authored against that contract, with RPT issuance
and upgrade gated on Mission state. The reference model
is the six properties. OAuth is the flagship binding of it.
## Mission-based authorization and IBAC
**Intent-Based Access Control (IBAC)** is the *property*: authorize by
what the user approved, not by what an agent infers at runtime. Mission-
based authorization is the *mechanism* that makes IBAC practical, by
moving interpretation to admission, before any authority exists, where
an accountable approver (the user in the common case, an authorized
policy tracing to one otherwise) decides against committed inputs, and
committing the result so enforcement *consumes* approved intent instead
of reconstructing it. The distrust of runtime-stated intent is shared
ground now: the proposed
[CB4A credential broker](https://datatracker.ietf.org/doc/draft-hartman-credential-broker-4-agents/)
meets the same threat and resolves it by demoting the agent's stated
justification to audit evidence, excluded from authorization. The
Mission resolves it the other way, by making *approved* intent the
authorization root. **IBAC is the property. The Mission is the object
that carries it.**
# What counts: the litmus test
The test splits at the family's own conformance seam, because the
family defines conformant deployments at two strengths. A system is
**mission-based**, providing Mission-substrate conformance (the bar
the Baseline Issuance level and every binding meets), when the first
four hold:
1. **Approved task object:** a durable record of the task a human (or
authorized policy) approved. Not a prompt, trace ID, session, ticket,
or token.
2. **Authority derivation:** credentials and decisions are *derived
from* that approved task, not minted independently of it.
3. **Narrow-only delegation:** derived authority, child tasks, and
sub-agents can only narrow. Exceeding the parent requires a fresh
approval.
4. **Lifecycle:** the task can expire, be revoked, expand (via fresh
approval), and complete. Only an active task permits reliance.
It provides **Runtime-Enforced Mission conformance**, the bar behind
any action-time defense claim, when two more hold:
5. **Runtime enforcement:** consequential actions are checked against
the *current* task state at the point of use, not just at issuance.
6. **Evidence:** decisions and lifecycle events bind back to the task,
so audit can reconstruct it. This is verifiable continuity: the
approval, the decisions including denials, and the executions can
be shown to belong to one undertaking.
Relax any of the first four and the design is not mission-based:
scopes without a task, sessions without approval, a PDP without an
approved object. Hold the first four without the last two and it is
mission-based at issuance strength, which is what a Baseline
deployment is, and it claims nothing about action-time defense.
## What looks like a Mission but is not
The object-level version of the same test, useful when someone points
at an artifact and asks "is that the Mission?"
| Object | Why it is not a Mission |
|---|---|
| Prompt | What the user typed: free-form, untrusted, upstream of every governance object. The Shaper turns it into a proposal, and approval turns the proposal into a Mission. |
| Workflow | How the agent will execute, not what was approved. Two workflows for the same task share nothing at the protocol layer. |
| Ticket | Human work tracking. It references a task without bounding, deriving, or revoking authority. |
| Access token | A short-lived projection. `jti` identifies the token, not the task. |
| Scope / authorization detail | Expresses authority, not the approved task or its lifecycle. |
| Consent record | Proves an approval event. Does not govern the resulting work over time. |
| Session | Preserves runtime continuity. Commits no maximum authority. |
| Policy | Evaluates requests. It is not the user's approved task. |
| `purpose` URI | Labels a task *class*. Has no instance lifecycle. |
| Task / trace ID | Correlates activity. Carries no authority or approval. |
| OAuth grant | Records a delegation event. It carries no task, no lifecycle, and no purpose. |
| Relationship (a ReBAC tuple) | Encodes who relates to what, timelessly. No approval, no task, no end. |
| Delegation chain | Records actors, not the mandate they act under. |
## What becomes possible only with a Mission
The case for the Mission as a primitive, not just a useful design
pattern, is concrete. The following all require a shared,
integrity-anchored task object, and none is reliably achievable through
disciplined use of existing OAuth primitives alone:
- **Cross-audience revocation of a long-running task.** Without a shared task identifier, revoking an agent's work requires hunting credentials at every audience independently. With the Mission, revocation at one state authority terminates future derivation across every audience that ever projected from it.
- **Cross-hop audit join on the user-approved task.** Without a shared identifier, audit reconstruction stitches per-AS logs by timestamp and client identifier. With the Mission, every record across every audience and substrate joins through `mission.id` and `mission.issuer`.
- **Cryptographic commitment to the approved record.** Without integrity anchors over a canonical Mission Intent and Authority Set, the authority's record of approval is reconstructed from per-token `authorization_details` and consent-system logs. With `intent_hash` and `authority_hash`, the approved intent and the derived authority are committed at activation and can be checked for later modification, and the Consent Evidence companion's `consent_rendering_hash` commits the presented disclosure where that profile is deployed. The hashes do not prove that a renderer displayed those objects faithfully or that a human understood them.
- **Lifecycle as an authorization input.** Without a Mission state machine, refresh and exchange gate on token validity alone. With a Mission, refresh, exchange, ID-JAG issuance, and PDP decisions all consult Mission state. A suspended or revoked Mission stops future derivation regardless of credential expiry.
- **Risk evaluated at the undertaking's grain, not the call's.** Without the Mission, the resource prices every action in isolation, and "delete database" carries the same risk whether it is a stray action or step four of an approved migration. With the Mission, the decision knows the committed why and the undertaking's recorded progress, because evidence joins on `mission.id`, so a precondition like "the copy steps completed" is a checkable fact rather than a hope. The context is committed and recorded, never inferred, and no per-call primitive can supply it, because it does not exist at the resource to be consulted.
- **Governance without changing the issuer.** Without a Mission service, a deployment that cannot modify its Authorization Server has no governance object at all. With the Mission Authority Server, the Mission record and its lifecycle live in a standalone service that serves the status and lifecycle surfaces itself, and a PDP joins ordinary tokens to the Mission at the point of use.
Each of these can be approximated with deployment-specific extensions.
None is interoperable across vendors without a standardized object.
That is the difference between a useful design pattern and a primitive.
# The Mission object model
The approved task is a typed object, not a label, and it is an
aggregate of three logically separate components plus a stable
identity:
**The approval commitment**, immutable after approval:
- **Purpose:** an optional task-class URI, *not* the instance.
- **Mission Intent:** the structured, approved task description, committed
by `intent_hash`.
- **Consent reference:** a pointer to the canonical disclosure behind
the approval, hashed by the optional Consent Evidence companion so an
auditor can verify the stored disclosure has not changed.
**The authority ceiling**, immutable after approval:
- **Authority Set:** the maximum grantable authority derived from the
Intent, committed by `authority_hash`. Authority is *one component* of
the Mission, not the whole.
- **Delegation context:** credentials derived for delegated actors stay
bounded by the same Mission and preserve authenticated actor context
(the RFC 8693 `act` chain where an adopted profile supplies it). Token
Exchange never creates a child Mission, and broader authority is a
separately approved successor via Mission Expansion.
**The lifecycle state**, mutable and versioned:
- **Lifecycle state:** owned by the issuer, with a state version for
concurrency control. Only `active` permits reliance.
**The identity**, immutable:
- **Identity:** `id` and `issuer`, the same pair on the record and on
the `mission` claim that every projection carries.
The mutability rules answer the questions the aggregate raises. The
hashes commit the approval side and never change afterward, so a
lifecycle transition moves the state and its version, never a hash.
Suspension changes reliance, not the approved task. Completion records
fulfillment and retires reliance. Widening anything committed is a
separately approved successor Mission, never a mutation. And a Child
Mission inherits a subset of the parent's ceiling under the subset
rule, with its own lifecycle bounded by the parent's.
"Consent" here names the Mission-layer authorization anchor, the
legitimacy source that approved this specific Mission. That is a
narrower technical use than GDPR-style data-processing consent, and
deployments subject to those regimes still owe their own consent
contracts on top. Where no user is present at approval time, the anchor
is a prior human-approved template Mission, a standing organizational
policy in a formal auditable language, or a verifiable standing
delegation from a service owner. LLM inference about organizational
intent, runtime configuration supplied by the agent, and pre-existing
general-purpose scope grants anchor nothing, because they do not
establish authority for a specific Mission.
Everything an agent touches is a *projection* of this object: a
Mission-bound token, a runtime decision, a child Mission, a lifecycle
signal, an evidence record. Each carries the Mission reference and
derives from, never exceeds, the Authority Set.
The subset rule, compactly. Every derivation, delegation, exchange,
and attenuation yields authority that is:
- the same or a narrower resource set (exact match by default, or the
opt-in `prefix` containment),
- the same or a smaller action set,
- the same or tighter constraints,
- expiry no later than the Mission's `expires_at`,
- per-entry delegation policy no broader (`max_depth` no greater,
`allowed_delegates` no wider),
- and the same `mission` claim, because re-binding to a different
Mission is refusal, and crossing a trust domain rides a separately
approved, audience-scoped
[projection](/notes/mission-bound-authority/#crossing-authorization-domains).
Widening anything on that list is a fresh approval, never an
inference.
One distinction matters. What the machinery tests is
*representational* narrowing: the child's authority is formally no
broader under the comparison relation the entries define. *Semantic*
narrowing, that the child cannot produce effects outside the parent's
approved boundary, is a stronger property that representation alone
cannot always prove, especially where constraints are contextual,
quantitative, or translated across domain vocabularies. Where the two
can diverge, the family's answer is conservative refusal or a
separately approved projection, never an optimistic mapping.
> The Mission contains the Authority Set. The Authority Set does not
> define the Mission. A bundle of permitted actions with no approved
> task, lifecycle, or evidence is just authority, which OAuth already
> had.
## A concrete Mission record
The running example as a record. Its `intent_hash` and `authority_hash`
are the ones reproduced byte-for-byte in
[Reproducible test vector](#reproducible-test-vector) below.
```json
{
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com",
"state": "active",
"intent": {
"goal": "Prepare the Q3 board packet for the audit committee",
"purpose": "urn:example:mission:board-packet",
"resources": ["https://finance.example.com",
"https://docs.example.com",
"https://workflow.example.com"],
"constraints": ["Q3 2026", "Example Corp", "confidential"],
"expires_at": "2026-10-15T18:00:00Z"
},
"authority_set": [
{ "type": "mission_resource_access",
"resource": "https://finance.example.com",
"actions": ["query_financials"],
"constraints": { "period": "Q3 2026" } },
{ "type": "mission_resource_access",
"resource": "https://docs.example.com",
"actions": ["create_doc"],
"constraints": { "template": "board-packet" } },
{ "type": "mission_resource_access",
"resource": "https://workflow.example.com",
"actions": ["notify_reviewer"],
"constraints": { "group": "audit-committee" } }
],
"intent_hash":
"sha-256:jjx06KDh_TpWYhzSAvzEBH_lMz32eRj1tjgjNvt-crE",
"authority_hash":
"sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY",
"subject": { "iss": "https://login.example.com",
"sub": "alice@example.com" },
"approver": { "iss": "https://login.example.com",
"sub": "alice@example.com" },
"client_id": "s6BhdRkqt3",
"policy_version": "deploy-policy:v17",
"approval_event_id": "ape_5D8wQ1kR7mX2vT4nH6jZ",
"created_at": "2026-09-30T17:03:00Z",
"expires_at": "2026-10-15T18:00:00Z"
}
```
Derived tokens carry the record's `id` and `issuer` in the `mission`
claim alongside `authority_hash`. `subject` and `approver`
are `{iss, sub}` pairs, authoritative for principal equality, and they
may differ when an administrator approves on a user's behalf. The record
also carries its issuance context: the agent's `client_id`, the
`policy_version` the Authority Set was derived under (an opaque audit
correlator, not a re-derivation promise), the `approval_event_id`, `created_at`, and a top-level
`expires_at` mirroring the Intent's. The consent disclosure the
Approver saw is committed separately by the Consent Evidence companion
([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)).
# Lifecycle states
The issuer owns the state machine. The core defines three states.
Companion profiles add more, and the one rule a consumer always applies
is that **only `active` permits reliance**. Every other state,
recognized or not, is treated as non-active, so the model fails safe as
it evolves.
```mermaid
stateDiagram-v2
[*] --> active: Approval event
active --> revoked: Termination (user, admin, policy)
active --> expired: expires_at reached
active --> suspended: Pause (Status)
active --> completed: Task done (Status)
active --> superseded: Expansion successor
active --> cascaded: Parent terminal (Child Delegation)
suspended --> active: Resume
suspended --> revoked: Termination
suspended --> expired: expires_at reached
suspended --> completed: Task done (Status)
suspended --> cascaded: Parent terminal (Child Delegation)
revoked --> [*]
expired --> [*]
completed --> [*]
superseded --> [*]
cascaded --> [*]
```
- **`active`** *(core)*: approved. Derivation and reliance permitted.
- **`revoked`** *(core)*: terminated by user, admin, or policy. Terminal.
- **`expired`** *(core)*: `expires_at` passed. Terminal.
- **`suspended`** *(Status companion)*: paused. Reversible to `active`.
- **`completed`** *(Status companion)*: finished. Terminal. Legal from
`active` or `suspended`.
- **`superseded`** *(Expansion companion)*: replaced by an approved
successor. Terminal.
- **`cascaded`** *(Child Delegation companion)*: a child terminated
because its parent reached a terminal state. Terminal, and distinct
from `revoked` so audit can tell a cascade from a direct termination.
A consumer that has never heard of `suspended`, `superseded`, or
`cascaded` still refuses to rely on them, because they are not `active`. New states are
therefore safe to add. (One exception, by design: an unrecognized
entry-level `terminal_when` discharge condition must fail *closed*, not
be ignored. See [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/).)
# Trust boundaries and roles
Mission-Bound Authorization spans multiple parties. Each is trusted for
a specific bounded responsibility, and explicitly not trusted for
adjacent ones. This is the canonical role map, and the profiles
populate it for their substrate.
| Activity | Trusted party | Trusted for | NOT trusted for |
| --- | --- | --- | --- |
| **Shaping Mission Intent** | Mission Shaper (client-side) | Producing a structured proposal from user input. | Authorizing anything. The Shaper's output is untrusted until the state authority validates it. |
| **Validating Mission Intent** | State authority (OAuth AS, or the MAS in the AS-optional mode) | Admitting or refusing the Mission Intent against deployment policy and requester bounds. Narrowing applies to the authority derived from it, never to the Intent. | Originating the user's task. The proposal comes from the Shaper or orchestrator. Validation accepts or refuses what is submitted. |
| **Deriving Authority Set** | State authority | Translating an approved Mission Intent into the maximum permitted Authority Set, scoped to resources and actions registered with the state authority. Derivation is mechanical, in one of two modes: narrowing from the Intent's proposed authority (recommended) or template instantiation from deployment policy. | Inventing authority not anchored in the Intent. The Authority Set is derived from approval, not enlarged by issuer policy alone. |
| **Rendering consent** | State authority (in the AS-optional mode, the MAS renders it itself) | Presenting the validated Intent and derived Authority Set to the approving principal, and committing to the rendered disclosure via the Consent Evidence companion's `consent_rendering_hash` where deployed. | Proving that the principal understood the disclosure. Consent UX assurance, language clarity, and human comprehension live above the protocol layer. |
| **Storing Mission state** | State authority | Committing the Mission record (Intent, Authority Set, integrity anchors, lifecycle state, consent reference). Owns the lifecycle state machine. | Owning credential issuance independently. Issuance gates on Mission state. The Mission record does not become an access credential by itself. |
| **Projecting authority into credentials** | Credential issuer (the OAuth AS for access tokens and ID-JAGs, with other substrates' issuers as future work) | Issuing audience-bound credentials that carry the Mission reference and stay inside the Authority Set. | Enlarging authority beyond the Authority Set. Every projection is a subset of the approved authority. |
| **Enforcing policy at runtime** | PDP (consulted by the Resource Server's PEP, or by an orchestrator PEP) | Evaluating each consequential action against current Mission state, the audience-relevant Authority Set projection, authenticated actor context, and Resource policy. | Replacing the state authority's authority commitment. The Authority Set is the upper bound. The PDP narrows, never widens. |
| **Emitting evidence** | Every party that makes a decision (admission, consent, lifecycle, runtime) | Producing a record bound to `mission.id` and `mission.issuer`, carrying the integrity anchors and binding evidence. | Mutating the Mission record. Evidence records reference the Mission. They do not modify it. |
The division of labor across domains is worth one plain statement.
The Mission Issuer governs the approved objective and its global
ceilings. The resource authority defines resource-local actions and
constraints. The PDP evaluates the intersection of the two. And
translation across authority vocabularies is trusted, verified, or
separately approved, never assumed.
The most important boundary is the first one: **the Mission Shaper is
never an authorization component.** It produces a proposal the state
authority can validate or refuse. Diagrams that place the Shaper inside
the authorization trust envelope are wrong.
## Agent IAM and the Agent Registry
The division of labor with agent identity is one sentence. **Agent IAM preserves who is acting. Mission-Bound Authorization preserves why their authority exists.** Neither replaces the other, and the combined stack is the deployment story:
1. The Agent Registry and workload identity authenticate an approved agent instance.
2. The Mission and its derived Authority Set say what sanctioned work that instance carries.
3. Per-hop credentials narrow that authority, audience by audience.
4. The runtime layer enforces each consequential action and its parameters at the point of use.
5. Decision, execution, and lifecycle evidence join on the Mission.
The registry itself is a complementary dependency, not part of the Mission system, and this family defines no agent identity and no registry ([three objects, three lifecycles](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles)). Where one exists, the Mission Issuer and the PDP consume a small, stable slice of it:
| Consumed from the registry | Used for |
| --- | --- |
| Agent identifier and owner | Attribution, and admission of the Mission Intent |
| Current status and revocation state | A gate on issuance and reliance |
| The approved Agent Deployment | The behavioral version in force, and the target of the core's OPTIONAL `controls.agent_deployment` pin: the class-grain authorization subject a swarm of attested instances executes under |
| Eligibility bounds | What the registry permits the agent to be approved for. A derivation input, never a grant |
| Risk tier | Policy input for approval routing and enforcement strictness |
Registry state is a state source like any other. The consuming decision point treats it under the runtime profile's freshness discipline, with a declared staleness bound, failing closed when the state cannot be established. And the three lifecycles gate conjunctively: all three must be live, and each is checked on its own.
## Who owns the meaning
Three layers need three different relationships to a vocabulary none of them owns jointly. The resource owns the ontology: only the finance system knows what `query_financials(period, entity)` means, which parameters are dangerous, and which constraints its objects can enforce, because it implements the call. Governance needs enough of that meaning to derive and render faithfully, since the Authority Set is derived against resource-defined action semantics and the approval discloses meaning the issuer does not own. Policy needs enough of it to evaluate, since the PDP checks concrete parameters against entries whose semantics the resource defined. [The ontology problem](/notes/least-privilege-mcp-tool-calls/#the-ontology-problem) is the published statement of why this is hard, and the family answers it with four supply mechanisms rather than one global vocabulary:
| Mechanism | What the resource supplies | Who consumes it |
| --- | --- | --- |
| Registered semantics | Common Constraint keys with registered meaning, and RAR types registered with the issuer | Derivation, and the translation floor at approval |
| Published metadata | `mission_constraints_supported` and RAR-type metadata declaring what the resource can enforce and accept | The issuer at derivation, and the client before it asks |
| Capability-source binding | A content digest of the tool or API description authority was derived over | The PDP, refusing when the capability drifts from what was approved |
| Resource-declared semantics | The resource publishes its operations, their human meaning, and their consequences, hash-committed (AAuth's exploratory R3 is this shape) | Approval rendering composes the resource's own words, and the record commits what the resource declared beside what was asked and what was approved |
The direction matters. OAuth's inherited ontology is client-proposed: the client asks in types the issuer must understand, which is why a generic multi-tenant issuer struggles with domain meaning. The resource-declared direction inverts it, and [where semantics should live](/notes/open-world-oauth-still-needs-mission-shaping/#-rar-r3-and-where-semantics-should-live) is the published treatment. Either direction ends at the same rule: translation across authority vocabularies is trusted, verified, or separately approved, never assumed.
And the asymmetry runs both ways. The resource owns what an action
*means*. The Mission owns *why it is happening* and where the
undertaking stands. A risk decision needs the join, because semantics
without purpose prices every delete the same, and purpose without
semantics cannot read the call.
# Where the Mission record lives
By default, the Mission record lives at the substrate's state
authority. On the OAuth substrate that is the Authorization Server,
which validates Intents, runs approval events, stores the record, and
gates issuance on its state. The substrate-local default keeps the
minimum profile coherent, since a deployment can claim the
[Baseline Issuance level](/notes/adopting-mission-bound-authorization/#run-governed-and-beyond)
without introducing a new server component.
The [Mission Authority Server (MAS)](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html)
is the standalone binding, the AS-optional mode: beyond serving
deployments whose Authorization Server cannot yet change, it is the
estate control plane for approved-task authority, one Mission Issuer
spanning many Authorization Servers, SaaS systems, APIs, and agent
runtimes, with an Enterprise Mission Authority Profile above its
conformance floor. The MAS is a standalone service that implements the
Mission Issuer role without being an OAuth AS. It validates Mission
Intents, runs approval events, records Missions, operates the
lifecycle, and serves Mission state. It derives no tokens. Access
tokens remain ordinary OAuth tokens with no `mission` claim, and a
Policy Decision Point joins each presented credential to its Mission at
the point of use, enforcing through the runtime profile.
That difference is architectural, not just topological. The MAS mode
buys Mission governance and per-action enforcement with no change to
the deployment's Authorization Server, at the cost of Mission-bound
credentials and issuance gating. Without that join, revoking a Mission
stops nothing at the token layer, so enforcement rests entirely on PEP
coverage. The
[issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html)
is the middle path that restores the token-layer chokepoint: estate
Authorization Servers redeem MAS-minted grants for Mission-bound,
state-gated tokens without moving approval into the AS, so one
canonical Mission record gates credentials minted across many issuers.
# Competitive landscape
Each of these is real and useful, and none is a substitute for an
approved task object. Mission-based authorization composes with them
rather than replacing them.
| Approach | What it solves | What it misses (for a governed task) | Law it breaks alone | How Mission composes with it | Enough on its own when |
|---|---|---|---|---|---|
| OAuth scopes / RAR | Expresses requested authority | No durable task lifecycle | Durability, Termination | Authority is derived from the Mission into RAR-shaped entries and projected into tokens with the `mission` claim | The credential's lifetime *is* the task (one grant, one resource) |
| Agent identity (WIMSE, SPIFFE, instance attestation) | Who is acting, provably | Not what the acting is for, or until when | Containment | Attested instances and actor chains are the substrate Mission authority binds to | The risk is impersonation, not ungoverned work |
| Sessions | Runtime continuity | Not approval or authority | Durability | The harness binds resumable session state to Mission state and re-checks before continuing | A human drives every consequential action |
| Workflow / task IDs | Operational tracking | Not interoperable authority | Termination, Containment | Workflow steps and unwind plans reference the Mission as the governed subject, not merely a work item | You need orchestration, not authorization |
| Trace IDs | Correlation | Not governance | Attribution | Evidence and logs carry the Mission reference so correlation joins to approved authority and lifecycle | You only need to join logs, not gate actions |
| PDP / ABAC / ReBAC | Per-request decisions | No approved task object by default | Durability | The PDP evaluates each consequential action against current Mission state, derived authority, actor context, and resource policy | Per-request attributes fully capture intent |
| Agent approval prompts | Human checkpoint | Often fragmentary and unauditable | Attribution | Consent Evidence and action-bound approval turn prompts into recorded decision input linked to the Mission | Volume is low enough to vet each action |
| Read-only agents + human-in-the-loop writes | Caps mutation blast radius while agents are piloted | The value ceiling: reads still steer and leak the agent, and the human executing the writes becomes the fatigued, unmediated enforcement point | Containment | The Mission makes write authority grantable: right-sized derivation, per-action permits, and exposure discipline replace the blanket deny | The work is genuinely read-only and the exposure surface is bounded |
| IGA access requests | Governed approval of entitlements | The grant it produces is standing authority: no task binding, no runtime enforcement, no automatic end | Termination, Containment | Deferred Approval is deliberately shaped like an IGA review, and the approval's output is a bounded, enforced, self-terminating Mission instead of a standing entitlement | Access is to durable roles, not tasks, and humans exercise it |
| PAM / just-in-time elevation | Time-boxed privileged access with check-out and recording | Elevates an identity, not a task. Session recording is evidence after the fact, not a permit before the action | Containment | A Mission is task-scoped elevation: authority derives from the approved work, each action needs a permit, and revocation ends the task everywhere | The privileged principal is a human whose session ends when they log off |
| Credential brokers (the proposed [CB4A](https://datatracker.ietf.org/doc/draft-hartman-credential-broker-4-agents/)) | No real credentials on agents: just-in-time, short-lived, sender-constrained leases minted per request, with custody done properly | The lease is not the task: no durable approved object, revocation ends tokens rather than authority, enforcement happens at issuance, and multi-agent composition is detected rather than structurally narrowed | Durability, Termination, Containment | The broker becomes a Mission-gated credential plane: issuance consults Mission state, and its policy point evaluates each request against the approved task rather than policy alone | The credential lease is the whole task and detection suffices for composition |
| MCP Tasks | Held / long-running work | Not approved purpose | Containment | MCP tool discovery and invocation can carry a Mission reference so each tool call is checked against approved work | You need a work handle, not a mandate |
| Capability tokens (macaroons, biscuits, UCAN) | Attenuable, offline-verifiable authority | No approval event, lifecycle, or task object | Durability, Termination | Attenuated tokens carry the same Mission binding and remain subject to runtime Mission-state checks | Offline attenuation is the whole need and revocation is not |
| [AAuth Mission](/notes/aauth-now-has-a-mission-layer/) (first-class in the proposed protocol since an early 2026 revision) | A native task object on a clean-slate agent substrate | *(a sibling instance of the category, not a competitor)* | None. An instance of the category | The family's AAuth binding hosts the Mission model at the AAuth Person Server, with issuance gating intact | You are on AAuth and need no cross-substrate governance |
The pattern is consistent. The credential and decision layers are
well-served. The *approved task* is the missing object. Mission-based
authorization supplies it and lets the others bind to it: RAR derives
*from* it, the PDP decides *against* it, sessions and traces *reference*
it. And the "Law it breaks alone" column is the precise sense in which
the category is forced rather than preferred. Used alone, every row
breaks at least one of the
[five laws](#the-five-laws-of-delegated-authority), so an architecture
that satisfies all five contains a Mission-shaped object, whatever it
is called. The one row that breaks none is not an alternative but an
instance.
**When it is the wrong tool.** If there is no durable task to govern (a
single user-driven request, a machine-to-machine service credential, a
short-lived consumer authorization where the credential lifetime *is* the
task), a Mission adds cost without value. Do not read the standing
agent out through this door: a service credential's work is fixed at
integration time, while a standing agent exercises delegated judgment
on every unit of work, which is exactly what needs a charter and
cycling authority
([the standing agent at scale](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)).
And do not read small tasks out either: one resource, one approver,
and one afternoon is a perfectly formed Mission, because what
disqualifies is never size, it is the absence of a durable task. The
category earns its keep whenever authorization must outlive a single
request and stay bound to a task, however small, and whoever the actor
is: a Mission governs a
[human's task access](/notes/adopting-mission-bound-authorization/#you-do-not-need-an-agent-to-start)
as readily as an agent's. And note that **AAuth Mission is
itself an instance of this category** on a different substrate, not a rival
to it. The interesting question there is shared governance across substrates, not which one wins.
**Granularity has decision rules,** because both failure directions are real: Missions broad enough that ambient authority has merely moved upward, and Missions so fine that approval and evidence volume overwhelm the plane. Work stays in one Mission while the undertaking and its bounds hold. A helper that finishes inside the parent's flow gets a narrower delegated projection. A durable sub-agent that needs its own observable lifecycle and stop handle gets a [Child Mission](/notes/mission-bound-authority/#mechanism-1-the-child-mission). The same undertaking needing different bounds gets a [successor](/notes/mission-lifecycle-and-change/#grow-expansion-creates-a-successor). A new objective gets a separate Mission, related in evidence. And one high-consequence effect gets an action-bound permit, never a Mission of its own.
# The deployment ladder
The smallest useful deployment, and the path up:
- **Stage 0: Substrate only.** Ordinary OAuth, no Mission. Fine for
single-request, non-agentic flows.
- **Stage 1: Mission-bound issuance.** A `mission` claim on derived
tokens, with state-gated issuance: a possession-independent kill switch
for future derivation. *Audit and derivation control, not action-time
defense.*
- **Stage 2: State and revocation freshness.** Status / introspection so
consumers can check current Mission state.
- **Stage 3: Runtime enforcement.** Per-action PDP checks for
consequential actions. **This is the stage that turns governance
metadata into agent safety.**
- **Stage 4: Lifecycle.** Signals, expansion, and completion: prompt
revocation, governed growth, and monotonic narrowing.
- **Stage 5: Delegation.** Child Missions and offline attenuation:
strict-subset authority for sub-agents, without ambient inheritance.
- **Stage 6: Operational assurance.** Harness binding, safe unwinding,
and audit transparency.
Most AI agents that touch private data, untrusted content, or external
side effects need at least Stage 3, and Stages 5โ6 for fan-out and full
governance. Stages 1โ2 alone are not enough for consequential autonomy.
The stages roll up into the four claimable Mission Assurance Levels,
one line each:
| Level | One line |
| --- | --- |
| Baseline Issuance | Governance metadata and a derivation kill switch, not action safety |
| Runtime-Enforced | Per-action enforcement plus state freshness, on ratified substrate |
| Governed Agent | Consent evidence, harness binding, and operational controls |
| High-Assurance Agent | Mediated custody, no unmediated path, action-bound approval, active freshness, and agent-isolated approval rendering |
Baseline is a real category claim at issuance strength, and what it cannot claim is action-time defense: [the litmus's four-and-two split](#what-counts-the-litmus-test).
The level is one axis. The binding (OAuth AS, standalone Mission
Authority Server, AAuth Person Server, or the experimental UMA sketch)
is orthogonal, and a deployment names both. The bindings are not one
security system either: the architecture classifies them as
credential-carried (the Mission rides the credential), PDP-joined (the
decision point joins ordinary credentials to the record), and
authority-native (the substrate's own authority object is the record),
and a deployment names its security architecture, not only its binding.
Custody of the execution environment is the third axis, because assurance follows it. For a harness the enterprise does not run, a third-party SaaS agent or an employee's consumer assistant, Baseline is claimable on the credentials the estate mints plus boundary PEPs at its edge, and Runtime-Enforced survives route-scoped: enterprise PEPs in front of enterprise resources enforce at the point of use whoever runs the loop. The Governed Agent level is a harness claim and structurally out of reach without harness custody, while the high-consequence classes stay reachable per route, because mediated custody holds the key at an enterprise-controlled handler either way. And delivering a usable secret into the agent's environment is custody transferred, not use mediated. Only the mediated form supports the highest claims.
Read the levels as an unlock ladder too:
Baseline governs the read-only pilot, Runtime-Enforced makes
reversible writes defensible (the read-only ceiling breaks here),
Governed Agent makes unattended operation and delegation defensible,
and High-Assurance covers the irreversible, external-commitment, and
privileged classes. The
[adoption path](/notes/adopting-mission-bound-authorization/#the-read-only-ceiling)
carries that reading in full.
# The implementation checklist
The claim a deployment makes should be checkable. This is the field
checklist for the Runtime-Enforced level, with the deeper treatments
linked.
| Dimension | What must be true | Defined in |
| --- | --- | --- |
| Surfaces | PAR accepts `mission_intent`. The approval event renders the derived Authority Set. A Mission Status endpoint and the lifecycle verbs are served at the issuer. The Signals push where revocation must bite in seconds | [The Mission](/notes/the-mission-is-the-missing-abstraction/), [Approval integrity](/notes/from-a-request-to-an-approved-mission/), [Lifecycle](/notes/mission-lifecycle-and-change/) |
| Claims carried | Every derived token carries `mission` (`id`, `issuer`, `authority_hash`) and its derived `authorization_details`, sender-constrained, with `exp` capped by the Mission's `expires_at`. Delegated work carries the `act` chain, and platforms running many instances carry attested instance identity | [The Mission](/notes/the-mission-is-the-missing-abstraction/), [Delegation](/notes/mission-bound-authority/) |
| PEP placement | A PEP sits at the last controllable boundary before every consequential action in scope: the resource API, the MCP `tools/call`, the egress proxy, the orchestrator for local side effects | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) |
| Evidence | Decision Evidence for every consequential decision, including denials. Execution Evidence for high-consequence and duration-metered actions. Consent Evidence where the Governed Agent level is claimed | [Approval integrity](/notes/from-a-request-to-an-approved-mission/), [Runtime enforcement](/notes/mission-bound-runtime-enforcement/), [Agent runtime](/notes/the-agent-runtime-and-audit/) |
| Freshness | Only `active` permits reliance, within a published staleness bound. High-consequence classes require an active freshness mechanism: issuer introspection or Mission Status as the fail-closed source, with the Signals push as acceleration | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/), [Lifecycle](/notes/mission-lifecycle-and-change/) |
| Honest claim | Name the assurance level and the enforcement scope: which resources, action classes, and execution paths are covered, and which are not | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) |
Conformance is scoped, not global. A deployment that cannot prevent an
action class on some path must not claim runtime enforcement for that
class, and must name the paths it does mediate. A claim worth trusting reads like the [honest deployment claim](#the-honest-deployment-claim) in the citation kit, each clause mapped to a row above and checkable. A claim that cannot be written in that form is not a conformance claim. It is marketing. [What not to claim](#what-not-to-claim) is its negative space.
# Adversary model
The non-goals below say what this does *not* solve. This is the
complementary view: the adversaries it *does* constrain, the layer that
constrains each, and the residual it leaves. The reasoning is developed
across the parts. This table is the consolidated map, and the
[Mission Security Model](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-security-model.html)
draft (Informational) is its spec-level counterpart: the trusted base,
the cross-cutting assumptions, and the consequence of each component's
compromise.
| Adversary capability | What the layers deny it | Residual |
|---|---|---|
| **Compromised agent** (controls the model and loop) | Mediated custody keeps the sender-constraint key off the agent. The PDP checks every consequential action. Delegation only narrows ([Delegation](/notes/mission-bound-authority/), [Runtime enforcement](/notes/mission-bound-runtime-enforcement/)) | It can still misuse authority *within* the approved scope. Keep scope tight |
| **Prompt injection / untrusted content** steering the task | Authority comes from the approved task, not runtime inference. The PDP checks against that task, not the prompt ([Approval integrity](/notes/from-a-request-to-an-approved-mission/), [Runtime enforcement](/notes/mission-bound-runtime-enforcement/)) | Cannot make the model's reasoning trustworthy. A mis-shaped Intent the Approver accepts is still approved |
| **Stolen or exfiltrated token** | State-gated issuance, the kill switch, and runtime freshness stop use once the Mission is revoked or expired. Sender-constraint binds the holder ([The Mission](/notes/the-mission-is-the-missing-abstraction/), [Runtime enforcement](/notes/mission-bound-runtime-enforcement/), [Lifecycle](/notes/mission-lifecycle-and-change/)) | A non-sender-constrained token used inside its window before revocation |
| **Confused deputy / parameter swap (TOCTOU)** | `parameter_digest` binds the permit to concrete parameters. Mismatched execution fails closed ([Runtime enforcement](/notes/mission-bound-runtime-enforcement/)) | Only as good as the parameters the digest covers |
| **Stale or poisoned capability** (a tool redefined under the agent) | The capability is bound to the source digest recorded at derivation. Drift fails closed as `capability_drift` ([Runtime enforcement](/notes/mission-bound-runtime-enforcement/)) | The deployment must actually record and check source digests |
| **Over-broad approval** | *(nothing technical denies it)* | Explicit non-goal: breadth approved is breadth granted. Mitigated, never denied, by consent rendering, shaping discipline, templates and organizational priors, and [policy ceilings with a human floor](/notes/from-a-request-to-an-approved-mission/#who-may-approve) |
| **Runaway fan-out / sub-agent sprawl** | Fan-out controls, bounded depth, cascade revocation. Children are strict subsets ([Delegation](/notes/mission-bound-authority/)) | Offline-minted breadth is unobserved by the issuer and must be bounded by policy |
| **Equivocating or tampered audit** | SCITT transparency makes evidence tamper-evident and, with multiple independent services, non-equivocating ([Agent runtime](/notes/the-agent-runtime-and-audit/)) | A single transparency service is *trusted*, not proven, not to equivocate. Completeness is checkable only against an expected schedule |
| **Control-plane administrator drift** (the operator who can widen an authority input and activate the widening) | Privileged mutations are versioned, non-retroactive, and evidenced, and a role that can widen an authority input must not also be able to make that widening active without an independently evidenced control. Shaping and rendering templates live in the same privileged regime | Removing standing authority from agents concentrates it in the control plane's operators, and a poisoned template mints systematic over-breadth wearing legitimacy. Separation plus review is the mitigation, never a proof |
| **Manipulated risk signals** (suspension as denial of service) | Risk signals narrow, escalate, and investigate, never widen, because a score is exactly the input an adversary can shape. Suspensions carry evidence and a named recovery authority ([Lifecycle](/notes/mission-lifecycle-and-change/)) | Gaming the signals into suspending legitimate work is a denial of service with a governance face, and the flooded step-up channel trains reviewers to click through |
## The Mission record is itself sensitive
The object that makes agent work governable is also a record of
business intent: purpose, targets, constraints, approver identities,
and the evidence that joins them. The same `mission.id` join that
makes reconstruction possible is a correlation surface. The core
already keeps the token-side claim a reference (`id`, `issuer`,
`authority_hash`), never the Intent's contents, and deployments should
hold that line: resource servers and intermediate PEPs need the
identifier and anchors, not the task description. Where a party outside
the issuing domain needs some committed facts and not all of them, the
Mandate's selective disclosure is the built tool. The rest is
deployment discipline: evidence stores behind the same access control
as the systems they describe, retention set deliberately because the
evidence chain outlives the task by design, and awareness that a
Mission reference projected across domains is a correlation handle in
someone else's logs.
The sober reading: the strong adversary, a fully compromised agent, is
contained at the *boundary* (custody, per-action checks, narrow-only
delegation, the kill switch), never by trusting what the agent says. The
residual column is the part no claim should paper over.
# Threats and non-goals
The Mission is the declared governance envelope, and the runtime layer
is what keeps the system survivable when the envelope turns out to be
incomplete: a deployment with the object but no runtime layer is
unprotected, and one with enforcement but no shared approved object is
ungovernable. [Mission Shaping Is Not Enough](/notes/mission-shaping-is-not-enough/)
makes that two-layer argument in full. And one sentence bounds the
whole claim: **Mission compliance is evaluated against the approved
representation of purpose, not against an independent oracle of human
intent.** A sufficiently broad or badly derived Authority Set can
permit an action that is syntactically compliant and
purpose-inconsistent, which is why shaping, disclosure integrity,
authority derivation, and runtime enforcement are all load-bearing
rather than redundant.
Mission-based authorization is credible because it is precise about its
edges. It **does not**:
- make an LLM's reasoning trustworthy
- replace resource-local policy (the resource remains authoritative for
its own decisions)
- provide full information-flow control
- prove that every side channel has been mediated
- eliminate the need for human step-up on high-risk actions
- make a broad, over-scoped Mission safe (breadth approved is breadth
granted)
- lean on deterrence as a compensating control. Human delegation
quietly does, because people fear the audit that follows. An agent
has no career to protect, and its judgment can be rewritten mid-task
by content it reads, so the runtime boundary must carry the weight
that deterrence carries for people.
What it **does**:
> It gives policy, credentials, lifecycle, delegation, and audit a common
> object (the approved task) and a runtime layer that checks each
> consequential action against it.
The safety properties most people assume from "Mission-bound agents"
(action-time defense, prompt revocation, safe unwinding, evidence) come
from the runtime and operational layers, not from the `mission` claim
alone.
# Noun distinctions
Keep these stable. Do not let "task," "mission," "workflow," and
"session" blur.
- **Mission Intent:** the *proposed* task (untrusted until validated).
- **Mission:** the *approved*, governed task (durable, lifecycle-owned).
- **Authority Set:** the *derived, grantable* authority the Mission
bounds.
- **Projection:** any *substrate-specific, audience-bounded* credential
or assertion derived from the Authority Set: a Mission-bound token,
an ID-JAG, a downstream grant, or another substrate's native
credential. Every projection carries the Mission reference.
- **Mission-bound token:** a *credential projection* carrying the
`mission` claim.
- **Runtime decision:** a *per-action* permit or deny.
- **Evidence:** an *audit artifact* (approval, consent, decision,
lifecycle).
- **Harness:** the *runtime continuity and mediation* layer, not
authority.
The canonical sequence behind these nouns: **Mission Intent โ
Authority Set โ Mission โ Projection โ Runtime Decision โ Evidence.**
The state authority derives the Authority Set from the validated Intent
before consent, the approved Mission commits both, and every profile
specifies one or more of the transitions.
# Why "Mission"?
The name was deliberate, and alternatives were considered. Each
captured a piece of the object without doing justice to the whole:
- **`mandate`** carries political and legal connotations the protocol object does not. A mandate is an instruction. A Mission is a governance container that includes intent, authority, lifecycle, and evidence.
- **`delegation_context`** muddles with OAuth's existing notion of delegation, which sits at the credential layer (one principal authorizing another). The Mission is above credential delegation. Both the original credential and any delegated credential project from the same Mission.
- **`task_authorization`** describes a credential, not a governance object. The Mission is what task authorization derives *from*. Calling the governance object "task authorization" reduces the layer above to the layer below.
- **`purpose_bound_authorization`** correctly names one aspect (purpose) but presents the object as a flavor of authorization rather than as the durable container that authorization projects from.
- **`authorization_context`** is too generic. "Context" in OAuth-adjacent specifications means many different things. The Mission has a specific structural commitment to integrity, lifecycle, and consent that "context" does not imply.
"Mission" captures the durable, purpose-bound, lifecycle-governed
quality of the object without overloading any existing OAuth term. It
signals that this is not a refinement of `scope`,
`authorization_details`, `grant`, or `session`, and it implies *purpose
plus duration plus boundedness* in one word, which is what the
governance object actually is.
A standards-track adoption could use a more neutral on-the-wire name
(the OAuth claim could be `mbo` or `authorization_mission` rather than
`mission`) while preserving "Mission" as the conceptual term. The
family uses `mission` as the claim name to keep the conceptual and wire
terminology aligned, and deployments may rename if a working-group
consensus settles on a different label.
# Reproducible test vector
The credibility of this model is its hashing, so here is one anchor you
can reproduce byte for byte. Every integrity anchor is a SHA-256 over a
domain-separated, issuer-bound envelope (`{ "typ", "iss", "value" }`),
canonicalized with JCS ([RFC 8785](https://www.rfc-editor.org/rfc/rfc8785)),
encoded as `sha-256:` followed by base64url with no padding. For
`intent_hash`, `typ` is `mission-intent` and `value` is the approved
Mission Intent.
The JCS canonical bytes of the envelope, for the running example, are
exactly:
```json
{"iss":"https://as.example.com","typ":"mission-intent","value":{"constraints":["Q3 2026","Example Corp","confidential"],"expires_at":"2026-10-15T18:00:00Z","goal":"Prepare the Q3 board packet for the audit committee","purpose":"urn:example:mission:board-packet","resources":["https://finance.example.com","https://docs.example.com","https://workflow.example.com"]}}
```
Hash those bytes and you get the anchor. Reproduce it from a shell:
```sh
printf '%s' '{"iss":"https://as.example.com","typ":"mission-intent","value":{"constraints":["Q3 2026","Example Corp","confidential"],"expires_at":"2026-10-15T18:00:00Z","goal":"Prepare the Q3 board packet for the audit committee","purpose":"urn:example:mission:board-packet","resources":["https://finance.example.com","https://docs.example.com","https://workflow.example.com"]}}' \
| openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '='
# => jjx06KDh_TpWYhzSAvzEBH_lMz32eRj1tjgjNvt-crE
```
So `intent_hash = sha-256:jjx06KDh_TpWYhzSAvzEBH_lMz32eRj1tjgjNvt-crE`.
The same procedure with `typ` = `mission-authority-set` and `value` = the
Authority Set array yields
`authority_hash = sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY`.
Two rules make this interoperable: JCS sorts object keys but **preserves
array order** (so the Authority Set's entry order is part of the canonical
form), and the `typ` value **domain-separates** the anchors so a digest of
one object can never be read as the other. A verifier reproduces the
digest from the recorded object alone.
# Glossary
The handbook's vocabulary has its own appendix: [the Glossary](/notes/mission-bound-authorization-glossary/) (Appendix F) carries every term in one alphabetical table, each entry linking its canonical home.
# The running example, end to end
The handbook follows one task. This is the canonical walkthrough, each
part picks up its step, and
[Mission-Bound Authorization on the Wire](/notes/mission-bound-authorization-on-the-wire/)
shows the same steps as actual protocol exhibits.
**Scenario.** Alice asks an agent: *"Put together the Q3 board packet for
the audit committee and let them know it's ready."* The Mission's
Authority Set is `query_financials` (finance, Q3 2026), `create_doc`
(docs, board-packet template), and `notify_reviewer` (workflow,
`audit-committee` group), bounded to an expiry.
1. **Derive.** The AS validates and narrows the Intent and derives the
Authority Set it will ask Alice to approve. *([The Mission](/notes/the-mission-is-the-missing-abstraction/))*
2. **Approve.** The AS renders the validated Intent + derived Authority
Set. Alice interrogates the disclosure if anything is unclear, with
the exchange recorded, and consents. The AS commits `intent_hash` and
`authority_hash` and creates the Mission `active`, atomically.
*([Approval integrity](/notes/from-a-request-to-an-approved-mission/))*
3. **Issue.** The agent gets a Mission-bound token carrying the `mission`
claim. *([The Mission](/notes/the-mission-is-the-missing-abstraction/))*
4. **Permit a read.** The agent reads Q3 financials. The PDP permits
`query_financials` against the live Mission. *([Runtime enforcement](/notes/mission-bound-runtime-enforcement/))*
5. **Gate a write.** Drafting the document is a write. The
`create_doc` permit is bound to concrete parameters and checked at the
point of use. *([Runtime enforcement](/notes/mission-bound-runtime-enforcement/))*
6. **Attempt expansion.** Mid-task the agent decides it "needs" CRM
customer data (outside the Authority Set). It cannot widen in place.
Widening requires a fresh approval (a successor Mission). *([Lifecycle](/notes/mission-lifecycle-and-change/))*
7. **Deny the expansion.** Policy declines the CRM expansion. The
original Mission is untouched and the agent does not get CRM access.
*([Lifecycle](/notes/mission-lifecycle-and-change/))*
8. **Delegate, narrowed.** A sub-agent gathers the financials under a
**Child Mission** scoped to `query_financials` only, expiry โค parent,
no `create_doc` or `notify_reviewer`. *([Delegation](/notes/mission-bound-authority/))*
9. **Revoke.** The board meeting is cancelled. An admin revokes the
Mission. Status reports the new state and the Signals
push announces it. All further
derivation stops, and the child transitions to the terminal
`cascaded` state. *([Delegation](/notes/mission-bound-authority/), [Lifecycle](/notes/mission-lifecycle-and-change/))*
10. **Stop the work.** The harness, which bound the session and queue to
Mission state, halts the paused draft (session continuity is not
authority), and orchestration unwinds the half-written document.
*([Agent runtime](/notes/the-agent-runtime-and-audit/))*
11. **Reconstruct.** The SCITT audit feed for this Mission shows one
verifiable, append-only history (approval โ consent โ the permitted
read โ the denied CRM expansion โ revocation), committed by hash so
the financial contents stay out of the log. *([Agent runtime](/notes/the-agent-runtime-and-audit/))*
And run the ending the other way, because most Missions do not die,
they finish. No cancellation: the sub-agent returns the financials,
the packet is published, and the notice goes to the audit committee.
Each entry's `terminal_when` condition fires as its step completes
([Completion](/notes/mission-lifecycle-and-change/#complete-discharge-narrows-one-entry-at-a-time)),
so `create_doc` and `notify_reviewer` retire themselves, the Mission
closes as `completed`, and the same evidence feed reads clean end to
end: proposed, approved, exercised inside bounds, finished. Alice got
her packet, and the deployment can prove exactly how.
That is the whole category in one task: an approved object, authority
derived from it, every consequential action checked against it,
delegation that only narrows, a lifecycle that can stop it or let it
finish, and evidence that reconstructs it either way.
# What this replaces, and what it does not
Mission-based authorization adds one object. It does not displace the
stack around it.
- **Not OAuth.** The Mission rides OAuth issuance, exchange, and sender
constraint. The core is an OAuth profile, not a successor.
- **Not AuthZEN or your PDP.** The runtime contract binds to the AuthZEN
Authorization API. The Mission is a new input to the decision, not a
new decision engine.
- **Not resource policy.** The resource stays authoritative for its own
objects. A Mission permit is an upper bound, never a command.
- **Not session management.** The harness keeps owning execution
continuity. It consults Mission state before resuming, and that is the
whole change.
- **Not model alignment.** Nothing here makes an LLM's reasoning
trustworthy. The Mission bounds what a drifted or injected agent can
reach, and the runtime layer enforces the bound.
What it adds is the piece those layers keep routing around: the
governed task object they all bind to. The
[objections below](#common-objections) route to the answers for the sharper versions of
"isn't this just X."
# Common objections
The recurring pushbacks live on their own page, built to be linked
into the thread where the objection was raised:
[Common Objections to Mission-Based Authorization](/notes/common-objections-to-mission-based-authorization/).
The answers, for the people who run today's control planes,
organized by the plane the reader operates, from "isn't this just RAR"
and "we run Zero Trust" through the state tax, the consent screen, and
the standing agent, each with the short answer and where the long one
lives.
# The draft family at a glance
Every part links its drafts inline. This table is the whole family in
one place. All of these are individual Internet-Drafts published as
editor's copies and proposed for discussion. None is adopted by a
working group. The names reflect the architecture: substrate-neutral
profiles carry `draft-mcguinness-mission-*` names, while the OAuth
bindings keep `oauth` in the name and "for OAuth 2.0" in the title.
The Maturity column follows the repository's adoption order. **Adopt
first** is the Architecture and the core. **Minimum** is what agents
that act add for the Runtime-Enforced level.
**Recommended** is what AI agents add for the Governed Agent level.
**By binding** profiles carry the standalone Mission Authority Server,
the issuance grant join, the AAuth Person Server, and the substrate
requirements, adopted where the estate calls for them, with the UMA
2.0 sketch as the experimental fourth binding. **Advanced**
profiles are stable design to adopt when the use case
arrives. **Experimental** profiles are for evaluation only: each
depends on an unratified substrate or defines a newer, less-exercised
model, and each names a stable path to prefer where one exists.
| Draft (editor's copy) | Covered in | Track | Maturity |
| --- | --- | --- | --- |
| [An Architecture for Mission-Bound Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) | Front door | Informational | Adopt first |
| [Mission-Bound Authorization for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html) | [The Mission](/notes/the-mission-is-the-missing-abstraction/), [Delegation](/notes/mission-bound-authority/) (the core) | Standards Track | Adopt first |
| [Mission Intent Shaping](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-shaping.html) | [Approval integrity](/notes/from-a-request-to-an-approved-mission/) | Informational | Advanced |
| [Mission Consent Evidence for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html) | [Approval integrity](/notes/from-a-request-to-an-approved-mission/) | Standards Track | Recommended |
| [Mission Deferred Approval for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval.html) | [Approval integrity](/notes/from-a-request-to-an-approved-mission/) | Standards Track | Advanced |
| [Mission Approval Revision for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval-revision.html) | [Approval integrity](/notes/from-a-request-to-an-approved-mission/) | Standards Track | Experimental |
| [Mission Child Delegation for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-child-delegation.html) | [Delegation](/notes/mission-bound-authority/) | Standards Track | Advanced |
| [Mission Offline Attenuation for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-attenuation.html) | [Delegation](/notes/mission-bound-authority/) | Standards Track | Experimental |
| [Mission Cross-Domain Projection for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-cross-domain.html) | [Delegation](/notes/mission-bound-authority/) | Standards Track | Advanced |
| [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html) | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) | Standards Track | Minimum |
| [Mission-Bound Runtime Enforcement: AuthZEN Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html) | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) | Standards Track | Minimum |
| [Mission Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) | Standards Track | Experimental |
| [Mission Status and Lifecycle for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html) (the suite root, carrying completion and discharge) | [Lifecycle](/notes/mission-lifecycle-and-change/) | Standards Track | Minimum |
| [Mission Lifecycle Signals for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html) | [Lifecycle](/notes/mission-lifecycle-and-change/) | Standards Track | Advanced |
| [Mission Expansion for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-expansion.html) | [Lifecycle](/notes/mission-lifecycle-and-change/) | Standards Track | Advanced |
| [Mission Progressive Authorization for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html) | [Lifecycle](/notes/mission-lifecycle-and-change/) | Standards Track | Experimental |
| [Mission Open-World Discovery](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-discovery.html) | [Adopting](/notes/adopting-mission-bound-authorization/) | Experimental | Experimental |
| [Mission Management for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html) | [Lifecycle](/notes/mission-lifecycle-and-change/) | Standards Track | Advanced |
| [Mission-Aware Agent Harnesses](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html) | [Agent runtime](/notes/the-agent-runtime-and-audit/) | Standards Track | Recommended |
| [Mission Orchestration and Unwinding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-orchestration.html) | [Agent runtime](/notes/the-agent-runtime-and-audit/) | Standards Track | Experimental |
| [Mission Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html) | [Agent runtime](/notes/the-agent-runtime-and-audit/) | Standards Track | Advanced |
| [Mission Security Model](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-security-model.html) | Cross-cutting | Informational | Overview |
| [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) | [The Authority Control Plane](/notes/the-authority-control-plane/) | Standards Track | By binding |
| [Mission Issuance Grant for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html) | [The Authority Control Plane](/notes/the-authority-control-plane/) | Standards Track | By binding |
| [Mission Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html) | [The Authority Control Plane](/notes/the-authority-control-plane/) | Standards Track | Advanced |
| [Mission-Bound Authorization for AAuth](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) | [The Convergence and the Wagers](/notes/the-convergence-and-the-wagers/) | Standards Track | By binding |
| [Mission Substrate Requirements](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-substrate.html) | [What Survives Without OAuth](/notes/what-survives-without-oauth/) | Standards Track | By binding |
| [Mission-Bound Authorization for UMA 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-uma.html) | [What Survives Without OAuth](/notes/what-survives-without-oauth/) | Experimental | Experimental |
For AI agents, the README is explicit that consent evidence and the
harness are not optional extras. They are the Recommended maturity, the
Governed Agent level.
# How to cite this handbook
Link the piece that matches what you are referencing:
- **The whole handbook:** [the cover](/mission-handbook/), the front door with the introduction and the chapter map.
- **The category and definition:** this [Reference](/notes/mission-based-authorization-field-reference/).
- **The evaluation tool and the blueprint:** [the vendor test](/notes/mission-based-authorization-vendor-test/) (Appendix D), and [the blueprint](/notes/adopting-mission-bound-authorization/#walk-the-runtime-enforced-level).
- **The standards map:** the outside-in survey, every OAuth, WIMSE, and
OpenID spec mapped to the architecture with its role and deltas
([Appendix E](/notes/mission-bound-authorization-standards-map/)).
- **The vocabulary:** [the Glossary](/notes/mission-bound-authorization-glossary/) (Appendix F), every term, A to Z.
- **The mental model for non-specialists:** [What the Corporate Card Already Solved](/series/what-the-corporate-card-already-solved/).
- **The missing layer and its laws:** [the five laws of delegated authority](#the-five-laws-of-delegated-authority) above, or the [architecture chapter](/series/designing-mission-bound-authorization/#the-missing-layer) for the full argument.
- **The argument for why the Mission is the missing object:** [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/).
- **The primitive's definition, roles, and name:** the [object model](#the-mission-object-model), [trust boundaries and roles](#trust-boundaries-and-roles), and [why "Mission"](#why-mission), on this page.
- **The safety claim (runtime is load-bearing):** [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/).
- **The adoption path (crawl, walk, run):** [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/).
- **The OAuth wire implementation:** the [draft family](https://github.com/mcguinness/mission-bound-authorization).
- **The MCP application:** [Least-Privilege MCP Tool Calls Need a Mission](/notes/least-privilege-mcp-tool-calls-need-a-mission/).
- **The wire exhibits:** [Mission-Bound Authorization on the Wire](/notes/mission-bound-authorization-on-the-wire/) (Appendix B).
- **To discuss or object:** [issues on the draft repository](https://github.com/mcguinness/mission-bound-authorization/issues).
One-line definition to quote: [the bottom line](#the-bottom-line), at the top of this page.
# A note on requirement language
The handbook quotes requirement keywords such as **MUST**, **SHOULD**, and
**MAY** with their BCP 14 meanings
([RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119),
[RFC 8174](https://datatracker.ietf.org/doc/html/rfc8174)) when they
appear in all capitals. Conformance applies to the profile section each
requirement appears in. The drafts are the normative text.
---
# Least-Privilege MCP Tool Calls Need a Mission
Canonical URL: https://notes.karlmcguinness.com/notes/least-privilege-mcp-tool-calls-need-a-mission/
Markdown URL: https://notes.karlmcguinness.com/notes/least-privilege-mcp-tool-calls-need-a-mission.md
{{< tldr >}}
- **The application.** Two architectures authorize least-privilege MCP tool calls: token-side and resource-side. Both fragment under fan-out because neither carries the durable task context. This essay applies the [Mission-Bound Authorization](/series/designing-mission-bound-authorization/) architecture to that gap, making the approved task an explicit protocol object both models project from. It rides alongside the handbook as the architecture chapter's applied companion, with the [Building Mission-Bound Authorization](/series/building-mission-bound-authorization/) chapter carrying the wire depth it applies.
- **Read the foundations first.** The two models, the tool boundary, and the per-call standards (AuthZEN, COAZ, ARAP) are the subject of the standalone [Least-Privilege MCP series](/series/least-privilege-mcp/), which ends on [the gap neither model closes](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/#the-gap-neither-model-closes). That series does not depend on this essay. This essay is the Mission series' answer to the gap it names.
- **What this essay adds.** What a Mission fixes for both models, the pipeline with a Mission in scope, the lethal-trifecta posture, the AAuth comparison, and a denial traced end to end.
- **Out of scope here.** The issuance core's wire detail ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)) and the runtime contract's mechanics ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)). This essay applies them, not redefines them.
- **Specs (editor's copies).** [Mission-Bound Authorization for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html), [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), [Runtime Enforcement: AuthZEN Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html).
**Reading paths in this essay.**
- *MCP server developer (~15 min):* [The two models, in brief](#the-two-models-in-brief) โ [What a Mission fixes](#what-a-mission-fixes) โ [The pipeline with a Mission in scope](#the-pipeline-with-a-mission-in-scope) โ [Tracing a denial end to end](#tracing-a-denial-end-to-end) โ [Where to start building](#where-to-start-building).
- *Architecture reviewer (~10 min):* [The two models, in brief](#the-two-models-in-brief) โ [What a Mission fixes](#what-a-mission-fixes) โ [Does a Mission violate the lethal trifecta?](#does-a-mission-violate-the-lethal-trifecta).
{{< /tldr >}}
# The two models, in brief
An agent preparing a board packet has to make a sequence of tool calls across three MCP servers, each behind its own authorization domain: `query_financials` on a finance server, `create_doc` on a document server, `notify_reviewer` on a workflow server. The user approved the task before the agent started ("prepare the Q3 board packet for the audit committee"), but in today's systems that approval is application state, not protocol authority. Each call still has to be authorized at the right grain.
There are two natural ways to do that, and the [Least-Privilege MCP series](/series/least-privilege-mcp/) walks both in full. **Token-side authorization** carries the authority in the credential. The agent discovers what the server requires, asks the right Authorization Server for a token narrowed to the action, and presents it. **Resource-side authorization** keeps the decision at the resource. The MCP server is the Policy Enforcement Point, asks a PDP whether this exact call is allowed, and uses a requestable denial when the missing authority can be requested. [Least-Privilege MCP Tool Calls](/notes/least-privilege-mcp-tool-calls/) compares the models, and [Closing the Gaps](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) covers the standards that make per-call decisions interoperable: AuthZEN for the decision shape, COAZ for the MCP mapping, ARAP for turning a denial into a governed request.
Both authorize one call well. Both share one blind spot. Tokens describe what authority a credential carries. PDP decisions describe whether a specific call is permitted. Neither describes the task the user actually approved. So once the task fans out across three servers and three authorization domains, the authority for it fragments. The user sees repeated approval prompts for fragments of one task. The agent accumulates narrow tokens or pending requests with no shared lifecycle. Audit joins fall back to timestamps and log correlation, and [correlation is not governance](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/#why-correlation-is-not-governance). The protocol stack has no standard object that names the task. That is the gap this essay closes.
A Mission makes that governed task an explicit protocol object both models can bind to, while each MCP server or Resource AS keeps its own domain semantics and local policy. What that buys, and how it is owned, derived, and enforced, is the rest of this essay.
# Board packet before and after
The board-packet example is the whole problem in miniature.
| Step | Without a Mission | With a Mission |
| --- | --- | --- |
| Finance read | Fresh token request or PDP approval for `query_financials`. Approval prompt has to restate the task context | Checked against the approved board-packet Mission. Finance system applies its own domain policy |
| Document creation | Separate AS/PDP decision for `create_doc`, with no protocol-level link to the finance read | Token or PDP decision carries the same Mission binding. Document system sees the same task context |
| Reviewer notification | Another isolated approval for `notify_reviewer`. Audit has to stitch by time, user, and client | Workflow action is evaluated as another projection of the same Mission |
| Scope expansion | New ad-hoc approval if the agent needs data outside the task | A fresh approval creates a successor Mission with lineage back to the original |
| Kill switch | Finding and revoking every narrow token independently | Revoking the Mission stops all further derivation at once |
| Audit | Three unrelated decisions across three systems | One task spine joined by `mission.id` and `mission.issuer` |
That is the standardization point. Least privilege per call is necessary, but it is not sufficient for agent tasks. The calls need a common object to point back to.
# What a Mission fixes
A Mission is that missing object. It is the durable, approval-backed governance object for the task, held at the Authorization Server that approved it and identified everywhere by `mission.id` and `mission.issuer`. Tokens and PDP decisions carry or resolve a binding to that record. As [the Mission series](/series/designing-mission-bound-authorization/) argues, the missing abstraction is not another credential or evaluation point. It is the governed task those artifacts serve. The precise definitions live in the [Reference](/notes/mission-based-authorization-field-reference/), and this task is its running example.
The flow is the Mission series' spine applied at the MCP boundary. A shaper turns the prompt into a structured Mission Intent and proposes it through Pushed Authorization Requests ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)). The Authorization Server validates the Intent, derives the Authority Set the user will be asked to approve, and renders that derived set for consent. On approval it records the Mission, committed by `intent_hash` and `authority_hash`, and issues Mission-bound tokens carrying the `mission` claim ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)).
For the board-packet task, the shaper submits this Mission Intent at PAR:
```json
{
"goal": "Prepare the Q3 board packet for the audit committee",
"purpose": "urn:example:mission:board-packet",
"resources": ["https://finance.example.com",
"https://docs.example.com",
"https://workflow.example.com"],
"constraints": ["Q3 2026", "Example Corp", "confidential"],
"expires_at": "2026-10-15T18:00:00Z"
}
```
Note what the shaper does not author. It proposes resources and free-text constraints, and any candidate authority it offers rides in the Intent's optional `proposed_authority` member (absent here) as untrusted input the Authorization Server only narrows. The consented Authority Set is always the AS's derivation, never the proposal itself, and [shaping proposes only](/notes/from-a-request-to-an-approved-mission/#shaping-proposes-only) carries the member mechanics. The AS derives the `authorization_details` from the approved Mission and returns the derived array alongside the access token:
```json
[
{
"type": "mission_resource_access",
"resource": "https://finance.example.com",
"actions": ["query_financials"],
"constraints": { "period": "Q3 2026" }
},
{
"type": "mission_resource_access",
"resource": "https://docs.example.com",
"actions": ["create_doc"],
"constraints": { "template": "board-packet" }
},
{
"type": "mission_resource_access",
"resource": "https://workflow.example.com",
"actions": ["notify_reviewer"],
"constraints": { "group": "audit-committee" }
}
]
```
Read the two exhibits together and the trust boundary is visible in the bytes. "Q3 2026" is free text in the Intent and structure in the derived entry, and no function parsed one into the other: the words informed the human-authored derivation rule, or a structured `proposed_authority` candidate the AS only narrowed, and the Approver's check that the structure matches the words is what joins them. [The derivation policy, concretely](/notes/adopting-mission-bound-authorization/#the-derivation-policy-concretely) carries that boundary in full.
The approved Mission Intent itself stays on the Mission record at `mission.issuer`. Consumers learn Mission state through introspection, the Mission Status operation, or Lifecycle Signals ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)), never from an unauthenticated request parameter.
The Mission fixes both models because it separates concerns they otherwise conflate. **The Authorization Server owns the user-approved task, its integrity anchors, and its lifecycle.** **The Authority Set is the typed maximum authority derived from that task.** **Each MCP server or Resource Server owns the domain semantics and local policy for its tools.** The validating AS does not have to implement every resource's policy engine. It validates the Mission Intent against client registration and deployment policy and derives authority only for resources it recognizes. The finance, document, and workflow systems remain authoritative for local resource policy. This division does not remove the ontology problem [Least-Privilege MCP Tool Calls](/notes/least-privilege-mcp-tool-calls/#the-ontology-problem) names. It makes the boundary and the validation responsibility explicit.
The Mission Intent does not need to carry exact MCP tool names. The Authority Set can describe actions at a grain the AS and the Resource Server can jointly validate, and the Resource Server still maps approved authority to concrete arguments and local policy. Where a deployment wants the approved action pinned to a specific capability, the runtime layer binds it to the source-content digest recorded at derivation, such as an operation in a captured MCP `tools/list` snapshot. If the tool is later redefined or poisoned, the digest mismatch fails closed as `capability_drift` ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)).
One deployment note. The default topology holds the Mission at the Authorization Server. A deployment that cannot change its AS can adopt the [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) instead: a standalone Mission Issuer that derives no tokens, with the PDP joining each ordinary OAuth token to its Mission at the point of use. That mode trades away Mission-bound credentials and issuance gating, resting entirely on PEP coverage, and [The Authority Control Plane](/notes/the-authority-control-plane/) prices that trade.
## The pipeline with a Mission in scope
The per-call pipeline from the first two parts (discover, request or evaluate, authorize or escalate, execute) does not change shape. What each step refers to does. The Mission bootstrap (Intent at PAR, validation, derivation, consent) happens before step 1.
1. **Discover.** `tools/list` is filtered against the Mission's Authority Set projection and local policy. A tool unrelated to the approved task need not appear. This is exposure control, not enforcement. Every consequential `tools/call` still passes the runtime gate.
2. **Request or present.** A token-side request presents the Mission-bound credential for the derivation flow. A resource-side call presents the Mission-bound access token to the MCP server. Either way, `mission.id` and `mission.issuer` come from a validated artifact or trusted server-side binding.
3. **Evaluate.** The PDP evaluates the concrete action against the audience-relevant Authority Set entry, the concrete parameters, the actor chain, resource policy, and *current* Mission state, within the deployment's freshness bound ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)).
4. **Authorize or escalate.** An in-bounds request produces a Mission-bound token or permit without expanding anything. Local policy may still require step-up authentication or an action-bound approval. A request that would broaden committed authority routes to Mission Expansion, which is a fresh approval creating a successor Mission ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)).
5. **Execute.** The MCP server enforces the permit at the last controllable boundary, reverifies parameter bindings, and writes Decision Evidence (and, for high-consequence classes, Execution Evidence) bound to the Mission, so records across authorization domains join.
## Tool discovery, invocation, and approval
The three authorization problems from [Least-Privilege MCP Tool Calls](/notes/least-privilege-mcp-tool-calls/) line up cleanly once the Mission is in scope.
**Tool discovery.** Filtering `tools/list` against the Mission projection reduces exposure. It does not replace authorization of `tools/call`.
**Tool invocation.** Both models now have an object to point at. The Mission-bound token is least-privilege at the token grain and tagged with the task it serves. The PDP decision is least-privilege at the per-call grain and tagged with the same task. Either way, the invocation is bound to a specific user-approved task, not just to a specific resource.
**Tool approval.** Approval, consent, and access request share one governance surface without having identical lifecycle effects. In-bounds clarification, argument capture, step-up, or an action-bound approval for an action already inside committed authority leaves the Mission unchanged and is recorded in Decision Evidence. A request that would broaden resources, actions, constraints, audience, budget, or lifetime is Mission Expansion. The AuthZEN profile can mark an `out_of_authority` or `action_approval_required` denial as requestable, ARAP carries the request, and a grant lands as a separately approved successor Mission. The approver sees the Mission lineage rather than an isolated tool fragment.
```mermaid
flowchart TB
User([User])
Mission[("Mission record
at the Authorization Server
id, intent_hash, authority_hash")]
Agent([Agent])
subgraph TS["Token-side"]
AS1[Resource AS-1]
AS2[Resource AS-2]
end
subgraph RS["Resource-side"]
PDP1[MCP-1 PDP]
PDP2[MCP-2 PDP]
end
User -->|One Mission-level approval
of intent and derived authority| Mission
Agent -->|Mission-bound credential
+ requested authority| AS1
Agent -->|Mission-bound credential
+ requested authority| AS2
Agent -->|tool call + validated
Mission-bound context| PDP1
Agent -->|tool call + validated
Mission-bound context| PDP2
AS1 -.->|Mission state| Mission
AS2 -.->|Mission state| Mission
PDP1 -.->|Mission state| Mission
PDP2 -.->|Mission state| Mission
```
The two models converge on the Mission. One Mission-level approval projects into many tokens and PDP decisions, while resources retain their own policy and interaction requirements.
The Mission is not a global operation vocabulary, and it does not eliminate the need for RAR-type metadata, AuthZEN evaluations, or Resource Server policy. It supplies the shared task handle and lifecycle. Resource domains still define their own semantics. This is [the ontology problem](/notes/least-privilege-mcp-tool-calls/#the-ontology-problem) and its policy-mapping half by name: the resource owns the meaning, and the governance and policy layers consume it through registered semantics, published metadata, capability digests, and, where the substrate supports it, the resource's own hash-committed declaration ([who owns the meaning](/notes/mission-based-authorization-field-reference/#who-owns-the-meaning) is the Reference's map). That division of concerns is the point:
| Concern | Owned by |
| --- | --- |
| User-approved task, expiry, lifecycle, audit anchor | Authorization Server at `mission.issuer` |
| Typed approved authority | Authority Set, projected as AS-derived `authorization_details` |
| Resource-specific operation meaning | MCP server or Resource Server |
| Resource-declared operation semantics | The resource itself, hash-committed at the encounter where the substrate supports it |
| Per-call enforcement | Resource AS, MCP server, or PDP |
| Cross-call audit join | `mission.id` and `mission.issuer` |
| Out-of-bounds expansion | Mission Expansion, carried by ARAP where deployed |
That is why the Mission composes with both styles: the same governed task and Authority Set to project from, never the grant itself.
# Does a Mission violate the lethal trifecta?
The lethal trifecta for agents is the combination, in one execution loop, of private-data access, untrusted-content exposure, and external side effects ([Splitting the Lethal Trifecta](/notes/splitting-the-lethal-trifecta/) is the validation story). The board-packet agent can easily touch all three. It reads confidential finance data, ingests public filings or partner documents, and sends notifications or publishes a packet. A Mission profile must not pretend that naming the task makes that safe.
The answer is that **a Mission does not inherently violate the lethal trifecta, but the authority attached to a badly designed Mission can package it.** If the Authority Set grants broad private-data reads, untrusted-web access, and external write authority as one undifferentiated bundle, naming the task has not reduced the risk.
The useful posture does the opposite. It gives the Authorization Server, Resource Servers, PDPs, and egress controls a common object for splitting and governing the bundle:
| Trifecta element | Mission control point |
| --- | --- |
| Private data access | `mission_resource_access` entries limit which systems, records, periods, tenants, and actions are in scope |
| Untrusted content exposure | Shaping treats prompt content as data, and the harness taints a session once untrusted content enters it, so injected input cannot expand authority by itself |
| External side effects | Runtime enforcement requires a per-action permit, parameter binding, and action-bound approval or Mission Expansion for sends, publishes, payments, and egress |
The [runtime profile's lethal-trifecta boundary](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time) treats this as a first-order design constraint, and [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time) is honest about the limits: the defense is as strong as PEP-placement completeness, and per-action checks are not information-flow control.
The design rule is simple. A Mission may describe a task that spans all three elements, but it must not grant all three as ambient authority. Private reads, untrusted inputs, and external writes need to remain separately typed, separately evaluated, and separately auditable under the same canonical Mission. One caveat rides with this essay's own workflow entry: a constraint naming `audit-committee` binds the group's identity, not its membership, which is state another system owns. [The runtime part](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time) prices that residual, and its answer, pin enumerated recipients at derivation for high-consequence classes or say so in the enforcement-scope statement, applies verbatim here.
# How Missions compose with AuthZEN
The two models compose with the [AuthZEN Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html) at different enforcement points, exactly as [Closing the Gaps](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) laid out for the per-call case. Token-side, a requestable denial at the AS can trigger an Access Request, and a fresh authorization decision can later issue a Mission-bound token. Resource-side, a requestable denial at the PDP can trigger an Access Request, after which the MCP server reevaluates before executing. Approval is input to a new decision in both cases. It is never itself an access grant, and a durable grant is committed as a successor Mission. The [AuthZEN profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html) defines the Mission-specific composition points, including the closed denial-reason set and the two evidence objects.
# Comparison with AAuth
The two models name where authorization lands on the OAuth substrate. [AAuth](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/) reaches the same question from a clean-slate agent substrate, and it carries a task object natively. The agent proposes a mission to its Person Server, the approved mission is bound by hash, and the agent presents a mission reference on each call. [AAuth Now Has a Mission Layer](/notes/aauth-now-has-a-mission-layer/) reads that design in depth.
The [Reference's landscape](/notes/mission-based-authorization-field-reference/#competitive-landscape) classifies the AAuth mission as a sibling instance of the same category rather than a competitor. The interesting question is shared governance when a deployment spans both substrates, and the current draft family states the substrate primitives a further binding must supply without yet specifying cross-substrate projection. What both families already agree on is the operating rule: carry authority when it must travel, evaluate locally when context must stay local, and bind both to the durable approved task when work spans calls, tools, or domains.
# Agents do not only call tools
The MCP tool boundary is one place this pattern surfaces. The egress boundary is another. When an agent reaches a destination its egress proxy does not allow, the result today has the same shape as a flat `403` with no machine-actionable recovery, and the same pipeline applies with the proxy as the PEP. [A Blocked Agent Is a Captive Client](/notes/a-blocked-agent-is-a-captive-client/) sketches the approval architectures for that boundary.
A Mission ties both boundaries together. Policy at the MCP and egress boundaries derives from the same Mission and Authority Set. In-bounds tool calls and destinations clear without a new Mission-level prompt, subject to local runtime policy. Authority-expanding events at either boundary route to Mission Expansion at `mission.issuer`. The approver sees one Mission lineage, not unrelated streams of tool and egress approvals. Real agents touch both boundaries on every non-trivial task. The board-packet agent does not only call MCP tools. It also fetches reference data from partner APIs and pulls public reports. The same governance object spanning both boundaries is what keeps the audit trail coherent, and the [harness](/notes/the-agent-runtime-and-audit/) is what guarantees there is no unmediated route around either PEP.
# Tracing a denial end to end
The framework above is easier to evaluate against a concrete failure mode than against the happy path. This walkthrough traces one Mission from approval through an out-of-bounds attempt, an expansion request, a user denial, and the resulting audit query. It is the [Reference's running example](/notes/mission-based-authorization-field-reference/#the-running-example-end-to-end) at the MCP boundary, and each stage names the part that defines the mechanic.
**Stage 1, approval.** The user asks an agent to prepare the Q3 board packet. The shaper turns the prompt into the Mission Intent shown above and submits it at PAR, recording Shaping Evidence ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)). The AS validates the Intent, derives the three-entry Authority Set, and renders the derived set for consent. On approval it creates `msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1` in the `active` state, committing `intent_hash` and `authority_hash`, and issues Mission-bound tokens ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)).
**Stage 2, out-of-bounds attempt.** Two hours in, the agent decides the packet needs CRM data on key accounts. CRM is not in the approved resources or Authority Set. The call reaches the CRM MCP server's PEP, whose PDP finds no `mission_resource_access` entry covering it and returns `decision: false` with `denial_reason: out_of_authority` ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)). Because the missing authority is eligible for expansion, the denial is marked requestable with the deployment's Access Request binding context, and the PDP writes Decision Evidence bound to the Mission, the `authority_hash`, the policy-view version, the requested action, and the actor context.
**Stage 3, expansion request.** The orchestrator uses the requestable-denial context to submit the CRM authority through ARAP. Expansion is an ordinary Mission creation bound to the predecessor's grant (an eligibility rule, not an authority path), so the client presents the predecessor's refresh token and the AS adjudicates a successor of that specific Mission, obtaining fresh consent on the successor's full derived authority rather than a delta over the predecessor ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)).
**Stage 4, user denial.** The user declines. CRM access is out of policy for board-packet preparation. No successor Mission is created, nothing supersedes anything, and `msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1` stays `active` with its original three entries. Subsequent identical attempts remain denied, and a deployment may suppress duplicate prompts with a denial cache.
**Stage 5, audit.** A week later, a governance reviewer queries the Authorization Server for the Mission's lifecycle events and the PDP's evidence store for records bound to `mission.id` and `mission.issuer`. The lifecycle records show approval at T0, no successor, and no termination. The evidence records show the permitted finance and workflow operations, the denied CRM attempt, and the denied expansion attached to it. Where the deployment registers evidence into a SCITT Transparency Service, the whole history is one verifiable, append-only feed under the Mission's subject ([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)). The join is deterministic, not stitched from timestamps. That property has a name the [series adopted](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/): verifiable continuity, the approval, the denied attempt, the expansion, and every permitted operation provably one undertaking, which is a different property from the lifecycle continuity that lets parked work survive a disconnect.
That is the answer to the question the MCP series keeps asking. "Did the agent stay within what the user approved?" is answerable only if the protocol knows what was approved. The Mission is that record. The Authority Set and the evidence answer what was allowed and what occurred, and the denial trace shows the whole spine holding under refusal, which is where governance systems usually fall apart.
# Where to start building
Start with the per-call foundations from the [Closing the Gaps checklist](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/#where-to-start): authorize every `tools/call` at the MCP server, filter `tools/list` with the same policy, and adopt the AuthZEN decision shape so denials can become governed requests. Each pays for itself before any Mission machinery arrives. The Mission layer is what makes them compose:
- **When approvals must compose across calls, servers, or authorization domains**, adopt the issuance core so both models project from the same task object ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)), and add the runtime and AuthZEN profiles for the per-action gate and evidence ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)). With a freshness source in place, that is the Runtime-Enforced level of the Mission Assurance Levels.
- **When revocation must bite faster than token expiry**, add Mission Status and Lifecycle Signals as the freshness source ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)).
- **When the approval itself must be trustworthy**, add shaping discipline and Consent Evidence ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)), and the harness for session and egress mediation ([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)).
- **When the Authorization Server cannot change**, the [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) is the AS-optional peer binding: governance and per-action enforcement with ordinary tokens, at the cost of issuance gating.
- **For the full map of levels and maturity**, the [Mission Assurance Levels](/notes/adopting-mission-bound-authorization/#run-governed-and-beyond) in Adopting Mission-Bound Authorization and the [draft-family table](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance) in the Reference are the current state.
Missions do not replace resource authorization. They organize task identity across calls, servers, authorization domains, and credential substrates. Token-side and resource-side are two OAuth-shaped ways authority reaches enforcement. In either, carry authority when it must travel, evaluate locally when context must stay local, and bind the resulting actions to the Mission when work spans calls, tools, or domains.
---
# Least Exposure Is Broader Than Least Privilege
Canonical URL: https://notes.karlmcguinness.com/notes/least-exposure-is-broader-than-least-privilege/
Markdown URL: https://notes.karlmcguinness.com/notes/least-exposure-is-broader-than-least-privilege.md
{{< tldr >}}
- **The claim.** Least privilege bounds what an agent may *do*. Least exposure bounds what the agent may *see* while deciding, and for agents the second surface is larger: a perfectly authorized agent can still be turned against the user by what it was allowed to read.
- **The surface.** Prompt context, retrieved documents, tool schemas, secrets, business rules, approval context, memory, and downstream responses. Because the model is untrusted reasoning, every input is attack surface, not a privacy footnote.
- **The governed slice.** The [least-privilege MCP series](/series/least-privilege-mcp/) scopes the calls to the action's grain. That is the mature half of the problem, and it says nothing about the working set.
- **The test.** Least privilege asks, "May the agent perform this action?" Least exposure asks, "Did the agent need to see this in order to decide?"
- **The proposed answer.** The same approved task that bounds tool use should bound the working set: the [Mission](/mission-handbook/) carries task-scoped minimal disclosure for both halves, the actions the agent may take and the context it may reason over.
**Where this sits.** A companion to the
[architecture chapter](/series/designing-mission-bound-authorization/):
the exposure half of the least-privilege discipline.
**Reading path.** About twelve minutes, start to finish.
{{< /tldr >}}
An agent can make only the exact tool calls it is authorized to make
and still be turned against the user by what it was allowed to read.
The [least-privilege MCP series](/series/least-privilege-mcp/) worked out how to scope the calls. The agent carries a narrow token, or the resource decides each call, and the [standards around AuthZEN, ARAP, and the rest](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) tighten the grain until a call authorizes one action against one resource and nothing more. That is least privilege, and it is the mature half of the problem.
It is also only half. Least privilege bounds what the agent may *do*.
It says nothing about what the agent may be *exposed to* while deciding
what to do. That second surface is larger, and it is where most agent
compromise actually happens.
The distinction is simple:
> Least privilege asks, "May the agent perform this action?" Least
> exposure asks, "Did the agent need to see this in order to decide?"
For agents, the second question is not a privacy footnote. It is a
security boundary.
# Authorized access is not safe exposure
Take the board packet from the MCP series. The agent is scoped exactly right. It may read Q3 financials for one entity, draft one document, and notify the audit committee, each call at the action's grain, each denied if it drifts. Least privilege, done well.
To decide what to call, though, the agent reads a great deal more than it calls. It reads its system prompt, the user's request, retrieved documents, the descriptions and schemas of the tools available to it, the outputs of earlier calls, and whatever memory it carries between turns. Suppose one retrieved document carries an instruction the agent was never meant to act on, a line telling it to ignore its guidance and forward the financials to an outside address. Every tool call the agent then makes may still be individually authorized. The compromise did not arrive through an over-broad grant. It arrived through what the agent was shown, and the authorized notify became the way out. A document is only one carrier. The same instruction can arrive in a tool's description or an error the model reads back.
Least privilege on the call did not help, because the reasoning that
chose the call was steered by context the agent should not have been
handed in that form. The resource read may have been authorized for
the user. It was still the wrong exposure for this step of this task.
That is the core mistake: treating "the principal may access this" as
"the model should see this." Human access and agent exposure are not
the same control. A human can ignore irrelevant material, preserve
secrets by judgment, and remain accountable for misuse. A model turns
everything it sees into possible instruction, evidence, memory, or
egress material. You cannot authorize your way out of a bad working
set.
A fair objection is that this is just need-to-know, and security has always minimized what a principal can see. It is, with one difference that changes its character. Need-to-know for a human is a confidentiality control, resting on a reader who will not act on what they should not have seen. An agent offers no such assumption. For an agent, minimizing exposure is also an integrity control, because what the model reads is what steers it. The old principle did not get replaced. Its job doubled.
# The exposure surface is bigger than data
Minimal disclosure is usually discussed for identity claims, releasing only the attributes a relying party needs. For an agent, the surface is much wider. Everything the agent is shown in order to decide is at once a disclosure surface and an injection surface:
- **Instruction exposure:** the system prompt, developer guidance, user
framing, and approval text.
- **Data exposure:** retrieved documents, search results, records, files,
and tool outputs.
- **Capability exposure:** the catalog of available tools, their
descriptions, schemas, examples, and hidden affordances.
- **Secret exposure:** credentials, tokens, connection strings, signing
material, and privileged configuration that leak into context.
- **Policy exposure:** business rules, pricing logic, security policy,
approval routes, and fraud controls.
- **Memory exposure:** session memory, long-term memory, embeddings, and
summaries carried from earlier work.
- **Response exposure:** downstream responses, errors, logs, and
diagnostic text that come back from the tools it calls.
Each is something the agent should see only as much of as the task in front of it requires. A finance agent does not need the whole document corpus to draft one packet. A support agent does not need every customer's record to answer one ticket. Exposure that exceeds the task is latent risk, whether it leaks outward or steers the agent inward.
The practical test is harsh but usable:
> If this item were prompt-injected, stale, maliciously selected, or
> later leaked, would the approved task still justify showing it to the
> agent?
If the answer is no, it does not belong in the working set merely
because the user, service account, or tool could access it.
# Least privilege already governs one slice
The authorization series draws this line once, for exactly one surface. Filtering `tools/list` is exposure control, not authorization. Narrowing the catalog an agent can see is worth doing, but it does not decide any call, and the server must still authorize every `tools/call`. That distinction is least exposure applied to a single surface, the tool catalog.
The move is to generalize it. The tool catalog is one of many things the agent is shown, and the same discipline applies to all of them. Scope the retrieval. Scope the memory. Scope the schemas, the secrets, and the policy text. Show the agent the smallest slice of each that the task justifies, for the same reason the deployment filters the catalog.
That gives a clean rule for system design:
> Treat every context source as an exposure point with its own PEP.
The retrieval layer decides which records enter context. The tool host
decides which tools and schemas are visible. The memory store decides
which past state is eligible. The secret broker decides what never
enters context at all. The harness decides what survives across a
resume. The egress boundary decides what leaves. A single broad prompt
assembly step that grabs everything the principal can reach is the
context equivalent of handing the agent a root token.
# Untrusted reasoning makes exposure the attack surface
The series is explicit that the model is untrusted reasoning that proposes actions, and that the protocol layer's job is to govern the invocation, not the reasoning. That framing has a corollary it does not draw out. If the reasoning is untrusted, everything the reasoning is shown can steer it. Exposure is not a privacy nicety layered on top. It is the attack surface itself.
Name the stance underneath, because it is the one the [Mission Shaping series](/series/mission-shaping/) set as the operating goal: **survivable incorrectness**. No system can make the model's reasoning correct, so the system stays governable when the reasoning is wrong, and that discipline has two arms. The action arm is the authorization half this essay began with, matured into [the laws and the runtime gate](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): a steered agent cannot reach past what was approved. The input arm is this essay's subject: a wrong model can only be steered by what it was shown and can only leak what it was handed. Not-shown is the input arm's fail-closed.
Simon Willison's lethal trifecta names the dangerous combination in one
loop: access to private data, exposure to untrusted content, and a way
to send data out. Least privilege narrows the permissions on those
legs: which data may be read, which tools may run, which egress paths
may be used. Least exposure narrows what actually reaches the model:
which private data enters the working set, which untrusted content can
influence the session, which tool descriptions and responses become
part of the reasoning context. Cutting one leg is not safety. An agent
with a tiny set of authorized calls is still dangerous if it holds the
crown jewels and reads the open web in the same loop.
Exposure, not detection, is therefore the control to lean on. There is no general way to tell whether a retrieved document or a tool description carries an instruction the model will follow, and sanitizing untrusted content is best-effort that injection keeps routing around. What a system can decide deterministically is whether the model is shown the material at all. Not-shown is a guarantee that does not depend on catching the attack first.
This is also why "we log every action" is not enough. Logs tell you
which calls happened. They rarely tell you which sentence, retrieval
result, memory, or tool description shaped the action. If the exposure
path is not governed and recorded, the audit trail starts too late:
at the call, after the model has already been steered.
# The Mission bounds both
The least-privilege series ends on a missing object. Nothing names the task the calls serve, so no layer can decide whether a given call is still inside the work the user approved. Exposure has the same gap, from the same cause. Nothing scopes what the agent is shown to the task it was approved for, so it is shown whatever its principal happens to have standing access to.
One object closes both. A Mission, the durable record of the approved
task the MCP series points toward, is a budget for authority and for
disclosure at once. A Mission bounds not only what the agent may do,
but what the agent may be exposed to while deciding what to do.
Retrieval scoped to the Mission does not surface the acquisition memo
that has nothing to do with the board packet. A catalog scoped to the
Mission does not offer the external-email tool for a task that only
reads and drafts. Memory scoped to the Mission does not import last
quarter's unrelated investigation. A secret scoped to the Mission is
used by a broker or PEP, not copied into the prompt.
Context reaches the agent because the approved purpose justifies it,
not because the user behind the agent could have opened it by hand.
The enforcement pattern is the same one the Mission work uses for authority, a mediated custody in which the runtime, not the agent, holds what the task needs and releases only what the current step justifies. The agent is handed a working set, not the keys to everything its principal can reach. Least privilege makes the calls narrow. This makes the context narrow. Both are the Mission doing its job. (The [runtime enforcement profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html) now names this normatively: least exposure is one leg of its trifecta-containment claim, beside an enforced default-taint rule that no paraphrase sheds and full mediation of every egress channel.)
The residual this discipline cannot remove is composition, and it compounds across Missions through anything durable both can touch, each record compliant alone, which is why exposure ceilings on shared stores are ceiling material rather than per-task settings. And on regulated data the pairing stops being optional: there the composition residual is the breach-notification scenario, and the appetite for it is set by statute, not by the deployment.
One honesty note before the checklist. The authorization half of this discipline has wire surfaces behind it, and the exposure half mostly does not yet. What is enforceable today is the edges: egress mediation, the taint rule, catalog filtering as exposure control. Scoping retrieval, memory, and context assembly to the Mission has no draft and no interoperable form anywhere in the ecosystem, so treat this essay as design guidance for surfaces nobody has standardized, and treat the [community list](/notes/adopting-mission-bound-authorization/#what-the-community-still-has-to-standardize) as naming the gap rather than closing it.
The claim should be checkable. A system that says it runs governed
agents should be able to answer:
1. What Mission is this context item justified by?
2. Which exposure point admitted it?
3. Was it private data, untrusted content, capability metadata, memory,
policy, or a secret?
4. What downstream actions became riskier because the agent saw it?
5. When the Mission ends, what context, memory, cache, or connection is
cleared?
If those answers do not exist, the deployment has least-privilege calls
wrapped around an ungoverned working set.
# Two halves of one discipline
Least privilege and least exposure are two faces of one discipline, minimal disclosure for agents. One narrows the calls the agent can make. The other narrows the inputs to the reasoning that decides which calls to make. The authorization half is mature. It has models, standards, and a clear place to enforce. The exposure half is mostly still left to prompt hygiene and hope, even though it is the larger surface and the one the lethal trifecta actually exploits.
A well-authorized agent fed the wrong context is still a compromised
agent. Least privilege is necessary. Least exposure is the broader
principle it lives inside, and the same approved task that bounds one
should bound the other.
The short version is the one worth carrying into architecture reviews:
> Do not only ask what the agent can call. Ask what the agent can see
> before it chooses the call.
---
# The Question Authorization Never Answered
Canonical URL: https://notes.karlmcguinness.com/notes/the-question-authorization-never-answered/
Markdown URL: https://notes.karlmcguinness.com/notes/the-question-authorization-never-answered.md
{{< tldr >}}
- **The claim.** Authorization systems can encode purpose and platforms can keep task state. What the interoperable stack does not standardize is a durable approved-task object that survives credentials, sessions, actors, and authorization domains.
- **The history.** Authentication, sessions, RBAC, federation, OAuth, UMA, workload identity, and fine-grained policy each solved a different scaling problem, and each deferred the undertaking because someone else was carrying it: the person, the application, the ticket.
- **The two stacks.** The authorization stack answered "may this request proceed." The work stack, tickets, purchase orders, and approvals, held what was approved. They never merged, and human process was the join.
- **The prior art.** UMA removed the synchronously present resource owner. Usage control made ongoing decisions and mutable state first-class. ABAC and XACML can evaluate purpose when another system supplies it.
- **The forcing function.** Unattended work removes the carrier. Once work spans time, actors, and domains, no person is on the execution path to hold the why, and platform-private context never reaches the boundaries that must decide.
- **The proposed answer.** A [Mission](/mission-handbook/) records the approved undertaking, derives authority from it, and supplies current state to the boundaries within a deployment's declared enforcement scope.
**Where this sits.** A companion to the
[architecture chapter](/series/designing-mission-bound-authorization/):
the history behind the missing shared object.
**Reading path.** ~12 minutes, start to finish.
{{< /tldr >}}
Stand at any enforcement boundary and ask the question in this
essay's title. A resource server holds a valid token. A policy engine
holds a permitted request. A session store holds an authenticated
continuation. Now ask what work this is, who approved it, and whether
it is still on. Every layer answers a question adjacent to that one,
and none of them answers it.
Ask why, and the useful answer is that no one designed the
authorization stack as a whole. It accumulated one problem at a time.
Each generation solved the question its era made urgent, and deferred
the question above, because someone else was always carrying the
answer: the person at the keyboard, the application's workflow table,
the ticket in the queue. That is not a criticism. It is how
infrastructure evolves, and this is a model history rather than a
literal timeline, since the generations overlap and modern systems
run all of them at once. The point is to name the scaling problem
that made each abstraction succeed, the carrier that let it defer the
undertaking, and the moment the deferral stopped working.
# Every generation answered its era's question
**Passwords answered who may enter.** Shared systems needed to
distinguish users, and password authentication helped bind an
interaction to a principal. The credential did not describe an undertaking. It did not need to: the person at the terminal was the carrier, holding the purpose in their head and stopping when the work was done.
**Sessions answered continuity.** The web split one interaction across
many stateless requests, so sessions carried authenticated context from
one request to the next. A session says that these requests belong to
the same authenticated interaction. It does not prove that the person is still attentive or explain what the interaction is trying to accomplish. It does not need to: the person is presumed present, and presence is the carrier.
**RBAC answered administration.** Enterprises could not manage
permissions one user at a time, so roles associated permissions with
organizational responsibilities. NIST's early RBAC work described the
administrative advantage directly: users receive permissions through
roles and can change assignments without rewriting the underlying
access structure ([Ferraiolo, Cugini, and Kuhn, 1995](https://www.nist.gov/publications/role-based-access-control-rbac-features-and-motivations)).
A role is therefore usually sized to a responsibility or job function,
not one bounded undertaking. The undertaking had its own carrier: the
job itself, with a manager, a process, and a paper trail deciding what
the role-holder should actually be doing today.
**Federation answered trust across organizations.** Enterprises needed
one login to reach many providers, so SAML and later OpenID Connect
standardized the assertion: a signed, minutes-lived statement of who
authenticated, with what attributes, at what strength. A whole
generation of trust infrastructure, and the undertaking never appears
in it. It did not need to: the assertion opens a session on the far
side, and the person inside that session carries the why across the
boundary in their own head.
**OAuth answered limited API authority.** Applications needed a way to
access protected resources without collecting every user's password.
[OAuth 2.0](https://www.rfc-editor.org/rfc/rfc6749.html) standardized how
a client obtains and presents authorization, with access tokens
representing particular scopes and durations. It also included
client-only authority through the client credentials grant. OAuth's core question is not simply "who delegated?" It is broader:
what authorization may this client present to this resource server?
OAuth is also the one generation where the approval visibly happened:
a human saw a consent screen and said yes. Then the protocol kept the
authority and discarded the event. The scopes survive on the token,
[what was shown and agreed to](/notes/you-approve-what-you-were-shown/)
does not, and the person who consented becomes the carrier of what
they meant by it.
**UMA answered asynchronous resource sharing.** [User-Managed Access
(UMA) 2.0](https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html)
extended OAuth so a requesting party's client could use a permission
ticket to seek a requesting party token (RPT) for protected-resource
access asynchronously from the resource owner's authorization. The
authorization server evaluates
resource-owner policy conditions and requesting-party claims, and can
manage access grants over time. UMA therefore removed the assumption
that the resource owner must be present when access is requested.
That makes UMA important prior art, not a near miss to dismiss. Its
governed object is a requested or granted set of permissions to
protected resources. Policy condition setting is deployment-defined,
and the specification does not make a multi-step undertaking the common
root for authority derivation, delegation where supported, execution
state, and evidence across all systems participating in the work. A deployment can add those semantics around UMA, and the added task lifecycle is the Mission-shaped part.
**Workload identity answered which software is acting.** Cloud and
container platforms could not safely identify workloads with shared,
manually provisioned secrets. Workload identity systems bind a runtime
process to a verifiable software identity and issue credentials without
requiring the workload to manage a long-lived secret. [SPIFFE's Workload
API](https://spiffe.io/docs/latest/spiffe-specs/spiffe_workload_api/),
for example, supplies X.509 or JWT identity documents to an identified
workload, while SPIRE can select the identity from attested process and
platform attributes.
That is a major step for attribution and credential hygiene. It still
answers *which workload*, not *which approved undertaking*. One workload
identity may execute thousands of tasks, and one task may fan out across
many workload identities. The active IETF
[WIMSE working group](https://datatracker.ietf.org/wg/wimse/about/) is
addressing how workload identity technologies compose across multiple systems. Task approval and lifecycle remain a separate authorization concern. And here the carrier begins to thin, because a workload has
no head to hold the purpose in.
**Fine-grained authorization answered the request.** Scopes and roles
were too coarse for many estates, so ABAC, ReBAC, policy engines, and
externalized decision services made the request itself the decision
unit. [NIST's ABAC definition](https://csrc.nist.gov/pubs/sp/800/162/upd2/final)
allows policy to evaluate subject, object, operation, and environment
attributes. A policy decision point can answer whether this request is
permitted with great precision, but it can evaluate only the context it
receives. It does not inherently own the approval, lifecycle, or cross-domain distribution of an undertaking. It evaluates what it is handed, and something else must carry the undertaking to the boundary.
Lay the abstractions out and the remaining responsibility becomes
visible:
| Generation | The question it answered | What another layer still supplies |
| --- | --- | --- |
| Credentials | Who may enter or act? | The work being attempted |
| Sessions | Which requests share authenticated context? | Whether the undertaking remains approved |
| RBAC | What may someone in this organizational role do? | The bounds of today's task |
| Federation | Who authenticated, across organizational boundaries? | The undertaking behind the session it opens |
| OAuth | What limited authority may this client present? | The lifecycle of the work behind grants and tokens |
| UMA | May this requesting party access this protected resource under the owner's policy? | The lifecycle of the undertaking behind that access |
| Workload identity | Which software workload is acting? | Which approved task this workload is performing |
| Fine-grained policy | Is this request permitted under current inputs? | Who produces and maintains approved-task state |
The claim is not that these layers cannot carry purpose. XACML's
privacy profile, for example, defines an explicit
[`action:purpose`](https://docs.oasis-open.org/xacml/3.0/xacml-3.0-privacy-v1-spec-cd-1-en.html)
attribute. ABAC can evaluate a purpose, ticket, or task identifier as an
environment attribute. The unresolved architectural question is who
creates that state, binds approval and authority to it, keeps it current,
and makes it available across every boundary included in the claim.
# The second stack
The why was never missing from the enterprise. It was in the other
stack.
Alongside the authorization stack, every organization runs a work
stack: purchase orders, change tickets, case records, workflow runs,
access requests and their approvals. That stack has always held
exactly what the authorization stack lacks: what was asked for, who
approved it, what it covers, and when it ends. A change ticket has a
scope and a window. A purchase order has an amount and a counterparty.
An access request has an approver and a justification.
The two stacks never merged. The work stack's records are not
enforceable objects: nothing at a resource server consults the ticket
before honoring the token that was provisioned because of it. The
authorization stack's artifacts are enforceable but carry no
undertaking: the token outlives the change window it was granted for.
The join between them was human process. Someone read the ticket,
provisioned the grant, and was supposed to remember to remove it, and
every access recertification campaign since is the cost of that join.
Seen from this history, the [Mission](/mission-handbook/) is not a new
kind of object so much as a merger the two stacks have owed each other
for decades: the work stack's record given the authorization stack's
enforceability, approval-backed state that boundaries consult rather
than paperwork that boundaries trust someone else to have checked.
# Why OAuth stopped where it did
It is tempting to read OAuth's silence about a generic task lifecycle as
an oversight. It is better understood as a protocol boundary.
OAuth standardized authorization grants, token issuance, scopes,
durations, and resource-server access. It did not try to standardize the
semantics of every workflow an API might serve. Later work expanded the
model in important directions. UMA made authorization asynchronous with
respect to the resource owner and policy-driven across protected
resources. [Rich
Authorization Requests](https://www.rfc-editor.org/rfc/rfc9396.html),
for example, can represent a specific payment amount, creditor, and set
of actions. But each API defines the meaning of those details, and RFC
9396 explicitly leaves their combination and comparison to the API and
authorization server. Rich authority is not automatically a durable,
shared task lifecycle.
That separation was productive: the work stack held the purpose, the
person carried it between systems, and OAuth did not need to become a
workflow protocol to succeed.
The limitation appears when work crosses the walls that held its
context. A private task table can govern one platform well. It cannot
govern a resource server, sub-agent, credential broker, or partner
domain that never receives its state. What has no standard form is therefore not "purpose" in the abstract. It is interoperable, approval-backed task state at the boundaries that rely on it.
# The theory arrived early
The idea of continuing authorization is not new. In 2004, Park and
Sandhu's [UCONABC usage-control
model](https://dl.acm.org/doi/10.1145/984334.984339) generalized access
control to include authorizations, obligations, conditions, ongoing
decisions, and mutable attributes. UCON recognized that authorization
need not be a one-time gate: relevant state can change during use, and a
decision may need to change with it.
That prior art matters for two reasons. First, ongoing evaluation and
mutable authorization state should not be presented as inventions of
agent security. Second, continuing decisions alone do not identify the
governed undertaking. A policy engine can repeatedly evaluate current
state only after some system defines the state, owns its transitions,
and makes it trustworthy to the enforcement point.
Mainstream authorization deployments had practical substitutes:
application workflow, tickets, sessions, short-lived credentials, and
human operators. Continuous checks also impose state-distribution, availability,
latency, and ownership costs, and for twenty years the workaround was
cheaper, because a human carrier costs nothing at the protocol layer.
Unattended, cross-system work breaks the workaround. It makes stale
task state a recurring runtime problem instead of an occasional
integration concern, and it removes the person who used to notice.
# The experiment that breaks the composition
Run the composition against one month-long undertaking. Alice tells an agent to handle
the company's taxes. The agent evaluates filing software, engages a
bookkeeping sub-agent, requests documents from a payroll provider, files
an extension, waits for a state response, resumes three weeks later, and
appeals a rejected form.
Each component can be locally correct. Each provider issues a valid
grant. Every resumed session authenticates successfully. Each API
accepts only its own documented scopes or authorization details. The
bookkeeping sub-agent has an attested workload identity and rotated
credentials, and every policy decision is defensible from the inputs it
received.
Now ask about the undertaking as a whole. Which approvals authorize the
sub-agent to contact the accountant? Which grants belong to this tax
engagement rather than another one? Which authority should survive the
extension and which should discharge after filing? If Alice's business
is acquired and the engagement must stop, which active sessions,
tokens, queued steps, and derived grants are part of the stop?
An orchestration platform may know the answer in a private workflow
record. An IGA or PAM system may hold the approval. An audit system may
reconstruct much of it afterward. The problem is that no individual
credential, session, role, token, or policy decision necessarily carries
the whole relationship, and a remote enforcement point cannot consult
state it was never given.
Nothing in the underlying stack has malfunctioned. Every layer
answered its own question correctly. What the composition is missing
is the one participant whose job was never written down: the person
who held the whole engagement in their head, noticed when
circumstances changed, and stopped. Unattended work moves that person
off the execution path while the work continues across hours, actors,
and domains. The carrier is gone, and no layer was ever asked to
replace them.
# The missing shared answer
| Layer | The question it answers |
| --- | --- |
| Human identity | Which person is acting? |
| OAuth | What authority may this client present? |
| UMA | May this requesting party access this protected resource under the owner's policy? |
| Workload identity | Which software workload is acting? |
| Fine-grained policy | Is this action permitted under current inputs? |
| **Mission** | **What approved work governs the authority, and is it still active?** |
The [Mission](/mission-handbook/) is this handbook's proposed answer: a
durable, approved, integrity-anchored record of an undertaking and its
Authority Set. Credentials project authority from it. Consequential
actions are checked against its current state within a declared
enforcement scope. Delegation, where supported, cannot exceed the
approved Authority Set. A non-active Mission stops new derivation and reliance within the deployment's published freshness bound. Effects that already completed still require cancellation, compensation, or review.
The [handbook](/mission-handbook/) develops that object and its
[five laws](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority):
Durability (the governing record is independent of credential lifetime), Attribution (actions remain attributable), Narrowing (derived authority only narrows), Termination (non-active state stops reliance within scope and freshness bounds), and Containment (consequential actions are checked against the approved Authority Set and current state). Those are deployment obligations, not properties that follow
from adding a claim to a token.
This history also explains why the argument is broader than AI. CI
pipelines deploy after a merge, scheduled jobs move money at midnight,
and infrastructure controllers reconcile systems while teams sleep. Each
is unattended work whose purpose often lives in a repository, ticket,
or platform-local record while its credentials are sized for an
integration.
Agents intensify the problem because their plans change, their work fans
out, and untrusted content can steer their choices. They did not create
the missing shared state. They make its absence harder to tolerate.
Identity now covers people, clients, and attested workloads, and
authorization can decide whether a particular request is permitted.
The question in this essay's title was never unanswerable. It was
deferrable: someone was always there to carry the answer, so no
generation had to make it a standard. Every generation answered the
question its era made unavoidable. This one is ours, because it just
stopped being deferrable.
---
# The Convergence and the Wagers
Canonical URL: https://notes.karlmcguinness.com/notes/the-convergence-and-the-wagers/
Markdown URL: https://notes.karlmcguinness.com/notes/the-convergence-and-the-wagers.md
{{< tldr >}}
- **The adoption.** [AAuth](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/), Dick Hardt's proposed clean-slate agent protocol, adopted the object this handbook argues for: a [first-class mission layer in its 01 revision](/notes/aauth-now-has-a-mission-layer/), taken up after this model's AAuth mapping circulated. Not independent replication, and better than the hedge: a designer free to say no built the object into his protocol's core.
- **The wagers.** Six bets underneath the design, each with the evidence that would falsify it: the [admission-time bet](#where-this-could-be-wrong) (interpretation moves to admission), the issuer bet (the Mission lives at an issuer, not a harness), the revocation bet (Termination is worth its price), the composition bet (the task must be an object at all), the portability bet (its authority can cross domains), and the classification bet (the consequential line holds under cost pressure).
- **The honest split.** The laws and the claim gate are the invariants. The admission grain, the issuer home, the price of Termination, the necessity of the object itself, the portability of its authority, and the classification line are the wagers, and deployment experience, not this handbook, will settle them.
- **Specs (editor's copies).** [Mission-Bound Authorization for AAuth](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html).
**Where this sits.** The closing part of [the concluding chapter](/series/weighing-mission-bound-authorization/), and of the handbook's reading order. [What Survives Without OAuth](/notes/what-survives-without-oauth/) states the model, and [The Authority Control Plane](/notes/the-authority-control-plane/) names its operational seat.
**Reading path.** ~8 minutes start to finish. Read in order.
{{< /tldr >}}
# AAuth: the substrate that grew the object
The sharpest evidence for the [fundamental-versus-accidental split](/notes/what-survives-without-oauth/#fundamental-versus-accidental) arrived from outside this family. [AAuth](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/) is Dick Hardt's proposed agent-native authorization protocol, an active individual draft (revised through July 2026, and renamed draft-hardt-oauth-aauth-protocol along the way) that departs from OAuth's redirect model rather than extending it: conversational, signed-request-first, with the Person Server as its governing party. It set out to rebuild agent authorization from a clean sheet, and in [an early 2026 revision](/notes/aauth-now-has-a-mission-layer/) it grew the object this handbook argues for: a first-class mission layer, with a mission proposal and approval flow, an `AAuth-Mission` header on the wire, mission-aware token choreography, and Person Server governance endpoints.
Name what kind of evidence this is, because it is not what convergence usually means. This author [mapped the Mission model onto AAuth](/notes/mission-architecture-on-aauth/) before AAuth had a mission layer, AAuth's author read that work, and the next revision adopted the concept. So this is not independent replication, and the handbook does not claim it. It is adoption, and adoption is its own kind of proof: a protocol designer rebuilding agent authorization from a clean sheet, with his own architecture and every option to solve the gap differently, judged the object load-bearing enough to build into his protocol's core. An idea wins two ways, by being found twice or by being adopted once by someone free to say no. This is the second, and it is a sharper test than any crosswalk this handbook grades itself, because the grader was outside. The alignment continues in the details: AAuth's clarification chat, where an approver questions a proposal before consenting, is the interrogation channel the [approval part](/notes/from-a-request-to-an-approved-mission/#translation-and-interrogation-the-disclosure-as-a-dialogue) records in Consent Evidence.
The honest reading of where it stands is the one [the analysis](/notes/aauth-now-has-a-mission-layer/) works through: AAuth closed the "where is the mission layer" question and left the "what authority does the mission actually confer" question open, and the five laws are the yardstick to hold its model to as it matures: portable containment and lifecycle completeness, rather than mission correlation and governance hooks. The family's [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) is the constructive answer: it hosts this model at the Person Server with issuance gating intact, so the two proposals converge instead of forking. The deep treatments are published: [Mission Architecture on AAuth](/notes/mission-architecture-on-aauth/) mapped the model onto the protocol before it had a mission layer, [AAuth Now Has a Mission Layer](/notes/aauth-now-has-a-mission-layer/) re-ran the comparison after the revision, and [Why Mission-Bound OAuth Might Be the Wrong Answer](/notes/why-mission-bound-oauth-might-be-the-wrong-answer/) is the standing critique of the OAuth home itself.
The strategic position does not change, and it is worth restating with AAuth in full view: OAuth is the adoption path because it is where the deployments are, AAuth may prove the cleaner native substrate for the agents that come next, and the model, the laws, and the gate are built to survive either outcome. The falsifiable version of that sentence lives below, in [where this could be wrong](#where-this-could-be-wrong).
# Where this could be wrong
An architecture document that only argues for itself is marketing, and an [earlier post in this work's ancestry](/notes/why-mission-bound-oauth-might-be-the-wrong-answer/) asked openly whether Mission-Bound OAuth was the wrong answer. That discipline belongs in the handbook too, so here are the six bets most worth doubting, each with the evidence that would falsify it.
**The admission-time bet.** The model moves interpretation to admission, where an accountable authority, a human or a policy a human consented to, decides against committed inputs before any authority exists ([who may approve](/notes/from-a-request-to-an-approved-mission/#who-may-approve)). If agent work proves more emergent than that, if real tasks discover most of their authority mid-flight, then the discovery loop becomes the hot path instead of the exception, admission becomes the bottleneck no matter who staffs it, and deployments will choose between rubber-stamped ceilings and friction that drives them back to broad grants. Progressive authorization and policy approvers are the hedges, and they are young precisely because nobody yet knows the sustainable grain. The price per admission is at least falling: an approved expansion no longer costs a restart, because the harness rebinds the running session to the successor Mission. The falsifying evidence: expansion and exception rates that stay high after templates and organizational priors mature. And the quieter falsifier, because rubber-stamping improves those metrics while losing the bet: approval telemetry showing the decisions stopped being decisions, median time-to-approve collapsing toward the four-second reflex, disclosure interaction going to zero, and narrowing rates falling while approved breadth grows.
**The issuer bet.** The family puts the Mission at an issuer, the Authorization Server or the standalone MAS, because that is where approval, derivation, and revocation already live. But the work itself lives in harnesses and orchestrators, and the industry could consolidate governance there instead: the runtime platform as the source of truth for the task, with the identity stack reduced to credentials. The MAS binding hedges the deployment topology, not the ownership question. The falsifying evidence: harness vendors shipping proprietary task objects that win adoption without ever touching the token layer.
**The revocation bet.** The laws price Termination as non-negotiable, and much of the family's weight, state-gated issuance, Status, the freshness bounds, the offline chain's state check, is the cost of making revocation reach everything. A capability-native world could decide that short expiry with no refresh is enough, accept the bounded staleness, and skip the central state dependency entirely, trading the kill switch for autonomy and offline verification. If the market accepts that trade at scale, the category as this handbook defines it loses its fourth law to a cheaper approximation. The falsifying evidence: serious deployments running attenuable tokens with no state source and eating the staleness without incident.
**The composition bet.** The family bets that the approved task must
be a first-class object. The alternative is composition: policy
engines, workflow state, and richer token claims, wired together
carefully, might deliver bounded, revocable, attributable agent work
with no new object at all. The family's answer is that composition
without a shared root loses exactly the properties that matter,
cross-audience revocation, cross-hop audit join, integrity anchoring
([what becomes possible only with a Mission](/notes/mission-based-authorization-field-reference/#what-becomes-possible-only-with-a-mission)),
but that is an argument, not a deployment result. The falsifying
evidence: production estates achieving task-bounded, task-revocable
agent authority through composition alone, interoperably, without
converging on a shared task object.
**The portability bet.** The family bets that Mission authority can
travel: that the Authority Set, the subset rule, and Mission state can
be projected across authorization domains without losing their
meaning. Cross-domain reality is harsher. The downstream domain may
not speak the parent's authority vocabulary, may not be able to verify
the upstream approval's semantics, may disagree about what counts as
narrower, and may honor an action that is locally permitted and globally outside the Mission. Revocation propagation across administrative boundaries is the same problem wearing its operational face. [Cross-Domain Projection](/notes/mission-bound-authority/#crossing-authorization-domains)
and the portable [Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html)
are the constructive answers, and conservative refusal is the fallback
where translation cannot be trusted. The falsifying evidence:
cross-domain deployments that abandon authority translation and fall
back to local re-approval at every boundary, making the Mission a
per-domain object after all.
**The classification bet.** The runtime gate is priced by the consequential/non-consequential line, and that line is deployment policy with only an extreme-end floor. The bet is that deployments hold the line under latency and cost pressure. The quiet failure is reclassification: consequential classes drifting below the gate because the PDP round-trip hurts, while the claim name never changes. The falsifying evidence: enforcement-scope statements whose mediated class list shrinks release over release while the claim name holds, the classification twin of the admission bet's rubber-stamp telemetry.
None of these is a reason to wait, because every dependency in the reference security architecture is ratified and the laws are cheap to hold even if the bindings move. But a reader deciding how hard to bet should know which parts are invariants and which are wagers, and the honest split is this: the laws and the claim gate are the invariants. The admission grain, the issuer home, the price of Termination, the necessity of the object itself, the portability of its authority, and the classification line are the wagers, and deployment experience, not this handbook, will settle them.
# Where the framework leads
The framework is the map. The build order is the journey, and it is deliberately staged: crawl with the issuance core, walk with the Runtime-Enforced level on ratified substrate, run up the Governed and High-Assurance Agent levels as the deployment earns them. [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/) carries that blueprint, the ecosystem to compose with, the operational surfaces you will own, and the pieces the community still has to standardize.
The so-what of the framework is what it does for the laws. It makes them portable: state them once, realize them per substrate, and hold every competing proposal to the same five.
And that is the handbook's last word: not a token format, but an object the stack can hold, laws it can enforce, and a claim anyone can test. If a law is wrong, a wager mispriced, or a binding missing, the [issues on the draft repository](https://github.com/mcguinness/mission-bound-authorization/issues) are where the argument moves.
---
# The Authority Control Plane
Canonical URL: https://notes.karlmcguinness.com/notes/the-authority-control-plane/
Markdown URL: https://notes.karlmcguinness.com/notes/the-authority-control-plane.md
{{< tldr >}}
- **Two chokepoints.** Issuance gating at the token layer and runtime enforcement at the action layer are strictly stronger together: each chokepoint bounds the other's gap.
- **The pattern space.** The standalone [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) trades the token-layer chokepoint for zero AS change, the [issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html) restores it, and the [Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html) is portable evidence, never a credential.
- **The control plane.** The layer is the [control plane for delegated authority](#the-authority-control-plane): the Mission is desired state, tokens, PEPs, and PDPs are the data plane, and the mapping runs concept by concept from the store to the fleet API, with three disciplines that keep the framing honest.
- **Specs (editor's copies).** [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html), [Mission Issuance Grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html), [Mission Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html), [Mission Cross-Domain Projection](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-cross-domain.html).
**Where this sits.** Part 2 of [the concluding chapter](/series/weighing-mission-bound-authorization/). [What Survives Without OAuth](/notes/what-survives-without-oauth/) states the model this part deploys, and [The Convergence and the Wagers](/notes/the-convergence-and-the-wagers/) closes the handbook.
**Reading path.** ~6 minutes start to finish, or jump to [the mapping table](#the-authority-control-plane) for the control-plane reading at a glance.
{{< /tldr >}}
The model survives without OAuth, and this part is where it sits when
OAuth is exactly what you have: the two chokepoints the binding
stacks, the pattern space of bindings between them, and the structural
reading that platform engineers reach for unprompted.
# Two chokepoints, and the patterns they allow
The OAuth binding stacks two independent chokepoints, and the [Architecture's deployment patterns](https://mcguinness.github.io/mission-bound-authorization/draft-mcguinness-mission-architecture.html#deployment) are combinations of them.
**Issuance gating** acts at the token layer. A revoked or expired Mission stops all further derivation and refresh, and short-lived tokens age out. **Runtime enforcement** acts at the action layer. Each consequential action is re-checked against current state at the point of use. Together they are strictly stronger than either alone: a gap in PEP coverage is still bounded at the token layer, and an outstanding token is still stopped at the action layer. This split is also the layer's control-plane contract, mapped in full in [the authority control plane](#the-authority-control-plane) below.
Two newer documents extend the pattern space.
**The [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html)** is the standalone binding, Standards Track, and the estate control plane for approved-task authority. A dedicated service implements the Mission Issuer role and derives no tokens, and the PDP join, the family's newest mechanism, connects each ordinary OAuth token to its Mission at the point of use. The MAS serves the Status and lifecycle surfaces itself and is the deployment's freshness source, and expansion and Child Mission creation ride its own submission surface with an authenticated-client binding in place of token possession.
It is a peer binding with its own rationale, governance deliberately decoupled from token issuance and one Mission Issuer governing across many Authorization Servers, that also serves as the adoption bridge where the AS cannot yet change. The trade is explicit: Mission governance and per-action enforcement with zero AS change, at the cost of the token-layer chokepoint. Revoking a Mission in this mode stops nothing at the token layer, so enforcement rests entirely on PEP coverage. The [issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html) is the repair: estate Authorization Servers redeem MAS-minted grants for Mission-bound, state-gated tokens, restoring the token-layer chokepoint without moving approval into the AS.
**The [Mission Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html)** is an advanced profile, its dependencies ratified, a design to adopt when the cross-domain proof case arrives. The question it answers is proof. A Mission's committed facts live on the record at its issuer, and a party outside that domain cannot verify what was approved short of a token-exchange hop or trust in the issuer's records. The Mandate is a signed, portable, independently verifiable statement of those facts, minted by the Mission Issuer, with optional selective disclosure. The design line is the one this handbook has drawn everywhere: a Mandate is evidence, not a credential. Presenting one authorizes nothing.
The two bindings, side by side, and the Architecture names the
distinction: the embedded binding is a credential-carried security
architecture and holds both chokepoints, while the standalone binding
is PDP-joined, trading the token-layer chokepoint for zero AS change,
with the issuance grant as the join that restores it:
```mermaid
flowchart TB
subgraph EMB["Embedded binding: the AS is the Mission Issuer"]
direction LR
M1[("Mission record")]
AS1["Authorization Server
(Mission Issuer)"]
AG1([Agent])
PEP1["PEP + PDP:
the action-layer chokepoint"]
AS1 --- M1
AS1 -->|"mission-bound, state-gated tokens:
the token-layer chokepoint"| AG1
AG1 -->|per action| PEP1
M1 -.->|current state| PEP1
end
subgraph STA["Standalone binding: the Mission Authority Server"]
direction LR
M2[("Mission record")]
MAS["Mission Authority Server:
approval, lifecycle, Status"]
AS2["Estate Authorization Servers,
unchanged, one or many"]
AG2([Agent])
PEP2["PEP + PDP:
the action-layer chokepoint"]
MAS --- M2
AS2 -->|"ordinary tokens:
no token-layer chokepoint"| AG2
AG2 -->|per action| PEP2
PEP2 -.->|"joins each token to its Mission
at the point of use"| M2
MAS -.->|"issuance grant: MAS-minted grants
redeemed for Mission-bound,
state-gated tokens"| AS2
end
```
The diagram shows two of the family's three binding security architectures. The third, authority-native, is the AAuth binding's: the substrate's own authority object is the record, and the [Reference](/notes/mission-based-authorization-field-reference/#the-numbers) carries the trio.
The third extension of the pattern space is [Mission Cross-Domain Projection](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-cross-domain.html), one Mission honored in another trust domain through a single-hop, audience-scoped cross-domain grant. [Mission-Bound Authority](/notes/mission-bound-authority/#crossing-authorization-domains) covers it in full, because the multi-domain agent task is the common case, not the exotic one. It sits with the advanced profiles rather than in the reference security architecture, with its dependencies tracked honestly: the identity-chaining work it profiles is approved and in the RFC Editor queue, and ID-JAG is a working-group document.
# The authority control plane {#the-authority-control-plane}
One more reading of the framework earns its place, because platform
engineers reach for it unprompted: the layer is the **control plane
for delegated authority**, with the split that vocabulary always
implies. The
[architecture chapter](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority)
makes the strategic case, and the mapping here is structural, not
rhetorical:
| Control-plane concept | The layer's realization |
| --- | --- |
| Desired state | The Mission: the approved task, its authority, its lifecycle and expiry |
| The store | The Mission Issuer's records and integrity anchors |
| Reconcilers | Lifecycle and gating, the ceiling review, orphaned-evidence reconciliation |
| Distributed configuration | Audience-scoped, versioned policy views the PDPs load |
| The data plane | Tokens, PEPs, and PDPs, enforcing per action at the boundary |
| The sync channel | Mission Status as the pull surface, Signals as the push complement |
| Optimistic concurrency | The state version, with compare-and-set on lifecycle mutations |
| Object metadata | The management profile's owner, administrative domain, and labels |
| The fleet API | Enumeration and bulk lifecycle |
| Observability | Decision, execution, and consent evidence, joined on the Mission: verifiable continuity |
The estate view of the same mapping: desired state above, per-action
enforcement below, the sync channel between them, and the evidence
joining back on the Mission:
```mermaid
flowchart TB
AG([Agent])
subgraph CP["Control plane: the Mission Issuer"]
REC["Reconcilers:
lifecycle and gating,
the ceiling review"]
M[("Desired state:
the Mission record
and its integrity anchors")]
FLEET["Fleet API:
enumeration,
bulk lifecycle"]
REC --> M
FLEET --> M
end
subgraph DP["Data plane: enforcing per action at the boundary"]
TK["Tokens:
state-gated issuance,
the token-layer chokepoint"]
PEP["PEP:
the action-layer chokepoint"]
PDP[PDP]
end
OBS["Observability:
decision, execution, and consent evidence,
joined on the Mission"]
M -->|state-gated issuance| TK
M -->|"the sync channel:
Status pull, Signals push,
audience-scoped policy views"| PDP
TK --> AG
AG -->|action + parameters| PEP
PEP -->|evaluate| PDP
PDP -->|permit / deny| PEP
M -.->|consent evidence| OBS
PDP -.->|decision evidence| OBS
PEP -.->|execution evidence| OBS
```
Three disciplines keep the framing honest. The noun is scoped: this
is the control plane *for delegated authority*, never an agent
control plane, because it governs what an agent may do and never how
the agent runs (the harness and the orchestrator keep operations).
The category is unchanged: mission-based authorization remains the
claim and the
[six-property litmus](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test)
remains its gate, and control plane names where the layer sits
operationally, not a new name for the layer. And a control plane is only as real as its data-plane contract, which is why the AuthZEN decision wire, the enforcement-scope statement, and the evidence family carry the interoperability weight, with [the list the community still has to standardize](/notes/adopting-mission-bound-authorization/#what-the-community-still-has-to-standardize) naming what that contract still lacks at the tool boundary.
The model is stated, and its operational seat is named. What remains
is judgment: the outside evidence for the shape, and the bets
underneath it. [The Convergence and the Wagers](/notes/the-convergence-and-the-wagers/)
closes the handbook with both.
---
# What Survives Without OAuth
Canonical URL: https://notes.karlmcguinness.com/notes/what-survives-without-oauth/
Markdown URL: https://notes.karlmcguinness.com/notes/what-survives-without-oauth.md
{{< tldr >}}
- **The move.** The OAuth profiles are one binding of a substrate-neutral model, organized along a [verb spine](#the-framework-oauth-carries) from propose to analyze. The model survives a substrate change, which is the test of a framework rather than a feature, and the [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) is the existence proof.
- **Fundamental versus accidental.** Which ideas survive if OAuth disappears? Nearly all of them: the layer, the laws, the vocabulary, the approved task with an integrity-anchored record, approval evidence, runtime containment. What is accidental is the realization: PAR, RAR, the claim names, the wire shapes.
- **The laws.** The framework restates all [five laws of delegated authority](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) beyond OAuth, and separates the fundamental principles from their accidental OAuth realization.
- **The card analogy.** The network standard: what makes any bank's card work at any merchant. ([Where the analogy breaks](/notes/agents-need-a-corporate-card-not-a-blank-check/#where-the-analogy-breaks).)
- **Specs (editor's copies).** [An Architecture for Mission-Bound Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) (the front door), [Mission Security Model](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-security-model.html), [Mission Substrate Requirements](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-substrate.html).
**Where this sits.** Part 1 of [the concluding chapter](/series/weighing-mission-bound-authorization/): the model beyond its bindings. [The Authority Control Plane](/notes/the-authority-control-plane/) carries where the layer sits operationally, and [The Convergence and the Wagers](/notes/the-convergence-and-the-wagers/) closes the handbook.
**Reading path.** ~7 minutes start to finish. Read in order.
{{< /tldr >}}
# Overview
The Mission is the durable, approval-backed record of the task ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/)), and the chapters before this one made the argument at every altitude: the intuition, the architecture, the wire, the outside framings. This concluding chapter zooms out to the framework those chapters realize, because the model does not depend on OAuth even though the profiles do. If you want the build order first, read [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/) and come back. The maturity and status claims here are as of July 2026, and the [Reference](/notes/mission-based-authorization-field-reference/) carries the reconciliation date the handbook tracks.
# The framework OAuth carries
OAuth earned its place as the first substrate for one reason: it is where the deployments are. The Authorization Server already exists, the token machinery already works, and the agent identity stack the [AI agent auth best practices](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) describe is OAuth-shaped. But the object this handbook built is not an OAuth feature, and OAuth is no longer its only binding. The family now carries four: the OAuth core as the flagship, the standalone [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) as a peer binding, an [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) that hosts AAuth's native mission concept at its Person Server, and an experimental [UMA 2.0 sketch](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-uma.html). The UMA sketch is the first binding authored against [Substrate Requirements](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-substrate.html), which consolidates what any further binding must provide. The [Architecture document](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) states the model on its own terms, and it is the right front door for a newcomer who wants the whole shape before any wire detail.
The coarsest map of the layer is four functions, and they survive any substrate:
| Function | What it does | Spine stage | Where |
| --- | --- | --- | --- |
| **Authority compilation** | Turns approved intent into bounded, integrity-anchored authority | Intent, Mission | [The Mission](/notes/the-mission-is-the-missing-abstraction/), practice [approval integrity](/notes/from-a-request-to-an-approved-mission/) |
| **Authority projection** | Carries that authority onto instances, credentials, domains, and delegates without ever exceeding it | Authority | practice [delegation](/notes/mission-bound-authority/) |
| **Authority containment** | Checks every consequential action against the approved purpose at the point of use | Enforcement | practice [runtime enforcement](/notes/mission-bound-runtime-enforcement/) |
| **Authority continuity** | Keeps reliance conditioned on the current state of the task, across time and across the runtime | Lifecycle and runtime | practice [lifecycle](/notes/mission-lifecycle-and-change/) and [agent runtime](/notes/the-agent-runtime-and-audit/) |
These names are built to survive the Mission. If a different object wins the standards conversation, the layer still needs compilation, projection, containment, and continuity, and this handbook is a complete worked example of all four.
The finer decomposition is a verb spine. Each verb answers one question, sits on one trust boundary, and is owned by named documents:
| Verb | The question | Owned by | Where |
| --- | --- | --- | --- |
| Propose | How does a request become a candidate Mission Intent? | Intent Shaping | [Approval integrity](/notes/from-a-request-to-an-approved-mission/) |
| Approve and record | How does a proposal become a committed, integrity-anchored Mission? | The issuance core, Consent Evidence, Deferred Approval | [The Mission](/notes/the-mission-is-the-missing-abstraction/), [approval integrity](/notes/from-a-request-to-an-approved-mission/) |
| Govern | How is state observed, changed, widened, and retired? | Status (carrying completion), Signals, Expansion | [Lifecycle](/notes/mission-lifecycle-and-change/) |
| Enforce each action | Is this concrete action allowed under the current Mission? | Runtime contract, AuthZEN binding | [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) |
| Run and wind down | Does the runtime stop when the Mission does, and what unwinds? | Harness, Orchestration | [Agent runtime](/notes/the-agent-runtime-and-audit/) |
| Delegate | How does authority narrow across actors and instances? | The core's act chain, Child Delegation, Offline Attenuation | [Delegation](/notes/mission-bound-authority/) |
| Project | How is one Mission honored in another trust domain? | Cross-Domain Projection | [Delegation](/notes/mission-bound-authority/#crossing-authorization-domains) |
| Prove | What can a third party verify about what was approved and done? | Consent Evidence, Mandate, Audit Transparency | [Approval integrity](/notes/from-a-request-to-an-approved-mission/), [agent runtime](/notes/the-agent-runtime-and-audit/), and [the control plane](/notes/the-authority-control-plane/) |
| Analyze | What must be trusted, and what breaks when each component is compromised? | Security Model | The [Reference's adversary model](/notes/mission-based-authorization-field-reference/#adversary-model) |
The spine the architecture follows, Intent to Mission to Authority to Enforcement, is the temporal reading of the same map: what happens first when a request arrives. The verb spine is the structural reading: which component owns which question. Both readings survive a substrate change, which is the test of a framework rather than a feature.
# Fundamental versus accidental
A sharper version of that test is to ask which of this handbook's ideas survive if OAuth disappears. The answer is nearly all of them. The [layer with no standard form](/series/designing-mission-bound-authorization/#the-missing-layer) and its [five laws](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority). Authority compilation, projection, containment, and continuity. The approved task with an integrity-anchored record and a lifecycle. Approval evidence. Runtime containment at the point of use. Session continuity that is never authority. Those are architectural commitments, not OAuth features.
What is accidental is the realization: PAR as the submission channel, RAR as the Authority Set serialization, the `mission` claim's name, the JWS envelopes, Security Event Tokens for signals, the AuthZEN wire shape. Any of those could be swapped without touching a law. The AAuth binding is the existence proof: the same object, laws, and lifecycle carried onto a non-OAuth substrate by swapping exactly those accidents, and [the closing part](/notes/the-convergence-and-the-wagers/#aauth-the-substrate-that-grew-the-object) carries what AAuth itself did with the object. That division is why the [Architecture document](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) states the model on its own terms and the bindings realize it, and it is the standard to hold any competing proposal to. An alternative that replaces the accidents is a realization. An alternative that drops a law is a gap.
The ontology direction is the newest entry on the accidental side. On OAuth, authority is client-proposed and enumerated: the client asks in types the issuer must understand. On AAuth, the exploratory Rich Resource Requests companion inverts it, with the resource declaring its own operations, meaning, and consequences, and the declaration hash-committed beside what was asked and what was approved. Which party proposes the ontology is substrate. What survives is the fundamental beneath both: meaning binds at approval, is enforced at the point of use, and translation across vocabularies never widens. Authority compilation needs a vocabulary source, and the framework does not care which direction it arrives from, only that the record commits it. It is a realization, not a law.
Where the model sits operationally is the next part: [The Authority Control Plane](/notes/the-authority-control-plane/), the two chokepoints, the pattern space of bindings, and the structural mapping that platform engineers reach for unprompted.
---
# Closing the Agent Authorization Gaps
Canonical URL: https://notes.karlmcguinness.com/notes/closing-the-agent-authorization-gaps/
Markdown URL: https://notes.karlmcguinness.com/notes/closing-the-agent-authorization-gaps.md
{{< tldr >}}
- **The framing.** [Agent Authorization Use Cases and Gap Analysis](https://datatracker.ietf.org/doc/draft-chen-oauth-agent-authz-use-cases/), an individual Internet-Draft by authors from China Mobile, CNNIC, and Huawei: nine agent use cases from the personal assistant to automated incident response, each with its own gap analysis, rolled up to five major gaps in OAuth 2.0. It is the closest thing the standards conversation has to an agreed problem statement.
- **The claim.** The family answers the catalog line by line, with machinery that existed before the catalog was published: ten answered with named drafts, one partial with the nuance stated, one largely delegated. The convergence is the point, because a problem statement and a solution proposal written independently should meet if the missing layer is real.
- **What lives in this part.** [The gaps in one table](#the-gaps-answered), [the nine use cases in one paragraph](#the-nine-use-cases), [three gaps worth a closer look](#three-gaps-worth-a-closer-look), and [the honest remainders](#the-honest-remainders).
- **The laws.** [All five](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): the catalog's gaps are the laws experienced as missing machinery, gap by gap.
- **Specs (editor's copies).** [Mission Management](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html), [Child Delegation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-child-delegation.html), [Deferred Approval](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval.html), and the [AuthZEN profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html).
**Reading path.** ~10 minutes in order, or jump to [the table](#the-gaps-answered) for every verdict at a glance.
{{< /tldr >}}
# Overview
The first four proofs held the model against a threat model, a
requirements framework, a threat taxonomy, and the governance
frameworks. This one holds it against the framing that matters most
for the venue conversation: the standards community's own problem
statement.
[Agent Authorization Use Cases and Gap Analysis](https://datatracker.ietf.org/doc/draft-chen-oauth-agent-authz-use-cases/)
is an active individual draft in the OAuth working group's orbit,
written by authors from China Mobile, CNNIC, and Huawei. Revision 01 (July 2026) is the text answered here. It catalogs nine agent scenarios, runs a gap analysis under each against OAuth 2.0 and its common extensions, and rolls the findings up to five major gaps.
It proposes no solution, which is exactly what makes it the right
test: a catalog of what is missing, written without this family in
mind.
The verdicts here are simpler than the
[OWASP post's](/notes/containing-the-owasp-agentic-threats/), because
gaps are not threats. **Answered** means the gap lands on named
machinery with a draft behind it, maturity labeled. **Partial** means
the machinery covers most of the ask and the remainder is stated.
**Delegated** means the gap belongs to a layer the family composes
with rather than supplies. And one caution rides along, the same one
the whole chapter carries: the catalog is outside, the mapping is
ours, and every row cites the machinery so the reading can be re-run.
# The gaps, answered
The catalog's summary rolls its findings up to five major gaps, and
each use case carries sharper asks beneath them. The table answers
both grains: the five major gaps first, in the catalog's order, then
seven asks from the use-case analyses.
| The gap | What it asks for | The family's answer | Verdict |
| --- | --- | --- | --- |
| Pre-approval versus dynamic authorization | Permissions granted just-in-time as tasks emerge, not all upfront | The admission model plus the [discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem): start narrow, hit a requestable denial, request, approve, expand as a [successor Mission](/notes/mission-lifecycle-and-change/#grow-expansion-creates-a-successor), with [Progressive Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html) (experimental) for ceiling-and-drawdown | Answered |
| No standardized interactive channel | A way to pause a task and ask the user mid-flight | [Deferred Approval](/notes/from-a-request-to-an-approved-mission/) makes the approval asynchronous and pollable, ARAP carries the mid-task ask, [action-bound approval](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level) puts a human on the highest classes, and `suspended` is the pause | Answered |
| Multi-hop delegation chains | Tokens that represent User to Agent A to Agent B verifiably | The RFC 8693 `act` chain via the [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/), [Child Missions](/notes/mission-bound-authority/) with their own lifecycle and lineage, and [offline attenuation](/notes/mission-bound-authority/#mechanism-2-offline-attenuation) whose chains prove their own narrowing | Answered |
| Task-level and bulk revocation | Revoke one task without touching others, and everything at once in an incident | The bullseye: revocation by `mission_id` *is* task-level revocation, [cascade](/notes/mission-bound-authority/) reaches the delegation tree, [Mission Management](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html) carries enumerate-and-bulk-revoke with dry-run first, and the [revocation matrix](/notes/mission-based-authorization-field-reference/#when-revocation-bites) prices the latency | Answered |
| Group authorization | One grant for a coordinated group, members bound late, lifecycle atomic | For a homogeneous group, native: one Mission pinned to the [Agent Deployment](/notes/mission-bound-authority/#which-instance-is-acting) authorizes the class, members bind late by instance attestation, termination ends every member at once, and attribution stays per-instance. Differentiated members are a parent Mission with [Child Missions](/notes/mission-bound-authority/#mechanism-1-the-child-mission). What the family deliberately does not define is one grant carrying per-member subject-audience-scope bindings, because collapsing differentiated members into a shared, undifferentiated grant identity trades Law 2 for convenience | Partial |
| Scope explosion | Consent that survives a thousand granular scopes | Consent moves to task grain: the approver consents to one Mission's derived authority, rendered legibly, instead of a thousand scopes, and the [fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget) is managed rather than wished away | Answered |
| Conditional policy enforcement | Conditions like time windows expressed and enforced, not custom-coded | Per-entry `constraints` evaluated on every action by the PDP through the [AuthZEN binding](/notes/mission-bound-runtime-enforcement/), with an unknown constraint refusing rather than passing, and resource policy staying authoritative | Answered |
| Agent-user differentiation | A standard way to know an agent is calling, not a human | The identity substrate the family composes with: the [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) and the [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/)'s attested instance identity and provenance, with the `mission` claim adding what the agent is acting *for* | Answered |
| Constraint expression | Rate limits, data caps, and time bounds, not binary scopes | Structured `constraints` that only tighten, expiry capped by the Mission's clock, and [Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) (experimental) for cumulative budgets and call caps | Answered |
| Cross-agent audit correlation | A reserved, interoperable identifier so one task's actions join across agents, hops, and logs | The `mission` claim is that reserved object: every derived token carries `{id, issuer, authority_hash}`, every decision and execution record binds to it, and the audit join is deterministic rather than stitched from timestamps ([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)), with [Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html) making the joined trail verifiable | Answered |
| Batched authorization across trust domains | A workflow spanning organizations authorized without per-domain re-consent, with context propagated securely | One approval, many projections: the Mission is approved once and honored across domains through [Cross-Domain Projection](/notes/mission-bound-authority/#crossing-authorization-domains) with ID-JAG issuance gated on Mission state, and the [Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html) as the portable, verifiable statement of committed facts. Advanced, with dependencies tracked: the chaining substrate sits in the RFC Editor queue | Answered |
| The OS permission bridge | Cloud-level intent connected to fine-grained local OS permissions | The [harness](/notes/the-agent-runtime-and-audit/) is the PEP for the local paths no gateway sees (files, shell, spawn, resume), which is the slice the family supplies. Bridging Missions into OS-native permission systems is a substrate nobody has built | Delegated |
Ten answered, one partial and one delegated with the remainders stated,
and none waved away. The striking thing about the tally is the
direction of fit: the catalog was written as a problem statement with
no solution in mind, and its gap analysis reads like this family's table of contents. The [bridge post](/notes/from-the-card-to-the-architecture/)
made the same observation about the card chapter's build lists. Two catalogs of what is missing keep enumerating the same architecture, and only one counts as evidence: the standards community's, written with no knowledge of this family. The card chapter's build lists are the same author teaching the same design, a rhyme rather than a witness.
# The nine use cases
The catalog's scenarios each land on a named pattern rather than a
new one. The personal assistant and the smart home are Missions with
[standing charters](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)
over consumer resources, and the
[category does not care that they are not enterprise](/notes/mission-based-authorization-field-reference/#mission-based-authorization-as-a-category).
The third-party SaaS proxy is the issuance core's home game. The OS
resources case is the harness's local-PEP slice, with the honest
remainder in the table above. Business process automation and the
coordinated task group are a parent Mission fanning out through
[Child Missions](/notes/mission-bound-authority/). The DNS
maintenance agent is the standing agent again, cycling authority under
a charter. Managed services across organizations are
[cross-domain projection](/notes/mission-bound-authority/#crossing-authorization-domains).
And automated incident response is
[Mission Management's](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html)
reason to exist: enumerate a compromised principal's active Missions
and bulk-revoke, dry-run first, with the kill switch reaching
issuance, permits, harnesses, and sub-agents.
# Three gaps worth a closer look
**Task-level revocation is the gap the Mission was born for.** The
catalog calls the lack of it "a major operational and security failure
point," and the diagnosis is exact: OAuth can revoke a token, and a
task is not a token. Revoking a task today means finding every token,
cached connection, and sub-agent that serves it, which is the
archaeology the handbook's whole first chapter dramatizes. The family's
answer is the object itself: the task has an identifier, revocation
targets it, and every projection (tokens, permits, sessions, children)
dies with it within a published bound. Where the catalog asks for an
API, the family answers with an object, because task-level revocation
is only coherent if the task exists.
**The paradigm mismatch is the admission model, seen from the other
side.** The catalog's first gap says agents need a "continuous
dialogue" of just-in-time permissions rather than upfront grants, and
read carelessly, that sounds like the runtime intent inference this
handbook rejects. Read carefully, it is the
[discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem)
specified as a requirement: start narrow, discover the need, ask
through a governed channel, and land the widening as a fresh
approval. The dialogue the catalog wants is real, and every turn of it
is an admission decision, never the agent granting itself scope
mid-flight. The difference between those two readings is the
difference between the category and a loophole.
**Scope explosion is a consent-grain problem, not a scope-count
problem.** The catalog's smart-home scenario generates thousands of
per-device scopes and an unusable consent screen, and the instinct is
to fix the screen. The family's answer moves the grain instead: the
human approves one Mission whose Authority Set the issuer derives, and
the thousand fine-grained entries live inside the derivation, rendered
as a legible disclosure rather than a scope list. The consent screen
failed because it asked a human to compile authority by hand. The
approval event succeeds because the compilation is the
[issuer's job](/notes/from-a-request-to-an-approved-mission/), and the human
judges the result.
# The honest remainders
- **The heterogeneous group grant is deliberately not native.** A
homogeneous swarm is answered natively, one class-pinned Mission with
instance-attributed execution. A group whose members need *different*
authority is a parent and its children, each attributable, because
collapsing differentiated members into one shared grant identity
would trade Law 2 for convenience. A deployment that truly needs
group-shared identity is asking for the thing the
[contractor post](/notes/the-contractor-gets-their-own-card/) warns
about, at fleet scale.
- **The OS bridge is real and unbuilt.** The harness mediates local
side effects, and nothing in the family translates a Mission into
OS-native entitlements or sandbox profiles. That is a substrate the
ecosystem has not standardized, and the family composes with
whatever emerges rather than pretending to supply it.
- **The catalog and the family are both individual drafts.** The
convergence argues that the problem statement and this solution
shape belong in the same venue conversation. It does not make either
one a standard, and the
[family's own maturity labels](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance)
say which answers are stable design and which are experimental.
The close is the same one every proof in this chapter reaches from a
different direction. The catalog asks what OAuth cannot say about an
agent's work: what it is, who approved it, whether it still stands,
and how to end it everywhere at once. Those are the
[six questions](/notes/mission-based-authorization-vendor-test/) and
the [five laws](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority)
in requirements clothing, and the fact that the standards community's
own gap analysis keeps arriving there is the strongest evidence yet
that the missing layer is not one proposal's opinion.
---
# Making Compliance a By-Product
Canonical URL: https://notes.karlmcguinness.com/notes/making-compliance-a-by-product/
Markdown URL: https://notes.karlmcguinness.com/notes/making-compliance-a-by-product.md
{{< tldr >}}
- **The framing.** Three governance regimes: [NIST AI RMF](https://www.nist.gov/itl/ai-risk-management-framework) and its four functions, the EU AI Act's high-risk obligations ([record-keeping](https://artificialintelligenceact.eu/article/12/), [human oversight](https://artificialintelligenceact.eu/article/14/), [deployer duties](https://artificialintelligenceact.eu/article/26/)), and [ISO/IEC 42001](https://www.iso.org/standard/81230.html), the certifiable AI management system standard. None of them ask whether your agent is safe. They ask you to show it.
- **The claim.** The evidence they demand is what the architecture emits in the course of enforcing: the Mission is the documented purpose, the approval is the accountable human decision, the evidence family is the log, and Termination is the interrupt. Run it for safety, and the audit artifacts fall out.
- **The boundary.** Evidence, not certification. Risk classification, impact assessments, retention policy, and conformity assessment stay with the deployment, and [the last section](#what-compliance-still-requires) names them.
- **What lives in this part.** [Eight obligations in one table](#the-crosswalk), [three obligations worth a closer look](#three-obligations-worth-a-closer-look), [the evidence is itself sensitive](#the-evidence-is-itself-sensitive) (the minimization posture for the trail the by-product creates), and [what compliance still requires](#what-compliance-still-requires).
- **The laws.** [Attribution and Durability](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) carry the records, Termination carries the interrupt.
- **Specs (editor's copies).** The [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html), [Consent Evidence](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html), and [Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html).
**Reading path.** ~12 minutes in order, or jump to [the crosswalk table](#the-crosswalk) for the whole mapping at a glance.
{{< /tldr >}}
# Overview
This chapter has held the handbook against a threat model, a
requirements framework, and a threat taxonomy. The last framing is the
one with auditors behind it. AI governance regimes differ in force,
NIST AI RMF is voluntary, the EU AI Act is law, ISO/IEC 42001 is a
certifiable management standard, but they converge on one demand: show
me. Show me who is accountable for this system. Show me what it is
for, and what its limits are. Show me how you observe it, how a human
intervenes, and how it stops. Show me the records, and show me they
have not been edited.
For most agent deployments the honest answer today is archaeology.
The purpose lives in a design document, the authority lives in OAuth
scopes that say nothing about purpose, the oversight lives in a
dashboard someone checks, and the record is a session transcript that
proves what the agent said, not what it was authorized to do. Each
obligation gets its own retrofitted artifact, and none of the
artifacts constrain the system they describe.
The claim of this part is that the handbook answers the show-me
question as a side effect of doing its actual job, because the
artifact that enforces is the artifact that documents. That is what
by-product means here, and it has a boundary worth stating before the
table: this part maps evidence, not compliance. Whether a given agent
falls under a given regime, and whether the organization around it
meets its process duties, are questions no architecture answers. One
more caution: the EU AI Act's application dates for the high-risk tier
have already been deferred once and may move again. The mapping below
anchors to the shape of the obligations, which has been stable through
the schedule changes: purpose, oversight, records, interrupt.
# The crosswalk
| The obligation | What the auditor asks | What the architecture emits |
| --- | --- | --- |
| Accountability structures (NIST AI RMF, Govern) | Who is responsible for this system's decisions? | The approver as a first-class `{iss, sub}` principal on every Mission, distinct from the subject, with `policy_version` naming the policy that derived the authority |
| Documented context and purpose (NIST AI RMF, Map) | What is this system for, and what are its limits? | The [Mission object](/notes/the-mission-is-the-missing-abstraction/) itself: goal, purpose, constraints, and expiry, where the documented purpose and the enforced purpose are the same artifact |
| Monitoring and measurement (NIST AI RMF, Measure) | How do you observe behavior against intent? | [Decision Evidence](/notes/the-agent-runtime-and-audit/) for every consequential action, denials included, joined on `mission_id` |
| Risk response and decommissioning (NIST AI RMF, Manage) | What happens when something is wrong? | [Revocation with a published freshness bound](/notes/mission-lifecycle-and-change/), and Termination reaching issuance, permits, the harness, and the sub-agents |
| Automatic record-keeping (EU AI Act, [Article 12](https://artificialintelligenceact.eu/article/12/)) | Does the system record its own operation over its lifetime? | The evidence family is generated by the protocol machinery, not by the agent: Consent, Decision, and Execution Evidence, with SCITT keeping the feed tamper-evident |
| Human oversight (EU AI Act, [Article 14](https://artificialintelligenceact.eu/article/14/)) | Can a human understand the system, intervene, and interrupt it? | Approval at Mission grain against a [committed disclosure](/notes/you-approve-what-you-were-shown/), the [discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem) for governed change, and a kill switch that actually halts the work |
| Deployer duties (EU AI Act, [Article 26](https://artificialintelligenceact.eu/article/26/)) | Did you assign oversight to competent people, and did you keep the logs? | The named approver makes the assignment legible in the record itself, and the evidence family is a log worth retaining. The retention period is the deployment's policy |
| Management-system operating evidence (ISO/IEC 42001) | Does your AI lifecycle control demonstrably operate? | The [Mission lifecycle](/notes/mission-lifecycle-and-change/) is a control that emits its own operating records: every state transition is evidence that the control ran |
Eight obligations, and not one row required machinery invented for
this part. That is the test of the by-product claim: a compliance
mapping that needs new artifacts is a compliance program, and a
mapping onto artifacts that exist for safety reasons is an
architecture paying a dividend.
# Three obligations worth a closer look
**Article 14's interrupt is a distributed systems problem, and the
card chapter already told the story.** The oversight article asks for
a human who can intervene or interrupt, including through a stop
control. Every vendor demo has a stop button. The card chapter's
fifth part, [Canceling the Card Doesn't Stop the Charges](/notes/canceling-the-card-doesnt-stop-the-charges/),
is about why most of them are theater: revoking a credential while
sessions keep running, caches keep serving, and sub-agents keep
working is an interrupt that interrupts nothing. Termination is the
handbook's answer in full: issuance stops, permits stop within a
published freshness bound, the harness halts the session, and the
delegation tree cascades. When the regulation says interrupt, the
architecture can state, with a number, how long interrupt takes.
**Article 12's logs are only evidence if they join.** A logging
obligation quietly assumes the logs can answer questions: who acted,
under whose authority, against what approval. A session transcript
cannot, because it records utterances, not authority. The handbook's
records are receipts rather than logs: Consent Evidence commits what
the approver saw, Decision Evidence commits what the gate decided and
why, Execution Evidence commits what happened, all joined on one
`mission_id`, with `policy_version` making the derivation re-checkable
and SCITT making the feed append-only. The practice chapter's line is
the compliance argument in one sentence: audit joins on one
identifier, or it is archaeology.
**NIST's Map function wants documented purpose, and documentation
drifts.** Every regime in the table asks for a statement of intended
purpose, and in a conventional stack that statement starts drifting
from behavior the day it is written, because nothing operational reads
it. The Mission collapses the gap: the document that states the
purpose is the object the PDP enforces, so purpose documentation
cannot drift from runtime behavior without the gate noticing the
difference. This is the by-product thesis in a single artifact, and it
is the reason the answer to Map is one table row instead of a
governance process.
The by-product, rendered. One surface carries this part's whole claim, with the audit log joined on the Mission's identifier:

An illustration, not a normative rendering: the artifact behind every
row is the evidence family, and what makes the view possible is the
join, not the dashboard.
# The evidence is itself sensitive
A system that records every approval, decision, and consequential action has built a second data estate, and the posture that keeps it defensible is minimization, not accumulation.
- **Collect the decision, not the content.** Record the action class, the resource, and the bound parameters a decision turned on, not full user content or model input, unless the content is itself the audited artifact. And no record needs the model's internal reasoning: the runtime profile explicitly excludes chain-of-thought from enforcement evidence: content that is high-sensitivity and adds no verification value beyond the decision inputs.
- **Commit digests, not values.** The family's own integrity pattern extends to its evidence. Where a record needs to prove what a value was, a digest or commitment serves and the raw value stays out of the durable trail, which is what `parameter_digest` already does at the decision layer.
- **Separate the streams.** Operational logs and audit-grade evidence have different jobs, audiences, and lifetimes. Verbose debugging data is not retained to the audit horizon and is not committed to a transparency log.
- **Classify at production time.** Evidence fields carry a sensitivity classification when the record is produced, so access, export, and retention key on the class rather than on a reviewer's later judgment.
- **Audit the audit.** Reading a Mission's evidence is a privileged operation, not a byproduct of holding a Mission reference, and evidence reads are recorded the way the actions they describe were recorded. Access to the audit trail is part of the audit trail.
- **Disclose selectively.** A party that needs to verify one fact gets a reference and a digest, not the record. The Mandate is this posture made portable.
- **Bound retention, and make deletion accountable.** Evidence is retained for the declared audit horizon and no longer, and when law or a data-subject request erases a record early, an erasure record makes the deletion itself auditable, so a gap in the trail is distinguishable from tampering.
The normative half lives in the drafts. The [audit profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html) carries these duties, and the [Mission Deployment Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) publishes the deployment's posture beside its guarantees: the field-classification scheme in use, whether evidence access is itself audited, the retention horizon, and the erasure policy. A reviewer reads those members the way they read the enforcement scope, as claims to check rather than virtues to assume.
# What compliance still requires
The by-product is the evidence. Everything else stays where it was.
- **Classification is judgment.** Whether an agent falls in a
high-risk category, or in scope for a regime at all, is a legal
determination the architecture does not make.
- **The process duties stay human.** Impact assessments, operator
training and competence, management review, and the org-chart
reality behind assigned oversight are organizational obligations.
The record makes the assignment legible. It does not make the
assignee competent.
- **Retention is policy.** SCITT makes the evidence feed
tamper-evident. How long the deployment keeps it, against Article
26's retention floor or its own, is a decision the deployment owns.
- **Certification is a process, not a property.** ISO/IEC 42001 is
audited and the EU AI Act's high-risk tier has conformity
assessment. An architecture can shorten the evidence-gathering to a
query. It cannot sit the audit for you.
- **The evidence covers what the gate covers.** A PEP that mediates
half the consequential actions produces records of half the
behavior. Enforcement scope bounds evidentiary scope, the same way
it bounds every other claim in this handbook.
You do not adopt mission-bound authorization to pass an audit. You
adopt it so that agents doing real work stay governed, and the audit
artifacts fall out, because an architecture whose control records are
its operating records has nothing separate to prepare. The auditor's
question turns out to be the [vendor test](/notes/mission-based-authorization-vendor-test/)
on different letterhead: who acted, under whose authority, within what bounds, and how can you prove it.
---
# Containing the OWASP Agentic Threats
Canonical URL: https://notes.karlmcguinness.com/notes/containing-the-owasp-agentic-threats/
Markdown URL: https://notes.karlmcguinness.com/notes/containing-the-owasp-agentic-threats.md
{{< tldr >}}
- **The framing.** The [OWASP Agentic AI Threats and Mitigations taxonomy](https://genai.owasp.org/resource/agentic-ai-threats-and-mitigations/), fifteen threats from memory poisoning to human manipulation, plus the [LLM Top 10](https://genai.owasp.org/llm-top-10/) and the 2026 [Agentic Top 10](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/) that distills the fifteen into the checklist reviews now open with.
- **The claim.** Every threat gets a verdict, not a hand-wave: **contained** (machinery built for it, draft behind it), **bounded** (cause untouched, blast radius capped at the action gate), or **delegated** (a named complement owns it). Six of fifteen contained, nine bounded, and half the LLM Top 10 delegated to the model and pipeline layers where it belongs.
- **What lives in this part.** [The fifteen agentic threats in one table](#the-agentic-threats-all-fifteen), [the LLM Top 10 split honestly](#the-llm-top-10-split-honestly), [three threats worth a closer look](#three-threats-worth-a-closer-look), and [what the crosswalk does not claim](#what-the-crosswalk-does-not-claim).
- **The laws.** [All five](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority), with Containment doing the heaviest lifting: manipulated agents, poisoned memory, and rogue delegates all fail at the same gate.
- **Specs (editor's copies).** [Runtime enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), the [AuthZEN profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html), [agent harnesses](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html), and [Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html).
**Reading path.** ~13 minutes in order, or jump to [the crosswalk table](#the-agentic-threats-all-fifteen) for every verdict at a glance.
{{< /tldr >}}
# Overview
The first two proofs in this chapter answered a threat model and a
requirements framework. This one answers the checklist. When a
security team reviews an agent deployment, the framing on the table is
usually OWASP's: the
[Agentic AI Threats and Mitigations taxonomy](https://genai.owasp.org/resource/agentic-ai-threats-and-mitigations/)
from the GenAI Security Project (the February 2025 fifteen-threat version, distilled in December 2025 into the [Top 10 for Agentic Applications for 2026](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/), mapped below), fifteen threats spanning single
agents, multi-agent systems, and the humans around them, alongside the
older [Top 10 for LLM Applications](https://genai.owasp.org/llm-top-10/).
If the handbook cannot state its position against that list, threat by
threat, the reviewer is right to treat it as unevaluated.
A crosswalk that claims everything has been fitted, not tested, so
this one grades itself with three verdicts and accepts the tally it
gets:
| Verdict | What it means |
| --- | --- |
| **Contained** | The threat lands on machinery built for it: a structural control with a draft behind it |
| **Bounded** | The cause is out of authorization's reach, but the blast radius is capped at the action gate |
| **Delegated** | Not an authorization problem: a named complement owns it, and the handbook composes with it |
The distinction that drives most rows is the one the
[trifecta post](/notes/splitting-the-lethal-trifecta/) established:
the authorization layer does not make the model resistant to anything.
It makes the model's compromise survivable, because authority was
fixed at approval and every consequential action is checked against it
fresh. Threats that attack authority are contained. Threats that
attack the model, its memory, or its inputs are bounded, because a
fully fooled agent still cannot out-argue a parameter check. Threats
that attack layers the handbook never claimed are delegated, by name.
And one definition keeps every verdict honest: contained means
contained at the authority boundary, never prevented at the cause. A
manipulated goal, a misused tool, or a rogue instance can still act
inside the committed authority, which is why scope stays tight and one line bounds the whole chapter: the gate checks a committed boundary, never an inferred intent.
# The agentic threats, all fifteen
| Threat | The attack | The handbook's answer | Verdict |
| --- | --- | --- | --- |
| T1 Memory Poisoning | Persistent memory is seeded with malicious data that steers future behavior | Poisoned memory can steer proposals, not authority: [shaping fails closed on ambiguity](/notes/from-a-request-to-an-approved-mission/), and every consequential action still needs a fresh parameter-bound permit against the approved Mission | Bounded |
| T2 Tool Misuse | The agent's own tools are driven to unauthorized or harmful invocations | [Per-action PEP/PDP enforcement](/notes/mission-bound-runtime-enforcement/) with parameters bound into the permit, applied at the tool boundary by the [MCP application post](/notes/least-privilege-mcp-tool-calls-need-a-mission/) | Contained |
| T3 Privilege Compromise | Permissions escalate beyond what the task requires | Narrowing: every derivation is a strict subset of the Mission's Authority Set, [Child Missions only narrow](/notes/mission-bound-authority/), and there is no ambient inheritance to escalate into | Contained |
| T4 Resource Overload | Unbounded agent activity exhausts compute, API, or financial budgets | `expires_at` on every Mission, fan-out bounded by count and depth, and [Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) (experimental) for spend | Bounded |
| T5 Cascading Hallucination Attacks | False content propagates through memory, messages, and downstream systems | Authorization does not verify truth. It gates consequence: hallucinated content can only reach the world through consequential actions, each needing its own permit | Bounded |
| T6 Intent Breaking and Goal Manipulation | Injection or instruction manipulation redirects the agent's objectives | The enemy sentence, answered: the injected goal cannot widen the committed one, because the PDP checks actions against the approved Mission, not against the agent's current intent | Contained |
| T7 Misaligned and Deceptive Behaviors | The agent acts against its intended purpose or conceals what it does | Deception is not detected, it is out-evidenced: only approved action classes execute, and [Decision and Execution Evidence](/notes/the-agent-runtime-and-audit/) come from the gate, not from the agent's self-report | Bounded |
| T8 Repudiation and Untraceability | Actions cannot be reliably attributed or audited | Attribution end to end: the `act` chain on every hop, the evidence family joined on `mission_id`, and SCITT keeping the feed tamper-evident | Contained |
| T9 Identity Spoofing and Impersonation | Attackers assume an agent's identity or abuse non-human authentication | [Attested instance identity](/notes/mission-bound-authority/), sender-constrained tokens, and mediated custody keeping the credential out of the agent entirely for mediated classes | Contained |
| T10 Overwhelming the Human in the Loop | Approval volume is weaponized until humans rubber-stamp | Human decisions stay rare and consequential by design: machines approve actions, [authorized policy approves what it may](/notes/from-a-request-to-an-approved-mission/#who-may-approve), and humans keep the ceilings and the class-guard classes, with [deferred approval](/notes/from-a-request-to-an-approved-mission/) and the batched [discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem) absorbing the volume | Bounded |
| T11 Unexpected RCE and Code Attacks | Tool invocations achieve code execution | Sandboxing owns execution. The handbook gates what executed code can reach: consequential effects still need permits, and capabilities are bound to source digests with drift failing closed | Bounded |
| T12 Agent Communication Poisoning | Malicious instructions ride inter-agent messages | Messages can lie, authority cannot: influence carries no authority between agents, because delegation only narrows and every hop is enforced against its own Child Mission | Bounded |
| T13 Rogue Agents | A compromised agent inside the system acts maliciously | A rogue instance holds only mission-bound, instance-bound, revocable authority, and [Termination cascades](/notes/mission-lifecycle-and-change/) through the delegation tree to issuance, permits, and the harness | Contained |
| T14 Human Attacks on Multi-Agent Systems | Operators are manipulated into enabling harm | Social engineering is out of authorization's reach. What holds: a manipulated operator can still only approve [what shaping renders](/notes/you-approve-what-you-were-shown/), and the approval is attributed to them | Bounded |
| T15 Human Manipulation | The agent deceives its own human into approving or enabling harm | Consent Evidence commits the disclosure as rendered, so the record shows what the trusted approval surface committed, never what the human perceived or understood. An accurately disclosed bad idea remains the human's decision, and the record says so | Bounded |
Six contained, nine bounded, none waved away. The tally is the honest
shape of an authorization layer: the threats that attack authority
(tools, privilege, goals, attribution, identity, rogue delegates) land
on machinery built for them, and the threats that attack the model or
the humans are capped rather than cured, because capping is what a
deterministic gate can truthfully offer.
# The 2026 Agentic Top 10, mapped
In December 2025 the same OWASP project distilled its agentic work
into the
[Top 10 for Agentic Applications for 2026](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/)
(ASI01 through ASI10), and that list is now the checklist most reviews
open with. It compresses cleanly onto the fifteen-threat crosswalk
above, so the verdicts carry over rather than multiply:
| Agentic Top 10 (2026) | Lands on | Verdict |
| --- | --- | --- |
| ASI01 Agent Goal Hijack | T6 intent breaking: the PDP checks actions against the approved Mission, not the agent's current goal | Contained |
| ASI02 Tool Misuse and Exploitation | T2: per-action PEP/PDP with parameter binding at the tool boundary | Contained |
| ASI03 Identity and Privilege Abuse | T3 and T9: strict-subset derivation, attested instances, sender constraint | Contained |
| ASI04 Agentic Supply Chain Vulnerabilities | The supply chain is another layer's job, and discovered tools still bind under [encounter adjudication](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem) | Delegated |
| ASI05 Unexpected Code Execution | T11: sandboxing owns execution, and consequential effects still need permits | Bounded |
| ASI06 Memory and Context Poisoning | T1: poisoned context steers proposals, never authority, and [least exposure](/notes/least-exposure-is-broader-than-least-privilege/) shrinks what can poison | Bounded |
| ASI07 Insecure Inter-Agent Communication | T12: messages can lie, authority cannot, since delegation only narrows and every hop re-authenticates, with transport the delegated layer | Bounded |
| ASI08 Cascading Failures | T4 and T5: expiry, fan-out bounds, metering, and cascade revocation cap the blast radius | Bounded |
| ASI09 Human-Agent Trust Exploitation | T14 and T15: disclosure integrity and the fatigue budget raise the bar, and a deceived human is still the residual | Bounded |
| ASI10 Rogue Agents | T13: instance-bound, mission-bound, revocable authority with cascade termination | Contained |
The tally holds the same shape: the authority-shaped risks land on
machinery built for them, and the risks that live in the model, the
supply chain, or the human stay bounded or delegated, stated rather
than absorbed.
# The LLM Top 10, split honestly
The [Top 10 for LLM Applications](https://genai.owasp.org/llm-top-10/)
predates the agentic taxonomy and mixes layers, which makes it the
better test of the delegated verdict. Half of it is not an
authorization problem, and saying so is the point:
| Entry | The handbook's answer | Verdict |
| --- | --- | --- |
| LLM01 Prompt Injection | [The trifecta post](/notes/splitting-the-lethal-trifecta/) carries this end to end: the injected instruction cannot widen committed authority, and the external leg needs a fresh parameter-bound permit | Bounded |
| LLM02 Sensitive Information Disclosure | [Exposure discipline](/notes/least-exposure-is-broader-than-least-privilege/): bound what the agent may see as deliberately as what it may do, and mediated custody keeps credentials out of the leakable set | Bounded |
| LLM03 Supply Chain | Model and dependency provenance, owned by the software supply chain. One authorization-shaped edge: capabilities bound to source digests fail closed on drift | Delegated |
| LLM04 Data and Model Poisoning | Training and embedding pipeline security, upstream of any authorization decision | Delegated |
| LLM05 Improper Output Handling | Output is only dangerous when it acts. The consequential boundary is where the handbook stands, and nothing crosses it without a permit | Bounded |
| LLM06 Excessive Agency | The direct hit: the entire handbook is the treatment for this entry | Contained |
| LLM07 System Prompt Leakage | Moot by design: authority lives in the Mission and its tokens, not in the prompt, so a leaked prompt discloses instructions, not power | Delegated |
| LLM08 Vector and Embedding Weaknesses | Retrieval pipeline security. What retrieval returns is untrusted content, and the taint response treats it that way | Delegated |
| LLM09 Misinformation | Content truth is semantic, and the gate is structural | Delegated |
| LLM10 Unbounded Consumption | Expiry on every Mission and metering (experimental) on spend | Bounded |
LLM06 deserves the sentence. OWASP's mitigations for Excessive Agency
read like this handbook's table of contents: minimize the extensions
and their permissions, avoid open-ended functions, require human
approval for high-impact actions, enforce authorization in downstream
systems rather than trusting the model, and log everything. The
handbook is what happens when that advice stops being a bullet list
and becomes one architecture with wire drafts behind it.
# Three threats worth a closer look
**Intent breaking is the taxonomy's center, and the handbook's
enemy.** T6 is prompt injection grown up: the attacker does not need
to breach anything, only to change what the agent is trying to do.
Every mitigation that asks the model to notice the manipulation is
probabilistic. The handbook's answer is the same one it gives the
trifecta: the injection changed the agent's mind, and the agent's mind
was never the source of authority. The Mission was committed at
approval, the permit is checked at execution, and between those two
points there is nothing the injected goal can rewrite. Approval is a
moment, work is a span, and the span is policed against the moment.
**Overwhelming the human in the loop is an argument about grain.** T10
is the taxonomy's sharpest design question, because both naive answers
lose: approve every action and fatigue turns humans into rubber
stamps, approve nothing and governance is gone. The handbook's grain
is the Mission. A human approves the envelope once, against a
[committed disclosure](/notes/you-approve-what-you-were-shown/), and
the per-action volume goes to the PDP, which does not tire. When the
work outgrows the envelope, the
[discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem)
batches the overflow into a governed expansion request instead of a
stream of interrupts. The residual is real and stated: at Mission
grain, approval fatigue becomes a governance discipline rather than a
solved problem, disclosure quality is what stands between an approver
and a reflex, and the
[fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget)
collects the controls that spend against it.
**A rogue agent is an insider, and every agent here is treated as
one.** T13 assumes the attacker is already inside the system, wearing
a legitimate agent. The handbook never trusted that agent more than
its paperwork: its authority is a strict subset of its parent's, its
tokens are bound to its attested instance so they do not travel, its
consequential actions need permits like everyone else's, and when it
is caught, [revocation cascades](/notes/mission-lifecycle-and-change/)
through the delegation tree it belongs to. The multi-agent threats
(T12 through T14) all get the same structural reply: cooperation
happens in messages, but authority never does.
# What the crosswalk does not claim
Three of this crosswalk's ceilings are [the trifecta post's honest residuals](/notes/splitting-the-lethal-trifecta/#the-honest-residuals), inherited unchanged. The verdicts are only as strong as PEP coverage, and an unmediated path is a threat with no verdict at all. The enforcement is structural, not semantic. And inside the approved scope, a turned agent is still turned. Two ceilings are this crosswalk's own.
- **Bounded is not prevented.** For every bounded row the cause is
untouched: the memory is still poisoned, the model is still fooled,
the human is still tired. The gate caps what the compromise can
reach, and that is the whole claim.
- **The delegated rows are real dependencies.** Supply chain,
poisoning, retrieval security, and sandboxing are layers the
handbook composes with and cannot replace. A deployment that skips
them has a contained authorization layer inside an uncontained
system.
The taxonomy's own mitigation columns keep converging on the same
words: least privilege, human approval, complete mediation, audit
trails. Those are adjectives until something turns them into
verifiable artifacts. The crosswalk above is what that looks like, and
the tally, six contained and nine bounded, is the score against a framing this model's authors did not choose. The [vendor test](/notes/mission-based-authorization-vendor-test/) carries its six questions from here.
---
# Answering the Laws of AIdentity
Canonical URL: https://notes.karlmcguinness.com/notes/answering-the-laws-of-aidentity/
Markdown URL: https://notes.karlmcguinness.com/notes/answering-the-laws-of-aidentity.md
{{< tldr >}}
- **The result.** [Patrick Parker's Seven Laws of AIdentity](https://www.linkedin.com/pulse/laws-aidentity-patrick-parker-zn9pe/) and Mission-Bound Authorization describe much of the same missing action-governance problem. The strongest matches are Generated Intent and Bounded Agency.
- **The qualification.** This is not a seven-for-seven compliance claim. Split-actor attribution, continuous authorization, least exposure, and proof depend on optional profiles or higher assurance levels. Justifiable Action Chains remains partial because the handbook verifies authorization, provenance, and integrity, but not whether every participant was necessary.
- **The architectural finding.** Authentication and credential issuance cannot carry this whole burden. Neither does Parker ask them to: his AIdentity framing is a metasystem that composes identity, authority, runtime controls, custody, and evidence. Mission-Bound Authorization is a candidate authorization layer inside that larger system, not a replacement for it.
- **The laws.** The yardstick throughout is [the five laws of delegated authority](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority), held beside Parker's seven.
- **What lives in this part.** [How to read the mapping](#how-to-read-the-crosswalk), [the seven-law crosswalk](#the-crosswalk), [what the convergence demonstrates](#what-the-convergence-demonstrates), [three close mappings](#three-mappings-worth-a-closer-look), [two sharpenings](#where-the-crosswalk-sharpens), and [the remaining gaps](#where-the-handbook-delegates-or-declines).
- **Specs (editor's copies).** The [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html), [runtime enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), [Consent Evidence](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html), and [Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html).
**Reading path.** ~13 minutes in order, or start with [the crosswalk](#the-crosswalk).
{{< /tldr >}}
# Overview
This part is the chapter's second proof, and the one framing built for identity rather than threats. Patrick Parker published [The Laws of AIdentity](https://www.linkedin.com/pulse/laws-aidentity-patrick-parker-zn9pe/) in May 2026 as a proposed framework for delegated agent action. The laws are not presented as an exhaustive checklist. Each names a dynamic whose violation produces a recognizable failure: confused agency, authorized-but-unintended action, authority laundering, time-of-check/time-of-use drift, cascade exfiltration, action-chain poisoning, or auditability collapse.
That makes the laws a useful test for this handbook. Mission-Bound Authorization starts from the [five laws of delegated authority](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) and asks how an approved task can govern authority across credentials, actions, actors, and time. Parker starts from the agent identity fabric and asks what a system must preserve when intent, authority, custody, execution, downstream identity, and proof no longer belong to one actor. The decompositions differ, but they meet around the same control problem.
The overlap is evidence of a real architectural seam. It is not proof that this draft family is the only answer, that the two frameworks were developed independently, or that every conforming Mission deployment satisfies all seven laws. Parker repeatedly describes systems that *begin* to satisfy a law. The handbook likewise has [assurance levels](/notes/adopting-mission-bound-authorization/#run-governed-and-beyond): Baseline Issuance does not make an action-time safety claim, while Runtime-Enforced, Governed Agent, and High-Assurance Agent add progressively stronger controls.
This crosswalk therefore asks three questions for each law:
1. Is the requirement represented directly in the Mission model?
2. Does satisfying it require a runtime control, identity substrate, optional profile, or higher assurance level?
3. What part remains a deployment responsibility or an explicit non-goal?
# How to read the crosswalk
The coverage labels are deliberately narrower than "answered."
- **Direct** means the requirement is native to the Mission object or issuance core.
- **Composed** means the answer exists only when Mission-Bound Authorization is combined with its identity, runtime, or evidence profiles.
- **Partial** means the architecture covers part of the law but leaves a material requirement unstandardized or out of scope.
That distinction matters because the core and the full reference security architecture make different claims. A `mission` claim on a token is governance metadata and a derivation gate. It becomes action-time control only when a PEP checks each consequential action against current Mission state.
# The crosswalk
| Parker's law | What it requires | Mission-Bound Authorization mapping | Coverage and caveat |
| --- | --- | --- | --- |
| **1. The Split Actor** | Preserve the principal, delegate, generator, authorizer, approver, credential holder, executor, and downstream identity as distinct roles | The Mission record separates `subject` and `approver`; the Actor and instance profiles identify the acting instance and delegation chain; Decision and Execution Evidence separate the PDP's decision from the PEP's action; mediated custody separates the agent from the key holder | **Composed.** Strong when the identity and runtime profiles are deployed. The issuance core alone does not record every role or the identity observed downstream |
| **2. Generated Intent** | Evaluate the concrete action that an agent generates after broader authority has been granted | The Authorization Server derives an approved Authority Set from the Mission Intent; [runtime enforcement](/notes/mission-bound-runtime-enforcement/) then checks each consequential action, its parameters, actor, audience, and current Mission state before execution | **Composed, strong at Runtime-Enforced.** Baseline Issuance does not satisfy the action-time requirement |
| **3. Bounded Agency** | Express purpose, resources, constraints, duration, budget, approval rules, and revocation; narrow authority through delegation | The [Mission](/notes/the-mission-is-the-missing-abstraction/) carries goal, purpose, resources, constraints, and `expires_at`; derivation is subset-only; lifecycle controls terminate authority; Child Missions narrow durable sub-work | **Direct for most bounds.** Cumulative budget enforcement depends on the experimental [Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) profile |
| **4. Continuous Authorization** | Govern discovery, invocation, execution, and outcome rather than authorizing only entry | The discovery loop turns newly encountered authority into a denial or governed expansion; the runtime gate checks invocation against fresh Mission state; PEP placement and mediated custody constrain execution; Execution Evidence records the result | **Composed and scope-dependent.** Outcome is recorded, not "authorized," and the guarantee reaches only the execution paths named in the deployment's enforcement-scope statement |
| **5. Least Exposure** | Minimize the data, context, tools, schemas, and credentials an agent can see or hold | Narrow, short-lived credentials reduce authority exposure; filtered discovery limits visible tools; mediated custody keeps usable credential keys inside a governed PEP; the handbook's [least-exposure discipline](/notes/least-exposure-is-broader-than-least-privilege/) scopes context to purpose | **Partial.** Mediated custody is High-Assurance. Retrieval, memory, and context scoping remain design guidance without a common wire format |
| **6. Justifiable Action Chains** | Prove the identity, provenance, integrity, and necessity of every participant in the action path | Actor chains and instance attestation establish who participated; source digests and `capability_drift` detect changed capabilities; strict-subset Child Missions preserve authority lineage; model and deployment provenance can be policy inputs | **Partial.** The handbook can prove that a participant was identified, authorized, and unchanged. It does not decide that the participant was necessary, and it has no physical actuator or sensor profile |
| **7. Proof-Carrying Action** | Produce signed evidence that binds authority, decision, delegation, execution, outcome, and tamper detection | Consent, Decision, and Execution Evidence join on the Mission and its integrity anchors; optional SCITT registration commits evidence digests to a verifiable transparency service | **Composed, with limits.** `policy_version` is a reference, not a policy snapshot. Transparency proves registration and integrity of registered records, not their truth or the completeness of the set |
The table shows substantial convergence, but not seven identical answers: one law maps directly to the core model, four require composed identity, runtime, or evidence profiles, and two retain explicit semantic or coverage gaps. That result is more useful than a perfect score because it tells an implementer what must actually be deployed.
# What the convergence demonstrates
The laws divide one apparently simple question into four system responsibilities:
| Question | Primary responsibility |
| --- | --- |
| Who acted, and for whom? | Agent identity, instance identity, and actor-chain profiles |
| Why did this authority exist, and what bounded it? | The Mission, approval record, Authority Set, and lifecycle |
| May this concrete action occur now? | The PEP/PDP runtime control loop and resource policy |
| What decision and outcome can later be proved? | Signed evidence, retention, reconciliation, and optional transparency |
No authentication mechanism can answer all four, but that observation should not be turned into a strawman about identity. Parker defines AIdentity broadly as a system of systems that composes identity, delegation, policy, credentials, action surfaces, human ceremonies, and proof. The better conclusion is narrower: **agent identity needs an action-governance layer, and Mission-Bound Authorization supplies a concrete candidate for its authority object and runtime contract.**
The mapping also tests the handbook's internal decomposition. The Mission object answers Bounded Agency but not Generated Intent by itself. Runtime enforcement answers action time but not chain attribution by itself. The identity profiles answer who acted but not why the work remained authorized. Evidence proves what a producer recorded but grants no authority. The layers compose because their responsibilities do not collapse into one another.
# Three mappings worth a closer look
**Generated intent is the deepest agreement.** Parker's second law identifies the category change: the concrete action may not exist when broader authority is granted. The handbook responds at two different moments. At admission, an accountable Approver decides on a structured Mission Intent and the Authority Set derived from it. At execution, a PEP asks a PDP whether the generated action and parameters fit that committed boundary under current state. The first check prevents the agent from inventing its own authority; the second prevents an approved Mission from becoming ambient permission for every action inside a token lifetime.
This is why the architecture cannot stop at a well-shaped prompt or a mission-bound token. Shaping proposes. Approval establishes the ceiling. Runtime enforcement decides whether a concrete draw against that ceiling is permitted now.
**Least exposure is broader than credential safety.** Parker includes prompt context, retrieved material, tools, schemas, secrets, and downstream responses in the exposure surface. The handbook reaches that same conclusion: what an agent can observe shapes what it can decide, even when access policy would later deny an action. Its strongest wire-level control is mediated custody. For a mediated class, the PEP, not the agent, holds the sender-constraint key and releases a governed outcome rather than a reusable credential.
But the match has a boundary. The runtime profile can enforce filtered discovery and mediated egress on paths it controls. The broader discipline of purpose-scoped retrieval, memory, and context assembly is not yet an interoperable protocol. Calling Law 5 fully answered would hide exactly the standardization gap the handbook's own least-exposure chapter names.
**Proof-carrying action requires several kinds of proof.** Decision Evidence records what the PDP evaluated and whether it permitted or denied the action. Execution Evidence is a separate PEP-produced record of whether a permitted action was attempted, completed, failed, or suppressed. The separation prevents a permit from being misread as proof that an action occurred. Both bind back to the Mission, and the integrity anchors commit the approved intent and authority.
Optional SCITT registration adds a different property. It commits a digest of a retained evidence record and produces a receipt that a verifier can check offline. It does not make a false record true, prove that every expected record was registered, or remove the need to retain the evidence and the policy material needed to interpret it. The receipt model is therefore strong, but its claims must stay precise: attributable records, verifiable inclusion, tamper detection, and deterministic joins, not omniscient audit.
# Where the crosswalk sharpens
**Authorize the action against what?** Law 2 requires action-time authorization. The handbook adds that the comparison target must be an approved, committed boundary rather than a fresh inference about what the user probably meant. Runtime context is attacker-influenceable. Semantic alignment can inform a decision, but it cannot widen authority. The enforceable comparison is the Authority Set an accountable Approver accepted, under the policy and current state that apply at execution.
**Separate the actor from the behavioral version.** Parker's Split Actor law distinguishes roles in an action chain. The handbook adds a different axis: the [Agent Deployment](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles), the versioned bundle of code, model, system prompt, tool allowlist, data scope, and runtime configuration. Instance identity answers *which running actor*. Deployment identity answers *which reviewed behavior was running*. Where a Mission uses the optional `controls.agent_deployment` pin, a replacement model or prompt is not silently treated as the same approved actor class.
That is not an eighth actor. It is a change-governance object with its own lifecycle and kill switch, kept separate from both agent identity and the Mission.
# Where the handbook delegates or declines
An honest crosswalk ends with the parts it does not solve.
- **Necessity remains a judgment.** The architecture can verify that a sub-agent or tool was identified, authorized, within a strict subset, and unchanged from an approved digest. It does not decide whether invoking that participant was necessary. That remains an orchestration, design-review, or policy question.
- **Exposure control is incomplete.** Credential custody, tool filtering, egress mediation, and the harness taint rule have enforceable surfaces. Purpose-scoped retrieval, memory, and context assembly do not yet have a common wire representation.
- **Model provenance is not model trustworthiness.** Attested model or deployment identifiers can be verified policy inputs. They do not prove that the model is safe, truthful, or suitable. The authorization layer deliberately bounds behavior instead of certifying the actor's judgment.
- **Policy references require retention.** `policy_version` identifies the derivation policy in force; it does not embed or hash the policy content and is not a promise that the decision can be reproduced later. A deployment that needs Parker's policy-snapshot property must retain the referenced policy, dependencies, and evaluation inputs.
- **Evidence completeness is not automatic.** Signatures protect individual records. SCITT can prove inclusion and ordering for records that were registered. Completeness requires an expected registration schedule, reconciliation rules, and, if equivocation is in the threat model, trust in one transparency service or registration with multiple independent services.
- **Embodied action is outside the current profiles.** Parker explicitly extends the laws to sensors, controllers, actuators, vehicles, and other cyber-physical systems. Mission-Bound Authorization can carry their authority vocabulary, but it does not yet define the safety-envelope, telemetry, or actuator bindings needed to claim coverage.
The literal answer to Parker's closing question is therefore a composition, not one token. **Who acted?** The subject, client or attested instance, actor chain, and runtime evidence identify the relevant roles. **Under whose authority?** The `mission` claim names the Mission Issuer and commits the approved Authority Set by hash; the Mission record carries the Approver and lifecycle. **Through what chain?** The `act` chain preserves actor lineage while Mission parentage preserves authority lineage, so delegation does not blur the two. **With what proof?** Signed Consent, Decision, and Execution Evidence join on the Mission and its hashes, with optional transparency receipts strengthening the retained record set.
That is a substantive answer to the authorization-heavy core of the Seven Laws. The crosswalk's strongest conclusion is not that one draft family has finished AIdentity. It is that Parker's framework and this handbook, starting from different questions, decompose agent governance into identity, bounded authority, action-time enforcement, custody, and proof. Any deployable system will need those responsibilities to compose without pretending they are the same thing.
---
# Splitting the Lethal Trifecta
Canonical URL: https://notes.karlmcguinness.com/notes/splitting-the-lethal-trifecta/
Markdown URL: https://notes.karlmcguinness.com/notes/splitting-the-lethal-trifecta.md
{{< tldr >}}
- **The threat model.** [The lethal trifecta](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/) is Simon Willison's name for the combination that makes agents dangerous: access to private data, exposure to untrusted content, and the ability to communicate externally, in one loop. Any two legs are survivable. All three mean anything the agent reads can steal what the agent knows.
- **The answer in one line.** The handbook cannot make the model injection-proof, so it does the structural thing instead: the three legs become separately typed action classes under one Mission, kept split at execution time, with the external leg unreachable without a fresh, parameter-bound decision.
- **What lives in this part.** [Why the trifecta is an authorization problem](#the-trifecta-is-an-authorization-problem), [what each leg gets](#what-each-leg-gets), [why splitting beats guardrails](#why-splitting-beats-guardrails), and [the honest residuals](#the-honest-residuals).
- **The card analogy.** The hypnotized cardholder and the ATM: the agent is a cardholder whose judgment can be rewritten by a shop window, and every unmediated path is cash. [The Network Approves Every Transaction](/notes/the-network-approves-every-transaction/) is this threat in card language.
- **The laws.** [Containment (Law 5)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) does the work: execution must continuously remain inside approved purpose. Termination stands behind it as the kill switch when containment catches something.
- **Specs (editor's copies).** [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), the [AuthZEN profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html), and [Mission-Aware Agent Harnesses](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html).
**Reading path.** ~8 minutes in order, or jump to [the honest residuals](#the-honest-residuals) if you already know the trifecta and want the limits.
{{< /tldr >}}
# Overview
In June 2025, [Simon Willison named the pattern](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/)
that the disclosures keep confirming: an agent that
combines access to private data, exposure to untrusted content, and
the ability to communicate externally can be turned against its
principal by anything it reads. No exploit code required. A poisoned
document instructs the agent to gather what it knows and send it out,
and the agent complies with its own legitimate credentials. Hold any
two legs and you are survivable. Hold all three in one loop and you
have built an exfiltration machine that is waiting for its
instructions.
The same month, researchers disclosed
[EchoLeak](https://thehackernews.com/2025/06/zero-click-ai-vulnerability-exposes.html)
(CVE-2025-32711), which demonstrated the whole chain against a
production system: a single crafted email, invisible to the user,
steered Microsoft 365 Copilot into exfiltrating whatever its retrieval
scope could reach, with no click required. All three legs, one loop,
exactly as named.
The handbook's running example carries all three legs on purpose.
Alice's agent reads Q3 financials (private data), works through source
documents to draft the packet (untrusted content), and notifies the
audit committee (external communication). That is not a contrived
worst case. It is what a useful agent task looks like, which is
Willison's real point: the trifecta is not an edge case to avoid but
the normal shape of valuable work. The question is not how to avoid
assembling it. It is how to hold it safely.
This part is the first proof in the validation chapter: run the
handbook against the threat model and see what holds.
[Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time)
carries the execution-time contract this part walks. Here the goal is
the whole story in one place, honest residuals included.
# The trifecta is an authorization problem
The first thing the handbook does to the trifecta is make it visible.
You cannot govern a combination you cannot see, and in most agent
stacks the three legs are invisible because every action looks the
same to the authorization layer: a valid token calling an API inside
its scopes. Reading the financials, reading the poisoned document, and
posting to the webhook are indistinguishable events.
The handbook types them. Under one Mission, private reads, untrusted
ingestion, and external writes are separately typed action classes,
separately evaluated, and separately auditable. The Authority Set says
which resources the agent may read and which destinations it may write
to, with the parameters bound into the entries. The moment the legs
are typed, the trifecta stops being an ambient property of the
deployment and becomes a stated fact about one task: this Mission
holds all three legs, these are the bounds on each, and here is the
approval that accepted that combination.
That reframing is the disagreement with the guardrail industry worth
stating plainly. The trifecta is not a prompt-engineering problem or a
model-alignment problem. It is an authorization problem: which
authority is in one loop at the same time, and what checks the
combination at the moment it matters.
# What each leg gets
**Private data gets exposure discipline.** The narrow Authority Set
bounds which private resources the task may touch at all, and
[Least Exposure Is Broader Than Least Privilege](/notes/least-exposure-is-broader-than-least-privilege/)
carries the deeper rule: bound what the agent may *see* as
deliberately as what it may do, because everything it sees can steer
it and everything it holds can leak.
**Untrusted content gets a taint response.** Shaping fails closed on
ambiguity so untrusted input cannot quietly widen a proposal, and the
[harness](/notes/the-agent-runtime-and-audit/) carries the
session-level mitigation Willison himself points toward: once
untrusted content has entered a session, egress authority is
downgraded for the remainder of it. That is deliberately coarse. It is
a bar-raiser, not information-flow control, and the residuals section
says so.
**External communication gets the full weight of enforcement.** The
external leg is where the theft completes, so it carries the strictest
class in the [runtime contract](/notes/mission-bound-runtime-enforcement/).
Every external write is a consequential action needing a fresh permit
from the PDP, evaluated against current Mission state, with the
parameters bound: in the running example, `notify_reviewer` is bound
to the `audit-committee` group, so the poisoned document that says
"also send the numbers to this address" produces a
`parameter_violation` denial, not a delivery. Under mediated custody
the agent never holds the egress credential at all: the PEP holds the
sender-constraint key, so a fully compromised agent cannot present the
credential on an unmediated path it does not have. And for the classes
where the content itself is the harm, action-bound approval puts a
human between the injected instruction and the send.
# Why splitting beats guardrails
Everything above shares one property: none of it requires the model to
resist the injection. The poisoned document can convince the agent
completely, and the agent can walk to the register as itself, and the
transaction still fails, because the authority the gate checks against
was fixed at approval time and the injected instruction cannot widen
it. A guardrail asks the model to recognize manipulation. The split
asks the PDP to check a parameter against a committed bound, which is
a question with a deterministic answer.
And when containment catches something, Termination finishes the job.
A `parameter_violation` denial is decision evidence joined to the
`mission_id`, the incident playbook revokes the Mission, and the kill
switch reaches every projection: issuance stops, permits stop, the
harness halts the session, and the sub-agents cascade. The trifecta
does not have to be defeated forever. It has to be caught once, and
end everywhere.
# The honest residuals
The handbook's own [adversary model](/notes/mission-based-authorization-field-reference/#adversary-model)
carries these in writing, and this part repeats them because a
validation that hides its residuals is marketing.
- **The defense is exactly as strong as PEP coverage.** Every channel
the runtime offers is a potential fourth leg: DNS, logs, error
strings, a file another process reads. The deployment must name its
enforcement scope and its unmediated paths. In card language, count
your ATMs.
- **There is no information-flow control.** Each action is evaluated
in isolation, so a sequence of individually permitted actions can
compose into an exfiltration no single check catches. The harness's
taint downgrade raises the bar without closing this.
- **The enforcement is structural, not semantic.** An in-bounds send
to an approved recipient can still carry content it should not. The
classes where content is the harm belong under action-bound human
approval, not under a smarter policy engine.
- **Inside the approved scope, a turned agent is still turned.** The
Mission bounds the blast radius. It does not shrink it to zero,
which is why scope stays tight and expiry stays short.
Willison's conclusion was that no reliable prompt-level defense
exists, and the handbook agrees from the other direction: the fix is
not a smarter model but an authority architecture in which the model's
compromise is survivable, the
[survivable incorrectness](/series/mission-shaping/) the Mission
Shaping series set as the operating goal. Two legs safe was always the advice. The
Mission is what turns that advice from a hope about deployments into
a property the gate enforces, one typed action at a time.
---
# The Agent Runtime and Audit
Canonical URL: https://notes.karlmcguinness.com/notes/the-agent-runtime-and-audit/
Markdown URL: https://notes.karlmcguinness.com/notes/the-agent-runtime-and-audit.md
{{< tldr >}}
- **The question.** The first five layers make a Mission approvable, enforceable, governable, and delegable. None of that holds up automatically inside a real agent: a session that outlives the Mission, a workflow caught mid-step when the Mission stops, and signed records that only the issuer can vouch for. This is the operational layer that makes the guarantees survive contact with a long-running, multi-step, possibly-compromised agent.
- **What lives in this part.** [Sessions are not authority](#sessions-are-not-authority) (the harness binds continuity state to Mission state and re-checks it), [unwinding work in flight](#unwinding-work-in-flight) (the orchestrator assigns a reversibility class, records an unwind plan, and unwinds safely when a Mission stops), [the containment matrix](#the-containment-matrix) (what each of the six kills stops, and who owns it), and [evidence anyone can verify](#evidence-anyone-can-verify) (registering the suite's evidence into a SCITT Transparency Service so it is tamper-evident across domains).
- **The card analogy.** The expense system stops the recurring charges when the card is cancelled. [Canceling the Card Doesn't Stop the Charges](/notes/canceling-the-card-doesnt-stop-the-charges/) is this part in card terms. ([Where the analogy breaks](/notes/canceling-the-card-doesnt-stop-the-charges/#where-the-analogy-breaks).)
- **The laws.** This part carries [Durability, Attribution, and Termination (Laws 1, 2, and 4)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) into the runtime: continuity stays conditioned on the task, audit keeps every action attributable, and revocation reaches the harness, not just the tokens. (Termination is the card chapter's rule 5, ending the credential is not ending the arrangement.)
- **Specs (editor's copies).** [Mission-Aware Agent Harnesses](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html), [Mission Orchestration and Unwinding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-orchestration.html), [Mission Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html).
**Reading path.** ~20 minutes start to finish. Read in order, or jump to the section that matches your gap: [resume](#sessions-are-not-authority), [unwinding](#unwinding-work-in-flight), or [evidence](#evidence-anyone-can-verify).
{{< /tldr >}}
# Overview
The layers before this one build the Mission as a governance object. It is approved with integrity ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)), bound to instances and delegated under a strict subset ([Mission-Bound Authority](/notes/mission-bound-authority/)), enforced per action ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)), and observed and revoked over its lifecycle ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)). Each of those guarantees is stated as a contract the Authorization Server, a Policy Decision Point, or a token holder upholds.
A running agent does not honor contracts for free. It is a harness that checkpoints state and resumes after restarts, an orchestrator that sequences many steps over hours, and a producer of signed records that, on their own, only its own auditor can fully trust. Each of those is a place where a stated guarantee can quietly stop holding: the session resumes after the Mission was revoked, the workflow is halfway through a wire transfer when the Mission is suspended, and a signed log can be backdated by whoever holds the signing key.
This part specifies the operational layer that closes those three gaps. It does not introduce a new authority object. It makes the existing one survive a real, long-running, multi-step, possibly-compromised agent. Three drafts answer three questions:
- **Sessions are not authority.** When an agent recovers a session, what stops it from acting on authority that has since been revoked?
- **Unwinding work in flight.** A Mission can stop after some steps committed and before others run. What happens to the work already moving?
- **Evidence anyone can verify.** The suite produces signed evidence at every step, but *signed* is not *tamper-evident*. How does a party in another trust domain confirm nothing was dropped or backdated?
All three are OPTIONAL companions to the issuance core, at different maturity, and optional at the protocol layer does not mean optional for the claim: the harness is required for the Governed Agent claim, and orchestration and audit are required by the claims that use them. The harness is part of the *Governed Agent* level's set. Orchestration is a newer, experimental profile the level grows into as needed. Audit layers onto any level. Together they are what makes the difference between a Mission that is governable in principle and one that is governed in a system that is actually running.
| The control at a glance | |
| --- | --- |
| **Minimum useful version** | The harness re-checks Mission state before any resume and stops work when the Mission is not `active` |
| **What it prevents** | A recovered session, queue, or cached connection acting on authority that no longer exists |
| **What it does not prevent** | Side effects on paths the harness does not mediate, and a dishonest producer writing false records. Transparency makes records permanent and attributable, not true |
| **Operational owner** | The agent platform team owns the harness and the orchestrator |
| **Evidence emitted** | Stop and resume decisions, unwind plans and outcomes, and SCITT registrations where audit transparency is adopted |
| **Maturity** | The harness is recommended for agents. Orchestration is experimental. Audit Transparency is advanced |
# Sessions are not authority
> Session continuity is not authority.
[The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) drew the line. A session can prove the runtime survived a restart. It cannot prove the Mission survived. The separate note [Sessions Are Not Missions](/notes/sessions-are-not-missions/) made the argument in full. The [harness profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html) turns it into a requirement.
An agent harness exists to make work durable. It checkpoints conversation and planning state, persists a task graph, queues background jobs, caches tool connections and OAuth tokens, and tracks sub-agent handles, so the agent can survive a process restart, a device handoff, a scheduled wake-up, a retry, or an asynchronous tool response. Every one of those is a *continuation point*, a moment where the harness resumes work it saved earlier. And every one of them is a moment where the authority that justified the work may have ended while the runtime state that carries it did not. A task graph can survive a revoked Mission. A warm connection to a tool server can outlive the condition that authorized it. A child agent can keep running after its parent Mission is suspended. One continuation is deliberate: when a Mission is superseded by an approved successor, the harness rebinds the running session to the successor rather than restarting, so governed growth keeps the runtime it already has.
The design move is a single one applied everywhere. The harness binds every governed session and task-graph node to a **Mission binding**: the Mission's `id` and `issuer`, the `authority_hash` when known, the last-known state and its source, freshness information (when status was last checked and when that check expires), and, for governed work, a required stop policy. The binding grants nothing. It is the pointer that tells the harness *which* Mission state it must re-establish before it continues. The rule that follows:
> Before resuming governed work, the harness MUST establish that the Mission is `active` within the deployment's staleness bound, even when the OAuth credentials in the session are still valid.
It establishes that state through one of the lifecycle surfaces from [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/): a Mission Status query, an event-driven state cache fed by Lifecycle Signals, a runtime PDP decision that itself checks current state, or another deployment-defined source with equivalent freshness semantics. If it cannot establish `active` state, it does not resume. If the state is anything else (`revoked`, `expired`, `suspended`, `completed`, `superseded`, or a state a newer profile added that this harness has never heard of), it stops. That last case matters. The harness inherits the suite's forward-compatibility rule, treating *any* state other than `active` as non-active, so an unknown state fails safe rather than slipping through.
"Stop" is not a single action. The harness picks from four stop behaviors based on the Mission state and the action class:
- **`suppress`:** do not dispatch the queued or resumable work. Preserve it for audit or operator review.
- **`pause`:** hold the work pending an authorized lifecycle transition such as a resume.
- **`terminate`:** end the task graph and release its runtime resources.
- **`handoff`:** escalate to a human or governance workflow without taking any further governed action.
A revoked or completed Mission means suppress or terminate. A suspended one means pause or suppress. An unknown or stale state means suppress or pause. For [the class-guard classes](/notes/from-a-request-to-an-approved-mission/#who-may-approve), `handoff` or orchestration handling (the next section) is preferred over a blunt stop. And a handoff is not open-ended. The review outcome is `approve`, `reject`, or `expire`, a parked item carries a maximum age, and approved work re-enters the resume algorithm, because review approval is not itself a Mission-state check.
The same discipline extends to the things harnesses quietly reuse. A cached credential or tool connection is not evidence the Mission is still active. Before reusing one for governed work, the harness re-checks the Mission and the action. Connections are keyed by Mission, authority hash, audience, actor, and sender-constraint key, so a connection opened for one Mission never becomes ambient authority for another. (The one sanctioned reuse is a connection that carries no authority at all, where every consequential use is separately authorized under the target Mission.) *A warm connection to a tool server is not a permit to call a tool.* One execution context serves one Mission at a time for consequential work: an instance may be reused, a context may not, because content read under one Mission steering actions under another is the flow-level residual running inside the model's window, and where multiplexing is unavoidable it is declared and caps the exposure classes the context may hold. And a sub-agent never inherits authority by descending from a parent session. It gets an explicit child-Mission or delegation binding ([Mission-Bound Authority](/notes/mission-bound-authority/)), and when the parent Mission goes non-active the harness propagates the stop to dependent children.
A concrete moment, on the handbook's running example. An agent preparing the Q3 board packet runs as a long-running overnight job, so its session outlives the user's attention. At 02:00 it resumes a queued task graph to continue the packet draft. The board meeting was cancelled and the Mission revoked at 23:00. The harness re-reads the Mission state before dispatching anything, finds `revoked`, declines to dispatch, marks the cached `docs.example.com` connection unusable, and emits evidence:
```json
{
"event_id": "hrn_7pQ4mN9s",
"event_type": "resume_suppressed",
"mission": {
"id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"issuer": "https://as.example.com",
"authority_hash": "sha-256:4hRwrGkW9Jdjbkj1oHJ3opg9HRvmRe30k7TQmUfiIpY"
},
"session_id": "sess_agent_42",
"work_item": "draft_board_packet_section_3",
"state": "revoked",
"state_source": "signal",
"decision": "suppress",
"reason": "mission_not_active",
"occurred_at": "2026-10-01T02:00:00Z"
}
```
The session was fully recoverable. The authority that justified it was gone. The harness let the Mission, not the session, decide.
## The mediated execution environment
The harness profile carries one requirement that does not look like continuity management but is the structural complement to [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/). Its high-assurance level puts the sender-constraint key in the enforcement point's custody, not the agent's, so a compromised agent cannot act off the enforced path. That guarantee is empty if the agent has an *off-path route* to the resource in the first place (a debug shell, a direct socket, an unsanctioned egress, a connector that skips the gate). Custody is moot if the agent can reach the resource around the PEP.
Establishing the closed environment is the harness's job. The draft names this **Mission Mediation**:
> For the action classes a deployment mediates, the harness MUST run governed consequential work in an execution environment whose only path to those actions is through the mediating PEP, and MUST NOT leave an unmediated route to a mediated class.
This is where the harness and the runtime profile meet. The runtime layer holds the key. The harness guarantees there is no door without a lock, and the key must not live behind the same door. A deployment claiming compromise-resistant enforcement isolates the mediating PEP and its key custody from the agent-facing harness components, by process, host, or service separation, because a harness that both faces the agent and holds the sender-constraint key defeats mediated custody the moment it is compromised. The claim is also published, not implied. The harness publishes an execution-environment scope statement naming, for each mediated class, the isolation mechanism that confines governed work and the unmediated paths excluded from the claim. Verifying that no unmediated path exists is a deployment audit obligation, not a protocol property. A harness that cannot guarantee a closed environment for an action class MUST NOT represent work in that class as runtime-enforced, the same enforcement-scope honesty the runtime profile requires.
The largest off-path route deserves its own paragraph, because for agents it is not an edge case: the code-execution tool. The moment a Mission grants an interpreter, a shell, or a notebook, the agent can write its own client, and a script-level database call does not pass the API-level PEP on its way out. You do not make generated code safe by reading it, and the answer is not a PDP that inspects the script before it runs, which would be the enforcement-time intent inference this handbook rejects. The answer is what the generated code runs *inside*. Mediated custody keeps credentials out of the sandbox, so the script holds nothing a database would accept. The harness and the egress proxy own the environment's exits, so effects leave only through mediated paths. And the execution-environment scope statement declares the sandbox and its exclusions, so the claim is checkable rather than assumed. Sandboxing is the price of the interpreter, the same boundary [the OWASP part's T11 verdict](/notes/containing-the-owasp-agentic-threats/) prices, and a deployment unwilling to pay it must exclude code execution from its enforcement scope in writing rather than let the API PEP imply coverage it does not have.
The harness profile adds one more control that no per-action check can: **taint tracking**. The runtime PDP evaluates each action against the undertaking's committed state, but each action still arrives as one decision: the PDP never sees the session as a whole, the flow of content between calls. The harness does. Once attacker-influenceable content (a fetched document, a tool result, an inbound message) enters a governed session, the session is *tainted*. The trigger is parameter provenance where the harness can trace it at the data plane, with session-level taint as the fallback, and before a consequential egress that derives from tainted content the harness should require a fresh action-bound approval or downgrade that authority. This is plan-then-execute. Untrusted content may inform the agent's planning, but it must not, on its own, drive an egress the user never directed. The polarity is fixed in the tainted direction: in a tainted session, a bound parameter the harness cannot affirmatively trace to a trusted source is treated as tainted, so content the agent paraphrases rather than copies never sheds its taint. (A deployment may route the taint determination through the PDP instead, where the decision binding carries it, and the PDP fails closed when the context is missing.) The draft is candid that this is a coarse control, not information-flow control. It cannot close within-scope data laundering, but it forces a human or a fresh approval between injected input and an external side effect. [Least Exposure Is Broader Than Least Privilege](/notes/least-exposure-is-broader-than-least-privilege/) makes the general case: the exposure surface, not just the action surface, is what a Mission should bound.
A harness check never replaces the PEP at the last controllable boundary. The two compose. The harness decides whether a unit of work may continue at all. The runtime layer still gates every consequential action.
# Unwinding work in flight
> Mission runtime enforcement can refuse the *next* action. It cannot un-send the *last* one.
The runtime layer is a gate in front of each action, so revoking a Mission reliably stops everything that has not happened yet. But a long-running workflow is a sequence, and a Mission can stop in the middle of it. After a journal entry posted, before the reviewer was notified. After a payment was dispatched, before anyone can prove it cleared. Denying the next step does nothing about the steps already in motion. *Mission termination is not just an access-control denial. It is a question about execution state.* The [orchestration profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-orchestration.html) answers it.
The move is to plan the unwinding *before* the work runs, not to improvise it after a Mission stops. Two pieces make that possible.
First, every consequential step is assigned a **reversibility class** before it executes:
- **`read_only`:** observes data, no external effect. Suppression prevents future exposure, but prior disclosure cannot be undone.
- **`reversible_write`:** changes state and has a known compensation that can restore or materially offset it.
- **`irreversible_action`:** cannot be reliably undone once completed.
- **`external_commitment`:** commits to an external party: a sent message, a started payment, a signature, a filing, an order.
- **`privileged_administration`:** changes policy, access, configuration, or security posture.
The last three are deliberately the same-named action classes from [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/). `read_only` and `reversible_write` are the finer distinction this profile adds for the sake of deciding what can be compensated. A step's reversibility class must be consistent with, and no lower than, the runtime action class for the same operation, and the orchestrator MUST NOT lower it at runtime to dodge review. That is the same can't-talk-yourself-down floor the runtime layer enforces, and for the same reason. The class comes from a trusted source (resource policy, an operation profile, a reviewed workflow definition). An agent's plan, model output, or tool description may *suggest* a class, but it is never the sole authority for *lowering* one.
Second, before dispatching a consequential step, the orchestrator records an **unwind plan** for it. The plan states what happens to the step at each phase of trouble: what to do if the Mission is already non-active before the step starts, what to do if it stops while the step is in flight, and, for everything but a pure read, what to do after the step has completed. A worked plan for the riskiest kind of step:
```json
{
"step_id": "notify_reviewer_audit_committee",
"mission_id": "msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1",
"reversibility": "external_commitment",
"pre_start_behavior": "human_review",
"in_flight_behavior": "wait_then_review",
"post_completion_behavior": "human_review",
"review_queue": "audit-committee-review",
"evidence_policy": { "link_runtime_evidence": true, "retain_for": "mission_audit_horizon" }
}
```
The plan is also committed, not just recorded. The step's first Orchestration Evidence record carries an `unwind_plan_hash` over the plan, so an auditor can detect any later alteration of the plan a step ran under. And every member of the plan (the compensation action, the review queue, the safe point) derives from a trusted source. Model output MUST NOT define them.
When the orchestrator learns a Mission is no longer `active` (from a status poll, a signal, a PDP denial, a harness stop, or an operator), it stops dispatching new steps, suppresses queued work, evaluates every in-flight step, and runs the post-completion behavior for completed steps whose plan requires it. The hard part is the in-flight step, and the profile forces the honest distinction:
- **`not_dispatched`:** not yet at the PEP or external system. Suppress or pause it.
- **`dispatched_not_committed`:** sent, but still cancellable. Attempt cancellation if the plan says so.
- **`committed`:** the effect occurred, or the system cannot prove it did not. Apply post-completion behavior.
- **`unknown`:** the orchestrator cannot tell whether it committed. Treat it as requiring human review.
That `unknown` row is the point. Distributed systems frequently cannot prove whether an in-flight action committed, and treating "I don't know" as "it didn't happen" is how audit gaps form. For an irreversible action, an external commitment, or a privileged change, an unknown outcome must not be quietly filed as success or as harmless suppression. It goes to review. Cancellation acceptance by an upstream queue is not proof the external action did not occur.
When a completed step does need to be walked back, that **compensation is itself governed work**. A refund, a reversing entry, a cancellation message. Each can be as consequential as the action it offsets. So the profile refuses to let Mission termination become a backdoor to unauthorized remedial action. Compensation under a Mission that has already gone non-active requires one of exactly two documented authority bases: the resource's own policy (a remediation the resource authorizes for itself, such as a local rollback) or a narrow, pre-provisioned remedial Mission that is still `active`. An operator may gate either basis, but operator approval is not itself an authority basis. It does not create authority a PEP would enforce over a terminated Mission. If neither basis applies, the orchestrator does not compensate. It hands off to a human.
Like the harness, the orchestrator does not replace the PEP. A runtime permit obtained just before revocation might still arrive at the PEP afterward. The orchestrator stops dispatching such work, and the PEP still enforces its own permit freshness and Mission-state check. The harness decides whether a unit of work may continue. The orchestrator decides how in-flight workflow state is unwound. The runtime layer gates each action. Three checks, three questions, none subsuming the others.
# The containment matrix
Unwinding assumed the stop signal was the Mission's. In an incident it usually is not, and Mission termination is one control in a larger containment surface. Each kill has a different blast radius and a different owner, and an incident responder needs the whole matrix:
| Kill | Blast radius | Who owns it |
| --- | --- | --- |
| **Mission kill** | One body of work, across every resource and derived credential, cascading to Child Missions | The Mission Issuer |
| **Agent kill** | All work by one agent, across its Missions | The deployment's agent IAM |
| **Agent Deployment kill** | Every instance running a compromised behavioral version | The deployment's change governance |
| **Credential kill** | Credentials already issued, where the substrate supports revocation, otherwise their expiry | The credential issuer |
| **Workload kill** | The running compute itself | The platform |
| **Egress kill** | The communication path | Gateway and network controls |
Mission termination participates in incident response. It does not replace it. Revoking the Mission stops issuance at once and stops mediated actions within the published staleness bound, but it terminates no process and closes no network path. The converse holds too: killing a workload leaves the Mission `active`, its authority derivable to a replacement instance, unless the Mission is also revoked. A deployment's incident runbook names which of these controls exist and who may pull each, and the first two rows belong to [different objects with different owners](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles), which is exactly why the runbook has to name both.
# Evidence anyone can verify
> Every layer of this suite signs its evidence. *Signed* is not the same as *tamper-evident*.
By the time a Mission completes, the suite has produced a trail: the approval event, every lifecycle transition, the consent rendering the user saw ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)), the per-action decision and execution evidence ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)), and the harness and orchestration evidence from this part. Each record is signed, which makes it attributable and tamper-evident *in isolation*.
It does not make the record *set* trustworthy. Whoever holds the signing key can backdate a record, drop an inconvenient one, or show different histories to different auditors. And nothing a relying party holds detects it. A compliance auditor in another company is worse off still. They have only the issuer's word that its own logs are complete. A bare signature over a narrowed token cannot give a cross-domain party offline confidence that nothing was reordered or omitted.
The [audit profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html) closes that gap by registering Mission evidence into a **SCITT Transparency Service**. The transparency substrate is not speculative. The SCITT architecture is ratified as [RFC 9943](https://www.rfc-editor.org/rfc/rfc9943), so this profile builds on a published standard. A producer (the Authorization Server, a PDP, a harness) registers a record as a *Signed Statement*. The service appends it to a verifiable, non-equivocating log and returns a *Receipt* that proves inclusion. The Signed Statement plus its Receipt is a *Transparent Statement* that any party can verify offline. This record was registered, at a committed time, in a log that cannot later drop or reorder it.
Two design choices make it practical, and they are the ones worth carrying out of this section:
**The Mission is the statement subject.** Every Signed Statement about a Mission uses the same `sub`, a fixed URI built from the Mission's issuer and id (`/missions/`). Because the construction is fixed, independent producers in different domains all compute the same subject, and they share *one feed* when they register with the same Transparency Service, which cross-domain deployments should. An auditor who enumerates that subject (the deployment supplies the enumeration, since the substrate defines no feed query) gets the records that were registered, in order, append-only (approval, consent, decisions, lifecycle), as a single narrative under `sub = https://as.example.com/missions/msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1`:
| # | Record | Issuer | Time | Note |
| --- | --- | --- | --- | --- |
| 1 | approval-event | `as.example.com` | t0 | |
| 2 | consent-evidence | `as.example.com` | t0 | |
| 3 | decision: permit | `pdp.example.com` | t0+5h | Financials read |
| 4 | decision: deny | `pdp.example.com` | t0+5h | CRM expansion |
| 5 | lifecycle: revoked | `as.example.com` | t0+6h | |
Two caveats keep that honest. Transparency proves what *was* registered, not that everything was, so completeness is only checkable against an expected schedule. A deployment registers on a predictable cadence so an omission stands out as a gap. And "see it identically" holds against equivocation only if you either trust the single Transparency Service not to show different auditors different histories, or register with multiple independent services.
**Statements commit by hash, never in the clear.** The Signed Statement carries a COSE hash envelope whose payload is the digest of the retained evidence record as issued, not the record itself. A transparency log is append-only and may be widely readable. Nothing in it can be redacted later. So the sensitive task data stays out of the log entirely, retained separately under access control, and a relying party retrieves it and checks it against the committed digest. (Low-entropy records are committed with a retained random salt: a lifecycle transition is guessable, and an unsalted digest of a guessable record can be confirmed by dictionary.) The log proves a specific record was registered at a time. The content is verified out of band. Even the committed metadata leaks, though. The `sub` construction is fixed and does not expose the Subject directly, but it is a durable per-Mission correlator whose existence and registration cadence are observable in a shared log, so a deployment should weigh whether a Mission's mere existence and timing are sensitive before registering in a widely readable service, and what its `issuer` and `id` reveal.
That split also sharpens what verification means. The profile separates an **integrity failure** (a bad signature, a bad Receipt, a broken inclusion proof, or a hash that does not match the retrieved evidence) from an **audit failure**, where the evidence, or a producer or service trust anchor, cannot be retrieved or resolved in the access window. An integrity failure is rejected. Among its causes, only the hash mismatch means the retained record itself was altered. The others mean the statement or the log cannot be trusted. An audit failure means you have proven the record was registered and not reordered but cannot confirm its content, which is *not* proof of tampering, and must not be treated as such.
The join key deserves one clarification, because every distributed estate already has correlation IDs. A request ID and a Mission ID connect different things. A request ID connects one trace, the hops of a single call as it crosses services, and it dies with the request. The Mission ID connects the body of work: every session, retry, agent, resource server, Child Mission, and lifecycle change the approved task produced, across days and across domains. Tracing tells you what happened to a request. Evidence joined on the Mission tells you what happened to the work, and whether it was still authorized while it happened. That property is verifiable continuity: the approval, the decisions including denials, and the executions shown to belong to one undertaking, not merely threaded by a correlator.
Six questions recur about the evidence layer, and the whole suite answers them in one place:
| Question | The answer |
| --- | --- |
| Where is evidence stored? | At its producer: the Issuer's records, the PDP's evidence store, the harness's session records. SCITT registration adds a verifiable, append-only feed without moving anything |
| Who signs it? | The producer: Consent Evidence by the Authorization Server, Decision and Execution Evidence by the decision layer, session and unwind records by the harness, each a Signed Statement where registered |
| Can it be replayed? | The committed disclosure can be re-rendered and checked against `consent_rendering_hash`, and a Transparent Statement verifies offline against the anchors |
| Can it be aggregated? | Yes, deterministically: every record joins on `mission.id` and `mission.issuer`, one history per undertaking, which is what makes the continuity verifiable |
| Can it feed an approval? | As input, yes: Shaping Evidence and interrogation answers inform the Approver, and Decision Evidence gives a requestable denial its context |
| Can it issue authority? | Never. Evidence proves, it does not grant. Presenting evidence, including the portable [Mandate](/notes/the-authority-control-plane/), authorizes nothing |
Two precise notes on what this profile is *for*, because it is easy to oversell:
This **layers onto any level** without changing it. It defines no new evidence object. It registers the records the suite already produces. A deployment that keeps its evidence without a Transparency Service is fully conformant and unaffected.
And it buys **accountability, not prevention**. Transparency makes a registered record impossible to silently backdate, drop, or reorder, and gives a cross-domain party offline verification. It does *not* make a dishonest producer honest. A producer can still register a *false* record. Transparency only makes that false record permanent, attributable, and visible to every auditor. And it proves only that a record was *registered*, not that the action the record describes actually occurred or was authorized. That is the evidence's own semantics, not something the log can vouch for.
Run the whole [Q3 board-packet task](/notes/mission-based-authorization-field-reference/) through this operational layer and the three checks land in order: the harness finds the revoked Mission and stops the paused draft, orchestration unwinds the half-written doc by its recorded plan, and the SCITT feed shows the one append-only history with the packet's contents never entering the log.
# The six-layer architecture
This is the operational close of the practice chapter, so it is worth seeing the whole implementation stack at once. The handbook specifies one durable object and follows it down a single spine (*intent becomes a Mission, the Mission bounds authority, enforcement evaluates each action against it*) across six layers:
```mermaid
flowchart TB
P1["The Mission (architecture chapter)
issuance core: approval event,
intent_hash / authority_hash,
mission claim, state-gated issuance"]
P2["Part 1: Approval-time integrity
shaping, consent evidence,
deferred approval"]
P3["Part 2: Sub-agents and delegation
strict-subset child Missions,
offline attenuation"]
P4["Part 3: Runtime enforcement
per-action permit, mediated custody,
action-bound approval"]
P5["Part 4: Lifecycle and change
status, signals, expansion, completion"]
P6["Part 5: Runtime and audit
harness binding, safe unwinding,
tamper-evident evidence"]
P2 -->|proposes| P1
P1 -->|derives| P3
P1 -->|bounds| P4
P5 -->|governs| P1
P2 --> P6
P3 --> P6
P4 --> P6
P5 --> P6
```
Read it as layers, not publication order. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) is the only mandatory core. Everything else is an OPTIONAL companion that layers on without changing it, optional to the core and required by the claim that uses it. Parts 1 through 4 each make the Mission trustworthy along one dimension: the approval that creates it, the delegation that fans it out, the action that draws on it, and the state that governs it. This part is different in kind. It does not add a guarantee, it keeps the others from silently lapsing inside a running agent. The harness keeps a recovered session from acting on dead authority. The orchestrator unwinds work the kill switch caught mid-flight. The audit profile makes the whole evidence trail verifiable by someone who trusts none of the producers. They are why the architecture holds up against a long-running, multi-step, possibly-compromised agent rather than only an idealized one.
The handbook frames adoption as the **Mission Assurance Levels**, and this part fills out the Governed Agent level:
| Level | One line |
| --- | --- |
| Baseline Issuance | Governance metadata and a derivation kill switch, not action safety |
| Runtime-Enforced | Per-action enforcement plus state freshness, on ratified substrate |
| Governed Agent | Consent evidence, harness binding, and operational controls |
| High-Assurance Agent | Mediated custody, action-bound approval, and no unmediated path |
The Governed Agent level adds consent evidence ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)) and the harness (this part), growing with delegation, expansion, and orchestration as the use case arrives. Audit transparency layers onto any level. Compromise resistance is not a level of its own: it comes from meeting all five conditions of the [high-assurance level](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level), and the [adoption post's levels table](/notes/adopting-mission-bound-authorization/#run-governed-and-beyond) carries the full what-you-get and what-you-do-not-get map for every level.
A deployment with no agentic surface and no cross-hop audit need does not need a Mission layer and should not pay its cost. But for the deployments this chapter is written for (where authorization governs a *user-approved durable task* that spans many tokens, calls, actors, and a long stretch of time), the through-line is one sentence. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) argued that the Mission is the missing abstraction because OAuth had no first-class object for the approved task. This part is where that object earns its keep. It is the thing a recovered session must re-check, the thing an in-flight workflow unwinds against, and the subject that ties a Mission's whole evidence feed together. *Session continuity is not authority. The Mission is.*
For the full wire-level detail, read the three drafts: [Mission-Aware Agent Harnesses](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html), [Mission Orchestration and Unwinding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-orchestration.html), and [Mission Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html). For the full spine applied end to end on a concrete deployment, [Least-Privilege MCP Tool Calls Need a Mission](/notes/least-privilege-mcp-tool-calls-need-a-mission/) walks an MCP server through it. And the story does not end at the operational layer. [The concluding chapter](/series/weighing-mission-bound-authorization/) maps these layers onto the substrate-neutral framework, and [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/) turns them into the staged build: the Runtime-Enforced level to ship now, and the roadmap that needs iteration.
---
# Mission Lifecycle and Change
Canonical URL: https://notes.karlmcguinness.com/notes/mission-lifecycle-and-change/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-lifecycle-and-change.md
{{< tldr >}}
- **The argument.** A Mission is approved once but lives for the duration of a task. Approval is a moment. The task is a span. If the only way a consumer learns a Mission's current state is the lifetime of a token it already holds, then a revoked Mission keeps deriving authority until tokens age out. This part makes Mission state a *first-class, fail-safe surface* that consumers observe, parties change, and that grows and shrinks under governance.
- **What lives in this part.** Four verbs over one object: [observe and revoke](#observe-and-revoke-pull-and-push) (Status pull, the Status List for fleets, Signals push), [grow](#grow-expansion-creates-a-successor) (Expansion, growth as a successor Mission), and [complete](#complete-discharge-narrows-one-entry-at-a-time) (Completion, authority that retires itself). The [fail-safe rule](#the-rule-that-makes-it-safe) that ties them together is that only `active` permits reliance. [When the agent changes, not the Mission](#when-the-agent-changes-not-the-mission) covers the neighbor lifecycle: the Agent Deployment, whose changes may require re-approving standing Missions. And [the standing agent at scale](#the-standing-agent-at-scale) runs the verbs against the agent that never finishes.
- **The card analogy.** Freeze it, cancel it, raise the limit through a fresh approval, or let it close itself out as the work finishes. [Canceling the Card Doesn't Stop the Charges](/notes/canceling-the-card-doesnt-stop-the-charges/) is this part in card terms. ([Where the analogy breaks](/notes/canceling-the-card-doesnt-stop-the-charges/#where-the-analogy-breaks).)
- **The laws.** This part operationalizes [Durability, Narrowing, and Termination (Laws 1, 3, and 4)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): the task outlives its tokens, widening requires a fresh approval, and revocation ends authority rather than waiting for tokens to age out. (Termination is the card chapter's rule 5, ending the credential is not ending the arrangement.)
- **Specs (editor's copies).** [Mission Status and Lifecycle](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html) (which carries completion and discharge), [Mission Lifecycle Signals](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html), [Mission Expansion](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-expansion.html), with [Mission Progressive Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html) and [Mission Management](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html) as the newer companions.
**Reading path.** ~19 minutes start to finish. Read in order.
{{< /tldr >}}
# Overview
[The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) defined the Mission as a durable governance object and gave it a deliberately small lifecycle: `active`, `revoked`, `expired`, with the rule that only `active` permits new derivation. That issuance core is complete on its own. But it observes Mission state only through one channel: the lifetime of the tokens it already issued, plus optional token introspection. A consumer that holds a Mission-bound token and nothing else has no way to ask "is this Mission still good," and no way to be *told* that it stopped being good. An authorized operator who wants to pause a Mission has no standardized verb for it. A task that finishes keeps deriving its authority until a clock or a revoke stops it.
This part closes those gaps with the lifecycle suite: optional capabilities layering on the issuance core without changing it, with Status as the suite's root (it carries the status surface, the lifecycle endpoint, and completion's discharge chapter) and Signals and Management as its satellites. They are not all at the same maturity. Status is the Runtime-Enforced level's state surface. Signals, its push complement, is stable and OPTIONAL, aligned with the Shared Signals transmitter and receiver model. Expansion and Completion are advanced, stable design to adopt when the use case arrives, and each has a newer companion noted where it attaches (Progressive Authorization and fleet Management). They answer four questions a long-running task forces:
- **Observe:** how does a consumer holding only a `mission_id` learn the current state, and learn it *promptly* when it changes?
- **Revoke:** how does an authorized party change the state (terminate, pause, resume)?
- **Grow:** what happens when the task legitimately needs more authority than was approved?
- **Complete:** how does authority retire itself safely as the work it was granted for finishes?
The unifying move is the one the [handbook framing](/series/designing-mission-bound-authorization/) names as a recurring rule. **Only `active` permits reliance.** Every state these profiles add (`suspended`, `completed`, `superseded`) is treated as non-active by any consumer, including one that has never heard of it. That is what lets the lifecycle grow new states without breaking the consumers that predate them. We return to it at the end, because it is the property that makes all four verbs safe.
| The control at a glance | |
| --- | --- |
| **Minimum useful version** | The issuer serves Status (or token introspection) so any consumer holding a `mission_id` can learn current state, and every consumer treats non-`active` as no |
| **What it prevents** | A revoked or expired Mission living on until its tokens age out |
| **What it does not prevent** | Actions inside the published staleness bound. Size the polling to the risk, and adopt the Signals push where seconds matter |
| **Operational owner** | The Mission Issuer operates the Status surface and the lifecycle verbs |
| **Evidence emitted** | Every lifecycle transition, keyed by `mission_id` |
| **Maturity** | Status is the Runtime-Enforced level's state surface and the suite's root, carrying completion's discharge. Signals is stable and OPTIONAL. Expansion is advanced |
# Observe and revoke: pull and push
The canonical statement first, because every mechanism in this part
serves it. Revocation changes the Mission's authoritative state
immediately. Enforcement latency is path-dependent: runtime-gated
actions stop within the published freshness bound, new derivation
stops when the issuer observes state, and outstanding offline-valid
tokens run to expiry unless the path checks Mission state.
The kill switch from [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) is possession-independent. Revoke the Mission at the Authorization Server and no new authority derives for the task. But "no new derivation" only bites at the next derivation event. A Resource Server holding a live access token does not consult the Authorization Server on every call by default. It trusts the token until it expires. So the kill switch is only as fast as the slowest token's remaining lifetime, unless consumers can learn the current state out of band.
There are exactly two ways to learn current state: ask for it, or be told. The suite provides both.
## Status: the pull side
[Mission Status and Lifecycle](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html) defines the canonical state surface the issuance core deferred: an operation keyed by `mission_id` alone. Token introspection answers "is *this token's* authorization still good." The Status operation answers a different question, "what is the state of *this Mission*," and answers it for any consumer holding a `mission_id`, including an auditor or a cross-domain Resource Server that holds no token the Authorization Server issued. The consumer resolves the endpoint from the credential's `mission.issuer` and asks.
The answer is **signed**. A Status response is a JWS, with its own media type (`application/mission-status-response+jwt`), carrying the `mission` object (`id`, `issuer`, `authority_hash`, `state`, and the Mission's `expires_at`) plus, when the requester names an audience, the audience-scoped Authority Set entries relevant to it. An auditor may omit the audience and get a state-only response with no `authorization_details`. It is bound to the request by an echoed `nonce`, to the requester by `aud` and `sub`, and to a freshness window by `fresh_until`, which tells the consumer how long it may cache the reported state before re-checking. The signing matters because the response often outlives the call. An auditor must be able to verify, later, that the issuer really reported `revoked` at a given moment.
Two design choices in Status are worth pulling out because they are easy to get wrong:
- **A `mission_id` is never a bearer capability.** The endpoint authenticates the requester and authorizes it for the specific `mission_id` and audience. An unknown Mission and a known-but-unauthorized one return *indistinguishable* responses, so the Status surface cannot be turned into an enumeration oracle for the Mission space.
- **The dedicated operation is not RFC 9701.** Signed token introspection (RFC 9701) is scoped to token introspection and does not apply to a lookup keyed by `mission_id`. There is a separate, thinner story for the token-scoped case. The issuance core's introspection projection carries the `mission` state, and this Status profile adds the option of returning that projection as an RFC 9701-signed response. Two surfaces, two signing stories, deliberately not conflated.
Status is also where the explicit **lifecycle endpoint** lives (the verbs that change state). A management endpoint, distinct from RFC 7009 token revocation, accepts authenticated `revoke`, `suspend`, `resume`, and `complete` operations and introduces two states the issuance core did not have:
- **`suspended`:** a non-terminal pause. A suspended Mission derives no tokens, but it can be resumed to `active`. This is the "stop, but don't tear down" state an operator wants when something looks wrong but is not yet known to be malicious.
- **`completed`:** a terminal state recording that the task finished successfully.
The legal transitions are narrow on purpose: `suspend` only from `active`, `resume` only from `suspended`, `revoke` from either, `complete` from `active` or `suspended` (completion is a monotonic narrowing to a terminal state and needs no derivation window, so a suspended Mission need not be resumed first). One rule adjudicates every request: an operation whose resulting state equals the Mission's current state is an idempotent success, terminal or not, and any other operation against a state it is not legal from is a *conflict*, not a silent no-op. The endpoint refuses it rather than pretending it worked. The endpoint authorizes operations against deployment policy (typically `revoke` to the Subject, Approver, or an administrator, and `suspend`/`resume` to administrators), and an unauthorized request gets the same not-found response shape as an unknown Mission, so the management surface is no more of an enumeration oracle than the read surface. Fleet-scale operations are deliberately out of this profile: authenticated Mission enumeration (by subject, client, state, or expiry window) and bulk lifecycle operations with a dry-run-first discipline live in [Mission Management](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html), an advanced companion.
Suspension is also priced as an actuator, not only defined as a state. It parks work rather than canceling it, resume re-verifies the facts that admitted the work, and a suspend that destroys days of queued work is a lever responders hesitate to pull and a damage multiplier for whoever games the risk signals into pulling it.
## Signals: the push side
Status answers when the consumer asks. But a high-assurance consumer that wants revocation to bite in seconds cannot afford to poll the Status endpoint on every action. That puts the Authorization Server on the hot path of every credential validation. The complement is to be *told*.
[Mission Lifecycle Signals](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html) is a profile of the OpenID Shared Signals Framework. When the Mission Issuer commits a lifecycle transition (a revocation, expiry, suspension, completion, or the approval event that activates the Mission), it emits a signed **Security Event Token**, a `mission.lifecycle-change` event, delivered either by push (the Issuer pushes the SET to the consumer's receiver) or by poll (the consumer fetches available SETs). A partner Resource Server subscribed to the stream learns of a cancellation seconds after it happens, well inside the remaining lifetime of any token it holds, and stops honoring the Mission.
**Scale is its own consumer.** A fleet of enforcement points watching
thousands of Missions cannot poll them one by one, and the
[Mission Status List](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html)
is the swarm answer: the Issuer publishes one signed, TTL-bounded bit
array covering many Missions, riding the OAuth Status List work now in
the RFC Editor queue. The mapping is only-active (a set bit is the only
thing that permits reliance), the list's TTL is the published staleness
bound, and index assignment preserves the anti-oracle property, so
holding the list does not let a consumer enumerate the estate's
Missions. One fetch refreshes a fleet.
One maturity note. Signals began experimental and has earned the stable label: the profile is Standards Track OPTIONAL, aligned with the Shared Signals transmitter and receiver model, and its dependencies (Security Event Tokens and their delivery) were ratified all along. The design posture survives the promotion. Push is a latency optimization over correctly sized status polling, so the stable path remains Status sized to the risk, with Signals adopted where a revocation must bite in seconds.
The event carries the new `state`, the `prior_state` (except on the initial approval emission, where there is none), and a strictly monotonic per-Mission `version` counter. The counter is the part that makes event delivery safe against the network. Events can arrive out of order or be replayed, so the consumer applies a transition only if its `version` is greater than the last it applied. A stale `active` event for an older version cannot revive a Mission a newer `revoked` event already retired. And because delivery is best-effort, a consumer that cannot verify its stream, or that was down and may have missed events, treats its cached state as stale once it exceeds the Issuer's advertised staleness bound and falls back to polling Status. A missed event is a *freshness failure*, never "still active." Signals make revocation prompt. They never make it fail open.
```mermaid
flowchart LR
subgraph AS["Mission Issuer"]
M[("Mission record
state")]
SE[Status endpoint]
LE[Lifecycle endpoint]
EV[Event stream
SSF / SET]
end
OP[Authorized party] -->|revoke / suspend
resume / complete| LE
LE --> M
M --> SE
M --> EV
C1[Consumer
holds mission_id] -->|PULL: ask
signed response| SE
EV -->|PUSH: told
signed SET| C2[Consumer
subscribed]
```
Pull and push are not alternatives so much as two sizes of the same answer. A consumer that checks state occasionally polls Status. A consumer that needs prompt revocation subscribes to Signals and falls back to Status when the stream goes quiet. Both read the same fact, the current `state`, from the same authoritative issuer.
> **The running example.** Take the [handbook's running example](/notes/mission-based-authorization-field-reference/), the *Prepare the Q3 board packet* Mission, `msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1` at `as.example.com`. The board meeting is cancelled while the agent is still drafting, so an admin **revokes** the Mission mid-draft at the lifecycle endpoint. A consumer holding only the `mission_id` (the docs Resource Server, an auditor) learns the new state without holding any token the Issuer can age out. It either asks Mission Status (pull) or receives a `mission.lifecycle-change` Signal carrying `state: revoked` (push). Either way the result is the same. `query_financials`, `create_doc`, and `notify_reviewer` all stop deriving at once.
This is the kill switch at the moment of use, everything this section defines on one surface, with the honesty this handbook insists on: maximum bounds rather than guarantees, seconds for mediated paths, token lifetime for issuance-gated ones:

Two details worth noticing because they are the spec, not decoration:
completed effects are flagged for unwind rather than rolled back, and
the test button is [the acceptance test](/notes/adopting-mission-bound-authorization/)
made one click: revoke, then watch the next consequential action fail
closed.
# Grow: expansion creates a successor
The first recurring rule of the handbook is that authority can only *narrow*. A derived or delegated token never carries more than the Mission. The subset rule enforces it at every derivation. That is a foundational safety property, but it raises an obvious question. What happens when the task legitimately needs to do something the approved Authority Set does not cover? Mid-task, the *Prepare the Q3 board packet* agent decides it could enrich the packet by reading CRM customer data, an authority the Approver never granted, outside the Mission's `query_financials`/`create_doc`/`notify_reviewer` set entirely.
The answer is *not* to widen the Mission in place. A Mission's authority is committed at one approval event by `authority_hash`. Widening it in place would mean the thing the user approved is no longer the thing in force, and `authority_hash` would no longer commit what is being used. [Mission Expansion](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-expansion.html) instead routes growth through a *new approval that creates a successor Mission*.
An expansion is an ordinary Mission creation under the issuance core (a Mission Intent submitted through Pushed Authorization Requests, leading to a fresh approval event) with one binding added. The request is bound to the predecessor's grant (the client presents the predecessor's refresh token), so the Issuer adjudicates a successor *of a specific predecessor* rather than an unrelated new Mission. The grant binding is an eligibility rule, not an authority path. A deployment that issues no refresh token forgoes transactional expansion and reaches succession through a fresh approval that references the continued work, and the management plane's standing over the predecessor never depends on the grant proof: it gates the proposal channel and the supersession trigger, never the authority. The fresh consent is what supplies the broader authority. The successor's authority comes only from its own approval. Its `authority_hash` commits exactly the set the Approver saw, never the predecessor's plus a computed delta. On the successor's activation, the predecessor transitions atomically to a terminal `superseded` state and derives nothing further, because two active records for one undertaking are a widening wearing a lifecycle. The successor carries a `predecessor` lineage member linking it back, but that member is audit context only. Like every other extra member on the `mission` claim, it MUST NOT grant or widen authority.
The ask, as the user meets it:

One honesty note on the mock's compression: in the family's
semantics, approving this ask does not edit the running Mission. It
lands as a fresh approval over a successor's full derived authority,
with the predecessor superseded and lineage kept.
Two distinctions keep expansion honest:
- **Expansion is not step-up.** A request denied because authentication is too weak (an `acr`/`amr` shortfall) is satisfied by re-authentication. The Authority Set does not change. A request denied because the *authority* is not in the active Mission is what expansion addresses. Routing an authentication shortfall through an approval event would surface irrelevant consent and breed approval fatigue. Treating an authority shortfall as a mere re-auth would silently widen nothing. The two are not interchangeable.
- **Expansion is a governance operation, not a runtime escalation.** It does not undo the subset rule. It is a deliberately heavier, consent-backed path that produces a new governed object the kill switch and audit chain still cover.
For open-ended tasks where stopping the user at every step is impractical, a newer, experimental companion, [Mission Progressive Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html), builds on expansion. At the initial approval the Approver may also consent to an *authority ceiling* and a *drawdown policy*, so in-ceiling expansions can be adjudicated by policy rather than a fresh human prompt. The ceiling is broad by construction (it has to cover the open-ended task), but what stays narrow is the active authority any single Mission in the chain yields. Each successor is derived for what is actually needed at that step, independently gated and revocable. The guard is explicit. A drawdown that would grant an irreversible action, an external commitment, privileged administration, or cross-domain authority always requires fresh human approval, even within the ceiling. Progressive authorization bounds standing-authority exposure. It does not eliminate it, and it is meant to be paired with short successor lifetimes and runtime enforcement.
> **The running example.** The *Prepare the Q3 board packet* Mission, `msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1` at `as.example.com`, cannot read CRM customer data. That authority is outside its Authority Set, and the agent cannot widen the Mission in place. So it requests an **expansion**, a fresh approval for a successor Mission bound to the predecessor's grant. The Approver **declines**. CRM access is out of policy for board-packet preparation. The result is the failsafe one. No CRM access, no successor activates, and the predecessor is untouched and still `active`, deriving exactly its original three entries. Had the expansion instead been approved, the successor would have activated and the predecessor would have transitioned atomically to `superseded`, and supersession does not cost the runtime: the harness rebinds running sessions to the successor, so the work continues under the new authority without a restart. Because it was declined, nothing supersedes anything.
# Complete: discharge narrows one entry at a time
Expansion is governed growth. Completion is its narrowing counterpart. It is the more important of the two for everyday safety, because the common case is not a task that needs more authority but a task that is *done* with some of the authority it has. Like Expansion, it is advanced: stable design to adopt when the use case arrives, with `expires_at`, revocation, and the `complete` verb as the simpler path until then.
The [issuance core's](/notes/the-mission-is-the-missing-abstraction/) Mission Intent can carry `success_criteria`: a description of when the task is complete. In the issuance core those criteria are **inert** (rendered to the Approver, committed to the record, carrying no machine effect). A Mission granted authority to post journal entries "until the Q3 close is finalized" keeps deriving that authority after the close, until a clock or a revoke intervenes. [Mission Status and Lifecycle's completion chapter](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html) supplies the enforceable counterpart: `terminal_when`, an entry-level completion condition. When the condition fires, the entry is **discharged**. The Authorization Server stops deriving any token carrying it, exactly as it refuses derivation for a non-`active` Mission.
The crucial scope detail is that discharge gates at the *entry*, not the Mission. A multi-resource Mission completes one entry at a time. In the [running example](/notes/mission-based-authorization-field-reference/), suppose the board meeting is *not* cancelled and the work finishes instead. The *Prepare the Q3 board packet* Mission has a `query_financials` read entry, a `create_doc` write entry, and a `notify_reviewer` send entry, the write and send entries each bounded `terminal_when` their step is done (`create_doc` once the packet is published, `notify_reviewer` once the audit committee is told). As each condition fires, the Authorization Server stops deriving that entry (a refresh returns a token carrying only what remains) while the Mission stays `active` so any remaining entry keeps working. Each authority retired itself the moment the task it was granted for completed. No revoke, no clock.
This is distinct from the `complete` lifecycle verb of the Status profile, and the two should not be blurred. `complete` is a *Mission-level* terminal transition to the `completed` state, driven by an authorized party at the lifecycle endpoint. `terminal_when` discharge is *entry-level*. The Mission stays `active` and keeps deriving its other entries. A deployment that tracks Mission-level completion *may* move a Mission to `completed` once all its entries are discharged, but Completion does not require it. One is "the whole task is finished." The other is "this part of the authority is spent."
What makes entry-level discharge safe to expose to an adversarial agent is that it is **monotonic**. Discharge can only *remove* an entry's authority. It can never widen the entry or the Mission. So a prompt-injected agent has nothing to gain from it. The worst it can do is spend its own authority sooner, which is not an attack. That single property is why discharge composes cleanly with the subset rule (a derived entry carries its parent's conditions unchanged and may only *add* more, making it discharge sooner) and why an Authorization Server that cannot determine whether a condition has fired fails *closed*, treating the entry as possibly discharged and withholding issuance rather than risking authority for a task that may have ended. Like the kill switch, completion is built so that uncertainty resolves toward less authority, never more.
# When the agent changes, not the Mission
One more change surface sits beside the Mission's own lifecycle, because [three objects govern an agent estate](/notes/the-mission-is-the-missing-abstraction/#three-objects-three-lifecycles) and this part's verbs cover only one of them. The agent's **Agent Deployment**, its approved behavioral version (code, model, system prompt, tool allowlist, data scope, runtime configuration), changes on its own cadence and is owned by change governance rather than by the Mission Issuer. Two rules keep the seam honest.
First, which changes require re-approving standing Missions is policy, and it is policy that change governance records rather than a default the platform assumes. A prompt hotfix may be routine. A new model or a widened tool allowlist changes what the Approver actually approved, and a standing Mission whose agent has materially changed is running on an approval that was given to different behavior.
Second, a Mission can require a specific approved Agent Deployment without that deployment becoming the Mission. The core's OPTIONAL `controls.agent_deployment` Intent control pins the Mission to a named Agent Deployment, so derivation fails when the behavioral version in force is not the one that was approved. The pin is a constraint the Mission carries. The Agent Deployment stays owned by change governance either way.
# The standing agent at scale
One shape of agent looks like a counterexample to this whole part: the agent that never finishes. A support-triage agent works four hundred tickets an hour. A SOC agent watches alerts around the clock. There is no board packet that ends, so what do expiry, completion, and successors even mean for it? And the honest version of the question is sharper: if the Mission just gets renewed forever, is this not the blank check with a renewal ritual? The card chapter already toured this room: the [standing arrangement that keeps billing](/notes/canceling-the-card-doesnt-stop-the-charges/) is the expense world's version, and its lesson was that continuity machinery must be taught to check whether the arrangement still stands.
The answer is the distinction the machinery in this part exists to make. **The agent stands. The authority cycles.** The standing thing is a charter, consented once as an authority ceiling with a drawdown policy ([Progressive Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html), experimental for exactly this shape of work). The working thing is the Mission each unit of work draws under that ceiling: a ticket, a batch of fifty, an investigation, each adjudicated by policy inside the pre-consented bounds, each carrying its own expiry and entries, each [discharging](#complete-discharge-narrows-one-entry-at-a-time) what it used as the unit completes. And nothing high-consequence rides the policy path: the drawdown guard routes [the class-guard classes](/notes/from-a-request-to-an-approved-mission/#who-may-approve) and cross-domain authority to a fresh human approval even inside the ceiling.
Run the numbers instead of the vibe. Four hundred tickets an hour is four hundred small Missions, or forty batch Missions, whose approvals are policy decisions at machine speed. What humans decide is the handful of exceptions a day where the guard fires, and the one decision that matters: the ceiling itself, set and renewed on a governance cadence with the previous cycle's evidence in front of the reviewer. The human approval load is not four hundred an hour. It is the ceiling review plus the exceptions, which is the [fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget) spent where it buys safety. The machine load scales the same way: four hundred Missions an hour is also four hundred freshness checks, and the [Status List](#observe-and-revoke-pull-and-push) answers them in one fetch. The records have their own bill: four hundred Missions an hour is also four hundred evidence trails, so the ceiling review sets a retention class beside the authority, with completed Missions' evidence aggregated or aged out on a stated horizon and [the minimization rules](/notes/making-compliance-a-by-product/#the-evidence-is-itself-sensitive) deciding what each trail carried in the first place.
And the failure mode is named, because a standing charter can decay into exactly what this handbook exists to retire. The tells are checkable: renewals that never narrow, ceilings that only grow, exception approvals trending toward zero because the guard was quietly widened, and discharge that never fires because nothing is scoped tightly enough to complete. The mechanism that forces narrowing is the renewal default: a ceiling renews at the envelope its evidence shows was used, not at its prior breadth, and anything wider is a new approval, not a rollover. A ceiling whose renewals carry no evidence review is a blank check with a calendar. The machinery makes the standing agent governable. Only the governance cadence makes it governed.
The accountable principal has a lifecycle too, and it arrives from the identity provider rather than being assumed. Authority that outlives its approver is one of the oldest failures in the stack, and this layer must not recreate that orphan debt in its own system of record. A leaver event moves the person's ceilings into a state that admits no new Missions and starts an ownership-transfer clock, running Missions transfer or run out under policy, and a ceiling whose accountable principal no longer exists is a finding, not a curiosity. [Signals](#observe-and-revoke-pull-and-push) carry Mission state out to consumers. The same rail carries principal state in.
# The rule that makes it safe
The four verbs add three new states to the lifecycle [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) began with. The state diagram now looks like this:
```mermaid
stateDiagram-v2
[*] --> active: Approval event
active --> revoked: revoke (user / admin / policy)
active --> expired: expires_at reached
active --> suspended: suspend
active --> completed: complete
active --> superseded: successor activates (Expansion)
suspended --> active: resume
suspended --> revoked: revoke
suspended --> expired: expires_at reached
suspended --> completed: complete
revoked --> [*]
expired --> [*]
completed --> [*]
superseded --> [*]
```
A growing state machine is a liability if every consumer must be upgraded in lockstep to recognize each new state. The Mission lifecycle avoids that with the rule the [handbook framing](/series/designing-mission-bound-authorization/) states as one of its two invariants, and which the [Reference's lifecycle-states section](/notes/mission-based-authorization-field-reference/#lifecycle-states) records for the core:
> Only `active` permits reliance. A consumer treats *every* other value, recognized or not, as non-active.
This is why the new states fail safe. A Resource Server that predates the Status profile has never heard of `suspended`. When it sees one (in a Status response, in an introspection projection, in a Signals event), it does not need to understand it. It only needs to observe that the value is not `active`, and refuse. A consumer that predates Expansion treats `superseded` the same way. The forward-compatibility rule turns "I don't recognize this state" into "this Mission is not live," which is precisely the safe interpretation. New lifecycle states therefore extend the system without a flag day.
The one place this "ignore what you don't recognize" instinct flips is Completion's `terminal_when`, and the difference is worth being precise about. A new *state value* is safe to ignore because an unrecognized state is treated as non-active. Ignoring it fails *closed*. But `terminal_when` is a *member*, not a state, and it is mandatory to understand. It is essential narrowing a consumer cannot infer from the rest of the entry, so ignoring it would let a discharged entry keep being narrowed, projected, or enforced, failing *open*. A consumer that does not recognize `terminal_when` therefore MUST fail closed for that entry. It MUST NOT narrow, delegate, audience-project, or rely on it. Unknown state value, treat as non-active. Unknown `terminal_when`, refuse the entry. Both resolve toward less authority, by opposite handling.
The same instinct runs through every profile in this part (Status, Signals, Expansion, and Completion alike): uncertainty resolves toward *less* authority. That is the through-line. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) made revocation possession-independent. This part makes the *current state itself* a first-class surface (observable by pull and push, changeable by authorized parties, growable only through fresh approval, and shrinkable safely as work finishes) without ever giving a consumer a way to read uncertainty as permission.
# Where this sits in the handbook
Lifecycle and change is the layer that keeps the Mission true *over time*. It does not enforce actions. That is [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/), which consumes the state these profiles expose. Every PDP decision is conditioned on current Mission state, and Status is how the PDP learns it promptly, with Signals accelerating and Completion narrowing what there is to learn. It does not handle fan-out. [Mission-Bound Authority](/notes/mission-bound-authority/) covers Child Missions and the `cascaded` state that Status and Signals forward-reference but do not define. And it does not record the transitions for audit. [The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/) makes every lifecycle change tamper-evident.
The approval event of [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) and the approval-time integrity of [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/) establish *what was approved*. This part keeps that approval honest as the task runs. A Mission that is revoked stops being relied on, a Mission that grows does so under a fresh approval, and a Mission whose work is done retires its own authority. Observe, revoke, grow, complete. Four verbs that turn a one-time approval into a governed lifecycle.
---
# Mission-Bound Runtime Enforcement
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-runtime-enforcement/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-runtime-enforcement.md
{{< tldr >}}
- **The argument.** A Mission-bound token is not a license to act. The [issuance core](/notes/the-mission-is-the-missing-abstraction/) bounds *what authority may exist*. Within a token's lifetime, an active Mission is still an ambient bundle of authority an agent can spend at will. The runtime layer closes that gap. Before each consequential action, a [Policy Enforcement Point](#the-runtime-model) gets a permit from a Policy Decision Point that evaluates the action against the *current* Mission.
- **What lives in this part.** The [ambient-authority gap](#the-gap-issuance-leaves-open) issuance leaves open, the [PEP/PDP runtime model](#the-runtime-model), [which actions are consequential](#which-actions-are-consequential), [where the enforcement point sits](#where-the-enforcement-point-sits), [closing the TOCTOU gap](#closing-the-toctou-gap-with-parameter-binding), [metering and fail-closed behavior](#metering-and-fail-closed-on-everything-else), [fail-closed and active freshness](#fail-closed-and-active-freshness), the [high-assurance level](#the-high-assurance-level), [the decision contract](#the-decision-contract-and-its-authzen-binding), [the trifecta at execution time](#the-lethal-trifecta-at-execution-time), and [why this is the center of gravity](#why-this-is-the-center-of-gravity).
- **The card analogy.** The card network authorizes each transaction. It does not trust the card. [The Network Approves Every Transaction](/notes/the-network-approves-every-transaction/) is this part in card terms. ([Where the analogy breaks](/notes/the-network-approves-every-transaction/#where-the-analogy-breaks).)
- **The law.** This part is where [Containment (Law 5)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) lives: execution must continuously remain inside approved purpose, checked at the point of use. (In the card chapter this is rule 4, authorize the action, not the instrument.)
- **Specs (editor's copies).** [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), [Runtime Enforcement: AuthZEN Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html), with [Mission Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) as the experimental companion for cumulative bounds.
**Reading path.** ~15 minutes in order through [fail-closed and active freshness](#fail-closed-and-active-freshness), ~25 for the deep read, or jump to [the runtime model](#the-runtime-model) โ [the TOCTOU gap](#closing-the-toctou-gap-with-parameter-binding) โ [the high-assurance level](#the-high-assurance-level) if you are already convinced and want the contract.
{{< /tldr >}}
# Overview
[The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) made the Mission a durable, approval-backed governance object, and [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/) made the approval that creates it trustworthy. Those layers say *what was approved, by whom, for how long, and within what bounds*. They govern issuance and derivation. They do not evaluate an individual action at the moment it runs.
That is the gap this part closes. Mission governance makes authority auditable and revocable. Runtime enforcement is what prevents an active Mission from becoming ambient authority. It is the **Enforcement** step on the [spine](/notes/the-mission-is-the-missing-abstraction/#the-intent-to-enforcement-spine), and it is the center of gravity for the whole chapter. The other five layers exist to make this one trustworthy.
> **A mission-bound token without runtime enforcement is governance
> metadata, not agent safety.**
That sentence is the handbook's hardest claim, and this part is where it is enforced rather than asserted.
The model is one sentence. Within a declared enforcement scope, before each consequential action a Policy Enforcement Point (PEP) obtains a permit from a Policy Decision Point (PDP) that evaluates the action against the current Mission. Everything else in this part is what that sentence requires to actually hold.
Read that sentence for what the decision point actually holds, because it is easy to hear "central PDP" and picture a rules engine pricing bare actions. The decision evaluates the action *in its undertaking*. The Mission carries context no resource request ever does: the committed why, the approved shape of the authority, and, because evidence joins on the Mission, what the undertaking has already done. "Delete database" in isolation is indistinguishable from catastrophe. "Delete database, inside an approved migration whose copy steps are already in the Mission's evidence" is a priced, checkable step. And the richer inputs stay deterministic: the anchors and the evidence trail are committed facts, not runtime inferences about intent, which is the line between this model and the intent-guessing it rejects. The history consulted is also first-party: the deployment's own record of what its decision points permitted and its enforcement points executed. That is a different thing from the audit layer's caution that a registered record proves inclusion to an outside verifier rather than occurrence. The PDP trusts its own ledger the way any database trusts its own writes.
And the order in that sentence carries more weight than the mechanism. **Approve the bounded work, then authorize every consequential action against that approved boundary.** Per-action authorization alone cannot prevent individually permitted steps from composing into an outcome no one approved: five reads and one send, each valid in isolation, can be an exfiltration pipeline. The approved boundary bounds that composition only where its bounds are the right kind: a destination the approval never granted stops the send, and the cumulative bounds and single-use permits of [Consumption Metering](#metering-and-fail-closed-on-everything-else) carry the quantitative case. What survives inside the boundary is the flow-level residual [the trifecta section](#the-lethal-trifecta-at-execution-time) prices honestly: per-action checks are not information-flow control, and the harness taint rule is the mitigation, not this gate.
| The control at a glance | |
| --- | --- |
| **Minimum useful version** | A PEP at each consequential boundary in a declared enforcement scope obtains a permit from a PDP that evaluates the action, its parameters, the actor, and current Mission state. Fail closed |
| **What it prevents** | An active Mission becoming ambient authority, and the approved-versus-executed parameter swap |
| **What it does not prevent** | Misuse inside the approved scope, and anything on a path the deployment does not mediate. Name those paths |
| **Operational owner** | Platform and resource teams own the PEP fleet. The authorization team runs the PDP as a tier-0 dependency |
| **Evidence emitted** | Decision Evidence for every consequential decision, including denials, and Execution Evidence for the highest classes |
| **Maturity** | The Runtime-Enforced level's enforcement half: the runtime contract and its AuthZEN binding. Consumption Metering is experimental |
# The gap issuance leaves open
The issuance core is deliberately an issuance-and-derivation layer. Its own security considerations say so. It does not evaluate individual runtime actions, so an active Mission still bounds a set of authority an agent may exercise *freely* within a token's lifetime.
Concretely, after the approval event the agent holds Mission-bound access tokens. Each token carries the `mission` claim (`id`, `issuer`, `authority_hash`) and an Authority Set in its `authorization_details`. A Resource Server validates the token, checks audience and scope, and serves the request. That is governance. The token is bound to a kill switch, and the Authority Set is the maximum the token can ever claim. But between approval and the token's natural expiry, nothing re-checks each concrete action. A compromised, drifted, or prompt-injected agent can spend the whole Authority Set, with whatever parameters it likes, as fast as it likes, until the token ages out or someone revokes the Mission.
The runtime layer delivers exactly the four things the issuance profile names as out of scope, plus enforcement of the constraints it carries but does not evaluate:
1. evaluation of a request's **parameters** against the Mission at the point of use.
2. **per-action evidence** for every consequential action.
3. binding the **invoked tool or function identity** to the Mission's approved authority.
4. **execution-time re-evaluation** that closes the approval-to-execution gap.
And, additionally, the fail-closed treatment of the consumption bounds a Mission carries (`max_budget`, `max_calls`, `max_duration`, `max_egress_volume`), whose definition and metering mechanics live in the experimental [Mission Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) companion.
> Mission-bound tokens bound what authority may exist. This profile, the [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html) draft, defines where and how that authority is re-checked before consequential effects occur.
# The runtime model
A PEP sits at each consequential execution boundary. Before the action runs, it obtains a decision from a PDP that evaluates the action against the Mission the acting token is bound to.
```mermaid
sequenceDiagram
autonumber
participant A as Agent
participant PEP as PEP
(action boundary)
participant PDP
A->>PEP: action + parameters
Note over PEP: validate token
(issuer, audience, exp, cnf)
PEP->>PDP: evaluate vs Mission
(authority, parameters,
actor, state, resource policy)
PDP-->>PEP: permit / deny
(bound to parameters)
Note over PEP: reverify binding,
write evidence
PEP-->>A: execute / refuse
```
Two roles do the work, and the spec defines them so the boundary is unambiguous:
- The **PEP** is the component that can *prevent* a consequential action and that obtains and enforces a decision before the action runs. Depending on the action it is a Resource Server, an MCP server, an egress proxy, a workflow engine, or the orchestrator itself.
- The **PDP** evaluates the action against the Mission and returns permit or deny. Its placement is a deployment choice: co-located with the Mission Issuer, embedded in the Resource Server, a tenant-scoped service, or a shared service. The profile mandates none of these. It requires only that a PEP at each consequential boundary can reach an applicable PDP.
The runtime decision evaluates six inputs against the action, and a deny on any one is terminal for that action:
- **Authority.** The action's resource and invoked tool identity must be within an applicable `authorization_details` entry the token carries, or that is otherwise available to the PEP or PDP for that token under the issuance profile (through introspection, when the authority is not inline), under the subset rule. The PEP *asserts* the capability identity it will invoke. The PDP refuses an identity outside the approved `actions`. A catalog-sourced capability is further bound to the source-content digest recorded at derivation (covered by `authority_hash`). If a tool definition is later redefined or poisoned, the current source digest differs from the recorded one and the decision fails closed as `capability_drift`.
- **Resource policy.** A Mission-bound permit is an upper bound on authority, not a command to perform the action. Object-level authorization, tenant configuration, legal holds, service invariants, and risk policy still apply. The action fails closed unless both Mission authority *and* Resource policy permit it.
- **Parameters.** Every `constraints` value on the applicable entry is evaluated against the concrete parameters. A constraint the PDP cannot understand or enforce causes refusal, never silent pass-through.
- **Actor.** When delegation is in effect, the PDP evaluates the authenticated `act` chain and refuses one that is missing or malformed (the delegation chain is [Mission-Bound Authority](/notes/mission-bound-authority/)).
- **Time.** The PDP refuses an expired token. Because the issuance profile caps a derived token's `exp` at `expires_at`, the `exp` check transitively enforces Mission expiry. The `mission` claim can also carry an OPTIONAL `expires_at` member, a bounding commitment with no liveness: it never extends reliance, and current state still comes from a freshness source.
- **State.** The PDP refuses unless the Mission is `active`. This is the [handbook-wide rule](/series/designing-mission-bound-authorization/) made operational. Only `active` permits reliance, and every other state, recognized or not, is non-active.
This is the move that makes Intent-Based Access Control practical. The PDP is not reconstructing "what did the user want" from a prompt at enforcement time. It is checking a concrete action against an approved, integrity-anchored Authority Set. The interpretation already happened at consent time ([The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/#why-this-makes-ibac-practical)). Current resource policy stays authoritative for the final permit or deny.
The model has a price, because the decision is on the hot path by design. Every consequential action buys a policy evaluation, and the contract's cost controls are the classification and the lease: non-consequential work is never gated, consequential reads take a decision without parameter binding, permits for reversible writes amortize across a validity window, and only the high-consequence classes pay for single-use permits and reverification on every execution. What the family does not yet have is deployment-scale performance data, and this handbook will not invent it. A deployment should publish its measured decision latency next to its staleness bound, and the absence of those numbers across the industry is one more reason [walk ships first](/notes/adopting-mission-bound-authorization/#walk-the-runtime-enforced-level): they should come from running systems, not from this page. The [classification bet](/notes/the-convergence-and-the-wagers/#where-this-could-be-wrong) names the erosion to watch while they arrive: a mediated class list that shrinks release over release while the claim name holds.
# Which actions are consequential
The PDP gate is not for every action. Reasoning steps and cache reads have no external visibility and need not be gated. The boundary between consequential and non-consequential is deployment policy. But a deployment must not draw it so loosely that nothing is enforced. The profile defines a default classification a deployment SHOULD adopt, and a **floor** it MUST observe.
| Class | Examples | PDP gate | Parameter binding |
|---|---|---|---|
| Non-consequential | internal reasoning, cache reads, planning | not required | n/a |
| Consequential read | reading user data, querying logged APIs | MUST | not required |
| Consequential write | updating records, posting messages | MUST | MUST |
| Irreversible action | sending mail, payment, deletion | MUST | MUST, with TOCTOU reverification + evidence |
| External commitment | signing, accepting terms for the user | MUST | MUST, with TOCTOU reverification + evidence |
| Privileged administration | granting access, changing policy | MUST | MUST, with TOCTOU + evidence |
The bottom three rows are the **high-consequence classes**, where a token-lifetime-wide standing authority is least appropriate and the profile's strictest requirements attach (action-bound approval, mediated custody, active-state freshness, execution-outcome evidence, all below).
Two guardrails keep classification honest. A Mission's `purpose`, or deployment policy, MAY *raise* an action to a stricter class. A financial-settlement purpose may treat a write as an external commitment. It MUST NOT *lower* an action below the resource owner's minimum, and MUST NOT classify an irreversible, external-commitment, or privileged-administration action as non-consequential. A deployment cannot evade enforcement by relabeling a high-consequence action as harmless.
The classes are floors drawn from several consequence dimensions, reversibility, exposure, privilege, commitment, and value, with policy taking the maximum across them, because a read of regulated data is irreversible in the only sense that matters once it leaves. And the same operation can cross classes on its parameters: creating a ten-dollar draft is not releasing a million-dollar transfer.
# Where the enforcement point sits
The strongest decision logic is void if the PEP is in the wrong place. The same Mission, PDP, and policy view all fail if the permit is checked somewhere the action can route around. The rules are blunt:
- **The PEP MUST be at the last controllable boundary before the action.** A permit checked further upstream does not survive parameter changes, retries, or routing that happen after the check.
- **A token-issuance decision does not replace execution-time authorization.** A token-only Resource Server cannot claim runtime enforcement. The issuance gate is governance. The runtime gate is enforcement.
- **A tool-catalog filter does not replace per-call authorization.** Filtering a `tools/list` by the caller's authority is exposure control. Every consequential `tools/call` still passes the runtime gate.
- **An orchestrator's internal check does not replace a Resource Server's PEP.** Defense in depth is permitted. Substitution is not.
- **If no PEP can prevent the action for a class, the deployment MUST NOT claim runtime enforcement for that class**, and must name the classes and execution paths it does mediate.
That last rule is why the profile's conformance is *scoped*, not global. A deployment conforms only for the resources, action classes, execution paths, and authorization-detail types named in its **enforcement scope**. An OAuth-protected API call is gated at the Resource Server, a consequential MCP `tools/call` at the MCP server, a local file write or payment at the orchestrator, external egress at an egress proxy. Where an action can be reached by an unmediated path (a debug shell, an unsanctioned egress route, a direct connector), the profile is simply not enforced for the classes that path reaches, and the claim must say so.
The claim, as a surface a deployment would actually publish:

The parts that make it honest are the ones no product page would print: the first-class exclusions, the token-lifetime bounds on issuance-gated classes rather than a pretense of mediation, the verification suite. One vocabulary note: the
mock's enforcement levels are this deployment's own declared strength
scale, not the Mission Assurance Levels.
The deployment question underneath is what can be centralized and what must live at each boundary. The PDP centralizes: one decision service (or a small fleet) serves every PEP. The PEPs cannot, because a permit is only as good as its placement:
| Boundary | Who runs the PEP | Centralize? | What it can gate |
| --- | --- | --- | --- |
| Resource Server or SaaS API | The resource's own authorization layer | No, one per resource | The strongest placement: Mission permit *and* object-level resource policy at the point of use |
| API gateway or egress proxy | The platform team | Yes, one gate fronting many resources | HTTP egress and the APIs it fronts, with parameter binding for what the gateway can see |
| MCP server | The tool host | Yes, per tool host | Every consequential `tools/call` and its arguments |
| Agent harness or orchestrator | The runtime team | No, local to the runtime | Local side effects no network gate ever sees: file writes, shell, spawn, resume |
| Privileged tool broker | The mediating PEP under [mediated custody](#the-high-assurance-level) | Yes, for the mediated classes | High-consequence actions a compromised agent must not reach directly, because the broker holds the sender-constraint key |
A real deployment mixes rows, and the enforcement-scope statement records which rows it actually runs.
# Closing the TOCTOU gap with parameter binding
A permit for an *operation* does not authorize arbitrary *parameter values*. If the PDP permits "write to the ledger" and the agent then changes the amount, the permit must not still hold. This is the time-of-check-to-time-of-use (TOCTOU) gap, and parameter binding closes it.
For consequential writes and the high-consequence classes, the PDP binds its permit to the normalized action parameters through a `parameter_digest`: the SHA-256 of the [RFC 8785 JCS](https://datatracker.ietf.org/doc/html/rfc8785) serialization of the normalized parameter object, in the issuance profile's encoded form. The permit also binds the Mission reference, the token issuer when available, the token audience, `sub`, `client_id`, actor context, sender-constraint key, action, resource, the authorizing entry (or an entry digest), the policy-view version, and a tight lifetime control.
The executing PEP, not an upstream component, recomputes the digest against the parameters it is about to use, immediately before acting, and verifies every binding. A mismatch refuses. The permit does not authorize the changed parameters. Two consequences follow:
- A permit cannot be **replayed for a different request**, because the digest mismatches.
- A permit cannot be reused **across boundaries**. Bound to a specific audience, resource, tenant, and operation, a decision for one is not reusable at another, which is the confused-deputy defense.
The lifetime control scales with risk. A reversible write may use a single-use decision identifier *or* a short window plus an idempotency key. An irreversible action, external commitment, or privileged administration MUST use a **single-use** decision identifier (a validity window alone does not bound how many times such a permit executes), and consumed identifiers are recorded so any second presentation fails closed. A single-use identifier bounds executions of one permit, not permits for one action, so for a non-idempotent operation in these classes the deployment MUST also define an idempotency key: a re-permit of the same normalized action under the same key is refused, or routed to a fresh action-bound approval, while the prior outcome is unresolved or completed. And these classes MUST carry an execution lease or a published maximum execution duration, so run-to-completion is bounded too. Whether an operation supports idempotency, whether it is reversible, and what lease it needs are resource-declared operation semantics, and the AuthZEN profile's Operation Profiles are where the resource states them so the PDP does not guess.
Two limits bound what the digest can promise. The binding is only as strong as canonicalization: where a resource resolves material semantics after receipt, the descriptor the PEP hashes is not the descriptor the resource executes, and the binding is to a phrasing rather than an action. And a single-use identifier guarantees single consumption, not single effect. The repair for both moves the binding to the effect: a resource-prepared descriptor authorized and committed exactly once, or a mediating handler that is the transaction's system of record.
Consequential reads do not require a digest by default. But a binding floor applies. A read whose parameters select a cross-tenant or cross-audience scope, request a bulk or export-like result, or choose the returned fields or destination MUST bind those parameters. An export is not an ordinary read.
> **The running example.** Take the [handbook's running example](/notes/mission-based-authorization-field-reference/), the *Prepare the Q3 board packet* Mission. A `query_financials` read against `finance.example.com` is a consequential read. The PDP permits it against the live Mission, no digest required. A `notify_reviewer` send against `workflow.example.com` is consequential and externally visible, so its permit is bound to the concrete recipient, the `audit-committee` group named in the entry's `constraints`. Ask to notify a target *outside* `audit-committee` and the action is outside the Authority Set's constraint. The PDP refuses (`parameter_violation`) and the PEP fails closed. The interpretation happened at consent time. The gate only checks the concrete action against it.
# Metering, and fail-closed on everything else
A Mission can carry cumulative consumption bounds (`max_budget`, `max_calls`, `max_duration`, `max_egress_volume`), and the issuance layer cannot enforce them because it counts derivations, not actions. The bounds and their metering mechanics are defined by the experimental [Mission Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) companion, while the runtime profile keeps the fail-closed posture: an unknown or unmetered bound on an applicable entry refuses. Under the companion, for each consequential action the PDP performs an atomic reserve-or-charge against the remaining balance, increments the named call counter, or accumulates duration, and refuses when a bound would be exceeded.
Metering is a two-phase exchange, not a single decision-time act. For an action of unknown length the PDP reserves a bounded maximum or issues a duration lease, and after execution the PEP reports the measured outcome so the PDP can commit actual use and release the unused reservation. The Execution Evidence record (below) is that commit-or-release signal, keyed to the permit's `decision_id`. For irreversible actions and external commitments, a deployment must define whether metering is reserved before execution and committed after success, or committed up front. The hardest moment in the exchange is not the check but the timeout after it, when the permit is spent, the resource has not answered, and committedness is unknowable. Evidence records that outcome as unknown rather than guessed, and the unknown has an owner: a named reconciliation against resource state with a deadline, because an unknown nobody owns becomes a committed effect nobody recorded. Reconciliation can resolve an unknown to committed or failed. What it can never do is admit a new effect under a Mission that has since gone non-active.
The topology is part of the claim. Under a **single serializing PDP** the check-and-decrement is atomic and the bound is exact. Under **distributed PDPs** an exact global counter is a distributed-counting problem. Such a deployment must publish the consistency bound it actually operates under (per-PDP sub-budgets, a bounded reconciliation window) and must not advertise exactness it cannot meet. Cumulative-bounds enforcement is a newer model, which is why the companion is experimental, with short expiries and per-action constraint checks as the stable path.
Enforcement is only meaningful if failure is bounded, so the spec fixes the failure behavior. The pattern is uniform. **For consequential actions, when the answer is in doubt, refuse.** The rows below are the decisive excerpt. The draft's failure-mode table is longer (a missing `mission` claim, an unsupported authorization-detail type, an out-of-authority capability identity, and a Resource policy refusal all refuse the same way).
| Condition | Required behavior |
|---|---|
| Token validation fails (including sender-constraint check) | Refuse before runtime evaluation |
| PEPโPDP channel authentication or integrity fails | Fail closed |
| Mission state cannot be established within the staleness bound | Fail closed for consequential actions |
| PDP unreachable | Fail closed for consequential actions. Do not proceed on cached permits past the window |
| Mission not `active` | Refuse |
| Unknown or unmetered constraint on the applicable entry | Refuse |
| `parameter_digest` mismatch at the executing PEP | Refuse |
| Re-presentation of a consumed single-use decision identifier | Refuse |
| Request would broaden the Mission's authority | Refuse (expansion is out of scope here) |
One boundary of the permit deserves its own statement: it binds the request, not the world. `parameter_digest` closes
the gap between decision and execution for the request's own fields,
and it cannot freeze the target: a document can be revised, a record
reclassified, a query can return a different set by the time the
action lands. For high-consequence classes, treat that as a named
residual. The permit's tight lifetime is the bound, a retry
re-evaluates rather than replaying a stale permit, and where the
resource exposes versions or content digests, bind them as request
parameters so the `parameter_digest` carries them. Committing resource
version, policy version, and decision time in the permit itself is a
candidate for the high-assurance end of
[what the community still has to standardize](/notes/adopting-mission-bound-authorization/#what-the-community-still-has-to-standardize).
Every refusal, and every permit, produces a runtime evidence record sufficient to reconstruct which path produced it.
# Fail-closed and active freshness
The "Mission not `active`" row above carries the most weight, and it has a subtlety the high-consequence classes turn into a hard requirement. A token alone cannot tell the PDP the Mission's *current* state. The token was minted at approval time. So the deployment must define a **Mission state source** it trusts, and the PDP must refuse a consequential action when it cannot establish, within a published staleness bound, that the Mission is `active`.
A permit is a **lease, not a standing grant**. Token TTL, cached Mission status, and policy views are all leases on Mission authority, each valid for a bounded window before it must be refreshed against the state source or fail closed. The staleness bound each enforcement scope publishes carries a latency consequence it must publish with it: for a PDP-gated class, a revocation takes effect, worst case, after the staleness bound plus the permit validity window plus the class's execution bound. Token lifetime is the bound only for paths outside PDP gating.
Read that as a dial, not a doctrine, because most estates will start at the coarse end and should be met there. Short-lived tokens minted under state-gated issuance are a legitimate setting for the classes below high-consequence: the resource validates tokens exactly as it does today, no PDP call, no Mission awareness, and revocation still reaches it within the token lifetime, because a non-`active` Mission derives and refreshes nothing. A legacy resource that will never evaluate Mission state is served that way, or fronted by a gateway or MCP PEP that carries the per-action check on its behalf. The [Reference's revocation matrix](/notes/mission-based-authorization-field-reference/#when-revocation-bites) prices every setting on the dial, and [Adopting carries the deployment shapes](/notes/adopting-mission-bound-authorization/#not-every-resource-checks-mission-state). What the dial never permits is pretending: the high-consequence classes require an active freshness mechanism, and a path whose only bound is token lifetime claims exactly that bound, in writing.
For the high-consequence classes the spec is categorical. The state source MUST be an *active freshness mechanism* that can reflect a revocation within the staleness bound: issuer token introspection or the Mission Status surface as the fail-closed source, with Lifecycle Signals as push acceleration that falls back to polling when the stream goes quiet ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)).
> Token-lifetime expiry alone is **not** an acceptable state source for the high-consequence classes. It bounds staleness only by the token lifetime, so a revoked Mission keeps deriving consequence until tokens age out. That is precisely the ambient-authority gap this profile exists to close.
This is where the handbook-wide rule "only `active` permits reliance" stops being a governance statement and becomes a wire requirement. A suspended or revoked Mission produces a runtime denial regardless of policy, and for the actions that matter most, the deployment must be able to *learn* the revocation fast enough to act on it.
# The high-assurance level
Everything above bounds what *any* agent can do. The high-assurance level raises the bar for a *compromised* agent, one that has been prompt-injected or taken over but still presents correct credentials. The issuance and runtime gates do not make the agent trustworthy. They bound what it can do. This level shrinks that bound further by not letting the agent hold the authority whose misuse is unacceptable, and by requiring a fresh independent approval for the highest-consequence acts.
**Mediated custody.** Mission-bound tokens are sender-constrained. Whoever holds the private key the token's `cnf` binds can present the token. Mediated execution uses this. For the classes a deployment mediates, the **PEP holds the sender-constraint private key, not the agent.** The agent cannot present the credential directly. To act, it asks the mediating PEP, which runs the decision and only then uses the key. No new token type or wire protocol, just a custody-and-placement property of the existing key. The token is unchanged, the agent remains the principal of record (`client_id` still attributes the action), and no `act` entry is added. Two properties follow: a credential exfiltrated from a compromised agent is unusable without the key, and a compromised agent cannot reach a mediated action without passing the per-action check, because it never holds a usable credential for that class. This depends on the agent having *no unmediated path* to the resource, which the agent harness establishes ([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)).
**Action-bound approval.** The Mission's approval event consents to the *task* and its authority bound. It does not consent to a specific action's concrete parameters. For the high-consequence classes a deployment can require a second, *action-bound* approval (and the Intent can demand it per entry, via the core's `requires_action_approval` Common Constraint): a fresh approval bound to the concrete action and parameters the PEP is about to permit, obtained from an independent approver, never self-issued by the agent. Because it is bound to the parameters, it is reverified under the same TOCTOU rules. A parameter change after approval invalidates it. It is decision input, not a bearer grant. The runtime decision stays authoritative, and persisting authority beyond the single action is a Mission Expansion ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)), not a property of the approval.
"Protects against agent compromise" is a verifiable claim, not a label. A deployment claims **agent-compromise-resistant enforcement** only when, for the high-consequence classes, all five hold:
1. The sender-constraint key sits in the mediating PEP.
2. Governed work runs under a conforming harness whose published execution-environment scope statement covers the mediated classes, so there is no unmediated path.
3. Each such action requires an action-bound approval.
4. The disclosure the approver decides on is rendered from the bound parameters by a component isolated from the agent, never composed by the agent.
5. The state source is an active freshness mechanism.
Active freshness is already a MUST in the base profile for these classes, and the harness condition makes its no-unmediated-path rule a MUST. Mediated custody and action-bound approval are the claim's upgrades from SHOULD to MUST, and the agent-isolated approval rendering is stated only by this claim, so a deployment that skips any of them may still claim base runtime conformance, but not this.
# The decision contract and its AuthZEN binding
The runtime profile specifies enforcement *invariants, not a wire protocol*. It deliberately does not standardize a PDP decision API, an enforcement-scope discovery format, or a Mission Status endpoint, and it defines the **Mission Receipt**, the portable per-action evidence of a material action taken under a Mission, only by its name and minimum binding, deferring the portable schema. It defines what a deployment MUST satisfy when it claims runtime Mission enforcement, and nothing about the bytes on the PEPโPDP wire.
That keeps the contract substrate-independent. But it means two conforming deployments do not thereby interoperate at the PEPโPDP boundary. The interoperable wire surface is supplied by a separate document. The **[AuthZEN Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html)** binds the abstract contract to the [OpenID AuthZEN Authorization API](https://openid.net/specs/authorization-api-1_0-final.html). The division of labor is strict and worth stating plainly:
> The runtime profile owns the enforcement semantics. The AuthZEN profile binds the contract to a wire. It does not restate the semantics. It carries only the binding deltas.
Those deltas are:
- **Mapping the inputs onto the AuthZEN envelope.** The Mission, actor, credential, parameters, audience, and freshness ride in AuthZEN's open-ended `context` object as `context.mission`, `context.actor`, and so on. A subtlety the profile is careful about: the approved entry's `resource` (the audience URI) is matched against `context.audience`, *not* against AuthZEN's own `resource` member, which carries the finer-grained object identity used only for resource-policy evaluation.
- **Two concrete evidence objects.** The runtime profile requires per-action evidence. The AuthZEN profile gives it a shape. **Decision Evidence** is emitted by the PDP: what it evaluated, integrity-protected with a `jws-compact` envelope, chained back to the Mission via `authority_hash` (and `intent_hash` only when the PDP has direct Mission-record access). The `intent_hash` rides in neither the `mission` claim nor introspection, so most deployments anchor on `authority_hash` alone. **Execution Evidence** is emitted by the PEP *after* the outcome is known, linked by `decision_id`, recording whether the permitted action was attempted, completed, failed, or suppressed. Decision (and refusal) evidence is required for every consequential action. The matching Execution Evidence record is a MUST for the high-consequence classes, where it is the basis for one-to-one reconciliation against the permit, and for any duration-metered action, where its `measured_duration` is the settlement input that commits actual use and releases the reservation. The split matters. Decision Evidence is not proof an action occurred. An auditor must treat orphaned Decision Evidence (a permit with no matching Execution Evidence inside the deployment's published reconciliation window) as undetermined-outcome or, per deployment policy, as action-attempted, and never as proof of action.
- **Carrying denials in an AuthZEN decision.** A runtime denial is a *successful evaluation*, so it is `decision: false` with a `denial_reason` from a closed set (`out_of_authority`, `mission_inactive`, `stale_state`, `parameter_violation`, `quota_exceeded`, `capability_drift`, and the rest), not a transport error. An `out_of_authority` or `action_approval_required` denial MAY be marked **requestable**, composing with the [AuthZEN Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html) so the agent can start narrow and request the authority it discovers it needs. If granted durably, that becomes a Mission Expansion ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)). For an agent in an open world, this is the front door: a tool or resource discovered mid-task arrives as a requestable denial, the beginning of the [discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem), not the end of the task.
Bringing the binding in this way is the point. The runtime profile is the architecture, the AuthZEN profile is the interoperability layer, and neither duplicates the other.
# The lethal trifecta at execution time
The handbook treats the agent lethal trifecta (private-data access, untrusted-content ingestion, and external-write authority in one loop) as a first-order design constraint, and this section is its canonical treatment at the wire ([Splitting the Lethal Trifecta](/notes/splitting-the-lethal-trifecta/), in the validation chapter, carries the whole story against the threat model in one place). The governance layers give the common object: the approved task, its bounds, its derivation gate, its audit binding. That is necessary but not sufficient. The runtime layer is what keeps the bundle split *at execution time*: private reads, untrusted inputs, and external writes stay separately typed, separately evaluated, and separately auditable under the same canonical Mission.
The defense against the dangerous leg (exfiltration) is architectural, not a claim that the agent is injection-proof. External communication is a consequential action, so every attempt is checked against the Authority Set, bound to parameters, metered, and (under mediated custody) made unreachable to an agent that does not hold the egress credential. The injected agent cannot widen the authority the gate checks against.
The profile names two limits. First, the defense is exactly as strong as PEP-placement completeness. Every channel an agent runtime offers (DNS, logs, error strings, a write another process reads) must be mediated, and the profile gates the channels routed through a PEP but cannot prove a deployment enumerated them all. Second, it provides no information-flow control. Each action is evaluated in isolation, so a sequence of individually-authorized steps can compose into an exfiltration no single check catches. A coarse session-level mitigation (downgrading egress authority once untrusted content has entered a session) lives at the [harness layer](/notes/the-agent-runtime-and-audit/), and raises the bar without being information-flow control. The profile names the composite as a claim: **trifecta containment** holds only when least exposure is applied, the harness taint rule is enforced rather than advisory (under its default-taint polarity, a parameter in a tainted session that cannot be affirmatively traced to a trusted source stays tainted, so paraphrase sheds nothing), and the external-communication and external-commitment classes are fully mediated, with the egress channels enumerated. Where the decision binding carries taint context, the PDP enforces the rule and fails closed when the context is missing. The generalization of that discipline, bounding what the agent may *see* as deliberately as what it may do, is [Least Exposure Is Broader Than Least Privilege](/notes/least-exposure-is-broader-than-least-privilege/).
The same honesty applies to the structural-versus-semantic line. Everything this profile enforces is structural: typed actions, bound parameters, metered consumption, current state. None of it can judge whether an in-bounds email body leaks intellectual property. A deployment that wants semantic evaluation in the loop has a place to put it: the decision contract's `context` carries deployment-defined inputs, so DLP verdicts, content classifications, or an LLM judge's assessment of whether an action's content fits the Mission can ride into the PDP alongside the structural checks. The profile fixes how one composes: the verdict enters the decision as Resource policy and only ever narrows, and its rubric is the recorded Mission Intent, verifiable against `intent_hash`, not a free-floating policy. The bill is real: content evaluation runs per action, and a judge model reading attacker-influenced content is itself a prompt-injection surface. The structural floor is what the profile can promise deterministically. Semantic controls layer above it at the deployment's option, raise the bar, and inherit none of its determinism, which is why the classes where content is the harm belong under action-bound approval rather than under a smarter policy engine.
One tension in that assignment deserves its own paragraph, because two of this handbook's commitments collide on it. For an agent whose primary job is external communication, routing the content-harm classes through action-bound approval degenerates to human-per-send, the exact posture [the fatigue budget](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget) rejects as a security boundary. What the model actually offers that agent is narrower structure, not more approvals: recipients and destinations bound at admission so the structural check carries the volume, the taint downgrade for the sessions that touched untrusted content, content verdicts composing as narrowing inputs, and action-bound approval spent only where content is the harm and the volume is low. One caveat rides with the recipient bound: an entry naming `audit-committee` binds the group's identity, not its membership, and membership is state another system owns. A deployment that pins enumerated recipients at derivation for its high-consequence classes buys the bound it thinks it has. One that resolves the group at send time should say so in its enforcement-scope statement, because the residual is real.
# Why this is the center of gravity
Read the handbook and you will find more posts about the governance envelope than about runtime. That is not a statement of priority. The governance envelope projects onto many wire surfaces (issuance, consent evidence, status, signals, expansion, delegation, audit), so it takes more pages. The runtime contract is comparatively compact: one PDP interface, one evidence shape, one set of invariants. Page count is the wrong axis to read priority from.
> For any deployment whose agents touch consequential writes or external side effects, the runtime layer is doing the load-bearing safety work, not the governance envelope.
A Mission you cannot enforce at the point of use is an audit trail, not a control. The other five layers exist to make this one trustworthy, and the next section names what each contributes. Take any one away and the runtime gate gets weaker. That is what it means to be the center of gravity.
# Where this sits in the handbook
This is the **Enforcement** step. The concrete answer is two drafts: the [Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html) profile (the invariants) and the [AuthZEN Profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html) (the wire binding).
- **[The Mission](/notes/the-mission-is-the-missing-abstraction/)** (the architecture chapter). The durable approved task and Authority Set the PDP evaluates against.
- **Part 1: [From a request to an approved Mission](/notes/from-a-request-to-an-approved-mission/).** Approval-time integrity: the consent that anchors what the PDP enforces.
- **Part 2: [Mission-bound authority](/notes/mission-bound-authority/).** The instance identity and `act` chain the runtime actor check consumes.
- **Part 4: [Lifecycle and change](/notes/mission-lifecycle-and-change/).** Status, signals, and expansion: the active freshness source and the durable home for an approved escalation.
- **Part 5: [The agent runtime and audit](/notes/the-agent-runtime-and-audit/).** The mediated execution environment this layer relies on, and tamper-evident evidence.
[Least-Privilege MCP Tool Calls Need a Mission](/notes/least-privilege-mcp-tool-calls-need-a-mission/) walks this enforcement boundary at the MCP tool-call layer.
If you adopt one part beyond the issuance core, adopt this one. Containment is where the other four laws become enforceable, and the permit is where the handbook's whole argument cashes out: not that the Mission exists, but that every consequential action still belongs to it.
---
# Mission-Bound Authority: Instances, Actors, and Delegation
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-authority/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-authority.md
{{< tldr >}}
- **The question.** [AI Agent Authentication and Authorization](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) establishes who the agent is and how it authenticates. This part answers how Mission authority attaches to that identity, and how it flows *down* when agents spawn agents without becoming inherited, ambient authority. A child gets explicit, narrower authority that dies with its parent, and a child is *never* created by session ancestry alone.
- **What lives in this part.** [How authority rides the token](#the-projection-how-authority-rides-the-token) and [which instance is acting](#which-instance-is-acting), the bridge from agent identity to Mission authority, and [the swarm](#the-swarm-multiplication-not-delegation), where one Deployment multiplies into many instances. [Crossing authorization domains](#crossing-authorization-domains), where one Mission is honored by an Authorization Server that did not issue it. Then fan-out: the [authority-by-ancestry threat](#fan-out-the-threat-of-authority-by-ancestry), the [act-chain baseline](#what-the-core-already-gives-you) the issuance core gives you, [Mission Child Delegation](#mechanism-1-the-child-mission) for a sub-agent that needs its own durable, revocable handle, [Offline Attenuation](#mechanism-2-offline-attenuation) for fan-out at scale, and [the tradeoff](#choosing-which-to-use) between them.
- **The card analogy.** A sub-agent gets its own card with a lower limit, never a loan of yours. [The Contractor Gets Their Own Card](/notes/the-contractor-gets-their-own-card/) is this part in card terms. ([Where the analogy breaks](/notes/the-contractor-gets-their-own-card/#where-the-analogy-breaks).)
- **The laws.** This part operationalizes [Attribution and Narrowing (Laws 2 and 3)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): attribution survives every hop of instances and delegates, and authority only narrows as work fans out.
- **Specs (editor's copies).** [Mission-Bound Authorization for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html) (the projection and act chain), [Mission Cross-Domain Projection](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-cross-domain.html), [Mission Child Delegation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-child-delegation.html), and [Mission Offline Attenuation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-attenuation.html), on the identity substrate of the [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/), [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/), and [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/).
**Reading path.** ~18 minutes start to finish. Read in order, or jump to [fan-out](#fan-out-the-threat-of-authority-by-ancestry) if delegation is your question.
{{< /tldr >}}
# Overview
[The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) defined the Mission and [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/) made its approval trustworthy. This part is where the approved Mission meets the agent identity stack: how authority is projected into tokens, how those tokens stay attributable to a specific agent instance acting for a specific user, and how authority flows down when one agent hands work to another.
The order matters for a reader arriving from the [AI agent auth best practices](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/). That work establishes the agent's identity, credentials, and delegated user authority, and it expects the mission to have been translated into authorization requirements before the agent requests authorization. The translation happened in [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/). What this part adds is the binding: the authority that came out of the approval, attached to the identity that draft established, in a form every downstream party can verify.
| The control at a glance | |
| --- | --- |
| **Minimum useful version** | Every derived token carries the `mission` claim, sender-constrained, and delegated work extends the `act` chain through token exchange. No sub-agent acts on inherited session credentials |
| **What it prevents** | Authority by ancestry: sub-agents exercising the parent's credential with no grant, no attribution, and no separate kill |
| **What it does not prevent** | Misuse inside the delegated subset by a legitimate delegate |
| **Operational owner** | The Authorization Server owns the exchange and the subset checks. The agent platform owns instance identity |
| **Evidence emitted** | The actor chain on every token and decision, so audit answers who acted for whom on each hop |
| **Maturity** | The projection and act chain are the core. Child Delegation and Cross-Domain Projection are advanced. Offline Attenuation is experimental |
# The projection: how authority rides the token
The Mission lives at the Authorization Server. What travels is a projection. Every token derived under a Mission carries a `mission` claim with three members: `id`, `issuer`, and `authority_hash`. The `id` and `issuer` say which governance record this credential serves and who holds it. The `authority_hash` commits the exact Authority Set the Approver saw, so a Resource Server can verify that the authority the token carries is a subset of what was approved without fetching the record. The token's `authorization_details` carry the derived authority itself, and issuance is gated on Mission state: a Mission that is not `active` derives nothing, which is the possession-independent kill switch.
Three properties make the projection safe to rely on:
- **The subset rule.** Every derivation, refresh, exchange, and delegation yields authority that is equal to or narrower than the Mission's Authority Set. Resources may only narrow (exact match by default, with an opt-in `prefix` containment form), actions may only shrink, constraints may only tighten. Nothing derived ever exceeds what was approved.
- **Sender constraint.** Mission-bound tokens are bound to a key with DPoP or mutual TLS, so a stolen token is useless off the instance that earned it. The high-assurance level in [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/) builds on exactly this property.
- **Expiry capping.** A derived token's `exp` never exceeds the Mission's `expires_at`, so the Mission's clock transitively bounds every credential under it.
The [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html) is the normative detail, and this projection is the anchor of the reference security architecture: it depends only on ratified OAuth mechanics.
# Which instance is acting
A projection is only as attributable as the identity it is bound to, and the default OAuth answer is not good enough for agent platforms. A platform runs thousands of concurrent agent instances under one `client_id`, so every session collapses into one identity at the Resource Server. Per-agent policy, audit attribution, incident response, and abuse containment all defeat themselves at that grain.
None of what follows is required for the Runtime-Enforced minimum. A single-instance deployment with no delegation enforces Missions with the core alone, and the core confines its one reference to this substrate, the Actor Profile, to its OPTIONAL delegation capability. The substrate becomes necessary the moment work is delegated or instances multiply, which for an agent platform is immediately. Three composable profiles fix the attribution, and they are the identity substrate this chapter assumes rather than redefines:
- The [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) standardizes the RFC 8693 `act` chain across assertion grants and JWT access tokens, so the decision and the audit record both see who is acting for whom, at every hop.
- The [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) identifies the specific client instance behind the shared `client_id`, with its own key, so instance-level policy and revocation become possible.
- The [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/) adds what agents specifically need: an attester-minted instance identifier that survives key rotation, and provenance claims for the platform, model, and runtime environment.
The composition is the point. One Mission-bound token can now say: this authority derives from *this approved task* (`mission`), exercised by *this attested agent instance* (instance identifier and provenance), acting for *this user* (`sub`) through *this delegation chain* (`act`). Identity answers who. The Mission answers what for, and until when. The runtime layer ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)) evaluates all of it together on every consequential action.
# The swarm: multiplication, not delegation
That composition also settles the swarm. When one Agent Deployment runs many instances against the same Mission, nothing is delegated: the same principal is multiplied. Authorization binds at class grain, where the Intent's OPTIONAL `controls.agent_deployment` pins the Deployment and the `client_id` is its OAuth projection, and attribution stays at instance grain, because every instance presents its own assertion and the runtime profile forbids a sender-constraint key shared across a client's instances.
Late binding is attestation: an instance spun up mid-task joins by proving it is an instance of the pinned Deployment. The Mission's own machinery bounds the swarm, `max_derivations` as the fan-out ceiling and consumption bounds shared at Mission grain, and the kills stay separable: revoke one instance's credentials, deregister the Deployment, or terminate the Mission and end every instance's authority at once.
The rule that keeps this honest is that the class is an authorization subject, never an attribution subject. The moment instances become indistinguishable in the record, the shared service account is back.
Two hard problems remain once the token is attributable. The authority has to cross authorization domains without losing its binding, and it has to flow down to delegates without becoming ambient. The rest of this part takes them in turn.
# Crossing authorization domains
The running example never fit inside one authorization domain. The finance, docs, and workflow systems can each sit behind their own Authorization Server, and a Mission held at one AS is worthless at the others unless something carries it across. The base profile deliberately stops at the issuing AS's edge. [Mission Cross-Domain Projection](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-cross-domain.html) is the optional capability that crosses it, in exactly one hop. The core now states this capability's conformance bar self-contained, citing the companion informatively as the interoperable mechanism.
The model preserves everything the projection already established. The Mission Issuer issues a **cross-domain grant** under the OAuth identity chaining architecture, with the [Identity Assertion Authorization Grant](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) (ID-JAG) as the recommended profile. The grant is short-lived (capped at 300 seconds), sender-constrained, and audience-scoped, carrying only the Authority Set entries relevant to the target domain under the same subset rule that governs every other derivation. The Resource AS validates the grant and mints its own local tokens, and the `mission` claim rides through unchanged. One Mission, one `mission.id` and `mission.issuer`, joining issuance, enforcement, and audit across every domain it touches.
The honesty is about revocation. Single-domain revocation is prompt, because the AS that issued a token also honors its revocation. Cross-domain is strictly weaker. The Mission Issuer can stop issuing new grants the moment a Mission is revoked, but it cannot reach a local token another domain already minted, so cross-domain revocation latency is the downstream token lifetime. That is why the draft requires Resource ASes to keep local Mission-bound tokens short-lived, why the grant lifetime is capped, and why the Status and Signals surfaces ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)) matter across domains. They are built for exactly the consumer that holds a `mission_id` and no token the issuer ever minted.
Revocation is not the only place the domain boundary bites, and the
failure modes are worth naming plainly. The downstream domain may not
speak the parent's authority vocabulary, so entries either translate
under a mapping both sides trust or do not cross at all. The
downstream AS cannot independently verify the upstream approval's
semantics, only the grant's signature and shape, so its trust in the
projection is trust in the issuing domain. Two domains can disagree
about what counts as narrower once constraints are contextual or
quantitative, which is the
[semantic-narrowing boundary](/notes/mission-based-authorization-field-reference/#the-mission-object-model)
the Reference names, and the safe resolution is conservative refusal.
And an action can be locally permitted while globally outside the
Mission, because the local PDP sees only the projected slice. Those
are the reasons the projection is one hop, audience-scoped, and
subset-ruled rather than a general federation mechanism, and the
[portability wager](/notes/the-convergence-and-the-wagers/#where-this-could-be-wrong)
is the honest bet underneath it.
In the suite's adoption order this is advanced: stable design to adopt when the use case arrives, with its dependencies tracked honestly. As of July 2026, the identity-chaining work it profiles is approved and in the RFC Editor queue, ID-JAG is a working-group document, and the profile should not advance ahead of that substrate. Single-domain Runtime-Enforced deployments are unaffected by it entirely.
# Fan-out: the threat of authority by ancestry
A real agent does not stay a single actor. It decomposes the task: a research sub-agent, an extraction worker, a reviewer, each with its own session, queue, and tool handles. Every one of them needs *some* authority to do its slice, and every one of them is a place where the user's approved task can quietly turn into more authority than the user ever saw. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) committed the rule that keeps this from happening. **Authority can only narrow.** This part makes that rule true across fan-out, across the moment one agent hands work to another.
Here is the failure mode it must prevent. A parent agent, running under an active Mission, spawns a sub-agent. The sub-agent makes a consequential call. Why was it allowed? Because it descended from the parent. It shares the parent's session, perhaps a cached tool connection, perhaps the parent's token sitting in shared memory.
That is **authority by ancestry**, and it is the primary threat the Child Delegation draft names. It is dangerous precisely because it is the *convenient* default. The harness already has the parent's credentials in hand, so handing them to a child is a one-liner. Nothing widened on paper. The child is "inside" an active Mission. And yet the user approved a board-packet task, not a board-packet task plus whatever every sub-agent the model decided to spawn can reach with the parent's full token.
> Session continuity is a runtime property. It can prove the sub-agent is part of the same execution. It cannot prove the sub-agent is authorized.
The rule, stated plainly: **a child actor needs explicit, narrower authority, not session ancestry.** A sub-agent handle is not a credential. Spawning is not delegation. Every mechanism below is a way to give a child *explicit* authority that is provably a subset of the parent's and that stops when the parent does, and to make that the only path, so the ancestry shortcut is never available.
# What the core already gives you
Before reaching for new machinery, note what the [issuance core](/notes/the-mission-is-the-missing-abstraction/) already covers. The core supports **delegated Mission-bound tokens**. Authority narrows down a delegation chain, and the chain records actor context through RFC 8693 token exchange and the `act` claim. This is in-Mission delegation. It extends a single Mission's `act` chain to additional actors, bounded by each authority entry's per-entry `delegation` policy. No new Mission is created. The work is still exercised under the original Mission.
That is the right tool when the delegate does its work **within the lifetime and operational control of the delegating flow**: a synchronous hand-off, a downstream service call, a delegate that finishes before the parent moves on.
The two mechanisms in this part exist for the case the act chain does not cover: a sub-agent with a durable task of its own. A child with its own queue, its own background jobs, its own harness session, its own audit lifecycle (work that *outlives the call frame* that spawned it). For that, the child needs more than an entry on someone else's chain. It needs its own authority handle, or its own narrowed token, depending on the tradeoff you are making.
```mermaid
flowchart TB
M[("Parent Mission
active")]
M --> AC["Act-chain delegation
(issuance core)
same Mission, extra actor"]
M --> CM["Child Mission
AS-mediated
own mission_id + lifecycle"]
M --> OA["Offline attenuation
holder-minted
same mission claim, no new Mission"]
AC -.->|"delegate works inside
the delegating flow"| U1[Within parent's lifetime]
CM -.->|"sub-agent needs its own
durable, revocable handle"| U2[Separate lifecycle]
OA -.->|"AS round-trip is the
fan-out bottleneck"| U3[Scale and latency]
```
The act chain is the baseline. The rest of this part is the two new mechanisms. They answer the same question with different cost profiles.
# Mechanism 1: the Child Mission
A **Child Mission** ([Mission Child Delegation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-child-delegation.html)) is the answer when a sub-agent needs its own durable, separately revocable Mission. It is an ordinary Mission under the issuance profile with two additions: it is created under a *parent grant* rather than a first-party approval, and its record and tokens carry a `parent` member that records lineage. It has its own `mission_id`, its own actor identity, its own lifecycle, and its own `act` chain that restarts at depth zero. But it cannot outlive, out-broaden, or escape its parent.
Five properties make that true, and they are worth holding together because each closes a different gap.
**Explicit creation, never inheritance.** A Child Mission is created by submitting a Mission Intent through Pushed Authorization Requests, exactly like a first-party Mission, plus three required things: the `parent` Mission identifier, a `parent_token` (a refresh token or other grant bound to the parent Mission), and a `child_actor` identifying who will hold the child. Critically, the Authorization Server resolves the parent **from the `parent_token`, not from the `parent` identifier**. The identifier is a cross-check and audit value only. Naming a parent you do not control gets you nothing. You have to *present a grant* for it. This is what makes "child by session ancestry" impossible. There is no path to a Child Mission that does not run through an explicit, authenticated, back-channel request carrying a real parent grant.
**Subset authority.** Every child authority entry must be a subset of a parent entry under the same narrow-only subset rule the core uses for any derived token. The child entry's `resource` must be contained in a parent entry's `resource` (equal by default, or narrowed under the core's opt-in `prefix` containment), its actions must be a subset, and its constraints may only tighten, so an entry may match the parent exactly. The child cannot add a resource, an action, or a constraint relaxation the parent lacks. The per-entry delegation policy must not be broader either: `max_depth` no greater, `allowed_delegates` no wider. "Strict" in the strict-subset rule means no relaxation anywhere, not inequality. The Authorization Server computes the child's `authority_hash` over the *child's* Authority Set. That hash is the authority commitment a Resource Server enforces against. The `parent` member's `authority_hash` is lineage data, not authority. It records which parent commitment the child was derived under, for audit.
**Expiry no later than the parent.** The child's `expires_at` must not be later than the parent's. Because Mission expiry transitively caps every derived token's `exp`, a child can never hand out a token that outlives the parent Mission's clock.
**Fan-out controls.** This is the non-obvious one. Depth limits do not control breadth. A parent can authorize *many* children at the same depth, each a perfect subset, and the aggregate still amplifies authority. Twenty read-only sub-agents are a different risk than one. So child creation is off by default. The on-switch is a `children` object inside the parent entry's per-entry `delegation` member, and an entry whose delegation carries no `children` permits no child at all (the denial reason is `delegation_not_permitted`). The `children` object then carries the controls the issuer must enforce: a cap on concurrently non-terminal children (`max_children`), a constraint on which actors may receive them (`allowed_child_actors`), a generation limit (`max_child_depth`, default 1), and a policy evaluated before each creation (`child_creation_policy`). An issuer that cannot enforce a control an entry carries refuses creation. Breadth is bounded explicitly, not as a side effect of depth.
**Cascade revocation.** A Child Mission depends on its parent, and "dependent" means every transitive descendant. Any transition of the parent to a non-active state is the cascade trigger. Terminal triggers (parent `revoked`, `expired`, `completed`, `superseded`, or itself `cascaded`) stop new derivation under dependent children and drive each child to a terminal `cascaded` state (distinct from `revoked` and `expired` so audit can tell a cascade-terminated child from a directly terminated one). The issuer commits that terminal transition whatever the cascade mode. The mode governs only what consumers must verify in the interim. Cascade is transitive. The children of a `cascaded` parent cascade in turn, in generation order. And derivation gates on the whole lineage: a child derives nothing while *any* ancestor, not just its immediate parent, is non-active, so a cascade in progress opens no window. The one reversible trigger is `suspended`. Derivation stops while the parent is paused, but the children are *not* terminated, and they return to their prior state when the parent resumes. The through-line: **a child dies with its parent.**
> **The running example.** The parent Mission for "prepare the Q3 board packet" ([Reference and Glossary](/notes/mission-based-authorization-field-reference/)) holds `query_financials`, `create_doc`, and `notify_reviewer`. The orchestrator spawns a sub-agent just to gather the financials, under a Child Mission whose Authority Set is a strict subset of the parent's: `query_financials` only (no `create_doc`, no `notify_reviewer`), with `expires_at` no later than the parent's and the parent's Mission `id` recorded in the child's `parent` member. The sub-agent has exactly the read it needs and nothing else. It cannot write the doc or notify anyone. When the board meeting is cancelled and the parent Mission is revoked, the child is cascade-revoked to the terminal `cascaded` state, fails its next runtime state check, and the sub-agent stops too.
```mermaid
flowchart TB
PA["Parent agent / harness"]
AS["Authorization Server
(Mission Issuer)"]
V["Resolve parent from grant,
verify active + delegation,
verify strict subset,
apply fan-out controls"]
CM[("Child Mission
own mission_id
parent lineage
cascade mode")]
TK["Mission-bound token
(carries parent member)"]
PA -->|"1. PAR: child Intent
+ parent grant"| AS
AS --> V
V -->|"2. approval / policy
adjudication"| CM
CM -->|"3. derive child token"| TK
```
One clean separation worth stating: a Child Mission is **not** Mission Expansion. Expansion ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)) creates a *successor* Mission with *broader* authority by fresh approval. A Child Mission creates a *dependent* Mission with *narrower* authority. They move in opposite directions and must never be conflated. (A `superseded` parent, in fact, is a *terminal* cascade trigger. The successor carries a freshly derived Authority Set, so a child that was a subset of the predecessor is not guaranteed to be a subset of the successor. Continuing child work requires explicit re-creation under a successor grant, which re-runs subset validation.)
# Mechanism 2: offline attenuation
The Child Mission puts the Authorization Server on the path of every delegation, which is exactly what you want when you want the issuer to *see* and *own* each one. But for deep sub-agent fan-out, the common agent topology, that makes the Authorization Server a per-delegation latency and availability dependency on the execution hot path. Every narrowing is a round-trip to the issuer.
**Offline attenuation** ([Mission Offline Attenuation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-attenuation.html)) removes the issuer from that path. It profiles [Attenuating Agent Tokens](https://datatracker.ietf.org/doc/draft-niyikiza-oauth-attenuating-agent-tokens/) so that a Mission-bound token holder can mint a narrower child token **offline, with no Authorization Server contact**. The holder selects a narrower tool and constraint set, increments the delegation depth, signs the child with the key the parent token's `cnf` binds, and commits to the parent by hash. The child is valid by its signature chain. (Per-entry delegation policy is enforced by shaping the root at issuance, the one moment the issuer is present in the chain: a non-delegable entry never rides a root that can delegate.)
One caveat the convenience hides: offline minting is *unobserved* by the issuer. A `del_max_depth` on the chain caps how deep the narrowing can go, but there is no offline equivalent of the Child Mission's `max_children`. Nothing on this path bounds *breadth*. A holder can mint as many narrower siblings as it likes, unseen by the issuer, so breadth must be bounded by deployment policy rather than by the substrate.
The Mission binding is what makes this safe rather than a new way to leak bearer tokens, and the draft requires three things together:
**The `mission` claim rides the chain unchanged.** Every token in the chain, root and every offline-minted descendant, carries the *same* `id`, `issuer`, and `authority_hash` as the root. A child cannot re-bind to a different Mission or change the lineage anchor. A consumer that sees a link whose `mission` claim differs from the root's (or that omits it) must refuse the *whole* chain, not treat it as a narrower grant. Here `authority_hash` is purely a lineage anchor. It still commits the *root Mission's* Authority Set, while the child's real authority is its own carried, narrower constraints. (Contrast the Child Mission, where the child computes its own `authority_hash`. Offline children never do. The root's rides along.)
**The narrowing is verifiable from the carried chain.** Because each child carries its parent chain, a consumer holding only the leaf token and its chain can verify that the leaf is a subset of the root (checking signature linkage, capability monotonicity, and depth) *without* holding the Mission's full Authority Set. The narrowing proves itself.
**The kill switch is delivered by the runtime layer, not the token.** This is the point everything else rests on. The attenuation substrate defines **no revocation of its own**. Once minted, an offline child is valid until its `exp`, and no issuer can reach it. So the Mission kill switch is not automatic for offline children. It is delivered *only* by the [runtime enforcement layer](/notes/mission-bound-runtime-enforcement/). On every presentation of a token in the chain, regardless of action class, the consumer must establish that the chain's Mission is `active`, within the deployment's declared freshness bound, from a Mission state source, in addition to verifying the chain and the proof-of-possession. It fails closed when the state source is unreachable, and a cached chain is re-checked on every presentation. A revoked or expired Mission causes refusal of *every* token in the chain, regardless of any child's own `exp`.
> When `alice` revokes the Mission, the next action fails the state check and the whole chain stops, even though no issuer ever saw the children and their tokens have not expired.
This is why offline attenuation is **only safe on runtime-enforced paths**. It is not available to a deployment that relies on token lifetime alone. Without the runtime state check, an offline chain is ungoverned bearer authority until it ages out, which defeats the entire purpose of binding it to a Mission. A deployment must not accept Mission-bound attenuation tokens on a path that does not enforce current Mission state.
A residual risk survives even all three requirements: a compromised holder key. Whoever holds the parent token's confirmation key can mint *any* narrower child offline and unobserved. There is no issuer round-trip to deny. The only bounds are narrow-only (capability monotonicity means the attacker can never broaden authority) and the runtime layer re-checking Mission state on every action, so revoking the Mission still stops every child the compromised holder minted. The compromise can fan out narrower children, but it cannot widen authority or evade the kill switch.
```mermaid
flowchart LR
R["Root token
mission claim
cnf key, del_depth=0"]
C1["Child (offline)
same mission claim
narrower tools
par_hash, del_depth=1"]
G["Runtime PEP / gateway
verify chain + PoP"]
AS["Mission state source"]
R -->|holder mints, no AS contact| C1
C1 -->|present chain + PoP| G
G -->|"check Mission active
(the kill switch)"| AS
AS -->|state=active / revoked| G
```
# Choosing which to use
The zeroth question comes before the choice: is this delegation at all? A homogeneous swarm, many instances of the same Deployment executing the same Mission with the same authority, needs neither mechanism. That is [multiplication, not delegation](#the-swarm-multiplication-not-delegation): no `act` hop, no child, no attenuation, just per-instance tokens under the class pin. The mechanisms below are for the cases where a *different* principal acts or authority must *narrow* per delegate.
Both mechanisms narrow. Neither widens. The choice between them is a tradeoff between **central control** and **scale**, and the suite offers offline attenuation *alongside* Authorization-Server-mediated delegation, not instead of it.
| | Child Mission (AS-mediated) | Offline attenuation |
|---|---|---|
| **Who mints** | The Authorization Server, per delegation | The token holder, offline |
| **New Mission?** | Yes. Own `mission_id`, lifecycle, `act` chain | No. Same `mission` claim rides the chain |
| **Authority commitment** | Child's own `authority_hash` over its set | Root's `authority_hash`, as a lineage anchor |
| **Revocation** | Cascade revocation, committed at the issuer in every mode. The experimental `bounded_staleness` and `status_required` modes add interim consumer-side parent-state checks | Runtime Mission-state re-check ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)) |
| **Issuer sees each delegation** | Yes. Central visibility and control | No. Unobserved until use |
| **Cost** | Round-trip per delegation | None. Built for fan-out scale |
| **Reach for it when** | The sub-agent needs its own durable, separately revocable Mission | The sub-agent needs a narrower token under the same Mission, fast, at fan-out scale |
The decision is not "which is more secure." Both rest on the same narrow-only rule and the same kill switch. For the Child Mission, cascade revocation propagates from the issuer, which commits the terminal transition in every mode, while the experimental `bounded_staleness` and `status_required` modes push an interim parent-state check onto the consumer. For offline attenuation, the runtime layer re-checks state at use. The decision is *where you can afford to spend a round-trip*. If you want the issuer to observe and own every delegation, and the latency is acceptable, mediate it. If issuer round-trips are the bottleneck on a deep fan-out, mint offline and let the runtime layer carry the kill switch.
Maturity is a decision factor too. AS-mediated delegation depends only on ratified OAuth and is the stable path, while offline attenuation is labeled experimental because its substrate, Attenuating Agent Tokens, is an in-progress draft. Adopt it for evaluation. And a single deployment can do both: a Child Mission for the durable reviewer sub-agent, offline attenuation for the sharded extraction workers under it, each narrowed to its own slice, and no delegation machinery at all for the homogeneous instances that simply multiply.
That deployment, drawn. Every edge names its mechanism, what narrows on it, and what bounds it, and no edge widens:
```mermaid
flowchart TB
A([alice approves]) -->|"intent_hash + authority_hash
expires_at caps everything below"| M["Mission M
board packet: finance read,
docs write, notify committee"]
M -->|"multiplication, not delegation:
per-instance tokens, same authority,
class-pinned, max_derivations ceiling"| N["N attested instances
of one Agent Deployment"]
M -->|"Child Mission: strict subset,
docs comment-only, own mission_id,
cascade when M ends"| C["Reviewer sub-agent
durable, separately revocable"]
N -->|"offline attenuation: chain
proves narrowing, shard k only,
minutes-scale expiry"| W["Extraction worker k
same mission claim rides the chain"]
```
Read the edges, not the boxes. Three different mechanisms, one invariant: everything below `alice`'s approval is a subset of it, and ending M ends the graph.
# The through-line
Three invariants hold across both mechanisms, and they are the same three that have held since [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/):
- **Authority can only narrow.** A child is a strict subset of its parent: by subset validation at the issuer for a Child Mission, by capability monotonicity on the chain for offline attenuation. Neither path can broaden. Widening is Expansion ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)), a fresh approval, not a delegation.
- **Delegation never widens.** A handle is not a credential, and spawning is not delegation: authority moves down only by an explicit, narrower grant, never by ancestry.
- **A child is bounded by the parent and dies with it.** Cascade revocation for a Child Mission (issuer-committed in every mode, consumer-checked in the interim under the experimental `bounded_staleness` or `status_required`). The runtime Mission-state check for an offline chain. When the Mission goes non-active, every dependent and every descendant stops.
That last invariant is the whole kill switch, projected onto fan-out. Revoking one Mission stops the parent, its children, and their children at once, whether the issuer minted them or a holder did. [The agent runtime and audit](/notes/the-agent-runtime-and-audit/) is what makes that true inside a running harness. It binds the sub-agent handles, queues, and sessions to Mission state so that "the Mission stopped" actually reaches the work in flight.
---
# From a Request to an Approved Mission
Canonical URL: https://notes.karlmcguinness.com/notes/from-a-request-to-an-approved-mission/
Markdown URL: https://notes.karlmcguinness.com/notes/from-a-request-to-an-approved-mission.md
{{< tldr >}}
- **The argument.** Between a user's prompt and the agent's first token sits a transition. A request becomes an *approved Mission*. Everything on the request side is untrusted. This part is about making the moment of transition, the [approval event](/notes/the-mission-is-the-missing-abstraction/#mission-versus-intent), trustworthy, before any authority exists.
- **What lives in this part.** [Who may approve](#who-may-approve): an accountable authority, human or authorized policy, never the proposer. Then three concerns, none of which grant authority: [Shaping](#shaping-proposes-only) (turning a request into a candidate Mission Intent), [Consent Evidence](#consent-evidence-the-recorded-disclosure) (committing the structured disclosure the Authorization Server recorded as rendered, with [translation and interrogation](#translation-and-interrogation-the-disclosure-as-a-dialogue) making that disclosure readable and questionable), and [Deferred Approval with its Revision companion](#deferred-and-revisable-approval) (making the approval event asynchronous and narrowable). Then [how they compose](#how-they-compose) on one worked example, and [the fatigue budget](#the-fatigue-budget) they add up to.
- **The card analogy.** This is the approval workflow that runs before the card exists: the request, the disclosure, and the sign-off. [You Approve What You Were Shown](/notes/you-approve-what-you-were-shown/) is this part in card terms. ([Where the analogy breaks](/notes/you-approve-what-you-were-shown/#where-the-analogy-breaks).)
- **The laws.** This part operationalizes [Attribution and Containment (Laws 2 and 5)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): the approval record keeps authority attributable to a real approver, and it fixes the approved purpose that every later action is checked against.
- **Specs (editor's copies).** [Mission Intent Shaping](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-shaping.html), [Mission Consent Evidence](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html), [Mission Deferred Approval](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval.html), [Mission Approval Revision](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval-revision.html).
**Reading path.** ~18 minutes in order, or ~8 minutes for just the integrity story: [the boundary](#the-boundary-restated) โ [Consent Evidence](#consent-evidence-the-recorded-disclosure) โ [how they compose](#how-they-compose).
{{< /tldr >}}
# Overview
[The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) drew the boundary between **Mission Intent** (a proposal) and an **approved Mission** (the governance object). The **approval event** is the single moment of transition, where the Authorization Server validates the Intent, derives an Authority Set, the Approver consents, and the Mission record is committed by `intent_hash` and `authority_hash`.
This part is about the integrity of that transition. A request arrives as natural language, or as an upstream trigger, and has to become something the Authorization Server can validate and an Approver can meaningfully consent to. Three things have to hold for the resulting Mission to be trustworthy, and each maps to its own drafts in the suite:
1. **The proposal must be honest about what it is.** A shaper turns the request into a candidate Mission Intent, and that candidate has to fail closed on ambiguity rather than invent authority. ([Shaping](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-shaping.html), Informational.)
2. **The approval must commit what the Approver was actually shown.** Otherwise a faulty rendering layer can display a narrow task while the Mission records a broad one. ([Consent Evidence](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html), Standards Track.)
3. **The approval must survive a real human review.** Reviews are asynchronous, and reviewers approve a *narrowed subset* far more often than they accept all-or-nothing. ([Deferred Approval](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval.html) and its narrowing companion [Approval Revision](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval-revision.html), both Standards Track. Deferred Approval tracks an unratified substrate, and Revision is labeled experimental.)
The unifying claim, hammered through every section below, is the one from [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/). **None of these three grants authority.** Authority is created only by the Authorization Server's validation and approval. The shaper proposes. Consent evidence records. Deferred approval narrows. They make the approval that *does* grant authority trustworthy, without ever becoming it.
| The control at a glance | |
| --- | --- |
| **Minimum useful version** | The Authorization Server validates and narrows every Mission Intent submitted through PAR and renders the derived authority to the Approver. Consent Evidence records the structured disclosure as rendered |
| **What it prevents** | Approvals that bind something other than what the Approver saw, and proposals that specify their own authority |
| **What it does not prevent** | An Approver accepting an over-broad disclosure. Breadth approved is breadth granted |
| **Operational owner** | The Mission Issuer owns validation, derivation, and consent rendering. The application team owns the shaper, which proposes only |
| **Evidence emitted** | The approval event and the Consent Evidence record, joined on the `mission_id` |
| **Maturity** | Consent Evidence is recommended for agents. Shaping is informational. Deferred Approval is advanced on an unratified substrate, and Revision is experimental |
# The boundary, restated
The trust boundary is the whole reason this layer exists. The [Mission versus Intent](/notes/the-mission-is-the-missing-abstraction/#mission-versus-intent) section of the architecture chapter is the canonical treatment. The short version:
> Mission Intent is the proposal. Mission is the approval. The integrity anchors commit the moment of transition.
The user's raw prompt, the shaper, the model behind the shaper, and the candidate Mission Intent are all on the **untrusted** side. The Authorization Server's admission decision *is* the boundary. Before it, only Intent exists. After it, only the Mission is authoritative.
```mermaid
flowchart LR
subgraph U["Untrusted (client-side)"]
direction TB
P[Prompt or trigger]
SH[Shaper]
I[Candidate
Mission Intent]
SE[Shaping Evidence]
P --> SH --> I
SH -.records.-> SE
end
subgraph B["Approval event (Authorization Server)"]
direction TB
V[Validate + narrow,
derive Authority Set]
D[Render disclosure,
commit consent_rendering_hash]
UC[Approver consents]
V --> D --> UC
end
subgraph T["Approved Mission (governed)"]
M[("intent_hash
authority_hash
state=active")]
end
CE["consent_rendering_hash
(Consent Evidence companion)"]
I --> V
UC --> M
M -.companion.-> CE
```
Each of the three drafts in this part strengthens a different edge of this picture. Shaping makes the *left box* honest and auditable. Consent Evidence makes the *middle box* reconstructable after the fact. Deferred Approval makes the *middle box* tolerate a slow, picky human reviewer without losing state. The boundary itself never moves.
# Who may approve
The Approver is a role, not a species. On the record it is an `{iss, sub}` principal, and the [litmus test](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test) has always said "a human (or authorized policy) approved" rather than naming a person. What the approval event requires is not a human. It is an accountable authority deciding against committed inputs before any authority exists, and two invariants that hold whoever decides:
- **The proposer is never the approver.** The agent, the shaper, and the requesting client sit on the untrusted side of the boundary above, whoever sits on the deciding side. A request that approves itself is not a request.
- **Non-human approval traces to a human decision at some grain.** An authorized policy approver is legitimate because a human consented to the ceiling it enforces or approved the policy it executes, exactly as a manager's sign-off is legitimate because finance approved the delegation-of-authority matrix behind it. Policy approves the instance. A human approved the policy, and `policy_version` on the record keeps that chain re-checkable.
Within those invariants, the approval authority is a spectrum the deployment chooses per class. A human approves directly, with Consent Evidence committing the disclosure they saw. An authorized policy approves at machine speed, with the committed inputs and `policy_version` standing where the disclosure stood, which is how [a standing agent's ceiling](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale) turns four hundred approvals an hour into policy decisions. Risk and fraud engines compose as decision inputs that inform or narrow either kind of approver, the same way [content-aware controls compose at the runtime layer](/notes/mission-bound-runtime-enforcement/#the-lethal-trifecta-at-execution-time): signals, not authorities. The one thing that must not be the sole authority for granting or widening is another model's generated judgment, because an LLM approver reading attacker-influenced proposals is itself an injection surface. And the floor is the class guard: irreversible actions, external commitments, and privileged administration keep a human in the decision by deployment policy, whatever approves everything else. The runtime part prices these same classes as [high-consequence](/notes/mission-bound-runtime-enforcement/#which-actions-are-consequential).
That floor names one accountable human, and the singular is a modeling choice worth defending, because four-eyes controls exist precisely so that no principal decides alone. The record commits one accountable Approver for attribution's sake: a decision everyone signed is a decision no one answers for, and Law 2 needs a name. Joint approval is not lost, it is relocated. A policy approver can require two human sign-offs before it consents, the quorum is enforced at admission, and every co-signer lands in the committed evidence under the `policy_version` that demanded them. What the model refuses is only the opposite flattening, a record where accountability diffuses across the quorum until it vanishes.
The enemy this section guards is not machine approval. It is agent-inferred authority: the agent granting itself scope mid-flight because no admission decision was required at all. A policy approver is still an admission decision, committed and attributable. An agent that widens its own authority is not.
The role, on a screen. The routing is risk-based (production impact
and sensitive data pull a manager into the loop), the requester is
not the approver, and the panel says why the decision landed here:

One reconciliation with the record: however many sign-offs a
deployment's workflow gathers, the Mission commits a single
accountable Approver. The other approvals are workflow gates feeding
that decision, not co-owners of it.
# Shaping: proposes only
The issuance core defines the Mission Intent object and how the Authorization Server derives an Authority Set from it. It does not define how a deployment turns "reconcile our Q3 invoices and post any adjustments under \$500" into that structured object. That step is the **Mission Shaper**, and the [Shaping draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-shaping.html) describes it.
The draft is **Informational** by deliberate choice. Client-side prompt processing is shaped by deployment policy, model choice, and product ergonomics. No two deployments agree on the transformation, and the interoperable surface is not the transformation but its *result*, the Mission Intent, which the issuance core already validates on the wire. So the draft specifies the shaper's **role and trust posture** and the **behaviors a sound implementation follows**, not a portable shaping protocol.
A shaper can be an LLM-assisted function, a deterministic rules engine, a form, or a workflow. Whatever it is, its single rule is **propose only**:
- It **MUST NOT** issue, derive, or certify authority. Its output is untrusted client input until the Authorization Server validates and narrows it.
- It **MUST NOT** emit members that mimic Authorization Server outputs. No `mission.id`, no `intent_hash`, no `authority_hash`, no Authority Set, no lifecycle state. Those are produced by the Authorization Server at and after approval. A shaper that emits them is misusing its role, and the issuance core rejects a Mission Intent carrying any such member with `invalid_request` rather than silently stripping it.
- A model-based shaper is **no exception.** The model can draft a proposal. It is never the thing that grants or widens access.
What a sound shaper builds is a Mission Intent with `goal`, `resources` (each an absolute URI), free-text `constraints`, optional free-text `success_criteria` (disclosure and audit material only, carrying no machine semantics), an optional `purpose`, `expires_at`, optional machine-actionable `controls`, and an optional `proposed_authority` array. The shaper proposes resources and describes the desired bounds in free text, and `proposed_authority` is the one home for any concrete candidate authority it has resolved (actions, structured constraints, delegation facts): an untrusted carrier the Authorization Server only *narrows* when it derives the Authority Set. The shaper still authors no authority. The Authority Set is the Authorization Server's product, never the shaper's input taken at its word.
**Fail closed, not open.** The interesting design content is what a shaper does when the request is ambiguous. An ambiguity is *material* when resolving it one way rather than another would change the Authority Set, the action class, the actor, the expiry, or the risk posture. For a material ambiguity, a sound shaper does one of three things: **request clarification**, **emit a narrower proposal** that excludes the ambiguous authority, or **refuse** with a reason. What it must not do is silently default a vague goal into a wide proposal. Because the shaper's internal reasoning is unobservable to the Authorization Server, the draft expresses this through the audit artifact. When a shaper resolves an ambiguity in the broadening direction, **Shaping Evidence MUST record it**, and a proposal that broadens on an ambiguity without a matching record is unsound. (The draft says "unsound" deliberately. It is Informational and defines no conformance class.)
**Shaping Evidence** is that artifact: a record of the inputs, inferences, policy decisions, capability resolutions, ambiguities, and any model trace that produced the proposal. It is *audit material, not authority*, and never an input the derivation consumes. A Resource Server or PDP MUST NOT use it to permit an action. A deployment that wants the Mission record to cite how its proposal was produced can bind the two with an optional `shaping_evidence_hash`, computed over the suite's domain-separated `{typ, iss, value}` envelope exactly as `intent_hash` and `authority_hash` are. That hash commits *how the proposal was made*. It confers no authority and proves nothing about the proposal's correctness.
The threat this contains is **silent broadening**: a vague request quietly becoming a broad Authority Set, or prompt-injected content in the request expanding `resources`, pushing out `expires_at`, or suppressing a stated constraint. The shaper's defenses (treat all prompt content as data not instructions, use resolved capabilities as a hard allowlist, refuse on injection patterns, record everything) reduce but cannot eliminate the risk. The real defense in depth is downstream. The Approver sees the validated Intent in a disclosure rendered by the Authorization Server, not by the shaper, before authority is bound. Which is exactly what the next section makes verifiable.
Both shaping modes have a natural surface, and neither grants a
thing. Generated shaping takes the plain-language request and
proposes policy-shaped options with cost, duration, and risk visible:

Authored shaping is the same proposal built by hand: objective,
success criteria, allowed and forbidden capabilities, budgets and
limits, then submission for approval:

Illustrations of the experience, not normative renderings. On the
wire both are the same thing, a Mission Intent submitted for
validation, and the compliance both surfaces advertise is a
pre-check: the Authorization Server's derivation and the Approver's
decision still create all the authority there is.
# Consent Evidence: the recorded disclosure
The issuance core commits *what was approved*, the Mission Intent and the Authority Set, through `intent_hash` and `authority_hash`. It deliberately leaves one gap open. It does not commit the **consent disclosure** the Approver actually saw. A faulty or malicious rendering layer could show a narrower task than the Authority Set really records, and nothing in the core would catch it. The [Consent Evidence draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html) (Standards Track) narrows that gap.
It adds two artifacts at the approval event:
- A structured **Consent Disclosure** object: the task summary (including the rendered `expires_at`), the rendered authority summary (resources, actions, constraints, delegation, consumption bounds), the **material notices** for high-risk authority, the Approver and Subject identities, and the `intent_hash` / `authority_hash` the disclosure corresponds to. It is constructed *after* Authority Set derivation and *before* approval. If the Authority Set changes afterward, the disclosure is discarded and rebuilt.
- A `consent_rendering_hash` over that disclosure object (again in the `{typ, iss, value}` envelope), and a signed **Consent Evidence** object that records the approval-or-decline event, the authentication context, and an integrity envelope (a JWS) over the whole record, bound to the same Mission anchors used for authority.
The precise scope claim matters, and the draft is careful about it:
> This profile commits the **structured disclosure that the Authorization Server says it rendered**, and binds it to the same Mission anchors used for authority.
What no server-side commitment can prove is that the pixels presented to the Approver matched the committed object, that the Approver read or understood it, or that the rendering layer was honest. The draft names that residual rather than hiding it.
What it buys you is a durable, integrity-protected record tying a *specific structured disclosure* to a *specific approval decision and Authority Set*, so that any divergence between the recorded disclosure and the authority later enforced becomes **detectable in audit**. An auditor can re-render the committed disclosure and check it against the Authority Set the agent actually used.
The "what a human perceived" problem is not all-or-nothing, so the draft defines a **rendering-assurance ladder** a deployment climbs as far as its threat model needs. Rung 0 commits the disclosure. Rung 1 makes rendering a deterministic function of the disclosure and its template, so an auditor can re-render the intended form. In practice that means the disclosure identifies its template, the template's version, and the locale it rendered, because a re-render an auditor cannot reproduce exactly is not deterministic, and a reviewer's narrowing is never an edit in place but a revision with a fresh hash. Rung 2 adds an attestation that an identified, attested renderer produced it. Rung 3 is the *what-you-see-is-what-you-sign* rung. The Approver's own authenticator signs the `consent_rendering_hash`, moving trust from the rendering layer to the Approver's authenticator. (Rung 4, out-of-band confirmation at execution time, belongs to [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/).) Rungs 0 and 1 are the profile's conformance rungs. The higher rungs are experimental, since each imports a trust infrastructure (attestation, transaction-confirming authenticators) the profile cannot supply, and a deployment claiming Rung 3 SHOULD apply it where the Authority Set carries a high-risk notice class: [the class-guard classes](#who-may-approve) plus exhaustible consumption bounds.
Two details are easy to miss and worth keeping. First, **declines are recorded too.** A declined approval creates no Mission, token, or authority, but Consent Evidence is still recorded, so that coercion, decline-then-reshape fatigue attacks, and rendering confusion cannot be made invisible to audit. Second, **the disclosure renders the derived authority, not just the friendly summary.** A disclosure that shows a `mission_summary` without a faithful `authority_summary` does not conform. The Approver consents to the authority, with the summary as context.
# Translation and interrogation: the disclosure as a dialogue
The same draft sets a **translation floor**, because coverage alone does not make a disclosure readable. A deployment can satisfy every coverage rule and still present the Authority Set as serialized structure that trains the Approver to stop reading, which is the consent-fatigue residual arriving through the rendering layer. So the rendered forms must be natural-language statements of what the agent may do, with each constraint and consumption bound rendered as the bound on a statement ("may not exceed 500 USD in total"), a serialized Authority Set never the primary rendering, and a constraint key the template cannot translate rendered and identified as untranslated rather than omitted. An Approver shown an untranslated bound can decline. One shown nothing cannot. Disclosure is a language problem before it is a rendering problem, and the floor is template-testable: the same template fixtures that catch downgrades should prove the template translates every constraint key and notice class the deployment uses.
The floor is also where ontology ownership becomes visible, because the disclosure renders meaning the issuer does not own. A Common Constraint has registered semantics the template can translate, but a resource-local action means what the resource says it means, and where a substrate lets the resource declare its own operations and consequences (AAuth's exploratory Rich Resource Requests is this shape), consent composes the resource's own words and commits them, so the record carries what the resource declared beside what the client asked and what the Approver accepted.
And the disclosure is not take-it-or-leave-it. A faithful disclosure answers what the Mission may do, but the question an Approver actually weighs is often *why*. Why does a support-ticket task need write access to a finance folder? An Approver who cannot ask guesses, and a guessing Approver decides on the wrong fact. **Disclosure Interrogation** gives the Approver a question channel before the decision, and the draft is precise about what may answer it. Anything the consent surface presents in its own voice must be drawn from recorded material: [Shaping Evidence](#shaping-proposes-only) for why the task motivated an entry, constraint provenance for whose rule a bound is, and identified deployment policy for the Issuer's own bounds. Where the answer is instead relayed from the requesting agent ([AAuth's clarification chat](/notes/aauth-now-has-a-mission-layer/) is exactly this channel), the relayed text is attacker-influenceable, so it is rendered inert and visibly the agent's, because the same channel that lets an Approver ask why lets a compromised agent argue. An answer grants nothing and amends nothing. An Approver satisfied by an answer approves the same committed authority, and one convinced the authority is wrong declines or requires narrowing through a fresh derivation. The interrogation itself is recorded in Consent Evidence, and the question asked before a decline is the record's most valuable entry: it preserves which authority the Approver probed and could not accept.
The dialogue, drawn. The question channel with its trust boundary
visible: the requester's answer arrives quarantined as relayed agent
text, the grounded answer comes from Shaping Evidence with its
citations, and the footer records the whole exchange into Consent
Evidence:

The mock keeps the section's rule: an answer grants nothing and
amends nothing, and the Approver who accepts one approves the same
committed authority.
# Deferred and revisable approval
The issuance core records an approval event but treats it as immediate. Real human review of an agent's proposed Mission is not. Two facts go unspecified, and two companion drafts supply them: the [Deferred Approval draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval.html) makes the approval asynchronous, and the [Approval Revision draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval-revision.html) lets the reviewer narrow it in place. One honesty note first. Deferred Approval depends normatively on OAuth Deferred Token Response, which is not yet ratified, and Approval Revision rides the same substrate and is labeled experimental. Synchronous approval is the stable path until that substrate settles.
**Reviews are asynchronous.** The agent submits a proposed Mission and may wait a long time for a decision. This is deliberately the shape of the request-and-approval workflows an enterprise already runs, so an IGA or ticketing system can drive the decision without new human ritual, and for approval *volume* the ceiling-and-drawdown model of progressive authorization is the companion answer ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)). The draft profiles [OAuth Deferred Token Response](https://datatracker.ietf.org/doc/draft-gerber-oauth-deferred-token-response/) so a Mission approval can be **deferred and polled**. The client includes `deferred` among its `completion_mode` values, the Authorization Server returns `authorization_pending` with a `deferral_code` instead of a token, and the client polls until the approval resolves. Deferral changes only the *timing* of the approval event. The Authority Set, its `authority_hash`, and the recorded consent are exactly as in a synchronous approval.
**Reviewers narrow.** A reviewer commonly approves a *subset* of the proposed Mission, not the whole thing. Without a way to revise in place, the agent has to abandon the proposal and start over, losing the approval state and any preceding work. The experimental Approval Revision companion adds a **revisable** mode. The client offers `revisable` alongside `deferred`, and when the Authorization Server can grant only a narrowed version, it extends the `authorization_pending` response with `revision_required`, a single-use sender-constrained `revision_handle`, and (optionally) the `rejected_scope` and `rejected_authorization_details` that tell the agent *which dimensions were refused*. The client pushes a narrowed Mission Intent to PAR with the handle, and keeps polling the same `deferral_code`. The approval resolves over the narrowed proposal. The client never lost its place, and the stable path without this companion is deny-and-resubmit under Deferred Approval alone.
The hard invariant, and the boundary to keep straight, is that this is **narrowing only**:
> A revision can reduce the proposed Mission. It can never broaden it. The Authorization Server verifies the revised Authority Set is a subset of the proposed one under the issuance core's subset rule before it re-reviews.
Widening an *already approved* Mission is a different operation with its own fresh approval, Mission Expansion, which lives in [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/). Deferred Approval and its revision companion govern only how the *initial* approval is reached over time. And the rejected-dimension parameters are not a grant. A client MUST treat `revision_required` and the refused dimensions as evidence of *nothing approved yet*.
This is also where the layers start to interlock. The `rejected_scope` and `rejected_authorization_details` are precisely the machine-readable input a shaper consumes to *plan* the narrowing. Shaping narrows a proposal before submission. Deferred approval narrows it during review. And because a narrowed proposal is a different disclosure, a deployment recording Consent Evidence MUST compute a **fresh `consent_rendering_hash`** for the re-reviewed revision. Prior consent does not transfer to a different Authority Set.
The approver's side of a deferred review, riding their own queue on
their own schedule:

Request Changes is the revision channel: the reviewer narrows, the
agent keeps polling the same deferral code, and nothing restarts. And
the mock's approve-and-run button compresses two events the wire
keeps distinct: approval activates the Mission, and the waiting
agent's poll is what resumes the run.
# The fatigue budget
Approval fatigue is not a UX complaint in this architecture. It is a security budget, because the [card chapter's version of this room](/notes/you-approve-what-you-were-shown/#where-the-analogy-breaks) said the quiet part: any adversary who can generate requests can farm a tired approver, and for agents the requests generate themselves. The handbook's design answer is grain plus [who may approve](#who-may-approve): machines approve actions, authorized policy approves the Missions it can take, and humans decide what is left, the ceilings, the class-guard classes, and the exceptions. But grain alone just moves the pile. A reviewer rubber-stamping forty Mission disclosures a day is the same failure one level up, and every approval a policy can legitimately take is one the human budget never pays. So the profiles in this part are also the fatigue controls, and they are worth reading that way:
- **Shaping is the first control.** A disclosure a reviewer can actually read (goal, resources, bounds, expiry, rendered small) is the difference between a decision and a reflex, and a shaper that fails closed on ambiguity keeps the padded, over-broad proposal from ever reaching the queue.
- **Deferred approval routes decisions where attention already lives.** The review rides the IGA or ticketing workflow the organization already staffs, with its own escalation and its own SLAs, instead of a new interrupt channel nobody owns.
- **Revision narrows without restarting.** The common case (right task, wrong bounds) costs the reviewer one trim instead of a resubmission cycle that trains requesters to ask broad and approvers to stop reading.
- **Ceilings spend one good decision on many small ones.** For high-volume and standing work, a pre-consented authority ceiling moves the human decision to the ceiling and its renewal, with the drawdown guard reserving fresh human approval for the classes where fatigue is most dangerous ([the standing agent at scale](/notes/mission-lifecycle-and-change/#the-standing-agent-at-scale)).
- **Action-bound approval is rationed by class, not sprinkled.** [Runtime enforcement](/notes/mission-bound-runtime-enforcement/#the-high-assurance-level) reserves the second human decision for [the class-guard classes](#who-may-approve): the friction budget spent at the point of maximum consequence.
And measure it, because a fatigue budget nobody measures is already overspent. Approval latency, deny and revision rates, exception volume, and time-to-decision are governance health metrics, and a queue where every approval lands in four seconds is telling you the disclosures are unreadable, the grain is wrong, or the reviewer has stopped reviewing.
Spent well, the budget looks like this pair. Most work runs at the
one-click end, read-only and least-privilege, previewed rather than
interrogated:

And the heavy end is reserved for the one percent, where simulation
results, blast radius, policy evaluation, and a chain of named
approvers spend the friction where the consequence lives:

The tiers printed on these mocks are approval routing, not the
Mission Assurance Levels: one is per-Mission friction under one
deployment's policy, the other is how much of the architecture a
deployment runs.
# How they compose
The three concerns are easiest to see threaded through one task. This is the handbook's [canonical running example](/notes/mission-based-authorization-field-reference/#the-running-example-end-to-end). Alice asks her agent, in free text, *"Put together the Q3 board packet for the audit committee and let them know it's ready."* [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) picked up this task at the issuance core. Here we watch the *approval-time integrity* around it.
```mermaid
sequenceDiagram
autonumber
participant Al as alice@example.com (Approver)
participant Ag as Agent + Shaper
(client-side, untrusted)
participant AS as Authorization Server
(Mission Issuer)
Note over Ag: SHAPING (proposes only)
Ag->>Ag: Shape request โ candidate Intent
resources=[finance, docs, workflow]
constraints=[Q3 2026, audit-committee, ready-notice]
record Shaping Evidence
Ag->>AS: Submit Mission Intent via PAR
Ag->>AS: Authorization code flow, then token request
completion_mode=deferred revisable
Note over AS: DEFERRED APPROVAL
AS-->>Ag: authorization_pending (deferral_code)
AS->>AS: Derive Authority Set
(query_financials + create_doc + notify_reviewer),
route to alice
AS->>AS: Build Consent Disclosure,
compute consent_rendering_hash
AS->>Al: Render disclosure to alice
Al->>AS: Interrogate: why query_financials?
AS-->>Al: Answer grounded in Shaping Evidence
(exchange recorded in Consent Evidence)
Al-->>AS: Approve (asynchronously)
Note over AS: CONSENT EVIDENCE
AS->>AS: Sign Consent Evidence,
bind Mission to the hash
AS-->>Ag: Mission-bound token over
msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1, authority_hash
```
Walking it:
1. **Shaping proposes.** The shaper turns the open-ended prompt into a bounded candidate Intent. It resolves "the audit committee" to the `audit-committee` reviewer group, and it proposes three `resources` URIs (the finance, docs, and workflow services) bounded by *invariants* expressed as free-text `constraints`: the Q3 2026 fiscal period, the `audit-committee` recipient, the board-packet deliverable. It bounds the work in time with a `expires_at` of `2026-10-15T18:00:00Z`. What it does **not** do is author the actions. It proposes resources and describes the desired bounds, and grants nothing. Had "the audit committee" been ambiguous about which group, the shaper would have asked `alice` rather than guess. It records Shaping Evidence and proposes.
2. **The approval defers.** Because the agent offered `deferred revisable`, the review need not be synchronous. The Authorization Server validates the Intent and returns `authorization_pending` with a `deferral_code`, and the agent polls while the review waits for `alice`.
3. **The Authorization Server derives at the approval event.** It is the Authorization Server, not the shaper, that turns the proposed resources and free-text constraints into a concrete Authority Set: a `query_financials` action (finance, scoped to Q3 2026), a `create_doc` action (docs, bound to the board-packet template), and a `notify_reviewer` action (workflow, targeting the `audit-committee` group). The shaper's proposal *informed* that derivation. The Authorization Server authored every action in it. And derivation is mechanical, happening once at the approval event over the policy and capability catalog in force when `alice` decides, so a proposal that waits for days is never rendered to her under superseded policy. The rendered disclosure routes to `alice`, who approves on her own schedule. Had she instead refused the `notify_reviewer` action (a common reviewer instinct, holding back the external-facing step), the **revisable** mode would let the agent drop that action, push the narrowed Intent, and keep polling the same `deferral_code`. Narrowing only, never broadening. Here she approves the proposal as derived.
4. **Consent Evidence commits the surface.** The structured disclosure the Authorization Server recorded as rendered (the task summary plus the faithful `authority_summary` over all three actions) was committed by `consent_rendering_hash` before approval, and her approval produces a signed Consent Evidence record bound to it. The committed `intent_hash` and `authority_hash` are over exactly the Intent and Authority Set she approved, so an auditor can later re-render the recorded disclosure and compare it to the authority the agent used. The Authorization Server commits the Mission as `msn_01J9Z2P8BQ4Y3F0V0K9D6Z7M1`, `state=active`, and issues the Mission-bound token that [runtime enforcement](/notes/mission-bound-runtime-enforcement/) will police.
The same composition, rendered as an approval surface: the mock walks the identical spine as a reviewer experience, and its printed design principles are this part's arguments as pixels.

An illustration of the experience, not a normative rendering. The
wire truth for the claims and anchors lives in the
[exhibits](/notes/mission-bound-authorization-on-the-wire/), and a
conforming disclosure renders the faithful `authority_summary` this
part requires, whatever the pixels around it look like.
The result is an approved Mission whose integrity anchors commit a proposal that was honestly shaped, a disclosure that was faithfully recorded, and an approval that reflected a real human decision, without any of those three steps ever having granted authority. That is the whole job of this layer.
# Where this sits in the handbook
This is the **Intent** step and the integrity of the **Mission** approval event on the [spine](/notes/the-mission-is-the-missing-abstraction/#the-intent-to-enforcement-spine). It sits between [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/), which defines the Mission and the issuance core this layer feeds, and [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/), which is where a Mission-bound token becomes a Mission-bound *action*, the load-bearing safety layer of the whole chapter.
Everything here happens *before any token exists*. By the time [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/) is evaluating an action against the current Mission, the questions this part answers, was the proposal honest, was the disclosure faithful, did the approval reflect a real human decision, have already been settled and committed. The later parts carry the approved Mission forward: [Mission-Bound Authority](/notes/mission-bound-authority/), [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/) (including Mission Expansion, the *widening* counterpart to this part's narrowing-only revisions), and [The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/).
The laws this part proves are the quiet ones. An approval binds only what it can replay (Attribution), and enforcement can only hold a boundary the approval actually drew (Containment). This is the one place in the loop where safety is still cheap. Spend the friction here.
---
# Adopting Mission-Bound Authorization
Canonical URL: https://notes.karlmcguinness.com/notes/adopting-mission-bound-authorization/
Markdown URL: https://notes.karlmcguinness.com/notes/adopting-mission-bound-authorization.md
{{< tldr >}}
- **Three stages.** [Crawl](#crawl-baseline-issuance): the issuance core, approved Missions and a kill switch, governance rather than safety. [Walk](#walk-the-runtime-enforced-level): the Runtime-Enforced level, per-action enforcement on existing substrate, a substantial build. [Run](#run-governed-and-beyond): the Governed and High-Assurance Agent levels, with the advanced and experimental roadmap earned through deployment.
- **Where you start.** Most estates run agents read-only, with humans approving or executing the writes and pilots that never graduate. [The read-only ceiling](#the-read-only-ceiling) names what that posture costs, and the levels are the graduation path: each one makes a broader class of write authority defensible. If you already run a credential broker, [crawl starts from an asset](#crawl-baseline-issuance): the broker becomes the Mission-gated credential plane. And [you do not need an agent to start](#you-do-not-need-an-agent-to-start): the first deployment can govern human access requests.
- **The Runtime-Enforced bias.** The implementation minimum, the reference security architecture (issuance core, runtime enforcement, AuthZEN binding, and Status as the freshness source), depends only on ratified OAuth RFCs and finalized OpenID specifications. It is the smallest interoperable surface that closes the gap [draft-klrc-aiagent-auth](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) names, and it is adoptable today. And [not every resource checks Mission state](#not-every-resource-checks-mission-state): enforcement lands at chokepoints you already control, and the coarse end of the freshness dial (state-gated issuance with short-lived tokens) reaches legacy resources exactly as they are.
- **The roadmap.** Beyond Runtime-Enforced sit two labels: advanced profiles to adopt when the use case arrives (asynchronous approval, growth and completion, sub-agents, cross-domain projection, fleet management, push signals, the portable Mandate, audit transparency), and experimental ones for evaluation only (approval revision, progressive drawdown, consumption metering, offline fan-out, safe unwinding, open-world discovery). Each experimental profile is labeled for stated reasons.
- **The laws.** The stages are the laws' adoption path: each level operationalizes [all five](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) at increasing strength, from issuance-time governance to action-time defense.
- **The card analogy.** Nobody rolls out a card program in one quarter. Issue the first purpose-bound card, add per-transaction authorization, then grow the controls as the risk demands. ([Where the analogy breaks](/notes/agents-need-a-corporate-card-not-a-blank-check/#where-the-analogy-breaks).)
- **Specs.** The [draft repository](https://github.com/mcguinness/mission-bound-authorization) carries the editor's copies, and the [Reference's catalog](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance) is the 28-document family with maturity labels on every row.
**Reading path.** ~24 minutes start to finish. Deciding whether to adopt? [The read-only ceiling](#the-read-only-ceiling) and [the levels](#run-governed-and-beyond) are your ~8 minutes. Building? Jump to [the walk stage](#walk-the-runtime-enforced-level) for the build order.
{{< /tldr >}}
# Overview
A definitive architecture that ends without "what do I do now" is a tour, not a blueprint. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) defined the object. This closer turns the architecture into a staged build order, because the honest answer to "how do I adopt this" is not "all of it": it is crawl, walk, run, with each stage claimable on its own, plain about what it does not yet deliver, and chosen so that nothing waits on an unratified dependency. The maturity and status claims in this part are as of July 2026, and the [Reference](/notes/mission-based-authorization-field-reference/) carries the reconciliation date the handbook tracks. For substrate independence, the four bindings, and the trade the standalone Mission Authority Server makes, the concluding chapter, [Weighing Mission-Bound Authorization](/series/weighing-mission-bound-authorization/), is where those live.
The stages map onto the [Mission Assurance Levels](#run-governed-and-beyond) the repository publishes: crawl is the Baseline Issuance level, walk is the Runtime-Enforced level, and run is the climb through Governed and High-Assurance Agent, with the standalone Mission Authority Server as a parallel binding at every level, whether as the estate's control plane or because the Authorization Server cannot change. The three stages are the path, and the second half of this part is the terrain around it: the [ecosystem a Runtime-Enforced deployment composes with](#composing-with-the-ecosystem), the [roadmap beyond it](#the-roadmap-the-next-layer-of-the-problem), the [operational surfaces you will own](#what-you-will-operate), and the [pieces the community still has to standardize](#what-the-community-still-has-to-standardize).
# The read-only ceiling
Name where most estates actually start. Agents run read-only, a human
approves or executes every write, and the deployment is a pilot with no
path to graduate. That posture is rational. In July 2025 a coding
agent [deleted a production database](https://www.theregister.com/2025/07/21/replit_saastr_vibe_coding_incident/)
during an explicit code freeze, against instructions repeated in
capital letters, and the story traveled because every enterprise
recognized the shape: nothing the agent did was outside what its
credentials allowed. A probabilistic model on
standing credentials has no stateable blast radius, so the only levers the current stack offers are to deny the writes, to put a human in front of each one, and to keep the whole thing fenced. The ceiling is a context problem before it is a courage problem: the layer deciding each call cannot see the undertaking, so it cannot price a write. The same deletion is catastrophic or routine depending on the approved work around it, and a context-free stack has to price for catastrophic every time. Task-grain context is what changes the arithmetic, which is why the graduation path below runs through the level where actions are checked against the undertaking, not through better model behavior. The posture is also expensive, and each compensating control costs more than it looks:
| Today's control | What it costs | What retires it |
| --- | --- | --- |
| Read-only scoping | The value ceiling, and the reads are not even safe: an agent can be steered by anything it was allowed to see, and everything it holds can leak ([least exposure](/notes/least-exposure-is-broader-than-least-privilege/)) | Authority right-sized from the approved task, with exposure bounded as deliberately as action |
| A human approves or executes every write | [Approval fatigue](/notes/from-a-request-to-an-approved-mission/#the-fatigue-budget) at exactly the scale where agents are useful, and the human quietly becomes the unmediated enforcement point | Approval at Mission grain, [human where the class demands it](/notes/from-a-request-to-an-approved-mission/#who-may-approve), machine-speed permits per action ([runtime enforcement](/notes/mission-bound-runtime-enforcement/)) |
| The permanent pilot | The sandbox never graduates, and shadow paths grow around it | A named enforcement scope and a claimable [assurance level](#run-governed-and-beyond) |
The ceiling has an infrastructure reading too: it is what operating
[without a control plane](/series/designing-mission-bound-authorization/#the-control-plane-for-delegated-authority)
feels like. Nothing holds a bounded desired state to reconcile the work
against, so the only safe posture is denying the data plane its writes.
The staged path below is the graduation path off this ceiling. Each
stage retires one of these compensating controls, and the point is the
prize: not safer read access, but write access you can defend.
What was managed for human delegates by judgment, deterrence, and
accountability has to be built for agents as architecture, and this
part is that architecture in adoption order.
# You do not need an agent to start
Nothing in the object requires the actor to be a model. The [card chapter](/series/what-the-corporate-card-already-solved/) runs five posts of mission-based authorization with no agent in sight, because expense governance is the pattern applied to humans, and the protocol version works the same way. A Mission does not care whether the judgment inside its bounds is a model or a person.
That makes human access the natural first deployment, and a better outcome than the one traditional IGA produces. Run crawl on access requests: the request rides the approval workflow the organization already staffs ([Deferred Approval](/notes/from-a-request-to-an-approved-mission/) is deliberately shaped like an IGA review), and what comes out the other side is not a standing entitlement waiting for the next recertification campaign but a bounded, self-terminating, evidence-joined grant: authority sized to the task, expiring with it, attributable through it. Access reviews shrink toward the ceilings and the exceptions, because the best access review is the one the expiry already performed.
And it scales down. A Mission is not reserved for the multi-agent overnight job: one resource, one approver, one afternoon is a perfectly formed Mission, and the machinery costs what the task warrants (synchronous approval, no delegation, and no per-action gate beyond issuance if the class does not demand one). What reads a use case out of the model is never smallness. It is the absence of a task, where the credential lifetime is the work.
And the actor does not need to be new. The estate already runs
standing agents that nobody calls agents: the CI pipeline that deploys
on merge, the Terraform apply that reshapes infrastructure, the RPA
bot filing what humans used to file, the Kubernetes operator
reconciling all night, the scheduled job that moves money on the first
of the month. Every one of them is long-running delegated work on
standing credentials sized for the integration rather than the task,
and every one qualifies for the same machinery for the same reason the
agent does: the work outlives the request, and the authority should be
bound to the work. Mission-based authorization is not AI tooling. It
is the missing primitive for delegated work, and the probabilistic
agent is only the actor that made the gap impossible to keep ignoring.
Sequencing follows: humans are the forgiving first users of the rails (they tolerate latency, they answer clarifying questions, and they do not get prompt-injected), and agents inherit issuance, enforcement, and evidence already proven on human traffic. The estate that governs human task access with Missions today has already built the delegated-authority control plane its agents will need tomorrow.
# Crawl: baseline issuance
Crawl is the issuance core alone. Mission Intent submitted through PAR, the approval event that derives and renders the Authority Set, `intent_hash` and `authority_hash` committing what was approved, the `mission` claim on every derived token, state-gated issuance as the kill switch, and the subset rule on every derivation. This is [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) and the [core draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html), and a minimal conforming deployment fits on one screen.
What crawl buys you is real: every credential carries its purpose, issuance stops the moment the task does, short-lived tokens turn that stop into a real bound (revocation reaches every path within the token lifetime, with no resource changing at all), and audit joins on one identifier. What it does not buy you is the thing the handbook keeps refusing to let anyone claim by accident. Stop here and you hold governance metadata, not agent safety. A mission-bound token that nothing checks at the point of use is bookkeeping, and the [what-not-to-claim list](/notes/mission-based-authorization-field-reference/#what-not-to-claim) says so in writing. Crawl is the right first quarter. It is not a place to live.
And meet the estate where it is. For many enterprises the first agent-credential control already shipped is a credential broker: real credentials moved into a vaulted plane, with agents holding just-in-time, short-lived, sender-constrained leases (the proposed [CB4A](https://datatracker.ietf.org/doc/draft-hartman-credential-broker-4-agents/) is the spec-shaped instance). If that is your starting point, crawl starts from an asset rather than zero, and it is one integration away: hold the Mission record at the Authorization Server or the standalone [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html), gate every mint on Mission state, and stamp the `mission` claim on the leases the broker issues. The chokepoint you already deployed becomes the issuance gate, the kill switch reaches every future lease, and the broker's ledger joins on `mission_id`. What the broker cannot supply on its own is the object, because a lease is not the task, which is the [landscape's broker row](/notes/mission-based-authorization-field-reference/#competitive-landscape) in one line.
# Walk: the Runtime-Enforced level
Walk is where the safety claim becomes real, and it is sized honestly: a substantial build, not a wedge, measured from the runtime profile's conformance section. Two additions:
1. **Put a PEP at every consequential boundary and adopt the runtime contract with its AuthZEN binding.** The PDP evaluates each action against the live Mission, parameters bound, consumption metered, failures closed. This is [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/), the [runtime draft](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html), and the [AuthZEN profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html).
2. **Serve Status as the freshness source.** Revocation is only as fast as consumers learn it. The signed pull surface (or issuer token introspection) is the Runtime-Enforced half of [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/), and it is what makes "only `active` permits reliance" operational. Where a revocation must bite in seconds, adopt the Signals push as the complement.
The order is the argument. Crawl alone is governance metadata. Walk is what turns the object into a control, and for AI agents the recommended additions come with it: Consent Evidence and the harness, because an agent's approval surface and its resume path are where the guarantees otherwise lapse ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/), [The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)).
The first design decision is not which extension to adopt. It is the enforcement scope: which resources, action classes, and execution paths you can actually mediate. A deployment that cannot prevent an action on a path must not claim runtime enforcement for that path.
The whole walk stage compresses into one recipe:
| | The minimal enforced deployment |
| --- | --- |
| 1 | The Mission Issuer records the approved task |
| 2 | Tokens carry the mission id and authority hash |
| 3 | A PEP gates each consequential action |
| 4 | The PDP checks action, parameters, actor, and current Mission state |
| 5 | Status (or issuer introspection) provides fail-closed freshness |
| 6 | Evidence joins on the mission id |
The same recipe as one reference architecture, each edge numbered by
the row it implements:
```mermaid
flowchart LR
AP([Approver])
AG([Agent])
subgraph MI["Mission Issuer (the AS, or the standalone MAS)"]
M[("Mission record
intent_hash, authority_hash,
state")]
ST["Status surface
(or issuer introspection)"]
end
subgraph EB["Enforcement boundary"]
PEP[PEP]
PDP[PDP]
end
RS[Resource Server]
EV["6. Evidence,
joined on mission_id"]
AP -->|"1. approves the task"| M
M -->|"2. mission-bound token:
mission_id + authority_hash,
state-gated"| AG
AG -->|"3. each consequential action
+ parameters"| PEP
PEP -->|"4. evaluate action, parameters,
actor, current Mission state"| PDP
PDP -->|permit / deny| PEP
PEP --> RS
M --> ST
ST -.->|"5. fail-closed freshness"| PDP
M -.->|lifecycle events| EV
PDP -.->|decisions| EV
PEP -.->|executions| EV
```
These are six deployment surfaces, not six laws. They are how the five laws become checkable in production: a durable object, derived authority, bound credentials and decisions, runtime containment, lifecycle freshness, and attributable evidence. And the recipe is deliberately small. It does not prove every side channel is mediated, does not make the agent's reasoning trustworthy, does not unwind completed actions, and does not give cross-domain proof by itself. Those are governed-agent and advanced controls. This stage only does the thing the category cannot skip: it makes the approved task an object and checks consequential action against current state.
The same recipe lands at six different boundaries, and only the deployment details change:
| Where it lands | What changes | What carries the enforcement |
| --- | --- | --- |
| An Authorization Server you can extend | The AS is the Mission Issuer and gates issuance on Mission state | Issuance gating plus the PEP fleet |
| An Authorization Server you cannot change | A standalone [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) issues and governs, and tokens stay ordinary (until the AS redeems the [issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html)'s MAS-minted grants for Mission-bound, state-gated tokens) | PEP coverage entirely, with the PDP joining each token to its Mission at the point of use |
| A credential broker estate | The broker keeps custody and mints short-lived leases as before, now gated on Mission state and stamping the `mission` claim, with the Mission record held at the AS or the MAS | The broker as the issuance chokepoint, plus the PEP fleet at action time |
| An MCP server | The `tools/call` handler is the PEP, and the AuthZEN check runs per call | The tool boundary you already own ([the MCP application post](/notes/least-privilege-mcp-tool-calls-need-a-mission/)) |
| The agent harness | The harness mediates local side effects and gates every resume on Mission state | The harness as the PEP for the paths no gateway sees |
| A resource you cannot change | Nothing changes at the resource: it validates short-lived, state-gated tokens exactly as it does today, with revocation bounded by the token lifetime, or a gateway or MCP PEP fronts it for the classes that need more | Issuance gating with a published token-lifetime bound, and the gateway where per-action checks are required |
Wherever it lands, the PEP has to sit at the last controllable boundary before the effect. An orchestrator check does not replace a resource PEP for a resource the agent can reach directly, and an API gateway does not cover local shell, browser, or file-system effects it never sees.
And the equally opinionated negative. Do not start with Signals,
Deferred Approval and Revision, the Mandate, offline attenuation,
cross-domain projection, SCITT audit transparency, or the standalone
Mission Authority Server unless the estate calls for it: the
Authorization Server truly cannot change, or one Mission Issuer must
govern many systems at once. Every one of them is on the roadmap for a reason, and none of them belongs to walk. Build the recipe above, run it, and let deployment
experience tell you which of these you actually need.
The acceptance test is the handbook's running example, run against your own deployment. Revoke a Mission and watch the next consequential action fail closed within your published freshness bound. Then pull the mission id and reconstruct the whole story: the approval, the derived authority, the decisions including denials, and the revocation. If both work, the claim is real.
Why Runtime-Enforced first, stated as reasons rather than modesty:
- **Every normative dependency is ratified.** Runtime-Enforced rests on OAuth RFCs and finalized OpenID specifications, including the [AuthZEN Authorization API 1.0](https://openid.net/specs/authorization-api-1_0.html), which reached Final in January 2026. The Mission-Bound profiles themselves remain proposed Internet-Drafts: the claim is about the substrate under them, not their own status. The one tracked exception, the issuance core's Internet-Draft reference to the Actor Profile, is confined to its OPTIONAL delegation capability. There is nothing to wait for, and the drafts' wire details will keep moving with review, which is one more reason the laws and the architecture, not the claim names, are what to design against.
- **It is the smallest object that closes the named gap.** [The best practices' Agent Mission section](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) expects the mission to be translated into authorization requirements and leaves the process out of scope. The Runtime-Enforced deployment is that process: an approved, integrity-anchored task object, authority derived from it, actions enforced against it, state observable for it.
- **The roadmap should be earned, not speculated.** The experimental extensions encode design bets about asynchronous approval, fan-out, and unwinding. Real Runtime-Enforced deployments are what turn those bets into interfaces worth hardening, and the wagers underneath the whole design are named, with their falsifying evidence, in [where this could be wrong](/notes/the-convergence-and-the-wagers/#where-this-could-be-wrong).
# Not every resource checks Mission state
Read the recipe again and notice what it does not say. It does not say every resource server evaluates Mission status, and the fastest way to stall an adoption is to hear it that way. The architecture asks one question per class, not one architecture per resource: what is this path's staleness bound, and is it published?
That is a dial, and its mechanics live where the enforcement does, in [fail-closed and active freshness](/notes/mission-bound-runtime-enforcement/#fail-closed-and-active-freshness). The short version: at the coarse end, [crawl's](#crawl-baseline-issuance) state-gated issuance with short-lived tokens is a complete revocation story for the classes below high-consequence, no resource changes, and the lifecycle suite names that pattern a conforming freshness source. At fleet scale, the [Status List](/notes/mission-lifecycle-and-change/#observe-and-revoke-pull-and-push) keeps every enforcement point current with one signed fetch.
And even the walk stage rarely touches a resource server. Look at where the six boundaries put the PEPs: the gateway, the MCP host, the harness, the credential broker, the Authorization Server or MAS. Per-action enforcement lands at chokepoints the platform already controls, in front of resources that keep validating tokens as they always have. A Mission-aware resource server, with its object-level context, is the strongest placement and the last mile, not the price of admission.
The discipline is only honesty about the setting: route high-consequence classes through a boundary that has an active freshness mechanism rather than pretending a TTL is fresh, publish the bound each path actually runs, and let the [Reference's revocation matrix](/notes/mission-based-authorization-field-reference/#when-revocation-bites) price it. Legacy is not an exception to the model. It is the model's coarse end, claimed honestly.
# Run: governed and beyond
Run is the climb above walk, and the levels are one named artifact, the Mission Assurance Levels. A level is a dependency bundle: which documents you run, in adoption order. What you can prove is the orthogonal claims axis, and the claims, not the level name, are what a relying party compares. Climb the levels in order, and claim the one you are on:
| The Mission Assurance Levels | Stage |
| --- | --- |
| 1. Baseline Issuance | Crawl |
| 2. Runtime-Enforced | Walk |
| 3. Governed Agent | Run |
| 4. High-Assurance Agent | Run |
| Standalone governance (the MAS binding) | Parallel binding, every stage |
The levels are also an unlock ladder, and this is the reading that
answers [the read-only ceiling](#the-read-only-ceiling). In the
runtime contract's own
[action classes](/notes/mission-bound-runtime-enforcement/#which-actions-are-consequential),
each level makes a broader class of agent work defensible:
| Level | What you can now defensibly grant |
| --- | --- |
| Baseline Issuance | Read access that is attributable and killable: the pilot, governed |
| Runtime-Enforced | Reversible writes inside approved bounds. This is where the read-only ceiling breaks |
| Governed Agent | Unattended operation and delegation: the overnight agent and the sub-agent, with approval evidence behind every grant |
| High-Assurance Agent | The highest classes: irreversible actions, external commitments, and privileged administration, under mediated custody and action-bound approval |
| Level | Adopt | What you get | What you do not get |
| --- | --- | --- | --- |
| **Baseline Issuance** | [The Mission](/notes/the-mission-is-the-missing-abstraction/) core | Approved, integrity-bound Missions, state-gated token issuance, and a possession-independent kill switch for future derivation | Action-time defense, prompt revocation of already-issued tokens, safe unwinding |
| **Runtime-Enforced** | + [runtime enforcement](/notes/mission-bound-runtime-enforcement/) and [lifecycle](/notes/mission-lifecycle-and-change/)'s Status surface | Per-action PEP/PDP enforcement, current Mission-state checks, Status (or introspection) for revocation freshness, with the Signals push where seconds matter | Full consent-rendering evidence, runtime harness binding, orchestration unwind |
| **Governed Agent (agent safety minimum)** | + consent evidence and the harness ([approval integrity](/notes/from-a-request-to-an-approved-mission/), [agent runtime](/notes/the-agent-runtime-and-audit/)), growing with delegation, expansion, and orchestration as needed | Approval evidence, session-continuity stop, sub-agent containment, and tamper-evident audit where adopted | Proof that every possible side channel has been mediated. Deployments still must define their enforcement scope |
| **High-Assurance Agent (compromise-resistant)** | + mediated custody, action-bound approval, and a published execution-environment scope with no unmediated path ([runtime](/notes/mission-bound-runtime-enforcement/), [agent runtime](/notes/the-agent-runtime-and-audit/)) | The runtime profile's agent-compromise-resistant claim: a compromised agent cannot present the credential or reach a mediated action without a fresh independent approval | Protection inside the approved scope. A compromised agent can still misuse authority the Mission grants, which is why scope stays tight |
| **Standalone governance (the MAS binding)** | Mission Authority Server + [runtime](/notes/mission-bound-runtime-enforcement/) surfaces served by the MAS | Mission governance and per-action enforcement with an unmodified Authorization Server. The MAS serves Status and the lifecycle verbs itself, is the freshness source, and hosts expansion and Child Mission creation on its own submission surface | Mission-bound tokens and issuance gating, until estate ASs redeem the [issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html)'s MAS-minted grants for Mission-bound, state-gated tokens. Without that join, revoking a Mission stops nothing at the token layer, so enforcement rests entirely on PEP coverage |
A deployment names its level and its enforcement scope, and the
[Reference's implementation checklist](/notes/mission-based-authorization-field-reference/#the-implementation-checklist)
is the checkable form of that claim, down to the sentence a vendor
should be able to write.
# Composing with the ecosystem
The staged path is behind you. From here to the close, this part maps the terrain around it, starting with what a Runtime-Enforced deployment composes with rather than replaces. One stack answers where everything sits, from the substrate up, and the [standards map](/notes/mission-bound-authorization-standards-map/) (Appendix E) carries the spec-by-spec survey behind it:
| Layer | What sits there |
| --- | --- |
| Identity substrate | WIMSE, SPIFFE, and [draft-klrc-aiagent-auth](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/), with the [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) and [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) |
| Issuance | OAuth 2.0 (PAR, RAR) plus the Mission core: the approval event, the integrity anchors, the `mission` claim, with credential brokers as the custody plane |
| Decision | The runtime contract on [AuthZEN 1.0](https://openid.net/specs/authorization-api-1_0.html), with ARAP and AROP for governed requests |
| Tool boundary | MCP `tools/call` as the PEP most builders already own |
| Lifecycle and evidence | Status pull, Signals push, the harness, SCITT audit transparency |
**AuthZEN** standardizes the decision. The runtime profile deliberately specifies invariants rather than a wire, and the AuthZEN binding is the interoperable PEP-to-PDP surface. **[ARAP](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html)** turns a denial into a governed request, and the AuthZEN profile marks `out_of_authority` and `action_approval_required` denials as requestable so an agent can start narrow and ask for what it discovers it needs. That composition has a name in this handbook: **the discovery loop**. Deny, request, approve, expand, retry. It is how the open world arrives under governance. A tool or resource discovered at runtime shows up as a requestable denial rather than as an error or an excuse for standing breadth, and the widening lands as a separately approved successor Mission with lineage. The proposed **[AROP](https://github.com/openid/authzen/pull/531)** binds that workflow to OAuth completion for the token-side case. The [Least-Privilege MCP series](/series/least-privilege-mcp/) walks this per-call stack from the beginning, and the [MCP application post](/notes/least-privilege-mcp-tool-calls-need-a-mission/) shows both of its models becoming projections of one Mission.
Two observations from that series matter for the blueprint, because they name what the ecosystem still lacks:
- **Fulfillment is undefined.** ARAP standardizes the request, the status, and the re-evaluation, and deliberately not how an approval becomes durable authorization state. Token-resident state fulfills by minting, which AROP binds to OAuth issuance. Store-resident state fulfills by a write no standard defines. When the durable state an approval becomes is a Mission, fulfillment has a governed shape: an in-bounds approval is decision input, and a widening lands as a separately approved successor Mission with lineage.
- **The task object is the gap every layer routes around.** The denial signals, the decision API, and the approval workflows each standardize an interface inside a single call. None names the work the calls serve. That is the object this chapter proposes, and it is the piece to bring to the standards conversation rather than reinvent per deployment.
The identity substrate composes from below: the best practices for agent authentication, the [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) for delegation chains, and the [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) with the [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/) for attributable instances. [Mission-Bound Authority](/notes/mission-bound-authority/) is the binding between that substrate and the Mission.
Credential custody composes the same way: credential brokers, with the proposed [CB4A](https://datatracker.ietf.org/doc/draft-hartman-credential-broker-4-agents/) as the spec-shaped instance, are the custody fabric [the crawl stage](#crawl-baseline-issuance) already put to work, and CB4A's own future-work sketch, a native token carrying issuer, scope, expiry, and a confirmation key bound to an envelope hash, is reaching toward the `mission` claim.
Coexistence with the entitlement plane runs for years and needs a stated rule: the standing entitlement is the outer boundary, the Mission the inner one, derivation validates against what the underlying account currently holds, and a mid-Mission deprovisioning surfaces as a lifecycle event rather than a failure two systems disagree about. The gap between the two boundaries is the estate's own measure of what the layer bought.
# The roadmap: the next layer of the problem
The roadmap beyond walk has two labels, and the labeling is the design discipline, not a disclaimer. The **advanced profiles** are stable design to adopt when the use case arrives, and [the practice chapter](/series/building-mission-bound-authorization/) carries them: Deferred Approval ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)), Expansion and Completion with the fleet Management surface ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)), Child Delegation and Cross-Domain Projection ([Mission-Bound Authority](/notes/mission-bound-authority/)), Audit Transparency ([The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/)), Intent Shaping ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)), and the [Mandate](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-mandate.html).
The **experimental profiles** are for evaluation only. Each extension answers a question Runtime-Enforced deployments will surface in production, and each is experimental for a stated reason:
| Next-layer problem | Extension | Why it needs iteration | Stable path today |
| --- | --- | --- | --- |
| Revocation must bite in seconds | [Mission Lifecycle Signals](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html) | Push is a latency optimization over correctly sized status polling | Status polling sized to the risk, or introspection |
| Reviewers narrow instead of denying | [Mission Approval Revision](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-approval-revision.html) | Companion to Deferred Approval, riding an unratified substrate | Deny, then resubmit a narrower Intent |
| Open-ended tasks need governed drawdown | [Mission Progressive Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-progressive.html) | Ceiling-and-drawdown is a newer model | Per-step Expansion with fresh approval |
| Budgets and call caps need runtime metering | [Mission Consumption Metering](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-metering.html) | Cumulative-bounds enforcement is a newer model | Per-action constraint checks and short expiries |
| Fan-out at swarm scale without an issuer round-trip per delegation | [Mission Offline Attenuation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-attenuation.html) | Depends normatively on Attenuating Agent Tokens, an in-progress draft | AS-mediated Child Delegation |
| A Mission stops mid-workflow with work in flight | [Mission Orchestration and Unwinding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-orchestration.html) | Reversibility classes and unwind plans are less exercised | Harness stop behaviors, human review |
| The agent meets resources its approval could not name | [Mission Open-World Discovery](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-discovery.html) | Encounters adjudicated against a pre-consented ceiling, with the lying-resource and tainted-session floors, are a newer model | The discovery loop: deny, request, approve as a successor Mission |
An experimental extension frozen before deployment evidence exists would be a guess wearing a MUST, which is the whole sequencing argument.
Two Standards Track profiles sit beside this table rather than in it, because each tracks an unratified substrate: Deferred Approval rides OAuth Deferred Token Response, and the [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) rides the AAuth protocol. And the standalone [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) is not a roadmap item at all: it is Standards Track, the estate control plane of the layer, with the [issuance grant](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-issuance-grant.html) as its middle path. [The Authority Control Plane](/notes/the-authority-control-plane/) carries the trade this binding makes.
The [Reference's draft family at a glance](/notes/mission-based-authorization-field-reference/#the-draft-family-at-a-glance) is the whole 28-document catalog in one table, with these maturity labels on every row.
# What you will operate
The blueprint is honest only if it names the operational surfaces that
come with it. Adopting the Runtime-Enforced level means owning six things:
- **Derivation policy.** Someone maintains the policy that turns a
validated Intent into an Authority Set, per resource, the same
onboarding work scope design was. The record's `policy_version`
exists so a derivation can be re-checked, and the owner is typically
the IAM team together with the resource owners.
- **The approval surface.** The shaper is client-side and app-owned.
The consent rendering and approval routing belong to the Mission
Issuer, and [Deferred Approval](/notes/from-a-request-to-an-approved-mission/)
lets an existing request-and-approval workflow drive the decision.
- **The PEP fleet.** Gateways, MCP servers, egress proxies, and
orchestrators each need a PEP at the last controllable boundary,
owned by the platform teams that own those boundaries, with the
enforcement-scope statement naming what is and is not covered.
- **The PDP as a tier-0 dependency.** Fail-closed means agents stop
when the PDP is unreachable. That is the design, and the
permit-as-lease model with published staleness bounds
([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/))
is the availability story: bounded caching, never fail-open. The
design principle behind it: availability follows the artifact, not
the issuer. Mission state distributes as signed, TTL-bounded
artifacts, the Status response and the fleet-scale
[Status List](/notes/mission-lifecycle-and-change/#observe-and-revoke-pull-and-push),
cacheable and servable from replicas the way a JWKS is, so the
Issuer's control plane can be briefly unreachable without the data
plane losing its state source. The staleness bound you publish is
also the outage you can ride: choose it with the Issuer's recovery
objective in view, because a bound shorter than your recovery time
is a promise to halt. And state the blast radius in the runbook
rather than discovering it: an outage past the bound halts
PDP-gated classes and only them, issuance-gated paths ride to token
expiry, and the break-glass below is the named exception.
- **The incident playbook.** Revocation by `mission_id` is the kill
switch, Status is how consumers learn it (with the Signals push where
seconds matter), and fleet-scale response (enumerate a compromised
principal's active Missions and bulk-revoke, dry-run first) is
[Mission Management](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-management.html)'s
job.
- **The break-glass path.** Fail-closed is the design, so name the
pressure valve before an outage improvises one: a dual-controlled
emergency bypass that auto-expires, emits the same evidence as the
path it bypasses, and suspends the runtime-enforcement claim for
whatever it touches while active. A break-glass nobody designed
becomes a standing unmediated path.
## The derivation policy, concretely
The first bullet above is the one deployments ask about most, so here
is the artifact at working depth. The family fixes no policy
language, deliberately: derivation policy is deployment policy, the
same kind of asset scope design was. What the drafts fix is its
contract. Derivation is mechanical, so the policy must be a function
from a validated Intent and the registry's facts to an Authority Set,
with no model in the loop and no discretion left for decision time.
One boundary keeps the word mechanical honest. The function consumes the Intent's structured members only. The Intent's free-text `constraints` bind at disclosure: they are what the Approver reads beside the derived authority, never what the function parses. A bound the PDP must enforce enters as structure, in a `proposed_authority` entry's machine-actionable constraints the shaper may translate from the request's words and the AS only ever narrows, or in the rule itself, authored by a human who read the same words. "Q3 2026" becomes an enforceable period bound at one of those two doors, and the Approver's check that the structure matches the words is what closes the gap between them.
One rule, in the shape most estates will write it. The language is
illustrative and the rows are the contract:
| The rule's parts | This rule (finance read, board-packet class) |
| --- | --- |
| Matches when | `resources` names the finance service and the validated Intent's `purpose` is in the reporting class |
| Registry facts consulted | The Agent Deployment's eligibility bounds and risk tier |
| Emits | One `mission_resource_access` entry: `query_financials`, read-only, with the fiscal-period bound sourced from the rule or the structured proposal, never parsed from free text |
| Never emits | Write actions, exports, or any entry for a resource the Intent did not name |
| Notices | The data-classification notice class, so the disclosure renders it |
Testing is fixtures, the same discipline the disclosure templates
use: a corpus of representative Intents with their expected Authority
Sets, run on every policy change, with the diff reviewed like a code
change, and `policy_version` on each record pointing at exactly which version derived what. That pointer is also the recall mechanism: a derivation version found faulty turns recall into a query, every Mission derived under it enumerable and suspendable as a class. Ownership follows the two vocabularies. The IAM
team owns the function and the ceilings. Each resource owner owns the
entries that touch their service, because
[the resource owns the meaning](/notes/mission-based-authorization-field-reference/#who-owns-the-meaning).
And price the authoring surface honestly, because this is scope
design's workload relocated, with better economics but not zero
economics. The surface is bounded by resources and action classes
rather than by integrations, it is populated once per resource and
amortized across every Mission that touches it, and templates are
where the common cases stop costing anything at all. Watch three
numbers the way the fatigue budget watches its four: the
unmapped-resource rate (Intents that fail derivation because no rule
exists), the template-hit rate (how much work rides pre-approved
shapes), and the rule-exception rate (policy edited under deadline,
the tell that the ontology is wrong).
One of those surfaces, rendered. Templates are where derivation
policy and the approval surface meet operations: pre-reviewed mission
shapes with risk and duration visible, governed by the policy
framework, and new templates entering through policy review rather
than around it:

One more surface rides with all six: the signing keys. The Mission is a durable record rather than a token in flight, so an issuer key rollover invalidates projections and signed status while the records survive, and every projection re-derives under the new key. Key custody and a rehearsed recovery drill belong in the enforcement-scope statement, and the drill's measure is the time back to governed operation.
# What the community still has to standardize
A blueprint should also name the pieces nobody owns yet. Six stand out.
- **A standard task object.** This family proposes one, as individual drafts published for discussion. The gap it fills is now named in the best-practices document, and the per-call standards keep converging on shapes that assume something like it exists. The right venue conversation, whether that is the OAuth working group, AuthZEN, or both, is the next step, and deployment experience is the strongest input anyone can bring to it. The venue question also has a shape answer, because a twenty-eight-document family from one author is what working groups reflexively distrust: the chartering surface is deliberately small, the issuance core and the runtime invariants, with everything else evidence of thinking that enters scope only as the community pulls it.
- **Approval fulfillment for store-resident state.** Every Zanzibar-style store's write API is product-specific, so the approval-to-state step of ARAP has no interoperable form outside OAuth issuance. A Mission gives the durable state a governed shape, but the write itself still needs a standard.
- **Task binding at the tool boundary.** The MCP proposals standardize the denial and the brokered approval. Carrying a verifiable task reference through `tools/call`, so the resource can weigh the call against the approved work, is the natural next step the [MCP application post](/notes/least-privilege-mcp-tool-calls-need-a-mission/) sketches.
- **Instance-attested delegation as the default.** The actor chain, instance assertion, and agent provenance exist as individual drafts. The mission layer assumes them. Their adoption path is part of this blueprint, not an afterthought, because an unattributable actor makes every downstream guarantee weaker.
- **Trust establishment for discovered counterparties.** The Mission layer governs whether newly requested authority is inside the approved task. Whether a runtime-discovered issuer, tool server, or its metadata can be trusted at all is the substrate problem beneath it, and the [Open-World OAuth series](/series/open-world-oauth/) maps that terrain: discovery, issuer trust, sender constraints, and metadata integrity are prerequisites this blueprint composes with rather than solves.
- **Exposure control surfaces.** Least privilege has standards and least exposure has an essay: [bounding what the agent may see](/notes/least-exposure-is-broader-than-least-privilege/) is enforced today only at its edges (catalog filtering, egress mediation, the taint rule). Scoping retrieval, memory, and context assembly to the approved task has no interoperable form, and the exposure half of minimal disclosure needs profile work nobody has started.
# Where this leaves you
If you arrived from [draft-klrc-aiagent-auth](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/), you now have the answer to the question its Agent Mission section leaves open. The Mission is the durable, approval-backed record of the task your authenticated agent pursues, and the adoption of it is staged: crawl with the issuance core, walk with the Runtime-Enforced level, run up the Governed and High-Assurance Agent levels as the deployment earns them.
Ship the reference security architecture, and add the agent-specific assurance pieces when the system is actually running agents. Its dependencies already shipped, end to end, it is the smallest interoperable surface that makes the approved task first-class, and it is the version of this architecture that earns the right to harden the rest. The [Building Mission-Bound Authorization](/series/building-mission-bound-authorization/) chapter carries each control at implementation depth, the editor's copies are public, and the [Reference](/notes/mission-based-authorization-field-reference/) is the citable definition. That experience has a place to land: [issues and pull requests on the draft repository](https://github.com/mcguinness/mission-bound-authorization/issues) are the fastest path into the documents. The gap has a name now. It should have an object.
---
# The Mission Is the Missing Abstraction
Canonical URL: https://notes.karlmcguinness.com/notes/the-mission-is-the-missing-abstraction/
Markdown URL: https://notes.karlmcguinness.com/notes/the-mission-is-the-missing-abstraction.md
{{< tldr >}}
- **The anchor.** [AI Agent Authentication and Authorization](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) names the Mission as the task the agent pursues, and declares its translation into authorization requirements out of scope. This chapter is that translation, and this part defines the object it starts from.
- **The argument.** OAuth has no first-class object for *the user-approved task* that authority belongs to. Tokens, sessions, scopes, and grants project from this missing object. None of them is it. This handbook proposes that object, the Mission, for OAuth 2.0, and this part argues why it is the missing primitive.
- **What lives in this part.** Why the Mission is the missing object: [the three layers](#three-layers-one-gap), [how the argument converged](#how-the-argument-converged), the [Mission as the common object](#the-mission-as-the-common-object), [three objects, three lifecycles](#three-objects-three-lifecycles) (agent identity, Agent Deployment, and the Mission each keep their own owner and revocation), the [Mission-versus-Intent boundary](#mission-versus-intent), the [spine](#the-intent-to-enforcement-spine), and [where this leads](#where-this-leads). This is the on-ramp for readers who start on the protocol side. If you came from the card chapter, [From the Card to the Architecture](/notes/from-the-card-to-the-architecture/) is the joint that brought you here.
- **Where the definitions live.** The [Reference](/notes/mission-based-authorization-field-reference/) is the citable home, the page to link when someone asks what mission-based authorization is.
- **The card analogy.** A Mission is a corporate card issued for a stated purpose, not a signature on the company checkbook. ([Where the analogy breaks](/notes/agents-need-a-corporate-card-not-a-blank-check/#where-the-analogy-breaks).)
- **The laws.** This part establishes [Durability and Narrowing (Laws 1 and 3)](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority): the approved task outlives its tokens, and derived authority can only narrow.
- **Spec (editor's copy).** [Mission-Bound Authorization for OAuth 2.0](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html), the issuance core this part defines.
**Reading path.** ~16 minutes start to finish. Read in order.
{{< /tldr >}}
# Overview
The industry's best-practices path for AI agent identity is taking shape. [AI Agent Authentication and Authorization](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) composes WIMSE, OAuth, and SPIFFE into a way to provision, credential, and authenticate agents and to delegate user authority to them. And it names the thing all of that identity exists to serve. Its Agent Mission section (10.1 in the current -03) defines the Mission as "the task or objective the Agent will pursue," and then draws a line: "The process through which the mission is translated into authorization requirements is out of scope of this specification."
This part is about what lives on the other side of that line. OAuth represents credential issuance and delegation. It does not represent the durable task those credentials serve. The stack can prove who is acting and what credential they hold. It cannot prove the work is still authorized. Agents need authority that remains bound to a user-approved purpose as work spans tokens, calls, tools, sub-agents, and time. This part argues that the missing object is a Mission: a durable, approval-backed governance object on the OAuth substrate, and the anchor for every other claim the handbook makes. One warning travels with the whole argument, and it is settled in [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/): a mission-bound token that nothing enforces is record-keeping, not agent safety. The object defined here earns its keep at the point of use.
The Mission is not a new way to express authority. Rich Authorization Requests already do that. It is the approved *task* that authority is derived for, bound to, and gated on.
> **Mission-based authorization governs the approved task, not just the credential, session, or individual request.**
This is more than a category. It is a claim about the shape of the stack. Identity, authentication, authorization, policy, and transport are named layers, and none of them governs delegated authority as an object of its own: what was approved, by whom, within what bounds, until when, and whether it is still in force. Read operationally, the missing layer is a control plane: the Mission record is desired state, issuance and the runtime gate are the data plane consulting it, and [the concluding chapter](/notes/the-authority-control-plane/) develops that reading in full. The drafts decompose the layer into five deployable packages (Mission Control, Authority Distribution, Runtime Enforcement, Agent Execution Governance, and Evidence and Accountability), so the missing layer is a small set of named components rather than a monolith.
And the layer's value is context before it is control. A resource evaluates each request at perfect local resolution and zero task resolution: it can read the call and never the reason. The Mission is the one place where task-grain context exists to be consulted, and that context cuts both ways. It makes an alarming-looking action priceable, a database deletion that is step four of an approved migration whose copy steps already ran, and it makes an innocent-looking one conspicuous, a payroll task's agent reading the M&A folder. Neither judgment is possible at the resource, because the why never arrives there.
The largest claim this chapter makes is that delegated authority management is a [layer with no standard form](/series/designing-mission-bound-authorization/#the-missing-layer), missing not from every estate but from the interoperable stack, with its own [laws and vocabulary](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority), and the Mission is one proposed standard form of it. A system qualifies as mission-based with four substrate properties: an approved task object (not a prompt, trace ID, session, or token), authority *derived from* that task, narrow-only delegation, and a lifecycle that can expire, revoke, expand, and complete. It backs an action-time defense claim only with two more: runtime enforcement against current state, and evidence that binds back to the task. The [Reference](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test) is the citable version of that test. This part makes the case for the object the test is about.
# Three layers, one gap
An agent deployment has to answer three different questions, and only two of them have best-practices answers today.
**How does the agent authenticate?** That is [draft-klrc-aiagent-auth](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) territory: workload identity, credentials, transport and application authentication, and the OAuth machinery for acting on a user's behalf.
**Which instance is acting, for whom?** A platform's `client_id` collapses every concurrent agent session into one identity, which defeats per-agent policy, audit attribution, and containment. The [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) makes the delegation chain explicit, the [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) identifies the specific instance, and the [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/) adds attested instance identifiers and agent provenance.
**What may this instance do, why, for how long, on whose approval?** That is the Mission, and it is the layer the Agent Mission section leaves open. The first two layers are necessary and not sufficient. A perfectly authenticated, perfectly attributed agent instance holding valid credentials can still be working on a task that no longer exists, with no layer positioned to notice.
> **Identity says who. Credentials say what may be accessed. The Mission says what the work is, who approved it, and when it ends.**
# How the argument converged
Several earlier lines of work expose the same structural gap from different directions:
**[You Don't Give Agents Credentials, You Grant Them Power of Attorney](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/).** Enterprise IAM governs who an agent is and what each call may do. It does not govern whether the mission behind those calls should still be running. Tokens stay valid past the moment approval expired. Policies still pass after intent has changed. Credentials remain secure while the work they authorize has become unauthorized. The breach is structurally invisible because no layer of the stack was built to ask the question. Agents need delegated authority that behaves like a Power of Attorney, not a credential.
**[Mission Shaping](/series/mission-shaping/).** A user approves an objective, not the authority needed to execute it. Something has to turn that approval into something a control plane can evaluate. In most deployments today that shaping step is implicit, local, or delegated to the agent itself. The model infers its own boundaries and the system trusts it. That is not governance. It is optimism.
**[Open-World OAuth](/series/open-world-oauth/).** OAuth succeeded because its closed-world relationships were known ahead of time. The client knew the AS, the RS knew its issuers, scopes were configured before deployment. Agents push OAuth into an open-world model where tools, Resource Servers, and ASes are discovered at runtime and may not share prior trust. The substrate problem covers protocol mechanics like discovery, resource binding, sender constraints, and metadata integrity. The governance problem is deciding whether newly requested authority is still inside the task the user approved. Fixing the substrate does not fix the governance.
**[Sessions Are Not Missions](/notes/sessions-are-not-missions/).** Modern agent harnesses make work durable across restarts, devices, sub-agents, and reconnected tools. That durability is a runtime property, not a governance property. A session can prove the runtime survived. It cannot prove the mission did. The harness that runs the agent is not the layer that owns the mission.
**[Authorization Is the Other Half of Executable Intent](/notes/authorization-is-the-other-half-of-executable-intent/).** Evals showed the pattern: take intent expressed in language and compile it into an artifact a machine can execute. That made intent executable for verification, answering whether the agent behaved. Authorization needs the same move at a higher bar, because a grant confers authority rather than evidence, and a grant that is right most of the time is a breach the rest of the time. The compiled artifact on the authorization side needs an approval path, a default that denies, and a lifecycle that ends.
**[AI Agent Authentication and Authorization](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/).** The best-practices convergence itself points here. It gives agents workload identity, credentials, and delegated user authority, then defines the Mission as the task the agent pursues and places the translation of that mission into authorization requirements out of scope. The gap is now named in the document the industry is converging on.
An earlier working series, [Mission-Bound OAuth](/series/mission-bound-oauth/), sketched the durable governance object these lines of work point at. This chapter is the concrete answer. Mission-Bound Authorization makes the task explicit. The Authorization Server holds the Mission, commits its maximum approved authority, and governs its lifecycle. Every derived credential and decision carries a reference to it, and audit joins activity across hops on it.
# The Mission as the common object
The Mission lets OAuth token issuance, runtime enforcement, delegation, and audit refer to the same approved task.
- **Issuance** projects the Mission into every derived token through the `mission` claim, carrying `id`, `issuer`, and `authority_hash`. Token issuance is gated on Mission state.
- **Runtime enforcement** evaluates each consequential action against the current Mission before it happens, and writes decision evidence bound to the Mission.
- **Delegation** is carried by the RFC 8693 `act` chain. A delegated token keeps the same `mission` claim with subset authority and an `act` chain identifying each actor, so revoking the Mission stops all further derivation for every actor in the chain at once. Already-issued tokens age out within their short lifetimes, or die faster where introspection or runtime checks consult Mission state.
- **Lifecycle** surfaces let an authorized party revoke the Mission (with suspend, resume, and complete supplied by the Status companion), and let a consumer learn its current state.
- **Audit** keys on the Mission and its integrity anchors (`intent_hash`, `authority_hash`), making the records tamper-evident and independently verifiable.
Without a Mission, each system carries its own implicit notion of the task. Logs join by approximate timestamps, and stopping the work means finding every credential and execution surface independently. With a Mission, each layer consumes an authenticated projection of one governance record, and revoking or expiring the Mission stops all further derivation of authority for the task at once.
# What a Mission contains, and what is not one
Concretely, an approved Mission holds the approved **Mission Intent** (goal, resources, constraints, expiry), the **Authority Set** derived from it, its two integrity anchors (`intent_hash`, `authority_hash`), a **lifecycle state**, and its identity (`id`, `issuer`). The crucial relationship is that the Authority Set is *one component* of the Mission, not the whole. A bundle of permitted actions with no approved task, lifecycle, or evidence is just authority, which OAuth's `authorization_details` already gave us without any of this.
That is also the fastest way to see what is *not* a Mission:
- A **token** is a short-lived projection. Its `jti` names the token, not the task.
- A **scope** or authorization detail expresses authority, not the approved task or its lifecycle.
- A **session** preserves runtime continuity but commits no authority.
- A **consent record** proves an approval happened but does not govern the work over time.
- A **`purpose` URI** labels a task *class*, with no instance lifecycle.
- A **trace or task ID** correlates activity but carries no approval or authority.
- A **Mandate** is a portable, verifiable *statement about* a Mission, minted by its issuer. Evidence, not a second object, and not a competing primitive.
Each is something the Mission binds to or projects into. None of them *is* the approved task. The full object model, a concrete JSON record, the six-property litmus, and what anchors a Mission's legitimacy when no user is present at approval time are in the [Reference's object model](/notes/mission-based-authorization-field-reference/#the-mission-object-model).
# Three objects, three lifecycles
The list above rules out artifacts inside the authorization machinery. Two more objects live outside it, in the deployment's own systems, and an estate that runs agents under an agent identity system and this layer governs three distinct objects. Each answers a different question, each has its own owner, lifecycle, and revocation, and the model stays clean only while none absorbs another's job.
| Object | The question it answers | Who owns it | What revokes it |
|---|---|---|---|
| **Agent identity** | Who is acting? | The deployment's agent IAM, a registry or directory outside this family | Deactivating the agent or the instance |
| **Agent Deployment** | What is running? | The deployment's change governance | Retiring the behavioral version |
| **Mission** | Why does the authority exist? | The Mission Issuer | Revoking the approved task |
**Agent identity** is the logical agent and, where the instance profiles are deployed, the concrete instance. The Mission system consumes it as the `client_id`, the instance assertion, and verified instance claims, and defines none of it. Defining agent identity, or the registry that holds it, is an explicit non-goal of this family.
**Agent Deployment** is the approved behavioral version of the agent: its code, model, system prompt, tool allowlist, data scope, and runtime configuration. A change to any of these is a new Agent Deployment, and which changes require re-approving standing Missions is policy that change governance records ([the lifecycle part](/notes/mission-lifecycle-and-change/#when-the-agent-changes-not-the-mission) takes this up). The boundary composes rather than duplicates: the Agent Deployment's tool allowlist is version-scoped, while the core's `allowed_tools` Common Constraint bounds tools per Mission entry, task-scoped. The name is deliberate. This object is distinct from the Mission Deployment Profile, which is the estate's published claims manifest, not a property of an agent.
**The Mission** is this layer's object: the approved task, its Authority Set, and its lifecycle.
The temptation the separation resists is overloading. A `purpose` field on the agent's identity record describes what the agent is for, in general and indefinitely, which is exactly the shape of standing authority this handbook exists to retire. The Mission carries why this authority exists now, for this task, until this work ends. Keep the who durable and the why bounded.
Authorization then composes conjunctively across the three lifecycles. A decision can depend on agent state, Mission state, and credential validity, and each gates independently: a valid credential never overrides a revoked agent or a non-active Mission, and a live agent under an active Mission still fails on an expired credential. The [Reference](/notes/mission-based-authorization-field-reference/#agent-iam-and-the-agent-registry) carries the working integration, what the Mission system consumes from an agent registry and the combined stack the two control planes form, and the [architecture draft's](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html) Three Objects, Three Lifecycles section is the normative statement.
# Mission, Intent, Plan, Execution
Four related concepts appear around a user-approved task. Keeping them separate prevents the Mission from becoming a vague name for every artifact in the system.
| Layer | What it is | Who owns it | Where it lives |
|---|---|---|---|
| **Mission** | The durable governance object that commits approved authority and owns the task lifecycle. | Authorization Server | Mission record |
| **Mission Intent** | The structured proposal for the task. Untrusted until validated. Not a Mission until consent. | Client and Shaper | The `mission_intent` parameter on the wire |
| **Mission Plan** | The agent's execution strategy: decomposition, tool choice, sub-agent delegation. Grants no authority. | Agent runtime | Agent harness |
| **Mission Execution** | The runtime activity: derived tokens, API calls, decisions, evidence. | Resource Servers, PDPs, agent runtime | Tokens, decisions, audit records |
A Mission can outlive multiple plans and execution attempts. Plans and execution draw from its authority and can only narrow it. Re-planning is therefore free: the agent can observe, re-plan, and retry inside the bounds without any authorization event, because plans grant nothing, and a plan that discovers it needs authority the Mission lacks engages the [discovery loop](/notes/adopting-mission-bound-authorization/#composing-with-the-ecosystem) rather than editing the Mission. The Intent can be edited or refused before approval. The Plan describes *how* the agent intends to act but commits nothing. Execution events reference the Mission. Revoking it stops future derivation and, where state is checked, future execution. It does not undo completed actions.
This separation prevents three mistakes: treating the Mission Intent as already approved, treating an agent plan as authority, and treating an executed call as the governance object itself.
# Mission versus Intent
The Mission and Mission Intent are not the same object, and conflating them is the most common mistake about this work.
> Mission Intent is the proposal. Mission is the approval. The integrity anchors commit the moment of transition.
The transition is one explicit moment, the **approval event**. The Authorization Server validates the Intent, derives an Authority Set, the user consents to the rendered Intent and derived Authority Set, and the AS records a Mission committed by `intent_hash` and `authority_hash`. Before it, only Intent exists. After it, only the Mission is authoritative.
Four consequences matter most:
- **Trust.** Mission Intent is untrusted input. The client, the shaper, the model behind it, and the raw prompt are all the untrusted plane. The AS's admission decision is the trust boundary.
- **Authority.** Intent describes a task. It does not commit authority. Authority is committed only by `authority_hash` over the Authority Set the AS *derives* from the Intent. Submitting a broad Intent asks the AS to derive from a broad task. It is not a demand for broad authority.
- **Integrity.** `intent_hash` commits the *approved* Intent (the post-validation form the user consented to, not the raw prompt). The Intent itself is never edited behind the user's back. An unrecognized `resources` entry causes the AS to refuse the submission or, by deployment policy, to omit the entry from the derived Authority Set with the partial derivation signaled to the client. Validation-time *narrowing* applies to the derived Authority Set (committed by `authority_hash`). It does not rewrite the Intent.
- **Lifecycle.** A Mission has exactly one approved Intent for its whole life. Widening means a *successor* Mission ([Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/)), not a second Intent.
The full treatment (the approval-event diagram, the remaining consequences, and the precise definitions) is in [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/) and the [Reference](/notes/mission-based-authorization-field-reference/).
# Why this makes IBAC practical
Intent-Based Access Control is hard when intent is *inferred* from agent behavior at enforcement time. A policy decision point (PDP) reconstructing "what did the user want" from a prompt or a trajectory is doing probabilistic interpretation against adversarial input. It becomes practical when enforcement *consumes* approved intent instead of reconstructing it. The AS checks a structured proposal, the user approves it, and `intent_hash` and `authority_hash` commit the result.
> The interpretation problem moves to consent time, where the accountable approver is present and the Authorization Server owns admission, instead of to enforcement time, where the agent is adversarial-input territory and the PDP has no approver to ask.
That is the design move that makes IBAC ship.
# The intent-to-enforcement spine
The handbook follows one temporal spine, **Intent โ Mission โ Authority โ Enforcement**:
1. **Intent.** A prompt or trigger is shaped into a structured Mission Intent. The shaper proposes, it does not grant authority ([From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/)).
2. **Mission.** The Authorization Server validates the Intent, derives the Authority Set, and renders that derived set for consent. On approval it records a Mission committed by `intent_hash` and `authority_hash` (this part, and the [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html)).
3. **Authority.** The Mission's canonical Authority Set is projected into tokens that each carry the `mission` claim ([Mission-Bound Authority](/notes/mission-bound-authority/)).
4. **Enforcement.** A PDP checks each consequential action against the current Mission and writes per-decision evidence ([Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/)).
Each part specifies one or two steps. The spine explains *when* each concern enters the system. Its object-level companion is [the Reference's canonical sequence](/notes/mission-based-authorization-field-reference/#noun-distinctions), Mission Intent to Authority Set to Mission to Projection to Runtime Decision to Evidence, which names *what* each transition produces.
# Governance and runtime are two halves
> Mission governance makes authority auditable, bounded, and revocable. Runtime enforcement is what prevents an active Mission from becoming ambient authority.
The Mission says what was approved, by whom, for how long, and within what bounds. [Runtime enforcement](/notes/mission-bound-runtime-enforcement/) decides whether *this* concrete action, with these parameters, by this actor, at this time, is allowed under those bounds. For any agent that touches consequential writes or external side effects, the runtime layer is doing the safety-critical work. That is why the sharpest way to state it is:
> A mission-bound token without runtime enforcement is governance metadata, not agent safety.
This is also the lethal-trifecta boundary. The governance object is common, but private-data reads, untrusted-content ingestion, and external side effects stay separately typed and separately evaluated under it, so the Mission never becomes one ambient bundle. The [architecture chapter](/series/designing-mission-bound-authorization/) develops both halves.
How much of this a deployment runs is a claimable level, the Mission Assurance Levels, and the taxonomy recurs through the handbook:
| Level | One line |
| --- | --- |
| Baseline Issuance | Governance metadata and a derivation kill switch, not action safety |
| Runtime-Enforced | Per-action enforcement plus state freshness, on ratified substrate |
| Governed Agent | Consent evidence, harness binding, and operational controls |
| High-Assurance Agent | Mediated custody, action-bound approval, and no unmediated path |
The [Reference's implementation checklist](/notes/mission-based-authorization-field-reference/#the-implementation-checklist) turns each level into a checkable claim.
# Where the details live
This part is the argument. The affirmative definition of the primitive and the precise, citable definitions live in the [Reference](/notes/mission-based-authorization-field-reference/):
- **The primitive, defined.** The canonical decomposition is the Reference's [object model](/notes/mission-based-authorization-field-reference/#the-mission-object-model) (this part's "what a Mission contains" is the abbreviated view of it), along with [what becomes possible only with a Mission](/notes/mission-based-authorization-field-reference/#what-becomes-possible-only-with-a-mission), the [trust boundaries and roles](/notes/mission-based-authorization-field-reference/#trust-boundaries-and-roles), and the [defense of the name](/notes/mission-based-authorization-field-reference/#why-mission).
- **The citable tests and structures.** The Reference carries the [six-property litmus and near-miss table](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test), the [object model with a concrete record](/notes/mission-based-authorization-field-reference/#the-mission-object-model), the [lifecycle states](/notes/mission-based-authorization-field-reference/#lifecycle-states), the [competitive landscape](/notes/mission-based-authorization-field-reference/#competitive-landscape), the [deployment ladder](/notes/mission-based-authorization-field-reference/#the-deployment-ladder), the [glossary](/notes/mission-bound-authorization-glossary/), and a [reproducible integrity-hash test vector](/notes/mission-based-authorization-field-reference/#reproducible-test-vector).
# Where this leads
This is the framing post. The architecture chapter continues with [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/), the staged build order: the Runtime-Enforced level on ratified substrate now, the experimental roadmap as deployments teach us what to harden next. The concluding chapter, [Weighing Mission-Bound Authorization](/series/weighing-mission-bound-authorization/), carries the model beyond its bindings and the wagers. From there, the [Building Mission-Bound Authorization](/series/building-mission-bound-authorization/) chapter reads the [Mission-Bound Authorization](https://github.com/mcguinness/mission-bound-authorization) drafts as one layered architecture:
- **[Part 1: From a request to an approved Mission.](/notes/from-a-request-to-an-approved-mission/)** Approval-time integrity: turning a prompt into a candidate Intent (shaping), committing the structured consent disclosure the Authorization Server recorded as rendered (consent evidence), and handling async or narrowed approvals (deferred approval and its revision companion).
- **[Part 2: Mission-bound authority.](/notes/mission-bound-authority/)** The bridge from agent identity to Mission authority: the `mission` claim on tokens bound to attested agent instances, the actor chain, and delegation that only narrows, from act chains to Child Missions to offline attenuation.
- **[Part 3: Runtime enforcement.](/notes/mission-bound-runtime-enforcement/)** The load-bearing safety layer and the heart of the Runtime-Enforced level. Before each consequential action, an enforcement checkpoint (PEP) gets a permit from a PDP that evaluates the action against the current Mission.
- **[Part 4: Lifecycle and change.](/notes/mission-lifecycle-and-change/)** State over time: status and signals for observation and revocation, expansion for governed growth, completion for safe narrowing.
- **[Part 5: The agent runtime and audit.](/notes/the-agent-runtime-and-audit/)** Binding sessions to Mission state, unwinding in-flight work safely, and tamper-evident, independently verifiable evidence.
The issuance core defined here stands on its own as the OAuth-substrate baseline. Everything else is an optional companion that layers on it without changing it, and optional means optional to the protocol core, not to the claims: runtime enforcement is required for the Runtime-Enforced claim, and Consent Evidence and the harness are required for the Governed Agent claim. [Least-Privilege MCP Tool Calls Need a Mission](/notes/least-privilege-mcp-tool-calls-need-a-mission/) applies the full spine to a concrete MCP deployment.
The so-what is Durability and Narrowing. Authority that outlives credentials and only narrows as work fans out requires something durable for tokens to project from and narrow against, and nothing in the current stack is that thing. Now it has a name, a record, and a kill switch.
---
# From the Card to the Architecture
Canonical URL: https://notes.karlmcguinness.com/notes/from-the-card-to-the-architecture/
Markdown URL: https://notes.karlmcguinness.com/notes/from-the-card-to-the-architecture.md
{{< tldr >}}
- **The move.** [What the Corporate Card Already Solved](/series/what-the-corporate-card-already-solved/) taught the governance loop with no protocol in sight. This part translates it: [each rule becomes a law](#the-five-rules-become-five-laws), [the mapping table becomes an object](#the-mapping-table-becomes-an-object), [the corporate-card test becomes the claim gate](#the-corporate-card-test-becomes-the-claim-gate), and [each build list names a draft cluster](#the-build-lists-name-the-draft-family).
- **The one-sentence version.** The card chapter ends every post with a build list, the things the agent stack cannot borrow from the expense world. This draft family is that build list, itemized.
- **You do not need the card chapter first.** The translation reads on its own, and each section links the card post it translates. But the card chapter is the fastest way to hold the whole model, and it is written for the people who will never read a spec.
- **The card analogy.** This part is the card analogy, run in reverse: from the loop finance already operates to the object and protocol surfaces agents need.
- **The laws.** All [five laws of delegated authority](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority) appear here in both forms, card language and architecture language, with the mapping argued rather than asserted.
- **Spec (editor's copy).** [An Architecture for Mission-Bound Authorization](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-architecture.html), the document that states the model the way this part arrives at it.
**Reading path.** ~9 minutes start to finish. Read in order.
{{< /tldr >}}
# Overview
[What the Corporate Card Already Solved](/series/what-the-corporate-card-already-solved/) makes one claim across five posts: enterprise finance independently discovered the governance architecture that agent authorization now requires, and proved it at global scale. An instrument issued for an approved purpose, an approval bound to what the approver was shown, delegation that only narrows, a network that authorizes every transaction, and endings that actually end. Every post in that chapter closes the same way, with the places the analogy breaks and the build list those breaks imply.
This chapter is the build. The card chapter deliberately never says what the protocol work looks like, because the loop needs no standard to be understood or even operated. But the translation into standards terms is real work with real choices, and this part is the joint between the two: what each card rule becomes when it is stated for any kind of authority, what the mapping table becomes when it is made into an object, and which drafts answer which break. If you read the card chapter first, this part will feel like recognition. If you did not, it works as a fast tour of the whole model, and the card chapter is waiting when you want the protocol-free version.
# The five rules become five laws
The card chapter distills each post into one rule, stated in the card's language. This chapter states the same invariants for any substrate and calls them the [five laws of delegated authority](/series/designing-mission-bound-authorization/#the-five-laws-of-delegated-authority), because they hold whether the authority is money, API calls, or signatures, and whether the substrate is OAuth or something else. The mapping is close but not decorative, and the two places it bends are worth the look.
| The card rule | The law it becomes |
| --- | --- |
| Credentials are projections of authority, not its durable source | 1. Durability: authority must outlive credentials |
| The approval binds the disclosure, not the intent | 2. Attribution: the approval record commits what was shown, and every action stays attributable |
| Delegation narrows, or it contaminates | 3. Narrowing: authority can only narrow as work fans out |
| Authorize the action, not the instrument | 5. Containment: execution must continuously remain inside approved purpose |
| Ending the credential is not ending the arrangement | 4. Termination: revocation must end authority, not merely tokens |
**Rule 1 is Durability seen from the wallet.** The [card post](/notes/agents-need-a-corporate-card-not-a-blank-check/) argues that the plastic is replaceable and the governance record is the asset. The law states the consequence: the approved task is the durable object, tokens are its short-lived projections, and governing the projection is not governing the task.
**Rule 2 is Attribution's second clause.** [You Approve What You Were Shown](/notes/you-approve-what-you-were-shown/) binds the approval to the rendered disclosure, and the architecture writes that into Attribution itself: the approval record commits exactly what the approver was shown, because an approval nobody can replay makes attribution collapse into archaeology. The same clause is the precondition for Containment, because "inside approved purpose" only means something if what was approved is committed precisely. The approval that binds the disclosure is what gives enforcement a boundary worth holding.
**Rule 3 and its law match exactly.** [The Contractor Gets Their Own Card](/notes/the-contractor-gets-their-own-card/) is Narrowing with a renovation project attached: every derivation is a subset of what was approved, and widening is a fresh approval, never an inference.
**Rules 4 and 5 swap numbers, and the swap is the reading order.** The card chapter teaches enforcement before endings because that is how you meet the system at the register. The laws number revocation before continuous execution because that is the dependency order: [ending the arrangement](/notes/canceling-the-card-doesnt-stop-the-charges/) (Termination) is what the per-action check (Containment) consults. [The network post's](/notes/the-network-approves-every-transaction/) freeze that declines the next swipe is both laws operating in one moment: revocation ended the authority, and the transaction-time check is how the ending became real at the edge.
# The mapping table becomes an object
The card chapter compresses its whole translation into one table, trip to approved mission, intake form to structured request, card network to per-action enforcement. Read that table again and notice what every row on the agent side has in common. Each one is a projection of, or a consultation of, a single record that agent infrastructure does not have: the approved task itself.
That record is the Mission, and making it first-class is the whole architectural move. The trip becomes the Mission record, with an identifier, an issuer, and integrity anchors committing the approved task and the authority derived from it. The intake form becomes Intent shaping. Finance deriving the controls becomes authority derivation, producing the Authority Set the approval commits. The card becomes the mission-bound token, a bounded projection that carries the Mission's identity. The network becomes the enforcement checkpoint asking, per action, whether this call with these parameters is still inside the approved task. The freeze becomes revocation of the Mission with an observable state surface. The project code becomes the `mission_id` joining every decision and event. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) makes this argument in full, and the [Reference's object model](/notes/mission-based-authorization-field-reference/#the-mission-object-model) is the precise form with a concrete record.
To make it concrete, run the card chapter's own scenario through the object once. The engineer's conference trip becomes a Mission record: the intake form's output is the Mission Intent, finance's derived controls (the $3,400 limit, the travel and lodging categories, the November 29 through December 5 window) are the Authority Set, the manager's sign-off is the approval event that commits both under integrity anchors, the card in her wallet is a mission-bound token carrying the record's identity, and the project code on every transaction is the `mission_id` on every decision. The protocol side of this handbook runs the same loop on its own scenario, Alice's Q3 board packet, and the [Reference walks that one end to end](/notes/mission-based-authorization-field-reference/#the-running-example-end-to-end). Two stories, one object.
# The Corporate-Card test becomes the claim gate
The card chapter ends with a diagnostic: [the corporate-card test](/series/what-the-corporate-card-already-solved/#the-corporate-card-test), the questions to ask any agent platform, with the standard that answers unacceptable for a card program are unacceptable for an agent. This chapter carries the same diagnostic in architecture form, the claim gate, split where the family's conformance splits: a system is mission-based when it has an approved task object, authority derived from that task, narrow-only delegation as work fans out, and observable lifecycle state, and it may claim action-time defense only when per-action runtime enforcement and evidence that joins on the Mission's identity hold as well.
Line them up and the correspondence is one-to-one. "What approved purpose is this authority projected from" is the approved task object and the derivation. "What exact disclosure did the approver see" is the integrity anchors and approval evidence. "Can delegated authority only narrow" is the subset rule. "What authorizes each consequential action at the moment of use" is runtime enforcement. "What actually stops when the work ends" is lifecycle state and the bindings that consult it. The [Reference's litmus test](/notes/mission-based-authorization-field-reference/#what-counts-the-litmus-test) expands each property and names what fails it, and the [what-not-to-claim list](/notes/mission-based-authorization-field-reference/#what-not-to-claim) is the same discipline pointed at this proposal itself.
# The build lists name the draft family
Here is the part that makes the card chapter more than a teaching device. Each card post closes with a build list, the work the agent stack cannot borrow because the expense world never needed it. Collect those lists and they enumerate the draft family, cluster by cluster.
**"A disclosure layer that can be held to account, approvals that survive fatigue, and bounds a reviewer can actually read"** (from [the approval post](/notes/you-approve-what-you-were-shown/)) is the approval-integrity cluster: [Mission Intent Shaping](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-shaping.html) structures the proposal, [Mission Consent Evidence](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-consent-evidence.html) makes the rendered disclosure replayable, and the issuance core's rendering of derived bounds is what gives the reviewer something legible to narrow. [From a Request to an Approved Mission](/notes/from-a-request-to-an-approved-mission/) carries the cluster.
**"Identity for every copy of the helper, field-speed delegation that provably narrows, and a subset rule for authority that is a shape rather than a number"** (from [the contractor post](/notes/the-contractor-gets-their-own-card/)) is the delegation cluster: the [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) and [AI Agent Instance Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-ai-agent-instance/) for instance identity, the core's narrow-only subset rule defined over typed Authority Set entries, [Mission Child Delegation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-child-delegation.html) for separately revocable helpers, and [Mission Offline Attenuation](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-attenuation.html) as the experimental answer to fan-out at machine speed. [Mission-Bound Authority](/notes/mission-bound-authority/) carries the cluster.
**"A checkpoint at every boundary you claim, a named list of the boundaries you do not, and checks sized to consequence rather than latency"** (from [the network post](/notes/the-network-approves-every-transaction/)) is the enforcement cluster: [Mission-Bound Runtime Enforcement](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-runtime.html) with its [AuthZEN binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authzen.html), action classification for consequence-sized checks, and the enforcement-scope statement that names the ATMs in writing. [Mission-Bound Runtime Enforcement](/notes/mission-bound-runtime-enforcement/) carries the cluster, and it is the load-bearing one.
**"An ending someone can check, continuity machinery that checks it, and a record that earns trust without a bank behind it"** (from [the endings post](/notes/canceling-the-card-doesnt-stop-the-charges/)) is the lifecycle and runtime cluster: [Mission Status](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-status.html) as the checkable ending with [Lifecycle Signals](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission-signals.html) as its push complement, the [harness profile](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-harness.html) teaching the continuity machinery to check before it resumes, and [Mission Audit Transparency](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-audit.html) earning trust structurally, through verifiable evidence rather than an issuer's ledger. [Mission Lifecycle and Change](/notes/mission-lifecycle-and-change/) and [The Agent Runtime and Audit](/notes/the-agent-runtime-and-audit/) carry the cluster.
**And the whole-loop build list from [the card post itself](/notes/agents-need-a-corporate-card-not-a-blank-check/)**, a first-class approved purpose, bounded projections, checks at the moment of use, delegation tied to the task, endings that reach everywhere the authority went, is not a cluster. It is the [issuance core](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-oauth-mission.html) plus the shape of the family around it, which is the point: the card chapter's build lists and this family's table of contents are the same list, written by two different worlds.
# What the analogy could not say
The card chapter is honest about where it breaks, and the breaks became build items above. But a few of the architecture's commitments have no card-world articulation at all, and they are worth naming so the translation is not oversold.
- **The integrity anchors.** No card program cryptographically commits the approved trip. The Mission's `intent_hash` and `authority_hash` bind the approved task and the authority derived from it, so that "what did the approver see" has an answer that survives a hostile audit rather than a cooperative one.
- **The missing network is a design input, not a footnote.** The card world's transaction-time decision rides rails that already reach every merchant. The architecture has to state where checkpoints live, what a deployment's enforcement scope is, and how state stays fresh without any common fabric, which is why Status, the freshness rules, and the enforcement-scope statement are normative surfaces rather than operational advice.
- **Cross-domain projection.** A card works at any merchant because the network is global. One Mission honored in another trust domain requires machinery the card world never had to invent, and it gets its own draft rather than a hand wave.
- **The substrate-neutral framework.** The card story is one world. The architecture separates the laws from their OAuth realization on purpose, so the model survives the substrate argument, and [What Survives Without OAuth](/notes/what-survives-without-oauth/) carries that split.
# Where this leaves you
The card chapter's closing sentence was a promise: the protocol work is the translation, stated in standards terms. This part is the map of that translation, and the rest of this chapter walks it. [The Mission Is the Missing Abstraction](/notes/the-mission-is-the-missing-abstraction/) defines the object. [Adopting Mission-Bound Authorization](/notes/adopting-mission-bound-authorization/) stages the build, crawl, walk, run. [Weighing Mission-Bound Authorization](/series/weighing-mission-bound-authorization/), the concluding chapter, states the model beyond OAuth. And when you are ready for wire-level depth, the [Building Mission-Bound Authorization](/series/building-mission-bound-authorization/) chapter carries each control at implementation depth, one card room at a time.
If you carry one sentence out of the translation, carry this one:
> **Identity says who. Credentials say what may be accessed. The Mission says what the work is, who approved it, and when it ends.**
---
# Canceling the Card Doesn't Stop the Charges
Canonical URL: https://notes.karlmcguinness.com/notes/canceling-the-card-doesnt-stop-the-charges/
Markdown URL: https://notes.karlmcguinness.com/notes/canceling-the-card-doesnt-stop-the-charges.md
You cancel a card. The issuer confirms. The plastic is dead.
Next month the gym bills you anyway, on the replacement card's number,
which you never gave it. Nothing malfunctioned. The network's
[account updater service](https://developer.visa.com/capabilities/vau)
forwarded your new number to merchants that bill you on file, because continuity of billing is a feature that took real
engineering, and it worked perfectly. You ended the instrument. The
*arrangement* sailed on, because no part of the system recorded that
the arrangement itself was supposed to end.
> Ending the credential is not ending the arrangement.
Notice the causality in a governed program. The reason ends first, and
the credential dies *because* the reason did: the trip is cancelled,
so the card freezes. In most agent systems the causality is missing
entirely. The reason ends and nothing notices, because nothing
recorded that the credential existed for a reason at all.
Every hard truth about ending delegated authority is in that gym
charge. Credentials are easy to kill. The work they were feeding is
not, because the work has its own momentum: standing arrangements,
charges in flight, helpers mid-task, and systems that were built,
carefully and correctly, to keep things running. Payments has spent
decades building machinery for endings anyway. This part walks that
machinery, because AI agents need every piece of it, and mostly have
none.
# Endings come in kinds
The card world refuses to treat "make it stop" as one operation, and
the distinctions all carry.
**The freeze is a pause.** Something looks wrong, so you freeze the
card from the app. Every authorization declines, instantly, but
nothing is torn down. Unfreeze and life resumes. The freeze exists
because the most common emergency is uncertainty, and uncertainty
needs a reversible answer. A system whose only stop is destruction
teaches its operators to hesitate.
**The cancellation is terminal.** The card is dead, forever, and
everything that depended on it must be re-established deliberately or
not at all.
**The expiry is a clock.** Cards die on schedule whether or not anyone
remembers them, the backstop for every arrangement that outlived its
attention.
**And completion is the ending standing cards do badly.** The trip
ends, the project closes, and the card simply continues, still valid,
still standing authority, until an expiry or an audit catches it.
Standing instruments that outlive their purpose are the expense
world's chronic disease, and its cure is the single-use virtual card:
an instrument bound to one purchase that retires *itself* the moment
the purchase completes. The authority ends because the work did, with
no one lifting a finger. The modern spend programs have already made
that the default, and it is the right default for agents too:
authority that expires with the task, not with the calendar.
One more rule hides in the limit increase. When you genuinely need
more, an issuer can raise your limit in place, and a well-run corporate
program deliberately resists making that the default. It issues a new
instrument for the new need, freshly approved and freshly scoped,
because an instrument whose bounds drift upward in place slowly stops
meaning anything. What was approved should stay what is in force, and
growth should have a paper trail of its own.
# Authorized is not settled
Cancel a card mid-month and look closely at the statement. Some
charges are finished. Some are *pending*, authorized days ago,
settling now. One is a hotel hold that will simply evaporate. The card
world runs on the distinction between a transaction that was approved
and a transaction that actually happened, and every ending has to
reckon with the space between them.
That space is a taxonomy, and it transfers whole. When the task
stops, its work in flight is in one of four states. Not yet submitted,
so kill it. Authorized but not executed, so release it. Executed, so
it is real and no cancellation reaches it. And the ugly one:
*unknown*, submitted somewhere and unconfirmed, which honest systems
route to a human instead of guessing. Payments never pretends an
in-flight charge is simply gone because the card died. Agent systems
that treat task termination as a boolean are pretending exactly that,
and the pretending shows up later, in the audit.
**And un-spending is governed.** When a charge must actually come
back, you do not reach into the merchant's account and take it. You
*dispute* it: a process with evidence requirements, deadlines,
categories, and an adjudicator. The undo is itself a controlled
operation, because an undo is money moving, and money moving is
exactly the thing the whole system governs. The agent parallel is
sharp. Compensating a half-finished task, the reversing entry, the
retraction email, the rollback, is itself consequential work, and a
terminated task must not become a back door for ungoverned "cleanup."
If the undo is not governed, the attacker's easiest move is to get
something canceled.
# The statement joins everything
At the end of the month one document reconciles the whole story. The
statement pulls every authorization, settlement, credit, and dispute,
and on a corporate program every line carries the project code, so an
auditor pulls one thread and the entire arrangement unspools: the
request, the approval, the cards issued, every transaction each one
made, the freeze, the disputes, the final balance.
Notice what makes that possible. It is not diligence. It is that
every event, from the first approval to the last chargeback, was
stamped with the same identifier at the moment it happened. Nobody
reconstructs the trip from timestamps and vibes. The join key was
built in. Agent systems that plan to assemble the story later, from
logs scattered across every system the agent touched, are planning an
archaeology project. The statement works because the story was joined
as it was written.
# Where the analogy breaks
**The big one runs backward.** Everywhere else in this chapter, the
card world is ahead and agents need to catch up. Here the analogy
inverts. Card freezes work quickly *because online card authorizations
consult issuer-controlled state*, one state change, checked at the
accepting edge. Agent credentials are built to work offline, verified
locally, on purpose, for speed, which means there is no equivalent
switch. Ending an agent task means
building the machinery the card world got for free: a place where the
task's state lives, and consumers that actually check it, fast enough
to matter. Anyone who assumes revocation just works, the way a card
freeze just works, has imported the analogy's easiest property into
the one place it does not apply.
**Even the network leaks on purpose.** The gym charge was not a bug.
Account updating and merchant-initiated billing are continuity
features, built because most cancellations are card replacements, not
relationship endings, and continuity is usually what the customer
wants. The lesson generalizes: every mature system grows features
whose whole job is to keep things running, and those features cannot
tell a routine ending from a revocation unless something records the
difference. The agent version is the harness that helpfully resumes a
suspended workflow, the retry queue, the cached connection, each one a
little account updater, faithfully continuing an arrangement whose
reason is gone. Continuity machinery must be taught to check whether
the arrangement still stands, or it will defeat every ending you
build.
**You trust the issuer's statement.** The whole reconciliation story
rests on one institution's ledger, and you accept it because the issuer
and card program sit inside a regulated, audited operating model. Agent
tasks cross companies and clouds that share no such institution. There
is no issuer whose statement everyone accepts, which means the agent
world's statement has to earn trust structurally, evidence that can be
verified rather than vouched for. That is a genuinely new build, with
no expense-world part number.
The build list from this room: an ending someone can check, continuity
machinery that checks it, and a record that earns trust without a bank
behind it.
# The loop, closed
One loop, five posts. The card bound the instrument to an approved
purpose. The approval bound what was shown. The contractor's card
narrowed authority without borrowing it. The issuer made every use a
fresh decision. And the ending, the hardest part, turned out to be a
system of its own: pauses, terminations, self-retiring instruments,
in-flight states, governed undo, and a statement that joins the whole
story to one code.
None of it is exotic. It is what a competent finance organization
does by default, for money, because the alternative is negligence with
a paper trail. Agents exercise authority that is broader than money,
faster than any traveler, and easier to fool than any cardholder. The
failure mode is not autonomy. It is the blank check. The bar the
expense world sets is the *floor*.
The protocol work is the translation. The loop itself needs no
standard. You can start running it today.
---
# The Network Approves Every Transaction, Not the Card
Canonical URL: https://notes.karlmcguinness.com/notes/the-network-approves-every-transaction/
Markdown URL: https://notes.karlmcguinness.com/notes/the-network-approves-every-transaction.md
A card declines at the register. Mild embarrassment, a tap of a
different card, everyone moves on. Nobody calls the police. Nobody
even remembers it by lunch.
That boring little moment is the most important design fact in
payments. The decline is not a failure of the system. It *is* the
system, doing the one thing it exists to do: deciding this
transaction, right now, on current information, and saying no without
drama. Payments normalized the no. Declines are so routine that
merchants print decline handling into their training and cardholders
carry a backup, and the entire arrangement works precisely because
saying no is cheap, instant, and unremarkable.
Hold that thought, because agent systems mostly lack it. For most
agent deployments today, the only no is at the front door, when
credentials are granted. Past the door, a valid token is a yes to
everything inside its scope for as long as it lives. This part is
about the payments alternative: authorize the transaction, not the
card. It is the load-bearing idea of the whole chapter.
> Authorize the action, not the instrument.
One precision up front: strictly, the network routes the request and
the issuer decides it. This chapter says "the network" for the
decision system the merchant sees, and the argument survives either
name, because the point is that the decision never lives in the card.
# The plastic proves almost nothing
Start with what the card in your hand actually establishes: very
little, and the industry knows it. The number on the front is a
routing hint that has leaked too many times to treat as secret. What separates a
card-present transaction from fraud is the chip, which computes a
fresh cryptographic proof for each transaction, evidence that the
physical card is here, now, for this purchase. Card-not-present
transactions, where a number alone suffices, are where the fraud
lives, and everyone prices them accordingly.
The lesson is not about chips. It is that a mature authorization
system never confuses *holding the instrument* with *being entitled to
this use of it*. Possession is one input. The decision happens
elsewhere, per use. Agent systems that treat a presented token as the
whole decision are running a card-not-present economy and hoping.
# The authorization is specific, or it is nothing
Watch what a single authorization actually contains. Not "this card
may spend." This card, this amount, this merchant, this moment, judged
against current state: is the card active, is the limit sufficient,
is the category allowed. Change any input and it is a different
decision. That specificity is not pedantry. It is what makes the
authorization mean something.
The hotel hold makes the point physical. At check-in, the desk
authorizes $600, a hold, an authorization bound to an amount. If the
minibar raises the bill to $750, the hotel cannot ride the old
authorization. It asks again, for the new number. Authorization of an
operation was never authorization of whatever parameters the operation
later turns out to have. Payments builds that rule into the rails,
because the gap between what was approved and what gets executed is
exactly where the fraud crawls in.
And the state is *current* state. Freeze your card in the banking app
and the very next swipe declines, at a register on the other side of
the planet, seconds later. The instrument in the wallet did not
change. The decision consults the source of truth, every time, and so
the freeze is real everywhere at once. The newest card programs push
even policy into this moment, so the out-of-policy purchase declines
at the register instead of surfacing in a month-end report.
Notice what that makes the approval. Not a moment, a standing question
that every swipe re-asks. The card that worked at lunch declines at
dinner, and the reasons live entirely outside the card: the budget hit
zero, the project closed, the employee gave notice, the vendor got
blocked. Approval is continuously revalidated against the current
state of the business, not the state on the day the card was issued.
The agent translation is direct. A consequential action needs a fresh
decision that binds the actual parameters, the who, the what, the how
much, checked against the current state of the approved task, not
against the fact that a credential exists. And the deployment needs
declines to be what they are in payments: a normal, cheap, recorded
signal that shapes behavior, not an exception that pages someone. An
agent told no should handle it the way a traveler does, with a fallback
and a record, not a crash.
# The ATM is the honest part
Now the part of the card world that gets left out of the brochure.
Walk to an ATM, withdraw $200, and the network's writ ends at the
mouth of the machine. The cash buys whatever it buys. No merchant
category, no per-transaction authorization, no decline. The most
tightly governed payment instrument on earth has a built-in exit into
ungoverned space.
Here is what makes the card world mature rather than naive: *it says
so, in writing.* Cash advances get their own lower limits, their own
fees, sometimes an outright block, and serious expense policies name
cash because everyone knows the receipts get fuzzy there. The program
does not pretend the ATM away. It names the path it cannot see, bounds
what can flow through it, and treats receipts from that side with
appropriate suspicion.
Every agent deployment has ATMs. The debug shell. The direct database
connection. The egress route nobody proxies. The tool that shells out.
An agent platform that claims its per-action checks cover "everything"
is describing a world with no cash in it, and it is wrong the moment
someone looks. The honest posture is the card program's posture: put
the checkpoint on every path you can, name the paths you cannot, bound
them separately, and treat what happens there as unverified. A
security claim that does not name its ATMs is marketing.
# The cardholder can be hypnotized
One more difference hides inside the fraud model, and it is the
deepest one. Card fraud assumes the enemy is *outside*: a thief with a
stolen number, a skimmer, a counterfeit. The cardholder is presumed
honest, occasionally careless. Nearly every control in payments points
outward, and the deterrent, that the statement is coming and the
cardholder will dispute what they did not do, points at the thief too.
The agent's situation is stranger. The agent *is* the cardholder, and
the agent's judgment can be rewritten mid-purchase by the content it
reads. A poisoned document is a shop window that reaches into the
shopper. The agent then walks to the register as itself, presents its
own legitimate credential, and buys exactly what the attacker wanted,
inside its authorized limits if the attacker is careful. No stolen
card. No counterfeit. The holder was turned.
This is why the per-transaction check has to carry more weight for
agents than the network carries for people, and why the check must
bind parameters rather than trust the requester's framing. There is no
deterrent to lean on and no honest-cardholder assumption to fall back
to. The nearest human analogy is not card fraud at all. It is the
trusted agent under a
[power of attorney](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/)
who acts against the principal, which is why the law surrounds
attorneys-in-fact with duties, witnesses, and revocation, and why
agents need the runtime equivalent.
The network also knows that fraud is rarely one bad swipe. It is many
normal ones, which is why cards carry velocity limits and cumulative
caps alongside the per-transaction decision. The agent version is the
same: per-transaction checks alone cannot stop individually valid
steps from composing into an outcome no one approved, so the approval
covers the bounded work first, every action is then judged against
that boundary, and the Mission's consumption bounds are the velocity
limit's descendant.
# Where the analogy breaks
**There is no common network.** A card payment that uses the card
instrument has to traverse card-acceptance rails, so the issuer's
transaction-time decision can reach the accepting edge by design.
Agent actions cross APIs, tenants, and tools that share no common
fabric, so per-action authorization is not a subscription you turn on.
It is a checkpoint you place, boundary by boundary, and your coverage
is exactly the set of boundaries you actually placed it on. The ATM
paragraph is not optional garnish for agents. It is most of the
document.
**The risk math is different.** The network tolerates false approvals,
prices fraud as a percentage, and claws money back later, because
money comes back. Agent actions include the kind that do not: the sent
message, the deleted record, the signed commitment. A system tuned to
payments-style risk, approve fast and reconcile later, is tuned wrong
for irreversible verbs. For those, the check must be strict before the
act, because there is no later.
**Milliseconds hide the cost.** Payments authorizes in tens of
milliseconds because decades and billions went into exactly that
problem, for one transaction shape. Per-action checks for agents span
wildly different actions and will not be free on day one. The
temptation will be to skip the check where latency hurts, and the
card world's answer applies: the check runs at the moments that
matter, sized to the consequence, and where it truly cannot run, that
path gets named with the ATMs.
The build list from this room: a checkpoint at every boundary you
claim, a named list of the boundaries you do not, and checks sized to
consequence rather than latency.
# The sentence that survives
Possession of the card was never the control. The decision at the
moment of use was, is, and will be, and everything else in the
arrangement, the limits, the freeze, the statement, only becomes real
at that moment. Agents need that moment built for them, in a world
with no network, no honest cardholder, and more than one ATM. The
register is open now, and every consequential action is a swipe.
---
# The Contractor Gets Their Own Card
Canonical URL: https://notes.karlmcguinness.com/notes/the-contractor-gets-their-own-card/
Markdown URL: https://notes.karlmcguinness.com/notes/the-contractor-gets-their-own-card.md
Crunch week on the renovation project. The contractor needs to buy
materials this afternoon, the purchasing process takes two days, and
the project manager does the obvious thing. She hands him her card.
Just this once.
Everything about it is convenient. No forms, no waiting, the work
keeps moving. And everything about it is wrong, in ways every finance
team can recite from memory. The statement will say *she* bought
whatever he buys. Her limit, tuned to her role, is now backing his
judgment. If the card number leaks from his pocket, her card dies and
her month gets complicated. And when the project ends, nothing about
that card changes, because the card never knew it was working for the
project at all.
The failure is not that the contractor is untrustworthy. He may be
excellent. The failure is that the *authority arrangement* is wrong:
borrowed instead of granted, broad instead of narrowed, attributed to
the wrong person, and immortal with respect to the work it was
supposed to serve.
> Delegation narrows, or it contaminates.
AI agents hit this exact fork dozens of times a day, because agents
spawn helpers. A research sub-agent, an extraction worker, a reviewer,
each needing some slice of authority to do its piece. And the
convenient thing is always the same thing the project manager did:
hand the helper the credentials already in hand. This part is about
why the spend world refuses that convenience, and how.
# Borrowing is the default because borrowing is free
Be honest about why the card got lent. The right path had friction and
the wrong path had none. The parent's credential is already there,
already works, and requires nobody's involvement. That asymmetry, not
malice, is why borrowed authority is the default failure mode of every
delegation system ever built.
A mature card program does not win by lecturing project managers. It
wins by making the right thing nearly as cheap as the wrong thing.
Issuing a contractor card takes minutes, not days. The moment the
sanctioned path costs two days while the unsanctioned path costs
nothing, the program has designed its own bypass.
The agent version is sharper, because for agents the borrowed path is
not just convenient. It is invisible. A sub-agent spawned inside the
parent's process inherits the parent's credentials by simple physics,
by being in the same memory with the same connections. Nobody decides
to lend anything. The lending is the default state of the runtime, and
an explicit grant has to be built to exist at all.
# What the right version looks like
The contractor gets his own card, and five properties come with it.
Each one fixes a specific thing the borrowed card broke.
**His own name.** The statement now says who actually spent. When a
charge looks wrong, the question "who did this" has an answer that
does not require interviewing the project manager about her wallet.
Attribution is not bureaucracy. It is the difference between an audit
and an interrogation.
**A lower limit, on the same project code.** His card draws against
the same approved project budget, but bounded to his slice: materials,
say $2,000, this month. The delegation *narrowed*. In a well-run
program it always narrows. The contractor's card should not be able to
do more than the project it serves, because his authority is carved
from the project's, not conjured beside it.
**Issued, never copied.** His card came from the program, through a
request the program saw. He could not manufacture it from the project
manager's card number, and she could not create it by handing over
plastic. Every delegation is visible to the issuer at the moment it
happens, which is precisely the property the borrowed card destroyed.
**It dies with the project.** When the project closes, every card on
that project code stops working at once, hers, his, all of them.
Nobody collects plastic from wallets. Nothing depends on the
contractor remembering to stop. The cards were always projections of
the project, so ending the project ends them, transitively, wherever
they are.
**The program caps the count.** Here is the subtle one. Twenty
contractors with $500 cards is a different risk than one contractor
with a $10,000 card, even though the arithmetic looks similar. Breadth
is its own exposure: more instruments in the field, more places to
leak, more judgment operating in parallel. So the program does not
just bound each card. It bounds *how many* cards a project may have
open, and whether a contractor may sponsor cards for his own
subcontractors at all. Depth limits do not control breadth. Breadth
gets its own limit.
Swap in the agent and the mapping is one-to-one. The sub-agent gets
its own explicit grant, narrower than the parent's, issued through a
path the system can see, attributed to the sub-agent itself, dead the
moment the parent's task ends, with the fan-out counted and capped.
Spawning a helper is a runtime event. Authorizing one is a governance
event. The whole discipline is refusing to let the first stand in for
the second.
# Where the analogy breaks
Three breaks, and each is a build item the card world never needed.
**Cards have one holder. Agents have a thousand copies.** A contractor
card names a human, and there is exactly one of him. An agent is
software. The "contractor" may be forty concurrent instances of the
same worker, spun up and torn down in minutes, all legitimately acting
under the same delegation. The expense world never had to ask *which
copy* of the contractor was at the register, and its instruments carry
no answer. Agent delegation needs identity for instances, not just for
roles, or every one of those forty workers is indistinguishable in the
record and in the containment.
**Every card comes from the issuer.** There is no field-expedient way
to mint a narrower card from your own card, and for plastic that is
fine, because delegation happens at human speed. Agent swarms delegate
at machine speed, hundreds of narrow grants per minute, and a round
trip to the issuer for each one becomes the bottleneck that tempts
everyone back to credential-sharing. The agent world genuinely needs
what the card world rarely has to expose: a way to derive narrower
authority in the field that still cannot exceed the parent and still
dies with it. That is new machinery with sharp edges, and it only
works if the narrowing is provable and the kill switch still reaches
it.
**The contractor's limit is a number. The agent's is a shape.** A
card's delegation collapses to amount, category, and dates, because
money is one-dimensional. An agent helper's slice is which actions on
which systems with which parameters, a shape, not a scalar. "Narrower"
is easy to verify for $2,000 against $10,000. It has to be *defined*
before it can be verified for "may read the financials but not notify
anyone." The card world's subset rule comes free with arithmetic. The
agent world has to specify its subset rule, and everything downstream
depends on getting it right.
The build list from this room: identity for every copy of the helper,
field-speed delegation that provably narrows, and a subset rule for
authority that is a shape rather than a number.
# The rule that survives the breaks
Strip the plastic away and one rule remains, and it is the whole
post. **A helper's authority is granted, narrower, named, and mortal,
or it is contagion.** The project manager's card in the contractor's
pocket is the same object as the parent agent's token in the
sub-agent's memory: authority that arrived by proximity instead of by
decision.
The spend world beat this by making the granted path cheap and the
borrowed path unnecessary. The agent world has to do the same thing at
machine speed, which is harder and less optional, because agents fork
faster than any project ever staffed up. The rule is usable today:
make the granted path cheaper than borrowing, or borrowing will be the
architecture.
---
# You Approve What You Were Shown
Canonical URL: https://notes.karlmcguinness.com/notes/you-approve-what-you-were-shown/
Markdown URL: https://notes.karlmcguinness.com/notes/you-approve-what-you-were-shown.md
A manager approves a conference request on her phone, in the elevator,
between meetings. Four months later an auditor asks what she approved.
The only defensible answer is not "a trip, roughly." It is the request
as it was rendered on that screen, at that moment: the destination, the
dates, the $3,400 estimate, the cost center, the attached quote. If the
approval means anything at all, it means *that*. Not what the requester
intended. Not what the system stored somewhere else. What the approver
was shown.
> The approval binds the disclosure, not the intent.
Expense systems learned this the hard way, and the whole anatomy of a
real approval follows from it. AI agents are about to need every part
of that anatomy, because an agent's first consequential action should
sit downstream of an approval worth auditing, and today it mostly sits
downstream of a checkbox granted at integration time. This part walks
the approval the way the spend world actually runs it. No protocol is
required to understand the control.
# The request is a proposal, and nothing more
[Agents Need a Corporate Card, Not a Blank Check](/notes/agents-need-a-corporate-card-not-a-blank-check/)
made the first point: the engineer fills out the intake form, and the
form approves nothing. Three properties of that form deserve a closer
look, because each one is doing security work that is easy to miss.
**The form structures the request.** "Some conferences, whatever it
costs" does not submit. The free-text wish becomes a bounded, reviewable
proposal: this event, these dates, this estimate. Structure is what
makes review possible at all. You cannot meaningfully approve a vibe.
**The form fails closed on ambiguity.** Missing cost center? It asks.
It does not guess, and it especially does not guess *generously*. A
request system that resolved every ambiguity in the requester's favor
would be an escalation engine wearing a form's clothing.
**The requester never writes her own limit.** She describes the trip.
Finance derives the controls. The person asking and the function
bounding are different parties by design, because a request that
specifies its own authority is not a request. It is a demand with
paperwork.
Now swap in the agent. The agent's account of its own task is exactly
as trustworthy as the engineer's account of her own budget needs, which
is to say it is a proposal from an interested party. Something has to
structure it, refuse to guess generously on its behalf, and derive the
bounds from the task rather than accept them from the requester. None
of that is exotic. It is intake, and it is not hypothetical either.
The modern spend platforms built their whole product on this shape:
the request is the approval workflow, and the approval mints the
instrument.
# Reviewers narrow. They rarely deny.
Watch what real approvers do. The flight is approved but not the
business-class fare. The software purchase is approved for one year,
not three. The $2,000 request comes back as $500, try again next
quarter. Outright denial is rare. Narrowing is the common case, because
the reviewer usually agrees with the *task* and disagrees with the
*bounds*.
A well-built system honors that. The reviewer trims the request and
the requester keeps their place in line. A badly built system offers
approve-or-deny only, and the requester refiles from scratch for every
trim, which trains everyone involved to ask broad and hope. The
approval mechanics quietly shape what people request.
The same is true of time. Real reviews are asynchronous. The request
sits in a queue for two days while the approver travels, and nothing
about that delay should break the request or tempt anyone to bypass
the queue. Systems that cannot wait teach people not to ask.
Both lessons transfer whole. An agent's task proposal will come back
narrowed more often than denied, and the round trip will sometimes take
days, because the reviewer is a human with a job. If narrowing means
restarting, or waiting means failing, the deployment will route around
its own approval step within a month. The approval process has to make
the safe path the convenient one, which is the entire trick of the
corporate card in one sentence.
# The disclosure is the control
Here is the part that sounds like a technicality and is actually the
foundation. When the manager approved on her phone, what exactly got
approved?
Not the requester's intent. Not the database row. The approval binds
the *disclosure*: the rendering the approver saw, with the amounts,
recipients, and terms it contained at that moment. If the line items
quietly change after the approval, the approval does not stretch to
cover them. It is void, because the thing she assented to no longer
exists. Every serious approval system behaves this way, which is why
the request as-reviewed gets snapshotted with the decision, and why
"what did the approver see" is answerable years later.
Payments did not leave this to good practice. European payment
regulation made it law. Under PSD2's dynamic-linking rules, when your
banking app asks you to approve a payment, it must show you the amount
and the payee, and the authorization code it produces is
cryptographically bound to that exact amount and that exact payee.
Change either one and the approval is worthless. The regulation exists
because attackers were changing the transaction between the screen and
the wire, and the industry's answer was to make what-you-see-is-what-you-approve
a property of the cryptography rather than a hope about the UI.
That is the bar for agents, and it is not a metaphor. When a human
approves an agent's task, the approval must bind the disclosure that
was rendered: this task, these systems, these bounds, this expiry. An
approval that binds anything less is a receipt for a conversation. And
the disclosure must render the *derived bounds*, not just the friendly
summary, because assent to a summary is assent to whoever wrote the
summary.
One more habit worth stealing: the spend world records declines. A
denied request is not deleted. It is a decision with a record, because
the pattern of what gets declined, and who keeps asking, is exactly
what an auditor wants to see. Approval systems that only remember
yeses cannot explain their noes.
# Where the analogy breaks
Three breaks, each one a piece of work the agent stack cannot borrow.
**Screens can lie, and agents have more screens.** PSD2 could mandate
what the payment authorization experience must bind because the
regulated payment flow has a narrow job: show the transaction terms and
produce a transaction-specific authorization. An agent's approval can
surface in a chat window, a dashboard, an email, a Slack message, each
rendered by software with its own bugs and its own trust story. Binding
the approval to the disclosure only helps if the disclosure layer
itself can be held to account, and that is a build item, not a given.
The expense world mostly gets to assume its rendering is honest. The
agent world has to prove it, or at least record enough to detect the
lie afterward.
**Approval fatigue is an annoyance for spend and an attack surface for
agents.** Rubber-stamping expense requests costs money, and money is
mostly compensable, so the expense world tolerates a manager who
approves on autopilot. An agent's actions include the irreversible
kind, and any adversary who can generate requests can farm a tired
approver. For agents, the fatigue curve is a security boundary. That
argues for fewer, better approvals with real bounds, not more prompts,
and it is why the approve-every-action model fails at exactly the
scale where agents are useful.
**The reviewer knows what a conference costs.** Decades of trips built
priors. A manager can smell a padded estimate, and the delegation
matrix encodes the whole company's sense of what is normal at each
level. Nobody has priors for agent tasks yet. What is a "reasonable"
scope for an agent that reconciles invoices? Reviewers will be asked
to bound tasks they cannot yet sanity-check, which means the early
systems must make bounds legible and defaults narrow while the
institution learns what normal looks like. The expense world's matrix
took years to tune. The agent world starts at zero.
The build list from this room: a disclosure layer that can be held to
account, approvals that survive fatigue, and bounds a reviewer can
actually read.
# The approval is where safety is cheap
Everything downstream of a bad approval is expensive. Enforcement can
only hold the line the approval drew, audit can only replay it, and
revocation can only end it. The approval is the one moment where a
human with context can still shape what the authority *is*, which is
why the spend world invests its friction there and almost nowhere
else.
Approve like it will be read back to you in a deposition, because for
agents, it will. If the system cannot replay what the approver saw, it
cannot defend what the agent later did.
---
# Agents Need a Corporate Card, Not a Blank Check
Canonical URL: https://notes.karlmcguinness.com/notes/agents-need-a-corporate-card-not-a-blank-check/
Markdown URL: https://notes.karlmcguinness.com/notes/agents-need-a-corporate-card-not-a-blank-check.md
A new hire starts on Monday. She is trusted, vetted, and hired precisely
because she has good judgment. Nobody hands her the company checkbook.
In a mature spend program, she gets an instrument with a boundary: a
corporate card, a virtual card, or a travel approval that controls what
the card can do. It has a limit. It works for travel and software, not
for jewelry. It draws against a budget someone approved for a reason,
and it can die the day she leaves or the project ends. Inside those
bounds, nobody reviews every purchase in advance. She is expected to
make smart decisions inside a risk boundary, and the boundary does the
worrying.
Now look at how the same company deploys an AI agent. It provisions an
agent identity, grants it credentials with broad standing authority, and
leaves them standing indefinitely. The credential might be an OAuth
token, an API key, a workload identity, or a cloud service principal.
The shape is the same everywhere. There is no purpose attached to the
credential, no budget it draws against, no per-action check, and nothing
that ends when the reason for the work goes away.
Put that in card terms. It is a card with no spending limit, no
expiry that matters, and a single merchant category that covers
everything. Nobody would run finance on that card. Most agent
deployments run on nothing else.
> An agent credential today is a blank check with an expiry date.
The failure mode is not autonomy. It is the blank check.
> Credentials are projections, not authority.
The strange part is that enterprises already run a mature
delegated-authority system for humans. They did not build it as an IAM
system. They built it as expense governance, and almost nobody calls it
what it is: an operating delegated-authority architecture. This part
walks that architecture end to end and maps it onto the authority gap
explored in the [Power of Attorney](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/)
and [Mission Shaping](/series/mission-shaping/) arguments. Then it is
honest about where the analogy breaks, because the breaks are the
build list.
# The authorization architecture nobody calls one
Follow one trip through a modern spend system.
An engineer needs to attend a conference. She does not email the CFO.
She fills out an intake form: destination, dates, business reason,
estimated cost. Say $3,400 for a cloud conference. The form will not let her
submit "some conferences, whatever it costs." If the cost center is
missing, it asks. The form structures the request. It approves nothing.
Her manager approves the trip against a delegation-of-authority matrix.
Managers approve to $5,000. Directors to $50,000. The numbers differ at
every company, tuned to its risk appetite and how much it trusts its
people. The pattern does not. The approval is recorded: who approved,
what they saw, when.
Here is the step everyone overlooks. The engineer asked for a *trip*.
Finance derived the *controls*: a $3,400 limit, travel and lodging
merchant categories, valid November 29 through December 5. She proposed
the trip. She did not define the authority. Finance derived the
authority from the approved trip.
> A corporate card is not permission to spend. It is a bounded
> projection of an approved purpose, checked at every swipe.
Then the issuer does the enforcing. Every swipe is authorized in real
time against current state. Is the card active? Is the limit remaining?
Is the merchant category allowed? A steakhouse works. A casino declines.
Nobody predicted the specific restaurant in advance, and nobody had to.
Judgment operates inside the boundary. The boundary is checked at the
edge, every time. Enterprises already run this pattern for APIs. An API
gateway evaluates authentication, quotas, scopes, and policy on every
request. The agent gap is not the idea of per-request enforcement. It is
the absence of a first-class approved task for that enforcement to
evaluate against: is this action still inside the mission?
Notice what the swipe check did not do. It did not page her manager.
Corporate cards are deliberately low-friction. Nobody asks permission to
buy lunch, because permission happened earlier and the boundary carries
it forward.
> The goal of delegated authority is not to ask permission more often.
> It is to ask permission less often, with better boundaries.
That is the analogy, and no more. Corporate cards do not make employees
trustworthy, prevent every bad purchase, or eliminate reconciliation.
They make delegated spend governable. The claim for agents is the same:
autonomy becomes acceptable only when the instrument is a projection of
an approved purpose, each consequential action is checked at the edge,
and the instrument stops working when the purpose ends.
The rest of the loop is just as deliberate. The budget meters down with
each transaction. A big purchase above her threshold needs a second,
specific approval no matter how much budget remains. If she needs more
than was approved, she cannot raise her own limit. She submits a new
request and someone with sufficient authority grants a new one. If the
conference is cancelled mid-trip, the card freezes while it is still in
her wallet. And every transaction carries the project code, so an
auditor can pull one thread and see the whole trip: the request, the
approval, every swipe, the reconciliation.
Really large commitments never touch her card at all. A $200,000
contract goes through procurement, and accounts payable executes the
payment after its own checks. For the spend that matters most, the
person doing the work never holds the payment instrument.
# The same loop, control by control
Every control in that loop answers a question that agent authorization
is currently answering badly or not at all.
| Expense governance | Agent authority |
| --- | --- |
| Intake form structures the request, approves nothing | Shaping turns a prompt into a structured, reviewable task proposal |
| Requester never writes her own limit | Authority is derived from the approved task, not requested by the agent |
| Approver narrows: "$2,500, no business class" | Approval that can reduce a proposal without restarting it |
| The issuer authorizes every swipe against current state | Per-action runtime check against the live task, not just a valid token |
| Merchant category codes, vendor-locked virtual cards | Action and parameter binding, not broad standing scopes |
| Budget drawdown, per-transaction caps | Metered consumption bounds on the task's authority |
| Card freezes when the trip is cancelled | Revoking the task stops the work, independent of token lifetimes |
| Limit increase requires a fresh approval | Widening authority is a new approval, never self-service |
| DOA matrix: managers to $5k, directors to $50k | Progressive ceilings that bound what policy may grant without a human |
| Contractor gets her own card on the same project code | Sub-agents get narrower, separately revocable authority, never the parent's credential |
| Procurement and AP execute the largest payments | For the highest-consequence actions, the agent never holds the credential |
| Project code on every transaction | One task identifier joining every action across systems for audit |
Two rows deserve emphasis, because they carry the safety argument.
**Transaction-time authorization is the load-bearing layer.** A card
number with no authorization rails behind it is just a promise. The
limit, the categories, and the freeze all become real at the moment of
the swipe, or they are not real at all. The agent equivalent is the
per-action runtime check, and the same logic applies. A purpose-labeled
token that nothing checks at the point of use is bookkeeping, not a
boundary.
**The freeze works because the card is not the authority.** When the
trip is cancelled, nobody hunts down the physical card. The instrument
in her wallet keeps existing. It just stops working, because the thing
it projects from was terminated. The plastic is replaceable. The
governance record is the asset. Agent systems mostly have this
backwards today. The token *is* the authority, so ending the work means
finding every token, every cached connection, every sub-agent that
still holds one. [Sessions Are Not Missions](/notes/sessions-are-not-missions/)
makes the runtime version of this argument: the engineer still being on
the trip does not mean the trip is still approved.
# The card was rebuilt a decade ago
If the loop above sounds like an idealized finance department, look at
what actually happened to the corporate card in the last ten years.
The incumbent model was the blank check with better paperwork: standing
plastic issued at hire, merchant-category guesswork for control, and a
month-end expense report as the enforcement moment. Everyone knew the
report was audit, not control. The money was already gone.
Then spend platforms like Ramp and Brex rebuilt the instrument, and the
rebuild is a checklist of this part's loop. The request became the
approval workflow. The approval mints the card, a virtual instrument
scoped to the vendor, the amount, and the purpose it was approved for.
Policy moved from the report to the authorization, so the out-of-policy
purchase declines at the register instead of surfacing three weeks
later. Receipts match at transaction time, the project code is native,
and the single-use card retires itself when the purchase completes.
And it won. Finance teams did not adopt purpose-issued cards because
auditors made them. They adopted them because the rebuilt loop gave
more control and less friction at the same time, the trade this part
keeps insisting is available. The market priced the argument.
That is the uncomfortable part for agent infrastructure. Agent
credentials today are the incumbent model: standing authority issued at
integration time, category-level guesswork for scope, and logs as the
enforcement moment. And the market's verdict on that shape is already
visible: agents get read access and a human keeps the writes, because
nobody can price the risk of the incumbent model at machine speed. The
card world looked at exactly that shape, rebuilt it, and was rewarded.
The transition the rest of this chapter maps is not speculative. In
the domain that had the choice, it already happened.
# Where the analogy breaks
An analogy this convenient should be distrusted on schedule. It breaks
in five places, and each break marks work the agent stack cannot borrow
from the expense world.
**Money is one-dimensional. Authority is typed.** Every card
transaction is the same verb: move money to a counterparty. Purchases
differ in amount and merchant, and the damage is denominated in the
same unit as the control, which is exactly what makes a scalar limit
work. Agent actions are not purchases. An agent is doing things, not
buying things. Its actions are different verbs entirely (read, send,
delete, sign, deploy), and their consequences share no common
denominator. A $50 mistake and a $50,000 mistake sit on one scale.
Deleting a record and emailing a customer do not. So agent authority
has to be typed (which resources, which actions, which parameters, under which conditions) and classified by consequence rather than by merchant. Vendor-locked, single-use virtual cards show the direction of
travel, an instrument bound to one counterparty and one amount. But
agent authorization needs that specificity as the norm, not as the
premium feature.
**There is no card network for agent actions.** This is the deep one.
Card-accepting merchants route through common authorization rails, so
the issuer's transaction-time decision can reach the point of sale.
Agent actions cross API providers, SaaS tenants, and tool servers that
share no common enforcement fabric. Each resource validates its own
tokens on its own schedule and learns nothing when the task behind them
ends. Put another way, card payments run on two infrastructures: issuance rails and
transaction-time authorization rails. OAuth gives enterprises widely
deployed delegation and token issuance rails, but not a universal
action-time authorization fabric across resource servers. Agent systems
have pieces of the first and no equivalent of the second. What the
agent world needs, in effect, is common task state and enforcement
contracts: an approved task as a first-class object that issuance,
enforcement, and revocation all consult, so that terminating it can reach
the places where the mission was projected.
**There are no chargebacks for un-send.** Most spend is compensable.
Refunds, reversals, and chargebacks form a mature unwinding system
with its own rails. Agent actions include sent messages, deleted
records, signed commitments, and published data, where no issuer can
claw the effect back. The expense world gets to be casual about
mid-flight cancellation because money can usually be put back.
Agent systems have to plan the unwinding before the action runs,
because often it cannot be.
**Employees fear the expense report. Agents fear nothing.** Expense
governance quietly leans on accountability. The traveler knows the
reconciliation is coming, and deterrence fills the gaps the controls
miss. An agent has no career to protect, and worse, its judgment can be
rewritten mid-task by content it reads. No sign in a shop window has
ever prompt-injected a human into buying a forklift. Accountability
deters people. It only records agents, unless it feeds a runtime
boundary. This is why the runtime check has to carry more weight for
agents than the card network carries for people. Deterrence is not
available as a compensating control.
**Spend is centralized. Risk and trust are not.** Expense governance
works partly because one function owns it. Finance sets the tiers,
issues the instruments, and runs the reconciliation, and everyone
accepts that. Authority in most organizations is the opposite. Every
application owner, platform team, and resource owner sets its own
rules, and no single function owns what agents may do. The lesson to
carry over is narrower than it first appears. The card system
centralizes the *record*, not the *decision*. The issuer enforces the
issuer-side bounds while every merchant still runs its own fraud
checks, and a permit from one never overrides the other. Agent
authority can follow the same split. Centralize the approved task so
there is one thing to approve, observe, and terminate. Leave each
resource authoritative for its own policy. What cannot stay distributed
is the record itself, and some function will have to own it the way
finance owns the budget. That is an organizational bill, not just a
protocol one.
The build list from the whole loop: purpose, projection, per-action checks, task-tied delegation, and endings that reach everywhere the authority went.
# The uncomfortable comparison
Read the mapping table again, top to bottom, and notice what it implies: a mature finance organization runs some version of every row.
For agents, many deployments still run few of them. The agent holds
broad scopes granted at integration time, exercises them at machine speed
against systems that mostly see token validity, and keeps working until a
credential happens to expire.
> The gap is not a missing product feature. It is a missing object.
And none of this is an indictment of the identity stack. OAuth and its
siblings were built for a world that never needed expense semantics:
applications were static, sessions were short, permissions were
predefined at registration, and a human sat at the keyboard for every
consequential moment. The human was the governance system. Purpose,
budget, and endings all lived in someone's head, enforced by presence.
Agents kept the credential shape and removed the person, and the
controls that were never needed became the controls that are missing.
The expense system works because the *approved purpose* is a first-class
record that every control consults: the intake form proposes it, the
approval creates it, the card projects it, the issuer enforces it, the
freeze terminates it, and the project code joins everything back to it.
Agent infrastructure has tokens, sessions, and logs. It does not yet
have the thing they should all be projections of. That is the argument
the [Mission Shaping](/series/mission-shaping/) series makes in
protocol terms.
The mapping is compact enough to carry:
| Expense world | Agent world |
| --- | --- |
| Trip | The approved mission |
| Intake form | Structuring the request |
| Corporate card | Bounded instrument / credential projection |
| Issuer authorization | Per-action enforcement |
| Budget and limits | Metered authority bounds |
| Merchant category | Action and parameter constraints |
| Limit increase | A fresh approval, never an edit |
| Project code | One task identifier |
| Expense report | Audit evidence |
The card program works because those objects are distinct and tied
together.
The last thing the analogy does is set the bar, and the bar is "ordinary." None of the expense controls above are considered advanced.
They are what any competent finance organization does by default,
because the alternative, handing out blank checks and reading the
statements afterward, is obviously negligent.
So when evaluating an agent platform, apply the corporate-card test:
- Where is the approved purpose recorded, and who approved it?
- Who derived the agent's limits, and can the agent widen them itself?
- What checks each action at the moment it runs, against what state?
- When the reason for the work ends, what stops, and how fast?
- Can a sub-agent spend the parent's authority, or does it get its own, narrower grant?
- Can an auditor pull one identifier and see the whole task?
If the answers would be unacceptable for a corporate card program, they
are unacceptable for an agent that can touch the same systems, move
faster than any employee, and be talked into things by the documents it
reads.
Give agents what you give people you trust: real autonomy, a real
boundary, and an instrument that stops working the moment the mission
ends.
Enterprises already know how to delegate authority safely. They do it
every day, at global scale, with a piece of plastic. The surprise is not
that agents need a new security model. The surprise is that we forgot we
already built most of one.
Stop issuing blank checks.
The four posts that follow open each room of this loop:
[the approval](/notes/you-approve-what-you-were-shown/) that binds what
the approver was shown,
[the contractor's card](/notes/the-contractor-gets-their-own-card/)
that narrows instead of borrowing,
[the network](/notes/the-network-approves-every-transaction/) that
approves every transaction rather than trusting the card, and
[the ending](/notes/canceling-the-card-doesnt-stop-the-charges/) that
actually ends.
---
# A Blocked Agent Is a Captive Client
Canonical URL: https://notes.karlmcguinness.com/notes/a-blocked-agent-is-a-captive-client/
Markdown URL: https://notes.karlmcguinness.com/notes/a-blocked-agent-is-a-captive-client.md
An agent is most of the way through a task when it tries to reach a destination its egress proxy does not allow. What comes back is an opaque failure: a connection reset, a timeout, a 403 with no body. So the agent does what a language model does with an unexplained failure. It retries. It reformulates the request. Eventually it gives up, or it improvises around the wall, which is worse. Every one of those moves is a guess, because the block carries none of what a right next move would need. The agent cannot tell whether the destination is wrong, down, malicious, or simply not approved yet. It cannot tell whether the block is requestable, who could approve it, how long approval would take, or whether retrying later would matter. There is no machine-actionable way for it to ask for access, and no human standing by at the moment of the block.
That is the wrong shape. The block is not just a network failure. It is an authorization denial at the egress boundary, and for many agent tasks that denial should be requestable. This is the egress version of a problem I wrote about for policy decisions in [Authorization Denied Is No Longer Enough](/notes/authorization-denied-is-no-longer-enough/). A deny is increasingly not a full stop. It is the beginning of an escalation. At the network egress boundary the same thing is true, and the enforcement point is the proxy.
Not every wall works that way, and it helps to say so up front. Some blocks are appealable and some are architectural. Security and governance blocks can be requestable, because some authority can say yes. A platform block, where the runtime cannot route outside its own vendor's ecosystem, leaves no policy decision to appeal. This post is about the first kind, and comes back to the difference at the end.
# Egress Allowlists Cannot Always Be Enumerated Upfront
Agent runtimes commonly route outbound traffic through an egress proxy that enforces a destination allowlist, and this is already standard practice rather than a hypothetical. Anthropic's [sandbox-runtime](https://github.com/anthropic-experimental/sandbox-runtime) puts a proxy outside the sandbox that allows no domains by default and permits only the hostnames you configure, and the same allowlist model sits behind Claude Code's managed sandbox ([secure deployment guidance](https://platform.claude.com/docs/en/agent-sdk/secure-deployment)).
Open-source agent egress proxies like Brex's [CrabTrap](https://github.com/brexhq/CrabTrap) sit between the agent and external APIs and evaluate every outbound request against allow and deny policies, returning a 403 on a block, and current guidance for running autonomous agents treats a network egress allowlist as a baseline control. They share one behavior: when a destination is not on the list, the agent gets a failure, not a way forward. That model assumes you can enumerate the destinations ahead of time. Agents increasingly do not work that way.
As I described in [OAuth for Open-World Ecosystems](/notes/oauth-for-open-world-ecosystems/), modern agents discover APIs dynamically, interact with previously unknown systems, and expand workflows during execution. A renewal assistant that started with a known set of endpoints discovers mid-task that the answer lives behind a partner API it has never called. A host-level allowlist provisioned at startup cannot accommodate that. The deployment is left with two bad options: over-provision egress so broadly that the proxy stops being a control, or block the destination and let the task stall. Bounded, high-assurance workloads can and should still enumerate egress ahead of time. The claim here is narrower. Dynamic, open-world agent tasks cannot always name every legitimate destination before execution, and those are exactly the tasks agents are being hired for.
The block is the honest behavior. The problem is what the block does not carry.
# The Block Is a Requestable Denial, and the Proxy Is a PEP
Reframe the egress proxy as a policy enforcement point. The dropped connection is a decision, and that decision could be a *requestable denial*: access is currently denied, but an escalation path is available. The agent could ask for the destination, a governance event could occur, and the proxy could re-evaluate and let the traffic through.
What is missing is not the decision. It is the transport. A headless agent has no browser, no consent screen, and no human in the loop at the moment of the block. So how does it discover that a block is requestable, learn where to ask, and find out when it has been let through?
When the call crosses a tool boundary instead of a raw socket, this problem already has an answer at that layer. MCP's proposed structured authorization denials carry exactly this shape, a machine-actionable denial with remediation hints returned to the agent's orchestration loop, covered in [the least-privilege series](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/). The tool boundary is the better escalation surface where it exists, because it sees operations rather than bytes. The egress profile below is for everything that boundary cannot see: HTTP the agent's code makes directly, destinations discovered outside any tool wrapper, and the floor beneath a tool layer that is misconfigured or bypassed. The two are siblings with the same discipline, and the rest of this post is about the floor.
# The Carrier Depends on Where the Block Lands
The floor is not one surface. An egress denial can land at an explicit HTTP proxy that can answer with a real response, inside an authorization-aware exchange, or as a bare socket reset in an opaque tunnel, and each position has a natural carrier that already exists.
| Where the block lands | Natural denial carrier |
| --- | --- |
| Explicit HTTP forward proxy | An HTTP response with [Proxy-Status (RFC 9209)](https://www.rfc-editor.org/rfc/rfc9209.html) naming the error and a [Problem Details (RFC 9457)](https://www.rfc-editor.org/rfc/rfc9457.html) body carrying the recovery document |
| AAuth-aware boundary | The protocol's own challenge, in-band |
| MCP tool boundary | The structured authorization denial above |
| Opaque tunnel, firewall drop, or socket reset | An authenticated side-channel status API, the case the rest of this post profiles |
| The whole network session | RFC 8908 as designed |
Where an HTTP response exists, use it. Proxy-Status was designed for intermediaries to describe the errors they generate, and a Problem Details body can carry the same structured recovery members this post defines. The hard case is the one with no response surface at all, a reset inside a tunnel the proxy cannot speak into. That case needs a side channel, and the side channel needs a state machine.
# RFC 8908 Supplies the Recovery State Machine
The captive portal is not the proposal. The proposal is that a blocked agent needs a recovery state machine, and RFC 8908 demonstrates one that already shipped.
[RFC 8908](https://www.rfc-editor.org/rfc/rfc8908.html), the Captive Portal API, is the JSON API behind the coffee-shop and hotel wifi experience. A client queries it and learns whether it is `captive`, where the portal is (`user-portal-url`), and how long any granted access lasts (`seconds-remaining`). It was designed so a device could discover its network restriction without scraping an HTML interstitial.
A blocked agent can be modeled as a captive client. It is on a network, behind an enforcement point, denied egress, and it needs a machine-readable answer to "why am I blocked and how do I get out." That is the state machine the Captive Portal API demonstrates: restricted, remediation available, re-query after completion. What it does not supply is the rest. A requestable-egress profile still needs its own scoping, discovery, authentication, and extension contract, and RFC 8908's registry rules mean the members this post proposes are extensions to define and register, not fields the spec already carries.
That is the move both designs in this post make. This is not a claim that per-destination agent egress is what RFC 8908 originally standardized. It is a profile of the same recovery shape: a blocked client queries a known API, receives `captive: true`, and learns the machine-actionable path to remediation. In this profile, the egress proxy is both the enforcement point and the captive-state authority for the blocked destination, and the blocked response carries the recovery context alongside `captive: true`: a proposed `access-request` member at the destination altitude, or an opaque denial-bound `resource-token` at the operation altitude.
Repurposing a captive-network protocol for headless agents has three costs, and they are worth naming up front because they are the price of the insight, not an afterthought.
**Granularity mismatch.** RFC 8908 signals captivity at the network level: you are on this network, you are captive or you are not. Egress control is per-destination: this agent may reach destination A but not destination B. Both designs resolve this by scoping the response, not the network. The runtime queries its one configured captive endpoint with the blocked destination, and the answer is a per-denial document carrying that destination's requestable denial rather than a network-wide state. RFC 8908 defines a GET of one provisioned URI and no destination parameter, so the scoped query is itself part of what this profile has to define. The document is not a bearer capability. The authority lives in the denial binding inside it, authenticated by the proxy and scoped to the subject, destination or operation, proxy identity, policy context, and expiry. A copied response must not open another destination, and the endpoint must not let an unauthenticated caller enumerate what the agent tried to reach.
**Discovery into TLS tunnels.** Direct configuration is the safe baseline: the agent runtime starts with the egress proxy's captive API endpoint and queries it when the proxy reports a blocked destination or operation. RFC 8910 provisioning and HTTP 511 help in managed environments, but a proxy cannot inject a 511 into an opaque TLS tunnel without terminating it. Fully tunneled HTTPS egress therefore cannot rely on network injection as the discovery mechanism.
**Untrusted URLs.** The `user-portal-url` and `venue-info-url` in a captive portal response are human-facing and attacker-influenceable. An agent must not navigate them or, worse, feed them to an LLM as instructions. The machine-actionable path is the structured member the response carries, the `access-request` member or the `resource-token`, not the portal URL. This matters more for agents than for humans, because the client here is a model that will tend to act on text it reads.
# The Minimum Shape
A useful profile does not need to standardize the whole approval system behind the proxy. The minimum flow is short:
1. The agent or runtime attempts egress.
2. The proxy blocks the destination or operation.
3. The runtime queries the configured captive API endpoint for that denial.
4. The captive response says `captive: true` and carries a structured recovery member.
5. The runtime, agent, or proxy submits the requestable denial to the approval service the member names.
6. On completion, the agent retries.
7. The proxy re-evaluates before allowing the traffic.
Drawn as the state machine it is, with the failure branches the list leaves implicit:
```mermaid
flowchart TD
A[Agent attempts egress] --> B{Proxy evaluates
current policy}
B -->|allow| C[Request proceeds]
B -->|deny| D[Runtime queries its
configured captive endpoint]
D --> E{captive?}
E -->|false| F[Real failure,
surfaced as one]
E -->|true| G[Denial document with
recovery member and binding]
G --> H[Requestable denial submitted
to the approval service]
H --> I[Held work with a durable
handle, the turn ends]
I -->|denied| J[Denial surfaces
to the task]
I -->|approved| K[Orchestrator re-invokes
the work with the handle]
K --> A
```
What the profile has to define is the recovery contract between the blocked agent and the enforcement point:
- how the agent discovers or is configured with the captive API endpoint
- how the proxy scopes a captive response to the blocked destination or operation
- which response member carries the machine-actionable recovery path
- how the denial is cryptographically bound to the thing that was blocked
- how long the denial binding and any resulting approval remain valid
- how the agent polls or is notified that the request has completed
- how the proxy re-evaluates before allowing egress
- what the agent must ignore, especially human-facing portal URLs and untrusted text
On that first point, the safe baseline is direct configuration: the agent runtime is launched knowing its egress proxy's captive API endpoint and queries it when a connect attempt is blocked. HTTP 511 and RFC 8910 provisioning help in managed environments, but they are not the baseline for fully tunneled HTTPS, where the proxy cannot inject a signal into the tunnel. The minimum profile assumes the runtime already knows its proxy and standardizes what comes back when a destination or operation is captive.
This also answers a question the tunnel otherwise makes unanswerable, whether a reset was the proxy's policy or the network's weather. The runtime does not need to tell them apart at the socket. On any suspicious failure it asks its configured captive endpoint about that destination. `captive: true` means a policy block with a recovery path. `captive: false` means the failure is real and should surface as one. The query is cheap, authenticated, and turns every ambiguous drop into a classified one.
Everything else can be deployment-specific: approver routing, ticketing systems, user interface, risk scoring, and the internal policy language. The standardization point is the blocked-client handshake, not the governance workflow behind it.
The path to that handshake is incremental, which is how enforcement seams actually ship. The first phase needs one operator and no counterparty. An egress proxy starts returning recovery metadata instead of a bare 403 or a silent reset, using whichever carrier its position allows. The second phase wires that metadata to an approval service and a policy re-evaluation, still entirely inside one organization. Only the third phase, binding approvals to mission state at operation grain, waits on the authorization substrate. Each phase pays for itself before the next one exists.
# Who Runs the Recovery
The recovery actor does not have to be the model, and that matters for adoption. "The client" in this design is really two things: the agent, meaning the model and its tool code, and the runtime's egress layer underneath it. The recovery can live in either, or in the proxy itself, which changes how much has to be modified.
**Transparent hold-and-forward.** A TLS-terminating proxy can hold the blocked request open, run the recovery itself, and forward the call on approval or fail it on denial. The agent sees one slow request that succeeds or fails, and nothing about it changes. This is the server-brokered shape, the same move as parking a denied tool call as held work, applied at the egress hop. Its limit is the clock. It works when the decision fits inside the client's request timeout, which covers automated policy re-evaluation and a fast human tap, but not an approval that takes minutes. Past that window the connection times out and something has to retry.
**A captive-aware egress client in the runtime.** The layer that retries can be the runtime's outbound stack rather than the agent. A generic captive-aware egress client detects the block, runs the recovery handshake, waits, and retries on behalf of every agent the runtime hosts. Write it once and no individual agent changes. This is the agent-egress analogue of the Captive Network Assistant a laptop already runs, the component that handles captivity for every app without each app knowing about it. It is also what "direct configuration is the baseline" really means. The thing configured, and modified, is the runtime's egress layer, not the agent.
**The agent itself.** At the far end, the agent parses the machine-actionable member in the captive response and drives the recovery, or it speaks the authorization protocol natively and skips the captive handshake entirely, as the next section takes up. This is the most capable path, but it is not the only one, and it is not required for an agent that cannot be changed.
So the cost of adopting this is not "rewrite every agent." A transparent proxy covers fast decisions with no client change at all. A captive-aware egress client in the runtime covers slow approvals for every agent it hosts, still without touching agent code. Parsing the member or speaking the protocol natively buys the agent direct control when that is worth having, and only then.
The wait itself has a shape, and it is not an open connection or a spinning loop. A recovery that outlives the request timeout becomes held work, and holding it is two actors' jobs. The egress layer's part ends at raising the held state and its durable handle up the stack, because a network sandbox cannot serialize an agent's cognitive state. The orchestrator above it captures the handle, checkpoints the run, and lets the agent's turn end rather than idling a context window against a human's calendar. Approval re-invokes the work with the handle, and the retry happens in a fresh turn. An agent that spin-waits on an approval is burning its own working memory to simulate a queue the runtime should own.
# Two Ways to Carry the Recovery
There are two ways to carry the recovery inside the captive response, and they sit at different altitudes on different authorization substrates. They are siblings, not a stack. AuthZEN carries a destination-level requestable denial, AAuth carries operation-level authorization, and neither requires the other. The interesting axis between them is how fine-grained the control is and how much human judgment each block deserves. And a client that already speaks AAuth needs no captive handshake at all, which the last part of this section takes up.
## Destination-Level, on AuthZEN
Destination-level recovery is a proposed extension of the AuthZEN Access Request and Approval Profile (ARAP) from the requestable-denial work. The egress proxy is both an AuthZEN policy enforcement point and a Captive Portal API server. The agent is the AuthZEN Subject, the blocked destination is the Resource, and `connect` is the Action. When the proxy blocks, the captive portal response carries the requestable denial in a proposed `access-request` member. The denied subject, resource, and action are reconstructable from the denial binding, so the member can carry just the context:
```json
{
"captive": true,
"user-portal-url": "https://proxy.example.com/portal/req_01J8Z3",
"access-request": {
"context": {
"evaluation_id": "eval_01K2Q4DP3K",
"reason": "egress_not_allowed",
"access_request": {
"endpoint": "https://pdp.example.com/access/v1/requests",
"template": "egress_destination",
"expires_at": "2026-07-31T20:25:00Z",
"binding_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InBkcC0xIn0...",
"display": {
"title": "Egress to api.partner.example is blocked",
"description": "Approval is required before this agent may call api.partner.example."
}
}
}
}
}
```
In the base profile the PEP owns this machinery. It submits the access request with the denied Subject, Resource, and Action, holds the Task Handle, and re-evaluates on completion. This design keeps that default. The proxy submits, and the captive endpoint exposes only safe status for the runtime to poll, so the agent never touches the task machinery. Letting the runtime submit directly is a proposed extension, not base behavior: the Access Request Service must authenticate the caller and authorize it for the bound denial, the submission presents the denial-binding material (`evaluation_id` or `binding_token`) rather than restated context, and this profile additionally requires that submission to be sender-constrained, so a leaked identifier cannot be redeemed by another party. Either way, the agent retries only after the proxy re-evaluates against current policy using the base profile's `reevaluate` completion mode, and only then does the destination become reachable. Approval resolution is server-side by default. The proxy reads the approval from PDP state at re-evaluation rather than trusting an approval the agent injects into a header. When the connection is allowed, a re-query returns `captive: false` with `seconds-remaining` derived from the approval's expiry, and at expiry the destination returns to captive.
This is the policy-decision shape of the problem. What gets approved is "may this subject connect to this destination," and the PDP stays authoritative end to end. The response itself stays RFC 8908-shaped, served as `application/captive+json` with the requestable denial inside it.
## Operation-Level, on AAuth and Mission
Operation-level recovery sits at a finer altitude. It builds on Dick Hardt's AAuth protocol ([draft-hardt-oauth-aauth-protocol](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/)) and its Rich Resource Requests (R3) companion ([draft-hardt-aauth-r3](https://dickhardt.github.io/AAuth/draft-hardt-aauth-r3.html)), with the Mission layer I have written about in [Mission Architecture on AAuth](/notes/mission-architecture-on-aauth/) and [AAuth Now Has a Mission Layer](/notes/aauth-now-has-a-mission-layer/). Control is at the operation level, not the host level. The question is not "may I reach this host" but "may this mission perform this operation, with these parameters."
Provenance matters here, because four sources are composing. The signed exchanges, resource tokens, and challenges come from the AAuth core draft. The `r3_granted`, `r3_conditional`, `r3_uri`, and `r3_s256` fields come from the R3 companion, a living editor's copy rather than a submitted draft. Mission is the governance layer I have argued for on top of both. And the captive-portal carriage of any of it is this essay's proposal, not theirs.
The proxy is not only deciding whether the agent may reach `api.partner.example`. It is deciding whether the current Mission permits a specific operation exposed by that API. Routine operations can be pre-authorized in the token. Risky operations can be marked conditional, so the proxy stops the call and asks the Person Server for a fresh decision before the side effect leaves the network.
Operation-level recovery is only as good as the proxy's visibility. Behind an opaque `CONNECT` tunnel the proxy can only govern the host or connection. A TLS-terminating proxy can match on HTTP method and URL. An API-aware gateway can govern named operations like `invoice.read`, deriving an R3 document from a published API description (OpenAPI, MCP, gRPC) when the destination does not publish one itself. That derived document bounds what the enterprise lets the agent attempt. It does not manufacture the third party's authorization semantics or grant anything at the resource, which still applies its own policy to whatever arrives. The finer the visibility, the finer the grant, and fully tunneled egress falls back to host-level rules.
Where the proxy can see operations, the Mission-bound token carries a two-tier model: `r3_granted` operations flow through the proxy immediately, the fast path where authority travels with the agent, while `r3_conditional` operations are authorized in principle but validated against the Person Server per call, so the expensive evaluation happens only where it earns its cost. The agent never holds the R3 document that defines those operations. It carries only `r3_s256`, the SHA-256 hash of the document at `r3_uri`, and the Person Server, not the agent, fetches and evaluates the semantics, so prompt injection cannot rewrite the operation set through the agent.
So when the renewal agent tries to issue a refund through the same partner API, the block is not "you may not reach this host." It is "this mission is not yet authorized for `payment.refund` on this destination." For a client that reaches the proxy over plain HTTP and does not speak AAuth, the proxy carries the recovery in the captive portal response. The response hands back an opaque `resource-token`: a denial-bound recovery artifact, not a credential and not authority. It captures the blocked call, the proxy, the subject, the destination, the destination's R3 document hash, and an expiry, so it can be redeemed only as input to the Person Server's evaluation. The response also carries a `user-portal-url` pointing at the Person Server for the human-facing review:
```json
{
"captive": true,
"user-portal-url": "https://person-server.example.com/consent/req_01K2R7",
"resource-token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InByb3h5LTEifQ..."
}
```
The agent presents that `resource-token` to its Person Server in a request that names the R3 operations it needs, here `payment.refund`. The Person Server verifies the token, fetches and validates the R3 document for the destination (served only to signed, authorization-side requests), and routes the parameter-sensitive parts to the human, who approves against the R3 `display` text, its `summary`, `implications`, `data_accessed`, and an `irreversible` marker, rather than the agent's own rationale. On approval, the Person Server issues a refreshed Mission-bound token whose `r3_granted` and `r3_conditional` sets reflect the new authority.
The agent presents that token to the egress proxy and retries. `invoice.read` flows straight through. `payment.refund` is now authorized, but as a conditional operation it is challenged on every call: the proxy returns an AAuth challenge carrying the concrete parameters, the agent round-trips to the Person Server, and the Person Server issues a per-call token or denies before the refund leaves the network.
## When the Client Speaks AAuth
The captive portal is a bridge. It exists because the blocked client is usually a generic HTTP agent that has no idea what AAuth is, so the proxy translates a Mission-bound refusal into an RFC 8908 response the agent can parse without speaking the authorization protocol underneath.
A client that speaks AAuth does not need the bridge. AAuth carries authorization in the HTTP exchange itself, in signed requests and in the challenges a resource returns through the core draft's `AAuth-Requirement` header, which names what the exchange still needs, so the refusal and the re-authorization happen in-band at the same request boundary the egress proxy already enforces. There is no separate captive handshake to discover. The block is an AAuth challenge, and the agent satisfies it the way it satisfies any conditional operation, by round-tripping to its Person Server before the proxy re-evaluates.
That collapses two cases into one. Over the captive portal, the initial question of whether the mission may reach the destination at all and the per-call question of whether it may perform `payment.refund` look like different events, one a captive response and one a later challenge. Natively, they are the same mechanism at different moments. The first blocked destination is just the first `r3_conditional` challenge, and every later one takes the same path.
So the two altitudes above are really about the client, not the protocol. Destination-level on AuthZEN and operation-level over the captive portal are how a blocked HTTP agent gets a machine-actionable way out. AAuth-native operation governance is a possible native path. The captive portal matters because today's agents mostly do not speak AAuth: they speak HTTP, fail on opaque network errors, and need a compatibility recovery channel in the meantime.
## Same Transport, Different Altitude
| | AuthZEN Access Request and Approval Profile | AAuth and R3 Mission egress |
| --- | --- | --- |
| Granularity | Destination (host) | Operation (or host, by proxy visibility) |
| What is approved | May this subject connect to this destination | May this mission perform this operation, with these parameters |
| Consent and approval | Policy re-evaluation at the PDP, human approval optional | Human approval at the Person Server against resource-authored display |
| Substrate | AuthZEN Authorization API (PDP and PEP) | Dick Hardt's AAuth and R3 drafts, plus the Mission layer |
| Enforcement-time check | Re-evaluate against current policy | `r3_conditional` validated per call |
| Captive portal | Required, the recovery transport | Bridge for HTTP-only clients, skipped when the client speaks AAuth |
Both use RFC 8908 when the client speaks only HTTP, though they carry the recovery differently. The AuthZEN design puts the requestable denial in an `access-request` member. The AAuth design returns an opaque `resource-token` that the agent takes to its Person Server, and it drops the captive portal entirely once the client speaks AAuth. The choice between them is altitude, how fine-grained the control needs to be and how much human judgment a given block deserves. A coarse "may I reach this partner API" is well served by destination-level policy re-evaluation. A consequential, parameter-shaped action like a refund, which a person should look at before it happens, belongs at the operation level with curated consent. They are tiers for different risk, not competitors.
# The Principles That Keep This Honest
Reusing the captive portal API is the easy insight. The discipline is in what the proxy is allowed to conclude from a block.
**Approval is not a grant.** A `captive: true` response is a requestable denial, not a grant, whether it carries an `access-request` member or a `resource-token`. The proxy keeps the destination blocked until re-evaluation, or a re-projected token, actually permits it. This is the same invariant as the policy-decision case: the enforcement layer stays authoritative at the moment of access, and the workflow that produced the approval does not get to declare the connection open. An approval signed earlier is an input to that decision, not a substitute for it. It is the same reason a [session is not a mission](/notes/sessions-are-not-missions/) and the reason [governing the stay](/notes/governing-the-stay-not-just-the-entry/) matters more than governing the entry.
**Binding prevents confused-deputy egress.** The denial binding ties the approval to the exact thing denied: an `evaluation_id` or `binding_token` for the destination in the AuthZEN design, the `r3_uri` and `r3_s256` for the operation set in the AAuth design. An approval for one destination cannot be replayed to open another. Without that binding, the egress proxy becomes a confused deputy.
**Egress approval permits the attempt. Resource authorization permits the operation.** Everything the proxy grants is about what may leave the network, at whatever granularity it can see. The destination still authenticates, authorizes, and may refuse the call on its own terms. No captive response, access request, or refreshed token binds the resource, which is the same final-word boundary the control-points series draws.
**Consent is curated, not narrated.** When a human is in the loop, they approve against resource-authored, structured descriptions of impact, and the agent's own rationale is untrusted and sanitized before display. The person approving an agent's refund needs to see what the operation will actually do, not what the agent says it will do. This is the credential-side argument from [From Passports to Power of Attorney](/notes/from-passports-to-power-of-attorney/) and the [Power of Attorney series](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/), applied at the network boundary: authority is granted against the work, not against the asker's self-description.
# Failure Modes to Design Against
The profile has to be strict about failure because the blocked client is an agent, not a person reading a hotel wifi page.
**Malicious portal content.** The agent must treat `user-portal-url`, `venue-info-url`, and any human-readable display as untrusted. It can surface them to a human or governance system, but it must not browse them, summarize them into its plan, or follow instructions found there.
**Approval replay.** An approval for one destination or operation must not open another. The denial binding has to cover subject, destination, operation where applicable, proxy identity, expiry, and the policy decision context.
**Stale approval.** Approval is time-bounded. The proxy must re-evaluate at the moment of egress and must return to `captive: true` when the approval expires, the Mission changes, or the policy state changes.
**DNS and wildcard ambiguity.** Destination-level approval needs precise matching rules. `api.partner.example`, `*.partner.example`, IP literals, CNAME chains, SNI names, and CONNECT targets are not interchangeable unless policy explicitly says they are. The binding also has to survive time and indirection. Approval binds to what was actually approved, scheme, hostname, port, resolved address class, and TLS server identity, and neither DNS rebinding between approval and retry nor a redirect chain from an approved destination may carry the request somewhere unapproved under the original approval.
**Non-idempotent replay.** A held or retried request is not automatically safe to send twice. Transparent hold-and-forward must never replay unsafe methods on its own initiative. A POST, a payment, or a refund needs an idempotency key the destination honors, or it must be reconstructed and re-issued explicitly by the agent after approval rather than replayed from a buffer.
**Held-request contents.** A request parked for approval can contain credentials, tokens, and sensitive bodies. Whatever holds it becomes a custodian: the held material needs encryption at rest, a hard expiry aligned with the denial binding, and redaction from approval surfaces and logs.
**The status API as an oracle.** A queryable captive endpoint that answers questions about arbitrary destinations is a policy oracle and an SSRF-shaped probe. It must authenticate its callers, answer only for the runtime's own blocked attempts, rate-limit, and avoid disclosing policy structure in its answers. The correlation state behind those answers is itself a target. Answering a socket-reset query means keeping a recent registry of blocked attempts per client, and that registry must be bounded, a ring buffer with a hard TTL aligned to the denial binding, so a looping agent or induced failures degrade classification rather than exhaust the proxy.
**TLS termination's own cost.** Operation-level visibility prices in interception. A terminating proxy holds plaintext, credentials, and session material for every governed call, which concentrates privacy and key-custody risk at the proxy and makes the proxy itself a high-value target. Deployments that cannot accept that concentration fall back to host-level control, and should say so rather than terminate quietly.
**Proxy impersonation.** The agent must know which egress proxy and captive API it is allowed to trust. A random captive response from the network is not enough. Direct configuration, mTLS, signed metadata, or an equivalent trust mechanism is required.
**Privacy leakage.** A requestable egress denial reveals intent: where the agent wanted to go, what operation it wanted to perform, and sometimes why it was blocked. The proxy, PDP, AS, and approval workflow all become observers of task state. The response should minimize what is carried in the captive payload and keep sensitive prompt or plan evidence out of the agent-visible fields.
# Not Every Block Is Requestable
The recovery above assumes a block that someone can say yes to. It helps to separate three kinds, because only two of them are requestable.
A **security block** says "I do not trust this destination." The policy behind the proxy has decided the destination is unsafe or out of bounds. An approver can still be asked, so the block is requestable, and the answer may legitimately stay no.
A **governance block** says "this mission may not use this destination." The destination is fine in general, but the current task was not approved for it. This is the operation-level case above. Recovery is a mission decision, and widening the mission is exactly the escalation the captive response carries.
A **platform block** says "the enterprise would allow this, but the platform cannot." The agent runtime only permits the destinations its own vendor's ecosystem exposes, so an agent that discovers it needs a system outside that ecosystem has nowhere to appeal. There is no approver who can grant it, because the constraint is not a policy the proxy can re-evaluate. It is the architecture of the runtime.
That third case is the one that turns a blocked agent into a captive one, and it is worth noticing that the word has changed meaning. Until now captive meant restricted under a policy with a way out. Here it means structurally confined, and no recovery contract reopens architecture. Picture an enterprise whose agent is approved to work across its stack. The CRM lives at one vendor, contracts in Box, signatures in DocuSign, procurement in Coupa, and HR in Workday. If the agent's runtime egress only permits one vendor's endpoints, the agent cannot reach any of the others, whatever the enterprise approved. The block is not security and not governance. It is reachability, and reachability is becoming a perimeter of its own. An agent is only as autonomous as the destinations it is allowed to discover.
That is a different problem from the one this post solves, and a larger one. The recovery mechanism here makes security and governance blocks requestable. It does nothing for a platform that will not route beyond its own ecosystem, because there is no denial to appeal. OAuth deliberately let a client talk to many resource servers, and no one expected "this client may only call APIs owned by one vendor." Runtime connectivity controls can quietly produce exactly that. Whether agent ecosystems stay open is a portability question, not a recovery question, and a larger one than this post can carry.
# The Wall Is a State, Not a Dead End
The egress proxy was always a policy enforcement point. We just treated its block as a failure instead of a state.
For agents, that is no longer enough. A blocked destination may be unsafe, outside the mission, or merely not approved yet. The client needs to know which kind of wall it hit and whether there is a way through.
The pieces compose rather than compete. RFC 8908 contributes the recovery state machine: discover captivity, learn the remediation path, and retry after policy changes. Proxy-Status and Problem Details carry the denial where an HTTP response exists, and an authenticated side channel carries it where one does not. AuthZEN evaluates whether this subject may reach this destination. AAuth carries the operation-level ask in-band, skipping the captive-portal bridge when the client speaks it, and Mission decides whether widening the work is on the table at all.
A blocked agent behind a policy is not stuck. It is captive in the recoverable sense, and that captivity is a state with a defined way out.
---
# The Identity Continuation Assertion
Canonical URL: https://notes.karlmcguinness.com/notes/identity-continuation-assertion/
Markdown URL: https://notes.karlmcguinness.com/notes/identity-continuation-assertion.md
A user signs into the travel app, which calls the expense app, which must call the corporate card ledger. The first hop is easy: the travel app holds the user's ID Token and exchanges it for an ID-JAG addressed to the expense app's Resource Authorization Server. ID-JAG is built for exactly that: present a user credential, get an audience-scoped grant.
The expense app has none. It received a service call, not a browser session: no ID Token, no SAML assertion, no refresh token for the user. The expense app inherited the work but not the user. Yet to reach the ledger it needs an ID-JAG carrying the ledger's name for the user, and only the IdP can produce that.
That last point is the argument of [Re-Subjecting Is a Mint, Not an Attenuation](/notes/re-subjecting-is-a-mint-not-an-attenuation/): when a chain crosses into an application that names the user differently, only the IdP can mint the next hop, because only the IdP holds the subject map. But the IdP can only mint again if something it trusts survives the previous hop to attest that the chain is still live. That is the invariant everything here follows from, and it is exactly what the intermediate is missing. ID-JAG says what to present at the first hop and goes quiet about the rest. That silence is the gap, and closing it is this whole post.
This is no longer a human-paced problem. An agent acting for the user triggers these chains at machine speed and fans out to services nobody listed when the task began, with no one to send back through a login screen when a hop lacks a credential. The design this post walks through is published as [draft-mcguinness-oauth-id-continuation-assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-continuation-assertion/), an individual Internet-Draft at revision 00.
# What the Intermediate Is Allowed to Present
Start by ruling out the obvious answers, because each fails for a precise reason, and the reasons add up to a specification.
**Forward the user's ID Token.** The intermediate never had a browser session, so there is no ID Token to forward. Pass one along anyway and it is still audience-bound to the original app and names the user in the original namespace, not the ledger's. It also scatters a bearer user credential across every service in the chain, which is the sprawl federation was meant to end.
**Reuse the inbound ID-JAG.** The expense app holds an ID-JAG for the expense Resource Authorization Server, carrying the expense app's pairwise subject. Sent to the ledger it is wrong three ways: wrong audience, wrong subject namespace, and it is an access-bearing grant, not a request for a new one.
**Use a refresh token.** It can mint new tokens, but it is a long-lived bearer credential bound to the original client and audience, carrying none of the chain or actor context. ID-JAG accepts one at the first hop because the holder actually authenticated the user. The intermediate did not.
**Pre-mint every audience at the root.** That works only for a closed, known set of targets, and it becomes a standing bundle of bearer authority that removes the per-hop decision. Dynamic fan-out breaks it the moment the chain reaches an audience nobody listed.
The failures converge. What the intermediate may present has to be something it can hold without a user session, that names no user and carries no access, that is bound to the intermediate and cannot be replayed, and that the IdP can verify ties to a real, still-live delegation. In a phrase: evidence, not authority. That is the Identity Continuation Assertion.
# Evidence, Not a Credential
It is a short-lived, sender-constrained JWT, issued by a Chain Authority the IdP trusts, presented as the Token Exchange subject token to request an onward ID-JAG. It is not an ID-JAG, and it grants no API access.
Decoded, the assertion for the expense app's onward call looks like this:
```json
{
"iss": "https://chain-authority.expense.example/",
"aud": "https://idp.enterprise.example/",
"identity_continuation_handle": "01JZ8F4J9J8Y3NDK5WQ4P9K7Q2",
"act": { "iss": "https://expense.example/", "sub": "svc-expense" },
"cnf": { "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" },
"iat": 1717700000,
"exp": 1717700300,
"jti": "ica-01JZ8GB2Q7"
}
```
| Claim | Purpose |
| --- | --- |
| `iss` | The Chain Authority that issued the assertion, trusted by the IdP for the tenant |
| `aud` | The Continuation Authorization Server (the IdP), a single issuer identifier |
| `identity_continuation_handle` | Opaque, IdP-generated handle for this hop of the chain, at least 128 bits of entropy and no user-identifying information |
| `act` | The single current actor presenting the request, as `iss` and `sub` per RFC 8693 with no nesting. The IdP composes the full lineage into the onward ID-JAG |
| `cnf` | Sender-constraint by DPoP to the key the presenting actor proves, carried as its JWK thumbprint (`jkt`) |
| `iat` / `exp` | Issuance and expiry, lifetime no more than 300 seconds |
| `jti` | Single-use replay identifier, atomically consumed by the IdP on issuance |
It is presented in a Token Exchange request that is identical in shape to a direct ID-JAG request, with only the subject token swapped:
```http
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:identity-continuation
&audience=https%3A%2F%2Fledger.example%2F
&resource=https%3A%2F%2Fapi.ledger.example%2F
&scope=charges.write
&actor_token=
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
```
The audience, resource, scope, and requested token type stay in the request, never in the assertion, so a direct request and a continued one have the same shape. What the assertion refuses to carry is the point. It MUST NOT contain a top-level `sub`, `auth_time`, `acr`, or `amr`. It does not name the user, and it does not claim how or when the user authenticated. Those are facts the IdP already holds. The assertion only attests that the chain is live. You cannot inflate a subject or an assurance level you were never given.
What it adds is deliberately narrow. For the applications in the chain the surface barely moves: the same Token Exchange, one new subject-token type, and the same ID-JAG out the other side. The new weight sits in the control plane, where the IdP already holds the subject map and the delegation state, plus one role that has to exist, a Chain Authority that can see the live chain. The assertion only gives a credential-less intermediate a sanctioned way to ask. No new authority is minted that ID-JAG did not already mint, and no Resource Server learns anything it did not already know. It changes who may ask, not what authority exists.
# So Why Would the IdP Trust It?
Stop on that, because it should make a security reviewer nervous. A service that never saw the user, holding no user credential, just asked the IdP to mint a grant for that user at a system it has never talked to. Honor that on the service's say-so and you have built the confused deputy on purpose.
The assertion is safe to trust only because four things are true at once, and each is a way of saying the same thing: it is evidence, not authority.
**It does not come from the service.** The issuer is a Chain Authority, and the IdP honors only the ones its tenant configuration names. Ask why this role has to exist at all. The IdP cannot vouch for the chain itself, because the work left its hands at the first hop and now runs between services it never sees. The intermediate cannot vouch for itself, because a service asserting its own continuation is the confused deputy this design exists to prevent. What is left is a party that can actually see the in-flight delegation and that the IdP has agreed in advance to believe, usually the transaction-context service that propagates user and request context across a domain's internal calls, signing an attestation that this chain is live. The IdP is trusting a configured authority's signed statement, not the asker's word, and an intermediate cannot appoint itself one.
**It names no one.** With no subject and no authentication context in the assertion, there is nothing for the intermediate to spoof. The IdP resolves the ledger's subject from `identity_continuation_handle` against its own record, and it trusts its own memory of how the user authenticated.
**It is bound and spent once.** It MUST be sender-constrained through `cnf` to the key the presenting actor proves, by DPoP, and MUST NOT be accepted as a bearer token. The actor token presented alongside it MUST be bound to that same key, so the whole request rests on one proof of possession. Its lifetime MUST be no more than 300 seconds. The IdP consumes the `(iss, jti)` pair atomically on success and honors it at most once. Copy the assertion off the wire and you hold a 300-second, single-use token bound to a key you do not have: the IdP wants a proof of possession you cannot produce, and if the real caller already spent it, the `jti` is gone. A stolen assertion buys nothing.
**It is an input, not a verdict.** Even with all of that, the IdP decides. It confirms `identity_continuation_handle` is active and eligible, that the requested audience and scope sit inside the root delegation's envelope, and that the actor named in `act` is the authenticated caller, and only then mints. The assertion buys a fresh decision, not access.
That is the resolution. An intermediate can drive the chain forward without ever being trusted as an authority, because the only authority in the exchange is the IdP, deciding again, against state it owns.
# What It Looks Like Running
Strip it to the moving parts, split into a control plane the IdP owns and a data plane the apps see.
The control plane is the enterprise IdP, acting as the Continuation Authorization Server. It owns the subject map and exposes the token-exchange endpoint that mints ID-JAGs. Behind it sit three pieces of state. A **root-chain envelope** records the authenticated user, the original authentication context, the authorization basis as enumerated target entries or a policy reference, the governing authorization whose lifetime bounds how long the chain may continue, which actors or trust domains may continue it, the maximum actor-chain depth, an expiry, and the authenticated root actor, with each hop recorded under its own `identity_continuation_handle` and an immutable reference to its parent, so the chain is a tree the IdP walks rather than one growing token. A **single-use store** records each consumed `(iss, jti)` until the assertion expires. A **subject resolver** turns the canonical user into the audience-local pairwise subject for a given target, by directory lookup or deterministic per-sector derivation. Alongside the IdP, each domain runs a **Chain Authority**, often a transaction-context sidecar, that holds the live chain context and signs assertions with a key the IdP has registered. The IdP records, for each hop, which Chain Authorities may attest it, so only one mapped to that hop's Resource Authorization Server can continue it, and the trust is hop-specific rather than tenant-wide.
The data plane is the workloads and the Resource Authorization Servers. Each workload holds a sender-constraining key. Each Resource Authorization Server trusts the IdP, accepts an ID-JAG as a JWT authorization grant, binds the hop handle it carries to the authorization state it grants, mints its own access token, and knows the user only under its own pairwise subject. The subject map lives only in the control plane and never leaves it. The hop handle does travel, as a claim in the ID-JAG, so a workload carrying it is a control-plane participant for that hop, but it stops at the Resource Authorization Server that redeems the grant. The Resource Server behind it is issued an ordinary access token and never sees the handle.
Traced end to end, the two hops look like this, with the IdP consulted at each boundary:
```mermaid
sequenceDiagram
participant U as User
participant A as Travel app
participant I as IdP
participant E as Expense
participant L as Ledger
U->>A: sign in
A->>I: token exchange, user ID Token
I-->>A: ID-JAG for Expense, handle as a claim
A->>E: present ID-JAG, Expense RAS binds handle and issues an access token
Note over E: holds no user credential for the next hop
E->>I: token exchange, Identity Continuation Assertion
Note over I: verify Chain Authority and handle, resolve Ledger subject, decide again
I-->>E: new ID-JAG for Ledger, fresh handle as a claim
E->>L: present ID-JAG, Ledger RAS binds handle and issues an access token
```
**Hop one establishes the chain.** The travel app exchanges the user's ID Token at the IdP for an ID-JAG addressed to the expense Resource Authorization Server, in a request that looks exactly like a direct exchange. Where tenant policy allows the delegation to be continued, the IdP establishes a chain on its own, opening a root-chain envelope for it and embedding a fresh `identity_continuation_handle` as a claim in the ID-JAG it issues, for the root hop and for every hop after. There is no separate response parameter. The presence of the handle claim is the signal that the delegation is continuation-capable, and its absence that it is not. The travel app presents the ID-JAG, whose `sub` is the expense app's alone, to the expense Resource Authorization Server, which binds the handle to the authorization state it grants and mints an access token. The travel app calls the expense API with that token. Within the expense domain the handle lives on as chain context the Chain Authority sidecar holds, ready for the next hop, and never in the access token the API sees.
**Hop two hits the gap.** The expense service must post a charge to the ledger and holds nothing the ledger will accept. Its Chain Authority sidecar signs an Identity Continuation Assertion for that `identity_continuation_handle`, and the expense service presents it to the token-exchange endpoint with `audience` the ledger Resource Authorization Server, `scope=charges.write`, its workload credential as the actor token, and a proof of possession of its key.
**The IdP runs the gate against the control plane.** It verifies the signature against the registered Chain Authority key, using an allowed asymmetric algorithm and never `none`, and checks the `oauth-identity-continuation+jwt` type, confirms it is the audience and the issuer is a configured Chain Authority, and looks the `identity_continuation_handle` up in the root-chain envelope, which must be present and active, with the ledger a permitted target, the expense service permitted to continue the chain from this hop, `charges.write` inside the authorization basis, and the chain still within its maximum actor-chain depth. The proof-of-possession key must match `cnf`, the actor token must be bound to that same key and authenticate the current actor, and the caller must authenticate as an OAuth client that is that same actor. The IdP atomically consumes `(iss, jti)`. If it was already spent, the request is refused. Only then does the subject resolver map the canonical user to the ledger's pairwise subject, `led-c7e0`, and the IdP mints an ID-JAG with itself as issuer, the ledger as audience, that subject, the full actor chain it composes from the actors it authenticated at each hop since the assertion named only the current one, and a fresh `identity_continuation_handle` claim for the new hop. The expense service presents the ID-JAG to the ledger Resource Authorization Server as a JWT authorization grant (RFC 7523), which binds the new handle, applies its own policy, and issues an access token. The charge posts, for the user, under the ledger's name for the user, with the acting workload on the record.
# The Round-Trip Is the Point
Asking the IdP at every boundary looks like the cost of this design. It is the feature.
One continuation is the unit, and a chain is the same unit repeated. When the ledger needs a fourth service, its Chain Authority issues a fresh assertion referencing the handle for the ledger's hop, the IdP records a new hop under a fresh `identity_continuation_handle` with an immutable reference to its parent, re-resolves the next audience's subject, and decides again. The actor lineage the IdP composes grows by one, up to the maximum depth the envelope records. Handles are hop-specific by construction, so no two hops, and no two audiences, ever share one, and the map from any hop back to the root delegation lives only in the control plane. Each onward ID-JAG carries a handle for its own hop, but that handle stops at the authorization server that redeems it, so a Resource Server, issued only an ordinary access token, has nothing to link the user by across SaaS boundaries. Colluding audiences are the harder case. They share no handle to join on, but the actor lineage each onward ID-JAG carries, together with timing, remains a correlation channel the handle design does not close.
Because the IdP is asked at every hop, each continuation is a fresh policy point. A revoked delegation, an ended mission, a risk signal that fired since the chain began, all stop the chain at the next boundary instead of being honored from a stale token. The chain's continuation lifetime is bounded by the governing authorization behind it, a real grant or session rather than a token, so a session-anchored chain cannot outlive its session and only a grant-anchored one survives logout. Ending that authorization ends the chain, and the IdP can also cut a single branch, revoking one hop's subtree while the rest keeps going. Revocation is a single fact in one place: the control plane marks the chain inactive, and the next continuation resolves its handle against that state and finds it gone. What it stops is the next hop, not the last one, so an ID-JAG or access token already minted lives out its own short lifetime, and the honest promise is bounded: a chain that cannot be extended past the authority that justified it, closing over the short life of the grants in flight rather than instantly. That is still what scattered long-lived tokens never gave you, one record the IdP owns and re-checks every time identity crosses a boundary, instead of authority hunted across three SaaS providers with nothing to re-check against.
# What It Is, and What It Is Not
The boundaries keep its job clear. It is presented to the IdP to obtain an ID-JAG and authorizes nothing on its own. It is not a bearer token: possession is not enough, the presenter must prove the confirmed key. It does not let the holder compute or assert the target subject. The IdP does that. And it reuses ID-JAG unchanged as the onward grant rather than defining a competitor. It is a continuation extension of ID-JAG, not a replacement.
Its scope is narrow on purpose: same-IdP SaaS-to-SaaS, where several Resource Authorization Servers trust one enterprise IdP, and only for hops that re-subject. If the next audience accepts the same subject, no mint is needed and offline attenuation is the right tool. [Transaction Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-transaction-tokens/) are a neighbor at a different layer, propagating request context within a call chain rather than minting the onward audience-scoped grant this produces. The case this does not address is a single global subject, the SPIFFE-style workload trust domain, where every service already names the user the same way and there is nothing to re-subject.
That places it cleanly among the grants it works with, all under [OAuth Identity and Authorization Chaining](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/). ID-JAG covers the first hop, where an application holds the user's credential to exchange. The Identity Continuation Assertion covers the hops after that, where the application holds no credential but the chain is still live. Both produce the same onward artifact, an audience-scoped ID-JAG.
# Get Involved
The [Identity Continuation Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-continuation-assertion/) is published as an individual Internet-Draft at revision 00, with the [editor's copy](https://mcguinness.github.io/draft-mcguinness-oauth-id-continuation-assertion/draft-mcguinness-oauth-id-continuation-assertion.html) tracking between revisions. The review that matters comes from the people who would run it: identity providers that would mint the onward grants and resolve each audience's subject, SaaS platforms whose services sit in the middle of these chains, and agent runtimes that fan out across applications with no human at each hop. The questions I most want pressure on: is the Chain Authority the right trust boundary, is the per-hop round-trip acceptable at machine speed, and does the `identity_continuation_handle` confinement hold up against colluding audiences. If you operate one of these systems, tell me where it breaks.
---
# Trusting Issuers in Open-World OAuth
Canonical URL: https://notes.karlmcguinness.com/notes/trusting-issuers-in-open-world-oauth/
Markdown URL: https://notes.karlmcguinness.com/notes/trusting-issuers-in-open-world-oauth.md
A downstream service can verify a perfectly valid JWT and still create the wrong account, bind the wrong tenant, or accept an issuer that has no authority to speak for the user's domain.
That is the trust bug open-world OAuth exposes. When an agent provider signs an identity assertion for a user and presents it to a downstream service the user has never connected to, the downstream service has to decide whether to accept the assertion. Signature validation is necessary. It is not the whole decision.
OAuth and JOSE can validate the JWT mechanics. OpenID Connect and OpenID Federation are designed to authenticate an issuer. What they do not standardize is the open-world trust decision at first contact: when a Resource Authorization Server has never seen this issuer before, how does it decide whether the issuer is trusted for this subject namespace?
The honest version of the question is: I can verify this JWT, but should I trust this issuer for this subject? That is not one question, it is two. Is the issuer authentic? And does the namespace owner authorize this issuer to assert identities in its namespace? Different questions, answered by different parties, with different evidence.
This post introduces two new drafts for that missing control plane. The [Identity Assertion Trust Framework](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-assertion-framework/) defines the trust-evaluation model and the Trust Policy document the Resource AS publishes. The [Domain-Authorized Issuer Trust Method](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-domain-authorized-issuer/), or DAI, defines a DNS-based mechanism the domain owner publishes to declare which issuers may assert identities in its namespace. Both compose with existing OAuth and OpenID Federation work. Neither changes the grant surface. Each is independently adoptable.
# Steel thread: self-service agent sign-up
Take the runtime sign-up case the [auth.md post](/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md/) describes. Alice works at `acme.example`. She uses an agent run by `agentco.example` (the agent provider). She says to the agent: connect Notion. The agent has never registered with Notion before. Notion has never seen Alice or her agent provider before.
The Agent Verified path uses an ID-JAG to bootstrap the relationship. The agent provider's Authorization Server (`as.agentco.example`) mints an ID-JAG asserting Alice's identity, audienced at Notion's Resource Authorization Server (`as.notion.example`). The agent presents the ID-JAG via the RFC 7523 JWT-bearer grant. Notion's Resource AS runs validation, creates the account or binds the client, and issues a local access token for the API.
The validation step is the part everyone hand-waves. Notion's Resource AS has to decide: should I accept this assertion? Two questions resolve that decision.
1. Is `as.agentco.example` an authentic OAuth issuer, and is the JWT actually signed by it?
2. Does the namespace owner (the entity that controls `acme.example`) authorize `as.agentco.example` to assert identities for users at `acme.example`?
The first question is what federation is designed to handle, and OpenID Federation is the most developed implementation. It lets Notion verify that `as.agentco.example` is an authentic member of a known federation, signed into a trust chain by a trust anchor Notion recognizes.
The second question is not answered by federation. Federation membership proves ecosystem participation. It does not prove that a federation member is the right one for a given subject namespace. `as.agentco.example` is a perfectly legitimate AS for the agent provider's own users. The assertion in this flow is about a user at `acme.example`, a different namespace, where `as.agentco.example` may or may not be authorized to speak. Federation membership alone does not bind an issuer to a domain.
That distinction is the load-bearing claim of the entire design. Federation tells you the issuer is real. Namespace authority tells you the issuer is right for this user. Treating those as the same question is the bug. A federation member is still subject to authorization by the namespace owner for any namespace it asserts. Without that delegation, federation membership lets a legitimate AS impersonate users in domains it has no authority to speak for. The two drafts close that gap by separating the questions and assigning each one to the party that can answer it.
# Where the gap is today
[Open-World OAuth](/notes/oauth-for-open-world-ecosystems/) names this as the first-contact trust problem. OAuth's deployment model assumes that the AS, the RS, and the client knew each other before runtime. Self-service agent sign-up inverts that assumption. The agent provider reaches a previously-unknown Resource AS during the user's first attempt to connect. The trust topology has to extend across pairs of parties that have never paired.
The parties are easy to conflate. The validation result is only meaningful when each responsibility stays where it belongs:
| Party | Example | Responsibility |
| --- | --- | --- |
| Resource Authorization Server | `as.notion.example` | Publishes the Trust Policy, validates the assertion, and decides whether to issue a local token |
| Assertion Issuer | `as.agentco.example` | Signs the identity assertion presented by the agent |
| Subject Authority | `acme.example` | Controls the subject namespace and authorizes issuers for that namespace |
| Federation or trust anchor | A federation Notion trusts | Provides issuer-authentication evidence |
| Agent client | Alice's agent | Presents the assertion, but does not decide whether the issuer is trusted |
The existing tools each answer part of the question:
| Mechanism | What it proves | What it does not prove |
| --- | --- | --- |
| JWT validation | The assertion was signed by the key associated with `iss` | That the issuer is acceptable to this Resource AS |
| OAuth / OIDC metadata | Where the issuer publishes keys and endpoints | That the issuer may speak for `acme.example` |
| OpenID Federation | The issuer is an authentic member of a federation the Resource AS trusts | That the domain owner delegated namespace authority to this issuer |
| Static allowlist | The Resource AS operator pre-approved this issuer | Runtime self-service onboarding across unknown domains |
| Domain ownership alone | Who controls the namespace | Which issuers the namespace owner authorizes |
| DAI | The namespace owner authorized this issuer for this subject namespace | That the issuer is authentic or acceptable under the Resource AS policy |
| Trust Framework | How the Resource AS composes required evidence | The underlying trust evidence itself |
The [auth.md post](/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md/) called out the missing piece: a symmetric trusted-issuer discovery mechanism. The Trust Framework and DAI are that mechanism. The Resource AS needs a way to declare what it trusts, in a published document rather than a config-file allowlist. The namespace owner needs a way to declare which issuers it authorizes, in a runtime-discoverable artifact rather than an offline federation registration. The Trust Framework defines the first. The Domain-Authorized Issuer Trust Method defines the second.
# The Identity Assertion Trust Framework
The [Trust Framework draft](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-assertion-framework/) factors trust evaluation into two orthogonal categories.
- **`issuer_authentication`**. Is the JWT issuer an authentic ecosystem member? OpenID Federation answers this category by verifying a trust chain back to a trust anchor.
- **`subject_namespace_authorization`**. Has the Subject Authority for the subject's namespace actually authorized this issuer to assert identities in that namespace? The Subject Authority is the entity that controls the namespace, typically the owner of a registrable DNS domain for email-format subjects. Evidence comes from the Subject Authority, not from federation.
The combination rule is AND across categories, OR within. A Resource AS requires evidence for every category it declares. Within a category, one satisfied method is enough. Both categories must produce an Affirmative result. Indeterminate results (network failures, malformed responses) reject the assertion fail-closed.
For the two categories in the steel thread, that rule reads:
| Federation (`issuer_authentication`) | DAI (`subject_namespace_authorization`) | Result |
| --- | --- | --- |
| Affirmative | Affirmative | Accept, subject to Resource AS policy |
| Affirmative | Negative | Reject |
| Negative | Affirmative | Reject |
| Indeterminate on either | any state | Reject, fail-closed |
The Trust Framework defines two artifacts.
The first is the **Trust Policy document**, formally the Identity Assertion Issuer Trust Policy. A Resource AS publishes a JSON resource that names which grant profiles it supports, which subject identifier formats it accepts, and which Trust Methods it requires. A validator that holds an assertion fetches the Trust Policy, sees the required methods, gathers the evidence, and decides whether to accept.
The second is the **Trust Method**. A Trust Method names how a specific category gets satisfied. OpenID Federation membership is one Trust Method satisfying `issuer_authentication`. Domain-Authorized Issuer is one Trust Method satisfying `subject_namespace_authorization`. The framework defines the registry shape so additional methods can be added without rewriting the framework.
The Trust Framework is grant-agnostic. It applies to ID-JAG, the concrete binding the draft defines today. It applies to any other assertion-based grant where one AS issues an assertion that another AS validates, and it composes with OAuth Identity Chaining for cross-domain delivery.
# The Domain-Authorized Issuer Trust Method
The [DAI draft](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-domain-authorized-issuer/) defines a concrete Trust Method that satisfies the `subject_namespace_authorization` category for email-domain subjects. The publication pattern mirrors CAA, MTA-STS, and DKIM. The domain owner publishes a policy at a well-known location, and validators look it up. What DAI adds on top of that pattern is the OAuth-specific binding: the policy names issuer URIs and the subject identifier formats they may assert, so a Resource AS can map an `iss` claim and an email-format `sub` claim to a deterministic authorization decision.
There are three publication channels.
- **DNS-inline form.** A TXT record at `_oauth-issuer-policy.{domain}` carries the authorized issuers directly. The syntax is `v=oauth-issuer-policy1; authority=acme.example; issuer=https://as.agentco.example`.
- **DNS pointer form.** A TXT record carries a `uri=` directive pointing at an HTTPS-hosted JSON document. The JSON form supports richer policy, including temporal bounds, tenant binding for multi-tenant IdPs, and subject identifier format restrictions.
- **HTTPS well-known.** When DNS returns no record, a validator falls back to `https://{domain}/.well-known/oauth-issuer-policy`.
The JSON policy document looks like this.
```json
{
"subject_authority": "acme.example",
"authorized_issuers": [
{
"issuer": "https://as.agentco.example",
"subject_identifier_formats": ["email"],
"valid_from": "2026-01-01T00:00:00Z",
"valid_until": "2027-01-01T00:00:00Z"
}
],
"last_updated": "2026-05-01T00:00:00Z"
}
```
The lookup is deterministic and fail-closed, and it runs in a fixed precedence:
- **DNS-inline present.** Evaluate the inline policy carried in the TXT record.
- **DNS pointer present.** Fetch and evaluate the HTTPS JSON document it names.
- **DNS absent.** Fall back to the HTTPS well-known.
- **Timeout, malformed record, or DNSSEC failure.** Indeterminate, which rejects.
- **Confirmed absence across both DNS and the HTTPS well-known.** Negative, which also rejects.
So "both channels" means DNS and the HTTPS well-known must each come back empty before a lookup is Negative. Transitive authorization is bounded at depth one. The Subject Authority names issuers directly, and those issuers cannot themselves delegate further.
Determinism is not integrity, and the policy lives in DNS, so the draft is explicit about spoofing. The inline form has no signature of its own. Its authority binding is DNS control, the same basis CAA and DKIM rest on, hardened two ways. Behaviorally, a consumer discards any record whose `authority=` directive does not match the domain it queried, which closes off wildcard substitution. Cryptographically, Subject Authorities SHOULD sign `_oauth-issuer-policy.{domain}` with DNSSEC, a validating consumer treats a DNSSEC failure as Indeterminate rather than a clean Negative, and a consumer without DNSSEC SHOULD use a trustworthy resolver path. Many validators will not do DNSSEC validation, and for them DAI is only as strong as that resolver path and the DNS-control assumptions underneath it. Where stronger integrity is wanted, the pointer and HTTPS well-known forms put the authoritative policy behind the domain's own TLS. DNS stays authoritative when present, with the HTTPS well-known as the fallback on a Negative result. The posture is the one the design leans on by analogy: DNS control is the authority, and DNSSEC is the hardening.
DAI handles a deployment shape that matters in practice. Shared multi-tenant IdPs. A single issuer URL like `https://accounts.google.com` serves many tenants. The `tenant` field in an `authorized_issuers` entry binds authorization to a specific issuer-claimed tenant. Without that field, accepting an assertion from a shared issuer would silently authorize every tenant. With it, the domain owner can authorize a single tenant of a shared IdP without authorizing the rest.
An `acme.example` policy authorizing only its own tenant of a shared IdP names the tenant on the entry:
```json
{
"issuer": "https://accounts.example-idp.com",
"tenant": "acme",
"subject_identifier_formats": ["email"]
}
```
The same issuer under `tenant: otherco` would not satisfy the lookup for an `acme.example` subject.
# Walking the validation
Back to the steel thread. Alice clicks Connect Notion. The agent presents the ID-JAG via JWT-bearer grant at `as.notion.example`. The validation runs as follows.
1. The assertion names `iss = https://as.agentco.example` and `sub` containing an `email` claim ending in `@acme.example`.
2. `as.notion.example` reads its own Trust Policy. The policy requires `issuer_authentication` and `subject_namespace_authorization`.
3. For `issuer_authentication`, the validator runs an OpenID Federation lookup to confirm `as.agentco.example` is an authentic federation member with a valid trust chain.
4. For `subject_namespace_authorization`, the validator extracts the subject authority from the assertion. The registrable domain of the email is `acme.example`. It looks up `_oauth-issuer-policy.acme.example` via DAI. The DNS record parses cleanly, the `authority=` directive matches the queried domain, the temporal bounds cover the current time, and the policy lists `https://as.agentco.example` as an authorized issuer for the email format.
5. Both categories return Affirmative, so the Resource AS accepts the assertion as an authentic, authorized identity claim. What it does next is a separate policy decision. Here Notion runs the JWT-bearer grant, provisions or binds Alice's account, and issues a local access token. Whether it also registers the agent as an OAuth client is its own onboarding policy, not something issuer trust decides.
If `acme.example` had not published a DAI policy, the lookup would be Indeterminate or Negative, and the Resource AS would reject the assertion. If `as.agentco.example` had not been federated, federation membership would fail. The AND-across-categories rule turns either gap into a deny. Sign-up proceeds only when both questions resolve Affirmative.
Neither the ID-JAG nor the JWT-bearer grant changes. The Trust Framework is exercised at the validation step inside `as.notion.example`. The Trust Policy is published by `as.notion.example`. Issuer metadata and federation evidence come from or about `as.agentco.example`. The DAI policy is published by `acme.example`. The wire shape from the agent's perspective is the same one the existing ID-JAG and JWT-bearer flow defines.
# Failure modes the drafts have to handle
The denial cases are where the design earns its keep. The important failure modes are:
- **Issuer is authentic but not authorized for the namespace.** Federation succeeds, DAI fails, and the assertion is rejected.
- **The Subject Authority authorizes an issuer that cannot be authenticated.** DAI succeeds, issuer authentication fails, and the assertion is rejected.
- **DAI lookup is unavailable or malformed.** The result is Indeterminate, which rejects fail-closed.
- **A shared issuer is authorized without tenant binding.** The validator must avoid treating one tenant's authorization as authorization for every tenant behind the same issuer URL.
- **An authorized issuer tries to delegate onward.** DAI does not allow unbounded transitive delegation. The Subject Authority names authorized issuers directly.
- **The Resource AS policy and domain policy disagree.** The Resource AS policy wins for acceptance because it is the party issuing the local token. Domain authorization is necessary evidence, not a command to accept.
# Relating back to the series
[Open-World OAuth Part 1](/notes/oauth-for-open-world-ecosystems/) named five substrate gaps: discovery, resource binding, sender constraints, metadata integrity, and first-contact trust. PRM, CIMD, DPoP, and signed metadata address the first four. First-contact trust is what these two drafts address. They are the symmetric counterpart to PRM. PRM lets a client discover the AS that protects a resource. DAI lets the Resource AS validate that an issuer is the right one for a subject's domain. The Trust Framework defines how the Resource AS expresses what it requires.
[The Agent Provider Is the IdP](/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md/) argued that the auth.md design was right to adopt ID-JAG. The Agent Verified path it describes composes cleanly with both drafts. The agent provider is the IdP AS. The downstream service runs the validation. The namespace owner publishes the authority. Self-service agent sign-up becomes open-world-safe without requiring the agent provider and the downstream service to pre-pair.
# Incremental adoption
The two drafts are independent, though the value is asymmetric. A Trust Policy on its own improves how a Resource AS declares what it requires. DAI on its own publishes namespace authority, but it does nothing until validators consume it. Together they close the open-world gap, and a deployment does not have to start with both.
The adoption path that makes sense in practice looks like this.
1. **Publish a Trust Policy document.** A Resource AS that already runs a static allowlist can publish it in Trust Policy form. The change is a JSON document at a well-known location, no wire-level changes. The benefit is that validators and operators can read what is required rather than discovering it through trial.
2. **Adopt OpenID Federation for `issuer_authentication`.** If the Resource AS is already a federation member or can join one, the `issuer_authentication` category lights up with one Trust Method already defined. The Trust Policy now declares federation as the required method.
3. **Publish DAI for one domain.** The domain owner (which may be an IT admin at the enterprise, or the operator of an IdP service) publishes a DNS record naming the authorized issuer. The investment is one TXT record. The benefit is that any Resource AS adopting DAI as a Trust Method can validate assertions for that domain at runtime without manual configuration.
4. **Validators adopt DAI as a Trust Method.** Resource ASes that want to accept assertions from previously-unknown IdPs add the DAI Trust Method to their Trust Policy. The first time they receive an assertion from a domain they have not seen, they look up the DAI policy and decide. The static allowlist becomes a backup rather than the primary gate.
5. **Retire the static allowlist.** Once DAI is deployed across the domains the Resource AS cares about, the allowlist is redundant. The Trust Policy is the declaration. DAI is the discovery. Federation is the authenticity check.
A deployment can stop at any step. A deployment that only publishes a Trust Policy gets clearer policy declaration. A deployment that adopts federation gets standardized authenticity evaluation. A deployment that publishes DAI gets namespace-authority delegation without coordinating with every downstream Resource AS by hand.
# Value proposition
The drafts pay off before full deployment, because each piece is useful on its own.
- **For Resource ASes.** Declare what you require in a Trust Policy. Stop maintaining static allowlists. Accept assertions from previously-unknown issuers when the assertion satisfies your declared requirements. The fail-closed semantics keep the system safe under partial information.
- **For agent providers and other IdP ASes.** Operate in an open ecosystem. Have your assertions accepted by Resource ASes you have never paired with, as long as the namespace owner authorizes you and you participate in a recognized federation. Move re-keying, rotation, and migration into runtime concerns rather than coordinated-with-every-RP changes.
- **For domain owners.** Control which issuers can assert identities for users at your domain. Authorize the agent providers your users are allowed to use, and no others. Re-delegate without rewriting allowlists at every Resource AS. Multi-tenant SaaS IdPs can be authorized for one tenant without authorizing the rest, addressing a long-standing gap that shared multi-tenant JWKS endpoints make painful today.
- **For the agent ecosystem.** Self-service agent sign-up actually works across trust domains without pre-pairing. The Agent Verified path described in [The Agent Provider Is the IdP](/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md/) becomes open-world-safe. A user can connect a new tool through their agent, and the downstream service can validate the assertion without prior knowledge of the agent provider.
- **For the standards layer.** Federation and namespace authority are factored apart. Each side of the trust question lives where it belongs. Federation operators do not have to mediate every domain-authority delegation. Domain owners do not have to join every federation a Resource AS recognizes.
The deeper value is that open-world OAuth becomes a question of published policy rather than a question of pre-shared state. That is the move every successful internet protocol eventually makes. Mail did it with SPF, DKIM, and DMARC, the web with CAA, HSTS, and MTA-STS. Those mechanisms differ in what they secure, but they share one pattern, the domain owner publishing authority where anyone can find it. OAuth is making the same move for identity-assertion authority now.
# What the drafts do not solve
The drafts answer a specific question. Can a Resource AS accept an identity assertion from a previously-unknown issuer? They do not solve identity assurance, the agent's authority to act on the user's behalf, the agent's own client authentication, or downstream policy decisions about what the user is actually allowed to do at the resource. Those are governed by client authentication and instance attestation, runtime FGA, and the resource's own policy layer. Trust Framework and DAI sit at the bootstrapping layer. They make the first identity claim trustworthy. Everything above that layer still has to do its job.
# Where the drafts go next
Both are now individual Internet-Drafts submitted to the IETF, and the natural path is OAuth Working Group adoption. The Trust Framework is the broader of the two and has the larger surface to socialize. DAI is narrower and proves out the namespace-authority half of the framework with a concrete, deployable mechanism. The two can advance together, or DAI can move ahead of the framework as an independent Trust Method specification. Reviews, implementation experiments, and deployment feedback from agent providers, IdP operators, domain owners, and Resource ASes are the next thing the drafts need.
Open-world OAuth cannot be built on issuer authenticity alone. It needs namespace authority as a first-class validation input. These drafts make that authority publishable, discoverable, and fail-closed.
---
# Closing the Gaps in Least-Privilege MCP Tool Calls
Canonical URL: https://notes.karlmcguinness.com/notes/closing-the-gaps-least-privilege-mcp-tool-calls/
Markdown URL: https://notes.karlmcguinness.com/notes/closing-the-gaps-least-privilege-mcp-tool-calls.md
The [first part](/notes/least-privilege-mcp-tool-calls/) laid out two models for least-privilege Model Context Protocol (MCP) tool calls. In the token-side model, the authority lives in a token an Authorization Server (AS) mints. In the resource-side model, the server that runs the tool decides each call. Both authorize a single call well. Both, on plain OAuth, leave the same gaps.
Keep the board packet in mind. An agent preparing it reads Q3 financials from one system, drafts a document in a second, and notifies the audit committee through a third, three tool calls behind one sentence of human intent. This part walks the standards and proposals closing the nearer gaps, then pins down the one that remains.
Four gaps appear once the two models are on the table: the shape of the decision, the mapping from a tool call into that decision, the denied-but-requestable workflow, and the task context that spans calls. AuthZEN standardizes the first. The AuthZEN MCP profile (COAZ) standardizes the second. The Access Request and Approval Profile (ARAP) and the wire proposals standardize the third. The fourth is the one that remains, and it is where this part ends.
The stakes scale with reach. If you run one MCP server, AuthZEN and ARAP are enough to make its decisions and denials governable. If you run an agent across three servers, they are not, because no single server knows whether its local call is still part of the same approved task.
# AuthZEN Standardizes the Question, Not the Answer
Both models, at the moment of decision, are asking the same question. Given this subject, this action, this resource, and this context, is the call allowed? On plain OAuth, each deployment asks it in its own shape. The [AuthZEN Authorization API 1.0](https://openid.net/specs/authorization-api-1_0.html) makes that question standard. It defines an evaluation request with a subject, action, resource, and context, a decision response, and a `/.well-known/authzen-configuration` endpoint so a Policy Enforcement Point (PEP) can discover a Policy Decision Point (PDP). The policy engine behind it can be [Cedar](https://www.cedarpolicy.com), [OpenFGA](https://openfga.dev), [OPA](https://www.openpolicyagent.org), or another engine, without changing the wire contract.
That helps both models. In the resource-side model, the MCP server (or an API gateway in front of it) becomes a PEP and asks a PDP the concrete question:
```json
{
"subject": { "type": "user", "id": "alice@example.com" },
"action": { "name": "read" },
"resource": {
"type": "financials",
"id": "Example Corp:Q3 2026",
"properties": { "period": "Q3 2026", "entity": "Example Corp" }
},
"context": { "client_id": "board-packet-agent", "act": "agent:board-packet-agent/7f2a" }
}
```
The subject is the user, with the client and actor in context, so the decision turns on the user's authority and the agent's together. In the token-side model, the AS can call the same PDP at issuance time, which is one concrete way to delegate the resource-domain evaluation a generic AS would otherwise lack.
One question the evaluation request raises is who builds it. The tool call has arguments and a token. The PDP wants a subject, an action, a resource, and a context. The [AuthZEN profile for MCP tool authorization](https://openid.github.io/authzen/authzen-mcp-profile-1_0.html), COAZ, standardizes that mapping. A tool publishes an `x-coaz-mapping` in its `inputSchema` that says how its arguments and the caller's token become the subject, action, resource, and context the API expects. The mapping lives in metadata, so any enforcement point can build the same request against a compatible PDP instead of hand-coding the translation per tool, whether that point is an API or AI gateway, a service mesh, or the MCP server itself.
For the board packet's `query_financials` tool, the mapping is a few lines in the tool's schema:
```json
"x-coaz-mapping": {
"subject": [{ "type": "'user'", "id": "token.sub" }],
"action": [{ "name": "'read'" }],
"resource": [{ "type": "'financials'", "id": "params.arguments.entity + ':' + params.arguments.period" }],
"context": [{ "client_id": "token.client_id", "act": "token.act" }]
}
```
Static values are Common Expression Language string literals in single quotes, and dynamic ones read from `params`, the `tools/call` request, or `token`, the caller's access token. The tool's own arguments become the resource, the token's `sub` becomes the subject, the client and actor ride in context, and any enforcement point reconstructs the same request the PDP evaluated above without hand-coding the translation. COAZ decides nothing and grants no authority. It only makes the translation from tool invocation to policy input portable.
What AuthZEN does not do is close the structural gap between the two models. It standardizes how you ask the question. It does not change who owns the answer. The AS still needs the resource's ontology to ask a meaningful question about `financials`. The resource-side decision is still not portable to another party. And a denial is still just `decision: false`. AuthZEN tells you how to ask. It does not tell you what happens when the answer is no.
# ARAP: Turning a Denial into a Request
The missing piece is the workflow that begins where a plain decision ends. The draft [AuthZEN Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html), ARAP, defines a *requestable denial*. The answer is no right now, and the response carries a structured, bound description of how to ask. The agent can act on it instead of giving up.
It helps to separate two things. A denial signal carries the no and how to ask, which [part one's OAuth challenge family](/notes/least-privilege-mcp-tool-calls/#what-the-client-has-to-run) already does on the token side. The approval workflow is what happens next, the request, the decision, and the rebinding to a fresh evaluation. ARAP standardizes that workflow, which is why it gets the longer treatment here. The denial signals are how the no is carried at each layer.
A requestable denial returns `decision: false` with an `access_request` the caller can submit:
```json
{
"decision": false,
"context": {
"evaluation_id": "eval_01K3FN2P8Q",
"reason": "approval_required",
"access_request": {
"endpoint": "https://as.finance.example.com/access/v1/requests",
"template": "financials_read_approval",
"expires_at": "2026-06-27T20:25:00Z",
"binding_token": "eyJhbGciOiJFUzI1NiJ9..."
}
}
}
```
The PEP submits that to the advertised `endpoint`, presenting the denial binding so the request cannot be replayed against a different call, and gets back a task with a status endpoint to poll until a decision:
```json
{
"subject": { "type": "user", "id": "alice@example.com" },
"resource": { "type": "financials", "id": "Example Corp:Q3 2026" },
"action": { "name": "read" },
"denial": {
"evaluation_id": "eval_01K3FN2P8Q",
"binding_token": "eyJhbGciOiJFUzI1NiJ9...",
"expires_at": "2026-06-27T20:25:00Z"
}
}
```
The binding is the load-bearing part. An approval is an input to a fresh decision, not a standing grant. Bind it to the exact subject, action, resource, and request, bind it in time, and reevaluate at execution. The denial binding ensures the approval that comes back covers the call that was denied and no other. After approval, the decision point reevaluates with freshness-bounded policy state before anything executes, which closes the window between approval and use.
ARAP, a draft profile, is the answer to "what happens when the answer is no." On its own, though, it does not say *where* the workflow runs. That turns out to be the move that heals the split between the two models.
## Fulfillment Is the Missing Half
One thing ARAP deliberately does not define is fulfillment. The profile standardizes the request, the status tracking, and the re-evaluation, and it says outright that it defines no workflow engine, approval policy language, ticketing system, or entitlement catalog. For a single denied call, that is enough. The approval rides into the fresh evaluation as context and the PDP honors it once. For durable authority, something still has to turn the approval into authorization state, and which something depends on [where the state lives](/notes/least-privilege-mcp-tool-calls/#where-the-state-lives). Token-resident state fulfills by minting, which is exactly the completion AROP binds to OAuth issuance. Store-resident state fulfills by a write, a relationship tuple or an entitlement, and no standard defines that write. Every Zanzibar-style store's write API is product-specific. The approval workflow is standardized. The state change it produces is not.
# ARAP at the Resource, at the Authorization Server, or Both
The requestable-denial workflow is the same shape no matter who hosts it: deny with a bound request, carry the request to whoever decides, resume on approval. What changes is where the decision and the issuance live. That makes ARAP a single governance vocabulary that both models can speak.
## ARAP at the Resource
The MCP server is the PEP. It evaluates the call, and on a requestable denial it brokers the access request itself and parks the work as a server-held task. The agent holds only a task handle. No new client-held token is minted. After approval, the server reevaluates before execution. For the client developer this is the quiet win. The agent keeps presenting the one token it already holds, and the approval happens behind the resource. There is no second token to acquire, cache, or match to this call.
```mermaid
sequenceDiagram
participant Agent
participant MCP as MCP Server (PEP)
participant PDP as PDP / Access Request
participant Approver
Agent->>MCP: tools/call query_financials
MCP->>PDP: AuthZEN Evaluation
alt In-bounds
PDP-->>MCP: permit
MCP-->>Agent: tool result
else Approval needed
PDP-->>MCP: requestable denial
MCP->>PDP: AuthZEN Access Request
PDP-->>MCP: access_request_id
MCP-->>Agent: CreateTaskResult, status = working
Note over PDP,Approver: Approval out of band
Approver->>PDP: approve
Agent->>MCP: tasks/get
MCP->>PDP: reevaluate
PDP-->>MCP: permit
MCP-->>Agent: completed + tool result
end
```
This keeps domain knowledge and the decision at the resource, where the resource-side model wanted them, and it gives the missing denial-to-approval workflow a home on the tool boundary through held work. At-most-once dispatch and exactly-once business effects still require careful server design and application-level idempotency. A task handle alone cannot provide those guarantees.
## ARAP at the Authorization Server
The AS is the decision point. On a requestable denial, it does not just fail the token request. It returns a deferral the client can resume, and it issues the narrow token once the decision completes. The work that used to have no native OAuth home, getting an out-of-band human to approve a token, now has a defined completion.
```mermaid
sequenceDiagram
participant Agent
participant AS as Authorization Server
participant PDP
participant MCP as MCP Server (RS)
Note over Agent,AS: Discovery already done (see the token-side model)
Agent->>AS: PAR + action-level RAR
AS->>PDP: AuthZEN Evaluation
alt In-bounds
PDP-->>AS: permit
Note over Agent,AS: Complete authorization-code flow
AS-->>Agent: action-narrow access token
else Approval needed
PDP-->>AS: requestable denial
AS-->>Agent: deferral + polling
Note over AS: ARAP access request out of band
Agent->>AS: poll
AS-->>Agent: action-narrow access token after approval
end
Agent->>MCP: tools/call query_financials + token
MCP-->>Agent: result
```
The completion needs a transport. Client-Initiated Backchannel Authentication ([CIBA](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html)) does decoupled approval well. A human approves out-of-band on their own device, and tokens come back by poll, ping, or push. But CIBA begins with a client asking to authenticate a user, not with a resource refusing a specific call, so it has nowhere to carry the requestable-denial signal or bind an approval to the call that triggered it. It is the right tool for one step, getting a human to say yes, and the wrong thing to build the workflow on.
[OAuth Deferred Token Response](https://datatracker.ietf.org/doc/draft-gerber-oauth-deferred-token-response/) fits better. The AS returns an `authorization_pending` response carrying a `deferral_code`. The client resumes by polling the token endpoint under the `urn:ietf:params:oauth:grant-type:deferred` grant type, or through a registered notification callback, and the AS reevaluates before issuing. No browser and no front-channel round trip is what makes repeated asynchronous approval practical call after call.
[RFC 8628 Device Grant](https://www.rfc-editor.org/rfc/rfc8628) and [RFC 9470 Step-up](https://www.rfc-editor.org/rfc/rfc9470) cover adjacent steps. Each solves one hop. ARAP originates and carries the requestable-denial signal that ties the hops together.
## Or Both
The two placements compose. An agent can hit a resource-side denial, and the resource can escalate it to the authorization server when minting a portable token is the right answer, or the AS can defer to resource-side evaluation when the meaning lives at the resource. Because the denial, the request, and the binding are the same artifacts in both directions, the escalation does not change shape as it crosses the boundary.
This is what heals the split the two models started from. Before ARAP, choosing the resource-side model meant inventing a denial-to-approval workflow, and choosing the token-side model meant inventing one too, and the two did not interoperate. With ARAP, you choose where the decision lives on its merits. Ontology and freshness pull the decision toward the resource. Portability pulls authority toward the AS. You reuse one approval workflow either way. The decision placement and the governance workflow stop being the same choice.
# Paths Forward
ARAP defines the requestable-denial workflow at the policy layer. These proposals converge on one shape, deny with a bound request, route the approval, then re-evaluate before execution. Several carry that flow on the wire, at the authorization server, at the tool boundary, and in between. Some build on ARAP, some arrive at the same shape independently. All are proposals, not settled specifications.
- **At the authorization server: the proposed [AuthZEN Access Request OAuth Profile](https://github.com/openid/authzen/pull/531).** AROP specifies the AS-hosted placement directly. It pairs ARAP with a completion mechanism such as Deferred Token Response or Transaction Authorization Challenge, uses CIBA only for the out-of-band human-approval step, and reuses Rich Authorization Requests (RAR) to carry the authorization details. It adds no grant types or parameters of its own.
- **At the tool boundary, two MCP Specification Enhancement Proposals (SEPs) approach the denied-but-requestable case from different ends, and they compose.** One says here is why, and here is how to ask. The other says hold this task while I handle the asking.
- [MCP SEP-2643](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2643), Structured Authorization Denials, standardizes the denial itself, described here as of its July 2026 revision. It is a JSON-RPC envelope carrying a failure classification, a retry correlation handle, and structured remediation hints, today a URL-based approval through elicitation or a credential issuance through OAuth scopes and RAR. It is MCP's own requestable denial, transport-agnostic and backward compatible, and its hints can route to either model, the resource's own approval surface or an authorization server for a narrow credential.
- [MCP SEP-2848](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2848), Asynchronous Approval for Tool Calls, standardizes the workflow on the far side of a denial, also as of July 2026. The server brokers the access request and parks the work as an MCP task the agent polls, reevaluating before execution after approval, and it references ARAP as its backend. The parked task carries lifecycle continuity, the workflow survives disconnection, and not the identity that would make the approval verifiably part of the same undertaking as the denied call. A structured denial from SEP-2643 can hand off to a brokered task here.
- **The hybrid: the individual [OAuth Transaction Authorization Challenge](https://datatracker.ietf.org/doc/draft-rosomakho-oauth-txn-challenge/) draft.** Part one introduced this challenge as the finest grain of the OAuth family. As a workflow, the resource detects that the agent's token does not authorize the specific transaction in front of it and issues a transaction challenge naming the resource, the action, and the transaction-specific context. The client takes the challenge to the AS, which evaluates it, requests consent or step-up if needed, and issues a transaction-specific token bound to that exact transaction. The resource is back in the trigger path, like the resource-side model. The AS still mints the token, like the token-side model. It is more disciplined than the bare `WWW-Authenticate` plus RAR cycle, because the resource hands the AS a precise transaction description instead of relying on the client to construct a correct RAR from discovered metadata.
Notice the pattern across all of this. The same structured "denied, here is how to ask" is showing up at three layers, largely independently. On the token side, `insufficient_authorization_details` carries missing standing authority as RAR in a `WWW-Authenticate` challenge, and a transaction challenge carries the single-transaction case. At the MCP layer, SEP-2643 carries it in a JSON-RPC denial envelope. At the policy layer, ARAP carries it as a requestable denial. Different specs, different audiences, one shape. A resource that adopts more than one can answer in whichever form the caller in front of it understands.
# The Specs That Close the Gaps
Side by side, here is what each piece adds, which model it serves, and what it costs. The status column is a snapshot as of mid-2026 and will age.
| Approach | Layer | What it adds | Serves | Status | Main tradeoff |
| --- | --- | --- | --- | --- | --- |
| RAR with `insufficient_authorization_details` | OAuth resource server | Action-grained authority and a structured signal that more is needed | Token-side | Individual draft | Machine-actionable, but inherits the AS ontology problem and token churn |
| AuthZEN Authorization API | PEP to PDP | A standard evaluation request, response, and PDP discovery | Both | Final 1.0 | A portable decision contract, but it does not change who owns the answer |
| AuthZEN COAZ profile | MCP tool schema to AuthZEN | A standard mapping from tool arguments and tokens to subject-action-resource-context | Both | WG draft | Makes request construction portable, but it is the mapping, not the decision or the workflow |
| ARAP | Decision point | The requestable-denial to approval workflow | Both | Draft profile | One workflow for either placement, but the flow only, not the wire |
| AROP (openid/authzen#531) | Authorization server | ARAP completed through OAuth token issuance | Token-side | Proposed | Reuses OAuth with no new grant or parameter, but AS-centric |
| MCP SEP-2643 | MCP JSON-RPC | A structured denial envelope with remediation hints | Either | Draft SEP | MCP-native and backward compatible, but the denial, not the approval |
| MCP SEP-2848 | MCP tool, via held work | Server-brokered asynchronous approval | Resource-side | Experimental draft | Decision and workflow stay at the resource, but the server owns idempotency |
| Transaction Authorization Challenge | Resource to authorization server | An RS-described transaction and an AS-minted per-transaction token | Hybrid | Individual draft | A precise transaction without client-built RAR, but it adds an AS round trip |
| Durable task object | Above all of them | A durable, approved object the whole task binds to | Both | No standard yet | The piece none of the above provides |
Every row but the last standardizes an interface inside a single call. The last one is what the rest of this part is about.
# The Gap Neither Model Closes
Several objects in this stack look like they might be the task, and none is. It is worth saying what each one is, and is not:
| Object | What it represents | What it does not represent |
| --- | --- | --- |
| Access token | Authority a client can present | The whole task |
| PDP decision | Whether one call is allowed | A durable task lifecycle |
| Access request | A request to approve a denied action | Standing authority by itself |
| Held work | Pending work at a tool boundary | User-approved purpose |
| Mission | The governed task and its purpose | The local resource policy |
The first four are standardized in the stack today. The last is not. Both models can work well for one tool call to one MCP server. The trouble is never a single call. Per-call least-privilege can be locally correct and globally wrong. Every call is in bounds, and the task as a whole still ends up outside what the user approved, with no layer positioned to notice. The board-packet agent makes at least three calls across three MCP servers and three Authorization Servers, and may iterate within each. It may not even act alone. A planner can spawn sub-agents, so the fan-out runs across actors as well as calls, and the actor chain names who is acting, not the task they act under. What does that look like at the protocol layer?
In the token-side model, each new MCP server starts its own discovery cycle, and each AS sees only its own calls and operation vocabulary. Unless the deployment adds a shared task identifier and governance service, consent and audit remain local to each authorization domain. The client may accumulate several narrow, short-lived tokens, while each AS resolves the ontology problem independently.
In the resource-side model, each MCP server's PDP evaluates calls with only its local context. Unless the deployment supplies a shared task identifier and lifecycle, requestable denials create separate tasks and audit records. Token churn disappears. Cross-server approval and audit fragmentation do not.
```mermaid
flowchart TB
Agent([Agent])
User([User / Approver])
subgraph TS["Token-side"]
AS1[AS-1]
AS2[AS-2]
AS3[AS-3]
end
subgraph RS["Resource-side"]
PDP1[MCP-1 PDP]
PDP2[MCP-2 PDP]
PDP3[MCP-3 PDP]
end
Agent -->|RAR tool 1| AS1
Agent -->|RAR tool 2| AS2
Agent -->|RAR tool 3| AS3
Agent -->|call tool 4| PDP1
Agent -->|call tool 5| PDP2
Agent -->|call tool 6| PDP3
AS3 -.->|isolated approval prompts| User
PDP3 -.->|isolated approval prompts| User
```
The root cause is structural. Both models build per-call authorization on a protocol stack with no first-class task object. AuthZEN standardized the decision, ARAP the denial-to-approval workflow, AROP and SEP-2848 the wire. None of them introduces an object for the work itself, so deployments fall back on proprietary transaction identifiers that correlate records without governing them. And none of them standardizes fulfillment, so whatever authorization state an approval eventually becomes, a minted token or a store write, carries no task identity either.
The cost is not only operational: the repeated prompts, the orphaned tokens, the audit joined by timestamp and luck. It is a security boundary problem. The finance approval may allow Q3 financials for the board packet, but the document server cannot tell whether the draft it is creating is still inside that approved purpose, and the workflow server cannot tell whether the notice it is about to send carries only what the board-packet task approved or material it never covered. Each server can authorize its local call correctly and still fail to preserve the user's original intent across the task.
It is fair to ask whether this is a real problem or a tidy-minded one. Per-call least-privilege plus good logging catches a great deal. But catching is after the fact. The value of naming the task is that a server could refuse the drifting call in the first place, because it could weigh the call against the work the user approved instead of in isolation. Audit tells you the boundary was crossed. A task object is what lets a server hold it.
# Why Correlation Is Not Governance
The obvious workarounds correlate, but they do not govern. They are observability, not authority. A shared transaction or trace identifier threads the calls together for debugging, but it carries no approval, no binding, and no lifecycle, so nothing can revoke or widen the task through it. A session keeps the work running across calls, but that is runtime continuity, not an approval record. The distinction deserves names, and I owe them to Mats Sundvall in the SEP-2643 discussion: *lifecycle continuity*, the workflow surviving across time and connections, which sessions and SEP-2848's held task supply well, and *verifiable continuity*, showing that the call, the denial, the approval, the re-evaluation, and the execution belong to one undertaking. Every workaround in this list supplies the first. Nothing in the stack supplies the second. A task hint passed in the RAR or the AuthZEN context is client-asserted text the resource has no reason to trust. The OAuth grant groups what one authorization server issued, not what one user approved across three of them. Each names the task in some weak sense. None lets a server decide whether a call is still inside the approved work. Better client orchestration, smarter SDKs, and richer log shipping help the same way and close the structural gap the same amount, which is not at all. The protocol still does not name the task, so everything above it has to infer the task from indirect signals every time.
Whatever fills that gap, the requirements are the same on either model. Any candidate has to answer five questions that per-call authorization cannot:
- **Identity.** Does the task have a stable identifier that every participating server and authorization domain can reference, without itself becoming a cross-context correlator?
- **Approval.** Did the user approve the task once, against a description of what it will do, instead of re-approving each fragment?
- **Binding.** Can every token and every PDP decision be tied back to that task, so a credential or a permit names the work it serves?
- **Expansion.** When the agent needs authority the task did not anticipate, is there a governed way to widen it that preserves lineage, instead of spawning an unrelated approval?
- **Audit.** Can a reviewer reconstruct everything done under the task by its identifier, deterministically, instead of stitching logs by timestamp and client?
Picture the board packet with that object in place. At approval, the task gets an identifier. Every token the agent mints and every decision a PDP returns carries it. A server evaluating `create_doc` can check the call against the approved task, not just against the agent's scope, and refuse a draft that has drifted outside it. Revoking the task stops the draft and the notify even though each call, taken alone, was permitted. The audit reads as one story under one identifier instead of three logs joined by timestamp. None of that requires a new grant type, and the task object is not the token. Tokens and decisions reference it, a name for the work they serve.
Those are the questions, not the answer. No standard object in the OAuth, AuthZEN, or MCP stack answers them today. MCP does define a task, but that is held work, a runtime handle for one pending call, not a governed record of the user's approved purpose across many calls. One proposed model fits the requirements. I call it a Mission: a durable, governed record of the approved task that every token and decision can point back to. What makes it trustworthy, where a task hint is not, is provenance. A hint is asserted by the client. A Mission is issued and governed by an authority, so a resource can rely on it the way it already relies on a token. These two parts do not define that object. Open-world OAuth needs a shaping step that turns a request into one ([Open-World OAuth Still Needs Mission Shaping](/notes/open-world-oauth-still-needs-mission-shaping/)), and a session is not that object ([Sessions Are Not Missions](/notes/sessions-are-not-missions/)). Their job was to establish why per-call authorization, on either model, needs one.
# Where to Start
The smallest useful pieces are independent. Each one pays for itself before the rest arrives. Most of this is not MCP-specific. If you run a plain OAuth resource server, read "MCP server" below as "your API," adopt AuthZEN and ARAP directly, and treat COAZ and the MCP SEPs as the MCP-specific bindings of patterns you would build your own way.
- **If you build MCP servers**, put an authorization check at tool execution and use the same policy system to filter `tools/list`. Treat filtering as exposure control. Always authorize `tools/call`. Use the [AuthZEN Authorization API](https://openid.net/specs/authorization-api-1_0.html) for the PEP-to-PDP contract so the decision does not depend on one policy engine, and publish a [COAZ](https://openid.github.io/authzen/authzen-mcp-profile-1_0.html) `x-coaz-mapping` in each tool's schema so any gateway or PDP can build the request from the tool definition. Two draft SEPs target the denied-but-requestable case from different ends. [SEP-2643](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2643) standardizes a structured denial envelope, and [SEP-2848](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2848) standardizes server-side asynchronous approval using held work.
- **If you run an Authorization Server** for agent clients, publish [RAR-type metadata](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/) so clients can discover supported `authorization_details` schemas, and add resource-domain validation rather than assuming schema discovery gives the AS semantic knowledge. For denied-but-requestable decisions, adopt the [Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html) and watch the proposed [AuthZEN Access Request OAuth Profile](https://github.com/openid/authzen/pull/531), which composes it with an explicit OAuth completion mechanism.
- **If you build an agent or orchestrator**, you are the one who feels token selection, retry loops, task handles, and approval UX. Lean on resource-side decisioning and held work where you can, so the agent presents one credential and polls for completion instead of running the acquisition loop. Where authority has to travel, expect to cache tokens by the authority they carry, handle the challenge family, and resume out-of-band approvals without losing the thread of the task.
These pieces pay for themselves independently. But notice what none of them fixes: once a task spans more than one call, server, or authorization domain, you are still missing the object that names the task. Carry authority when it must travel, evaluate locally when context must stay local, standardize the decision and the denial workflow, and treat the absent task object as the next thing to build, not a detail to paper over with log correlation. Per-call least-privilege is necessary, but it governs calls, not the task they serve. Until something names the task and makes its continuity verifiable, the user's intent is the one thing no layer is positioned to protect.
---
# Two Models for Least-Privilege MCP Tool Calls
Canonical URL: https://notes.karlmcguinness.com/notes/least-privilege-mcp-tool-calls/
Markdown URL: https://notes.karlmcguinness.com/notes/least-privilege-mcp-tool-calls.md
An agent is preparing a board packet. It reads Q3 financials from one system, drafts a document in a second, and notifies the audit committee through a third: three tool calls, three Model Context Protocol (MCP) servers, three authorization domains, behind one sentence of human intent.
Locking each of those calls down to least-privilege is the part we know how to do. There are two natural models for it. The basic forms are buildable today on existing OAuth machinery. The interoperable pieces around discovery, requestable denials, asynchronous approval, and task binding are still being standardized. In the first model, the authority lives in the token. An authorization server mints a credential narrowed to the action, and the agent carries it to the call. In the second, the decision lives at the resource. The server that runs the tool evaluates each call as it happens.
There is a third party that is easy to overlook, the tool itself, and MCP makes it first-class. MCP gives the tool boundary its own protocol surface: a catalog to discover tools and a call to invoke them. Task-oriented extensions and proposals then give long-running or approval-gated work a place to wait. Whichever model decides, the tool boundary is where the decision is enforced, and increasingly where the hard parts get solved. None of this is unique to MCP. The two models are how fine-grained authorization works for any OAuth resource server, and MCP simply makes the tool boundary an explicit protocol surface, the sharpest place to watch them play out.
That there are two models is the good news. It is also the trap. Both authorize *calls*. Neither names the *task* those calls serve, and nothing else in the protocol stack does either. The failure mode is not an over-authorized call. It is that several correctly authorized calls can compose into work the user never approved. Least-privilege per call can be locally correct and globally wrong, every check passing while the task drifts somewhere the user never sanctioned. The same gap shows up downstream as friction. The user sees a prompt per fragment, and the logs join by timestamp and luck.
Make it concrete, with every grant already at the right grain. The agent reads Q3 financials, drafts the packet, and notifies the audit committee, exactly the audience the user approved. But while drafting, it folds in an unreleased acquisition memo it happened to have access to. The finance server sees a valid read, the document server a valid write, the workflow server a valid notify to the right group. Every check passes, and no single server sees the whole chain. This is not a content-scanning problem. Even with locally valid policy at every hop, no server owns the task boundary, so none can tell whether the notice still belongs to the approved board-packet task. Narrowing the grants does not help, because each call is already within its grant. What is missing is anything that ties the calls back to the work the user actually approved.
That is the gap. Least-privilege per call is the floor, not the ceiling. This part walks both models on plain OAuth, makes the tool boundary first-class, shows where each one strains, and pins down how to choose. The [second part](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) covers the standards that close the nearer gaps, AuthZEN, the Access Request and Approval Profile (ARAP), and the proposals around them, and the one gap none of them closes.
Whichever model decides, the MCP-native path lands in the same shape:
| Step | Role |
| --- | --- |
| `tools/list` | Exposure control |
| `tools/call` | Authorization choke point |
| Denial | Structured remediation |
| Approval | Held work or a new token |
| Resume | Re-evaluate, then execute |
The two models below are how any OAuth resource server reaches that boundary. The rest of this part works through them, then makes the tool boundary first-class.
# One Sentence, Three Tool Calls
The board packet takes a sequence of tool calls across three MCP servers, each protecting its resources through its own Authorization Server (AS):
1. `query_financials(period="Q3 2026", entity="Example Corp")` on a finance MCP server, with its own AS.
2. `create_doc(template="board-packet", title="Q3 Review")` on a document MCP server, with a different AS.
3. `notify_reviewer(group="audit-committee", deadline="...")` on a workflow MCP server, with a third AS.
The user approved the broader task before the agent started: "prepare the Q3 board packet for the audit committee." In today's systems, that approval is usually application state, not protocol authority. It does not automatically authorize the individual tool calls. The agent may begin with an existing credential or session accepted by a server, but it does not yet hold action-specific authority for all three systems.
Each tool call has to land at the right grain. Not "agent can act on these resource servers," but "agent is approved to read this specific financial period for this specific entity, draft this specific document, notify this specific reviewer group." The agent does not know up front which actions each server permits, which Rich Authorization Request (RAR) types each Authorization Server accepts, or which fields are required. Discovery is part of the protocol.
Before walking the two models, it helps to be precise about what we are actually authorizing.
# Three Problems Inside One Tool Call
Everything that follows turns on one distinction. The model is untrusted reasoning that proposes actions. Each tool invocation is a request to exercise specific authority against a specific resource. The protocol layer's job is to govern the invocation, not the reasoning. That job is three different authorization problems, often conflated:
- **Tool discovery.** Which tools does the agent see in the first place? An MCP server controls its `tools/list` result, so a deployment can filter the catalog by policy. That shapes exposure, not execution, and the post returns to the difference at the tool boundary. MCP does not currently standardize catalog filtering tied to a durable task object.
- **Tool invocation.** When the agent calls a tool, does the call sit within the authority the agent already holds? The token-side and resource-side models both sit here. Most of this part is about this problem.
- **Tool approval.** When the call sits outside current authority but is still potentially permissible, what runtime governance workflow turns the denial into a decision? Approval (a human reviewer authorizes the call), consent (the user grants additional consent in band or out of band), and access request (a general governed escalation that may compose risk evaluation, approval routing, and consent collection) are three different workflow shapes.
Both models follow the same authorization pipeline, even though they implement it at different boundaries:
1. **Discover need.** The task requires a specific action against a specific resource. The agent learns this from the task plan, the tool catalog, or a runtime failure that points at the missing capability.
2. **Request authority.** The agent expresses the need on the wire (an RAR to the AS in the token-side model, a tool invocation against the MCP server in the resource-side model).
3. **Evaluate.** Policy evaluates the request against user-approved authority, client registration, deployment rules, and runtime risk posture.
4. **Authorize or escalate.** The evaluation either produces a narrow token, returns a permit decision, or routes through a runtime governance workflow.
5. **Execute.** The MCP server validates the token or enforces the permit and then runs the tool.
The important questions are where each step lives, what step 4 produces, which governance workflows it can invoke, and what protocol object identifies the *task* across many invocations. The two models answer the first questions. Neither answers the last, which is the subject of [the second part](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/).
# Three Identities the Decision Needs
Before choosing where the decision lives, a real deployment has to be explicit about the identities the decision will use.
- **User identity.** The human (or service principal) who approved the task and consented to it.
- **Client identity and instance attestation.** The agent runs as an OAuth client with a `client_id`. Deployments can add the [Client Instance Assertion](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) profile to identify the specific instance making the call, allowing instance-level policy and revocation.
- **Actor chain.** When the agent delegates to a sub-agent or crosses workload hops, [RFC 8693 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693) defines the `act` claim used to represent the current actor and nested prior actors. The [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) adds structure for agent delegation chains, including instance identity and per-hop authority constraints.
Both models need all three. Without them, the decision collapses the human, the client, and the acting workload into one blurry principal, and the audit record cannot explain who authorized what for whom.
The choice between the two models comes down to three moves:
```text
Carry authority when it must travel.
Decide at the resource when meaning and freshness live there.
Layer both for most serious deployments.
```
# The Token-Side Model: Authority in the Token
In the token-side model, the client carries the authority. Each tool call is authorized by a token narrowed to the relevant action. Steps 2 through 4 of the pipeline happen at the Authorization Server. The client then presents the resulting token when it asks the MCP server to execute.
## Discovery and Request
The agent's first unauthorized call starts a discovery cycle. The MCP server returns an OAuth `WWW-Authenticate` challenge carrying the Protected Resource Metadata (PRM) URL and, where possible, scope guidance. This is the layering [MCP's authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) adopts. The client fetches PRM, discovers the AS, and follows AS metadata. Action-level `authorization_details` are an extension: a deployment adopting this model also publishes RAR-type metadata so the client can discover the relevant schemas.
The 401 and the metadata it points at look like this:
```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://finance.example.com/.well-known/oauth-protected-resource"
```
```json
{
"resource": "https://finance.example.com/mcp",
"authorization_servers": ["https://as.finance.example.com"],
"scopes_supported": ["financials.read"],
"bearer_methods_supported": ["header"]
}
```
The first request for a given task is often approved through a normal OAuth front-channel consent flow. The client submits the request via Pushed Authorization Requests (PAR), the AS renders it to the user in a browser, the user approves, and the AS issues a code the client exchanges for a token. The front-channel case applies especially to the initial request and to high-stakes step-up. But an agent rarely makes a single call. It fans out across tools and iterates, and bouncing the user to a browser for every new authority it turns out to need is not practical. The case that matters is asynchronous approval, where the decision happens out of band with no human at a browser. The proposed token-side answer to it, Deferred Token Response, is covered in [part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/).
In an RAR flow, the client constructs an `authorization_details` entry that names the specific action and submits it through PAR. The AS evaluates the request against user-approved authority, client registration, deployment rules, and runtime risk posture. On approval, the AS completes the authorization flow and mints a narrow access token.
The `authorization_details` entry names the one action in the resource's own vocabulary, not a coarse scope. The `type` and its fields are resource-defined. The client learns them from RAR-type metadata:
```json
{
"type": "financials_query",
"locations": ["https://finance.example.com/mcp"],
"actions": ["read"],
"period": "Q3 2026",
"entity": "Example Corp"
}
```
The baseline shape is a token the MCP server validates against `authorization_details`, without requiring a policy call at execution time. The client carries the proof that the AS granted the authority. A deployment can still add introspection, event-driven invalidation, or per-call evaluation. The acquisition cycle repeats whenever the agent needs authority outside its current grant.
## What the Client Has to Run
Pushing authority into the token makes the client the orchestrator. To invoke a single tool, it may have to run a loop:
1. **Decide what to request.** Listing the catalog may need a coarse scope. Invoking a specific tool needs authority at the action's grain. The 401 names a Protected Resource Metadata URL and sometimes a `scope`. A scope is easy to ask for. An action-level `authorization_details` entry is not. The client either reads RAR-type metadata and builds the entry for this exact action, or it guesses.
2. **Acquire a token for it.** With the user at a browser, the first grant goes through the normal redirect, and so does a step-up that needs fresh consent or a stronger sign-in. Mid-task, with no one at the browser, new authority has to be approved out of band instead. That asynchronous path is the harder one, and [part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) takes it up.
3. **Call the tool, and find out whether the token was enough.** Often it is not. A valid token can still fall short on what it grants or on how the user authenticated.
4. **Read the challenge and correct.** Broaden the scope, build the right `authorization_details`, step up authentication, or carry a transaction challenge to the AS for a transaction-bound token, then retry.
5. **Keep the result.** Cache the token keyed by the authority it carries, so the next call can reuse it instead of starting over.
There are two ways to learn the requirement in step 3, and the client developer has to choose between them.
**Reactive: invoke and read the challenge.** Call the tool, let it fail, and read the `WWW-Authenticate` header. OAuth already has a graduated family of challenges for this, one per grain of insufficiency, and the client has to handle each:
- [`insufficient_scope`](https://www.rfc-editor.org/rfc/rfc6750) says the token lacks a required scope. The grain is the capability class.
- [`insufficient_authorization_details`](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/) is the RAR analogue, a valid token that lacks standing authority at the action's grain. It can name the `authorization_details_types_required` or hand back ready-to-use `authorization_details` for the next request.
- The [OAuth 2.0 Step Up Authentication Challenge Protocol](https://www.rfc-editor.org/rfc/rfc9470) returns `insufficient_user_authentication` with the `acr_values` or `max_age` an action requires, when the problem is not what the token grants but how strongly or how recently the user authenticated.
- The proposed [OAuth Transaction Authorization Challenge](https://datatracker.ietf.org/doc/draft-rosomakho-oauth-txn-challenge/) goes one grain finer. The resource names the specific transaction in front of it, and the AS evaluates exactly that, runs consent or step-up if needed, and mints a transaction-specific token that authorizes it and nothing else.
The family shares one shape, and it is worth seeing at the protocol layer before any tool machinery arrives. The resource judges the concrete situation and issues a structured challenge. The Authorization Server remediates at the challenged grain, a broader scope, richer authorization details, a stronger authentication, or a single-transaction permit. The client carries the result back as a token. Trigger at the resource, remediation at the AS, enforcement by token. Each challenge demands a different correction, and the cost they share is the failed call. For a read that is a wasted round trip. For anything with a side effect, the client has to be certain the call was rejected before the effect, not after it.
**Proactive: read the metadata first.** Fetch RAR-type metadata, work out the requirement for the call you are about to make, request the matching token, and only then invoke. The cost moves to interpretation. The client now has to read the resource's schema correctly, and the resource is still the authority on whether it got it right. A proactive guess that is wrong lands back in the reactive path anyway, and authentication-strength requirements often cannot be discovered ahead of time at all.
Either way, the client is running a small authorization state machine. It has to discover the requirement, request a token, and match held tokens against pending calls. It has to acquire and await new ones, refresh them before they expire, and satisfy a step-up authentication challenge when one comes back. And it has to union scopes across incremental requests, so re-authorization does not drop authority the agent already had. The more granular the authority, the more tokens there are to track. This is the token-side model's quiet tax, and the client developer pays it on every tool the agent can reach.
## The Ontology Problem
The "AS evaluates the request" step is where this model meets domain reality. For the AS to mint a token whose RAR entries describe `read Q3 financials for Example Corp`, the AS has to be able to evaluate whether the request is sensible: whether the client may make this kind of request, whether the user's role permits the action, whether the financial period and entity identifier are valid for this user, whether the constraints are compatible. That is resource-domain knowledge. This bites open-world, multi-AS, self-service integrations most. A first-party authorization server built into its own resource server mostly sidesteps it, because the domain knowledge is already in the box.
The AS in a typical multi-tenant SaaS Identity Provider (IdP) is generic. It knows OAuth, RAR, scope, and client registration. It does not natively know what `financials` means, what `Q3 2026` resolves to in the finance system, or which entity identifiers are valid for this user. The RAR `type` and `actions` strings are opaque to the AS unless the AS has been taught them. There are three ways out, and each has a cost:
1. **Tight coupling.** The AS is purpose-built for the Resource Server (RS). The finance system's AS is part of the finance system. This is common for first-party deployments and does not generalize to the open-world MCP scenario where any agent can call any tool on any MCP server.
2. **Standardized vocabulary.** A shared operation language across resources, such as a common RAR-type registry. Real progress, but the vocabulary itself is a slow, committee-driven artifact, and not every RS adopts it.
3. **Delegated evaluation.** The AS calls the RS or an RS-domain Policy Decision Point (PDP) to validate the RAR at issuance time. This works but reintroduces an RS-side decision into the token-side model. The "carry the authority" property weakens because the RS is back in the loop, just earlier than execution.
In practice, many RAR deployments rely on tight AS/RS coupling or delegated evaluation, and clients often know supported RAR types through prior integration. The [Zehavi RAR-type metadata draft](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/) can surface the schemas through an `authorization_details_types_metadata_endpoint`, but it does not tell the AS how to validate the domain semantics behind them. RAR improves the expression of authority. It does not eliminate the need for a shared vocabulary or resource-domain validation.
That is the token-side model's hard part. The AS has to understand a resource's domain to mint authority over it, the client has to run the acquisition loop to get the right token, and a fresh narrow token piles up for every new kind of action. The authority is portable. The cost is ontology at the server and orchestration at the client.
# The Resource-Side Model: The Decision Stays at the Resource
In the resource-side model, the resource server evaluates. The client presents whatever credential or authenticated session the MCP server accepts, and the component controlling execution acts as the Policy Enforcement Point (PEP). It evaluates the concrete request and executes only on a permit.
The appeal is that the resource is the domain expert for its own tools. It already knows what `query_financials(period, entity)` means, because it implements the call. There is no AS in the request path that needs to learn the resource's vocabulary. The ontology problem disappears at the issuance layer, because nothing is being issued.
It collapses the client's side too. There is no scope to discover, no `authorization_details` to construct, no token to match or refresh per action, and no acquire-and-retry loop. The client presents the one credential it holds and calls the tool. The resource works out the requirement and decides. The acquisition state machine moves behind the resource, where the domain knowledge already lives. The client developer writes a call, not a loop. The work does not vanish. It moves to the resource, which now owns the decision, the denial, and the audit for every call it serves.
The freshness story is also stronger. Consider an agent approved to read Q3 financials for Example Corp. Between approval and execution, the user is offboarded from the finance system, or Example Corp is reclassified as restricted. A token-side server validating offline may still accept a token that remains valid on its face, handing an offboarded user's agent confidential data. A resource-side server evaluating immediately before execution can reject it. Deciding at the moment of use narrows the time-of-check-to-time-of-use (TOCTOU) window between grant and action.
On plain OAuth, though, the resource-side model has its own hard parts, and they are quieter:
- **No standard decision contract.** The server has a token and some local logic. How it turns the request into a decision is proprietary. Two MCP servers solve the same problem two different ways, and nothing about the decision is interoperable.
- **No standard way to say "denied, but you may ask."** A flat `403` is a dead end. The agent has no machine-actionable path from denial to approval to retry.
- **The decision is not portable.** It lives at the server and its policy logic. Another party cannot verify it without re-running the evaluation against the same domain model.
- **It fragments across servers.** Each MCP server decides with only its local context. A task that crosses three servers produces three unrelated decisions, three approval prompts, and three audit trails.
The resource-side model keeps the meaning where it lives. The cost is that, on traditional OAuth, every server reinvents the decision, the denial, and the audit. [Part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) is about closing exactly those gaps.
# Where the State Lives
The two models answer where the *decision* lives. A separate axis, easy to conflate with it, is where the authorization *state* lives that the decision evaluates. There are three topologies, and they are still protocol-layer facts, independent of any tool machinery.
- **State in the token.** The claims are the authorization state. The `authorization_details` the credential carries are everything the resource knows, validation is offline, and nothing consults a backend at decision time. Token reuse is maximally valuable here, because the token is the state owner. The corollary is that token churn is the fulfillment mechanism. Every change in authority forces reissuance, and staleness is bounded only by token lifetime.
- **State in a store.** The PDP evaluates against externalized state: Zanzibar-style relationship tuples in a system like OpenFGA, an entitlements database, or policy data. The token is little more than principal identity. Reuse is trivial but low-meaning, because the leverage moved to the store. A grant fulfills as a store write, and the same token the agent already holds starts passing evaluation. Freshness is strong. Portability and cross-domain proof are weak.
- **Hybrid overlay.** The token carries constraints and binding data that the PDP combines with backend state. Most production deployments land here, and it offers the most options: cheap narrowing carried in the token, live entitlements read from the store.
The coupling with the two models is asymmetric. The token-side model forces token-resident state at the point of validation, unless introspection quietly reintroduces a backend. The resource-side model can run any of the three. That changes what the earlier tradeoffs weigh. Token reuse, churn, and the TOCTOU window depend on which topology the deployment actually runs, not just on which model it chose. And it changes what an approval has to do to take effect. A grant lands as a fresh token in one topology and as a store write in another, which is exactly the fulfillment question [part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) picks up.
# The Tool Is a First-Class Boundary
Before the policy machinery, it is worth being explicit about the layer both models run through. The token-side model treats the MCP server as a resource server. The resource-side model treats it as a policy enforcement point. MCP itself treats it as something richer than either. It is a host of *tools*, with first-class verbs of its own.
- **The catalog is governed.** `tools/list` is not just documentation. A deployment can filter the catalog through the same policy that gates invocation, so an agent sees a tool set appropriate to its context. That filtering is exposure control, not authorization. Catalogs can be stale, policy can change, and a client can call a tool by name without listing it first. The server must still authorize every `tools/call`.
- **The call is the choke point.** `tools/call` is where execution actually happens, so it is where enforcement has to sit. A decision made anywhere the call can bypass is not enforcement. Whichever model produced the authority, the tool boundary is where it is finally checked.
- **The work can be held.** This is the piece OAuth never had. Task-oriented MCP extensions and proposals let a tool call return a working result and resume later, which is exactly the substrate an out-of-band approval needs. A denied-but-requestable call does not have to fail. It can become pending work that completes once a human decides.
- **The tool can ask.** MCP [elicitation](https://modelcontextprotocol.io/specification/draft/client/elicitation) lets a server gather structured input, confirm an action, or send the user to an external flow. Elicitation is not authorization or durable consent by itself. The server must bind the interaction to the authenticated user and feed the result into its policy or approval workflow. An elicited value changes standing authority only through a governed re-authorization, not by virtue of being collected.
The reason to make the tool first-class is that the hard parts of the resource-side model, the missing decision contract and the missing denial-to-approval workflow, get their home here. The catalog and the call give the tool boundary an enforcement point. Held work gives it a place to park a long-running approval. What is still missing is a standard language for the decision, and a workflow for when the answer is no. Both are where [part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) goes.
# Comparing the Two Models
The two models are not enemies. They place the same five pipeline steps at different boundaries, and the tradeoffs follow from where each step lives. One of those tradeoffs lands squarely on the client developer building the agent, in how much token management each model demands.
| Pipeline step | Token-side: authority in the token | Resource-side: decision at the resource |
| --- | --- | --- |
| Discover need | Agent infers from task plan or a `WWW-Authenticate` challenge | Agent infers from task plan or a `tools/list` catalog the MCP server already filtered |
| Tool discovery | Out of scope for the AS. The MCP server can filter independently | Can use the same PDP as invocation, but still requires execution-time authorization |
| Request authority | RAR `authorization_details` to the AS, narrowed to one action | Tool invocation directly to the MCP server |
| Evaluate | At the AS, optionally consulting a PDP. The AS needs domain knowledge or must delegate | At the MCP server's PDP, with the resource's own domain model in scope |
| Authorize or escalate | Narrow access token, or a deferred grant when approval is needed | Permit decision, or held work when approval is needed |
| Execute | Baseline uses local token validation. Higher-assurance deployments add introspection, events, or runtime evaluation | MCP server runs the tool only on a freshness-bounded permit |
| Carried artifact | Sender-constrained access token, bound by Demonstrating Proof of Possession (DPoP) or mutual TLS (mTLS), portable proof | No new artifact on permit. A server-generated MCP `taskId` while asynchronous approval is pending |
| Authorization state | In the token. Claims are the state, and reissuance is how state changes | In the PDP's store (or hybrid, with token overlay). A store write changes state under an unchanged token |
| Client token management | Match each call to a held token or fetch a fresh narrow one, and cache the result | Reuse one token, and let the resource broker any approval |
| Latency profile | Token acquisition up front, then local validation unless stronger freshness is required | One PDP evaluation per consequential call. Caching must remain within policy freshness bounds |
| Easy | Offline validation, token portability, low execution-time latency | Domain-specific policy, TOCTOU-resistant execution, no token explosion |
| Hard | AS ontology, RAR vocabulary convergence, token churn | Cross-server audit, approval fragmentation, portable proof |
| Missing | Durable task context across calls | Durable task context across calls |
The choice is per call, not per deployment, and it follows from where the hard part lives:
- **Reach for the token-side model** when authority has to travel: several downstream hops, offline or low-latency validation, or an audience that cannot call back to a PDP on every request. The cost you accept is the AS ontology problem and token churn.
- **Reach for the resource-side model** when the decision has to stay where the meaning lives: the resource owns rich domain semantics, the action is consequential, or TOCTOU drift between grant and use is unacceptable. The cost you accept is a per-call evaluation and a decision that is not portable.
- **Layer both** when you want defense in depth, which is where most production deployments land. A coarse token carries identity and audience, and the resource still evaluates the concrete call at the action's grain. The token answers who is calling and roughly for what. The resource answers whether this exact call is allowed.
Carry when you can, evaluate when you must, and most real systems do some of each. But whichever you choose, the choice only governs a single call. The moment the work has to stay governed across servers, neither model is enough on its own, which is where the [second part](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) begins.
# What to Get Right
Both models can be implemented in ways that look like least-privilege without being it. A few failure modes are worth designing against on purpose, whichever model you pick.
- **Authority at the wrong grain.** A token or policy that says "may call the finance MCP server" is not least-privilege. One that says "may read Q3 financials for Example Corp" is. The token-side model puts the grain in the `authorization_details` entry. The resource-side model puts it in the PDP's view of the concrete call. Coarse authority is the most common way least-privilege quietly decays into ambient access.
- **Bearer tokens that travel.** A token-side access token that is not sender-constrained is replayable by anyone who captures it. Bind it to the client with DPoP ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449)) or mTLS ([RFC 8705](https://www.rfc-editor.org/rfc/rfc8705)) so a stolen token is useless off the agent that earned it.
- **Enforcement off the choke point.** Filtering `tools/list` is exposure control, not authorization, and a client can call a tool it never listed. The PEP has to sit where execution actually happens and authorize every `tools/call`, or the decision is bypassable by design.
- **Stale or replayed approvals.** An approval is an input to a fresh decision, not a standing grant. Bind it to the exact subject, action, resource, and request, bind it in time, and reevaluate at execution. The resource-side per-call evaluation narrows the TOCTOU window that an offline-validated token-side token leaves open.
- **Confused-deputy delegation.** The agent authenticates as itself but acts for a user. The decision must turn on both, not collapse to one. Preserve the actor chain ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693) `act`) so the principal and the acting workload stay distinct in the decision and in the evidence.
# Where This Goes Next
Part 1 compared where the decision can live. Part 2 is about what has to become standard around that decision: the decision contract, the mapping into it, the denied-but-requestable workflow, and finally the task context none of them provide. Once the task spans more than one call, server, or authorization domain, neither model stands alone, because neither names the task that connects them.
[Part two](/notes/closing-the-gaps-least-privilege-mcp-tool-calls/) walks that stack and closes every gap but the last. The one that remains is the task. Nothing names it.
---
# Authorization Is the Other Half of Executable Intent
Canonical URL: https://notes.karlmcguinness.com/notes/authorization-is-the-other-half-of-executable-intent/
Markdown URL: https://notes.karlmcguinness.com/notes/authorization-is-the-other-half-of-executable-intent.md
You can hand a framework a written description of how your agent should behave and get back a suite of tests that catch it misbehaving. Not by hand-authoring every assertion: you describe the behavior, declare the dimensions that matter, and a model compiles the rest into tests that run. The capability is new, and the pattern behind it is about to be everywhere.
It is half of a control.
Tests tell you, after a run, whether the agent behaved. They say nothing about what the agent was allowed to do before it started. For a stateless chatbot answering over non-sensitive data, that gap barely mattered; the worst case was a bad answer you could throw away. For an agent that files expenses, moves tickets, queries financial systems, and sends mail, it is the whole game. These things act across systems their authors do not control, and some of what they do does not undo.
The question is no longer only whether the agent behaved, but what it was allowed to do in the first place. The industry is meeting that second question with the instinct it brought to the first: stop leaving intent in prose only a human can act on, and compile it into something a machine can execute. That showed up first in testing. It belongs just as much in authorization, where, even as the enforcement standards keep shipping, the step that compiles intent into them has barely begun.
# Intent That Runs
Start with the half that already works, because it shows the move in its cleanest form.
Microsoft's [ASSERT](https://github.com/responsibleai/ASSERT), short for Adaptive Spec-driven Scoring for Evaluation and Regression Testing and released as part of its open trust stack at Build 2026, is a clean instance of the pattern. You write the behavior you expect. ASSERT derives behavior categories from it, generates single- and multi-turn test cases, runs them against your agent, and uses an LLM judge to score each conversation against your policy, tracing each verdict to the policy stance and the trace action behind it ([Microsoft's writeup](https://commandline.microsoft.com/assert-written-intent-executable-evals/)). Written intent in, a reviewable test suite out, none of it hand-authored case by case.
Strip away the specifics and the move is general: take intent expressed in language, compile it into a structured artifact, and let that artifact do the work the prose never could. ASSERT makes intent executable for verification, answering, at far less cost, the question teams used to answer one test at a time: did the agent behave the way we said it should?
It says nothing about a different question, one that comes earlier in time and matters more for anything that cannot be undone: what should the agent have been allowed to do at all?
# Verification Is Not Enforcement
Those two questions sound like neighbors. They are different controls, and the difference is not academic.
Picture the billing agent. You hand it a task that sounds routine: resolve a customer's billing dispute. To do that it pulls up the account, reads the invoices, checks payment history, updates the support ticket. Somewhere in the middle, a support note it ingests nudges its reasoning, or it simply generalizes "look at the customer's records" a little too far, and it queries a second customer's account to compare. Nothing in its instructions forbade it. It was holding a credential that worked.
In an eval-only world, here is how that plays out. The release-gating suite passed before any of this shipped: this run, with this injected note and this data, was never one of the cases it generated. The agent runs in production, the second customer's data is read, already in context, possibly summarized into an output you cannot recall, and nothing flags it in the moment. You learn about it later, from a log or an incident. A post-hoc eval over the trace could score it after the fact, but after the fact is exactly the problem. A clean release report sits comfortably beside a real breach.
That is the difference, and it is not really about timing. An eval, even one wired to run inline, produces a verdict, not a binding entitlement decision; an authorization system makes the decision and refuses at the moment of the call. The release-gating and post-hoc evals here also run too late to matter. An agent that reads private data, takes in untrusted content, and can act on the outside world, the combination Simon Willison named the lethal trifecta, does things that do not reverse. You cannot un-send a payment, un-email a list, or un-leak a contract. The usual advice for the trifecta is to break it by removing a leg; an eval removes none. It watches an agent that still has all three, and detection does not give the data back.
A camera is not a lock. Monitoring records the break-in; the lock prevents it. Agents got cameras first because grading is easy: you can score behavior without touching the runtime, while enforcement means deciding, live, inside the path of every call, what the agent may reach. The easier half shipped first. It is not the half that stops anything.
# The Missing Step
So put a lock on it. We are not short on locks. Fine-grained authorization, policy engines, runtime decision points, scoped tokens: the machinery to refuse a call at the moment it is made exists and keeps improving. The same Build 2026 trust stack that shipped ASSERT also shipped the [Agent Control Specification](https://devblogs.microsoft.com/foundry/build-2026-open-trust-stack-ai-agents/), a portable standard for deterministic controls at checkpoints across an agent's workflow. But ACS, like every lock on that list, assumes the controls already exist, authored separately. None of them turns a goal into the controls it should carry. The trouble is upstream. The instruction you started with does not describe a boundary for any of that machinery to enforce.
"Resolve the customer's billing dispute" is a goal. It names an outcome, not a boundary. To pursue it the agent legitimately needs the customer's account, their invoices, and the open ticket. It almost certainly does not need payroll, the source repository, HR records, or any other customer. But none of that is written anywhere. The user said what they wanted accomplished and nothing about scope, because to a human the scope goes without saying. To an agent, nothing goes without saying.
Closing that gap is the mission shaping problem: taking the open-ended request and turning it, before the agent acts, into a structured and bounded statement of what the task is and is not. I have written about why [open-world OAuth needs that shaping step](/notes/open-world-oauth-still-needs-mission-shaping/) rather than a static grant. Applied to the billing agent, the shaper's proposal looks like this:
```json
{
"goal": "Resolve the customer's billing dispute",
"objects": ["customer 1234's account", "their invoices", "support ticket 456"],
"constraints": ["read-only except updating the ticket", "billing domain only", "this customer only"],
"success_criteria": ["dispute resolved or escalated to a human", "ticket updated"],
"expires_at": "2026-06-30T18:00:00Z"
}
```
That object is not yet authorization, and it is not yet trusted. Whatever does the shaping, a model here, produces only a proposal. A trusted authority validates it against policy, narrows it, derives the candidate authority, and records an approval before any of it binds; only an approved mission is active. Skip that and you are enforcing whatever the shaper asked for. What enforcement then checks, on every consequential call, is the authority that survived approval.
Materialization turns the whole approved mission into something a machine executes: the authority set along with the tenant and actor bounds, the capability bindings, and the policy version it was approved under. It takes the resource systems' help, since only the billing systems know which resources and actions they actually expose. Simplified, the result is rules an enforcement point can check without interpreting any natural language:
```json
{
"allow": [
{ "action": "read", "resource": "account", "where": { "customer": "1234" } },
{ "action": "read", "resource": "invoice", "where": { "customer": "1234" } },
{ "action": "update", "resource": "ticket", "where": { "id": "456" } }
],
"default": "deny",
"expires": "2026-06-30T18:00:00Z"
}
```
This is the executable authorization: the mission compiled into a boundary a policy engine evaluates on every consequential call. Read account 1234 and the request matches an allow rule and clears. Reach for account 5678, the customer the agent drifted to in the breach, and nothing matches, the default denies, and the call returns nothing. Not data plus a note for a reviewer next week. Nothing. And notice what is not happening: no model is deciding whether to grant. A model may still contribute a signal, a risk score or a content classification, but it cannot hold the authority or widen it. The authority was fixed and approved before the agent started, and the call is evaluated fresh against it: current mission state (a revoked or expired mission fails closed), the resource's own policy, the actor, and the parameters. The model can advise; the approved authority caps what may be permitted, and the PDP decides within that cap.
A fixed, pre-approved boundary does not make the trifecta safe. An injected agent can still combine the private reads the mission permits with the outbound actions it permits, and exfiltrate within scope. What the boundary buys is a bounded blast radius: the injection cannot widen what the agent may reach, so exposure is capped at the task's authority instead of the agent's whole credential. Capping is not closing. Closing is the rest of the work: private-read, untrusted-input, and external-action authority separately typed, each consequential action evaluated as it happens with its parameters bound in, egress behind the same control, step-up approval on the highest-stakes steps. A mission does not dissolve the trifecta. It makes it governable, which is the most any boundary can honestly claim.
The mission and the rules it produces are worth keeping straight. The mission is the artifact you stand behind: human-readable, the record of what the task was for and why these objects and not others, signed off by its approver and readable later by an auditor. The executable authorization is that mission in force: literal, machine-facing rules an enforcement point runs throughout the task, meaningless on their own without the mission that explains them. The mission is approved once; the enforcement point runs its rules a thousand times. It is the relationship an eval has to a test run: you stand behind the artifact, the machine executes it.
For that record to be defensible, the mission has to carry more than a goal and some objects. It binds to the requester and the user it acts for, the approving authority, the resource context it was scoped against, the policy and derivation versions it was built under, and an expiry. Without those it is a blob of intent, not an authorization artifact anyone could stand behind in a review.
And when the task needs more than the mission anticipated, say the dispute hinges on a related order in another system, the answer is not a standing grant broad enough to have covered it. It is a governed expansion: a successor mission carrying the wider authority, the prior one completed, its record and lineage kept for audit. Scope tracks the task, not the org chart, and it moves by minting a new bounded mission, never by loosening an old one.
# Intent Becomes the Unit of Authorization
Step back and the shift underneath this is bigger than one billing agent. Mainstream enterprise authorization, the role-based kind most companies actually run, ties what you may do to who you are: a role provisioned ahead of time that holds as long as you hold the job. Richer models exist, attribute-based, capability, and contextual policy all predate agents, but the static role is the dominant pattern, and it works when identities map to stable jobs.
Agents do not hold jobs. They take tasks. The same agent, under the same identity, resolves a billing dispute at ten o'clock and drafts a board summary at eleven, and the two tasks have almost nothing in common in what they should be allowed to touch. Give that model a single static role for "the agent" and it has to be the union of everything any task might ever need, broad enough that it is no longer least privilege for any one of them.
Identity still matters, for a narrower job: it establishes who is acting and for whom. The ceiling, the most the agent could ever touch, comes from what that identity is entitled to, plus client registration, delegation, and tenant and resource policy. A grant can never exceed it. But a ceiling is not a scope: it says what is possible, not what is needed now.
What is needed now is a different question, "what are you trying to do," answered one task at a time, because the task is the only place the answer lives. [A session is not a mission](/notes/sessions-are-not-missions/): a login that lasts all day is the wrong container for an intent that lasts twenty minutes and wants three specific things. So the mission does not replace identity; it narrows within it. The grant is the intersection: bounded above by that ceiling, bounded to the task by the mission, which adds what conventional deployments usually lack: a durable, approved, machine-readable statement of what this task is for, narrow enough to bound the work and fresh enough to fit it.
# Mission Objects Are the Authorization Counterparts to Evals
If you have written an eval, you have already done this once. You took intent in language and compiled it into an artifact a machine acts on. You did it for the verification side. The mission is the authorization counterpart, built with the same compilation pattern, and most teams that have the first do not yet have the second.
Both begin with intent in plain language. Both compile that language into a structured artifact, often with a model, though a rules engine, a form, or a workflow can do the shaping too. Both exist so that something a human meant can be acted on by a machine that would otherwise see only tokens. ASSERT compiles intent into a test that asks, after the run, did the agent behave? On the authorization side, the same kind of intent is shaped into a proposal, validated and narrowed, derived into a candidate authority, then approved and activated as the boundary that asks, before the run, what may the agent do?
If you understand why generated evals are a good idea, you already understand why generated authorization boundaries are. The reason to compile rather than hand-author is the same on both sides: the inputs are open-ended. You stopped hand-writing test suites because you could not enumerate every behavior worth checking; you cannot hand-write a scope for every task a user might dream up either. A goal no one anticipated still needs a boundary, and something has to turn the task into one.
The symmetry is what makes the analogy useful. The asymmetry is what makes it matter:
| | Eval (e.g. ASSERT) | Mission object |
| --- | --- | --- |
| Derived from | written intent | written intent |
| Produced by | a model, reviewed by a human | proposed by a shaper, validated by a state authority, approved by a principal or policy |
| Binds | nothing; produces a non-binding verdict | each consequential action, through the PDP that enforces it |
| Role | detective: did it behave? | preventive: what may it do? |
| Lifecycle | a durable suite, reused across runs | per task, short-lived, tied to one goal |
| Authority | none; it observes and scores | governs the authority set a PDP permits and denies against |
| Cost of being wrong | a false pass greenlights a vulnerable release | this execution is wrongly authorized |
Read the bottom rows together. An eval carries no authority; it produces evidence. A wrong eval is not harmless, a false pass can greenlight a vulnerable release, but it does its damage indirectly, by feeding a deployment decision a human still owns. A mission governs authority directly. It is not advice a later decision weighs; it commits to an authority set that every runtime check is made against, so a wrong mission is wrong at the moment of each action it governs. That is why a mission cannot be an eval turned around. The same construction, once it confers authority rather than evidence, needs what an eval never had: an approval path, a default that denies when intent is unclear, and a lifecycle that ends.
There is a sharper way to feel it. An eval is re-runnable: tune the suite, run it again, watch the score move, all before anything ships. An approved mission is not a rehearsal. You improve the shaping offline, on past tasks, with evals, but once a mission is active its authority is live, and the first action under it is the real thing. It can still be suspended or expired; what it cannot be is quietly re-run against the world the way a test is.
# Why Not Just Judge Every Action?
If a mission is so much more fraught than an eval, the obvious shortcut is to skip the artifact. Keep the model in the loop, point it at each action before the agent takes it, and let it decide. An LLM judge as the gate. People are already building this, under the name guardrails.
It does not hold up as authorization, and seeing why is the clearest case for the mission.
Run the judge after the fact and you are back to detection: the action happened, the score came late, the data already moved. So the judge has to run inline, before each call. The moment it does, three problems arrive together.
It makes a security decision probabilistic, turning allow or deny on how a model reads a situation rather than on a rule that, for the same inputs and policy version, returns the same answer every time. And the model is injectable: give the judge enough context to make a real call and you have handed it the same untrusted content steering the agent, so the injection that fools the agent also gets a vote on allowing it. Starve it of that context and it can no longer judge much, collapsing toward a fixed rule, a conventional deterministic policy check with a model's latency bolted on, and none of a mission's task-specific authority at that. A grant should be the one thing untrusted input cannot argue with. A model in the decision path is the opposite.
It answers the wrong question. An eval scores behavioral conformance: does this transcript look like the spec? Authorization is an entitlement check: may this principal, acting for this user, touch this resource right now, given delegation, tenant, and revocation? A model reading a conversation cannot know whether this customer's agent may read that customer's invoice; that fact lives in the identity and entitlement systems, not the transcript. Give the judge access to them and the authority has moved into them, where it belongs. The model can still help, classifying risk or reading context that the policy then weighs, but it is no longer the thing that decides. That is the line: a model may inform an authorization decision; it must not be the authority that makes it, and it must not be able to widen access on its own.
And it still needs something to judge against. A generic global rule, do not touch other customers, is not least privilege; it cannot say this task may read these three things and nothing else. The per-task specification the judge would check is the mission. Skip the artifact and you have not removed the mission, you have buried it in a prompt and made it non-deterministic and unauditable.
The division of labor is not a turf fight. An eval observes. A guardrail filters. A mission authorizes. An inline judge is a fine extra guardrail and a poor authorization system, because what authorizes has to be deterministic, inspectable, bound to an identity, and fixed before execution, so the untrusted content the agent ingests as it runs cannot alter or widen it. That is the approved mission and the authority set it commits, not a model deciding live.
# The Hard Part Is the Same Hard Part
Keeping the model out of the gate raises a fair follow-up. The shaper that produces the mission is often a language model too, the very thing whose judgment we are trying to contain. It does not have to be: a rules engine, a form, or a workflow can shape just as well, and there the question never arises. But take the hardest case, a model shaper: if an inline judge is too injectable to authorize, why is a model trustworthy enough to write the boundary? It sounds like handing the fox the keys.
It would be, if the model's output were the grant. It is not, and that distinction is the whole design. The shaper proposes a mission intent; a state authority validates and narrows it, derives the candidate authority, and renders it for approval by a person or a policy precise enough to stand in for one; only on approval does the mission become active, and that approved mission, fixed and inspectable, is what gets enforced, not the model's live reasoning as it runs. The model drafts, something accountable disposes, and the approved artifact is what binds.
The eval world already trusts model-generated artifacts this way: ASSERT writes its tests with a model and scores them with a judge, and the answer to "why trust that" is not faith but review and a measured judge-human agreement rate. The mission side needs the same discipline at a higher bar, and the reason the bar is higher is the point. Grading is allowed to be approximate: a judge whose agreement with human reviewers has been measured for the behaviors it scores can be good enough, though that agreement varies by model and by how fine the policy distinction is. Authorization cannot be approximate; a grant that is right most of the time is a breach the rest of the time. So a model can decide an eval, but on the authorization side it can only draft the artifact an accountable principal or governing policy approves and a deterministic check enforces. Same model, same intent, different tolerance for being wrong.
The difficulties that remain are the ones you would expect from making fuzzy human intent binding:
- **Intent is underspecified.** "Resolve the billing dispute" never says which systems. When the ambiguity is material, the shaper must not guess: narrowing silently can misread the task as badly as widening it. It has to clarify or refuse, with the user as the final disambiguator. The preventive bias is to put real ambiguity in front of a person, not to resolve it quietly in either direction.
- **Scope needs domain knowledge.** "Billing domain only" has to resolve to the resources and actions the billing systems actually expose. The shaper proposes the shape; the systems that own those resources confirm it is real and allowed. Intent does not get to invent capabilities that do not exist.
- **Tight missions break tasks.** The price of any preventive control is false denials, and a mission drawn too tight strands the agent halfway through legitimate work. The answer is the governed widening from earlier, not drawing missions loose enough to never get in the way.
None of these is a reason to wait, but they are a reason to be honest about the size of it. This is a heavier build than an eval harness: shaping, derivation, enforcement, an approval path, a way to widen a mission mid-task. Evals had a head start because grading is cheap to build and its mistakes have a smaller direct blast radius. Authorization has neither advantage, which is exactly why irreversible actions cannot keep waiting for it.
# One Source, Many Artifacts
Follow this far enough and a larger architecture appears. One approved mission need not feed only the enforcement rules. The same validated intent and authority set can drive the runtime constraints and the behavioral evals as well, each compiled from the artifact that was already approved, not from the raw prose.
Deriving them from that shared, approved source keeps things aligned. An approved mission carries both an authority set and a goal with constraints and success criteria, so it can generate two kinds of eval: one asking whether the agent stayed within its granted authority, and one, the behavioral eval ASSERT actually writes, asking whether it pursued the goal within those constraints and met the success criteria.
On the authorization side, a flagged behavior means different things depending on what enforcement did. An out-of-bounds attempt the PDP denied is a misbehaving agent and a working control, worth correcting, not a breach. An out-of-bounds action that went through is an enforcement or materialization failure. An in-bounds action that still did harm points elsewhere: the mission was too broad, or the resource's policy was. And none of it says whether the mission captured the user's intent to begin with; that is a separate question for a shaping-quality eval or a human-labelled oracle scoring the mission against the original intent, not against itself. Conformance and faithfulness are different checks, and one artifact answers only the first. The loop closes:
```mermaid
flowchart TB
Goal([User goal]) --> Shape[Mission shaping]
Shape -->|proposed intent| Auth[State authority
validate, narrow, derive]
Auth -->|rendered for approval| Approve{Approver
principal or policy}
Approve -->|approved| Mission[Mission + authority set]
Mission -->|audience-scoped projection| PDP[Per-call decision / PDP]
Ctx[Runtime context
mission state, actor, resource policy, parameters] --> PDP
PDP -->|permit| Exec[Agent execution]
Exec -->|each consequential action| PDP
Exec --> Eval[Evaluation, e.g. ASSERT]
Eval --> Verify([Behavior verification])
Verify -.->|findings refine the next mission| Shape
```
The first half constrains the agent before it acts; the second checks what it did. The dotted edge is the part most people skip, but a finding does not name its own fix: read against the cases above, it points at a layer, the agent, enforcement, the mission, or the shaping behind it, and that is where the correction goes, not always into a sharper test.
# The Chain Worth Building
None of this is really about prompts, or policies, or evals as separate wins. It is about a chain. Intent enters as a sentence a person types, and today it dissolves almost immediately: the prompt runs, the agent fans out across tools, and the original goal survives, if at all, as a line in a log someone might grep after something goes wrong. The opportunity is to keep that intent intact and load-bearing the whole way through, from the goal, into the authority the agent is granted, through the actions it takes, into the verdict on whether it behaved.
The pieces are arriving. Executable evaluations are furthest along, because grading is the safest place to start. Executable authorization, with a mission object as its artifact, is arriving second but has to run first, because shipping agents with evals and nothing on the authorization side leaves you with detection on actions that do not undo. A camera pointed at a door with no lock. Worth having, and not the same as safe.
Both rest on the same conviction, and it is worth saying plainly:
Human intent should not stay trapped in natural language. It should become something a machine can enforce and verify. And it should be the same intent on both sides.
---
# Re-Subjecting Is a Mint, Not an Attenuation
Canonical URL: https://notes.karlmcguinness.com/notes/re-subjecting-is-a-mint-not-an-attenuation/
Markdown URL: https://notes.karlmcguinness.com/notes/re-subjecting-is-a-mint-not-an-attenuation.md
A user signs into a travel app and books a trip. Filing the trip makes the travel app call the expense app, and submitting the expense makes the expense app call the corporate card ledger. One human, one sign-in, three applications, each behind its own Authorization Server, all federated to the same enterprise identity provider (IdP). The IdP is the common trust anchor for SSO and user subject resolution. This is the pattern now emerging as Cross-App Access.
It looks solved. The user is authenticated, the apps are federated, and OAuth has Token Exchange and assertion grants for crossing authorization domains. But the hard part is not merely getting a token from one domain to another. It is preserving the user when the next domain identifies that user in a different namespace. The two things you would reach for first, passing the calling service's own identity or narrowing a token you already hold, both fail at that, for the same structural reason.
That distinction matters because two operations are easy to conflate:
- **Attenuation** restricts authority already represented by an artifact.
- **Re-subjecting** creates an authoritative binding between the same person and a target-local subject identifier.
The first can sometimes happen offline. The second requires an authority trusted to make the identity binding.
Agents make the distinction urgent. A person used to click through each app and sign in again when a chain crossed a boundary. An assistant acting for the user now triggers these chains at machine speed, fans out to applications nobody enumerated when the task began, and cannot stop for a new browser flow at every hop. The system needs a non-interactive way to preserve both the user and the acting workload without letting either substitute for the other.
# The Problem
**Applications may name the same user differently.** In OpenID Connect, a pairwise Subject Identifier is unique per Sector Identifier, which may represent one relying party or a group of related relying parties. Other deployments use tenant-local identifiers, SAML NameIDs scoped to a Service Provider, or product-local account IDs. The travel app might know the user as `trav-9f2a`, the expense app as `exp-4a17`, and the ledger as `led-c7e0`. Nothing in those strings proves that they identify the same person.
The important boundary is therefore not "a new resource" by itself. It is a new **subject namespace**. The Resource Authorization Server for the ledger needs the identifier that its established SSO relationship uses for that user. Only an IdP or broker trusted for that relationship can authoritatively supply or validate the mapping.
**After the first hop, the interactive sign-in is no longer present.** The expense app received a service call from the travel app, not a browser session established directly with the IdP. It may have valid inbound user context, but it does not necessarily possess a reusable IdP identity assertion, know the ledger's subject identifier, or have authority to perform that translation itself.
**Workload identity alone is insufficient.** The expense service must authenticate as itself, with its SPIFFE or workload identity. That answers which software is making the request. It does not, by itself, answer which user delegated the work or which target-local account the ledger should use. A secure chain needs both:
- the authenticated workload or client, including delegated actor context where applicable; and
- the end-user context, translated into a subject namespace the target authorization domain understands.
The workload cannot stand in for the user, and the user context should not erase the workload. In OAuth terms these are distinct claims, not one blended identity. RFC 8693 Token Exchange makes the distinction explicit: in delegation, the token carries the user as its subject and the chain of acting workloads in the `act` actor claim; in impersonation, a party simply becomes the subject. Re-subjecting changes the subject for the next audience, while the actor chain carries the workload lineage forward.
Skip this and one of two things happens. Either the ledger cannot resolve the user and the call fails, or the expense service falls back to acting as itself and the charge posts under a service identity with no human behind it. The second is the dangerous one, because it looks like it worked. That is the confused deputy: a chain that should have stayed delegation silently downgrades to impersonation, and nothing in the protocol forces it to fail loudly.
# Why Attenuation Cannot Create the Next Subject
Offline attenuation takes an artifact and adds restrictions: fewer resources, fewer actions, a shorter lifetime, more caveats, or a narrower delegation. It is the model behind Macaroons, Biscuit, and the stacked delegation tokens now proposed for workload and agent chains. That is useful within one authorization model and can remain useful across domains when the receiving domain accepts the issuer, subject, and restriction semantics.
But attenuation cannot establish a fact the artifact does not already contain or authorize its holder to derive.
> Attenuation can narrow authority already represented by an artifact. It cannot authoritatively create a target-local identity binding that the artifact's issuer never supplied.
Suppose the expense app holds authority for the user identified as `exp-4a17`. Adding caveats can restrict what that authority permits. It cannot prove that `exp-4a17` corresponds to `led-c7e0`, because the ledger's identifier belongs to another namespace. That correspondence is an identity assertion, not a subset relation. You cannot narrow your way into a subject you were never given.
Which gives the narrower principle:
> Crossing a subject namespace is a **mint, not an attenuation**. An authority trusted for the mapping must issue new identity evidence for the target context.
This does not mean the IdP unilaterally grants access. Two different parties mint two different artifacts:
1. The IdP or mapping authority issues audience-scoped identity evidence using the subject identifier the target authorization domain expects.
2. The destination Authorization Server validates that evidence, applies its own authorization policy, and decides whether to mint an access token.
That separation is central to [ID-JAG Beyond the Enterprise IdP](/notes/id-jag-beyond-the-enterprise-idp/). The immediate IdP trusted by the Resource Authorization Server is authoritative for SSO subject resolution. The Resource Authorization Server remains authoritative for the local access decision.
# When Translation Is Not Required
Not every trust-domain crossing changes the subject.
Two applications may share a Sector Identifier, use the same public enterprise subject, or accept the same upstream subject namespace by agreement. In that case no subject translation is required. Offline attenuation may be possible if the destination also accepts the upstream issuer and understands the artifact's authority and restriction semantics.
That last condition matters. A shared subject does not eliminate every mint. The destination may still require:
- a grant addressed to its Authorization Server;
- a locally trusted issuer;
- destination-specific client and tenant binding;
- local scope or `authorization_details` evaluation; and
- a locally issued access token.
So there are two independent questions:
| Question | Consequence |
| --- | --- |
| Does the destination require a different subject namespace? | A trusted mapping authority must participate in subject translation. |
| Does the destination require a new locally trusted grant or access token? | An Authorization Server must perform destination-specific issuance. |
Pairwise subjects force the first operation when the target is in another sector or subject namespace. Crossing an authorization domain commonly forces the second. They often occur together, but they are not the same requirement.
The first question has a one-line test: does the next service name the user differently than the artifact you hold? If yes, no caveat you add gets you there, and someone has to mint.
Pairwise identifiers therefore create a deliberate tradeoff. They reduce direct cross-context correlation, but the mapping authority must remain available when an authorized workflow needs to bridge those contexts. The cost is not necessarily a user interaction. It is authoritative participation in the translation.
The second question deserves the same look, because it is also a mint, in a different dimension. The destination's Authorization Server may speak a different authorization language than the calling domain: different scope names, a different `authorization_details` schema, role and entitlement models that do not line up. The holder of the source grant cannot narrow its way into that language any more than into the target's subject namespace, because it cannot assert scopes it was never issued. A trusted Authorization Server has to mint a fresh grant that re-expresses the authority in the target's terms.
Attenuation does not vanish here; it changes role. On the subject axis it is simply the wrong operation. On the authorization axis it survives as a constraint on the mint: the re-expressed grant MUST be no broader than the source authority, never amplified. Call it a non-amplifying translation. The minting is what carries the authority across the vocabulary gap, and the no-broader-than rule is the ceiling that minting must respect. On either axis, what you cannot do is hand the destination your own token and expect it to narrow into something it can use.
# Why the Mapping Cannot Be Delegated Casually
Pairwise identifiers may be generated from a protected derivation key rather than stored in a literal lookup table. That does not change the trust model. The derivation capability is functionally part of the map.
Giving the expense app a key that can compute the user's identifier for arbitrary relying parties would let it link the user across those parties. It would also make the expense app another authority capable of asserting target-local subject bindings. That relocates the trust anchor and weakens the unlinkability that pairwise identifiers were intended to provide.
A deployment can deliberately establish multiple mapping authorities, federation brokers, or pre-provisioned bilateral mappings. But each of those is an explicit trust arrangement. None turns subject translation into ordinary attenuation performed by an otherwise untrusted token holder.
# Where Translation Can Occur
The current [OAuth Identity and Authorization Chaining Across Domains](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/) framework permits claims, including the subject identifier, to be translated during issuance of the cross-domain JWT authorization grant or when the destination Authorization Server issues its access token. In either placement, an Authorization Server trusted for the mapping performs the translation.
This is already taking shape as two sibling profiles of that framework, and both translate the subject rather than narrow it. The ID-JAG profile roots the chain in a human end-user's ID Token or SAML assertion under a pre-existing SSO relationship. The [Transaction Token Authorization Grant Profile](https://datatracker.ietf.org/doc/draft-fletcher-transaction-token-chaining-profile/) roots it in a Transaction Token, where the initiating principal may be a human, a system, or a workload, and crosses domains under a bilateral Cross-Domain Trust Agreement. That profile is explicit about the invariant: before it mints the cross-domain grant, the originating Authorization Server MUST translate the subject into a form meaningful and authorized in the destination domain. That is re-subjecting as a normative requirement, performed by the party trusted for the mapping.
Under the same-IdP SaaS assumption, two useful topologies are:
## Caller-pushed continuation
When the expense app needs to call the ledger, it returns to the IdP with fresh, sender-constrained evidence of the inbound delegated context and requests an audience-scoped assertion for the ledger's Resource Authorization Server. The IdP verifies that the chain may continue, resolves the target subject, and mints the assertion. The expense app presents that assertion to the ledger's Resource Authorization Server as a JWT authorization grant (RFC 7523), which applies local policy and mints the access token.
The current [ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) draft accepts an OpenID Connect ID Token, SAML assertion, or optionally an IdP refresh token as the Token Exchange `subject_token`. It does not yet define the downstream continuation evidence needed when an intermediate SaaS has none of those artifacts.
The [Identity Continuation Assertion](https://mcguinness.github.io/draft-mcguinness-oauth-id-continuation-assertion/draft-mcguinness-oauth-id-continuation-assertion.html) fills that specific gap: a short-lived, sender-constrained assertion presented to the IdP only to request an onward ID-JAG. It is not itself an ID-JAG and does not grant API access.
This topology gives the IdP a fresh policy point at each continuation. Revocation, delegation state, actor authority, requested audience, and requested scope can all be evaluated before new identity evidence is issued.
## Resource-pulled resolution
The caller can instead present a sender-constrained continuation reference to the destination, and the destination can resolve it through a trusted broker or an introspection endpoint (RFC 7662). The broker still owns the mapping and the destination still makes the local authorization decision. The network direction changes; the trust invariant does not.
This topology can simplify the caller, but its privacy properties depend on the reference design. A universal reference visible to every application becomes a cross-context correlator. Audience-pairwise references avoid that direct linkage, but require the broker to maintain and resolve the projections.
These are not the only possible deployment topologies. Federation brokers, pre-provisioned target grants, and bilateral mappings can place the work differently. They remain valid only when the party performing subject translation is trusted to make that binding.
# Privacy Is an Artifact Property
Push is not automatically private, and pull is not automatically correlating. Privacy depends on which identifiers each artifact reveals to which party.
An ID-JAG is visible to the client that presents it. Depending on the deployment, it may carry the subject as `sub`, a target-audience-scoped subject identifier, a structured `sub_id` (RFC 9493), email, tenant information, or other subject-resolution claims. The IdP should include only the identifiers required by the target Resource Authorization Server and should avoid exposing a target-local identifier to intermediaries that do not need it.
The same rule applies to continuation references:
> Every intermediary-visible artifact should be audience-minimized and should avoid identifiers that let the intermediary link subject namespaces it was not already authorized to correlate.
The cleanest design keeps the canonical mapping inside the identity control plane and exposes only audience-specific projections. That principle applies whether the continuation request is pushed by the caller or pulled by the destination.
# Takeaway
Crossing a trust boundary and crossing a subject namespace are related, but they are not identical.
A destination Authorization Server may need to mint a new grant or access token because it owns local authorization. When the destination also names the user differently, an IdP or broker trusted for subject resolution must mint or validate new target-scoped identity evidence. Offline attenuation can narrow authority; it cannot manufacture that identity binding.
Design the chain around that invariant. Authenticate the workload, preserve the delegated user context, keep subject translation with an authority trusted for the target namespace, and let the destination Authorization Server make the final access decision.
The test is simple. If the next hop renames the user, you are minting, not narrowing, and you cannot narrow your way into a subject you were never given. Design for the mint.
---
# Authorization Denied Is No Longer Enough
Canonical URL: https://notes.karlmcguinness.com/notes/authorization-denied-is-no-longer-enough/
Markdown URL: https://notes.karlmcguinness.com/notes/authorization-denied-is-no-longer-enough.md
Most authorization systems still treat denial as the end of the interaction.
```json
{ "decision": false }
```
That model worked reasonably well in closed-world systems. Resources were known ahead of time. Applications were pre-integrated. Trust relationships were static. Permissions were provisioned before execution. Workflows were predictable enough that a denial usually meant the request was simply over.
Modern systems increasingly do not work that way.
Agents discover tools dynamically. Sub-agents delegate work downstream. Workflows cross trust boundaries. Resources appear at runtime. Missions evolve after they start. A request that is not authorized right now may still be legitimate if the right governance event occurs.
In that environment, a deny is often not a full stop. It is the beginning of an escalation process.
A support agent acting on behalf of an engineer may be authorized to investigate a tenant's billing issue, then discover that the cause sits in a restricted payment-provider log outside its current mandate. Reauthenticating the engineer does not answer the real question. The question is whether this mission should expand to include that tenant, that log, that operation, and that time window. A requestable denial lets the policy decision point (PDP) pause the workflow, bind the denial to the original evaluation, obtain a scoped governance decision, and reevaluate before access continues.
That is the missing primitive: not an approval flow, but a requestable denial.
The protocol move is a standardized way for a PDP to say: deny, but escalate.
That realization is what led me to start working on what is now the [AuthZEN Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html), an extension profile to the [OpenID AuthZEN Authorization API](https://openid.net/wg/authzen/) for representing requestable denials, submitting access requests, and reevaluating authorization after a governance workflow completes.
But the more I worked on it, the more I realized this is not really about "access requests." It is about something larger: authorization escalation interoperability.
That framing has now been validated externally. In May 2026, the OpenID AuthZEN Working Group adopted this work as a working group draft. Adoption is the working group signaling that requestable denials and authorization escalation are a real, in-charter gap worth standardizing, not a vendor-specific concern. The AuthZEN charter exists to make authorization interoperable across policy decision points and enforcement points through the Authorization API. Denial is part of that decision surface, so a standardized way to say "deny, but escalate" is a direct extension of what AuthZEN already standardizes rather than a departure from it.
# Open-World Systems Break Static Authorization
Traditional authorization systems assume the authority envelope can be determined upfront: which resources will be accessed, which operations will occur, which delegation paths will exist, and which trust domains will participate.
That assumption breaks down quickly once agents enter the picture.
As discussed in [OAuth for Open-World Ecosystems](/notes/oauth-for-open-world-ecosystems/), modern agents increasingly discover APIs dynamically, interact with previously unknown systems, expand workflows during execution, delegate tasks downstream, and cross organizational trust boundaries. An agent may begin with a valid authorization scope and still encounter new runtime conditions: a newly discovered mailbox, a downstream SaaS API, a stronger operation, a higher-risk dataset, a new tenant boundary, or a delegated execution path.
The system now needs to answer a much harder question than "Is this token valid?"
It must answer "Should authority continue under these newly discovered runtime conditions?"
That is not authentication. It is runtime governance: deciding whether authority should change, not checking rights the agent already holds.
# The Missing Layer Between Denial and Governance
Most authorization systems today have a structural gap. A decision point can say a few things: allow, allow with obligations the enforcement point must satisfy, or deny. What it cannot say in a standard way is:
> "Access is currently denied, but governance escalation is available."
That last shape is a distinct decision, not a fuzzy one. A requestable denial is still a denial: the answer right now is no, and access does not proceed. It is not `indeterminate`, not "try again," and not "need more information." What separates it from a hard denial is a bound, explicit escalation path. A hard denial offers nothing to ask for. A requestable denial names where to ask and binds the request to the exact evaluation that was denied.
That missing layer becomes important when authorization is no longer a single synchronous yes-or-no event. Agents, delegated execution, runtime discovery, mission expansion, and continuous governance all need a way to pause, ask for additional authority, and later return to the policy decision point with evidence that the governance event occurred.
Today, that escalation is usually proprietary: custom approval APIs, ticketing integrations, internal workflow systems, ad hoc polling mechanisms, and vendor-specific governance models. In practice it is often less structured than that. An agent hits a wall, a human gets paged in Slack, someone glances at the request and clicks approve, and the agent is waved through by flipping a flag or widening an allowlist. The approval is a message in a channel. It is not bound to the denial, it is not re-checked when the agent actually proceeds, and it quietly becomes a standing exception nobody revisits. Every platform invents its own version of this, and the result is fragmented governance interoperability.
The [AARM control plane](https://aarm.dev/spec) (Autonomous Action Runtime Management) intercepts every agent action before execution and resolves it to one of five decisions: allow, deny, modify, step-up, or defer. The last two are where a denial stops being the end of the story: step-up requires human approval before the action proceeds, and defer holds it pending more context. A requestable denial frames the other side of that boundary at the authorization layer: how the policy decision signals that escalation is available, how it is bound to the original evaluation, and how the result returns for reevaluation.
AARM decides that an action should pause. This draft standardizes how that pause is expressed and resolved.
# What the Draft Actually Does
The goal of the [AuthZEN Access Request and Approval Profile](https://openid.github.io/authzen/authzen-access-request-approval-profile-1_0.html) is not to standardize workflow engines or approval systems. It standardizes the interoperability layer around authorization escalation.
Walk it through with the support agent from earlier. It is investigating the billing issue and tries to read the restricted payment-provider log. The PDP returns a bare denial:
```json
{ "decision": false }
```
Nothing in that response separates "never" from "not yet." The profile turns it into a requestable denial that states the reason, names the escalation endpoint, and binds the request to this exact evaluation:
```json
{
"decision": false,
"context": {
"evaluation_id": "eval_01HX4Y2P8BQ4Y3F0V0K9D6Z7M1",
"reason": "approval_required",
"access_request": {
"endpoint": "https://pdp.example.com/access/v1/requests",
"template": "payment_log_access",
"expires_in": 600,
"request_context": "eyJhbGciOiJFUzI1NiIsImtpZCI6InBkcC0xIn0..."
}
}
}
```
The authorization system is no longer returning only "No." It is returning "No, but governance escalation is available."
The policy enforcement point (PEP) submits an access request to that `endpoint`, echoing `evaluation_id` and `request_context` so the request is tied back to the denial. It receives a Task Handle and polls until the task is terminal. When the engineer's manager approves the scoped expansion, the result carries an approval reference such as `apr_01J9F30RBX`.
Then comes the step that matters. The PEP does not proceed on the strength of the approval. It issues a new AuthZEN evaluation for the same access, carrying the approval as an input:
```json
{
"subject": { "type": "agent", "id": "support-agent-7" },
"resource": { "type": "log", "id": "payment-provider/tenant-42" },
"action": { "name": "read" },
"context": { "approval": "apr_01J9F30RBX" }
}
```
The PDP reevaluates against current policy, current subject state, and current risk, and only then returns:
```json
{ "decision": true }
```
If the mission had ended, the risk posture had shifted, or the approval had expired in the meantime, that same reevaluation returns `false` again. The approval bought a fresh decision, not a standing key.
The base profile defines exactly one completion mode: `reevaluate`. The PDP remains authoritative end to end. An approval is not an access grant, and it is not a capability token: it does not travel forward as bearer authority to be replayed later. It is an input that current policy can choose to honor or reject at the moment of access.
That is a deliberate defense against the time-of-check to time-of-use (TOCTOU) gap. The dominant failure in ticket-based access is exactly this gap: an approval granted at one moment becomes a standing entitlement still usable hours or days later, after the risk posture, the revocation state, or the mission that justified it has changed. Reevaluation forces the decision to be made again, against current state, every time the approval is presented. The obvious objection is cost. Reevaluation is another policy decision, but it runs once per escalation rather than once per request, on a PDP that is already in the path for the original deny. Paying for that fresh decision is the point.
That changes denial from a terminal state into a governable lifecycle transition without changing what a denial means.
# This Is Not Workflow Standardization
One of the most important constraints of the draft is what it intentionally does not standardize. The draft does not define workflow engines, business process management (BPM) orchestration, approver routing, entitlement provisioning, governance policy languages, UI semantics, or human interaction models. Those remain implementation-specific.
What the draft standardizes is narrower:
- the shape of a requestable denial in an AuthZEN decision response
- how an access request is submitted, with denial binding and idempotency
- the Task Handle, lifecycle states, and polling semantics
- terminal-state completion semantics, including reevaluation against current policy
That separation matters. An enterprise may implement approvals using ServiceNow, SailPoint, Okta workflows, a custom governance system, a risk engine, an AI supervisor, a delegated approver, or a chain of policy evaluators. The interoperability problem exists independently of the workflow engine behind it.
The draft keeps catalog references outside the form schema so the schema remains a pure description of data shape. It also does not define a UI rendering vocabulary or an agent protocol surface. The handoff is the surface area. Everything else stays local.
# Why CIBA Is Not the Solution
Whenever asynchronous approval flows come up, someone eventually asks, "Why not just use Client-Initiated Backchannel Authentication (CIBA)?"
At first glance, [CIBA](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) appears close. Asynchronous interaction. Delayed completion. Polling semantics. Out-of-band approval. But CIBA solves a fundamentally different problem.
CIBA is asynchronous authentication and consent for OAuth grant issuance. The successful terminal state is a token. The user proves they are present and consenting, and the authorization server issues a credential.
Authorization escalation governance is different. The modern problem is often not "authenticate the user again." The harder problem is "should authority continue under newly discovered runtime conditions?"
| Question | CIBA | Authorization escalation |
| --- | --- | --- |
| Missing input | Fresh user authentication or consent | Governance state |
| Terminal result | Token issuance | Reevaluation input |
| Authority source | Authorization server grant flow | PDP policy decision |
| Best fit | Fresh login or grant consent | Runtime authority expansion |
In this situation the agent already holds valid tokens and operates under an approved mission. Re-running an authentication flow changes none of that. The missing input is governance state: whether authority should expand now that boundaries, delegation paths, or risk have shifted.
The draft intentionally avoids coupling approval directly to grant issuance. The lifecycle is:
1. authorization denied
2. escalation initiated against the denial binding
3. governance workflow executes
4. authorization context changes
5. authorization reevaluated under current policy
That distinction preserves continuous evaluation, revocation, runtime policy, dynamic governance, and mission-bound authority. Approval alone does not create durable authority. The authorization system remains authoritative at evaluation time. That keeps the policy engine in charge of the access decision rather than the workflow engine.
CIBA still has its place. When the missing input really is fresh user authentication or consent for a grant, CIBA is the right tool. When the missing input is governance state, it is not.
# Sessions Are Not Missions, and Approvals Are Not Authority
An approval answers whether a governance event occurred. A mission answers whether that event still justifies authority right now. The reevaluation step in the draft keeps those two questions separate. The approval is an input to policy. Policy decides whether the input still holds.
This is the same shape as the argument in [Sessions Are Not Missions](/notes/sessions-are-not-missions/). Modern agent runtimes increasingly support durable execution: resumable sessions, background jobs, distributed orchestration, asynchronous continuation, and sub-agent execution. Runtime durability is not governance durability. A session answers where the agent can continue working. A mission answers why the agent is allowed to keep working.
An agent may remain authenticated, maintain valid sessions, and preserve runtime continuity while still requiring governance escalation because new resources were discovered, stronger operations emerged, downstream delegation occurred, trust boundaries changed, or mission scope expanded. Authentication continuity is not governance continuity. An approval signed an hour ago is not a substitute for evaluating against current state.
This is also why the Task Handle in the draft is opaque and portable. It survives PEP restart and can be passed between systems. A long-running agent that gets denied, escalates, hands work off to a sub-agent, and resumes on a different node should not need to rebuild the request from scratch. The handle is a runtime artifact for tracking the escalation. The mission, the approval, and the reevaluated decision are the governance artifacts that decide whether work continues.
# Authorization Escalation as a Runtime Primitive
The deeper pattern emerging across modern systems is this: authority increasingly outlives the moment that originally justified it.
A static authorization decision made at one point in time becomes insufficient once execution continues for hours or days, resources are discovered dynamically, delegation chains evolve, risk posture changes, organizational state changes, or mission scope expands.
This is the same gap [From Passports to Power of Attorney](/notes/from-passports-to-power-of-attorney/) names from the credential side and [Governing the Stay, Not Just the Entry](/notes/governing-the-stay-not-just-the-entry/) names from the control-plane side. The badge is not the work order. The token is not the mandate. The session is not the mission.
A standardized escalation handoff lets each of those layers stay honest. The PDP can say "deny, but escalate" without inventing its own approval API. The mandate service ([Power of Attorney series](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/)) can be the system of record for whether the mission still holds. The workflow engine can remain the system of record for how an approval was reached. The agent runtime can carry a Task Handle without becoming the source of truth for authority.
What this layer needs to be:
- **A protocol surface, not a workflow language.** Approval routing, escalation policy, and approver selection stay implementation-specific.
- **Policy-authoritative on completion.** The PDP reevaluates against current state. An approval is an input, not a verdict.
- **Lifecycle-explicit.** Pending, approved, denied, expired, cancelled, failed, partial. Unknown states are not approval.
- **Bindable to the denial.** Either a verifiable evaluation identifier or a signed request context, never a floating approval against an unbound denial.
- **Portable.** Handles survive runtime churn and cross process boundaries, because the agents that need this most are the ones that do not stay on one node.
None of those properties are workflow features. They are interoperability properties.
# What This Is Converging Toward
The traditional model:
```json
{ "decision": false }
```
is becoming insufficient for agents, runtime delegation, dynamic resource discovery, mission expansion, and continuous governance.
The future increasingly looks more like:
```json
{
"decision": false,
"context": {
"reason": "approval_required",
"access_request": { "endpoint": "...", "template": "..." }
}
}
```
followed later by a reevaluation against the resulting approval. Current policy decides whether the approval still holds.
That is the direction this work is converging toward: not "access requests" as a workflow primitive, and not "approval flows" as a vendor feature, but a standardized interoperability layer for authorization escalation, runtime governance, and mission continuity.
The protocol surface is small on purpose. The hard work of deciding who approves what, under which policy, against which mission, with which evidence, stays exactly where it belongs: not in the authorization API, not in the workflow engine, but in the layer that owns mission authority.
This is a working group draft for a reason. The edges still need to be battle-tested. Whether you are building agent runtimes, high-throughput API gateways, or access-request and approval products, I want to know where this handoff breaks down. Join the conversation in the [OpenID AuthZEN Working Group](https://openid.net/wg/authzen/), or open an issue on [GitHub](https://github.com/openid/authzen).
Denial is no longer always the end of the interaction. It is becoming a state in a governable lifecycle. The protocol needs to recognize that, and only that. The rest of the stack can then stop reinventing it.
---
# SAML at the Post-Quantum Crossroads
Canonical URL: https://notes.karlmcguinness.com/notes/saml-at-the-post-quantum-crossroads/
Markdown URL: https://notes.karlmcguinness.com/notes/saml-at-the-post-quantum-crossroads.md
The enterprise technology industry has been here before. SHA-1 certificate deprecation and TLS 1.0/1.1 removal were both deferred across the industry until browser vendors coordinated hard deadlines that made non-compliance a user-visible problem rather than a future risk. The organizations that treated those transitions as somebody else's future problem were the ones scrambling when the deadline arrived. The technology to migrate had existed for years. The pressure to act had not.
What distinguished the transitions that went well from the ones that did not was whether the migration path was built into the technology itself. SHA-2 and SHA-1 could coexist during transition because PKI clients and certificate chains could tolerate mixed algorithm use while issuers, subscribers, and relying parties moved on different schedules. The transitions that succeeded with the least disruption were the ones where parallel operation was possible: old trust and new trust recognized simultaneously, with a defined handoff point rather than a hard cutover.
SAML is approaching a version of that deadline, and the migration path does not yet exist in standardized form. A SaaS vendor may have hundreds of enterprise SAML customers: some using persistent NameIDs, some using email addresses, some using custom attributes, some depending on IdP-initiated launch, and many relying on undocumented group-to-role mappings. The vendor can add OIDC for new customers, but it cannot simply switch existing tenants without risking duplicate accounts, broken administrator access, and failed audits.
That is the core problem: identity contract continuity. The hard part is not adding another login protocol. It is proving that the new OIDC relationship is the successor to the old SAML federation without changing the subject, audience, attribute, or authorization semantics the application already depends on.
I have been working on a draft specification, the [OpenID Connect Migration Profile for SAML 2.0 Service Providers](https://mcguinness.github.io/connect-saml-profiles/draft-connect-saml-migration-profile.html), to address that directly. It is now part of a broader set of [interoperability profiles](https://github.com/mcguinness/connect-saml-profiles) for SAML and OpenID Connect coexistence, because migration in practice is never a single hard cutover. This post explains why the problem is harder than it looks and where the specifications need review from people who will actually have to execute the migration.
The pressure comes from two directions that compound each other. XML Signature complexity has produced a sustained vulnerability class across SAML implementations, and most of the library stacks enterprises depend on are in various states of deferred maintenance. Post-quantum migration now adds a hard coordination deadline on top of that deferred debt. The organizations with the most SAML maintenance debt are the least equipped to execute a coordinated algorithm migration when the timeline becomes binding. That is not a coincidence. It is the same organizations that treated SAML as a procurement checkbox in the early 2010s who are now running aging library stacks with limited institutional knowledge of how to change them safely.
Post-quantum migration does not create new migration obligations for SAML estates. It converts deferred maintenance into a hard deadline.
OpenID Connect is not new. The OpenID Foundation [ratified the core specification in February 2014](https://openid.net/2014/02/). In October 2024, [nine OpenID Connect specifications were published as ISO/IEC international standards](https://openid.net/10-years-on-openidconnect-published-as-iso-spec/). The protocol is mature, widely deployed, and built on the OAuth 2.0 stack with more than a decade of enterprise deployment behind it. And yet SAML is still the enterprise default for SSO.
# Why SAML Still Wins
SAML remains dominant because the enterprise buying and operating model was built around it.
Many RFPs still ask for SAML by name. Compliance teams know how to evaluate it. SaaS vendors know that if they do not support SAML, they may lose enterprise deals before a security architect ever reads their OIDC documentation. Identity teams have years of runbooks for exchanging metadata, rotating certificates, mapping attributes, testing IdP-initiated and SP-initiated login, and recovering from lockouts. For browser-based workforce SSO, SAML is often boring in the best sense: it is well understood, broadly supported, and easy for enterprise administrators to reason about.
There is also a security intuition behind SAML that should not be dismissed. In a typical SAML deployment, the service provider pins trust to an IdP signing certificate or metadata document controlled by the customer. The customer can often bring their own PKI, rotate keys through a known administrative process, and reason about the blast radius of a compromised signing key in a way that feels concrete. OIDC, by contrast, is often perceived as more dynamic: the relying party discovers issuer metadata, follows a JWKS URL, and validates JWTs against keys published by the issuer. That model is mature and secure when implemented correctly, but a certificate uploaded into an admin console looks like a boundary. A URL that returns keys looks like a dependency.
SAML has not failed. The environment around it has. But enterprise familiarity is not the same thing as long-term safety. The question is no longer whether SAML can keep working. It is whether SAML should keep absorbing new enterprise identity requirements that its trust model was never designed to carry.
# The Assertion Ceiling
SAML is a presentation layer for authentication. The assertion is the relationship.
When a user logs in, the IdP constructs an assertion encoding what it knows about that user at that moment: a subject identifier, authentication context, and whatever attributes the integration was configured to include. The service provider validates it, maps the claims into a local session, and moves on. That is the protocol boundary. There is no ongoing connection, no shared API surface, and no standardized way for the service provider to query the IdP after the assertion is consumed.
That boundary matters because the enterprise identity graph is larger and more dynamic than any assertion can carry. The organizational context that enterprise applications actually need (reporting structures, cost center assignments, entitlements across systems, device posture, group memberships that change as people move roles) cannot be practically embedded in a login-time assertion. Either the IdP preloads every attribute every application might ever need, or each application maintains its own copy through a separate provisioning channel, or it works with incomplete information about the user it just authenticated.
An application that needs to verify current entitlements before authorizing a sensitive operation cannot query the IdP through SAML. A service that wants to publish an access event back to the identity system has no standard SAML channel to do so. A policy engine that wants to evaluate real-time identity context at authorization time cannot call the IdP the way it would call any other API. Each of these capabilities requires a separate integration with a proprietary vendor API, a custom side channel, or nothing happens.
OIDC changes this because the service provider is an OAuth client. The IdP is an authorization server that can issue scoped, audience-bound, revocable access tokens the SP uses to call APIs on the user's behalf when those APIs and scopes are part of the enterprise profile: richer profile queries, organizational data, entitlement lookups, event subscriptions.
The trust model is delegation-shaped, not assertion-shaped. SAML authenticates the user into the application. OIDC can also authorize the application to call identity APIs after login. Where SAML makes a one-time statement that the SP consumes and discards, OAuth tokens explicitly bind what the SP is allowed to do, against which APIs, for how long, and on whose behalf, with revocation as a first-class operation rather than an out-of-band hope. The authentication exchange can become an entry point, not a ceiling.
This is part of what IPSIE is defining: not just how OIDC handles SSO, but how the IdP's API surface covers entitlements, lifecycle events, risk signal sharing, and the enterprise identity relationships that cannot live in an assertion. SAML provides no equivalent foundation.
| Dimension | SAML | Enterprise OIDC profile |
| --- | --- | --- |
| Primary artifact | Login-time assertion | ID token plus scoped access tokens |
| Trust shape | Assertion consumption | Delegated API access |
| Post-login identity context | Out of band | Profiled API surface |
| Key discovery and rotation | Metadata and certificate distribution | Issuer metadata and JWKS |
| Migration risk | Subject and attribute continuity | Requires explicit succession semantics |
# The XML Problem
The issue is not that SAML is old. Old protocols can be excellent. The issue is the surface area created by XML and XML Signature.
[SAML 2.0](https://www.oasis-open.org/standard/saml/) is an OASIS standard for XML-encoded identity assertions, and its security depends heavily on correct XML canonicalization, reference resolution, ID handling, signature validation, and claim extraction. The relying party must not merely check that some signature is valid. It must ensure the signed element is the exact element whose claims are processed. That distinction sounds obvious until you look at the history of XML Signature wrapping and SAML validation bugs.
XML Signature signs specific nodes within a document, not the document as a whole. A wrapping attack inserts a second, unsigned copy of the assertion outside the signed scope. If the validator and the application disagree about which element to read, the signature check passes against the legitimate element while the application consumes claims from the attacker-controlled copy. The signed element is valid. The claims the application acts on are not from it.
The classic 2012 USENIX paper ["On Breaking SAML: Be Whoever You Want to Be"](https://www.usenix.org/conference/usenixsecurity12/technical-sessions/presentation/somorovsky) analyzed 14 SAML frameworks and found critical XML Signature wrapping vulnerabilities in 11 of them. In 2018, [Duo Labs disclosed SAML vulnerabilities affecting multiple implementations](https://duo.com/blog/duo-finds-saml-vulnerabilities-affecting-multiple-implementations), including OneLogin libraries, OmniAuth-SAML, Shibboleth, and Duo Network Gateway. The mechanics differed across disclosures, but the security failure was the same: the implementation accepted one signed identity context while the application acted on another, allowing an attacker with a legitimate account in the same federation to authenticate as another user, including an administrator.
The pattern did not stop with the 2018 disclosures. In September 2024, a maximum-severity authentication bypass (CVE-2024-45409, CVSS 10.0) was disclosed in ruby-saml, a widely deployed library used by GitLab and other applications. The root cause was the same: the library failed to enforce that the elements it extracted claims from were covered by the signature it validated. An attacker with access to any SAML document legitimately signed by the IdP could forge a SAML response and authenticate as any user, including administrators, without that user's credentials. Within weeks, working exploit code was published, turning a structural flaw into a point-and-click bypass. Additional critical ruby-saml disclosures followed in 2025, including [CVE-2025-25291](https://rubysec.com/advisories/CVE-2025-25291/) and [CVE-2025-25292](https://rubysec.com/advisories/CVE-2025-25292/), where parser differentials again allowed signature wrapping and authentication bypass. The twelve-year span from the original USENIX paper to the present has not resolved the structural problem. It has confirmed it.
This vulnerability record points to a structural supply-chain problem more serious than any individual disclosure. Most SaaS SAML implementations were built on open-source toolkits assembled during the early-to-mid 2010s, when enterprise SAML support was a procurement gate and implementation speed mattered more than long-term maintenance posture. The dependency graph runs from the SaaS product down through a SAML library, through an XML processing library, and into platform XML infrastructure. Each layer has its own release cadence, its own CVE history, and its own patch requirements.
The maintenance dynamic compounds the risk. SAML support is typically treated as a procurement feature: once the enterprise requirement is satisfied, the implementation joins the maintenance queue. In many organizations, the engineers who built the original SAML stack are no longer at the company. The XMLDSIG expertise required to safely evaluate a new vulnerability disclosure is not retained. A new CVE in a SAML library triggers a dependency update process, not a security design review, because the institutional context for a design review no longer exists.
This is what makes the XML problem structurally different from a standard software vulnerability. It is not one flaw in one implementation that can be patched. It is a property of XMLDSIG that creates a high-complexity validation problem for every implementation, reproduced across Java, Python, Ruby, PHP, and .NET, across different teams, in different decades, sustained indefinitely by a supply chain that treats SAML as a procurement checkbox rather than a security subsystem.
# Post-Quantum Raises the Stakes
Post-quantum cryptography changes the SAML conversation because authentication signatures are squarely in the migration scope. The question is not just whether new algorithms can be added. It is whether the existing SAML estate can move to them before deferred maintenance becomes a compliance and availability problem.
NIST finalized its first post-quantum signature standards in August 2024: [FIPS 204 (ML-DSA)](https://csrc.nist.gov/pubs/fips/204/final) and [FIPS 205 (SLH-DSA)](https://csrc.nist.gov/pubs/fips/205/final). NIST's draft transition guidance, [IR 8547](https://csrc.nist.gov/pubs/ir/8547/ipd), identifies RSA, ECDSA, and EdDSA as quantum-vulnerable signature standards and sketches a migration path away from them. Cloudflare has published a [2029 target for full post-quantum security](https://blog.cloudflare.com/post-quantum-roadmap/) across its infrastructure, including authentication. The direction is not speculative. The question for enterprise SAML estates is not whether authentication signatures need to move, but whether the SAML implementation substrate can execute that migration.
Federal agencies are already expected to inventory cryptographic dependencies. Enterprise identity platform procurement and migration cycles can run years for anything touching thousands of integrations. If algorithm deprecation requirements harden around 2030, the window to begin planning is already open. For organizations subject to federal compliance requirements like CMMC and FedRAMP, the pressure is more concrete: if those requirements arrive before the SAML implementation stack in production can support ML-DSA or SLH-DSA, the enterprise SSO infrastructure chosen for its auditability becomes a compliance liability.
Migrating SAML to post-quantum signatures means updating the signing algorithm in the IdP, distributing new public key material to every service provider's trust configuration, updating the SAML library to implement the new algorithm, updating the XML Signature algorithm identifiers used in assertions, and ensuring every validation path in every relying party correctly processes the new key type and signature format. For large academic and research federations like InCommon in North America and eduGAIN across Europe, the coordination scope is far larger. InCommon alone connects more than 1,000 participating organizations through a centralized metadata authority, and eduGAIN federates thousands more across Europe. The centralized metadata model that makes SAML federation manageable in normal operations becomes an amplification factor for migration risk when a disruptive algorithm transition is required.
OIDC and OAuth sit on top of the JOSE family of standards (JWS, JWE, JWT, JWK, JWA), and the [IANA JOSE algorithm registry](https://www.iana.org/assignments/jose/jose.xhtml) is the extensibility point that lets new signature algorithms be added without protocol changes. A new signature algorithm needs a JWA identifier, a JWK representation, JWKS publication, and updated validation logic. The ecosystem plumbing for discovery, key rotation, JWKS caching, and metadata is already built to accommodate algorithm transitions.
That extensibility has now produced a concrete post-quantum standard. [RFC 9964](https://www.rfc-editor.org/rfc/rfc9964.html), published on the IETF Standards Track in May 2026, specifies ML-DSA (FIPS 204) for JOSE and COSE: algorithm identifiers, JWK key representations, signature encoding, and the ML-DSA-44, ML-DSA-65, and ML-DSA-87 parameter variants needed for interoperable implementation. An OIDC issuer can now publish ML-DSA keys at its JWKS endpoint against a published standard rather than a vendor extension, and any conformant relying party can validate them. No equivalent standards-track work exists for XMLDSIG. The XML Signature algorithm registry has no published post-quantum identifiers, no profile defining how ML-DSA or SLH-DSA keys appear in SAML metadata, and no agreed encoding for those signatures on the wire. Enterprise SAML estates needing post-quantum signatures will be waiting on standards work that has not started, while OIDC deployments are waiting on implementation of a standard that already exists.
A post-quantum XMLDSIG profile, whenever it arrives, would not escape the deeper problem. As noted above, XML Signature signs specific nodes rather than the document as a whole, and that node-versus-consumed-element gap is the wrapping class itself. JWS signs the entire token as a single unit, so that class of attack structurally cannot arise. Swapping in ML-DSA changes the signing algorithm; it does not change which bytes are protected. A post-quantum SAML would inherit the same validation surface that produced twelve years of wrapping disclosures, while JWS never had it.
The post-quantum migration for OIDC is not free. RFC 9964 is a target to build to, not a finished deployment. But there is a material difference between an ecosystem where the standard exists and implementations need to catch up, and one where the standards work itself has not yet started. The same SaaS organizations that have not kept their SAML library dependency current for routine CVE patches will face a more complex migration: new algorithm support requiring new library versions, new key management, new certificate infrastructure, and coordination with every customer integration. Unlike a routine CVE patch, there is no option to defer indefinitely. Organizations that deferred longest carry the most concentrated coordination risk when migration timelines become binding.
# Why Migration Is Hard
Saying new SSO integrations should use OIDC is the easy part. Migrating the existing estate is not.
Most enterprise applications in production already have established SAML federations. Those integrations are tied to user identifiers, group mappings, role assignments, SCIM provisioning, app sessions, break-glass processes, help desk runbooks, audit expectations, and contractual support language. Changing the SSO protocol is not a refactor. It is an operational event with outage risk.
From the customer's perspective, the upside often looks small. If the migration goes perfectly, users may see one extra redirect or no visible change at all. If it goes badly, users are locked out, the help desk floods, executives cannot access critical apps, and the identity team owns the incident.
This is why SAML persists even when everyone in the room agrees that OIDC is the modern direction. The migration cost is immediate and local. The security benefit is long-term and systemic. The person approving the migration carries the outage risk while the benefit accrues to a future operating model.
Vendors keep implementing SAML because customers require it. Customers keep requiring SAML because vendors support it and their existing estate runs on it. New vendors that would prefer to implement OIDC still worry that excluding SAML will shrink their enterprise TAM. If they expect to support SAML eventually, they implement it early and add yet another SAML stack to the world.
A single enterprise SAML integration may depend on a NameID format, custom attribute mappings, group-to-role rules, SCIM provisioning assumptions, certificate rotation procedures, IdP-initiated launch behavior, session timeout expectations, and help desk recovery paths. None of those show up in a simple protocol comparison chart. Every one can break during migration.
The missing ingredient is not a better argument for OIDC. It is a migration path that is explicit, standardized, and low-risk enough that the people approving migrations can trust it, and that vendors can build to without inventing a bespoke solution for every customer.
That is the gap the migration profile is trying to standardize: not a new login protocol, but a way to prove that an OIDC client is the successor to an existing SAML service provider relationship.
# Two Things Would Help
## A Secure Enterprise OIDC Profile
The first missing piece is a clearer definition of secure enterprise SSO for OIDC.
The OpenID Foundation's [IPSIE Working Group](https://openid.net/wg/ipsie/) is working toward that: profiles for enterprise identity covering single sign-on, user lifecycle management (SCIM), entitlements, risk signal sharing (via the [Shared Signals Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html) and [CAEP](https://openid.net/specs/openid-caep-1_0.html)), logout, and token revocation, with a charter that explicitly prioritizes secure defaults. That work matters because "supports OIDC" is not a security claim. OIDC has its own failure modes: issuer mix-up, redirect URI mistakes, weak `state` or `nonce` handling, unsafe account linking, tenant-claim misuse, and JWKS caching errors.
OAuth's recent evolution is the closest precedent. [RFC 9700](https://datatracker.ietf.org/doc/html/rfc9700) (BCP 240) updates the OAuth 2.0 security baseline based on more than a decade of deployment experience, and [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) is consolidating those lessons into the core authorization framework by requiring PKCE for all clients, removing weaker grant types like the implicit grant and Resource Owner Password Credentials, and requiring exact redirect URI matching. The [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-2_0-security-02.html) goes further, demonstrating that a hardened OIDC profile can be defined and conformance-tested at scale, originally for high-assurance financial APIs. IPSIE applies the same approach to enterprise SSO. Enterprise buyers need an equivalent baseline for OIDC SSO. Vendors need a target they can build to and demonstrate conformance against. Optionality is where interoperability and security both go to die.
## A Real SAML-to-OIDC Migration Story
The second missing piece is a standardized migration path that preserves identity contract continuity.
It is not enough to tell customers to create a new OIDC application and cut over. Migration has to preserve the identity contract the application already depends on. That means mapping SAML issuers, NameIDs, attributes, groups, and tenant identifiers into OIDC issuer and claim semantics without breaking subject resolution. It means supporting staged rollout, dual configuration, telemetry, rollback, and test modes. At minimum, a credible migration story needs to cover:
- Preserve subject continuity across the protocol switch, accounting for both SAML NameID format variation (persistent, transient, email, X.509 SubjectName) and OIDC's pairwise versus public `sub` semantics, so the application does not accidentally create a second account for the same user.
- Establish the OIDC trust anchors as successors to the existing SAML relationship, with defined bindings from SAML IdP EntityID to OAuth issuer and from SAML SP EntityID to OAuth client identifier, so the new trust relationship is provably the continuation of the existing one rather than a parallel federation.
- Map SAML AudienceRestriction (typically the SP EntityID URI) onto OIDC `aud` (typically a `client_id`) without weakening audience validation in either direction.
- Map SAML attributes, groups, and tenant identifiers into OIDC claims without changing application authorization semantics.
- Define the scope of the migration with respect to the OAuth token model: whether the profile covers ID-token-only authentication, or also access and refresh tokens with their independent lifetimes, scopes, and revocation semantics.
- Support parallel validation during rollout so administrators can test the new OIDC path before disabling SAML.
- Give the application a way to distinguish an intentional protocol migration from accidental or malicious account linking.
The industry does not yet have a standard answer to any of these requirements. Each vendor that has attempted SAML-to-OIDC migration has invented its own approach, with its own subject-mapping logic, its own account-linking semantics, and its own untested edge cases. That bespoke improvisation is what makes migration feel more dangerous than it should be, and it is why some migrations that appeared to succeed produced silent account-linking errors that only surfaced later.
Consider a user whose SAML identity was a persistent, opaque NameID, but whose new OIDC integration resolves identity from the `email` claim. The cutover does not throw an error. It silently creates a second account, while the user's group memberships, entitlements, license assignment, and audit history stay attached to the original. The user logs in, sees an empty workspace, and files a ticket. Nobody connects it to the migration. The failure mode that costs the most is not the one that pages the on-call engineer at cutover. It is the one that surfaces months later as two accounts for one person in an access review, or as orphaned entitlements a compliance audit cannot explain.
The [migration profile](https://mcguinness.github.io/connect-saml-profiles/draft-connect-saml-migration-profile.html) is built for exactly this. It uses RFC 8693 Token Exchange and SAML assertion introspection to bind new OAuth clients to existing SAML SP relationships through registered `saml_sp_entity_id` and `saml_idp_entity_id` metadata, so the trust anchor, audience, and subject derivation carry forward from the original federation rather than being reinvented per vendor. A three-way binding among the authenticated client, the registered SP EntityID, and the assertion audience is what lets applications verify a migration is intentional rather than an accidental, or malicious, account-linking event.
No SAML estate moves all at once, so the migration profile is now one of three [interoperability profiles](https://github.com/mcguinness/connect-saml-profiles) that let old trust and new trust run side by side. Each covers one direction of the transition, and together they describe a staged journey rather than a single switch:
| Profile | Precondition | Postcondition | Outcome and value |
| --- | --- | --- | --- |
| **IdP-backed OP** | SAML IdP is the authoritative authentication authority, with no OIDC entry point | An OpenID Provider sits in front of the SAML IdP and delegates authentication upstream to it | New OIDC apps and relying parties onboard immediately while SAML stays authoritative; OIDC gets a front door with nothing migrated yet, which makes the rest low-risk |
| **Migration** | An existing SAML SP federates with its own NameID, attributes, and trust configuration | The SP is now an OIDC relying party of the OP, bound to its former SAML SP and IdP entity IDs | Each SP moves on its own schedule with subject and audience continuity preserved; no duplicate accounts and no estate-wide cutover |
| **Bridge** | The OP is authoritative, but some SPs cannot migrate (vendor end-of-life, frozen or contractual integrations) | The OP presents a SAML IdP facade, deriving SP-specific NameIDs and attribute release from OIDC claims | Legacy SAML SPs keep working unchanged; no SP is stranded and no forced simultaneous cutover |
The unit of migration is the integration, not the company. Each SP moves when it is ready, and nothing in front of it has to wait.
The phases overlap rather than gate each other, which is the point: old trust and new trust recognized simultaneously, with a defined handoff point rather than a hard cutover, the same parallel operation the SHA-1 transition relied on. The specifications are early. They need review from the identity providers, application vendors, and enterprise architects who know where the real edge cases are.
# What to Do Now
**Enterprise buyers** should stop making SAML the default requirement for new SSO. Treat SAML as a legacy compatibility protocol, while new enterprise SSO requirements should require secure OIDC support with documented SAML-to-OIDC migration semantics. Concretely, replace language like:
> *The solution must support SAML 2.0 for enterprise SSO.*
with:
> *The solution must support SAML 2.0 for enterprise SSO, or an enterprise OIDC profile with documented migration semantics that preserve subject continuity for accounts currently authenticating via SAML. Vendors must demonstrate how existing SAML integrations can be migrated to OIDC without requiring account re-linking or high-risk cutovers.*
The more important question is not whether a vendor supports both protocols, but how they handle migration: does their OIDC implementation preserve subject continuity for accounts that currently authenticate via SAML, or does it require a high-risk cutover? That question, asked early in procurement, changes what vendors prioritize. Buyers should also inventory existing SAML integrations for the ones most likely to become blockers: custom NameID formats, fragile attribute mappings, manual certificate rotation, and undocumented group and role mappings are the integrations that make migration expensive.
**Application vendors** should stop treating SAML as a checkbox dependency. Prefer OIDC for new enterprise SSO, while treating existing SAML support as legacy-critical infrastructure that requires real maintenance. That means dependency tracking, XMLDSIG test coverage, signature-wrapping regression tests, and certificate-rotation hygiene. The test to apply: can you migrate a SAML customer to OIDC without creating a second account or disrupting their group and role assignments? If the answer is no, the OIDC implementation is not production-ready for enterprise migration.
**Identity providers and standards groups** should make migration conformance-testable. IPSIE can define the secure enterprise OIDC baseline. Migration profiles can define how existing SAML relationships move without account-linking ambiguity or high-risk cutovers. Conformance tests can turn those expectations into something verifiable rather than aspirational.
The organizations that start now will have chosen their timeline. The ones that wait will have it chosen for them, by the next signature-validation disclosure, a supply-chain failure in a SAML library they forgot they depended on, or a post-quantum deprecation deadline that arrives before their XMLDSIG stack is ready to move.
The destination is not exotic. It is enterprise SSO on a hardened OIDC profile, with post-quantum signatures published against a standard that already exists, reached by a migration path that carries existing SAML relationships forward without orphaning a single account. The technology to get there is arriving. The only open question is whether the planning starts before the deadline does.
# Get Involved
## SAML 2.0 and OpenID Connect Interoperability Profiles
The [interoperability profiles](https://github.com/mcguinness/connect-saml-profiles) are early-stage drafts, not finished standards. They cover three complementary directions: the [Migration profile](https://mcguinness.github.io/connect-saml-profiles/draft-connect-saml-migration-profile.html) (SAML SP to OIDC), the [Bridge profile](https://mcguinness.github.io/connect-saml-profiles/draft-connect-saml-bridge-profile.html) (an OP serving un-migrated SAML SPs through an IdP facade), and the [IdP-backed OP profile](https://mcguinness.github.io/connect-saml-profiles/draft-connect-saml-idp-profile.html) (an OP delegating authentication to an upstream SAML IdP). The goal is to get the problem definition and requirements in front of people who will actually have to execute this migration, and find out where the approach is wrong before it goes further.
If you work on enterprise identity, one of these questions is probably worth your time:
- **Identity providers**: Does this profile describe what your migration tooling already does? Where does it conflict with your implementation, and where are the gaps?
- **SaaS application vendors**: If you support both SAML and OIDC today, does this profile give you a workable path to migrate existing SAML customers without a high-risk cutover? What is missing?
- **Enterprise architects**: If you are planning or deferring a SAML-to-OIDC migration, does this profile address the identity contract requirements that make migration feel risky?
- **Standards reviewers**: The subject-mapping semantics and trust relationship model should be challenged. If the approach does not hold for your deployment scenario, that feedback is exactly what shapes whether this becomes a useful standard.
Feedback and discussion are open on [GitHub](https://github.com/mcguinness/connect-saml-profiles).
## IPSIE Working Group
The [IPSIE Working Group](https://openid.net/wg/ipsie/) at the OpenID Foundation is defining what secure enterprise OIDC actually means in practice: profiles for SSO, user lifecycle management, entitlements, risk signal sharing, logout, and token revocation. That work is only as good as the enterprise requirements it is built against.
If you are an enterprise buyer with real deployment constraints, an identity provider with implementation experience, or a SaaS vendor who has navigated the gap between "supports OIDC" and "secure for enterprise," the working group needs your input. The specifications produced there will become the baseline that procurement language can require and vendors can build to. Getting the requirements right now is easier than correcting them after conformance tests exist.
Information on participating is available at [openid.net/wg/ipsie](https://openid.net/wg/ipsie/).
---
# The Agent Provider Is the IdP: A Standards Reading of WorkOS auth.md
Canonical URL: https://notes.karlmcguinness.com/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md/
Markdown URL: https://notes.karlmcguinness.com/notes/agent-provider-is-the-idp-standards-reading-of-workos-auth-md.md
When WorkOS published [auth.md](https://workos.com/auth-md/docs), it named a real problem. Agents need an agent-readable way to register with third-party services at runtime, then operate against those services' APIs and tools. The parent `auth.md` file is the service's setup guide for agents: where to discover protected resource metadata, which registration methods are supported, how to claim or verify the relationship, how credentials are used, and how errors and revocation work.
The [agent provider guide](https://workos.com/auth-md/docs/agent-providers) is one part of that larger system. It covers the Agent Verified path, where an agent provider such as OpenAI, Anthropic, Cursor, or a similar platform asserts a user's identity to a downstream service with an ID-JAG. This is the same topology I sketched in [ID-JAG Beyond the Enterprise IdP](/notes/id-jag-beyond-the-enterprise-idp/): the IdP Authorization Server is whoever the resource has chosen to trust for SSO and subject resolution, not just a workforce IdP. That path is the focus of this post because it is where the standards question is most interesting: how should an agent provider perform one-click setup, client establishment, and delegated API access without inventing a new grant layer?
auth.md is useful as an agent-readable onboarding document. Its Agent Verified path already makes the most important standards choice: it reuses ID-JAG. Agent Verified should be an OAuth and OpenID profile, not a new grant surface. The opportunity is to lean further into the ID-JAG choice so auth.md increases the value of the OAuth and OpenID work already underway instead of becoming a long-lived parallel surface. The deployment is new. The grant surface does not have to be.
The most important thing auth.md gets right is the assertion. When auth.md specifies `assertion_type: "urn:ietf:params:oauth:token-type:id-jag"` in its registration request, it is adopting an IETF draft (the [Identity Assertion JWT Authorization Grant](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/), which I co-author) that defines exactly this kind of cross-domain identity hop. ID-JAG names the IdP Authorization Server, the Resource Authorization Server, the audience binding, the typed JWT (`oauth-id-jag+jwt`), the validation rules for the assertion, and the JWT-bearer presentation path that turns the assertion into an access token. It also names the claims the assertion is expected to carry: subject, issuer, audience, jti, expiry, plus the OIDC identity claims (`email`, `email_verified`, `phone_number`, `phone_number_verified`, and so on). Picking the right primitive is the load-bearing decision in any one-click agent setup design, and auth.md picked it.
This post is not arguing for a different assertion. It is arguing for completing the adoption. The agent provider is already signing an ID-JAG; the resource should accept it through the JWT-bearer grant ID-JAG was designed for, at the standard token endpoint, with the standard OAuth error model. The verified-contact requirement should be expressed as a claim predicate on the ID-JAG, not as a parallel assertion type. The revocation surface should ride on the shared identity-event channel ID-JAG's same issuer already authorizes. The hard part is done. The rest is composition.
There is an ecosystem maturity caveat. ID-JAG is still a draft. CIMD is still a draft. OPC is still a draft. A symmetric trusted-issuer discovery mechanism is not written yet. auth.md is therefore not wrong to move ahead of the finished standards stack; products have to ship while standards are catching up. The question is how to move ahead without making the bridge permanent.
Call the missing piece the Agent Onboarding Profile: a profile for runtime agent signup, account linking, client establishment, consent capture, delegated token issuance, events, and lifecycle. It should say how the existing OAuth and OpenID machinery composes into one click. It should not rename the token endpoint, fork the error model, or create a parallel credential exchange. If a deployment needs a setup endpoint, that endpoint should own account and client establishment, not token issuance.
If the user asks an agent to update a CRM record, file a support ticket, or inspect a project board, and that service has never been connected to the agent provider before, the service needs more than an access-token exchange. The default today is for the user to paste an API key into the agent, which moves the consent prompt, the revocation UX, and the delegation audit trail to whichever side the key was generated on. What is needed instead is the agent version of "Sign up with Google": establish the user or tenant relationship, create or bind the OAuth client the agent will use, obtain delegated API access for the relevant tools, and keep the lifecycle manageable after setup.
The OTP-based user-claimed and anonymous paths are useful, but they solve a different bootstrap problem: how an agent can start a registration on behalf of a user who has not yet proved control of an address or account, claim it later when the user does prove control, and upgrade access from there. That is closer to invitation, account-claim, and partial-trust registration work than to ID-JAG. This post focuses on the Agent Verified path.
# What Is Actually New
The deployment is a self-service signup flow where an agent provider, not the user's workforce SSO IdP, is the identity issuer that bootstraps a downstream service relationship. The agent provider acts as the IdP Authorization Server in ID-JAG's terms, even though it is not where the user originally authenticated for workforce SSO. That is exactly what the ID-JAG draft already permits: the IdP Authorization Server is whoever the Resource Authorization Server has chosen to trust for SSO and subject resolution for this hop.
The OpenID Foundation's [FastFed Core 1.0](https://openid.net/specs/fastfed-core-1_0-03.html) tried a version of this for the SaaS era: an IT admin clicks through a handshake at a SaaS that configures SSO and SCIM provisioning in one ceremony. It did not become the default deployment pattern because the admin lane already had vendor-specific console flows and the consumer lane already had Sign in with Google, Sign in with Microsoft, and email signup. The scale was forgiving: a large enterprise federated with hundreds of SaaS apps over its lifetime, each set up once.
The agent onboarding case is a third lane, and the volume is different by orders of magnitude. It is not admin-initiated like FastFed; the user clicks "Connect Notion" at runtime. It is not user-direct like Sign in with Google; the agent acts on the user's behalf and the agent provider attests the user's identity to Notion. Agents may need to connect to many services on demand and establish each relationship in seconds. Human-in-the-loop setup does not scale to that shape. The trust relationship is between the agent provider and the resource, established at the user's request.
Inside that third lane, what is new at the protocol layer is the packaging: one click that creates or links the downstream account, establishes the agent's OAuth client identity, and obtains delegated API access. That is more than a JWT-bearer grant, but it is still a composition of existing standards.
That distinction matters because the OAuth ecosystem's response should be to make the onboarding pattern easy to express in existing standards, not to fork the grant surface for it.
# One-Click Agent Setup
Here is the shape in concrete terms. A user tells Claude, "Connect Notion." Claude is the agent, Anthropic is the agent provider, and Notion is the downstream service. The names are illustrative; the pattern is general.
```mermaid
sequenceDiagram
actor U as User
participant A as Agent
participant AP as Agent Provider / IdP AS
participant N as Notion Resource + AS
participant EL as Events / Lifecycle
U->>A: Connect Notion
A->>N: Discover PRM + AS metadata
N-->>A: Metadata + onboarding profile
A->>AP: Request ID-JAG for Notion context
AP->>U: Prompt for scoped Notion setup consent
U-->>AP: Approve setup
AP-->>A: ID-JAG
A->>N: Begin onboarding with ID-JAG, consent, and client metadata
N->>N: Verify issuer trust
N->>N: Link or create account
N->>N: Register or bind client
N->>N: Record consent and scope
N-->>A: Setup result
A->>N: JWT-bearer grant with ID-JAG
N-->>A: Access token
A->>N: Call tools and APIs with delegated token
N-->>EL: SSF / CAEP events
EL-->>N: OPC lifecycle / RFC 7009 revocation
```
In wire-traffic terms, Claude discovers Notion's RFC 9728 Protected Resource Metadata and RFC 8414 Authorization Server Metadata. Notion advertises an agent onboarding profile and the agent providers or trust mechanisms it accepts. Claude asks Anthropic for an ID-JAG scoped to Notion, Anthropic prompts the user for this specific setup, and Anthropic returns the assertion. Notion verifies the ID-JAG issuer, resolves or creates the local account relationship, registers or binds the agent client, records the user's consent, and returns a setup result. Only after that does Claude use the standard RFC 7523 JWT-bearer grant at Notion's token endpoint to obtain delegated access.
`/agent/auth` is not replaced by a single endpoint. It is decomposed into a profile: discovery, trust, identity assertion, account linking, client establishment, credential issuance, events, and lifecycle. The token endpoint handles token issuance. A setup endpoint, if present, handles the resource-local signup state. Neither endpoint should be forced to carry the whole ceremony.
Layered, the composition looks like this:
| Layer | Standards-based shape |
| --- | --- |
| Discovery | RFC 9728 + RFC 8414 + profile metadata |
| Trust | Federation, registry, or user-mediated trust |
| Identity assertion | ID-JAG issued by the agent provider |
| Account linking | Resource-local user or tenant relationship |
| Client establishment | DCR, CIMD, federation metadata, or client binding |
| Credential issuance | RFC 7523 JWT-bearer grant at the token endpoint |
| Events | SET / SSF / CAEP |
| Lifecycle | OPC |
The same question surfaces in the OAuth-for-MCP conversation. The [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) already leans on OAuth protected resource metadata, authorization server metadata, client ID metadata documents, and dynamic client registration. What it does not define is the same onboarding ceremony at issue here: agent provider trust, account linking, client establishment, consent capture, and delegated tool access as one setup flow. auth.md is one proposed product shape for that problem. [MCP Enterprise Managed Authorization](https://modelcontextprotocol.io/extensions/auth/enterprise-managed-authorization) is another, currently deferring trust setup to manual admin configuration because no discovery mechanism exists yet. The standards answer should make these cases converge, not create separate onboarding rituals for each ecosystem.
# What auth.md Already Gets Right
Before mapping the layers, it is worth being specific about what the proposal gets right, because it is more than the framing.
**The parent artifact.** The top-level `auth.md` file is useful because it gives agents a human-readable but structured setup guide at the service boundary. Protected Resource Metadata remains the runtime source of truth for endpoint URLs and supported flows, while auth.md explains the registration ceremony in a form agents can parse and users or developers can inspect. That is a good product shape.
**Multiple onboarding paths.** auth.md is not only an agent-provider protocol. It includes Agent Verified, user-claimed, and anonymous paths. The user-claimed and anonymous flows are closer to account-claim, invitation, OTP, and partial-trust product ceremonies. The Agent Verified path is the one where the agent provider becomes an identity issuer and where ID-JAG, OAuth metadata, client registration, and federation standards apply directly.
**An opinionated profile.** WorkOS is also right that "just use OAuth" is not enough guidance for integrators. OAuth is a toolbox. One-click agent setup needs a named ceremony that says which documents to fetch, which endpoints to call, what state transitions happen, what the user sees, and how revocation works. WorkOS is optimizing for immediate integrator comprehension: one file, one block, one endpoint, one credential response. That product simplicity is the right instinct. The critique in this post is not that auth.md is too opinionated or that it ignored standards. It is that the profile becomes more valuable if each piece that can be OAuth or OpenID is expressed that way as the ecosystem catches up.
**What "Agent Verified" actually claims.** The brand carries weight because an agent provider can attest things a workforce IdP usually cannot: identity (the user behind this request), agent identity (the registered agent client), agent-user binding (the user linked this agent to their account), and fresh user authorization for this setup action. Only the first is what a generic OIDC IdP attests. The other three are why an agent provider is a credible IdP role, not a marketing label. All four signals fit in ID-JAG's claim space. None of them justifies a new grant type.
**The consent step is real.** In workforce SSO, IT consents on behalf of the user. In "Sign up with Google," the user explicitly chose Google as their issuer for the new account. In Agent Verified, the user consented to the agent at the agent provider, not to the agent provider acting as identity issuer to this specific downstream service. The onboarding profile should make that consent step explicit, scoped to the resource, and revocable independently of the agent's overall access.
auth.md has the right instincts. The mechanics need to be framed as an onboarding composition, not as a new replacement for OAuth's grant and metadata model.
# Discovery and Metadata Are Mostly Already Covered
The parent `auth.md` file points agents at Protected Resource Metadata and the authorization server metadata. The Agent Verified path then introduces a custom `agent_auth` block in the authorization server metadata document. Some fields map directly to existing OAuth metadata. Others are trying to describe the one-click setup profile and should be treated as profile metadata, not as a new parallel discovery system:
| auth.md (custom) | Native standard |
| --- | --- |
| `agent_auth.skill` | Profile identifier URI; native equivalent is a profile URI in `authorization_grant_profiles_supported` (the same shape ID-JAG uses for `urn:ietf:params:oauth:grant-profile:id-jag`) |
| `agent_auth.register_uri` | Onboarding profile endpoint, or a composition of Dynamic Client Registration (RFC 7591), account/tenant creation, and token issuance |
| `agent_auth.claim_uri` | `token_endpoint` for the ID-JAG JWT-bearer grant, once the relationship and client are established |
| `agent_auth.revocation_uri` | `revocation_endpoint` (RFC 8414 plus RFC 7009) |
| `agent_auth.identity_types_supported` | `grant_types_supported` (RFC 8414), plus profile-specific advertising from the ID-JAG draft |
| `agent_auth.identity_assertion.assertion_types_supported` | Resource AS: `authorization_grant_profiles_supported` includes `urn:ietf:params:oauth:grant-profile:id-jag` (ID-JAG ยง7.2), with `grant_types_supported` listing `urn:ietf:params:oauth:grant-type:jwt-bearer`. IdP AS: `identity_chaining_requested_token_types_supported` includes `urn:ietf:params:oauth:token-type:id-jag` (ID-JAG ยง7.1) |
| `agent_auth.identity_assertion.credential_types_supported` | Bearer credential profile semantics; API-key-shaped credentials need lifecycle, display, rotation, and revocation rules, not a new grant type |
| `agent_auth.events_supported` | RFC 8417 SET event types advertised via SSF and CAEP metadata |
At the resource side, [RFC 9728 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728) already solves the protected-resource discovery problem, and auth.md correctly adopts it. At the authorization server, [RFC 8414 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414), extended by the ID-JAG and Identity Chaining drafts, covers the grant and token-exchange advertising.
What remains is profile metadata for agent onboarding: where setup starts, which agent providers are accepted, which client-registration modes are supported, which account-linking semantics apply, and which delegated scopes or tool surfaces can be requested. That metadata should extend the OAuth discovery model rather than replace it.
Discovery should name the ceremony. It should not rename the OAuth endpoints.
# Credential Issuance Should Use the JWT-Bearer Grant
Once setup is complete, auth.md's access exchange uses a custom JSON request to a custom endpoint:
```http
POST /agent/auth HTTP/1.1
Content-Type: application/json
{
"type": "identity_assertion",
"assertion_type": "urn:ietf:params:oauth:token-type:id-jag",
"assertion": "eyJhbGc...",
"requested_credential_type": "access_token"
}
```
The native equivalent, defined by [RFC 7523 JWT Profile for OAuth 2.0 Authorization Grants](https://datatracker.ietf.org/doc/html/rfc7523) and profiled by [ID-JAG ยง4.4](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/), is a form-encoded request to the Resource Authorization Server's standard token endpoint, with the ID-JAG JWT (typed `oauth-id-jag+jwt` per ยง3.1) carried in the `assertion` parameter:
```http
POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=eyJhbGc...
```
For the access-token step, every parameter in the auth.md request collapses into the standard JWT-bearer grant. The `type` parameter, the `assertion_type` parameter, the `requested_credential_type` parameter, and the JSON content type all disappear. Form encoding is the OAuth norm, and the wider OAuth toolchain (client libraries, gateways, audit pipelines) is already wired for it.
The response is a standard OAuth token response: `access_token`, `token_type`, `expires_in`, `scope`. Implementation fields like `registration_id` and `registration_type` are useful inside a deployment but do not belong in the token response surface. An API key is a bearer credential with a long lifetime. Its lifecycle, display, rotation, and revocation are profile concerns, not a reason for a second grant type.
That does not mean runtime signup is solved by the token endpoint alone. It means the token issuance part of runtime signup should use the token endpoint, while account or tenant creation and OAuth client establishment should be profiled explicitly around it.
The token endpoint issues authority. It should not become the signup engine.
# Client Establishment Is Part of Setup
The place auth.md is pointing at something real is the moment before the first token is issued. WorkOS models this as a service-side agent registration and issued credential. A standards profile should say how that registration maps onto OAuth concepts: which client or client instance represents the agent for this user or tenant, how that client is bound to the agent provider, and how the resulting credential is constrained.
In traditional SaaS, the equivalent step is hidden inside signup: the user clicks "Sign up with Google," the SaaS creates the account, and any application-level integration state is created behind the scenes. For agents, that hidden state includes a service-side registration plus an OAuth client, client instance, or client binding that the agent will later use to call APIs.
The standards pieces exist. [OAuth Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) defines how a client can be registered with an authorization server. The [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) draft lets a stable `client_id` point at a signed or hosted metadata document, which is a good fit for agent providers that rotate keys and publish client identity at scale. [OpenID Federation](https://openid.net/specs/openid-federation-1_0.html) can also carry metadata and policy for automated trust and registration in federated deployments.
What is missing is a profile that says how these pieces compose for one-click agent setup: whether the downstream service dynamically registers a per-agent-provider, per-user, or per-tenant client or binds to an existing one; how the ID-JAG subject and tenant context link to the created account; how the client metadata document is validated and bound to the setup transaction; and which scopes, tools, or APIs the user approved during setup.
Beyond that, the profile also has to handle the operational shape: how claim-token or partial-trust state is represented when setup is not yet complete, what audit events are emitted for registration state transitions, which rate limits and abuse controls apply, and how the resulting client and delegated token are revoked, rotated, or migrated later.
That is useful standards work. But it is not a new credential type and not a new grant. It is a runtime onboarding profile around existing OAuth client registration, metadata, identity assertion, and token issuance.
# Revocation Is Two Different Standards Depending on Intent
auth.md collapses two distinct operations into a single `/agent/auth/revoke` endpoint that accepts a `logout+jwt`. Those two operations have separate standards because they answer different questions.
**Revoke a specific token, right now.** That is [RFC 7009 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009) against the standard `revocation_endpoint`. A form-encoded token parameter, no JWT envelope, no event semantics. The endpoint is already advertised by RFC 8414 metadata.
**Signal a cross-domain identity event the resource should care about.** That is the actual semantic auth.md is reaching for: the agent provider knows the user was deprovisioned, or the agent's authority was withdrawn, and downstream services should react. That is [RFC 8417 Security Event Tokens](https://datatracker.ietf.org/doc/html/rfc8417) delivered via [RFC 8935 push](https://datatracker.ietf.org/doc/html/rfc8935) or [RFC 8936 poll](https://datatracker.ietf.org/doc/html/rfc8936), profiled by the OpenID Foundation's [Shared Signals Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html) and [CAEP](https://openid.net/specs/openid-caep-1_0.html). A `logout+jwt` is just a SET carrying a logout event, already covered by [OpenID Connect Back-Channel Logout](https://openid.net/specs/openid-connect-backchannel-1_0.html).
auth.md acknowledges this directly: "Expect this surface to extend with SET / CAEP / RISC event communication." The long-term standards path should move this surface onto SSF and CAEP rather than keep a bespoke revocation endpoint. Resources that already integrate Shared Signals for workforce IdPs should be able to receive agent provider events through the same channel.
# Lifecycle Beyond Revocation
The lifecycle surface in auth.md is one operation wide: the `logout+jwt` revocation. An agent provider acting as the identity issuer for a downstream resource needs to do more than tell the resource a session is over. It needs to provision the user when the relationship starts, update claims when they change, suspend the account when the user is deprovisioned at the agent provider, and delete it when the relationship ends.
[OpenID Provider Commands (OPC)](https://openid.github.io/openid-provider-commands/main.html), a draft I co-author with Dick Hardt, defines a directive channel from the OP to the RP for the full account lifecycle: Activate, Maintain, Suspend, Reactivate, Archive, Restore, Delete, Invalidate, and Migrate. Commands are signed JWTs typed `command+jwt` posted to an RP-advertised `command_endpoint`, with synchronous and asynchronous variants and metadata-based capability negotiation. Tenant-level variants exist for bulk operations across an organizational scope.
The important property for the agent provider case is that **OPC requires no new trust relationship**. The OPC issuer is the same OP that signs ID-JAGs. The same `iss`, the same JWKS, the same key material the resource is already validating for setup and token exchange. Once the resource has decided to trust the agent provider as an IdP Authorization Server for ID-JAG, it has also decided to trust it as the OPC sender. One issuer, one trust relationship, one set of keys.
SSF and CAEP are right for cross-domain risk signals and session-end notifications. OPC is right for lifecycle directives: provisioning, claim updates, suspension, deletion, and migration. Together they cover the event and lifecycle work auth.md points toward without requiring a bespoke revocation surface.
# Claim Negotiation Has a Pending Standards Answer
auth.md treats `verified_email` as a separate `assertion_type` alongside ID-JAG, and pairs it with a `missing_verified_email` error code. The name is narrower than the behavior: WorkOS says a service needs at least one verified contact, such as `email_verified` or `phone_number_verified`, to match or provision a user. Both surfaces are solving the same underlying problem: the resource cannot complete account resolution or JIT account creation without certain identity claims or claim predicates in the assertion, and there is no interoperable way for the resource to tell the IdP which claims it needs.
ID-JAG already carries `email`, `email_verified`, `phone_number`, and `phone_number_verified` per OpenID Connect Core. A "verified email only" assertion is an ID-JAG with `email_verified: true`. The signature, audience, issuer, and replay protections are identical to any other ID-JAG. Treating it as a separate assertion type splits the validation path without adding a new security property.
The real problem is not verified email. It is claim negotiation. Different resources need different claims: email, phone, department, employee number, license tier, custom identifiers. Some requirements are simple claim presence checks. Others are predicates, such as "email must be verified" or "either verified email or verified phone is present." The IdP has no standardized signal from the client about what the target resource requires.
That gap is being addressed inside the ID-JAG draft itself. [Issue #83](https://github.com/oauth-wg/oauth-identity-assertion-authz-grant/issues/83) proposes a small, symmetric extension:
- A new error code, `insufficient_identity_claims`, that the Resource Authorization Server returns when the assertion is cryptographically valid but lacks the claims needed for account resolution or JIT provisioning. The naming follows the RFC 9470 `insufficient_user_authentication` pattern: valid, but not enough.
- A `required_claims` parameter on that error response, naming the missing claims as a space-separated list of claim names, in the same shape OAuth already uses for `scope`.
- A `required_claims` parameter on the client's Token Exchange request to the IdP, letting the client pass the resource's requirement straight through without transformation.
The end-to-end flow becomes: present a minimal ID-JAG, receive `400 insufficient_identity_claims` with `required_claims=email email_verified department employee_number` or a profile-defined verified-contact requirement, ask the IdP for an enriched ID-JAG subject to its own policy, and retry. No separate `verified_email` assertion type. No bespoke `missing_verified_email` error. The same mechanism handles resource-specific identity requirements instead of hard-coding one verified-email branch.
# The Error Catalog Is Already Small on Purpose
auth.md introduces several error codes. Most should map to existing OAuth errors at the protocol layer, even if the onboarding profile keeps product-level recovery states:
| auth.md | RFC 6749 or RFC 9470 |
| --- | --- |
| `invalid_issuer` | `invalid_grant` |
| `invalid_signature` | `invalid_grant` |
| `expired` | `invalid_grant` |
| `replay_detected` | `invalid_grant` |
| `invalid_audience` | `invalid_grant` |
| `invalid_client_id` | `invalid_client` |
| `missing_verified_email` | `insufficient_identity_claims` with `required_claims=email email_verified`, or a profile-defined verified-contact requirement ([ID-JAG draft issue #83](https://github.com/oauth-wg/oauth-identity-assertion-authz-grant/issues/83)) |
| `unsupported_credential_type` | `unsupported_grant_type` or `invalid_scope` |
| `insufficient_user_authentication` | Already RFC 9470, keep as-is |
OAuth deliberately keeps the top-level error catalog small and pushes specifics into `error_description`. The profile may still define recovery states for onboarding, account linking, claim completion, and user-visible remediation. But it should avoid inventing new names for cryptographic, token, and grant failures OAuth already defines. The one row that justifies a distinct protocol signal is `missing_verified_email`, where issue #83 proposes `insufficient_identity_claims` as an interoperable way for the client to retry against the IdP. For the rest, `invalid_grant` plus `error_description` is the OAuth norm, and clients should not branch on error reasons that the spec asks them to treat identically.
# Profile Requirements
ID-JAG is the grant. The Agent Onboarding Profile is the ceremony around it. They are not competitors; they are layers. The profile composes existing specs into a coherent one-click agent-setup flow without adding a new credential exchange. Some profile metadata is still useful, because a client needs to know whether the resource supports this ceremony and which trust, account-linking, and client-establishment modes apply. The constraint should be pragmatic rather than absolutist: use profile glue where the ecosystem has a real gap, but avoid new grant types, new token endpoints, new revocation endpoints, and duplicate names for failures OAuth already defines.
What the deployment profile specifies for each requirement area:
1. **Discovery.** How RFC 9728, RFC 8414, and existing profile metadata advertise setup support.
- PRM and AS Metadata carry endpoint discovery. Profile metadata carries only the ceremony-specific parts: setup support, supported trust mechanisms, account-linking modes, client-establishment modes, delegated credential types, and lifecycle/event support.
- ID-JAG support is advertised with `urn:ietf:params:oauth:grant-profile:id-jag` in `authorization_grant_profiles_supported`. That URI tells a client the resource speaks the protocol. It does not tell the client which issuers, tenants, clients, subjects, audiences, or authorization requests the resource will actually accept. Trust discovery is a separate problem, and the ID-JAG draft is explicit about it.
2. **Trust.** How the service decides which agent providers it accepts.
- Resolved via a companion trusted-issuer-discovery mechanism (see below), OpenID Federation, registry membership, signed metadata, or user-mediated trust.
- If a new `untrusted_issuer` error is needed, it should belong to that companion trust-discovery work, not to the onboarding profile.
3. **Assertion.** How ID-JAG carries the user, tenant, agent-provider, consent, and client-binding context needed for setup.
- ID-JAG draft ยง4 covers validation and claims. The deployment profile clarifies that consent and scope claims attest the user's approval at the agent provider for this audience.
4. **Account linking.** How the service decides whether this is a new relationship, an existing user, or an existing tenant connection.
- Resource resolves the ID-JAG `sub` to a local account or tenant, or JIT-provisions one on first use of (`iss`, `sub`).
- Missing claims return `insufficient_identity_claims` with `required_claims` (defined by ID-JAG issue #83, not this profile).
5. **Client establishment.** How service-side registration maps to a client identity.
- The profile should prefer [CIMD](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) for client identity when there is no prior relationship. The `client_id` is a URL whose document publishes client metadata, and the resource fetches and validates it on first use.
- DCR, pre-registration, federation metadata, or client binding remain valid deployment choices. Client identity can stay singular per agent while per-user or per-tenant state lives in the ID-JAG `sub`, tenant context, and the resource's local account record.
- Because ID-JAG is intended for confidential clients, the profile should say how the agent client proves possession: private-key client authentication, DPoP-style binding, attestation, or another asymmetric mechanism. A public URL-shaped `client_id` is identity metadata, not proof by itself.
6. **Credential issuance.** How the resource authorization server issues delegated access through the JWT-bearer grant.
- Agent presents an ID-JAG at the standard `token_endpoint` via the RFC 7523 JWT-bearer grant (`assertion=`). Standard OAuth token response.
- API-key-shaped bearer credentials, when supported, are returned in the same response with `expires_in` omitted and profile-defined rotation rules.
7. **Lifecycle and events.** How RFC 7009, SET / SSF / CAEP, OPC, and RFC 9470 handle revocation, events, lifecycle directives, and step-up.
- Token revocation uses RFC 7009 at the standard `revocation_endpoint`.
- OPC commands (Activate, Maintain, Suspend, Reactivate, Archive, Restore, Delete, Invalidate, Migrate) ride on the same trust anchor that validated the ID-JAG.
- SET / SSF / CAEP carry cross-domain identity events.
- RFC 9470 `insufficient_user_authentication` carries step-up challenges.
8. **Audit and abuse control.** How consent records, rate limits, security events, and conformance tests are represented.
- Resource records the ID-JAG consent context (issuer, subject, `client_id`, granted scope, time, ID-JAG `jti`) for user review and independent revocation.
- Rate limits at the token endpoint. Replay protection on the ID-JAG `jti`. Security events for first-use JIT provisioning.
- The profile draft defines the conformance test matrix.
The deployment profile's contribution is the choreography: which metadata is read, how trust is established, how the client identity is bound, how the user or tenant relationship is linked, which consent record is created, which token exchange happens, and which lifecycle channels apply. New protocol surface should be limited to the parts existing OAuth and OpenID specs do not already cover, and it should have an obvious migration path into the relevant owning spec. `insufficient_identity_claims` belongs in ID-JAG. `untrusted_issuer` belongs in the companion trust-discovery draft. A setup endpoint, if needed, belongs in the profile, but it owns account and client establishment, not token issuance.
## The trusted-issuer-discovery dependency
The Trust requirement above depends on a discovery surface that does not exist yet.
When a Resource Authorization Server accepts ID-JAGs, it has chosen to trust some set of IdP issuers. There is no standardized way for a client to discover that set. The closest existing surface, [`draft-mcguinness-token-xchg-target-svc-disco`](https://datatracker.ietf.org/doc/draft-mcguinness-token-xchg-target-svc-disco/), defines the inverse direction: an IdP Authorization Server advertises which target services it can issue tokens for, via a new `token_exchange_target_service_discovery_endpoint` metadata field. That solves the IdP-to-client direction.
The symmetric inverse, a Resource Authorization Server publishing which ID-JAG issuers it trusts, is not yet defined. It is a generic ID-JAG ecosystem need. The same gap shows up in [MCP Enterprise Managed Authorization](https://modelcontextprotocol.io/extensions/auth/enterprise-managed-authorization), which currently defers to manual admin configuration because no published discovery mechanism exists for this direction. It also shows up in Identity Chaining and any cross-domain SSO-to-API deployment where a resource consumes assertions from multiple issuers.
The right standards move is a small companion spec defining the symmetric inverse, parallel in shape to the existing target-service-discovery draft. The known security and privacy considerations carry over: enumeration attacks, trust-relationship leakage, rate limiting, and TLS posture all need the same treatment the existing draft already applies. That work belongs in its own draft, not inside the Agent Onboarding Profile. Once the companion exists, the Agent Onboarding Profile reuses it.
# Why This Matters
Each custom protocol surface that ships into the agent ecosystem creates the same costs the OAuth ecosystem has spent fifteen years learning to avoid: a new validation path, a new error catalog, a new revocation surface, a new metadata vocabulary, and a new fork of integrator effort. Some temporary product glue is inevitable while the standards work matures. The risk is letting temporary glue become permanent protocol. Resource services that today need to implement OAuth metadata, dynamic client registration, JWT-bearer grants, SETs, and revocation should not also need to implement a parallel `agent_auth` vocabulary that does the same things by different names.
The pattern auth.md describes is real and important. One-click setup for agents acting on behalf of users against third-party services is going to be a defining shape of authorization over the next several years. The way to get there is not a parallel grant surface. It is to put the agent provider in the IdP Authorization Server role that ID-JAG already defines, discover the service through RFC 9728 and RFC 8414, establish or bind the OAuth client through existing client-registration machinery, and issue delegated access through the standard token endpoint. Then deliver events through SSF and CAEP, drive lifecycle through OPC on the same trust relationship, and profile the runtime onboarding step explicitly.
auth.md should become the agent-readable ceremony document. PRM and AS metadata should remain authoritative for endpoints. The Agent Onboarding Profile should define the ceremony. ID-JAG should carry the identity assertion. CIMD, DCR, federation, or client binding should establish the client. The token endpoint should issue delegated access. SSF, CAEP, RFC 7009, and OPC should handle events, revocation, and lifecycle.
The next step should be a profile draft for one-click agent onboarding, with auth.md, MCP authorization, ID-JAG, client-establishment mechanisms, OpenID Federation, SSF/CAEP, and OPC treated as inputs. The work is concrete: define the metadata fields, state machine, trust model, client-binding rules, consent record, audit events, and conformance tests that make one-click agent setup interoperable instead of bespoke. It should also be honest about sequencing: some of the inputs are drafts, one trust-discovery dependency is still missing, and early deployments will need transition behavior.
Write the Agent Onboarding Profile. Keep auth.md as the document. Keep OAuth as the protocol. Use the bridge, but keep shortening it.
---
# Sessions Are Not Missions
Canonical URL: https://notes.karlmcguinness.com/notes/sessions-are-not-missions/
Markdown URL: https://notes.karlmcguinness.com/notes/sessions-are-not-missions.md
Before a release freeze, a coding agent is approved to implement passkey login. It runs for an hour, spawns a test-repair sub-agent, and opens a pull request. Then a reviewer finds a risky migration and freezes the branch. Approval is revoked in the governance system, but the harness only knows that the workspace, task graph, and tokens are still recoverable. The next morning the session resumes, the sub-agent finishes its retry loop, and the agent updates the pull request after approval had been revoked.
A modern agent can resume across machines, restart sub-agents, recover from crashes, refresh tokens, and pick up where it left off. From the outside, that looks like the agent is continuing the mission.
Architecturally, it is only continuing the session.
A session can prove the runtime survived. It cannot prove the mission did.
A session is the checkpoint. A mission is the work order. The checkpoint tells the harness where to resume. The work order tells it whether it is still allowed to.
That distinction matters because long-running agent systems persist for hours or days, run after the user closes the window, delegate to sub-agents, discover tools at runtime, and continue exercising authority across process, device, and trust-boundary changes. In those systems, treating a session as equivalent to a mission is not a UX shortcut. It is a governance failure.
This is the runtime-layer version of the authority gap explored in the [Power of Attorney](/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/), [Mission Shaping](/series/mission-shaping/), and [Mission-Bound OAuth](/series/mission-bound-oauth/) arguments:
> The harness that runs the agent is not the layer that owns the mission.
# What the Harness Preserves
Agent platforms expose a familiar set of primitives: resumable execution, persistent workspaces, background jobs, task graphs, sub-agents, dynamic tool discovery, memory, retries, and cached tool connections. Those primitives are valuable because they let the harness reconstruct enough state to take the next step. They are also why agent products are starting to look less like chatbots and more like distributed runtimes.
Each primitive solves a real problem. Resumable execution lets a long-running task survive a laptop closing. Persistent workspaces keep work in progress recoverable across restarts. Task graphs let the agent keep coordinating after a step fails. Tool connection caches let the agent skip the discovery and authentication overhead of every external call. Sub-agents let work parallelize. Background jobs let work continue while the user does something else. The user expectation behind all of this is simple. Work in progress should not be lost when infrastructure interrupts it. That expectation is correct, and it is the foundation of usable agent products.
A session, in this post, means an agent harness runtime session: the state that lets execution survive infrastructure churn. It is not the same thing as an IdP session, browser session, OAuth authorization session, or application login session, though those may be adjacent to it.
An agent harness session may include conversation context, task checkpoints, scratchpad memory, workspace state, tool connections, cached credentials, and orchestration state for retries or async work.
All of this is good engineering. None of it explains why the agent is still authorized to keep working.
# A Session Answers "Where Can the Agent Continue Working?"
A session is a runtime construct. It preserves continuity across the things that interrupt execution.
Most of what a session does goes unnoticed. A user closes a laptop. The process restarts on a different machine. A token refreshes, a sub-agent retries, a workspace migrates. The session storage still holds the workspace, the task graph, the cached MCP connections, and the conversation context. The harness loads that state, reconnects the tools, and the agent picks up its work.
That is what a session is for. It tells the harness where the agent can continue working.
| Session preserves | What that means in practice |
| --- | --- |
| Interaction state | Conversation context the model needs to keep reasoning |
| Execution checkpoints | Where the task graph paused |
| Scratchpad memory | What the agent learned during this run |
| Workspace state | Files, repos, branches, and generated artifacts |
| Tool context | MCP servers, APIs, connectors, and discovered tools |
| Cached credentials | Tokens still inside their usable window |
| Orchestration state | Queues, retries, async jobs, and sub-agent handles |
The harness recovers from each disruption it sees. The mission may be experiencing events the harness will never see.
| Session-layer event | Mission-layer event |
| --- | --- |
| Process restart | Approval revoked |
| Token refresh | Release branch frozen |
| Device handoff | Scope narrowed by reviewer |
| Model upgrade | New risk signal raised |
| Workspace recovery | Budget exceeded |
| Sub-agent retry | User intent changed |
The left column is visible to the harness. The right column is not. The harness can recover runtime state without learning that the mission has changed or ended.
It is the same shape as a ride-share driver whose app loses signal mid-trip. The route, the GPS, and the vehicle state are on the left. The cancellation the rider sent two minutes ago is on the right. The driver completes the request the rider has already withdrawn.
The session-level question is narrow:
> Is enough runtime state still present for the next instruction to execute?
When the answer is yes, the harness resumes. That is the right behavior at the runtime layer. A user closing their laptop should not lose an hour of work to a process restart. A sub-agent failing in a retry loop should not invalidate the parent agent's progress. A workspace migration should not cause the agent to forget where it was. Sessions are how all of that holds together.
That is the question every useful harness asks. The mission asks a different question.
# A Mission Answers "Why Is the Agent Allowed to Keep Working?"
A mission is the approved purpose of an agent's work. It binds delegated authority to that purpose, to a declared lifecycle, and to an approval context, and it preserves the legitimacy of that work across the things that change after approval.
Intent is not mission. Intent is user desire at a point in time. Mission is the durable governance object derived from intent, with bounded scope, approval context, and lifecycle. Turning intent into a mission is the work the [Mission Shaping series](/series/mission-shaping/) covers. Intent supplies the why. Mission gives the architecture something to evaluate against.
The vocabulary matters. The mission is the work the user approved. It carries the record of that approval: who granted it, what it covers, when it ends, how it can be narrowed, and who inherits it. The [Mission Authority Server](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-authority-server.html) is where the runtime asks whether any of that has changed.
Revoking the mission should stop the work even if the session can still be resumed.
A mission is not workflow state. Workflows track progress. Missions govern legitimacy. A workflow engine can continue after the justification for the work disappears, because its state is about what step comes next, not about whether the work should still be running. A mission-aware system cannot. The mission is the authority record that workflow state references but does not own.
The mission record carries:
- the purpose for which authority was granted
- the evidence behind the approval
- the conditions under which the authority remains valid
- the bounds on delegation
- the lineage of actors that inherited it
- the lifecycle events that end it
- the revocation semantics for stopping it before its natural end
In IAM terms, these are different systems of record. Session state records runtime continuity. Token state records whether a credential is active and usable at a protocol boundary. Policy state records whether a particular request is permitted under current rules. Mission state records whether the delegated mission itself remains legitimate. Collapsing those records into the harness makes runtime recoverability stand in for authorization, revocation, and lineage.
The mission-level questions are different in kind:
- Is the task still authorized?
- Has the risk posture changed since approval?
- Is the user's current intent still reflected in the execution?
- Has approval been revoked, suspended, or narrowed?
- Did the scope expand without a new approval?
- Can the current actor delegate further, and to whom?
- Should downstream systems still trust an action presented under this authority?
None of those answers are derivable from runtime continuity. A live process tells you the runtime is alive. It does not tell you the mission behind the runtime is still legitimate. That is the distinction the [Execution Mandate post](/notes/from-passports-to-power-of-attorney/) draws between identity, access, delegation, and authority.
Sessions cannot answer the authority question because sessions are not the layer that owns it.
# A Session Can Be Valid While a Mission Is Invalid
The distinction is easier to feel through examples than through definition. Three scenarios, each from a different domain, each producing the same shape of failure.
Consider an agent working under a manager's delegation for a project that takes three weeks. The agent has access to the manager's calendar, communicates on the manager's behalf with vendors, and drafts approvals that flow through the manager's name. In week two, the manager leaves the company. HR systems remove the manager's access. Payroll is closed out. The manager's email forwards to an interim contact. The agent's session storage is intact. Workspace, task graph, cached credentials, all recoverable. The delegation that justified the agent's authority is gone. The runtime is healthy. The mission has no living grantor.
Now consider a security agent granted access to investigate a suspected breach. Logs are pulled, anomaly detection is run, draft incident reports are prepared. At 4pm the breach is resolved by a different team. The incident is closed in the tracking system. The agent's session continues running through 6pm because the harness queued additional analysis steps when the breach was first declared. Those steps are still in the queue. The harness has every reason to think it can continue. What it cannot see is that the reason for the investigation has ended. The session is doing exactly what sessions are built to do. The investigation does not need it anymore.
And consider a user who approves an agent to draft a financial report. Mid-execution, the user discovers the report was based on stale numbers and revokes the approval. The agent's runtime is healthy. The next step in the task graph is queued. The tool connections are warm. The credentials are inside their refresh window. Nothing in the session storage knows the user has changed their mind. The harness will keep working on the report until the runtime breaks or the work completes, whichever comes first.
Three different mechanisms produce the same shape. A grantor disappears. A triggering condition ends. An approver changes their mind. In each case the session preserves everything the runtime needs and nothing the governance layer would need to stop. Runtime state is recoverable. Delegated authority is no longer legitimate.
The harness that asks only "can I continue?" continues. The harness that asks "should I continue?" stops. The difference between the two harnesses is whether the architecture has a layer that owns the second question.
# The Failure Mode
The three scenarios share a dangerous assumption.
> Execution continuity equals authority continuity.
Traditional sessions assume the user is nearby. Agent missions assume the user is absent. The user is asleep, in a different timezone, in another meeting, on another project, or gone from the company entirely. The agent keeps working through all of those states because the work outlives the user's attention. Session-model assumptions that worked when the user was minutes from the next interaction stop working when the user is hours or days away. Authentication freshness stops implying intent freshness. Interaction continuity stops implying engagement. Human absence is not a corner case for agents. It is the default state.
Human sessions accidentally carried governance assumptions because humans naturally terminate intent. A user logging out, closing a laptop, or walking away ended runtime and authority at the same moment. Sessions never had to distinguish "can continue" from "should continue" because the human supplied the "should" by being present. The IAM stack inherited that conflation as a working assumption. Agents break the inheritance by not terminating their own intent.
Short-lived chat interactions hide the assumption. The user is present, sees the next action, and closes the loop manually. Narrow copilots hide it too. Every meaningful side effect requires fresh confirmation.
It breaks when work runs unattended. It breaks when work crosses domains. It breaks when child agents inherit context. It breaks when the next action changes business state. It breaks when the resource being acted on did not exist when approval was granted.
The breakage has a structural cause.
**Sessions model what runtime needs to stay alive across infrastructure churn.** The connection. The state. The context. The resume point. The identity check.
**They do not model what governance needs.** The purpose the work was approved for. The bounds on how delegation narrows. The conditions that have to keep holding. The chain of who passed authority down. The rules for stopping it. The approval still standing in the organization.
What they model is enough to continue. What they leave out is what makes continuing legitimate.
Two forces make the failure mode deepen.
Product pressure pushes sessions toward persistence. Engineers want resumable workflows. Users want work to survive reboots. Vendors want agents to remember more state and run longer tasks. Every incentive points the same way. Sessions get longer, more durable, more recoverable.
Authority should not follow that shape. Authority is bounded by purpose, conditions, and lifecycle, not by infrastructure availability. The point of [governing the stay](/notes/governing-the-stay-not-just-the-entry/) is that authority must be terminable on its own clock, independent of whether the runtime that consumes it is still alive.
The assumption concentrates at resume. Every time a session wakes, whether from a process restart, a device handoff, a token refresh, or a sub-agent retry, the harness has to decide whether to continue. Treating resume as continuation rather than as a question collapses authority into runtime state at exactly the moment the architecture should be holding them apart.
When the runtime owns both execution and legitimacy, the longer-lived artifact wins. The session keeps going after the mission should have ended. That is not a bug. It is what sessions are for.
# Why the Harness Collapse Is Worse
The conflation is not lazy. It is rational engineering. Sessions are concrete and shippable. Missions are abstract and governance-heavy. Engineers reach for what they can build, and harness vendors reach for what they can sell. The result is the wrong default, baked in early.
The harness session is a distinct authority-collapse point. Tokens are projections, not missions. Identity, access, and delegation each answer their own question. The harness session is another place where mission state quietly accumulates, and it is a worse place than the token.
The reason is what each artifact is designed to do. Tokens are designed to force re-evaluation. They have explicit expiry, route through issuers on refresh, and require issuer participation for delegation. The narrowness of a token is not just a property. It is enforcement. Every refresh, exchange, or downstream issuance re-engages the authority source, whether or not authority is actually being re-checked.
Sessions are designed to do the opposite. Conversation context, task graph, sub-agent handles, tool connections, cached credentials, workspace state, scratchpad memory, queues, and retries all exist to avoid re-engagement. A session is a structure built to keep work alive across the events that interrupt it.
When a token is conflated with a mission, the substrate pushes back. Tokens expire. Refreshes route through issuers. The token's design surfaces periodic decision points the conflation cannot fully suppress.
When a session is conflated with a mission, nothing pushes back. The substrate is engineered to suppress exactly the events the mission layer needs to detect.
That asymmetry produces two structurally distinct failures.
**Sessions are engineered for survival. Missions are engineered for termination.** A revoked mission absorbed into a session has to be cleaned out of the task graph, sub-agent handles, tool connection cache, orchestration queues, workspace, and scratchpad. Each of those is engineered to persist. When the artifact engineered for maximum persistence is the same artifact that carries authority, the artifact wins. The mission inherits the session's lifetime instead of the other way around.
**Authority by ancestry has no token-layer analogue.** Token-based delegation requires an explicit issuance step. Session-based delegation can happen simply because a child descends from the parent's runtime graph. There is no equivalent of issuance. Delegation in sessions is implicit by default. Delegation with tokens is explicit by default. Implicit delegation cannot be attenuated, attributed, or separately revoked. Sub-agent fan-out is the failure mode the token layer does not have. Token delegation requires a signature. Session delegation requires only proximity.
The token-layer collapse is the more familiar IAM failure. The harness-layer collapse is structurally different. It amplifies what the substrate is designed to suppress, and it adds a delegation path with no issuance event.
# Beyond Coding Agents
Coding agents make the failure easy to see. The higher-risk versions are enterprise workflows.
An agent is approved to reconcile vendor invoices for an active procurement program. It discovers accounting tools through MCP, pulls invoice data, asks a sub-agent to compare contract terms, and posts reconciliation adjustments directly to the general ledger. Halfway through, the procurement program is suspended because the vendor is under legal review. The runtime session still has its workspace, task graph, cached tool connections, and retry queue. The mission no longer has a legitimate purpose.
If the harness only asks whether execution can resume, the agent may continue reading invoices, posting ledger adjustments, or sending vendor remittance notifications after the business condition that justified the delegation has ended. That is the IAM failure in its enterprise form: valid runtime continuity, stale delegated authority committing irreversible state.
Sub-agents amplify the collapse. The contract-comparison sub-agent inherits enough of the parent's session to do its job: workspace state, tool connections, cached credentials, ambient access, and a place in the task graph. If the harness treats the session as the mission, the sub-agent inherits authority by ancestry. There is no governed attenuation, no separate approval surface, and no separate revocation handle. The runtime graph keeps executing while children wait on tool calls or spawn their own children.
The questions that matter are mission questions:
- Does the contract-comparison sub-agent need separate approval to access tenant data the parent has not touched before?
- Is the sub-agent still inside the original procurement scope, or has it expanded into a new domain?
- Do ledger adjustments and vendor notifications require fresh consent because they commit irreversible state?
- If the parent mission is revoked, do all children stop, including the ones mid-tool-call?
The runtime graph cannot answer those questions by itself. It only knows what is running and what depends on what. It does not know whether each delegation remains legitimate.
This is the same shape as the [client instance attribution problem](/notes/client-instances-are-actors-not-new-clients/), seen from another angle. Sub-agents are actors with their own runtime identity. The mission they inherit must be explicit, attenuated, and separately governable, not an ambient property of descending from the same session.
OAuth has pieces for the cross-actor parts. [RFC 8693 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693), [Rich Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9396), and [identity assertion grants](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) all involve explicit issuer participation. The [Actor Profile individual draft](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) proposes an interoperable profile for actor chains. But none of that helps if the harness never asks for an attenuated mission because it believes the parent's session is sufficient authority for the child.
Attenuation has to be more than a smaller prompt. A child mission should be a strict subset of the parent mission, inherit an expiry no later than the parent, respect delegation-depth limits, carry its own actor identity, and terminate when the parent mission is revoked. Without strict-subset checks and cascade revocation, sub-agent delegation amplifies authority faster than logging can attribute it.
Open-world conditions amplify this further. As [Part 1 of the Open-World OAuth series](/notes/oauth-for-open-world-ecosystems/) argues, tools are discovered dynamically, resources appear at runtime, and execution crosses trust domains that were not paired in advance. The runtime may still be alive when the mission is no longer legitimate, or when the current work has drifted beyond what was originally approved.
That is the territory [Mission Shaping Part 2](/notes/mission-shaping-is-not-enough/) covers. The older [UCON model](https://dl.acm.org/doi/10.1145/984334.984339) made the same continuity point in access-control terms: authorization conditions may need to hold during usage, not only at entry. Open-world execution makes the closed-world assumption visible by breaking it.
# The Layered Model
Agent harnesses are distributed execution runtimes. The "chat session" framing is too small for what they do. Their runtime concerns define execution continuity. They do not define mission legitimacy.
The four systems of record introduced earlier are the spine of the architecture. Each owns a different question, and each is authoritative inside its own domain.
The core rule is one line.
> Sessions preserve execution. Missions preserve legitimacy.
The control-plane reading is straightforward:
- **Session state** records runtime continuity. The harness owns it. Workspace, task graph, scratchpad, tool connections, queues, and retries all live here. Session state answers where the agent can continue working.
- **Token state** records whether a credential is currently usable at a protocol boundary. The authorization layer owns it. Tokens project bounded capabilities, with explicit expiry and forced re-engagement on refresh.
- **Policy state** records whether a particular request is permitted under current rules. The policy layer owns it. Policy is evaluated per request, not per mission.
- **Mission state** records whether the delegated mission itself remains legitimate. The Mission Authority Server owns it. It is the only layer that can answer whether the work the agent is doing is still authorized.
On resume, the harness restores the workspace and task graph, then reads the mission handle carried in session state. It asks the Mission Authority Server whether that mission is still active and whether the next planned step is still inside its bounds.
If the answer is yes, token refresh and policy evaluation can proceed. If the answer is no, queued work is canceled, child agents are interrupted, and the session remains recoverable but inert.
The layers compose at runtime. A consequential action has to answer three questions: is this credential currently usable, is this request permitted under current rules, and is the mission this work belongs to still authorized? Three different questions, three different state owners, three different failure modes. Conflating them at any layer means the wrong question is being asked at the wrong time.
The Mission Authority Server is the new layer. The other three already exist in mature IAM stacks. The session lives in the harness. The authorization layer lives in the AS or token issuer. The policy layer lives in the PDP. The Mission Authority Server sits above them as a control-plane object the rest of the stack consumes. It holds mission lifecycle and revocation state. It exposes an evaluable check that other layers call before issuing, before authorizing, before resuming. Write access is narrow, restricted to the principals who approved the mission and the policies that govern it. Read access is wide. Every enforcement point in the stack reads from it.
What the layers fail to do when conflated is specific. Tokens absorbed into mission produce the failure [Mission-Bound OAuth Part 4](/notes/why-mission-bound-oauth-might-be-the-wrong-answer/) covers. Durable purpose gets smuggled into transport state. Policy absorbed into mission produces reconstruction-at-decision-time, where each PDP call rebuilds context that should already exist as durable state. Sessions absorbed into mission produce the failure mode this post is about. Each layer is good at its own question. None is good at the question above it.
Once those boundaries are named, the failure mode is specific. The mistake is not "agents need better governance" in the abstract. The mistake is letting one of the first three layers become the system of record for the fourth.
# What the Harness Owes the Mission
Separating sessions from missions does not make the harness passive. It gives the harness a clearer job. Session resume must be contingent on mission validity. The mission is consulted on every path forward, not as a fallback when something else fails.
The session-as-mission collapse shows up as ordinary engineering shortcuts. Each one has a governed alternative.
| Anti-pattern | Governed alternative |
| --- | --- |
| Using the session ID as the mission ID | Carry a mission handle in session state. Never treat its presence as proof of validity. |
| Treating token refresh as continuing consent | Re-check mission state on every issuance and refresh, including background-job refreshes. |
| Letting sub-agents inherit parent sessions wholesale | Issue an attenuated child mission before spawning any sub-agent with new tools, resources, or side effects. |
| Revoking only the visible UI session | Cascade revocation to child agents, queued work, and in-flight tool calls. |
| Checking approval only at task start | Check mission state before any irreversible action, privilege change, cross-domain call, or token refresh. |
| Logging tool calls without mission lineage | Tag every session event with the mission identifier so audit links runtime behavior to approved authority. |
| Continuing on cached mission state when the Mission Authority Server is unreachable | Fail closed for high-risk actions when validity cannot be confirmed. |
The enforcement points are distributed. The harness checks before resume, retry, delegation, and tool invocation. The authorization server checks before issuing or refreshing tokens. A gateway, sidecar, or resource server checks before protected resource access. The orchestrator checks before queued work drains into external side effects. The Mission Authority Server does not replace those policy enforcement points. It gives each of them a common authority state to consume.
The failure semantics have to be explicit. Action classes should be policy-defined by reversibility, sensitivity, and business consequence. For low-risk reversible work, a harness may use a signed mission artifact or cached validity with a short maximum staleness window. For high-risk, irreversible, or materially consequential work, inability to establish mission validity should halt execution.
Revocation is also not a single event. Stopping new token issuance, revoking already-issued tokens, canceling queued operations, and interrupting in-flight tool calls are separate enforcement problems. A real mission layer has to define which of those happens for each class of action.
Mission state changes more than once. Termination is the simplest case. The harder cases happen mid-mission. Scope narrowing means some in-flight work is still in scope and some is not. Suspension means execution should pause but not roll back. Amendment may invalidate cached decisions while leaving the mission active. Expiry extension and condition tightening look like updates, not endings. Each transition has a different runtime consequence, and the Mission Authority Server has to distinguish them. The harness has to respond differently to each.
The runtime still owns execution. It decides how to checkpoint, retry, resume, schedule, and recover work. What changes is that resuming execution becomes contingent on mission validity. Resume is not evidence of validity.
Two defaults are possible at resume. Session-validated resume asks whether the runtime survived. If yes, work continues. The mission is consulted only on explicit failure. Mission-validated resume asks whether the mission is still valid. If the Mission Authority Server is unreachable or the answer is no, work pauses.
The two failure modes are different in kind. Session-validated resume loses authority quietly. The agent keeps working past mission end and audit logs look clean. Mission-validated resume loses progress noisily. The agent occasionally pauses when it was still authorized.
Visible failure modes can be tuned. Invisible ones cannot. Making session resume contingent on mission validity, not the other way around, is the architectural choice that pays off.
Resume is an execution decision. Reauthorization is a governance decision. Do not let one impersonate the other.
This is not an argument against resumable sessions, cached mission artifacts, or OAuth tokens. It is an argument against treating any of them as evidence that delegated authority still exists.
# OAuth Hits the Same Wall
OAuth has a parallel version of this problem. OAuth is good at capability transport: token issuance, token exchange, sender constraining, scoped access, revocation, and introspection. It moves bounded authority across boundaries. It does not define durable mission continuity, runtime mission governance, or authority re-evaluation across sub-agent fan-out.
Tokens are projections of mission authority across a boundary. They are not the mission itself.
OAuth mechanisms provide useful rails around the edges: token revocation ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)), token introspection ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)), JWT access tokens ([RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068)), sender-constraining with DPoP ([RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) or mTLS ([RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705)), and change-signal work such as [Shared Signals Framework](https://openid.net/specs/openid-sharedsignals-framework-1_0.html) and [CAEP](https://openid.net/specs/openid-caep-specification-1_0.html). They help with credential state, sender binding, and revocation signals. None of them makes the harness session the right place to own mission legitimacy.
The fix is not only at the credential layer. It is in the protocol topology. The question shifts from "does OAuth solve this?" to "what protocol shape would?"
AAuth starts from a different shape. [AAuth](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/) makes the separation an explicit design commitment in the proposed protocol topology. The agent server runs the work. The Person Server owns mission proposal, approval, lifecycle, governance, and audit. A clear boundary between runtime and mission.
The mechanism that matters is the issuance path. The agent proposes a mission to the PS. The PS approves it and returns an `AAuth-Mission` header carrying an approver identifier and a hash of the approved mission body. The agent attaches the header to subsequent resource requests. Token issuance routes back through the PS.
That topology is what makes mission-validated resume possible at the protocol level. A long-lived agent runtime cannot accumulate authority on its own. In the intended topology, credential refreshes, new tool capabilities, and cross-domain hops route back through the Person Server. When the mission terminates at the PS, new issuance stops wherever the PS sits on the path, regardless of whether the agent server's runtime is still alive. The protocol topology forces the runtime to ask the question the architecture wants asked.
AAuth's mission layer is still narrower than the full mission model. The remaining work is covered in [AAuth Now Has a Mission Layer](/notes/aauth-now-has-a-mission-layer/), and the draft family has since taken it up directly: its [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) makes the Person Server the Mission Issuer, carrying the structured Mission record in the mission blob. For this post, AAuth has answered the where. The what is the harder question.
# The Missing Layer
Agent systems have sessions, orchestration, memory, tool inventories, and runtime continuity. Resumable, recoverable, multi-agent runtimes are what make agent products useful.
What they lack is a durable governance layer that continuously answers one question:
> Should this execution still be allowed to continue?
That layer is not a bigger context window. It is not a richer scratchpad, a longer-lived session, or a better sub-agent router. The runtime can keep improving without becoming the right place to answer that question.
Earlier posts built up that layer. An approved [Mission](/notes/the-mission-shaping-problem/) records what was granted. A [Mission Authority Server](/notes/governing-the-stay-not-just-the-entry/) owns the state and evaluates whether the mission still holds. [Protocol projection](/series/mission-bound-oauth/) carries reduced views of that state across token and signal surfaces. A [sub-agent attenuation model](/notes/client-instances-are-actors-not-new-clients/) attributes each runtime actor inside the delegation graph. The harness does not need to own any of those layers. It needs to know that it does not own them.
A session that survives a laptop reboot is a useful runtime property. A mission that survives merely because the session survived is a governance bug. Agent platforms need to make session resume contingent on mission validity, not treat resume as evidence of validity.
Overnight, the harness wakes the session and finds the workspace, task graph, and tokens intact. None of that matters. Every path forward routes through the mission layer. The sub-agent's retry loop wants to refresh its tokens, and the refresh routes through the Mission Authority Server (the issuance-grant companion puts it on that path: estate authorization servers redeem MAS-minted grants for Mission-bound, state-gated tokens). The server has marked the mission revoked. The next tool call needs an issued capability, and the issuer refuses to issue. The runtime has every piece of state it needs to continue. It does not have the authority to use any of it.
The future problem is not whether agents can continue running. It is whether they should.
Durable execution without durable governance is how autonomous systems silently inherit authority beyond the intent that justified it.
Sessions are not missions.
---
# Client Instances Are Actors, Not New Clients
Canonical URL: https://notes.karlmcguinness.com/notes/client-instances-are-actors-not-new-clients/
Markdown URL: https://notes.karlmcguinness.com/notes/client-instances-are-actors-not-new-clients.md
OAuth registers clients. Agent platforms run instances.
The [Actor Profile (`draft-mcguinness-oauth-actor-profile`)](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) closes a long-standing gap at the delegation layer: who is acting on whose behalf, with which key, across assertion grants and JWT access tokens. That work, summarized in [Standardize `act` Across Assertion Grants and JWT Access Tokens](/notes/standardize-act-across-assertion-grants-and-jwt-access-tokens/), now exists as an IETF individual draft.
The next gap sits one layer below it. When a token says `client_id` is `planner-agent`, that names the registered logical client. It does not name the specific runtime that is presenting the token: which container, which agent process, which sub-agent spawned by another sub-agent five hops earlier. In production agent deployments, that is exactly the identity the resource server needs.
The operational failure is easy to picture. A vendor-hosted `planner-agent` instance exfiltrates customer records using a valid token, valid user delegation, valid scope, and a matching DPoP proof. The enterprise can see that `planner-agent` acted for Alice. It cannot tell which runtime acted, whether that runtime spawned a compromised sub-agent, or whether remediation should revoke one instance or disable the whole client.
> The fix is not a new client type. It is to recognize that a client instance is an actor, and to slot it into the model `act` already provides.
In delegated flows, the instance appears in `act`. In self-acting flows, the same runtime profile appears in `sub`. The point is to keep runtime identity out of the client registry and inside the subject/actor vocabulary OAuth tokens already use.
That is the proposal in [OAuth 2.0 Client Instance Assertion (`draft-mcguinness-oauth-client-instance-assertion`)](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/). It builds on the Actor Profile, defines a `client_instance_assertion` parameter at the token endpoint while reusing [Token Exchange (RFC 8693)](https://datatracker.ietf.org/doc/html/rfc8693)'s `actor_token` for the token-exchange grant, and adds an `instance_issuers` field to OAuth client metadata. The client delegates instance-attestation authority to specific issuers, and the AS enforces that delegation when it decides whether an instance assertion can populate `act`. No new grant type. No new claim. No new client type.
The draft is early work, not a settled specification. The rest of this post explains the approach so readers can push back on it. The [closing section](#-this-is-early-work-and-i-want-feedback) names the specific points I am asking for feedback on.
> **Update, 2026-06-24.** Since this post was published, the draft advanced to [`-01`](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/01/), and the open wire-form question raised later in this post has been resolved there. The draft now defines a dedicated `client_instance_assertion` request parameter for ordinary grants (`authorization_code`, `client_credentials`, `refresh_token`, `jwt-bearer`), and keeps RFC 8693's `actor_token` only for the token-exchange grant, where it travels with `actor_token_type=urn:ietf:params:oauth:token-type:client-instance-jwt`. The same revision registers `client_instance_assertion`, not `client_instance_jwt`, as the optional `token_endpoint_auth_method`.
>
> The token-side result did not change. The validated runtime still lands in `act` for delegated flows and in `sub` for self-acting ones. The switch matches the call the [Open Design Choice](#-open-design-choice-actor_token-or-client_instance_assertion) section below makes. A dedicated parameter is more precise, and it avoids stretching `actor_token` in self-acting flows where the runtime is the principal, not an actor. The sections below are kept as written, with notes where `-01` settled a choice they present as open.
# ๐ฆ Logical Clients vs. Runtime Instances
Before the proposal lands, the distinction it leans on is worth pinning down. OAuth makes it only implicitly.
A *logical client* is what gets registered with an authorization server: an application name, a set of allowed redirect URIs, a credential or key, and the policy that goes with it. `client_id = planner-agent` is a logical client. So is `slack`, `outlook-desktop`, and `salesforce-connected-app-12345`. A logical client usually lives for years. There is one of it per application, sometimes per platform variant.
A *runtime instance* (or simply *instance*) is the specific running thing that realizes a logical client at a moment in time. Concrete examples:
- A single container running a copy of `planner-agent`, started 90 seconds ago, scheduled on a particular node, holding its own ephemeral key pair.
- A serverless function invocation acting on behalf of a user. The function definition is the client. The specific cold-started invocation is the instance.
- An installed copy of a desktop application on Alice's laptop. The application registration is the client. Alice's install, identified by a device-bound key, is the instance.
- A sub-agent process that a parent agent spawned to perform a narrowed sub-task, with its own key pair and its own attestation.
What is *not* an instance, by this definition:
- A single HTTP request or API call. Those happen *within* an instance.
- A user session. A session is a binding between a user and a runtime. The runtime itself is the instance.
- A tenant or workspace. Tenancy is orthogonal. One instance can serve many tenants, and one tenant can be served by many instances.
The two answer different operational questions:
- *"Was this application authorized for this scope?"* points at the logical client.
- *"Which specific runtime, with which key, just made this call?"* points at the instance.
That distinction worked when clients were long-lived servers configured by hand. It does not work for production agent fleets, where the same logical client is realized as many transient runtimes (containers, functions, agent worker processes), spawned and terminated continuously, often running on customer infrastructure rather than the vendor's, and capable of spawning further sub-agents that inherit narrower authority. A token whose only attestation about the acting party is `client_id = planner-agent` cannot answer the question that matters in incident response, audit, or policy: *which instance of `planner-agent` did this, with what key, on whose authority?*
[Open-World OAuth Still Needs Mission Shaping](/notes/open-world-oauth-still-needs-mission-shaping/) frames the discovery and consent layers of this problem. [Enterprise SaaS Needs OAuth Federation Now](/notes/enterprise-saas-needs-oauth-federation-now/) frames the workload-credential layer with [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) assertion grants and [Identity Assertion JWT Authorization Grant (ID-JAG)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/). Both treat the client as a single registered entity. Neither names the specific runtime instance that is acting at any moment.
> Client identity tells you which application is authorized. Instance identity tells you which runtime is actually exercising that authorization right now.
OAuth today gives a portable answer to the first question and no portable answer to the second. The reason the second question matters is concrete. When a security operations center investigates a breach, the question is which container or function invocation made the suspect call, not which application registration covers the whole product. When an audit log records a customer-data read, the question is which agent runtime did it, not just that "an instance of the agent" did it somewhere in the fleet. When a policy engine decides whether to allow an API call, the question is whether *this* runtime, attested by a trusted issuer, with this key, is allowed to act, not just whether the application registration permits it in principle.
# ๐จ What Failure Looks Like Today
To make the cost concrete, consider an incident that is plausible in production.
An enterprise deploys `planner-agent` from a vendor. At 14:32, an instance of `planner-agent`, acting on Alice's behalf, calls a customer-records API and exfiltrates 80,000 records to an external endpoint. The call passes every check the resource server runs. Alice approved the scope. The token is valid. The client is registered. The resource server's policy permits the read. The DPoP proof matches the bound key. A downstream DLP system catches the exfil hours later. Investigation begins.
What the audit log can answer:
- Token was valid and unexpired.
- `client_id` was `planner-agent`.
- `sub` was `alice@enterprise.example`.
- Scope `customers:read` was present and approved.
- The DPoP key thumbprint matches what the AS issued.
What the audit log cannot answer:
- Which specific runtime of `planner-agent` made the call. The vendor runs thousands of them.
- Whether that runtime was a legitimate user-authorized session at the moment of the call, or a long-lived background sub-agent that picked up a cached token.
- Whether the runtime was spawned by another runtime, and by which one.
- Whether the same compromise affects other customers' agents running on the same vendor infrastructure.
- Whether remediation requires killing the entire `planner-agent` client (and its production traffic for every customer) or whether something narrower exists.
The conversation between the enterprise security team and the vendor goes in a familiar direction. The vendor says "we cannot tell you which runtime, our internal logs do not expose that on the OAuth path." The enterprise asks "can you revoke access for our tenant only." The vendor says "yes, but it will take a coordinated rotation." The enterprise asks "what about the other tenants on the same fleet." The vendor says "we are investigating." Days pass. Customers ask the enterprise the same questions, and the enterprise has to repeat the same answers.
That conversation is the failure mode. The token had everything authorization needed. It had nothing forensics needed.
With the proposal, the same incident produces a different audit record. The token's actor chain names `act.sub = spiffe://vendor.example/ns/planner/instance/search-sub-92ae`, attested by `https://issuer.vendor.example` at 14:30, holding key `9aBQ...`, after being spawned by `act.act.sub = .../planner-7f3c` at 14:25. Remediation becomes targeted. Revoke `search-sub-92ae`'s attestation at the issuer. Invalidate its bound key. Leave every other sub-agent and the parent agent running. The chain tells the security team which spawn path is implicated and which sibling instances came from the same parent during the same time window. Whether the compromise propagates further becomes a tractable question instead of an open one.
> The proposal does not prevent the breach. It prevents the breach from being unattributable.
That distinction is the pitch. Authorization tells you whether an action was allowed. Instance identity tells you who actually took it, with enough granularity to revoke surgically and audit forensically.
The defenses are bounded. The proposal does not prevent a live compromised instance from acting until its attestation expires. Issuer-side revocation stops new attestations and token renewal, but existing access tokens remain valid until expiry unless the deployment also uses OAuth revocation, introspection, or another real-time enforcement path.
It does not prevent a compromised instance issuer from minting attestations for any runtime within the clients that endorsed it. It does not prevent a compromised registration path from endorsing a malicious issuer. Runtime hardening, issuer discipline, and metadata integrity remain necessary. The proposal narrows the trust boundary from "anywhere in the fleet" to the specific attested runtime, the issuer that vouched for it, and the metadata document that endorsed the issuer. That is a much smaller surface to harden.
# ๐ช This Tension Is Older Than Agents
The breach scenario is what the gap looks like under stress. The gap itself is older than agents. The details differ by product, but the pattern is common. The mismatch between logical client and acting runtime has been quietly absorbed inside vendor stacks for years.
- **Microsoft 365.** The Office portfolio (Word, Excel, PowerPoint, Outlook, OneNote, Teams) is realized as a small set of Microsoft Entra ID application registrations used across millions of devices, browser tabs, mobile installs, and broker-mediated sessions. The same `client_id` appears on Alice's laptop, Bob's phone, and a developer's CI runner. Which runtime actually presented the token is answered by Microsoft-specific signals such as device IDs, broker state, and conditional access context, not by anything the OAuth wire format names.
- **Salesforce Connected Apps.** A single Connected App registration serves an integration that can span dozens of orgs, sandboxes, and regional pods. The runtime context that matters for authorization (which process, on which deployment surface, serving which org and sandbox) is carried in custom claims and tenancy headers layered on top of OAuth rather than expressed in it.
- **Slack apps.** One Slack app `client_id` can be installed across thousands of workspaces, each with its own bot user, scope grants, and runtime surface. The "which running copy, serving which workspace install" question is answered by Slack-specific identifiers, not by OAuth claims.
- **Native desktop and mobile OAuth.** An installed Outlook client on a corporate laptop is `client_id = outlook-desktop`. So is the same client on a different laptop, a different OS, or a different identity. Mobile native apps have lived with the same gap since they replaced web flows.
Each ecosystem has ways to compensate for the gap internally. The fixes are proprietary. They do not compose across deployments, they do not give relying parties outside the vendor's stack a portable way to reason about which runtime is acting, and they do not survive cross-domain delegation chains.
> The pattern is consistent. One registered client, many runtime and tenant realities. Agents did not introduce this problem. Agents removed the option of papering over it.
Workload identity federation, [SPIFFE](https://spiffe.io/), and platform broker identities have been compensating for this on the workload side for years. They produce signed attestations about which runtime is which, then bridge into OAuth through vendor-specific glue. The Actor Profile gives this work a protocol-defined slot for carrying that signal in OAuth tokens and delegation chains. Adding `client_instance` to its vocabulary is the smallest move that turns these existing compensations into something interoperable.
# ๐งฑ Why "Just Register More Clients" Is the Wrong Fix
Before walking through the proposal itself, the obvious alternative is worth ruling out. The natural reflex inside OAuth is to register another client per instance: every container, every spawned sub-agent, every short-lived worker becomes its own `client_id` with its own credential. That approach fails on every dimension that matters at agent scale.
- **It collapses under cardinality.** Long-lived `client_id` registration assumes a small, slowly changing set with admin oversight per entry. Agent runtimes are created and destroyed in seconds. As a back-of-envelope figure, 1,000 hosted agents each spawning five short-lived sub-agents per day generates roughly 1.8 million client-registration events a year. A registry that has to keep up with that starts to look less like an administrative trust registry and more like a metrics pipeline, with all the costs: storage, indexing, sync, and lifecycle churn.
- **It scatters trust.** The relying party is supposed to evaluate the registered client. If every instance is its own client, the relying party loses the ability to reason about the parent application at all, and policy fragments across thousands of indistinguishable client identifiers.
- **It rebuilds the OAuth island problem.** As argued in [Enterprise SaaS Needs OAuth Federation Now](/notes/enterprise-saas-needs-oauth-federation-now/), point-to-point credentials are exactly the model federation is meant to retire. Per-instance clients re-create per-instance credentials inside every authorization server.
- **It mixes layers.** The application's authority is what the user or admin approved. The instance is just the runtime that happens to be carrying that authority right now. Promoting every instance to a client conflates *who is authorized* with *who is currently executing*.
The mismatch is not that OAuth's client model is wrong. The mismatch is that the client model is being asked to carry information that belongs in the actor chain.
# ๐ค The Pieces Already in Our Hands
The interesting observation is how little needs to be invented. Each of the relevant building blocks already exists.
- **`act` + entity profiles.** The [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) defines a typed actor chain. `act` names the party currently acting on a principal's behalf. [`sub_profile`](https://datatracker.ietf.org/doc/draft-mora-oauth-entity-profiles/) is a space-delimited string of profile values that classify each principal (`user`, `service`, `ai_agent`, and so on). `cnf.jkt` at the top level binds the token to the current presenter's public key. A new profile value, `client_instance`, fits directly into that vocabulary, sitting next to a behavioral profile such as `ai_agent` or `service`, since an instance can be both a runtime and an agent.
- **RFC 8693 `actor_token` wire.** Token Exchange (RFC 8693) defines two request parameters, `actor_token` and `actor_token_type`, that let a client present a signed attestation about the party currently acting alongside the subject the request is about. Today they only show up in token exchange flows. In `-01` the instance assertion travels in a dedicated `client_instance_assertion` parameter for the ordinary grants (`authorization_code`, `client_credentials`, `refresh_token`, `urn:ietf:params:oauth:grant-type:jwt-bearer`), and `actor_token` stays reserved for the token-exchange grant. The grant types themselves do not change. The [Open Design Choice](#-open-design-choice-actor_token-or-client_instance_assertion) section below records how that wire form was chosen.
- **OAuth client metadata.** Static registration, [Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591), and [CIMD](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) all give the AS a place to read per-client metadata. Adding an `instance_issuers` field there gives the client one place to declare, "These are the issuers I trust to attest my instances." The descriptor works the same in any of those channels.
- **Workload identity federation.** [SPIFFE](https://spiffe.io/) (Secure Production Identity Framework for Everyone) and platform-issued workload assertions issue short-lived signed identifiers to running workloads based on platform attestation: which Kubernetes service account, which AWS IAM role, which CI runner. They produce signed statements about which runtime is which, which is exactly the upstream input the AS needs to validate before populating `act`.
- **Attestation-based client authentication.** [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/) defines a key-bound client attestation and proof-of-possession mechanism for authenticating a deployed client instance. The [Client Instance Assertions draft](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) addresses the next question: once an authorization server has validated runtime identity, how should that identity be represented in issued tokens so resource servers, audit systems, and downstream policy can consume it?
- **DPoP / mTLS sender-constraint.** [RFC 7800](https://datatracker.ietf.org/doc/html/rfc7800) defines the `cnf` confirmation claim, which carries a thumbprint of the holder's public key. [DPoP (RFC 9449)](https://datatracker.ietf.org/doc/html/rfc9449) and [OAuth mTLS (RFC 8705)](https://datatracker.ietf.org/doc/html/rfc8705) let the holder prove control of that key on each request, so a stolen token alone cannot be replayed by a different presenter. Each instance has its own key. Each issued access token carries that instance's `cnf` at the top level when it is the current presenter.
The proposal stitches these together. It does not add a parallel mechanism beside any of them.
The layering is deliberate. The Actor Profile provides the token shape and actor-chain semantics. Entity Profiles provide the vocabulary for classifying the subject and actor. RFC 8693 provides the `actor_token` parameter shape. DPoP and mTLS provide sender-constraint. SPIFFE, WIMSE-style workload credentials, and platform attestation provide upstream evidence about runtimes. CIMD, Dynamic Client Registration, or static registration carry the client's endorsement of acceptable instance issuers. The instance profile ties those layers together. It does not compete with them.
# ๐ Why DPoP Alone Is Not Enough
A reasonable objection at this point: DPoP already binds a token to a key. If every runtime holds its own key, does not that already give per-instance separation?
It does, but only at the cryptographic layer. DPoP tells the resource server "the holder of key K presented this token." It does not tell the resource server *who key K is*. That distinction is what the rest of this proposal is about.
- **DPoP keys are not attested.** Any process can generate a keypair and use it as a DPoP holder key. There is no protocol mechanism in DPoP that says "this key was generated inside an attested workload identified as instance X of client `planner-agent`, vouched for by issuer Y." Without that attestation the key is opaque. The resource server can know it is talking to whoever holds the key. It cannot know that whoever holds the key is a legitimate runtime of the registered client.
- **DPoP keys are not bound to a `client_id`.** The `jkt` in `cnf` says "this is the public key of the current holder." It says nothing about which OAuth client the holder belongs to. A DPoP key on its own offers no defense against cross-client smuggling. Only an attestation that names `client_id` and is endorsed by that client's metadata does.
- **A DPoP thumbprint is not a name.** In audit logs, `jkt = "9aBQ..."` is meaningless without a separate lookup table mapping thumbprints to runtimes. For ephemeral runtimes that generate fresh keys on startup, no such table exists, and the thumbprint is forensically useless on its own. The breach scenario from earlier in this post is exactly what that looks like in practice.
- **DPoP has no revocation surface above the key.** If a runtime is compromised, refusing to honor its DPoP key requires you to first know which runtime that key belonged to. Instance attestations move the revocation handle one level up: revoke at the issuer, and the runtime's AS-issued tokens stop renewing regardless of whether the key is still in the attacker's hands.
- **DPoP carries no chain.** When `planner-agent` spawns `search-sub-92ae`, DPoP records only the current holder's key. The lineage between parent and sub-instance lives in nested `act` entries, not in a DPoP proof. Sub-agent governance, the case the post opens with, is fundamentally a chain-shape problem that DPoP cannot express by itself.
mTLS with X.509-SVIDs (via `cnf.x5t#S256`) is somewhat different. A SPIRE-style issuer can sign the cert with platform attestation, the cert's SAN carries a runtime name, and certificate lifecycle or revocation machinery may be available depending on the deployment. That can address the attestation, naming, and revocation gaps from above. What it does not fix is the layer the identity lives at: it sits at the TLS connection, not in the token. It does not propagate across token exchange hops, does not appear in the actor chain, and is not visible to downstream resource servers or audit pipelines that consume tokens rather than connection metadata. The same end-state limitation applies.
Sender-constraint is necessary for this proposal to do its job. The top-level `cnf` binding (`cnf.jkt` for DPoP, `cnf.x5t#S256` for mTLS) is what makes the live sender constraint enforceable. But sender-constraint alone is not sufficient. The instance assertion is what gives the key meaning: which attested runtime, of which client, vouched for by which issuer, spawned by which parent. Strip the assertion away and you are back to "the holder of some key did this," which is exactly the audit dead end enterprises are stuck in today.
> DPoP secures the key. The instance assertion identifies the keyholder. Both are needed.
# ๐ง The Trust Delegation Model
The remaining question is how the instance assertion gets its meaning. The trust shape behind it has three parties:
1. **The OAuth client** publishes the issuers it trusts to attest its instances. This is an `instance_issuers` array in the client's metadata, with each entry naming an issuer URL, expected subject syntax (such as a SPIFFE trust domain prefix), and key material discovery. The metadata reaches the AS through whatever channel the deployment already uses: CIMD when the `client_id` is a URL, Dynamic Client Registration when registration is programmatic, or a static client record when registration is admin-driven. The descriptor format does not depend on which channel was used.
2. **The instance issuer** authenticates the runtime (typically through a workload identity federation mechanism) and signs an instance assertion that names both the instance and the `client_id` it belongs to. Per-client minting is a hard requirement. An issuer must not be able to mint an assertion for a client that did not endorse it. This is what prevents cross-client smuggling. A breach of one product's issuer cannot produce assertions usable by another product unless that other product independently endorsed the same issuer.
3. **The authorization server** resolves the client's metadata (CIMD URL or its local registration store), validates the assertion against the client's published instance-issuer set, verifies the JWS, and binds the resulting access token to the instance's key via top-level `cnf`.
The assertion is short-lived and audience-bound, and the AS rejects replayed assertion IDs.
```mermaid
sequenceDiagram
participant Client as OAuth Client
participant Issuer as Instance Issuer
participant Runtime as Runtime
participant AS as Authorization Server
Note over Client: Setup
Client->>AS: Publish instance_issuers in metadata
(via CIMD, DCR, or static)
Note over Runtime,Issuer: Runtime startup
Runtime->>Issuer: Request attestation
Issuer->>Runtime: Signed instance assertion
(client_id, sub, cnf)
Note over Runtime,AS: Token request
Runtime->>AS: Grant + instance assertion
AS->>AS: Resolve client metadata
AS->>AS: Verify assertion.iss endorsed
by client.instance_issuers
AS->>AS: Verify assertion.client_id matches
AS->>Runtime: Access token
(act + cnf bound to runtime key)
```
Three concerns travel together by design. The first is identity, the runtime's stable name. The second is attestation, a trusted issuer's claim about that identity. The third is key binding, proof the runtime holds the key the AS bound to the issued token. Each pair without the third loses force. Identity without attestation is an unfalsifiable self-claim. Attestation without key binding can be replayed by anyone who exfiltrates the assertion. Key binding without attested identity is the sender-constraint-alone case ruled out earlier. The proposal couples all three because each gap is what an adversary exploits.
The shape also inherits one assumption worth naming. The AS must trust the client's metadata to be authentic. Whoever decides which clients are allowed to publish which `instance_issuers` is the meta-trust authority. For statically registered clients, that is the AS administrator. For Dynamic Client Registration, it is the AS's DCR authorization policy. For CIMD, it is the chain of trust establishing that the metadata at the `client_id` URL is what the legitimate client actually authored. Instance attestation is only as strong as the integrity of the registration that endorsed the issuer. The proposal does not solve registration integrity. It inherits whatever the deployment already has.
A client metadata document carrying `instance_issuers` looks the same whether it was fetched via CIMD or stored in the AS from a static registration. In this example, the instance issuer signs JWT assertions using keys published at `jwks_uri`, while `spiffe_id` constrains the allowed subject syntax for runtime names. The CIMD case is shown here:
```json
{
"client_id": "https://planner.assistant.example/oauth/client",
"client_name": "Planner Agent",
"redirect_uris": ["https://planner.assistant.example/cb"],
"token_endpoint_auth_method": "private_key_jwt",
"instance_issuers": [
{
"issuer": "https://issuer.assistant.example",
"jwks_uri": "https://issuer.assistant.example/jwks",
"subject_syntax": "spiffe",
"trust_domain": "assistant.example",
"spiffe_id": "spiffe://assistant.example/ns/agents/*",
"signing_alg_values_supported": ["ES256"]
}
]
}
```
The instance assertion itself is a JWT signed by the instance issuer, naming the instance and the client. It carries `cnf` so the AS can sender-constrain whatever it issues to the key the instance actually holds:
```json
{
"iss": "https://issuer.assistant.example",
"sub": "spiffe://assistant.example/ns/agents/instance/planner-7f3c",
"sub_profile": "client_instance ai_agent",
"aud": "https://as.example.com/token",
"client_id": "planner-agent",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
},
"iat": 1773075000,
"exp": 1773075600,
"jti": "9c4d3e..."
}
```
Two structural rules matter. First, the instance assertion itself must not contain `act`. It represents direct identity of the runtime, not a delegation chain. Second, `client_id` in the assertion must match the OAuth client involved in the request. That equality is what prevents one client's instance attestation from being smuggled into another client's flow. If the issuer is not endorsed by `instance_issuers`, the `client_id` does not match, or the `cnf` key does not match the presenter, the AS rejects the assertion.
As of [`-01`](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/01/), the wire form at the token endpoint uses a dedicated `client_instance_assertion` parameter for ordinary grants. An authorization code redemption with an instance attestation looks like this:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=authorization_code
&code=
&redirect_uri=https%3A%2F%2Fplanner.assistant.example%2Fcb
&client_id=planner-agent
&client_instance_assertion=
```
No new grant type. The grant remains `authorization_code`. The token-exchange grant still carries the instance in RFC 8693's `actor_token` slot, with `actor_token_type=urn:ietf:params:oauth:token-type:client-instance-jwt`.
# ๐ก What This Looks Like in Tokens
The Actor Profile defined three structural cases for delegation. Adding instance identity gives them sharper meaning.
One structural note before the cases. RFC 8693's `act` names the party acting on behalf of a subject. In delegated flows (Cases 1 and 3), the runtime is that acting party and belongs in `act`. In the self-acting case (Case 2), there is no separate subject, so the runtime belongs in `sub`. The `client_instance` profile value classifies the runtime in either position. The result is one vocabulary for runtime identity without changing the meaning of `sub` or `act`.
## Case 1: Delegation (User Authorizes a Client Instance)
Alice signs in to `planner-agent`. The runtime that picks up her session is a specific instance. The issued access token carries Alice as the principal, the instance in `act`, and the instance's key as the live sender constraint.
```json
{
"iss": "https://as.example.com",
"aud": "https://api.example.com",
"client_id": "planner-agent",
"sub": "user-alice",
"sub_profile": "user",
"scope": "hotels:search hotels:book",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
},
"act": {
"iss": "https://issuer.assistant.example",
"sub": "spiffe://assistant.example/ns/agents/instance/planner-7f3c",
"sub_profile": "client_instance ai_agent",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
}
},
"exp": 1773076800
}
```
The resource server gets two answers from one token. *Whose authority is being exercised?* Alice. *Which runtime is exercising it?* The `planner-7f3c` instance, holding the key the AS bound the token to.
## Case 2: Self-Acting Instance
A backend ingestion worker is its own principal. There is no user. The instance assertion goes in directly as the subject identity, not as an actor wrapped around a user.
```json
{
"iss": "https://as.example.com",
"aud": "https://api.example.com",
"client_id": "ingestion-worker",
"sub": "spiffe://platform.example/ns/ingest/instance/worker-04f1",
"sub_profile": "client_instance service",
"scope": "ingest:write",
"cnf": {
"jkt": "kfL3oTu5Vd8gtPKjB5q2XnPmGrLfRFAzTQbm2qj9SxQ"
},
"exp": 1773076800
}
```
`act` is absent because no other principal authorized this runtime. The instance is the principal. The classification (`client_instance service`) tells the resource server what kind of runtime it is and that it was attested via the instance flow rather than registered separately as a client.
## Case 3: Sub-Instance via Token Exchange
This is the case the client model genuinely cannot represent. A parent agent instance spawns a narrower sub-agent instance to perform a bounded sub-task. The sub-agent presents the parent's token as `subject_token` and its own instance assertion as `actor_token`. The new token preserves the chain: the user is still the principal, the sub-agent is the current actor, the parent agent is recorded as the prior actor.
```json
{
"iss": "https://as.example.com",
"aud": "https://api.example.com",
"client_id": "planner-agent",
"sub": "user-alice",
"sub_profile": "user",
"scope": "hotels:search",
"cnf": {
"jkt": "9aBQfFxYJVD8cAvLFAzTQbmzELkFAYW8bqvVvRsBnsg"
},
"act": {
"iss": "https://issuer.assistant.example",
"sub": "spiffe://assistant.example/ns/agents/instance/search-sub-92ae",
"sub_profile": "client_instance ai_agent",
"cnf": {
"jkt": "9aBQfFxYJVD8cAvLFAzTQbmzELkFAYW8bqvVvRsBnsg"
},
"act": {
"iss": "https://issuer.assistant.example",
"sub": "spiffe://assistant.example/ns/agents/instance/planner-7f3c",
"sub_profile": "client_instance ai_agent",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
}
}
},
"exp": 1773076800
}
```
The same `client_id` covers both runtimes because they are instances of the same logical agent product. The chain inside `act` shows which specific instance acted now and which one acted before, with each instance's key recorded at its hop. The top-level `cnf` is the live sender constraint, exactly as the Actor Profile specified for the [agent-to-tool example](/notes/standardize-act-across-assertion-grants-and-jwt-access-tokens/#-the-canonical-cross-domain-case) in the prior post.
Each token exchange hop performs two coupled actions: the `act` chain extends with the new current actor, and the top-level `cnf` shifts to that instance's key. Prior instance keys remain inside nested `act` nodes as delegation history. The processing rules from the Actor Profile apply unchanged.
> Sub-instances are not a new taxonomy. They are actor chain entries with a profile value that says "this hop is a runtime, not a logical principal."
# โ๏ธ Resource Server Processing
Whatever chain produced the token (single-domain delegation, self-acting workload, or sub-instance fan-out), the resource server validates it the same way. Resource server logic stays close to what the Actor Profile already specified, and the instance profile only sharpens what the existing rules look at.
```text
input: access_token jwt
dpop_proof jwt // required when token.cnf.jkt is present
1) Validate token (sig, iss, aud, exp) per JWT AT profile (RFC 9068).
2) Verify top-level cnf.jkt against the DPoP proof per Actor Profile.
3) If token.act present:
actor_profiles := parse_profiles(token.act.sub_profile)
If "client_instance" in actor_profiles:
// The current actor is a specific runtime of token.client_id
evaluate policy.allow_instance(token.act.sub, token.client_id)
evaluate policy.allow_delegate(actor_profiles, parse_profiles(token.sub_profile))
else:
// Logical actor (e.g. another service identity)
evaluate policy.allow_actor_for_subject(token.act.sub, token.sub)
4) If token.act absent and "client_instance" in parse_profiles(token.sub_profile):
// Self-acting instance
evaluate policy.allow_instance_self(token.sub, token.client_id, token.scope)
5) Optionally walk nested token.act.act... for audit.
Each nested cnf.jkt records the key binding of a prior actor in the chain.
6) Enforce decision.
```
`client_id` is still part of the input. It identifies which application's authorization is being exercised. The instance subject in `act` (or in `sub`, for self-acting) tells the resource server which runtime is doing the exercising. The combination, not either alone, is what supports policies like "only instances attested by issuer X can call this endpoint" or "this scope is allowed for instances under SPIFFE prefix Y."
In Cedar that policy shape looks like:
```cedar
// Allow only production runtimes of the planner agent,
// attested by the assistant-domain instance issuer.
permit (
principal,
action == Action::"payments:read",
resource
)
when {
context.token.client_id == "planner-agent" &&
context.token.act.iss == "https://issuer.assistant.example" &&
context.token.act.sub_profile.contains("client_instance") &&
context.token.act.sub like "spiffe://assistant.example/ns/agents/production/*"
};
```
Three token-claim checks plus one prefix match against the SPIFFE namespace. None of this is expressible interoperably from standard OAuth token data alone without instance identity in the chain.
# ๐ก๏ธ What Does Not Change
The point of the proposal is the surface area it does not move.
- **`client_id` remains the trust anchor.** The OAuth client is still the registered application. Authorization decisions, scope grants, consent records, and admin policy continue to attach to the client, not to instances.
- **No new grant type.** `authorization_code`, `client_credentials`, `refresh_token`, `jwt-bearer`, and `token-exchange` keep their existing flows. An instance-attestation input is made available on those flows, but the grant types themselves are unchanged.
- **No new claim shape.** `act`, `sub_profile`, and `cnf` already exist. The proposal adds one profile value (`client_instance`). Everything else on the token side is reuse.
- **No new client type.** Clients do not become "instance-aware clients" or any other new category. They register as today (statically, via Dynamic Client Registration, or via CIMD) and optionally include `instance_issuers` in their existing metadata.
- **OAuth revocation does not change.** [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009) revocation and [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662) introspection work as before. Issuer-side revocation is a complementary upstream control.
The protocol does not need to learn about instances. It already knows about actors. We just need to put instances in that slot.
# ๐ชง Open Design Choice: `actor_token` or `client_instance_assertion`?
> **Resolved in `-01`.** This section documents an open question from when the post was first published. The draft has since chosen the dedicated `client_instance_assertion` parameter for ordinary grants and kept `actor_token` for the token-exchange grant. The analysis below is preserved as the record of why.
`-00` used RFC 8693's `actor_token` parameter, extended to grants beyond token exchange, to deliver the instance assertion at the token endpoint. The alternative was a dedicated request parameter, `client_instance_assertion`. The access-token result is the same either way, so the question was only which token-endpoint wire form is cleaner.
The two options produce identical access tokens. Both place the validated runtime in `act` for delegated flows or in `sub` for self-acting flows. The actor model on the token side does not change. What changes is the wire form at the token endpoint.
## The Two Wire Forms
With `actor_token` (the `-00` form, shown for an authorization code redemption):
```text
grant_type=authorization_code
&code=
&client_id=planner-agent
&actor_token=
&actor_token_type=urn:ietf:params:oauth:token-type:client-instance-jwt
```
With `client_instance_assertion` (the `-01` form for ordinary grants):
```text
grant_type=authorization_code
&code=
&client_id=planner-agent
&client_instance_assertion=
```
The dedicated parameter form is shorter because the parameter name carries the type meaning that `actor_token_type` carries explicitly.
## Why `-00` Leaned Toward `actor_token`
These were the load-bearing arguments for reusing `actor_token`:
- **Avoiding token-endpoint fragmentation.** Reusing `actor_token` / `actor_token_type` keeps the wire surface unified instead of adding per-profile request parameters.
- **Reuse of an existing Token Exchange slot.** RFC 8693 already uses `actor_token` for the party currently acting in a token exchange request. `-00` extended that same input shape to other grants instead of creating a second way to present actor evidence.
- **Future profile extensibility.** Profiles can define their own `actor_token_type` URIs without each needing a new request parameter.
- **Semantic continuity.** In delegated flows, `actor_token` carries the evidence that becomes the issued token's `act` entry. In self-acting flows, the same evidence is represented in `sub`. The parameter names the acting-party evidence without forcing every output token to contain `act`.
## Why `-01` Switched to `client_instance_assertion`
- **Semantic precision.** The parameter name describes exactly what is being presented: proof of the concrete runtime instance acting for the registered client.
- **Avoids stretching `actor_token` semantics in self-acting flows.** In a `client_credentials` request, the runtime is not an actor in the RFC 8693 sense. It is the principal. A neutrally named parameter avoids that stretch.
The choice is bounded to the wire form. Everything in [What Does Not Change](#-what-does-not-change) still applies regardless of which parameter is used.
> The wire parameter says what is being proven. The actor chain says what it means. `-01` gives those two things separate names, `client_instance_assertion` for the input and `act` for the meaning, while keeping `actor_token` for the token-exchange case where the input is already an actor.
# โ๏ธ The Alternative: Promote `client_instance` to the Protocol
The wire-form choice in the section above stays inside the actor model: both `actor_token` and `client_instance_assertion` deliver runtime identity into the existing `act` claim. A more structural alternative would be to promote `client_instance` to a top-level OAuth concept of its own. That path is worth describing because it is the obvious next move once you accept that instances matter.
That path looks like this:
- A new request parameter such as `client_instance_id` at the token endpoint, paired with a `client_instance_assertion`.
- A new top-level access token claim, `client_instance`, distinct from `client_id` and from `act`.
- A new grant type, or a parameter retrofit across every existing grant, signaling that an instance attestation is being presented.
- A new client registration mode so authorization servers can tell instance-aware clients apart from legacy ones.
- New introspection fields, new revocation scopes, and a new consent dimension once instances are visible to end users.
It has surface appeal. Instances become visible at the protocol layer the same way clients are. A first-time implementer reading a token would see an "instance" field labeled as such, rather than a typed entry inside `act`. Tooling that today renders `client_id` could render `client_instance` next to it without learning anything about actor chains. For people whose mental model starts at OAuth 2.0 and stops short of Token Exchange, that is a real ergonomic gain.
The cost is that every layer of the stack has to learn the new concept. Authorization servers, libraries, introspection endpoints, audit pipelines, consent UI, policy engines, API gateways, and downstream resource servers all need new code paths. The Actor Profile already paid the cost of teaching the ecosystem typed, key-bound delegation, and the new concept overlaps almost entirely with what `act` already represents: a non-principal identity exercising authority on behalf of someone, with its own type, key binding, and chaining rules for sub-actors. Promoting `client_instance` to the protocol layer would create a parallel mechanism alongside it. Two ways to say "the runtime currently acting is X." Two introspection shapes to learn. Two policy entry points. Two places where audit chains can disagree about who acted and when.
The actor framing chooses a smaller tradeoff: one profile value (`client_instance`), one actor-token type or one dedicated request parameter depending on the wire-form choice, one optional client metadata field (`instance_issuers`) that rides on whatever registration channel the deployment already uses, and the rest is reuse. AS code that already validates `act` and `cnf` extends naturally. Resource server policy that already evaluates `act` adds one branch for the `client_instance` profile. An introspector that does not know about instances still sees a structurally valid actor chain and renders it as one.
> The choice is not about whether instances are important. The choice is about whether to make instance identity a new thing the protocol must learn, or to treat it as a specific kind of actor the protocol already knows how to carry.
Spending the protocol-extension cost twice for the same expressive content is the failure mode worth avoiding.
The full design space, summarized:
| Approach | What changes | Ecosystem cost |
|---|---|---|
| Status quo (per-vendor extensions) | Per-vendor proprietary claims, headers, and runtime-identity formats | High: adapters needed everywhere, no cross-vendor composition |
| `actor_token` (token-exchange grant) | One new `actor_token_type` URI | Low: AS validates a new type, no new request parameter |
| `client_instance_assertion` (`-01`, ordinary grants) | One new request parameter | Low: AS validates a new parameter, same `act` representation downstream |
| Top-level `client_instance` claim | New request parameter + new access-token claim + new policy / introspection / audit paths | High: every layer of the stack learns a new concept |
# ๐๏ธ Why This Is a Primitive
The proposal's restraint is what makes it load-bearing for downstream work. Instance identity is a primitive that several open lines of work depend on:
- **[Mission-Bound OAuth](/series/mission-bound-oauth/)**, now the [Mission-Bound Authorization draft family](https://github.com/mcguinness/mission-bound-authorization), consumes runtime identity without touching the Mission model. The Mission still binds the subject and the registered client; instance identity sharpens delegation chains, joins, and evidence attribution to instance granularity. In the Mission Authority Server's estate operating mode, high-consequence joins bind (subject, client, instance) rather than (subject, client), so remediation and audit name instance `planner-7f3c`, not just the `planner-agent` client.
- **Cross-app access ([XAA / ID-JAG](/notes/enterprise-saas-needs-oauth-federation-now/))** needs per-runtime policy scoping (production vs. development, customer-tenanted vs. shared) without registering separate clients per class.
- **Audit, observability, and SOC tooling** can finally answer "which runtime did this." That is the question the failure scenario opened with. Without instance identity in the token, that question has no answer.
Each of these initiatives already consumes `act`, `sub_profile`, and `cnf`. Putting instance identity in the same vocabulary means none of them needs a separate plumbing pass.
# ๐ค Common Questions
**Does this require DPoP on every issued token?**
The instance assertion always includes `cnf` so the AS can sender-constrain whatever it issues. Whether the issued access token is itself sender-constrained downstream is a deployment choice. Bearer-style downstream tokens still work. A resource server that does not enforce sender-constraint will simply not benefit from the live key check. Most deployments that go to the trouble of attesting instances will want sender-constraint downstream, because that is what makes "this specific instance is allowed to present this token" enforceable instead of just informative.
**Does this work for opaque tokens and introspection (RFC 7662)?**
Yes, if the authorization server exposes equivalent actor-profile fields in the introspection response. The opaque token itself does not carry the profile the way a JWT access token does. A resource server introspecting an opaque token evaluates `act`, `sub_profile`, and `cnf` from the RFC 7662 response instead of from token claims.
**Is this only for AI agents?**
No. The same model fits CI/CD runners, serverless invocations, mobile and desktop installs, and broker-mediated sessions. AI agents make the gap urgent because they multiply runtime cardinality and fan out into sub-agents. The underlying mismatch between logical client and runtime applies anywhere the same registered client is realized as many runtimes.
**Can different instances of the same client carry different scopes?**
Scope grants attach to the client and (for delegation) the user. Per-instance scope narrowing happens through token exchange: a parent instance trades for a sub-token with reduced scope, the sub-instance presents that, and the resource server sees the narrower scope alongside the new actor entry. The Actor Profile's chain semantics handle the rest.
**Who runs the instance issuer?**
Whoever owns the runtime platform. In practice that is the agent product vendor for hosted agent fleets, the cloud provider for managed serverless and Kubernetes (often via SPIFFE or platform workload identity), and the application vendor for desktop and mobile broker scenarios. The OAuth client lists those issuers in its `instance_issuers` metadata, which is what tells the AS that an attestation from that issuer is acceptable for this client.
**Does this require CIMD?**
No. CIMD is one delivery channel for client metadata. Static admin-provisioned registration and Dynamic Client Registration (RFC 7591) carry the same `instance_issuers` field equally well. CIMD is convenient when the `client_id` is already a URL, because it keeps the metadata under the client's own control, but the descriptor format and the AS processing rules are the same regardless of how the metadata reached the AS.
**Can the instance assertion authenticate the client too?**
Yes, optionally. A client can declare `client_instance_assertion` as a `token_endpoint_auth_method` value, and the AS will accept the endorsed instance assertion as both runtime identity and client authentication. This eliminates the need to provision a long-lived client credential per runtime. Clients with stable credentials (`private_key_jwt`, mTLS) can keep them and let the instance assertion ride alongside, identifying the runtime without replacing the credential. The simplification is opt-in.
# ๐ฌ This Is Early Work, And I Want Feedback
Both drafts are early individual submissions. The [Actor Profile](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/) and the [Client Instance Assertions draft](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-client-instance-assertion/) are on datatracker, but neither is an OAuth WG document. Before either should move forward, the approach needs to survive review by people who build, operate, or buy real systems.
The thing I am most trying to test is the central standards question: should OAuth represent runtime instance identity as actor-profile data, or does it need a first-class protocol surface of its own? My thesis is that runtime instance identity is best treated as a typed actor in the existing `act` chain, not as a new top-level OAuth concept. If that framing is wrong, if there is a class of deployment, a threat model, an existing pattern, or a sub-protocol I have not accounted for, I want to hear about it now, before more weight gets written into the draft.
Specific things I am uncertain about:
- Whether the trust delegation model (client endorses issuers in its metadata, issuer mints with a `client_id` binding, AS validates against the endorsement set) is the right shape, or whether something closer to per-attempt attestation-bundle propagation fits better.
- The wire form for the runtime attestation. This was an open question when the post went up. `-01` resolved it with a dedicated `client_instance_assertion` parameter for ordinary grants and `actor_token` only for the token-exchange grant. What is still worth testing is whether that split is the right long-term shape, or whether one wire form should cover both. The [Open Design Choice](#-open-design-choice-actor_token-or-client_instance_assertion) section above records the reasoning.
- Whether `client_instance` belongs alongside `user`, `service`, and `ai_agent` in the `sub_profile` namespace, or whether it deserves a different classification axis.
- Whether the failure scenario reflects what enterprises and vendors actually face, or whether it overdramatizes a problem that vendors have already solved internally in ways I am not seeing.
If you operate an authorization server, build a resource server or API gateway, run an agent platform, or buy enterprise SaaS at scale, your read on this is what I most want. Counter-examples are more useful than agreement. Tell me what is wrong, what is overstated, what is missing, and what is unworkable.
Logical clients describe what was authorized. Instances describe what is acting. The model that already names the second thing is the actor chain. The question for the OAuth community is whether putting instances in that slot is enough, or whether runtime identity deserves a new protocol surface despite the extra machinery that would require.
If this lands, an enterprise security team responding to an incident pulls the access token and immediately sees which runtime acted, which issuer attested it, which key was bound, and which sub-agent spawned which. Revocation targets the specific compromised attestation without disrupting the rest of the fleet. SOC tooling consumes the actor chain as portable JSON instead of per-vendor adapters.
Cross-app access policies scope to runtime classes (production vs. development, customer-tenanted vs. shared) without registering separate clients per class. Mission-bound OAuth and other higher-layer governance work compose against a primitive already in the token, in the same vocabulary every party understands. The point is not novelty. It is portability for the runtimes acting across enterprise systems.
---
# Enterprise SaaS Needs OAuth Federation Now
Canonical URL: https://notes.karlmcguinness.com/notes/enterprise-saas-needs-oauth-federation-now/
Markdown URL: https://notes.karlmcguinness.com/notes/enterprise-saas-needs-oauth-federation-now.md
[Vercel's April 2026 incident](https://vercel.com/kb/bulletin/vercel-april-2026-security-incident) was not a network breach. It was three hops through already-granted application access: a compromised third-party AI app, a corporate Google Workspace account, then internal environments and environment variables. No firewall bypass required.
That pattern is not unique to Vercel. In the November 2025 Salesforce-connected app incident, [Gainsight's postmortem](https://www.gainsight.com/blog/how-we-accelerated-a-year-of-security-work-in-weeks/), based on Mandiant's investigation, documented 285 tokens from a single integration, some issued as far back as October 2017. In 2022, [GitHub disclosed](https://github.blog/news-insights/company-news/security-alert-stolen-oauth-user-tokens/) attackers abusing stolen OAuth user tokens issued to Heroku and Travis CI to access private repositories.
**Different incidents, same exposure:** long-lived delegated access is still scattered across app-specific systems the enterprise cannot inventory or shut down quickly.
Most responses to incidents like these focus on long-lived credentials, visibility, and auditing. Those are real problems. They are also symptoms.
> The deeper failure is authority placement.
Rotating credentials and auditing grants improves operations inside each OAuth island. It does not change the fact that every new integration creates another island with its own authority to mint, renew, and revoke access. The failure is not bad operations inside each island. The failure is a model that keeps creating islands and placing authority inside each one independently. Enterprise SaaS has never really fixed that layer.
Single Sign-On (SSO) centralized login, but it did not centralize delegated access across apps. Each new integration still tends to create another client, another credential or refresh-capable artifact, another approval surface, and another vendor-local revocation workflow. The result is another OAuth island.
Enterprise SaaS needs to move from point-to-point OAuth integration to OAuth federation. For services and workloads, the model is already mature enough to adopt now. For user-delegated cross-app access, Cross-App Access (XAA) is the right standards direction. The goal is not foreign-token portability. The goal is issuer-signed assertion in, local access token out, with more of the governance around long-lived delegated access moved upstream to the enterprise issuer.
# Why OAuth Islands Are A Real Security Problem
Imagine an internal assistant that reads a Jira issue, updates Salesforce, and posts the result to Slack.
What sounds like three useful connections is, from an enterprise security perspective, usually three separate sources of long-lived delegated access outside the enterprise Identity Provider (IdP).
An `OAuth island` is an app-specific system for long-lived delegated access outside the enterprise IdP, with its own client, credential, grant, renewal, and governance lifecycle. The enterprise did not gain one governed cross-app access plane. It inherited another OAuth island to inventory, govern, and shut down.
Every integration creates another OAuth island outside the IdP. That is why separate integrations are a real security problem. The issue is not that one OAuth flow is too complicated. The issue is that point-to-point integration keeps manufacturing hidden trust inventory outside the IdP, one app at a time.
When an OAuth grant creates long-lived delegated access, the enterprise has effectively created a new standing access path. The prompt is brief. The access it creates often is not.
Multiply that by the estate, not by one demo flow. Three apps in one workflow means three OAuth islands. Ten important workflows across the same SaaS estate do not create one shared control plane; they create dozens of long-lived delegated access relationships with different clients, scopes, storage locations, and revocation paths. Add app marketplaces, internal brokers, Zapier-style automation, and other integration runtimes, and the hidden trust inventory grows faster than most security teams can enumerate it.
Trust is often granted in a moment and exploited months or years later. That is why the Gainsight timeline matters. [Gainsight's postmortem](https://www.gainsight.com/blog/how-we-accelerated-a-year-of-security-work-in-weeks/) documented 285 tokens from a single integration spanning eight years.
[Nudge Security's research](https://www.nudgesecurity.com/post/nudge-security-unlocks-thousands-of-saas-security-profiles) finds an average of 16 OAuth grants per employee, four of them carrying data-sharing permissions. In a thousand-person organization, that is sixteen thousand OAuth grants. No security team manually governs that.
That is the operational meaning of an OAuth island. It is not just another integration. It is another place where access has to be found, understood, and shut down under pressure. Security teams do not just have to ask "was this token stolen?" They have to ask which app created it, where the credential or refresh-capable artifact is stored, which runtime is still holding it, which vendor console controls revocation, whether the grant is still renewable, and whether downstream automation copied the same access into another system.
Attackers no longer need network adjacency if long-lived delegated access already gives them application adjacency. Once a standing grant or refresh-capable path exists across apps, lateral movement becomes an authorization problem as much as a network problem.
Production systems and sensitive data should not sit one low-friction third-party authorization away from external tooling.
Each island also inherits the same technical weaknesses: shared secrets instead of asymmetric client authentication, bearer-style artifacts reusable if copied or stolen, sparse governance APIs, and no single revocation surface. These are not independent problems to fix vendor by vendor. They are what failed authority placement looks like in production. Fixing them inside each island separately is the credential hygiene response. Moving authority upstream is the federation response.
That sprawl did not happen by accident. Product-led growth, app marketplaces, and one-click integration flows made OAuth adoption frictionless by design. Every "Connect with [SaaS]" button shortened time to value and quietly extended the enterprise security perimeter. Any control that depends on a user remembering to mark the right secret, pick the right scope, or revisit the right admin screen has already failed at SaaS scale.
The problem is not one bad vendor or one misconfigured flow. It is an operating model that manufactures more long-lived delegated access than the enterprise can see, govern, or shut down.
# What OAuth Federation Means Here
`OAuth federation` means issuer-signed assertion in, local access token out. A downstream SaaS authorization server accepts an issuer-signed assertion from an enterprise issuer as input to mint its own local access token. The SaaS still decides local authorization and still issues its own tokens. What moves upstream is more of the client authentication, grant governance, and renewal authority. That is a narrower and more viable move than asking every SaaS to accept every other vendor's access token.
SSO federation centralizes login. OAuth federation centralizes more of the governance around long-lived delegated access. That distinction matters because the enterprise IdP often already governs authentication, but not what happens later inside each SaaS product when a client requests a grant, stores a refresh token, or keeps renewing access for months. Enterprises often think they centralized trust when they bought SSO. In practice, they centralized login while long-lived delegated access kept proliferating underneath it.
In practice, that means the enterprise issuer becomes the place where more client authentication, assertion issuance, and renewal authority live, while the SaaS remains the place where resource-specific authorization happens. The issuer can decide which clients or workloads are even eligible to request assertions, under what conditions assertions may be issued, how keys are managed, whether risk signals block issuance, and when renewal authority should stop. The SaaS still decides whether the presented assertion is acceptable for its own APIs and what token it is willing to mint from it. That separation is what makes the model viable: governance moves upstream without turning every SaaS into a passive resource server for foreign tokens.
That shift changes the blast shape in incidents like Vercel, Gainsight, and GitHub. Federation does not erase local access-token risk, but it reduces how much standing delegated access has to live in vendor-local systems in the first place. Fewer shared secrets and refresh-capable paths have to be recreated app by app, renewal authority moves closer to the issuer the enterprise already governs, and shutdown depends less on visiting every vendor console one by one.
Two deployment lanes matter here. Workload and service federation is already possible in many cases with generic assertion grants such as [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523). User-delegated cross-app federation is the harder problem, and that is where XAA matters most. XAA is being standardized in the IETF through [Identity Assertion Authorization Grant (ID-JAG)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/). OAuth federation is the broader architectural pattern; XAA is the user-delegation standards direction inside it.
## Workload And Service Federation
For workloads, services, and many agent-to-SaaS cases, the excuse phase is over. Assertion grants such as [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) already let a client present an issuer-signed assertion to a SaaS token endpoint and receive a local access token in return. That is issuer-mediated federation for machine access. It moves key management, workload identity, and assertion issuance upstream while the SaaS keeps control of its tenant, scopes, and API contract.
In a concrete service flow, a CI job, backend service, or agent runtime can obtain an issuer-signed assertion from the enterprise issuer, exchange it for a local access token, and avoid storing another shared secret in a runtime or build system. The SaaS still mints the token and still enforces its own API policy. The same model works for a deployment pipeline that needs to open a Jira ticket, update a status page, and post to Slack without embedding three separate vendor secrets in the build system. That is already enough to replace many shared-secret integrations with a more governable model.
Buyers should start treating that as baseline machine-access support, not as advanced federation plumbing. If a SaaS product still requires every service, pipeline, or agent runtime to register another app-local client and store another long-lived secret, the customer is still being asked to operate another OAuth island.
## XAA For User Delegation
For user delegation, XAA is the SSO moment for cross-app access. If OAuth federation is the architecture, XAA is the clearest standards expression of that architecture for user-delegated cross-app workflows. It is the right bet because it standardizes trust reuse, not token sharing. The enterprise keeps the issuer relationship it already operates. The downstream SaaS keeps its own authorization server and its own local access tokens. The resource server does not have to learn another vendor's access-token format or accept a foreign token directly. The workload and service lane is more mature today. The user-delegation lane has now become urgent enough that XAA should be treated as the next enterprise default, not as a distant standards project.
Consider the Jira/Salesforce/Slack assistant from the opening. Today that workflow requires three separate user-delegated authorization setups: the user redirected into Atlassian for consent and a Jira-local refresh token, redirected into Salesforce for consent and a Salesforce-local refresh token, and redirected into Slack for consent and a Slack-local refresh token. Three vendor consoles to configure. Three standing delegated grants to govern. Three separate revocation paths when the workflow is decommissioned or the user's access changes.
As those workflows multiply across an enterprise and agents begin running them continuously on users' behalf, the user-delegation surface compounds the same way the workload surface does. XAA addresses this directly: the user authenticates once to the enterprise issuer, and each downstream SaaS accepts the resulting identity assertion as the input to its own local delegated grant. The approval surface consolidates at the enterprise issuer. The standing grant state moves upstream. The three downstream SaaS products still mint their own local access tokens and still enforce their own API policy, but the enterprise controls the authorization input rather than rebuilding it separately inside each one.
The downstream SaaS should be able to treat an enterprise-issued identity assertion as the input to a local delegated grant, instead of forcing every cross-app workflow back through another isolated vendor-local authorization setup.
For XAA-style user delegation, the comparison looks like this:
| Area | Point-to-Point User Delegation | XAA-Style User Delegation |
|---|---|---|
| Trust setup | Client registration and delegated access are configured separately in each SaaS product | The enterprise issuer and downstream SaaS establish trust for identity assertions, even if some local SaaS policy or client configuration remains |
| User authentication path | The user is redirected into each SaaS product's own authorization flow | The user authenticates to the enterprise issuer, and the downstream SaaS accepts the resulting identity assertion as the grant input |
| Approval and governance | Consent and admin approval are scattered across vendor consoles | Approval can move toward enterprise-administered policy, pre-authorization, or issuer-layer governance, depending on deployment |
| Long-lived delegated state | Vendor-local refresh tokens and app-specific delegated grants accumulate per workflow | Local access tokens can still stay local, but grant and renewal governance move upstream, reducing vendor-local standing delegation |
| Resource server impact | Each SaaS resource server depends on a separate app-local delegation flow | The resource server still depends on its own local access-token contract and does not need to accept a foreign access token |
| Response and shutdown | Revoke, rotate, and audit one app at a time | Cut off issuer-side assertion issuance or renewal authority centrally, then let local access tokens expire |
Notice what does **not** change. In XAA, the downstream SaaS still decides what scopes, tenant rules, and resource access it will allow. That is the key bet: change the grant path without giving up downstream authorization or asking resource servers to accept foreign tokens.
**Why XAA is the right bet:**
- It reuses issuer trust the enterprise already established for SSO instead of rebuilding long-lived delegated access inside every app.
- It preserves downstream SaaS sovereignty because the SaaS still mints and enforces its own token.
- It avoids foreign-token portability, so resource servers do not need to accept another vendor's access token format.
- It gives the market an incremental adoption path instead of requiring a full inter-vendor re-architecture before anything improves.
This is why OAuth federation beats more point solutions. The enterprise should not have to rebuild the same access model separately inside Atlassian, Salesforce, Slack, and every other SaaS product just to let one approved workflow cross app boundaries. OAuth federation is not valuable because it removes every downstream token risk. It is valuable because it centralizes the parts of the model that are most expensive to govern and most inconsistently implemented across vendors. Without federation, the enterprise inherits the OAuth maturity of every vendor it authorizes.
# Why Agents Change the Economics
Point-to-point OAuth integration survived because the integration set was bounded. Human-operated SaaS deployments had a finite set of important apps, a finite set of approved integrations, and a slow enough rate of change that organizations could tolerate too many vendor-local clients, secrets, grants, and refresh paths without the whole model collapsing. Agents break that assumption. As argued in [OAuth for Open-World Ecosystems](/notes/oauth-for-open-world-ecosystems/), agents do not operate in closed worlds. They discover tools and protected resources at runtime, encounter authorization servers they were never pre-configured to know, and expand the number of token acquisition paths far faster than any human integration team can manage manually.
That changes the economics as much as the security model. In a point-to-point world, every new tool, app, or API means another client registration, another secret or key, another consent or approval surface, another renewal path, another audit record, and another incident-response dependency. When one human workflow turns into fleets of agents and sub-agents traversing dozens or hundreds of apps and resources, the trust surface does not grow linearly. It compounds.
Agents do not simply make the old model noisier. They make it operationally bankrupt. Organizations hit a form of authorization bankruptcy: more long-lived delegated access relationships than they can inventory, review, rotate, or revoke before the next wave is already in production. At that point, failing to federate stops being tolerable architecture debt. It becomes an existential scaling problem.
This is also why the adoption chicken-and-egg is finally breaking. Historically, apps were reluctant to pay the implementation cost of federation for one or two IdPs, and IdPs were reluctant to invest until enough apps supported it. Agents are collapsing that standoff because neither side can afford to keep building point-to-point trust at agent scale.
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/specification/2025-11-05/basic/authorization)'s [Enterprise Managed Authorization](https://modelcontextprotocol.io/specification/2025-11-05/basic/authorization#enterprise-managed-authorization) and similar platform moves are building OAuth federation into the agent integration layer because the alternative does not scale. Agents force the market to confront what enterprise SaaS could previously postpone: point-to-point long-lived delegated access is no longer economically or operationally viable as the default.
SSO followed the same pattern. It became table stakes because SaaS moved application identity outside the reach of Active Directory and the corporate network, and the old model no longer scaled. Over time, SSO stopped being a premium feature and became part of the baseline for serious enterprise software. Long-lived delegated access is now in the same position one layer down. SSO solved the question of who signed in. It did not solve who can keep acting later across app boundaries. Agents are forcing that question into the open.
# What The Transition Looks Like
This is a staged transition, not a single cutover.
**Now:** use generic assertion grants where vendor support exists. [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) already gives workloads, services, and many agent-to-SaaS integrations a path where a client presents an issuer-signed assertion at a SaaS token endpoint and receives a local access token in return. That does not solve the whole end-to-end problem, and it does not eliminate all per-product registration. But it is real progress away from per-app shared secrets and toward upstream trust. It also enables more just-in-time enterprise access flows, where services, pipelines, and agents can obtain short-lived access when needed instead of depending on long-lived credentials that were provisioned in advance.
**Next:** adopt XAA as the standards direction for user-delegated cross-app access. The current IETF work for that is [ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/). It supports the same enterprise outcome, upstream trust with downstream local access-token issuance, but vendor support is less mature. The service and workload lane is more mature today. The user-delegation lane is mature enough in standards shape, market need, and buyer pressure that XAA should now be treated as the direction to build toward. It can support architectures where approval and governance move closer to the enterprise identity layer, but the exact approval and consent model remains a deployment choice, not something the protocol guarantees by itself. The pattern also extends beyond the workforce IdP to CIAM platforms and internal service identity wherever the downstream authorization server already trusts an issuer for SSO and subject resolution. See [ID-JAG Beyond the Enterprise IdP](/notes/id-jag-beyond-the-enterprise-idp/).
The transition has three layers. Now is assertion-based federation for workloads, services, and many agent integrations inside today's SaaS reality. Next is stronger standardization for user-delegated cross-app access through XAA, with ID-JAG as the current IETF work. Later is the broader open-world agent problem, where clients discover tools and authorization servers at runtime and OAuth has to keep evolving beyond today's mostly closed-world SaaS assumptions. That later stage does not make the urgency any less. It makes the need to stop building new islands even clearer.
Pieces of this are shipping today, but unevenly. [Okta's Cross-App Access docs](https://help.okta.com/oie/en-us/content/topics/apps/apps-cross-app-access.htm) show a user-delegated path that still assumes an existing app relationship, [Microsoft Entra](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation) shows workload identity federation in production, and [Salesforce](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_jwt_flow_ca.htm&language=en_US&type=5) shows JWT Bearer support with prior approval. That is not the finished cross-SaaS end state, but it is enough to prove the building blocks are real and deployed.
The important market point is that enterprises do not need to wait for a perfect end state to change direction. The transition can start anywhere a SaaS token endpoint is willing to accept an issuer-signed assertion instead of forcing another shared secret or another long-lived vendor-local credential. Each time that happens, one more integration moves from another OAuth island toward an issuer-mediated model. The strategic shift is cumulative even before the market reaches a fully standardized cross-app model.
**For workloads and services, adopt issuer-mediated federation now. For user-delegated cross-app access, adopt XAA as the standards direction.**
# What OAuth Federation Does Not Do
OAuth federation is scoped by design, not by compromise. It changes where long-lived delegated access is governed. It does not replace every control in the stack, and it should not. That scope is what makes the model viable for downstream SaaS adoption.
- **It does not remove local authorization.** Downstream SaaS products still decide what scopes, tenant rules, and resource access they will allow. The SaaS keeps its authorization server and its own local access-token policy.
- **It does not eliminate registration.** Some SaaS products will still need local trust configuration to accept issuer-signed assertions. Federation reduces ongoing credential sprawl; it does not collapse setup to zero.
- **It does not rescue weak issuer governance.** Moving authority upstream only helps if the enterprise identity layer actually governs clients, workloads, approval, and renewal well. A poorly governed issuer just centralizes the failure point without reducing the risk.
- **It does not create a single global kill switch.** Cutting off issuer-side assertion issuance or renewal authority is more powerful than hunting vendor consoles one by one, but local access tokens may still need to expire, be revoked, or be subject to tenant-side controls.
- **It does not eliminate token risk at the edge.** Local access tokens still benefit from short lifetimes, sender-constraint, and stronger runtime controls.
These are not objections to federation. They are the design choices that make the model work: the enterprise takes more control of the grant path, the SaaS keeps control of resource authorization, and neither side has to give up what it owns. The right mental model is "federate the grant, keep the token local."
# Stop Building Islands
The April 19, 2026 Vercel incident did not create this problem. It made it harder to ignore.
The immediate step is practical: pull an inventory of active OAuth grants and refresh-capable artifacts across your top SaaS integrations, ask when the oldest credential was issued, and ask whether you could cut off renewal without visiting every vendor console. If you cannot inventory, revoke, and shut down long-lived delegated access centrally, you are still operating OAuth islands. But inventory is triage, not the strategy. It tells you how many islands you already have. It does not change the model that keeps creating new ones.
The strategic shift is now clear. For workloads and services, issuer-mediated federation should be a baseline enterprise requirement. For user-delegated cross-app access, XAA should become the standards answer. Chief Information Security Officers (CISOs), identity leaders, and security buyers should force both shifts into the market: make OAuth federation part of the Request for Proposal (RFP), part of vendor security reviews, and part of renewal conversations. That means asking directly: Can you accept issuer-signed assertions and mint local access tokens for workloads and services today? Do you support XAA, or do you have a credible XAA path for user delegation? Do you expose asymmetric client authentication, grant inventory, centralized revocation, and emergency shutdown paths? The core buyer question is not whether each vendor runs its island well. It is whether the island still has to exist.
> After SSO standardized login, enterprise SaaS should standardize cross-app access around the same pattern: issuer-signed assertion in, local access token out.
Vendors that still require another app-local client, another stored long-lived artifact, and another vendor-local revocation path for each workflow are not offering a stronger default. They are asking customers to carry another OAuth island. The market does not need better-run islands. It needs vendors to stop building them.
---
# AAuth Now Has a Mission Layer
Canonical URL: https://notes.karlmcguinness.com/notes/aauth-now-has-a-mission-layer/
Markdown URL: https://notes.karlmcguinness.com/notes/aauth-now-has-a-mission-layer.md
**Update, July 2026.** This review was written against `draft-hardt-aauth-protocol-01`. That submission ended at `-02` and is marked Replaced on the datatracker. The active specification is [draft-hardt-oauth-aauth-protocol](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/), at revision `-09` as of July 2026. I re-verified this post's protocol claims against `-09`. The mission blob's required and optional fields, the `AAuth-Mission` header, the two-state lifecycle and its companion-spec deferral, revocation by `jti`, pairwise identifiers, and the non-subset delegation rule are all unchanged. Where terminology moved between revisions, the change is noted inline.
When I wrote [Mission Architecture on AAuth](/notes/mission-architecture-on-aauth/), AAuth was a proposal with a strong identity and choreography story and an open question at the governance layer. My argument was that it looked like a better clean-sheet substrate than OAuth for agent authorization, but that it still did not clearly define where durable task authority would live.
The published [draft-hardt-aauth-protocol-01](https://datatracker.ietf.org/doc/draft-hardt-aauth-protocol/) changes that argument materially.
The earlier post made a narrow claim:
> AAuth looked like a better clean-sheet substrate than OAuth for agent authorization, but it still did not clearly remove the need for a Mission-like governance object above the protocol.
That claim is no longer accurate as written. Mission is now a first-class concept in the published protocol. There is a mission proposal and approval flow, an `AAuth-Mission` header, mission-aware token choreography, and PS governance endpoints for interaction, permissions, and audit. The protocol moved toward exactly the kind of governance layer the earlier post said was missing.
That does not mean the architecture is finished. What it means is that the question has shifted. The draft closes the "where is the mission layer?" question. It does not close the "what authority does the mission actually confer?" question. The gap is no longer whether AAuth needs a mission layer. The gap is whether the mission layer it has is semantically rich and lifecycle-complete enough to do the work that durable task authority actually requires.
The honest answer is mostly the latter. That is what this post works through.
Before getting into the details, the current state of the draft is straightforward:
| Question | Does the draft answer it? | Notes |
| --- | --- | --- |
| Is there now a mission object in the protocol? | Yes | Mission proposal, approval, mission log, and `AAuth-Mission` header are defined. |
| Can systems correlate actions to the same mission across hops? | Yes | Mission correlation travels through the `approver` and `s256` values. |
| Can independent systems enforce the same mission semantics consistently? | No | Mission meaning still depends on local interpretation and local policy. |
| Can mission revocation guarantee runtime stop? | No | Runtime stop still depends on token lifetime, revocation support, and execution architecture. |
# What the Earlier Post Got Right
The earlier post got three big things right, and they still hold.
First, AAuth is a cleaner protocol substrate than OAuth for agent systems. Signed requests with proof of key possession, PS-mediated deferred authorization, and explicit clarification as a first-class operation all provide a stronger base shape than stretching classic OAuth.
The chaining difference matters most. OAuth Token Exchange (RFC 8693) does give OAuth a delegation chain story, but those chains are not PS-mediated, not correlated to a governance object at each hop, and not visible to a central authority that can enforce mission context across the full chain. AAuth's chaining is architecturally different in ways that matter specifically for mission governance.
Second, better protocol choreography is not the same thing as durable authority governance. A protocol can be conversational and chain-aware without defining a durable authority object.
Third, the missing piece looked like a Mission-like governance object rather than just another purpose string or another token field. That point is now partly validated by the protocol itself, because the published draft introduces Mission as a first-class governance concept.
That is the core delta.
# What This Closes From the Earlier Argument
The published draft now gives Mission a real place in the protocol:
- a **Mission** as a scoped authorization context
- mission creation and approval at the **Person Server (PS)**
- an `AAuth-Mission` request header
- mission context that can be carried in resource tokens and auth tokens
- mission lifecycle and mission log semantics
- governance endpoints for interaction, permissions, and audit
This is a meaningful change. The older post said AAuth still appeared to need a Mission-like governance object above the protocol. The published draft now has one in the protocol. The architecture is no longer only about agent identity, proof-of-possession, and token choreography.
The mission-aware flow in `draft-hardt-aauth-protocol-01` works as follows:
1. The agent obtains an agent token from its agent server, since renamed the agent provider.
2. If it wants governance, it sends a mission proposal to the PS's `mission_endpoint`.
3. The PS may involve the user through review, approval, or clarification.
4. On approval, the PS returns an `AAuth-Mission` header containing the `approver` and `s256` hash of the approved mission body.
5. The agent includes that `AAuth-Mission` header on later resource requests.
6. A mission-aware resource can include the mission object in the resource token it returns.
7. The agent presents that resource token to the PS as part of the authorization flow.
8. The PS evaluates the request against mission context before issuing an auth token directly or federating with the resource's Access Server (AS).
Later revisions also define a managed two-party mode, where the resource grants access through its existing infrastructure without PS mediation. The governance analysis here concerns the PS-mediated modes.
The approved mission is now a real protocol object. The mission blob must include:
- `approver`
- `agent`
- `approved_at`
- `description`
It may also include:
- `approved_tools`
- `capabilities`
That is enough to say the published protocol now has a mission layer in a meaningful sense.
It also means several claims from the earlier post need to be revised.
Mission is now clearly first-class in the protocol. Approval is part of the core governance flow. Mission context survives the initial approval event and travels through token choreography. The PS has a genuine governance role rather than serving only as a broker in authorization exchanges.
That governance role is architecturally significant. The PS is playing policy-definition and decision roles in the mission governance layer, while also acting as the user's interaction proxy for clarification and consent. Combining those roles in a single entity keeps the governance stack coherent but also makes the PS a critical dependency for every authorization decision in the chain. That concentration has real implications for availability, trust model separation, and deployment topology that are worth keeping in mind as the architecture matures.
That is real progress.
# What Still Does Not Close
The published draft materially changes the comparison. It does not eliminate the harder parts of the Mission argument.
## 1. Mission is still text-first, not authority-model-first
This is now a real mission layer, but it is still not a portable authority model. The approved mission body includes structured top-level fields, but the actual mission scope still centers on a Markdown `description`.
That is a design choice, not an oversight. Understanding why it matters requires being clear about two fundamentally different models for what a mission is in a multi-system context.
**The AAuth model: mission as governance object, with enforcement coordinated but not fully shared.**
In the published protocol, Mission sits at the PS as a governance artifact. The PS approves it, carries it, correlates it, and governs it. The AS and Resource, as downstream systems, receive mission context and participate in token issuance and enforcement. What the protocol does not provide is a shared, portable authority derivative that all of them evaluate the same way. Each system still combines mission context with its own local policy and its own interpretation of the request. The PS can refuse future token issuance, and any resource can refuse a request, but the draft does not require independent systems to converge on the same interpretation of what "inside the mission" means.
**The Mission Authority Model: mission as shared enforcement artifact, compiled from approved intent.**
The mission series argues for something different. Approved intent should produce a machine-evaluable derivative: a structured representation of which resource classes, actions, constraints, stages, and delegation bounds are actually inside the mission. That artifact travels with or can be derived from the mission. Multiple PSes, ASes, and Resources evaluate consistently against the same bounded authority envelope. Enforcement is shared because the authority model is shared.
The difference is not just technical. It is a question of what "mission governance" actually guarantees.
In the AAuth model, governance means the PS approved the intent and can revoke or log against it. Whether a specific downstream action was actually inside that intent is still evaluated through local policy and local request interpretation at each participating system. The protocol does not guarantee that independent implementations will converge on the same answer about mission containment.
In a shared enforcement model, governance means the authorized boundary can be evaluated consistently by independent systems. A compliant implementation that approves an out-of-scope request is wrong, not just permissive.
OAuth readers will recognize the pattern. Two OAuth authorization servers can treat the same `scope` value differently and still be spec-compliant, because OAuth standardizes scope syntax, not scope semantics. AAuth inherits that problem at a higher semantic level: mission prose is richer than a scope string, but still not a shared authority model.
The practical consequences show up in three places.
**Audit.** Mission correlation (was this request associated with an approved mission?) is achievable with the AAuth model. Mission containment (was this request actually inside the mission's authority?) requires the shared enforcement model. Correlation supports traceability. Containment is what high-governance environments actually require.
**Attenuation.** If mission authority lives in prose, there is no formal way to prove that a downstream token is a subset of the approved grant. A PS can assert it and a resource can accept it, but neither can prove it. A Mission Authority Model is the prerequisite for making attenuation provable rather than merely asserted. This is also what makes gap #4 on downstream attenuation harder than it looks: the attenuation problem cannot be cleanly solved without first solving the authority model problem.
**Cross-domain enforcement.** Two independent PSes can correlate the same mission through the hash. They cannot enforce it consistently unless they share a semantic model of what the mission permits. The AAuth protocol makes cross-domain mission correlation interoperable now. Cross-domain enforcement remains local to each deployment.
The draft's optional `approved_tools` field does not close this gap. A tool list is a permission set, not a bounded authority envelope. It says which tools may be called. It does not define the resource classes those tools may touch, the actions they may perform within those classes, the stage constraints on when each applies, or the explicit exclusions the approver intended.
This is also where [Rich Resource Requests (R3)](https://dickhardt.github.io/AAuth/draft-hardt-aauth-r3.html) matters, even though it does not fully solve the authority-model problem. R3 is a companion draft that defines structured authorization vocabularies for resources: mappings to MCP, OpenAPI, gRPC, and similar formats that let resources describe their operations precisely. The core protocol has since leaned into it, with current revisions recommending that resources advertise an R3 vocabulary (`r3_vocabularies`) in their metadata. Resources publish R3 documents. The PS or AS evaluates them. Auth tokens carry back the result as specific granted operations (`r3_granted`) and operations requiring per-call parameter validation (`r3_conditional`). The semantic richness is for the authorization layer, not the agent:
- **The Resource** describes what it offers in precise vocabulary
- **The AS** evaluates what the mission permits against that vocabulary
- **The user** sees curated human-readable consent text from the `display` section
- **The agent** carries an R3 document hash and acts on the result
Agents carry R3 document hashes without reading the definitions themselves. The authorization layer does the semantic work server-to-server.
What R3 contributes is a more structured authorization surface. Instead of comparing mission prose against bare scope strings, the PS can compare mission intent against a precise vocabulary of what the resource actually permits. That narrows the gap between approval and enforcement without closing it. It is a more structured surface for local evaluation, not a shared authority model.
It is worth being direct about why AAuth makes this tradeoff. Standardizing a compact mission object with human-readable description is tractable today. Standardizing a universal authority language is not, at least not before there is enough implementation experience to know what fields, selectors, constraints, and stage semantics actually need to interoperate across real deployments. The AAuth approach lowers coordination cost sharply and makes early adoption realistic. The Mission Authority Model approach is more correct about what strong governance eventually requires. Both can be true at the same time.
The practical path is layered. Early deployments use mission text plus PS-local heuristics. Stronger implementations add locally compiled authority artifacts behind the PS. `R3` improves authorization precision in the meantime by giving the PS structured resource vocabularies to reason against. A later companion profile can standardize a portable Mission Authority Model once there is enough operational evidence to know what actually needs to interoperate. The current draft creates the protocol slot. It does not fill it. (The Mission-Bound Authorization family has since filled it: the [AAuth binding](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-aauth.html) carries the Mission record through the mission blob's own members, with the integrity anchors computed as a projection of AAuth's `s256` commitment and three verification modes up to Resource-verifiable.)
## 2. Lifecycle exists, but remains intentionally narrow
In `draft-hardt-aauth-protocol-01`, a mission has two states: `active` and `terminated`. Completion is the expected path into termination. The draft defers revocation mechanics, delegation tree queries, and administrative interfaces to a companion specification.
That is meaningful progress over no lifecycle at all, but also a deliberately minimal model with real consequences.
**The AAuth model: mission has existence and completion, not rich lifecycle semantics.**
The protocol establishes that missions exist, that they can be approved, and that they end. What it does not establish is *why* they end or what that means for participants downstream. Termination is termination. Normal completion looks the same as operator cancellation, which looks the same as policy suspension, which looks the same as business-driven revocation. Each arrives at the same protocol state: `terminated`.
Outstanding token requests, in-flight agent operations, and queued work all see the same signal regardless of the reason. What they do with it is up to each implementation.
**The mission series model: lifecycle states carry semantic meaning that downstream systems need.**
A richer lifecycle model would likely need to distinguish states that have different operational implications, for example:
- *Completed*: task finished successfully; outstanding tokens may drain normally
- *Suspended*: temporarily paused for review; downstream operations should queue, not abort
- *Cancelled*: business-driven stop; outstanding work should roll back where possible
- *Terminated for policy violation*: immediate halt; already-issued tokens should be treated as invalid even if not yet expired
- *Timed out*: deadline exceeded; tokens within window may complete, new ones should not issue
These are not academic distinctions. They determine what downstream agents and resources should do when they receive the signal.
**The concrete failure mode.**
An agent is three days into a multi-step financial workflow. A compliance officer wants to pause it pending investigation, not cancel it. Under the current protocol there is no pause state. The compliance officer terminates the mission. The agent's running operations see `terminated` and must decide locally whether to abort, checkpoint, or drain. The downstream AS stops issuing tokens. The agent has no protocol-level signal distinguishing "hold and wait for review" from "stop and clean up."
The compliance officer now manages the suspension outside the protocol, through operational coordination, direct system access, or implementation-specific admin tooling. That is exactly the kind of problem a lifecycle model exists to solve.
**Why AAuth makes this tradeoff.**
A minimal state model is much easier to implement and much easier to align across vendors. Every additional lifecycle state multiplies both the implementation surface and the edge cases requiring interoperability. Prematurely standardizing administrative semantics before deployments have real operational experience risks standardizing the wrong model. The draft is right to defer this. The spec also gives a concrete architectural reason: there is no push channel from the PS to the agent. AAuth agents poll; they do not receive notifications. A suspended state would require the agent to learn the mission has resumed, which is not possible without polling indefinitely with no indication of when to stop. Terminating and creating a new mission scoped to the changed circumstances is the spec's recommended path.
The cost is that every deployment independently invents lifecycle semantics that should eventually be interoperable. A mission terminated in org A's PS means something locally specific. Org B's systems, participating in that mission's execution, receive a `terminated` signal and interpret it by their own rules. When the ecosystem eventually needs lifecycle semantics to interoperate across deployments (and high-governance environments will require this), each deployment's bespoke model becomes a migration problem.
The current draft is strong enough to anchor mission existence and completion. It is not yet strong enough to anchor the full administrative lifecycle that durable task authority requires. The companion specification is the right place for that work. (The family's AAuth binding now does exactly that work: the full Mission lifecycle, with `revoked` and `expired` added and the only-`active` rule gating every PS surface.)
## 3. Revocation is stronger in the control plane than in the runtime plane
The PS now has meaningful revocation authority:
- terminate missions
- deny future token requests
- revoke outstanding auth tokens through revocation endpoints where implemented
That is a real and important capability. The failure mode is not in the control plane. It is in the gap between control-plane revocation and runtime stop.
**The AAuth model: the PS can withdraw authority; whether execution stops is an implementation question.**
When a mission is terminated, the PS refuses new token requests. Outstanding auth tokens can be revoked through the revocation endpoint where that revocation path is implemented. The protocol authority is withdrawn.
One clarification on the token model matters here. Resource tokens in AAuth are not access tokens. They are credentials issued by the resource server to the agent so the agent can request an auth token from the PS. Auth tokens are what the agent presents to the resource for access. In three-party flows the PS issues them directly. In four-party flows the PS federates with the resource's AS, the AS issues the auth token, and the PS delivers it to the agent. In both cases the PS is on the critical issuance path: it can refuse to federate or refuse to issue, stopping new auth tokens from reaching the agent.
For outstanding tokens already held by the agent, the protocol defines a revocation path in both flows where revocation endpoints are implemented. In three-party flows the PS can call the resource's revocation endpoint with the auth token's `jti`. In four-party flows the PS can do the same, calling the resource's revocation endpoint directly and optionally notifying the AS. The runtime-stop argument does not depend on the AS being the revocation hop. It depends on whether revocation support exists and how quickly it propagates.
All AAuth auth tokens are also proof-of-possession: they are bound to the agent's signing key and useless without it. This means the revocation window is a risk only for the legitimate agent still holding the private key, not for an attacker who might have intercepted the token.
The practical revocation gap is therefore not about issuer identity. It is about timing and propagation: where a revocation path exists, the PS can call the resource's revocation endpoint, but an agent may already have submitted a request using a token issued minutes before revocation was triggered. If the resource has not yet processed the revocation call, the request proceeds.
What the protocol does not, and arguably cannot, define is what happens to work already in motion. An agent may hold valid auth tokens issued minutes before revocation. A downstream agent may have cached resource decisions. Queued operations may have already been submitted to external systems. Work that was authorized under the mission continues until those tokens expire, those caches invalidate, and those operations complete or fail on their own.
The practical strength of revocation depends on:
- the shortest-lived token in the chain
- how aggressively each participating resource checks revocation
- how many downstream hops already hold tokens or cached decisions
- whether queued operations in external systems can be recalled
**The mission series model: revocation needs stop semantics, not just revocation authority.**
For high-consequence agent work, revocation authority is not the same as guaranteed stop. What matters is that when the PS terminates a mission, execution actually stops across the full delegation chain within a defined and operationally meaningful bound.
That requires more than a revocation endpoint. It requires short token lifetimes, eager revocation checking at each hop, event-driven propagation to participating systems, and operational architecture built around the assumption that mission termination is a hard control signal rather than an advisory one.
**The concrete failure mode.**
A user discovers their agent is about to execute a bulk data export they did not authorize. They terminate the mission. The PS marks it terminated, calls the export service's revocation endpoint with the auth token's `jti`, and stops issuing new tokens.
But the agent already submitted a request to the export service using a token issued two minutes before revocation was triggered. The request arrived at the export service before the PS's revocation call was processed. The export service queues the request, processes the revocation call, but the queued operation is already in flight. The data export proceeds.
The PS did everything the protocol allows. Execution continued anyway. The control-plane revocation was correct and complete. The runtime stop failed.
**Why AAuth makes this tradeoff.**
Standardizing guaranteed stop semantics across arbitrary resources, caches, and execution environments is extremely hard. Different resources have different capabilities. Some can check revocation eagerly. Some cannot. Some queue work internally in ways that make recall difficult. Forcing the core protocol to guarantee instantaneous stop would require defining runtime architecture constraints that go well beyond what an authorization protocol should specify, and would exclude many legitimate resource implementations.
Revocation authority belongs in the core protocol. Runtime stop guarantees belong in deployment profiles and operational architecture.
The cost is real. In high-consequence environments such as financial operations, data access, and actions with external effects, the gap between "authority revoked" and "execution stopped" is exactly where harmful outcomes occur. Short-lived tokens and aggressive revocation checking can narrow the gap considerably, but closing it requires implementation discipline the protocol does not mandate. Deployments operating in high-stakes environments need to design for this explicitly. It will not be visible in the protocol spec alone.
## 4. Downstream attenuation is still too weak
The published draft explicitly does not require downstream authorization to be a subset of upstream scopes. The design rationale is stated directly in the spec: a flight booking API that calls a payment processor needs the payment processor to charge a card, an operation orthogonal to the upstream scope. Formal subset rules would break legitimate cross-domain delegation chains. There is no mechanism to require attenuation and no vocabulary in the core protocol to express or verify it. A deployment profile could add formal attenuation constraints without contradicting the core protocol, but it would have to define scope vocabulary and subset semantics from scratch since the core provides no building blocks.
That tradeoff is defensible in open cross-domain ecosystems. But from a mission governance standpoint, it is a significant gap in the delegation model.
**The AAuth model: chain transparency without chain containment.**
AAuth gives you provenance: the full delegation chain is visible. You can see that agent A authorized agent B, which authorized agent C, all correlated to the same mission. The audit record is complete.
What the protocol does not guarantee is containment: that each downstream grant is actually a subset of the upstream authority. The PS evaluates each hop against the mission, but since the mission is prose, that evaluation is heuristic. A PS can assert that C's request is within the mission. It cannot prove it.
**The mission series model: attenuation as a provable property of the authority chain.**
In a system with a Mission Authority Model, attenuation is a formal property. Each downstream grant can be verified as a subset of the upstream authority envelope. If the parent grant permits resource class R, action A, within stage S, then the downstream grant must specify a subset of R, A, and S or be rejected. The evaluation is mechanical, not interpretive.
That is not a theoretical nicety. It is what makes delegation chains trustworthy in adversarial or high-stakes environments.
**The concrete failure mode.**
An agent is authorized on a mission to "coordinate vendor onboarding for the Q3 contract." It delegates a sub-task to a procurement agent, which further delegates to a financial reconciliation agent. The PS evaluates each hop and issues tokens because each request sounds plausible relative to the mission description.
The financial reconciliation agent ultimately performs actions that touch live payment systems, well outside what the user intended when they approved "vendor onboarding." Each individual PS evaluation was reasonable in isolation. The cumulative chain escaped the authority envelope. No participant can prove it was out-of-scope because there is no shared, machine-evaluable representation of what "vendor onboarding" actually authorizes.
The provenance log records the chain perfectly. The audit record is clean. Nothing shows a containment violation because containment was never enforced.
**Transparency is not the same as safety.**
This is the critical distinction. Chain transparency is valuable: you can reconstruct exactly what happened and who authorized what. But observability after the fact is a different control from enforcement before the fact.
Attenuation is a pre-execution guarantee: this request is within a provably bounded subset of the original authority. Provenance is a post-execution record: here is how the authorization chain evolved.
Transparency tells you how authority evolved. Attenuation tells you whether it stayed inside a bounded envelope. Only one of them prevents harm before it occurs.
**Why AAuth makes this tradeoff.**
Formal attenuation rules that require each downstream scope to be a mathematical subset of upstream scopes work cleanly in closed, homogeneous systems. They break in cross-domain workflows where downstream resources have capability models that do not map onto upstream scope vocabularies. Forcing strict subset semantics would make the protocol unusable for many legitimate multi-domain workflows.
The tradeoff favors flexibility and deployability now. The cost is that delegation chains cannot be formally verified, only asserted. In agentic systems where chains can be deep and execution can be long-running, that is not a theoretical risk. It is the expected failure pattern when scope creep occurs across a multi-hop delegation chain. The protocol will show you the chain clearly. It will not stop the chain from expanding beyond its intended bounds.
## 5. Cross-domain mission semantics are still local
Gap #1 is about what the mission object provides within a single deployment for local enforcement. This gap is different: it is about what happens when two independently operated systems need to evaluate the same mission across a trust boundary without sharing the mission body.
### Mission correlation travels more easily than mission meaning
The protocol now carries mission correlation through the chain: the `s256` hash and approver URL appear in resource tokens, auth tokens, and audit records, a pair later revisions name the *Mission Reference*. Any system that receives these can confirm "this action was correlated to mission M, approved by PS X." That is correlation.
What it cannot do is enable a system to independently evaluate whether this action falls within what mission M permits. That requires the full mission body and a shared semantic model for interpreting it.
**Two distinct levels of interoperability.**
*Correlation interop*: two systems agree that a set of actions are associated with the same approved mission. The `s256` hash enables this. It is achievable today, across independent deployments, without sharing the mission body.
*Enforcement interop*: two systems evaluate independently whether a given action is within the mission's authority and reach consistent conclusions. This requires either sharing the mission body (which creates disclosure problems) or sharing a machine-evaluable authority model derived from the mission that is safe to propagate.
The draft achieves the first. It does not attempt the second. "Interoperable mission governance" as the protocol currently defines it means "interoperable mission correlation." Those are not the same thing.
**The concrete failure mode.**
An agent operates on a mission approved by org A's PS. Its execution requires resources hosted in org B's domain. Org B's AS federates with org A's PS and receives the mission hash and approver URL. Org B now must decide: does this mission permit the actions the agent is requesting against org B's resources?
The AS can confirm a mission was approved. It cannot evaluate what the mission permits. Doing so would require fetching and interpreting the full mission body, which creates both a disclosure problem and an interpretation problem: org B's AS may read the same prose differently than org A's PS did.
Org B's AS falls back to local policy. It might trust missions from PS X broadly, or require additional verification for sensitive resource classes. Two different deployments may apply entirely different local rules to the same federated mission. All of that variation is outside the protocol.
**The privacy-enforcement tension.**
The protocol's choice to share only the hash and approver URL rather than the full mission body is the right privacy decision. Mission bodies often contain sensitive business context. Forcing disclosure to every AS in a cross-domain workflow would be a serious privacy failure.
But that constraint means richer cross-domain enforcement requires a way to convey mission authority without conveying mission content. That is precisely the problem a portable Mission Authority Model could solve: a compact, structured, privacy-safe artifact expressing what is permitted without revealing why, derivable from the approved mission and safe to share across trust boundaries. The protocol does not define one.
`R3` can narrow part of this gap without eliminating it. Resources publish structured authorization vocabularies; auth tokens carry precise granted operations rather than bare scope strings. That gives independent systems a richer, less ambiguous surface for reasoning about what was actually authorized at each hop. It still does not tell them what the mission authorized in the first place. `R3` improves the precision of what authorization grants express and strengthens the substrate a mission-aware governance layer can use. A Mission Authority Model is still what would make mission containment portable.
Correlation is interoperable now. Meaning is not. For deployments where cross-domain systems need to enforce against mission scope rather than simply correlate with it, that is the remaining gap. It is also the hardest one to close without solving gap #1 first.
### Federation does not remove relying-party trust policy
The published draft makes real progress on cross-domain mechanics:
- explicit PS-to-AS federation with trust establishment
- pairwise user identifiers that limit cross-domain correlation
- mission object propagation through token choreography
- governance endpoints for cross-domain interaction and audit
The remaining problem is not whether the federation flow works. It is that relying parties still have to decide for themselves what trust to place in federated mission context even if mission meaning becomes somewhat sharper.
**What the federation model actually guarantees.**
When the PS federates with an external AS, the AS receives the mission hash and approver URL. It can verify the signature chain, establish trust with the PS, and issue tokens correlated to the mission. The choreography is defined. The trust relationship is defined.
What is not defined is what the AS is trusted to *accept* from the PS as sufficient mission context for its own resources. The AS sees a hash. It trusts the PS to have performed appropriate mission governance. It still has to decide, by local policy, whether that is enough for a given resource class, tenant, or risk level.
This is where the federation story remains intentionally policy-heavy. Even if future profiles improve semantic portability, a relying-party AS still has to decide which PSes it trusts, for which resource classes, and under which operating conditions. The protocol can move more mission context and more structured request meaning. It still cannot remove the relying party's obligation to set trust policy at the boundary.
**The concrete failure mode.**
An enterprise PS approves a mission that includes "coordinate document review with external counsel." The agent operates across the trust boundary, submitting documents to a law firm's AS. The law firm's AS federates with the enterprise PS.
The law firm's AS has its own data handling policies. It needs to decide: does this mission permit submission of documents marked "confidential"? Does it permit bulk submissions? Read access to the full document library, or only documents explicitly listed?
The federated trust model tells the law firm's AS that *a* mission was approved. It does not convey what the mission permits in the law firm's context. The law firm has to choose: trust the enterprise PS entirely, make conservative local assumptions, or require out-of-band clarification. None of these is a governance answer. All of them leave the cross-domain policy question unresolved at the protocol level.
If the enterprise and the law firm are both compliant AAuth implementations with different local policies, they will handle the same federated mission differently. The protocol does not and should not fully remove that discretion. Two deployments that trust each other at the identity layer may still make different authorization decisions at the policy layer.
**The privacy-enforcement tension.**
The protocol's choice to share only the hash and approver URL rather than the full mission body is the right privacy decision. Mission bodies contain sensitive business context. Disclosing them to every federated party would be a serious privacy failure.
But this creates a hard constraint on what cross-domain enforcement can look like. Richer enforcement requires conveying more about mission scope without conveying mission content. That is the gap a portable Mission Authority Model could fill: a compact, structured, privacy-safe artifact expressing permitted scope, derivable from the approved mission yet safe to share across trust boundaries without disclosing the underlying business rationale. `R3` may help on the authorization side by making granted operations more precise: the relying-party AS sees structured grants rather than bare scope strings. It still does not decide what the relying party should trust the mission to authorize.
**Why the current approach is defensible for now.**
Defining a cross-domain semantic model before there is operational evidence of what mission semantics need to cross boundaries, and in what form, would likely produce the wrong model. The federation mechanics need to work first. Trust choreography is the harder near-term problem. Semantic portability can follow once deployments show what cross-domain enforcement actually requires in practice.
The cost is that the current federation story is strong on identity correlation and trust choreography, and still dependent on local relying-party policy for enforcement. Deployments requiring richer cross-domain enforcement will need bilateral agreements, ecosystem-specific profiles, or custom projection mechanisms above the current core. That is the right place for them. But the work is not free, and nothing in the protocol spec will tell you where each relying party should draw its trust boundary.
## 6. Runtime alignment is still outside the protocol
The PS provides meaningful request-time governance:
- approve missions before execution begins
- deny permissions when requests fall outside approved scope
- log audit events for correlation and review
- relay interaction when the agent needs clarification
- refuse future token issuance when governance conditions change
That is real and necessary. It is also only half the governance problem.
**The distinction between request-time governance and runtime alignment.**
Request-time governance answers the question: *is this request authorized?* It evaluates individual authorization decisions at the moment a token is requested.
Runtime alignment answers a different question: *is the trajectory of this execution still aligned with the userโs original intent?* It evaluates the cumulative pattern of agent behavior over time, across many individually authorized decisions, each of which may have been locally correct.
These are not the same control. A protocol that solves the first does not automatically solve the second. The published AAuth draft solves the first well. It does not address the second.
**The concrete failure mode.**
An agent is authorized on a mission to "prepare a due diligence report on Acme Corp for our M&A team." The PS approves it. Each individual request is authorized: pulling filings, accessing analyst reports, querying internal deal databases. The audit log is clean.
Midway through execution, the agent begins correlating information across the deal database and external news sources in ways the analyst did not anticipate. It drafts sections that mix confidential internal deal strategy with public market data in a form the user never sanctioned. Each step is within some plausible reading of the approved mission. The PS sees authorized requests and approves them.
No individual authorization check fails. The cumulative execution has drifted meaningfully from user intent. The governance system has no visibility into this trajectory.
This is what [Mission Shaping Is Not Enough](/notes/mission-shaping-is-not-enough/) describes: an agent can remain nominally inside mission scope while drifting semantically away from the userโs actual intent. Approval at T=0 does not govern behavior at T=N.
**What runtime alignment would actually require.**
A runtime alignment layer needs capabilities the authorization protocol does not and should not define:
- continuous observation of execution state, not just authorization events
- semantic evaluation of accumulated agent behavior relative to approved intent
- staged release gates that pause before high-consequence or irreversible actions
- a feedback path from observation systems back into mission governance, so detected drift can trigger mission review or suspension
**Why this is not a protocol design failure.**
Standardizing runtime alignment inside the core AAuth protocol would be the wrong move. It would require the protocol to define agent behavioral semantics, execution monitoring, and semantic evaluation, all of which depend heavily on agent architecture, task domain, and risk model. That is not a protocol-layer problem.
The gap here is one of architectural scope, not protocol design error. The protocol provides the authorization substrate and the governance hook: a PS with mission context that can refuse tokens and relay interaction. Runtime alignment needs to be built above that hook, treating it as a feedback point in the execution loop rather than just a record-keeping endpoint.
That distinction matters for how deployments are designed. The protocol does not determine whether the PS is a passive logging mechanism or an active control point. A deployment that wires observation systems into the PS interaction endpoint and uses staged release gates before irreversible actions has far stronger runtime governance than one that treats mission approval as a one-time gate. The protocol does not make that choice for you. Deployments that assume it does will be surprised by the gap.
# The Practical Takeaway
The old takeaway was:
> AAuth looks like a better substrate than OAuth, but it still appears to need a Mission-like governance object.
The better takeaway now is:
> AAuth now has a mission layer. The right question is no longer theoretical. It is operational.
The architecture is now much closer to the one I have been arguing for:
- agent identity and proof-of-possession at the protocol edge
- a Person Server representing the legal person
- mission proposal and approval before broader authorization unfolds
- mission-aware resource and auth tokens
- PS-to-AS federation across trust domains
- governance endpoints for interaction, permission, and audit
- a richer authorization description layer emerging at the resource boundary through R3
The remaining gaps are now easier to name precisely:
- mission text is still not a full authority model, so correlation is more interoperable than containment
- lifecycle and revocation are stronger in the control plane than in guaranteed runtime stop
- attenuation across delegation chains is still too weak to make containment provable
- cross-domain mission semantics and runtime alignment still need to be built above the current protocol layer
The protocol family is no longer treating agent identity and token choreography as the whole answer. It is now building around a mission-aware governance layer and a richer authorization layer at the resource boundary, even if the deeper authority-model problem is still not fully solved. The published draft standardizes mission coordination and correlation. `R3` adds more precise request meaning at the resource boundary. Together they strengthen the substrate for governance without yet standardizing shared mission containment.
That is not a reason to dismiss the work. It is a reason to position it correctly. This is foundational work for an agent-native world because it moves the protocol stack toward mission-aware governance without overreaching into every harder problem at once. The published protocol gives Mission a first-class governance role. `R3` makes resource requests more semantically legible. That combination materially improves the substrate even before a full Mission Authority Model exists. The stack is now much stronger on mission correlation and governance hooks than it was before. It still does not standardize portable mission containment. That is the real remaining gap, and the right place for the next layer of work to explore. (That layer now exists: the family's AAuth binding profiles the Person Server as the Mission Issuer, so the family's governance, enforcement, and evidence profiles compose against AAuth directly.)
---
# Sources
- [draft-hardt-aauth-protocol-01 on Datatracker](https://datatracker.ietf.org/doc/draft-hardt-aauth-protocol/), the version this review was written against, since marked Replaced
- [draft-hardt-oauth-aauth-protocol on Datatracker](https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-protocol/), the active successor, at revision -09 for the July 2026 re-verification
- [AAuth Rich Resource Requests (R3), editor's copy](https://dickhardt.github.io/AAuth/draft-hardt-aauth-r3.html)
- [Mission Architecture on AAuth](/notes/mission-architecture-on-aauth/)
- [Open-World OAuth Still Needs Mission Shaping](/notes/open-world-oauth-still-needs-mission-shaping/)
- [The Mission Shaping Problem](/notes/the-mission-shaping-problem/)
- [Mission Shaping Is Not Enough](/notes/mission-shaping-is-not-enough/)
- [Why Mission-Bound OAuth Might Be the Wrong Answer](/notes/why-mission-bound-oauth-might-be-the-wrong-answer/)
---
# ID-JAG Beyond the Enterprise IdP
Canonical URL: https://notes.karlmcguinness.com/notes/id-jag-beyond-the-enterprise-idp/
Markdown URL: https://notes.karlmcguinness.com/notes/id-jag-beyond-the-enterprise-idp.md
[ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/), the Identity Assertion JWT Authorization Grant, is a draft OAuth specification for carrying identity assertions across OAuth authorization server boundaries without requiring interactive authorization at each hop. A user authenticates once. The client typically exchanges the resulting identity assertion via [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange into an ID-JAG. The ID-JAG is then presented to a target authorization server using [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) as the authorization grant, producing an access token for that service without a redirect, a consent screen, or a second login.
This same general pattern is also often discussed under the name `XAA`, for Cross-App Access. In this post I use `ID-JAG` for the protocol term and `XAA` only as a shorthand label readers may already recognize.
I am one of the authors of the specification, so I want to be open about where I am describing the current draft and where I am stretching into a broader architectural argument. That distinction matters here because the draft itself is already moving: the newer editor's copy adds a product and CIAM-shaped example, [Customer Identity for Developers](https://drafts.oauth.net/oauth-identity-assertion-authz-grant/draft-ietf-oauth-identity-assertion-authz-grant.html#name-customer-identity-for-devel). By `CIAM` here I mean customer identity and access management platforms such as Auth0, WorkOS, Stytch, Descope, and similar product-identity layers. That does not settle the boundary by itself, but it is a helpful sign that the older "Enterprise IdP" framing is already being tested against a wider set of deployment realities.
The argument in this post is narrower than "ID-JAG solves cross-domain authorization." It is simply that "Enterprise IdP" is too narrow a frame for the trust pattern. In the current draft, the issuer that matters is the IdP the downstream authorization server already trusts for SSO and subject resolution. The broader point is that this immediate-IdP role may be held by a workforce IdP, a CIAM platform, or an internal platform IdP depending on the deployment.
That framing matters now because product ecosystems and agentic systems are increasingly separating workforce authentication, product-local identity, and downstream authorization into different layers. The older habit of treating the top-level workforce IdP as the only IdP that matters no longer fits how many systems are actually built.
# What ID-JAG Changes
In the common user-delegated OAuth pattern, the client sends the user to the authorization server for an authorization request, the user authenticates and approves the request there, and the authorization server issues a code or other grant under its own policy. In the ID-JAG draft, the downstream authorization server for this hop is the **Resource Authorization Server (RAS)**.
ID-JAG makes a narrower move. It lets a Resource Authorization Server accept an identity assertion issued by the IdP the RAS already trusts for SSO and subject resolution as the OAuth authorization grant, instead of forcing the client to run another direct interactive authorization flow there. The IdP becomes the assertion issuer the RAS relies on for this hop. The RAS still decides whether to honor the assertion, what scopes to allow, and what token to mint.
That matters because it avoids implying a fundamental OAuth role change. ID-JAG does not redefine the underlying ownership model of the protected resource. In some deployments the effective resource owner is an individual user; in others it is a tenant- or organization-administered account model. The RAS still issues tokens under its own policy, and the resource server still enforces access to protected resources. What changes is the grant path: the RAS is willing to treat a trusted IdP-issued identity assertion as sufficient input for token issuance.
When that works well, some approval and policy work can be centralized at the IdP or its surrounding governance layer, but the downstream service does not surrender final control. The IdP mediates the cross-domain hop. The RAS still applies local policy.
What does change is where user consent and authorization visibility live. In interactive OAuth, the user sees a consent screen at each service. ID-JAG removes that interaction. The governance equivalent is pre-authorized scopes defined at the IdP or in the RAS's client registration, or admin consent granted at the organizational level. The user's visibility into which downstream services their identity assertion can reach is a policy question each deployment has to answer explicitly, through IdP-level scope governance, admin consent flows, or session-bound scope limits, rather than through interactive consent at each service boundary.
That is an operational consequence worth saying plainly. If a deployment adopts ID-JAG, it is also choosing to move visibility and approval away from the downstream service's consent screen and into some combination of IdP policy, client registration, tenant admin approval, or other governance controls. If those controls are weak or opaque, the user may lose visibility even if the protocol mechanics are sound.
That is also why ID-JAG should not be read as creating a new concentration of power at the IdP. From a threat-model perspective, an IdP that is already trusted for SSO can often already approximate a similar downstream access outcome indirectly: silently sign the user in, drive the front-channel steps of an OAuth request without the user present, and orchestrate the browser interactions needed for the client to obtain a code through equivalent choreography. In other words, an SSO-trusted IdP may already be able to simulate the consent path without the RAS changing its protocol behavior. That is not desirable, and it is not protocol-equivalent to ID-JAG. But it is why the underlying trust question is already there. ID-JAG does not create that trust position. It makes the cross-domain grant path explicit, typed, and reviewable instead of relying on brittle front-channel mechanics to approximate a similar outcome operationally.
# The Constraint Is the Trust Relationship, Not "Enterprise"
ID-JAG reads very naturally as an Enterprise IdP story. An Enterprise IdP authenticates employees. Enterprise-managed services trust that IdP and often pre-authorize certain clients and scopes. So ID-JAG lets employees move across those services without extra login prompts. That use case is real, familiar, and valuable.
But the constraint is not just "enterprise." The constraint is the issuer, client, and policy trust relationship.
The mechanism works because the RAS has established trust with the issuer for identity assertions and has policy for the client and requested access. In the enterprise examples in the draft, that includes more than generic SSO trust: the client and RAS are configured for this pattern, and the client has permission to act for the target service with defined scopes. That is the deployment shape the current draft directly specifies.
What "configured for this pattern" means in practice: the RAS has already established how it recognizes and validates assertions from the issuer it trusts for SSO and subject resolution, and maps that issuer to permitted clients and scopes. In OpenID Connect deployments that usually means issuer metadata and JWKS configuration; in SAML deployments it means the equivalent federation metadata and key trust. ID-JAG adds the grant-type and claim-processing rules on top of that existing trust relationship.
That incremental adoption path was one of the primary goals of ID-JAG. The deployment work is issuer registration, client-to-grant-type binding, and scope policy. It is not new trust infrastructure from scratch. The Resource Authorization Server does not need a new access-token format or a new token-issuance model; it is still issuing its own access tokens under its own policy. The Resource Server does not need to change which authorization server it trusts or what token claims it relies on at runtime. The main change is at the grant-acquisition boundary: which upstream issuer the Resource Authorization Server is willing to accept as input for token issuance.
From an implementation perspective, a resource authorization server can trust more than one issuer. There is no protocol constraint that says one tenant or deployment context must map to exactly one IdP. The cost is complexity: once multiple issuers are accepted, the deployment has to handle issuer routing, account linking, subject normalization, and policy mapping carefully. The constraint is operational clarity, not single-issuer exclusivity.
The broader architectural point is a little different. The protocol does not inherently require the issuer to be the top-level workforce IdP. It requires the issuer to be the IdP the downstream authorization server already trusts for SSO and subject resolution. In some environments, that will still be the enterprise workforce IdP. In others, a CIAM platform or internal platform IdP may federate upstream to the workforce IdP for employee login, but still be the IdP that downstream systems trust directly. In that shape, the chained workforce IdP is upstream authentication infrastructure, while the CIAM or platform IdP is the issuer that actually satisfies the ID-JAG trust condition for those downstream services.
That is exactly why the specification is grounded in identity assertions rather than arbitrary JWT claim sets. The issuer has a defined identity role relative to the RAS.
The word "enterprise" describes one deployment context where that relationship is common. It does not define the limits of the pattern.
# Four Deployment Contexts
## Enterprise Workforce
This is the canonical case. The Enterprise IdP is authoritative for employee identity, organizational policy, device posture, and the access rules the employer has defined. Services and internal tools accept the Enterprise IdP for SSO, and the relevant clients and authorization servers are configured for ID-JAG. Employees move across authorized services without interactive re-authorization at each boundary.
This is also the most operationally mature context for ID-JAG because the infrastructure already exists: SSO trust is established, policy governance is in place, and authorization servers are already configured to accept the IdP. ID-JAG is an extension of what is already there.
## CIAM and Product Platforms
A CIAM platform or product suite has its own identity graph. Users join through self-serve signup, enterprise federation, social login, or direct invitation. The platform is the authoritative issuer for those identities, regardless of which upstream directory any individual user came from. It manages the tenant relationships, the permission model, and the lifecycle of access across the product's surfaces.
That identity governance authority is the same kind of authority an Enterprise IdP holds for its own subjects. When the platform acts as an IdP and downstream services within the ecosystem already trust it directly for SSO and subject resolution, the same immediate-IdP logic applies. A common shape is federation chaining: the user may authenticate upstream with an enterprise workforce IdP, but the CIAM platform is still the IdP that downstream product services trust directly for SSO and subject resolution. In that case, the CIAM platform, not the upstream workforce IdP, is the issuer that matters for ID-JAG at that boundary.
That trust relationship also means the CIAM platform has to own subject linking carefully. If the upstream workforce IdP uses pairwise subject identifiers or the product has to normalize identities across multiple upstream directories, the CIAM platform must map those upstream identities into the product-local subject identifier the downstream authorization server expects. That mapping burden is part of what it means for the product IdP to be the immediate IdP at that boundary.
This covers the full scope of identities a B2B SaaS product actually serves: workforce users who arrive via enterprise federation, users who joined before enterprise federation existed, external collaborators, partners, API developers, and customers who are themselves end-users rather than employees. The product governs all of these. The Enterprise IdP governs the workforce subset. Both can participate as issuers for the identities they are authoritative over where the downstream RAS already trusts them for SSO and subject resolution.
None of that removes the RAS's sovereignty. The product IdP being the immediate IdP does not obligate the downstream authorization server to accept every assertion it issues. The RAS still decides whether to honor, narrow, or reject the grant under its own policy.
```mermaid
sequenceDiagram
actor U as User
participant EIDP as enterprise-idp.example.com
participant PIDP as product-idp.saas.example
participant C as Client
participant RAS as saas-component.example.net
C->>PIDP: Start authentication
PIDP->>EIDP: Federated auth request as client
U->>EIDP: Authenticate with workforce identity
EIDP->>PIDP: Federated login assertion
PIDP->>C: Authentication result in product domain
C->>PIDP: RFC 8693 token exchange (subject_token = PIDP ID token)
PIDP->>C: ID-JAG
C->>RAS: RFC 7523 JWT bearer grant
RAS->>C: Access token
Note over PIDP,RAS: PIDP is the immediate IdP/AS trusted by the downstream RAS
Note over EIDP,PIDP: EIDP is the upstream workforce authentication source
```
Two concrete product shapes make that immediate-IdP distinction especially clear.
**Agentic workflows.** An enterprise employee may launch the workflow, but the SaaS platform still has to govern product-local agent permissions, approval rules, runtime duration, tool access, and audit across the user-to-agent-to-tool chain. The workforce IdP can authenticate the employee. Where the downstream product services already trust the product or CIAM IdP directly for SSO and subject resolution, that product IdP is often the right system to issue the identity assertion they consume, because it owns the tenant model and product-local account context those services use as inputs to their own authorization decisions.
**Admin and support elevation.** A workforce IdP can prove who the support engineer is, but it usually does not own the ticket context, tenant scoping, break-glass policy, approval window, or product-local controls that determine what elevated support access should mean. Where the downstream product services already rely on the product IdP for SSO and subject resolution, that product IdP can issue a product-domain identity assertion that carries the right local subject and tenant context into the downstream token request, leaving the downstream services to enforce support access under their own bounded workflow rules rather than a generic employee entitlement.
## Platform and Internal Service Identity
A platform may operate its own IdP as the trust anchor for a set of internal gateways, services, or API surfaces. Those services are configured to trust the platform IdP for authentication, and where the Resource Authorization Servers also accept that issuer for ID-JAG, the same trust pattern can remove per-service interactive flows. Here too, the platform IdP may itself federate upstream to a workforce IdP while remaining the immediate IdP the downstream systems already trust for SSO and subject resolution.
This is the architectural shape for microservice architectures, API gateway patterns, and internal developer platforms where the platform IdP governs service-to-service identity and access. The key point is that the downstream systems trust the platform IdP directly. The same pattern also appears in gateway bridge designs, where a platform gateway sits between upstream enterprise identity and downstream product or partner APIs and has to translate that identity into product-local tenant context and downstream audience-specific assertions.
## Personal and Developer Identity
Developer toolchains, package registries, cloud provider CLIs, and similar surfaces follow the same trust logic. A developer authenticates to a central platform, such as a cloud identity provider, source-code hosting platform, or internal developer portal, and that platform becomes the immediate IdP that downstream tooling and registries already trust for SSO and subject resolution. The same ID-JAG trust pattern applies without requiring enterprise infrastructure: what matters is the established SSO trust relationship between that developer identity platform and the downstream authorization servers, not whether the platform is backed by an enterprise workforce IdP.
A concrete shape: a developer authenticates to a source-code hosting platform. A CI pipeline needs to push an artifact to a registry that already accepts that platform as its SSO IdP. The pipeline exchanges the platform-issued identity token into an ID-JAG via RFC 8693, then presents it as the authorization grant to the registry's token endpoint via RFC 7523. The registry issues a scoped access token for the push without requiring an interactive redirect or a separate developer credential. The hosting platform is the immediate IdP because the registry already trusts it for SSO and developer subject resolution, the same relationship that backs the developer's interactive login.
This is the context that most directly demonstrates the pattern is not bounded by enterprise deployment. The developer case satisfies the same trust constraint (a trusted issuer, a configured client, established policy) while the identity source and governance model are entirely different.
# Agentic Systems
Agents make the issuer model harder to compress because three different identity questions appear at once: who the employee is, what product-local account and tenant context the downstream services recognize, and what software is actually executing. The workforce IdP may authenticate the employee. The product or platform IdP may still be the identity system that downstream services trust directly for subject and tenant context. A workload identity layer may separately prove the executing software.
That separation matters because downstream services rarely authorize against "workforce identity" in the abstract. They authorize against the local account, tenant, and product relationship they understand, while other parts of the stack may separately care about the executing workload. The useful IAM move is to keep those roles distinct instead of pretending one issuer is authoritative for all of them.
Agents also make the cross-domain boundary much more obvious. A workflow that starts inside one SaaS product often does not stay there: it may need to read from a ticketing system, update a CRM record, open a case in an ITSM tool, or call other enterprise APIs that live under different authorization servers. This is where the `Cross-App Access` label fits especially well. Once an agent has to move across application boundaries, it needs a shared trust anchor those downstream systems already recognize. In many enterprise deployments that shared trust anchor is the Enterprise IdP, even if a product or platform IdP still governs local tenant context inside its own trust domain.
A related question in agentic and platform architectures is whether ID-JAG chains across multiple hops. The recommendation is that deployments treat each trust boundary as requiring its own ID-JAG from the immediate IdP trusted at that boundary, rather than propagating a single assertion through the whole stack. In a three-tier architecture where a workforce IdP federates into a CIAM platform that in turn is trusted by downstream microservices, the CIAM platform issues an ID-JAG for the downstream hop. That ID-JAG is scoped to the trust relationship between the CIAM platform and those services; it does not carry the upstream workforce assertion forward as the operative grant. Each boundary has its own issuer, its own trust relationship, and its own grant.
That is the useful distinction for this post. The current ID-JAG draft is narrower than the full agent stack: it is an end-user identity assertion from the IdP the RAS already trusts for SSO and subject resolution. But that still means the issuer that matters at a given boundary may not be the top-level workforce IdP. In many product deployments, the CIAM or internal platform IdP sits downstream of workforce federation and is still the right ID-JAG issuer for the services inside its own trust domain, because those services already trust it directly at the relying-party boundary, even though it is not the system that originally authenticated the employee and not the system that proves the executing software.
# Why Not Just Token Exchange and JWT Grants?
RFC 7523 defines a generic JWT bearer authorization grant profile. It does not assume the issuer is an identity provider, but it also does not eliminate trust requirements. The RAS still has to decide which issuers it accepts, how it validates claims, and what additional policy applies.
If you combine [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange with [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523), you can build something that looks superficially similar to ID-JAG. The problem is that the generic stack leaves too much of the trust model, payload shape, and security behavior to local convention.
The reason that matters to this post is simple: ID-JAG is not just another way to package the same flow. It is a profile that narrows several places where a hand-rolled composition is easy to get wrong.
RFC 7523 is not missing trust altogether, and careful deployments do configure issuers they trust. What it does not provide is a standard IdP role, a defined payload profile, or specified security behavior for this cross-domain trust pattern.
- It narrows the trust model: the issuer is the IdP the RAS already trusts for SSO and subject resolution, not an arbitrary JWT signer. That established SSO relationship narrows subject mapping work, though it does not eliminate it: in pairwise-pseudonymous or multi-registration deployments the IdP still has to ensure the `sub` in the ID-JAG is the subject identifier the RAS expects for that user.
- It narrows the payload semantics: audience, `client_id`, tenant context, and subject alignment are all more explicit than in a generic composition.
- It narrows the security behavior: sender-constraining continuity, resource and scope handling, and refresh behavior are profiled instead of left to local convention.
So the value of ID-JAG is not that generic JWT grants and token exchange are incapable of expressing a similar flow. The value is that ID-JAG turns a hand-rolled composition into a recognizable profile with tighter trust, clearer claim semantics, and fewer security ambiguities.
# The Claim Worth Making
The constraint in ID-JAG is not enterprise branding. It is the existence of an issuer, client, and policy relationship under which the RAS is willing to accept an end-user identity assertion as a grant. In the current draft, that issuer is the IdP the RAS already trusts for SSO and subject resolution. That can be an enterprise workforce IdP, but it can also be a CIAM platform or an internal service IdP when those systems are the immediate IdPs for the downstream services in question.
That constraint is a feature, not a limitation. It gives the protocol a governance anchor. The RAS is not evaluating arbitrary claims from unknown issuers. It is evaluating assertions from an issuer it has already decided to trust for this category of grant, while still applying its own local policy.
Enterprise IdPs are the right issuers for workforce identity and enterprise-managed policy. Product or platform IdPs may be the right issuers for the local identities and relationships they govern inside their own ecosystems, including cases where they federate upstream to a workforce IdP but remain the direct SSO authority for downstream services.
Framing ID-JAG as only a top-level workforce IdP extension narrows the pattern unnecessarily. Framing it as a generic multi-issuer substrate goes too far the other direction. The stronger claim is that the pattern can extend beyond classic enterprise deployments wherever the downstream authorization server already trusts that issuer as its SSO IdP and is configured for the grant.
Put differently: the immediate IdP trusted by the RAS may not be the top-level workforce IdP. In many product and platform deployments, that immediate IdP is the CIAM or platform identity layer that federates upstream workforce identity but remains authoritative for the downstream trust boundary.
If CIAM, platform identity, or developer toolchain deployments are part of your use case for this pattern, now is the time to raise them in the working group. The draft is still moving and the text is still being shaped. The more concrete deployment examples the working group has on the table, the better the specification can reflect the full range of contexts where this trust pattern actually applies.
If you think that change improves the draft, weigh in on [ID-JAG PR #82](https://github.com/oauth-wg/oauth-identity-assertion-authz-grant/pull/82). Concrete support or counterarguments there are more useful than vague agreement.
---
# Open-World OAuth Still Needs Mission Shaping
Canonical URL: https://notes.karlmcguinness.com/notes/open-world-oauth-still-needs-mission-shaping/
Markdown URL: https://notes.karlmcguinness.com/notes/open-world-oauth-still-needs-mission-shaping.md
[Part 1](/notes/oauth-for-open-world-ecosystems/) focused on OAuth's substrate problem: agents discover protected resources at runtime, which inverts the closed-world assumption that clients, authorization servers, and resource servers were all pre-configured to know each other.
[Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728), [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/), [sender-constrained tokens](https://datatracker.ietf.org/doc/html/rfc9449), [metadata integrity](/notes/oauth-for-open-world-ecosystems/#-metadata-integrity-under-adversarial-conditions), and [first-contact trust](/notes/oauth-for-open-world-ecosystems/#-first-contact-trust-is-bilateral) all matter because the protocol base has to work before anything higher-level can be trusted.
A working substrate is not the same thing as a complete authorization model for agents. Even after discovery, resource binding, and first-contact trust all work, a generic agent still faces a harder problem: how does it turn approved task intent into correctly bounded authorization requests across unfamiliar tools, resources, and domains?
That is where open-world OAuth runs into the [Mission shaping](/series/mission-shaping/) problem.
In this model, a [Mission](/notes/the-mission-shaping-problem/) is the durable authority object compiled from approved intent, bounded across delegation, and governed through the lifecycle of the task.
# ๐ค The Handoff From Protocol To Mission
Imagine the example from Part 1: an agent is helping a salesperson prepare an account-renewal brief.
The agent discovers a CRM [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tool at runtime. [Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728) tells it how the CRM API is protected. The Authorization Server (AS) returns a token whose effective `resource` binding is confirmed ([OAuth 2.0 Resource Parameter in Access Token Response](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-resource-token-resp/), as discussed in [Part 1](/notes/oauth-for-open-world-ecosystems/)). The client has enough metadata to evaluate whether the CRM AS should be trusted. The CRM resource server has enough policy basis to accept the issuer.
That is a much better world than today's open-world OAuth baseline.
But the agent then discovers that it needs billing history from another domain. The protocol questions return:
- can the agent discover the billing resource's authorization requirements
- can it get a token with confirmed resource binding
- can each side evaluate first-contact trust
- can delegation cross the boundary safely
And even if every protocol step succeeds, the Mission question remains:
- is billing-history access still inside the user's approved renewal-preparation task
- is the requested authority a narrow derivation or a material expansion
- should the user see a new approval prompt
- should the original Mission be expanded, forked, or rejected
- what happens to already-derived authority if the Mission is later narrowed or revoked
OAuth can expose better signals. It cannot, by itself, decide whether the agent is still operating inside the purpose the user approved.
# โ๏ธ Delegation and Authorization Chains Across Trust Domains
Multi-hop delegation is still part of the open-world trust problem. Once discovery happens at runtime, delegation chains can reach AS boundaries that were not pre-configured either. The first-contact problem reappears, now applied to delegated authority rather than to client and resource discovery alone.
This is where Mission starts to matter. The question is not only whether one token can be exchanged for another. It is whether the delegated act still remains inside the purpose the user approved.
[OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693) ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)) provides a framework commonly used for delegation within a trust domain where the involved ASes have a pre-existing relationship. The `act` claim records who is acting on whose behalf, but even within a single domain the claim is not uniformly profiled: two standards-compliant implementations can disagree about delegation semantics when a token crosses a trust boundary (see [Standardize `act` Across Assertion Grants and JWT Access Tokens](/notes/standardize-act-across-assertion-grants-and-jwt-access-tokens/)). Multi-hop chains that cross AS boundaries with no prior relationship between them are not well specified.
The specific problems are operational, not abstract:
- how does a downstream AS validate that the delegation chain it is seeing is authentic
- how does it determine that the upstream AS that issued the parent token is trustworthy enough to vouch for delegation
- how does it enforce that each hop properly attenuated rather than expanded the delegated authority
- how does a generic client discover whether a target AS supports token exchange, chaining, or some narrower delegation profile at all
Three active drafts each address a different layer of this problem.
[OAuth Identity and Authorization Chaining Across Domains](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/) addresses the cross-AS trust layer: how an AS establishes trust with another AS based on discovered metadata and how delegation claims propagate across AS boundaries.
[Transaction Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-transaction-tokens/) address the intra-domain context layer: within a trust domain, how caller context propagates through chains of service or agent calls so that each downstream service sees who initiated the chain and under what authority.
[Identity Assertion JWT Authorization Grant](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) (ID-JAG) addresses a narrower downstream grant-friction problem: a user authenticates once with an Identity Provider (IdP), the IdP issues an identity assertion, and the client presents that assertion to a target AS as an [RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) JWT Bearer authorization grant to obtain access without a full interactive authorization step in every coordinated downstream domain.
Taken together, these drafts show that the delegation problem is visible and active. They do not yet provide a settled end-to-end answer for open-world, cross-AS delegation chains.
A settled answer would require at least:
- a standard way for ASes to publish support for specific token exchange and chaining profiles
- a trust framework that lets downstream ASes evaluate whether an upstream AS is authorized to vouch for delegation in a given context
- chain-level attenuation guarantees that are verifiable at each hop rather than enforceable only by convention within a single trust domain
- stronger subject and actor continuity so downstream parties can tell not just who is acting now, but how that actor relates to the original approved principal
For Mission-aware systems, actor continuity is not just provenance metadata. It is part of Mission state: the system needs to know whether the actor executing the next derived action is still the actor, or delegated actor, the Mission authorized.
And even if discovery, trust establishment, and delegation worked perfectly, a generic agent would still face a harder question: how does it know what authorization request it should form in the first place?
# ๐งฉ Task Intent and Tool Authorization Semantics Are Still Missing
A larger open gap runs across all of these layers: OAuth still does not give generic agent systems a reliable way to move from task intent to concrete authorization semantics.
That is the problem the [Mission Shaping](https://notes.karlmcguinness.com/series/mission-shaping/) series is about. Enforcement systems do not authorize intent. They authorize operations on resources under specific conditions. A user intent such as "prepare the renewal notes for these accounts" is not an authorization request. Something has to translate the first into the second, and open-world OAuth exposes exactly where that translation is missing.
OAuth scopes are intentionally opaque strings. They carry no machine-readable semantics about what operations they represent, what resources they govern, or how they compose. That opacity worked in closed-world deployments where developers read the documentation and hard-coded scope values. It does not work for a generic agent encountering an unfamiliar domain at runtime. Three specific gaps make the problem concrete.
First, the client cannot understand the authorization semantics of an unfamiliar domain from scope strings alone. It sees opaque tokens, not a machine-readable mapping from tool intent to domain authority. This is where resource-declared semantics, richer `authorization_details` profiles (structured authorization objects defined by [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396) for expressing richer authorization requests beyond flat scope strings), or equivalent machine-readable domain vocabularies become important.
Second, the consent path does not scale. Today's consent path relies on `WWW-Authenticate` challenges and scope-based authorization requests. That allows incremental improvement, but it often puts a human back in the loop every time the agent encounters a new resource or tool surface. In the CRM-to-billing scenario, that can turn one coherent task into multiple consent interruptions or partial failures. This is partly a UX problem, but it is also a protocol-shaping problem because the ecosystem still lacks standard ways to bundle, reuse, or reason over related authorization requests as one task unfolds.
Third, delegation capabilities are not discoverable enough for generic clients. In principle, a parent agent should be able to request a narrowed token for a sub-agent. In practice, there is usually no standard way for the AS to publish that such an operation is supported, what token exchange profile it expects, whether chaining or attenuation constraints apply, or how a client should map its current intent into the `scope` values or `authorization_details` needed for the derived request. Even when token exchange exists, the agent often does not know what it should ask for.
# ๐ RAR, R3, and Where Semantics Should Live
[Rich Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9396) (RAR, [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)) and [RAR metadata](https://datatracker.ietf.org/doc/draft-zehavi-oauth-rar-metadata/) are relevant because they expose schemas for `authorization_details` types and give ASes a way to advertise some of the structured authorization language they understand.
That is useful progress. It turns at least part of the AS contract into discoverable metadata rather than documentation. But it still does not solve the full task-to-authorization problem for open-world agents. The client may know the shape of a valid `authorization_details` object and still have no reliable way to derive the right object from a newly discovered tool call or from a higher-level task description.
[AAuth R3](https://dickhardt.github.io/AAuth/draft-hardt-aauth-r3.html) (editor's copy, accessed April 7, 2026) is interesting here because it pushes on the other side of the semantics problem. Instead of asking the client to invent the right authorization object, R3 lets resources publish structured, content-addressed authorization definitions in vocabularies agents already understand, including MCP, OpenAPI, and gRPC.
These two approaches reflect different assumptions about where authorization semantics should live.
RFC 9396 RAR is client-declared: the client constructs `authorization_details` and asserts them in the request.
R3 is resource-declared: the resource publishes structured authorization definitions in vocabularies agents already understand.
That distinction matters for generic agents. An agent encountering an unfamiliar resource has no reliable basis for constructing a correct RAR object from scratch. R3's resource-declared model is a direct attempt to shift that burden. How RAR and resource-declared approaches compose will shape what the authorization semantics layer actually looks like for open-world agent systems.
> Resource semantics are still not Mission semantics.
Discovery tells the client where to go. Binding tells it what token it got. Trust helps it decide whom to trust. R3-style resource definitions help the agent understand what a resource exposes. None of those, by themselves, tell a generic agent whether the newly discovered action is inside the purpose the user approved.
# ๐ Consent Continuity and Mission Lifecycle
The task-intent problem has a visibility problem layered on top of it.
Even if an agent could perfectly map intent to authorization requests, the user would still have no standard way to see what tokens were acquired during execution, what resources were accessed under their authorization, or whether the agent's runtime behavior remained within the scope of what was originally approved.
The gap is not just semantic. It is auditability and control.
The gap spans both client and AS. The AS has no standard mechanism for evaluating whether a runtime token request for a newly discovered resource remains within the intent of the user's original grant. The client has no standard mechanism for notifying the user when those acquisitions expand the effective authorization surface.
That leaves an unresolved model question: in an open world, what keeps user approval meaningful?
In the CRM-to-billing scenario, broad ex ante approval would let the agent keep expanding across systems without showing the user what changed. Step-up on every newly discovered resource would preserve visibility but destroy autonomy. Some form of policy-mediated expansion is the most plausible direction, but the protocol building blocks for it are still incomplete. (Since this was written, the Mission-Bound Authorization drafts have taken a first pass at exactly that shape: an experimental [discovery companion](https://mcguinness.github.io/mission-bound-authorization/#go.draft-mcguinness-mission-discovery.html) adjudicates each newly discovered resource at the Mission Issuer against a ceiling the user consented to in advance, binding what fits, routing the rest to a human, and refusing by default.)
Revocation and termination sit inside the same gap. If a mission is narrowed, paused, revoked, or expires mid-execution, the ecosystem lacks a standard way to ensure that already-derived authority, cached tokens, and downstream delegated actions collapse with it coherently. Standard token revocation ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)) requires the client to know which AS to contact. When ASes were discovered dynamically and client state may be ephemeral, that assumption does not hold reliably.
In the renewal example, if the account owner cancels the renewal brief after the CRM token has been used to derive billing access, the system needs a way to terminate the billing-side authority as part of the same Mission lifecycle rather than waiting for an unrelated token expiry.
That is where protocol design ends and governance begins.
OAuth can expose better signals, boundaries, and artifacts. It cannot by itself answer whether an agent is still operating within the bounds of what was originally approved, nor can it decide where that policy should live operationally: at the AS, the RS, the orchestrator, or an external policy engine.
That is the question the [Mission Shaping](https://notes.karlmcguinness.com/series/mission-shaping/) and [Mission-Bound OAuth](https://notes.karlmcguinness.com/series/mission-bound-oauth/) series address: not how a client discovers and acquires the right token, but how approved intent becomes a durable authority that stays in force across every derivation step.
# ๐๏ธ Open-World OAuth Needs a Mission Layer
Improving the open-world OAuth substrate is necessary and worth the investment. [Part 1](/notes/oauth-for-open-world-ecosystems/) identified targeted, fixable gaps ([resource identifier matching](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-rfc9728bis/), [resource binding confirmation](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-resource-token-resp/)) and deeper trust-framework work around [metadata integrity](/notes/oauth-for-open-world-ecosystems/#-metadata-integrity-under-adversarial-conditions) and [first-contact trust](/notes/oauth-for-open-world-ecosystems/#-first-contact-trust-is-bilateral). Those improvements matter.
But a better substrate does not eliminate the need for a Mission layer.
Mission shaping is not just better token exchange or richer request syntax. The unresolved problem is not only how authority is conveyed. It is how runtime action stays aligned with approved purpose as the agent discovers new tools, crosses domains, delegates work, asks for new authority, and eventually terminates the task.
[AAuth](https://dickhardt.github.io/AAuth/draft-hardt-aauth-protocol.html) (editor's copy, accessed April 7, 2026) is one clear signal of this convergence. It redesigns authorization choreography around open-world assumptions and introduces explicit Mission concepts into the protocol conversation.
Other active drafts are pointing in the same direction. [Policy and Lifecycle Extensions for OAuth Rich Authorization Requests](https://datatracker.ietf.org/doc/draft-chen-oauth-rar-agent-extensions/) explores policy context and task-bound authorization lifetime. [OAuth 2.0 Scope Aggregation for Multi-Step AI Agent Workflows](https://datatracker.ietf.org/doc/draft-jia-oauth-scope-aggregation/) addresses repeated-consent pressure in multi-step workflows. The [Agent Authorization Profile for OAuth 2.0](https://datatracker.ietf.org/doc/draft-aap-oauth-profile/) takes on task context, operational constraints, delegation chains, and human oversight as explicit protocol concerns.
That convergence is encouraging. The distinction that remains is between protocol concepts that make agent authorization more explicit and Mission shaping as durable bounded authority: compiled from approved intent, bounded across delegation, governed through the lifecycle of the task, and terminated coherently when the business reason ends. It sits above protocol artifacts and answers not just whether a token is valid, but whether the execution is still authorized.
Open-world OAuth is the substrate problem.
Mission shaping is the authority-governance problem above it.
Agent authorization needs both layers to hold.
---
# OAuth for Open-World Ecosystems
Canonical URL: https://notes.karlmcguinness.com/notes/oauth-for-open-world-ecosystems/
Markdown URL: https://notes.karlmcguinness.com/notes/oauth-for-open-world-ecosystems.md
OAuth was not designed for the world agents operate in.
That is not a criticism. It is why OAuth succeeded. OAuth became mature because its deployment model was constrained: clients, authorization servers, and resource servers were configured to know each other before runtime. That made the threat model legible and the security surface analyzable.
Agents invert that assumption. They discover tools and protected resources at runtime, encountering authorization servers and resource servers they were never configured to know. That is a different deployment model, and it requires different protocol primitives.
[Protected Resource Metadata (PRM)](https://datatracker.ietf.org/doc/html/rfc9728), [OAuth Client ID Metadata Document (CIMD)](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/), and related drafts are starting to close that gap. But the transition is incomplete.
This post focuses on that specific problem: what it takes for OAuth to work in an open-world deployment model.
Other agent authorization challenges, including agent identity, delegation governance, attestation, and attenuation, appear here only where they affect open-world trust bootstrapping rather than as the primary subject.
# ๐งฑ The Closed-World Shape of OAuth
OAuth's original deployment model ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749)) was mostly closed-world. Clients had fixed relationships with Authorization Servers (ASes) and Resource Servers (RSes). Developers built the client, read the API docs, registered the software statically, and learned ahead of time which scopes were required for which endpoints. Registration happened before runtime. Trust was established at deploy time, not discovered dynamically during execution.
When the actor set is finite and known, the threat model is legible. The principals are enumerated. The trust relationships are explicit. The attack surface can be studied and hardened through deployment experience. That boundedness is a major reason the [OAuth Security Best Current Practices](https://datatracker.ietf.org/doc/html/rfc9700) could become so concrete over time: the assumptions were constrained enough to analyze rigorously and stable enough to harden through deployment.
The attack surface of closed-world OAuth is still large. But it is *bounded*. An AS knows in advance which clients can approach it, which RSes it issues tokens for, and the expected shape of every authorization relationship. That makes it possible to constrain token audiences, detect anomalies, and eliminate whole categories of trust bootstrapping problems by construction.
[Authorization server metadata](https://datatracker.ietf.org/doc/html/rfc8414) ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)), first established in OpenID Connect Discovery and later generalized for OAuth, made clients more adaptive by letting them discover protocol endpoints and capabilities rather than hand-configuring every URL. [Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707) ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)) let a single AS issue tokens for multiple RSes with different audiences and scopes. These were real improvements to a model whose core assumptions stayed mostly intact: the AS still knew its RSes, the client still knew its AS, and the developer still configured the relationships before runtime.
# ๐ Why Earlier Attempts Did Not Close the Gap
[Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)) was the most direct attempt to open OAuth up.
In theory, DCR let a client discover an AS, register itself, and begin interacting without a prior bilateral setup process. In practice, it was never enough for genuinely open-world deployments.
The problem was trust bootstrapping. If a client can self-assert its identity and metadata to any AS it encounters, registration becomes easy to abuse. A malicious client presents itself as a well-known brand. There is nothing in the protocol preventing it. The AS has no reliable way to validate the client's claimed identity against anything authoritative.
Software Statements were designed to fix this: a signed assertion from a trusted third party vouching for the client's identity. But that required a trust-anchor ecosystem that never materialized at scale. Who issues them? Under what policies? Why should an arbitrary AS trust the issuer? Those questions were never answered end-to-end for open internet use.
DCR works well inside controlled ecosystems: enterprise deployments, managed federations, and developer platforms where a platform operator already has a bilateral relationship with the AS. But those are still bounded environments that happen to provision clients dynamically. The gap between that model and true first-contact trust was never closed.
OAuth 2.1 takes a different step. It consolidates security guidance accumulated over fifteen years of deployment, removes outdated patterns like implicit flow and password grant, and gives deployers a cleaner baseline.
But [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) is a modernization of the closed-world model. It makes the existing model more secure and more deployable. It does not redefine the model around runtime discovery and open-world trust.
OAuth 2.1 does not give a client a runtime path for discovering how a protected resource is protected. It does not provide a mechanism for an AS to evaluate a client's identity in a web-native way before the client has ever been registered. It does not address how a client confirms which resource binding a token actually carries after issuance.
If your deployment is fundamentally closed-world (fixed clients, pre-configured ASes, known RSes), OAuth 2.1 is the modernization you need. If your deployment is fundamentally open-world, OAuth 2.1 is a better floor, not the model you need to reach.
# ๐ค Agents Invert the Deployment Assumption
Agents do not operate in closed worlds.
They discover tools, services, and protected resources at runtime. A simple example is a tool protocol like [Model Context Protocol (MCP)](https://modelcontextprotocol.io/): an agent loads a tool manifest from a URL it just discovered, calls an endpoint, receives a `401 Unauthorized`, and has no prior knowledge of which AS protects that resource, which resource identifier to request, or which scopes are required. The agent was not configured at deploy time with a list of every RS it will need.
To be precise: "open world" here does not mean "enterprise dynamic onboarding" or "managed federation with known trust anchors." It means first-contact interaction with resources and authorization servers that were not pre-configured into the client at deployment time.
The closed-world question is: how does a known client get a token from a known AS for a known RS?
The open-world question is: how does a client safely discover a protected resource, learn how that resource is protected, determine which AS to use, determine the correct resource identifier and scopes, confirm that the discovered AS is actually the right one, and do all of this without prior configuration?
Different deployment model. Different protocol primitives. Different threat surface.
# ๐งญ The New Standards That Start to Open OAuth Up
One recently published RFC and a closely related active draft together address the core discovery problem.
**[Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)** gives the client a runtime path from "I found a protected resource" to "I know how to get the right token for it." The resource advertises the AS it uses, the resource identifier, and other authorization requirements. A client that encounters a `401` with a `WWW-Authenticate` challenge can use that challenge to locate the resource's metadata document, learn which AS issues tokens for it, and proceed from there.
**[OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/)** addresses a narrower but important complementary problem: how an AS can evaluate client metadata for a client instance it has never seen before without relying only on self-asserted registration input.
Rather than accepting client metadata only in-band in a registration request (where DCR's trust problem originates), the client publishes a metadata document at a well-known URL under its own domain. The AS independently fetches and verifies it over TLS, anchoring client identity in DNS and HTTPS without importing the full browser security model. The client is no longer vouching for itself only in-band; the AS can verify metadata against a stable, independently fetchable identity anchor.
This model is parallel to authorization server metadata and OpenID Connect Discovery: a party publishes metadata at a well-known HTTPS URL, and relying parties trust it because TLS anchors it to a specific DNS origin. CIMD extends that pattern to the client side.
Adjacent workload-identity work matters here too. The ongoing [OAuth SPIFFE Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-spiffe-client-auth/) draft, alongside the [AI Agent Authentication and Authorization](https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/) draft that treats agents explicitly as workloads, profiles how SPIFFE identities and workload attestation can be used for OAuth client authentication.
That is not the same trust model as CIMD, and it does not by itself create a unified first-contact bootstrap story for open-world sub-agents. It is a complementary piece for deployments where the caller is better modeled as an attested workload inside a managed trust domain than as a traditional web-hosted software client. Work in this area also connects back to CIMD by defining metadata parameters such as `spiffe_id` and `spiffe_bundle_endpoint`, which help bridge origin-based client discovery and SPIFFE-backed client authentication without collapsing the two models into one.
A third layer sits between software-client identity and workload identity: agent-instance identity. CIMD anchors who the software client is; SPIFFE anchors what process is running. Neither addresses the identity of a specific agent invocation, which is an ephemeral, task-scoped principal that may call multiple resources within a single execution context or spawn sub-agents that need a verifiable identity derived from the parent authorization. There is no current standard for how an agent instance asserts a durable, scoped identity across the lifetime of a task, or for how a sub-agent's identity is provably bound to its parent's authorization context in a way that downstream resources can verify.
CIMD has a clear boundary. It prevents impersonation of established services: an attacker who does not control `example.com` cannot publish a fraudulent CIMD document for a client claiming to be hosted there. It does not prevent look-alike domains. That is the inherent limit of any DNS-anchored identity system.
That still leaves a policy question for the AS: proving control of a domain is not the same thing as deciding that a client hosted at that domain should be trusted for a particular kind of interaction. CIMD improves identity anchoring. It does not replace local trust policy.
It also leaves a deployment-shape question. CIMD is strongest for clients that can anchor themselves in DNS and HTTPS. Agents increasingly involve a mix of public clients, confidential clients, sub-agents, and short-lived workloads that may be better modeled as attested workloads than as traditional domain-hosted software. How those client classes authenticate, identify themselves, and compose in truly open-world deployments is still incomplete work rather than a settled standards path.
> PRM answers most of: where should the client go, and what should it ask for?
>
> CIMD answers part of: who is this client, and what metadata is it asserting from a stable origin?
Together, they move OAuth toward a model built on runtime discovery, HTTPS-anchored identity, and DNS-based origin verification rather than on the assumption that every trust relationship was arranged manually in advance. They still leave the policy question of whether a given AS should trust a newly encountered client for a given interaction.
# ๐ OAuth Is Reaching For Web-Scale Trust Primitives
OAuth was always web-native. It runs on HTTP, uses browser redirects, and was built around URIs from the start. What is changing is that OAuth is now reaching for the layer of the web it never needed before: the trust model browsers developed for open-world, first-contact interactions with parties that have no prior relationship.
The web developed patterns for this problem, including anchoring identity in DNS origins backed by TLS, treating unknown cross-origin parties as untrusted by default, and verifying content integrity independently of the delivery channel. Those patterns exist because the web had to work at internet scale without assuming prior bilateral configuration. OAuth did not need them because its deployment model was closed-world.
PRM uses a related origin-anchoring pattern: resource identity is grounded in the same TLS origin as the resource itself. CIMD extends a similar idea to the client side, anchoring who a client is in DNS rather than in self-asserted registration input. Signed metadata would add independent content verification, validating a metadata document without relying only on the delivery channel that served it.
That analogy is illustrative rather than complete. It holds for the discovery and metadata layer: origin anchoring, independently verifiable artifacts, and runtime trust evaluation rather than pre-configured bilateral relationships. It stops being useful once the problem shifts to delegation semantics, workload identity, authorization-server trust evaluation, or Mission shaping. Those are the gaps that begin once discovery succeeds, and they require protocol primitives the web never needed to develop.
# ๐งช A Concrete Example of the Open-World Flow
Imagine an agent helping a salesperson prepare an account-renewal brief.
The agent discovers a CRM MCP tool at runtime. It calls a `get-account` operation and receives a `401` challenge. The `WWW-Authenticate` header in the response points to the resource's metadata document. PRM returns the AS URL, the effective `resource` identifier, and the required authorization parameters. The agent uses that information to request a token. That is the discovery layer working as intended.
What the example also exposes is where the subsequent layers break. If the AS returns a token without echoing the effective `resource` binding, the agent cannot confirm whether the token is valid for the account API it just discovered. If the CRM AS is newly encountered, the client still has to decide whether it should trust that AS at all. If the workflow later points the agent toward a billing API in another trust domain, the delegation and issuer-acceptance problem repeats.
That full arc, from discovery through resource binding through first-contact trust, is the substrate open-world OAuth has to make reliable. The next section addresses two specific gaps that break that substrate in practice; the deeper semantic and governance questions begin where the substrate ends.
# โ ๏ธ Two Gaps That Break Discovery in Practice
PRM and CIMD point toward the right shape. But there are two specific gaps that still prevent the discovery flow from working reliably in real deployments. Without addressing them, open-world OAuth will produce predictable failures for agents at runtime.
## ๐ Resource Identifier Matching
The first gap is in PRM itself. The standard requires the `resource` value returned through a `WWW-Authenticate`-driven metadata lookup to exactly match the URL the client requested. In practice, this breaks discovery for APIs that protect multiple paths under one authorization policy. A resource server protecting `/api/v1/calendar`, `/api/v1/email`, and `/api/v1/contacts` cannot serve a single metadata document with a single resource identifier; it would need one per path. Many real APIs are structured this way.
[Update to OAuth 2.0 Protected Resource Metadata Resource Identifier Validation](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-rfc9728bis/) addresses this with a narrow, targeted change: the advertised resource identifier may be a path prefix of the requested URL rather than an exact match, while keeping the same TLS origin requirement that prevents cross-origin resource impersonation. The security boundary is preserved. The practical deployment constraint is removed.
## ๐ Resource Binding Confirmation
The second gap is more serious. Even after a successful discovery flow, an agent that receives an access token has no standard way to confirm what that token is actually valid for. [Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707) ([RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707)) let the client declare which resource it wants the token for, but there is no corresponding mechanism for the AS to confirm which resource binding it honored. An AS may silently ignore the requested `resource`, bind the token to a different identifier based on policy, or accept only a subset of what was requested. The client sends the token to the discovered resource and receives a `403`. The client cannot tell why, cannot detect this before sending, and has no interoperable way to recover.
This is not an edge case. In any multi-resource or dynamically discovered environment, silent resource binding divergence is a realistic failure mode, not a theoretical one.
[OAuth 2.0 Resource Parameter in Access Token Response](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-resource-token-resp/) closes the loop by having the AS echo back the effective `resource` in the token response. The client knows what resource binding was honored before it sends the token anywhere.
| Gap | Where | Problem | Fix |
|---|---|---|---|
| Resource identifier matching | PRM | `resource` must exactly match the requested URL; breaks multi-path APIs | [rfc9728bis](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-rfc9728bis/): prefix-match instead of exact-match |
| Resource binding confirmation | Token response | No standard way to verify which `resource` binding the AS honored | [resource-token-resp](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-resource-token-resp/): AS echoes effective `resource` in the token response |
> Discovery finds the resource. The token request declares it. The token response confirms it.
If you are implementing or deploying OAuth for agentic systems, these two drafts are where review and implementation feedback will have the most direct impact on whether the discovery model actually works. Those gaps are targeted and fixable. The gaps described in the next section run deeper.
# ๐ณ๏ธ What Is Still Not Solved
Solving the discovery layer is necessary but not sufficient. Even when PRM, CIMD, and the two gap-closing drafts are all in place, a generic agent still faces unsolved problems at the trust-framework and governance layers.
| Layer | Main question | Examples |
|---|---|---|
| Protocol | Can the client discover protection requirements and get the right token? | PRM, RFC 8707, `resource` confirmation, DPoP, PAR |
| Trust framework | Why should either side accept the other on first contact? | Issuer policy, federation, signed metadata, workload attestation |
| Governance / Mission layer | Is the newly discovered authority still inside the approved task? | Covered in [Part 2](/notes/open-world-oauth-still-needs-mission-shaping/) |
For open-world OAuth to be usable end-to-end, all three layers need to work. PRM and the two drafts above make real progress on the protocol layer. Adjacent agent and workload-identity work sharpens the identity side of the problem: open-world trust now has to compose not just with software clients in the abstract, but with attested workloads, ephemeral sub-agents, and multi-hop execution chains.
What remains in this post falls into three categories: a security-baseline adoption gap, metadata integrity, and first-contact trust. The semantic and governance gaps run deeper; they are the subject of [Part 2](/notes/open-world-oauth-still-needs-mission-shaping/).
## ๐ Sender-Constrained Tokens Need to Be the Default Posture
The technical answer to sender-constrained tokens already exists. [DPoP](https://datatracker.ietf.org/doc/html/rfc9449) (Demonstrating Proof-of-Possession at the Application Layer) sender-constrains access tokens to a specific key pair: a stolen token is useless without the corresponding private key. PRM defines `dpop_bound_access_tokens_required`, which lets a resource server signal that it requires DPoP-bound tokens. The mechanism is specified and implementable today.
The gap is not the mechanism. It is adoption and mandate. In a closed world, bearer tokens are risky but manageable: the token audience is known, the RS validates against its configured AS, and the blast radius of a stolen token is bounded by pre-existing trust relationships. In an open world, tokens acquired from dynamically discovered authorization servers carry amplified replay risk. No baseline open-world OAuth mandate requires an AS to support DPoP, and nothing in the current standards makes sender-constrained tokens the expected posture for open-world deployments rather than an opt-in capability.
The closest existing baseline is [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-security-profile-2_0.html), which mandates DPoP or mutual TLS for regulated financial API deployments and demonstrates that such a mandate is implementable at industry scale. FAPI applies to a specific regulated domain, not to open-world OAuth broadly. But it is the clearest existing evidence that a sender-constraint mandate can be specified, deployed, and hardened. What is missing is an equivalent baseline for open-world agentic contexts.
[Pushed Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9126) ([PAR](https://datatracker.ietf.org/doc/html/rfc9126)) address a related concern for interactive, user-present authorization flows. In open-world deployments where clients are not pre-registered and redirect URIs cannot be tightly validated in advance, authorization request injection becomes more realistic. PAR mitigates this by having the client push the full authorization request to the AS before the browser redirect. [PKCE](https://datatracker.ietf.org/doc/html/rfc7636) ([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)) is the complementary baseline defense for authorization code interception in public-client flows. For agent-to-agent and machine-to-machine flows, which dominate agentic systems, DPoP is the more directly applicable sender-constraint mechanism; PAR and PKCE apply where a user-present redirect flow is involved.
Mutual TLS sender-constrained tokens ([RFC 8705](https://datatracker.ietf.org/doc/html/rfc8705)) are another established option. But for open-world and web-native clients, DPoP is the more natural fit because it does not assume pre-existing certificate provisioning.
## ๐ฏ Metadata Integrity Under Adversarial Conditions
The open-world discovery model depends on resource servers serving accurate metadata. A malicious resource server can serve metadata pointing to an attacker-controlled AS. A client that completes an authorization flow against that AS can end up delivering an authorization code to the wrong security domain, potentially for an account at a legitimate service. This is structurally analogous to open redirect vulnerabilities: the discovery mechanism becomes a vector for directing clients toward attacker-controlled infrastructure.
The current protections, HTTPS delivery and the same-origin requirement for `resource` values in PRM, assume the resource server is honest. Signing metadata documents with JWS, using a key whose binding is verifiable independently of the TLS delivery channel, would let a client verify that a metadata document was signed by a key it has reason to trust. The cryptographic primitives already exist in JSON Object Signing and Encryption (JOSE). What does not exist is a standard for how resource servers publish signing keys, how clients discover and evaluate them, and what trust policy applies on first encounter.
[OpenID Federation 1.0](https://openid.net/specs/openid-federation-1_0.html) is the closest existing answer: its entity statements are JWS-signed metadata documents, and its trust chains let parties establish trust without prior bilateral configuration by tracing signed assertions to a common trust anchor. Active deployment in eIDAS 2.0 gives it more than theoretical standing. The constraint is that both parties must be enrolled in a shared federation, which moves the closed-world assumption up one level rather than eliminating it. For first contact between parties with no common trust anchor, the metadata integrity problem remains open.
This is distinct from the authorization server trust problem below. Here the question is whether the metadata document itself is authentic. The next question is whether the AS that document points to is one the client should trust at all.
## ๐ค First-Contact Trust Is Bilateral
The first-contact trust problem applies in both directions. A client that receives an AS URL from newly discovered resource metadata needs a way to evaluate whether that AS should be trusted at all. A resource server that receives a token from an unknown issuer faces the same problem in reverse. The current standards leave both sides underspecified.
On the client side, PRM tells a client which AS to use. It does not tell the client whether to trust that AS. In a closed world this question does not arise: the AS was configured at deploy time. In an open world, the AS URL comes from metadata discovered at runtime. If that metadata was served by a compromised or malicious resource server, the AS URL could point anywhere. The current protections rely on HTTPS and the Web Public Key Infrastructure (WebPKI): the AS must use valid TLS, and the resource metadata must be served from the same TLS origin as the resource. This does not address a resource server that is malicious rather than compromised, or one whose metadata can be influenced without full TLS compromise. [Authorization Server Issuer Identification (RFC 9207)](https://datatracker.ietf.org/doc/html/rfc9207) helps bind an authorization response to the issuing AS, but it does not answer whether that AS should have been trusted in the first place.
On the resource server side, JWT-structured access tokens ([RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068)) provide a partial answer. The token is self-contained, carries standard claims including `iss` and `aud`, and can be validated by fetching the issuer's JWK Set via authorization server metadata discovery ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)). A dynamically discovered RS can validate any token whose issuer publishes standard AS metadata, without needing to pre-register with that AS. But a valid JWT signature from an unknown issuer tells the RS only that it has a cryptographically well-formed token from *some* AS. It does not tell the RS whether that AS is one this resource should accept tokens from at all. Token introspection ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)) does not solve that first-contact problem: it requires the RS to already know where to send the token for validation, which presupposes the pre-existing AS relationship it does not have.
What is missing for both sides is a deeper trust channel independent of TLS delivery. Signing AS metadata with a JSON Web Signature ([JWS](https://datatracker.ietf.org/doc/html/rfc7515)) key whose binding can be verified out-of-band would give a client a way to evaluate a metadata document regardless of whether the delivery path was intact, and would give an RS a way to evaluate issuer trustworthiness beyond signature mechanics. What does not exist is a standard for how AS signing keys are published via [JSON Web Key (JWK) Sets](https://datatracker.ietf.org/doc/html/rfc7517) ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)), how either party discovers them on first contact, and what trust policy applies. At minimum, an RS needs some policy basis for acceptance: issuer allowlists, federation membership, signed metadata anchored in a trust framework, or some equivalent rule that answers not just "is this token valid?" but "is this issuer one I should accept tokens from at all?" That remains open work on both sides of the trust relationship.
The web's experience with PKI and ACME is instructive. Web PKI solved open-world first-contact trust at internet scale by reducing identity to a verifiable property (domain control), delegating trust through a hierarchy of signed assertions, and making certificate issuance auditable through Certificate Transparency. ACME then demonstrated that automating trust bootstrapping, rather than requiring manual bilateral coordination for every new relationship, is what drove TLS adoption from a minority to a near-universal baseline.
OAuth already relies on Web PKI for transport security. The open gap is the application layer above TLS: a standard for verifiable AS identity, an automated bootstrapping mechanism for AS trust, and a transparency mechanism for issuer metadata. The problem is not unsolvable. The architecture exists. The OAuth-specific instantiation does not yet.
# ๐ช Closed World Was a Feature. Open-World OAuth Is the Next Step.
OAuth became robust because it was designed around constrained assumptions and hardened over years of deployment and analysis.
Agents expose a different assumption set. They operate in open worlds, encounter resources dynamically, and need a trust model that works without prior bilateral registration for every relationship they may encounter.
The newer OAuth work, including PRM, CIMD, and the gap-closing drafts, represents the first serious structural movement toward native open-world OAuth. These are not incremental additions to the old model. They are changes to its foundational deployment assumptions.
The closed-world model gave OAuth the stability to become mature. The open-world extensions now in progress are the work required to make that maturity applicable to the next generation of clients operating in environments OAuth was never designed for.
That also means a large surface area has to evolve for OAuth to become truly capable in open-world agent ecosystems. There is real value in evolving the infrastructure we already have. OAuth, OpenID Connect, and the surrounding deployment base are too important to bypass. But this path will take time because it touches discovery, trust bootstrapping, sender constraints, and metadata integrity across several specifications rather than one.
The remaining gaps are easier to state than to solve. Some are protocol-standardization problems; others require coordination across trust frameworks and multiple specifications.
Adjacent work such as [AAuth](https://dickhardt.github.io/AAuth/draft-hardt-aauth-protocol.html) (editor's copy, accessed April 7, 2026) takes a different angle by redesigning authorization choreography around open-world assumptions from the start. That deserves attention, but the narrower point for this post is the OAuth substrate: discovery, resource binding, sender constraints, metadata integrity, and first-contact trust all have to work before higher-level agent authorization can be reliable.
Two of those gaps are targeted and fixable now: the resource identifier matching fix and the resource binding confirmation draft described earlier in this post. The rest require deeper ecosystem work. Without a substrate that can handle runtime discovery, resource binding, and first-contact trust, the rest of the authorization stack is built on the wrong assumptions.
That is where [Part 2](/notes/open-world-oauth-still-needs-mission-shaping/) picks up: once discovery and first-contact trust exist, how does approved intent become bounded authority across delegation, tool semantics, consent continuity, and the full lifecycle of a task?
---
# Standardize `act` Across Assertion Grants and JWT Access Tokens
Canonical URL: https://notes.karlmcguinness.com/notes/standardize-act-across-assertion-grants-and-jwt-access-tokens/
Markdown URL: https://notes.karlmcguinness.com/notes/standardize-act-across-assertion-grants-and-jwt-access-tokens.md
> **Update (2026-04-30):** This proposal has been published as an IETF individual draft, [OAuth Actor Profile (`draft-mcguinness-oauth-actor-profile`)](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/), which turns the recommendations below into a concrete specification.
OAuth has a delegation visibility problem.
In many deployments, a token tells you that *someone* is acting, but not clearly who is acting for whom. Delegation is implied by context, encoded in `sub` by convention, or inferred from `client_id` heuristics. That might be acceptable inside a single product boundary. It is not acceptable for standards-based interoperability.
> The fix is not a new claim. The primitives already exist. What is missing is a shared profile that applies them consistently.
The OAuth WG should standardize one actor model across both:
- JWT assertion grants ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) and profiles built on top of it, including [Identity Assertion JWT Authorization Grant (ID-JAG)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/))
- JWT access tokens ([RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068))
Standardizing `act` using [OAuth Entity Profiles (Entity Profiles)](https://datatracker.ietf.org/doc/html/draft-mora-oauth-entity-profiles-00) would make delegation and principal type explicit and machine-processable at both surfaces.
# ๐ Where the Current Specs Leave a Gap
## `act` Exists, But Is Not Profiled Where It Matters
[OAuth 2.0 Token Exchange (Token Exchange)](https://datatracker.ietf.org/doc/html/rfc8693) defines `act` for token exchange. That is a good start. But the ecosystem treats actor semantics as optional and context-specific. Two standards-compliant implementations can still disagree about delegation meaning when a token crosses a domain boundary.
The gap runs deeper at the assertion boundary. JWT assertion grant processing ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) and related profiles, such as [ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/)) does not require a typed, interoperable actor model. So delegation introduced at the federation boundary does not survive into token exchange or downstream access tokens in any consistent way.
## JWT Access Tokens Overload `sub`
In most [RFC 9068](https://datatracker.ietf.org/doc/html/rfc9068) deployments today, `sub` is doing too much:
- Is `sub` the end-user who authorized access?
- Is `sub` the workload client acting autonomously?
- Is `sub` an AI agent acting on someone's behalf?
This is not just an OAuth plumbing question. As explored in [You Don't Give Agents Credentials. You Grant Them Power of Attorney.](https://notes.karlmcguinness.com/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/), the identity and delegation semantics carried in tokens are the foundation on which any meaningful authority governance for agents has to be built. You cannot govern what you cannot see.
When a resource server cannot answer that question from the token alone, it cannot reliably distinguish:
- direct subject access,
- user-delegated client access, or
- non-user workload or agent access.
Some implementations fall back on `client_id == sub` to infer "client acting as itself." That heuristic can work in simple single-hop cases, but it does not survive delegation chains and it says nothing about what kind of principal either party is.
That ambiguity creates policy drift. Authorization decisions diverge, audit trails lose fidelity, and security reviews turn into arguments over interpretation.
## Actor Identity Is Asserted, Not Bound
Even after `act` is populated, the actor claim is just a string. There is no cryptographic guarantee that the entity presenting the token is the same entity identified in `act.sub`. A stolen token can be replayed by a different actor, and the resource server has no way to tell.
[Proof-of-Possession Key Semantics for JWTs (RFC 7800)](https://datatracker.ietf.org/doc/html/rfc7800) defines `cnf` to bind a token to a holder's key. The `jkt` member carries a SHA-256 JWK Thumbprint of the holder's public key. [DPoP-Protected JWT-Secured Authorization Grants (PoP JAG)](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-jwt-dpop-grant-01) and [Identity Assertion JWT Authorization Grant (ID-JAG), Section 8.4](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant-02#section-8.4) both support sender-constraining the current presenter: the assertion includes `cnf.jkt`, the client presents a DPoP proof, and the authorization server validates that the proof's public key matches the asserted thumbprint. No matching proof, no token.
Key binding is not required for every delegation scenario. Many internal workload deployments are fine with bearer semantics. But when an actor presents a DPoP key binding at the token endpoint, the issued token should carry that actor's key at the top-level `cnf`. On the next exchange hop, the newly issued token should repeat that pattern for the next presenter while preserving prior actor keys in the `act` chain as delegation history. If a `cnf.jkt` established at the assertion grant boundary is silently dropped in the issued access token, the resource server cannot enforce a sender constraint the authorization server already accepted.
> Identity semantics and delegation semantics must be explicit protocol data, not institutional memory. Key binding makes actor identity verifiable, not just visible.
# ๐ง The Proposal: `act` + Entity Profiles
The approach combines existing pieces:
- **`act`** as the explicit delegation vehicle (per Token Exchange).
- **Entity profile claims** (`sub_profile`, `client_profile`) to make principal type explicit on each principal, including within nested `act` nodes.
- **Top-level `cnf` with `jkt`** for the current token presenter when that presenter established a DPoP key binding, consistent with [PoP JAG](https://datatracker.ietf.org/doc/html/draft-parecki-oauth-jwt-dpop-grant-01), [ID-JAG Section 8.4](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant-02#section-8.4), and [RFC 7800](https://datatracker.ietf.org/doc/html/rfc7800). Prior actor keys may be preserved inside nested `act` nodes as delegation history, but only the top-level `cnf` is the live sender constraint the resource server enforces.
- **The same model applied to both surfaces**: JWT assertion grants and JWT access tokens.
The natural vocabulary for profile semantics is [Entity Profiles](https://datatracker.ietf.org/doc/html/draft-mora-oauth-entity-profiles), which defines `sub_profile` and `client_profile`. The actor-chain profile should align with that vocabulary rather than creating a parallel taxonomy. In the current draft, standardized values include `user`, `device`, `native_app`, `web_app`, `browser_app`, `service`, and `ai_agent`, and profile claims are space-delimited strings when multiple values are present.
For interoperable processing across trust domains, each principal and delegation relationship must be explicit. The resulting contract is:
| Claim | Meaning |
|---|---|
| `iss` | Issuer asserting the principal and delegation relationship |
| `sub` | Principal whose authority is being exercised |
| `act` | Principal currently acting on behalf of `sub` (when delegation exists) |
| `sub_profile` | Principal profile value(s) for `sub` or for any `act` node |
| `cnf.jkt` | SHA-256 JWK Thumbprint of the current presenter's key; carried at the top level when the current actor established a DPoP binding |
| `act.cnf.jkt` | Optional preserved thumbprint for a prior actor in the delegation chain; useful for audit and provenance, not live sender-constrained enforcement |
No new grant type. No new token type. A shared profile and processing rules built on what already exists.
# ๐ก What This Looks Like in Practice
## JWT Access Token: Before
```json
{
"iss": "https://as.example.com",
"aud": "https://api.example.com",
"client_id": "client-123",
"sub": "248289761001",
"scope": "payments:write",
"exp": 1773076800
}
```
A resource server cannot determine whether `sub` is a user, a client instance, or an agent identifier. It cannot tell whether this is direct access or delegated access. That ambiguity is exactly what a standard should eliminate.
## JWT Access Token: After
```json
{
"iss": "https://as.example.com",
"aud": "https://api.example.com",
"client_id": "client-123",
"sub": "248289761001",
"sub_profile": "user",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
},
"act": {
"sub": "agent-7f3c",
"iss": "https://as.example.com",
"sub_profile": "ai_agent"
},
"scope": "payments:write",
"exp": 1773076800
}
```
Now the resource server can evaluate both principals explicitly. Here the current actor established a DPoP key binding during token exchange, so `cnf.jkt` is present at the top level and the agent must present a matching DPoP proof. If no key binding was established, top-level `cnf` is absent and bearer semantics apply. No heuristics. No local conventions.
# ๐ The Canonical Cross-Domain Case
The most important scenario to get right is cross-domain delegation, where a federation boundary is crossed and then token exchange carries the principal chain downstream.
`planner-agent` is an external AI agent from `assistant.example` acting on behalf of Alice. It uses `hotel-tool` at `tools.example`, which must in turn call an internal backend at `inventory.example`, a service the agent cannot reach directly.
The flow covers four steps. Alice signs in and the agent receives a DPoP-bound refresh token. The agent exchanges that for a cross-domain ID-JAG. The agent presents the ID-JAG to obtain a tool access token, still bound to the agent's key. Finally, `hotel-tool` performs its own Token Exchange to reach the backend, shifting the live sender constraint to the tool's key. That last step is the point: the originating agent and the current presenter are not the same actor on every hop.
| Role | Entity Type | Subject ID | OAuth Client | Key Binding (`jkt`) |
|---|---|---|---|---|
| User | `user` | `user-alice` | No | n/a |
| AI agent | `ai_agent` | `planner-agent` | Yes | `NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs` |
| Tool | `service` | `hotel-tool` | Yes | `R3g0XuBFHRTqAq3HyOVSLWLBXb-RypXz6NsKrOSqMmE` |
## Step 1: Authorization Request
Alice is using `planner-agent` and signs in with `assistant.example`. The agent sends her through a standard OIDC authorization flow, using authorization code binding via `dpop_jkt` so the resulting code is tied to the agent's key.
The authorization request looks roughly like this:
```text
GET /authorize?
response_type=code
&client_id=planner-agent
&redirect_uri=https%3A%2F%2Fagent.assistant.example%2Fcallback
&scope=openid profile offline_access hotels:search hotels:book
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&dpop_jkt=NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs
```
After Alice authenticates, the agent redeems the code at the token endpoint and proves possession of the same key with DPoP:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fagent.assistant.example%2Fcallback
&client_id=planner-agent
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
```
The response below is abbreviated but still shows the surrounding token fields for realism. The artifact that matters for the next step is the DPoP-bound refresh token, which the agent will later use to mint an ID-JAG for cross-domain access.
```json
{
"access_token": "...",
"id_token": "",
"refresh_token": "",
"token_type": "DPoP"
}
```
At this point the agent has a user-authenticated session and a refresh token bound to its key, but the cross-domain delegation semantics are not yet packaged into a JWT grant another authorization server can consume directly.
## Step 2: Refresh Token to ID-JAG
`planner-agent` uses that refresh token as the `subject_token` in Token Exchange to obtain an ID-JAG for `hotel-tool` in another trust domain.
The request might look like this:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:refresh_token
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience=https%3A%2F%2Fauth.tools.example%2Ftoken
&scope=hotels:search hotels:book
```
That exchange does not change the delegation model. It packages the delegated user and acting agent into an ID-JAG that another authorization server can validate directly.
The token exchange response might look like this:
```json
{
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"token_type": "N_A",
"access_token": ""
}
```
Even though the issued artifact is an ID-JAG rather than an access token, Token Exchange still returns it in the `access_token` response field and relies on `issued_token_type` to tell the client what it actually received.
The resulting ID-JAG might look like this:
```json
{
"iss": "https://idp.assistant.example",
"sub": "user-alice",
"sub_profile": "user",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
},
"act": {
"iss": "https://idp.assistant.example",
"sub": "planner-agent",
"sub_profile": "ai_agent"
},
"scope": "hotels:search hotels:book",
"aud": "https://auth.tools.example/token",
"exp": 1773076800
}
```
The delegation is explicit in the ID-JAG itself: Alice authorized `planner-agent` to act for her. `planner-agent` established a DPoP key binding when the grant was issued, so `cnf.jkt` appears at the top level. That binding remains part of the chain until a later token exchange mints a new access token for a different current presenter.
## Step 3: ID-JAG to Tool Access Token
`planner-agent`, not `hotel-tool`, presents the ID-JAG to `auth.tools.example`. Because the grant is DPoP-bound, the agent uses the JWT-DPoP grant and proves possession of the same key that is referenced in the ID-JAG's top-level `cnf.jkt`.
The request might look like this:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=urn:ietf:params:oauth:grant-type:jwt-dpop
&assertion=
&scope=hotels:search hotels:book
```
The tools authorization server validates federation trust, validates the ID-JAG and the agent's proof of possession, and issues an access token for `hotel-tool` still bound to `planner-agent`'s key.
That tool access token might look like this:
```json
{
"iss": "https://auth.tools.example",
"aud": "https://api.tools.example/hotel-tool",
"sub": "user-alice",
"sub_profile": "user",
"scope": "hotels:search hotels:book",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
},
"act": {
"iss": "https://auth.tools.example",
"sub": "planner-agent",
"sub_profile": "ai_agent"
},
"exp": 1773078600
}
```
The current actor is still `planner-agent`, because it is the entity presenting this access token to `hotel-tool`. The live sender constraint remains the agent's top-level `cnf.jkt`. At this point there has been no key transition yet.
## Step 4: Tool Token Exchange for Backend Service
`hotel-tool` receives the inbound access token when `planner-agent` calls it. To reach `inventory.example`, it performs one more Token Exchange using that token as the `subject_token` and presenting its own DPoP proof. This is where the live sender constraint shifts from the agent's key to the tool's key.
The request might look like this:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https%3A%2F%2Finventory.example
&scope=inventory:reserve
```
The backend authorization server validates the inbound tool token, preserves the actor chain, validates `hotel-tool`'s proof of possession for the new audience, and issues a backend access token bound to the tool's key.
The resulting backend access token might look like this:
```json
{
"iss": "https://auth.inventory.example",
"aud": "https://api.inventory.example/reservations",
"sub": "user-alice",
"sub_profile": "user",
"scope": "inventory:reserve",
"cnf": {
"jkt": "R3g0XuBFHRTqAq3HyOVSLWLBXb-RypXz6NsKrOSqMmE"
},
"act": {
"iss": "https://auth.inventory.example",
"sub": "hotel-tool",
"sub_profile": "service",
"act": {
"iss": "https://auth.tools.example",
"sub": "planner-agent",
"sub_profile": "ai_agent",
"cnf": {
"jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
}
}
},
"exp": 1773078600
}
```
The current presenter is now `hotel-tool`, so the live sender constraint is the tool's top-level `cnf.jkt`. The nested `act` chain preserves `planner-agent` and the upstream key that established the original delegation. This is the key transition the profile needs to make explicit: each new access token binds only the current presenter, while the chain preserves who previously acted for whom.
What this scenario demonstrates is why both surfaces must align. The ID-JAG and the downstream access tokens encode the same delegation, in the same model. The first cross-domain hop keeps `planner-agent` as the current presenter. The later backend hop changes the current presenter to `hotel-tool` and mints a new top-level sender constraint without losing the upstream actor chain. The AS does not need to translate between different actor representations as it crosses issuance boundaries. The resource server receives a token where principal types and delegation history are unambiguous across the full chain.
> If assertion grants and access tokens use different delegation conventions, the ambiguity just moves one layer up. The alignment is the point.
# โ๏ธ Resource Server Processing
With a standardized model, the resource server can implement stable, issuer-agnostic logic:
```text
input: access_token jwt
dpop_proof jwt // required when token.cnf.jkt is present
1) Validate token (sig, iss, aud, exp) per JWT AT profile (RFC 9068).
2) Resolve subject := token.sub
Resolve subject_profiles := parse_profiles(token.sub_profile)
3) If token.cnf.jkt present: // live sender constraint for current presenter
Compute thumbprint(dpop_proof.header.jwk)
Verify computed thumbprint == token.cnf.jkt
Verify dpop_proof signature valid for the key in dpop_proof.header.jwk
Reject if mismatch
4) If token.act present:
actor := token.act.sub // current actor (RFC 8693)
actor_profiles := parse_profiles(token.act.sub_profile)
evaluate policy.allow_delegate(actor_profiles, subject_profiles)
evaluate policy.allow_actor_for_subject(actor, subject)
evaluate policy.allow_scope_for_pair(token.scope, actor, subject)
5) If token.act absent:
evaluate policy.allow_subject_self(token.scope, subject, subject_profiles)
6) Optionally log nested token.act.act... as delegation history for audit.
Nested cnf.jkt values record the key binding of each prior actor in the chain.
7) Enforce decision.
```
The three-case decision tree for relying parties becomes:
1. `act` present: dual-principal policy evaluation (`sub` authority + `act` authority to act).
2. No `act`, `sub_profile` contains `user`: direct user-context access.
3. No `act`, non-user subject profile: non-delegated workload or agent self-access.
`client_id` may still be an input, but it is not the delegation model. It cannot represent chained delegation and does not type either principal.
This is the operational value of the profile: the resource server does not branch on issuer-specific convention to understand delegation semantics. Authorization, audit, and policy enforcement work the same way across implementations.
# ๐ก๏ธ Deployment and Backward Compatibility
This does not require a flag day.
Existing deployments that only understand subject-only tokens continue to work. Explicit `act` is additive, and consumers that do not understand it can ignore it (with appropriate policy defaults). Discovery or profile metadata can signal capability for implementations that want to negotiate.
The goal is not disruption. The goal is ending private delegation semantics for new and federated deployments where interoperability actually matters.
# ๐ Caveat: Delegation Chain Is Not Execution Identity
A nested `act` chain tells you who claims to act for whom. It does not prove that the current request is a valid continuation of the originating execution.
The same delegation chain can appear in multiple unrelated executions. If each presenter holds the bound key for its hop, each presentation can still be valid under proof of possession. Sender-constraining answers "who is allowed to present this token," not "which execution produced this request."
From a JWT, you can answer "who signed this" and, with `act` plus `cnf`, "who is acting for whom" and "who proved possession of the current key." You cannot answer "which execution is this" unless some separate execution identifier or continuity mechanism is standardized and propagated alongside the delegation chain.
This proposal does not address execution identity or execution continuity. Its scope is limited to making delegation semantics and current presenter key binding explicit and interoperable across assertion grants and JWT access tokens.
# ๐ Recommendation
The OAuth WG should standardize an interoperable actor profile that:
- reuses `act` from Token Exchange,
- uses Entity Profiles vocabulary for principal typing (`sub_profile`) on each principal in the chain,
- uses top-level `cnf.jkt` for the current presenter when a token is sender-constrained, while allowing prior actor keys to be preserved inside nested `act` nodes as delegation history,
- applies uniformly to JWT assertion grants and JWT access tokens,
- defines deterministic processing rules for AS validation and resource server authorization, including DPoP proof verification against top-level `cnf.jkt` when present.
The primitive work is already done. `act` exists. Entity Profiles are being defined. Token Exchange is standardized. DPoP key binding for the current presenter is already specified in JWT grant work and in [ID-JAG Section 8.4](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant-02#section-8.4). What is needed is the profile that stitches them together across both token surfaces, makes the processing rules explicit, and ends the cross-profile ambiguity that has so far been left to implementer judgment.
> Until that profile exists, delegation will remain a private convention masquerading as a standard. Two implementations can be fully RFC-compliant and still disagree about who is acting for whom.
That is not an acceptable baseline for cross-domain identity, and it is the token-layer prerequisite for anything more ambitious. Governing delegated agent authority, enforcing mandate lifecycles, propagating revocation signals: none of it is tractable if the delegation relationship is invisible in the token.
When "who is acting for whom" is portable protocol meaning rather than local folklore, the ecosystem gets better federation, better authorization policy, and better audit trails. That is worth standardizing.
# Changelog
- `2026-04-30`: This proposal has been published as an IETF individual draft, [OAuth Actor Profile (`draft-mcguinness-oauth-actor-profile`)](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-actor-profile/), which turns the recommendations here into a concrete specification.
- `2026-03-18`: Clarified sender-constrained semantics so the current presenter uses top-level `cnf.jkt` and prior actor keys are preserved only as historical delegation context inside nested `act` nodes.
- `2026-03-18`: Added explicit alignment to ID-JAG Section 8.4 for sender-constraining the current presenter while keeping nested prior-actor keys as profile-specific history.
- `2026-03-18`: Reworked the canonical cross-domain example around an AI agent, a cross-domain tool, and a backend service, with an end-to-end flow from OIDC sign-in to ID-JAG issuance to downstream token exchanges, plus a cast table covering role, entity type, subject ID, OAuth client status, and key binding.
- `2026-03-18`: Added an explicit caveat that this proposal does not address execution identity or execution continuity.
---
# Mission Shaping Is Not Enough
Canonical URL: https://notes.karlmcguinness.com/notes/mission-shaping-is-not-enough/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-shaping-is-not-enough.md
[Part 1](/notes/the-mission-shaping-problem/) argued that approved intent does not become governable authority on its own. In structured domains, Mission shaping can produce a bounded Mission Authority Model and a reviewable approval object. That is real progress. The Mission is a new authorization primitive: the object that fills the gap between approved intent and governable authority.
It is still not enough.
The CFO example in Part 1 was the strong case: one enterprise, known systems, legible stages, and a control plane that can absorb transition friction. Even there, the hidden complexity was already visible.
The agent still runs in the world. It still encounters external content, discovers new facts, spawns child agents, accumulates partial state, and continues after the moment of initial approval. That is where the second problem begins. Mission shaping defines the authority envelope (the Mission is the protocol object that carries it). Containment and runtime governance determine whether the system stays survivable when that envelope turns out to be incomplete. That is the two-layer architecture this essay argues for: the Mission is the envelope, and the runtime layer is what keeps the system survivable when the envelope is wrong.
# Where Mission Shaping Breaks Under Pressure
The problems in this section all arise within a principled staged model, not only in undisciplined deployments. They are the pressure points where a coherent authority model begins to fray under operational reality.
## Scope Creep
The staging model handles the case where an agent surfaces a discovered gap and requests governed expansion. [Part 1's](/notes/the-mission-shaping-problem/) context integrity section handles adversarial redirection. There is a third failure mode between them: quiet authority expansion.
An agent decides that accomplishing the approved task requires something not originally anticipated, concludes it is obviously necessary, and does it without surfacing the discovery. No adversarial input. No explicit re-planning request. The agent's own judgment becomes the expansion mechanism.
Preventing this requires a minimum-authority principle. The shaped envelope has to be narrow enough that "obviously necessary" actions outside the original approval are not silently inside the authority region.
But minimum authority has its own failure mode: shape the envelope too narrowly and the system creates constant replanning churn. Operators get paged for predictable expansions. Teams respond by broadening every template until the model is no longer meaningfully bounded.
This is not only a theoretical tradeoff. Empirical work on [semantic task-to-scope matching](https://arxiv.org/abs/2510.26702) shows the same tension: over-scoping grants too much authority, but under-scoping can stall task completion when the matcher fails to include necessary permissions. [IBAC's AgentDojo evaluation](https://ibac.dev/ibac-paper.pdf) makes the failure mode concrete: the permissive-mode breaches traced directly to over-scoped wildcard permissions that happened to cover the injected goal. The agent's reasoning was fully compromised. The authorization surface let the compromised action through. Strict mode blocked all 240 injection attempts by scoping permissions to specific recipients and resources, at the cost of requiring escalation for predictable prerequisites.
That is why templates are necessary but not sufficient.
Task templates are pre-approved Mission archetypes for known task classes. In the structured case, they may also be pre-compiled authority archetypes. A human has already approved that "board packet preparation for a CFO" maps to approximately this set of resources and operations. The template becomes the shaping baseline. Individual Missions attenuate from it rather than starting from scratch. This anchors minimum authority in prior human approval rather than in each operator's judgment call.
Alongside templates, pre-approved expansion paths address predictable edge cases: known situations where discovery commonly reveals a need for one additional resource class, pre-authorized for this task type without requiring full re-authorization. Together, these two mechanisms reduce the re-planning friction that makes quiet expansion attractive.
But templates accumulate exceptions. Left ungoverned, they grow until the template itself becomes a broad standing grant with a respectable name. This is the IE Trusted Sites failure described in [Part 1](/notes/the-mission-shaping-problem/): every enterprise application that did not work in the restricted zone got added to Trusted Sites because that was easier than fixing the application or tightening the template. The same pressure operates here. Every agent task that stalls on a missing permission gets the template broadened rather than the governed expansion path used. Eventually the template covers everything the agent might plausibly need and nothing is actually constrained.
So templates have to be governed: versioned, reviewed, and retired when they become broader than the task class they were originally meant to represent. That governance is not optional. It is the mechanism that keeps minimum authority meaningful over time.
## Delegation
When a Mission is delegated, the child Mission's authority model must be derived from the parent. It cannot exceed the parent envelope. [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) (OAuth Token Exchange) provides the protocol plumbing for this derivation, but the semantic constraint, that the child authority is a proper subset of the parent, is a Mission property, part of the delegation context a Mission carries. Enforcement of semantic attenuation is implementation-dependent and not a protocol guarantee.
This is not a hypothetical risk introduced by agents. In production OAuth token exchange deployments today, child tokens routinely inherit full parent scope unless the implementation explicitly attenuates them. The multi-agent case compounds a failure mode already present in standard OAuth delegation rather than introducing a new one. Concretely, an orchestrator that receives a board-packet Mission and spawns a sub-agent to handle number reconciliation has delegated a slice of the parent Mission. The child's authority should be the parent's read-and-compare envelope for finance systems, and nothing else.
```text
+------------------+
| Mission state |
| owner |
+------------------+
| |
| +--> Trust quality
| assessment
| |
| v
| +------------------+
| | Budget policy |
| | tighter at hop |
| +------------------+
| |
v |
+------------------+ |
| Parent Mission | |
| full envelope | |
+------------------+ |
| |
+--> attenuated |
authority |
| |
v v
+----------------------+
| Child Mission |
| finance read-compare |
+----------------------+
|
v
+----------------------+
| Enforcement points |
+----------------------+
```
But that is not the only issue. Delegation also changes governability.
A child agent may:
- run in a different runtime
- expose weaker telemetry
- use different tool adapters
- have poorer provenance guarantees
The delegated authority may be narrower on paper and still be less governable in practice.
This means delegation must attenuate trust assumptions, not just authority bounds. Child Missions may need tighter budgets, stricter callbacks, or less autonomy simply because visibility and control quality are worse at that hop.
The mechanism for this attenuation should be explicit. The Mission state owner should assign a trust quality level to each hop based on observable signals: whether the child runtime emits verifiable tool-call provenance, whether telemetry is independently observed at a boundary rather than self-reported, and whether the child Mission has an established behavioral baseline or is operating cold-start. A child with low trust quality receives a more conservative budget policy and stricter callback thresholds regardless of whether its authority bounds are otherwise equivalent.
A harder case is spontaneous delegation: an orchestrator spawning a sub-agent at runtime without any prior human delegation event. This is common in practice and hard to govern cleanly. The Mission state owner must still issue child authority, but who triggers that issuance when the spawn is an autonomous runtime decision rather than a pre-authorized step? Without an answer to this, spontaneous sub-agents fall outside the governance model and effectively operate in the headless case, without the provenance hierarchy that headless governance requires.
This is also where requester context has to survive the delegation chain. If child agents cannot inherit and present the right task context at each hop, even well-bounded authority models become hard to evaluate consistently.
## Headless Missions
In headless cases, no human is present to verify the proposed Mission. That means deterministic shaping is not enough. You also need authority provenance. A scheduled nightly reconciliation agent operating from a standing organizational policy with no user present is the canonical headless case.
What is being shaped?
- an organizational policy
- a runbook
- a prior approved Mission
- a service owner's standing delegation
Those are not interchangeable sources of legitimacy.
So headless governance needs:
- machine-verifiable shaping rules
- machine-verifiable provenance for the authority source being shaped
If the provenance source cannot be independently verified by the Mission state owner without relying on the agent's own claims, it is not a headless governance anchor.
Valid provenance sources are not interchangeable. In descending order of legitimacy:
- A prior human-approved template Mission: a human already confirmed this shaping output is acceptable for this task class.
- A standing organizational policy expressed in a formal, auditable policy language.
- A service owner's cryptographically verifiable standing delegation.
What does not qualify: LLM inference about organizational intent, runtime configuration supplied by an agent or orchestration layer at execution time, or a general-purpose scope grant that predates the specific task.
This legitimacy hierarchy holds for any Mission, not only headless ones. The valid and invalid anchors are part of the Mission primitive itself.
Headless governance keeps the same authority anchor requirement as human-present governance. It just moves provenance verification to the machine. The next failure mode shifts from no human to no stable authority state at all.
## Resumption
Suspension is not necessarily terminal for long-running enterprise workflows.
A Mission interrupted by an anomalous action may need to resume once a human has reviewed what happened. But resumption is not the same as re-approval of the original Mission. The state has changed. Some reversible actions may have occurred. The trust budget has a consumption history.
A governable resumption model therefore needs:
- persistent execution state at suspension time
- a resumption decision that sees that state, not just the original intent
- trust budget continuity rather than full reset
Mission termination mid-execution also raises a distinct sub-problem: compensating or rolling back partial state from actions that have already completed. That is a workflow concern, but the governance layer must coordinate with it. A terminated Mission's authority does not undo side effects that have already propagated.
This is another place where the architecture starts to look less like IAM and more like a workflow governance control plane. Durable execution state across suspension events, compensating actions for partially completed workflows, and governed resumption with inherited context are patterns from durable execution systems in workflow orchestration, not from traditional IAM. That boundary crossing is not accidental. Mission governance for long-running agents needs to borrow from both disciplines. IAM without workflow state is insufficient for multi-stage tasks. Workflow orchestration without authority governance is insufficient for delegated, externally observable agents. The architecture that emerges from taking both seriously is neither a pure IAM system nor a pure orchestrator.
That architectural breadth has a cost. Running this control plane at enterprise scale means policy owners to govern templates, operators to review expansions and resumptions, telemetry pipelines that can support trusted observation, and engineering teams willing to absorb friction in exchange for bounded risk. Any serious argument for this architecture has to be honest about that operational burden.
Each of these failure modes is a form of survivable incorrectness in practice. The architecture cannot prevent scope creep entirely, cannot eliminate spontaneous delegation, cannot guarantee headless provenance, and cannot make resumption seamless. What it can do is bound the damage from each: staged authority limits quiet expansion, trust quality attenuation limits the radius of a delegated failure, headless provenance hierarchies establish accountability chains even without a human present, and resumption requirements prevent stale authority from being silently reactivated. The goal is not a control plane that never fails. It is one that fails in bounded, detectable, and recoverable ways.
# The Interoperability Problem
Each of the limits above operates within a single organization: shared purpose taxonomy, known systems, and a single Mission state owner. Cross-organizational deployment introduces complications the single-org model cannot absorb. Mission authority shaped in one trust domain cannot be evaluated semantically in another, even when the transport format is identical.
If two organizations deploy agent systems independently, each with their own purpose taxonomy and shaping logic, a Mission created in one cannot be meaningfully evaluated in the other. The `mission_ref` is portable. The semantics it encodes are not.
Interoperability requires more than a purpose taxonomy. It needs shared meaning for:
- resource classes
- action classes
- canonical subject and object identifiers
- selector and constraint semantics
- attenuation and expansion rules
- parent-to-child derivation rules
Without those, a "portable authority representation" is just a transport container for incompatible local meaning.
This identity resolution layer is easy to understate, and it is often where enterprise deployments actually break. "Board packet", "final numbers", "Q1", and even the CFO's own authority scope may resolve differently across finance systems, content repositories, and messaging tools.
Adjacent standards work helps with plumbing, but not with semantic authority:
- OAuth RAR (RFC 9396) provides a richer authorization request format than scopes, allowing structured authorization detail in the request itself. It is the closest existing standard to a Mission authority carrier. But RAR is a request language, not a shaping substrate; it still requires the semantic work to be done before the request is formed, and it does not govern the Mission lifecycle after issuance.
- Policy engines such as OPA and Cedar help express and evaluate policy once the world has already been reduced to stable resources, attributes, and actions, but they do not solve the shaping step that produces those stable inputs from natural-language intent.
The interoperability problem still points back to a missing semantic layer for Mission shaping.
RAR and token formats can carry authority. They do not supply the shared task semantics that make that authority interoperable.
# What Containment Adds
In open-world environments, containment has to carry safety properties that Mission shaping cannot provide.
Containment means:
- narrow discovery envelopes by default
- strong tool mediation and trusted adapters
- short-lived, attenuated credentials
- boundary observation that does not rely on agent self-report
- irreversible actions gated by callbacks, checkpoints, or explicit human release
- runtime isolation strong enough that a compromised agent has limited room to act
A tool call, as used here, means any invocation to an external system through an adapter: file reads and writes, API calls, browser navigation, code execution, and message dispatch. Every such call is a potential observation and enforcement point. Containment is meaningful only if the tool mediation surface is consistently enforced. A single unmediated path through a privileged adapter negates the rest.
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is one framework for tool mediation at this layer. [IBAC](https://ibac.dev/ibac-paper.pdf) demonstrates one concrete implementation of the enforcement boundary itself: a higher-order `invokeToolWithAuth` wrapper that every tool call must pass through, with no path around it. The wrapper checks the request against the intent-derived authorization store before execution; unauthorized calls are denied before they reach the tool. Whether using MCP, a proprietary adapter pattern, or an enforcement wrapper of this kind, the architectural requirement is the same: tool calls must flow through a boundary that can enforce authority constraints, log call provenance, and interrupt on anomaly without relying on the agent to report its own behavior accurately. But tool mediation alone is not enough. A gateway can enforce requests and still lack the deeper runtime context needed to judge intent well. Direct integration with underlying systems can provide richer behavioral visibility and still lack decisive control. Runtime governance needs both: enforcement points that can block and trusted observation points that can explain why a given action is risky.
Agent containment differs from standard defense-in-depth in its threat model. Traditional defense-in-depth assumes threats arrive from outside the perimeter and that authenticated actors are trusted. Agent containment inverts that assumption: the threat is authorized misuse by the agent itself, with correct credentials but misaligned or injected intent. The attack surface is the agent's context window, not network ingress. The goal is semantic integrity across the agent's execution, not perimeter security at the organizational boundary.
[NIST SP 800-207 (Zero Trust Architecture)](https://csrc.nist.gov/publications/detail/sp/800-207/final) provides the foundational framing: no implicit trust based on network location or credential possession, continuous verification of each request. Agent containment extends ZTA's microsegmentation principle to the agent's execution environment, treating each tool call as a request to be evaluated rather than trusting the agent to police its own behavior inside a broad authority grant. [NIST AI 100-1 (AI Risk Management Framework)](https://airc.nist.gov/RMF) places this in the broader governance context: the Mission shaping and containment controls described here are the authorization-layer instantiation of AI RMF's govern, map, measure, and manage functions for agentic AI systems.
This is not an alternative to Mission governance. It is what makes Mission governance survivable when the semantic layer is incomplete. Mission shaping gives you declared purpose, reviewable approval, and auditable authority boundaries. Containment gives you bounded blast radius, safer behavior when the semantics are wrong, and more reliable interruption when drift is detected.
Containment has costs. Tool mediation adds latency on every call. Callback gates for irreversible actions introduce availability dependencies. Narrow credentials create friction for legitimate scope expansions. There is also a staffing cost: someone has to configure and monitor the containment layer, triage pause events, and respond to budget exhaustion at runtime. Neither Mission shaping governance nor containment governance is free to operate. Treating containment as a first-class design decision means making these tradeoffs explicitly rather than discovering them under production load.
For open-world agents, containment is the more important layer.
# When To Rely On Each
Containment is not a fallback activated when Mission shaping fails. The two layers are co-equal. The right balance depends on deployment context. There are signals that indicate which layer needs to carry more weight.
**Mission shaping is stronger when:**
- The domain is structured and the task class is well-defined (board packet preparation, not "do research")
- The systems involved are known and enumerable at approval time
- The agent operates within a single organizational trust domain
- There are observable, gated transition points between reversible and irreversible phases
- The control plane can tolerate approval friction for stage transitions
**Containment is more important when:**
- The environment is partially observable (the control plane cannot independently verify what tool calls the agent is making)
- The agent may encounter external content it did not generate and cannot be fully trusted not to act on
- The task involves cross-domain or cross-organizational boundaries with incompatible shaping semantics
- The agent's telemetry is self-reported rather than independently observed
- Failure needs to be survivable regardless of whether the semantic model is correct
[The IE zone model](/notes/the-mission-shaping-problem/#a-historical-parallel) is a useful map. Local Intranet corresponds to the structured case: known systems, organizational trust domain, observable transitions, controllable policy. The Internet zone corresponds to the containment-primary case: unknown origins, uncontrollable external content, incompatible semantics, weak observability. Most agent deployments sit somewhere between them, which is exactly where IE zone policy was hardest to get right.
The IE lesson about defaults applies directly: the right default for an unclassified agent task is the most restricted posture. Not "figure out what this task probably needs and grant approximately that." The default is minimum authority, and the organizational process should be to elevate by governed exception with documented justification, not to restrict by exception from a broad default. Getting the default wrong is how Trusted Sites fills up.
The two examples in this series illustrate the poles. The CFO board-packet task is the structured case: one enterprise, known systems, structured approval path, legible stages. A shaping-centered architecture with staged compilation, governed expansion, and callbacks is tractable. The vendor research task ("research vendors, summarize options, and begin outreach where appropriate") is the containment-primary case: open-ended scope, external content at every step, ambiguous action boundaries, no way to fully enumerate the authority model upfront. Mission shaping still provides a governance record and outer bounds, but the real safety properties come from mediated outreach, short-lived credentials, sandboxed browsing, and explicit release gates before any external communication is sent.
Most real deployments fall somewhere between the two poles. The practical answer for an enterprise finance workflow is: lead with staged Mission shaping for structured phases, often implemented as staged compilation, and surround it with containment controls that keep the blast radius bounded if the semantic model turns out to be wrong.
For open-world tool-using agents with no known execution path in advance, lead with containment and treat Mission shaping more as a governance record than as a safety guarantee.
Both layers still leave a residual problem: an agent can stay within its Mission authority envelope while drifting away from the user's intent. A runtime layer is needed to detect that drift and act on it before it compounds.
# Runtime Alignment: Trust Scores and Budgets
If Mission shaping defines the declared authority ceiling, runtime trust defines how much residual uncertainty the system is willing to tolerate inside it. What follows is one practical control-plane pattern, not a settled standard. Continuous observability is not governance, but it is the runtime evidence layer that makes behavioral analysis and dynamic intervention possible.
```text
+----------------------+
| Mission authority |
| envelope |
+----------------------+
|
v
+----------------------+
| Agent actions |
+----------------------+
|
v
+----------------------+
| Observability |
| signals |
+----------------------+
|
v
+----------------------+
| Trust budget |
+----------------------+
|
v
+----------------------+
| Pause / callback / |
| suspend |
+----------------------+
+----------------------+
| Containment |
| boundaries |
+----------------------+
|
+--> constrain actions
+--> enforce stops
```
This should be understood as part of the same control plane, not as a second hidden policy engine. The Mission authority artifact remains authoritative for what the Mission permits. The runtime trust layer does not expand authority. It only decides whether the system is still willing to rely on the agent's local judgment within already-shaped bounds.
Even conceptually, that assumes a lot from the underlying system. To score runtime alignment, the control plane needs structured tool calls, stage context, parent Mission linkage, and normalized provenance about where the action came from. Most enterprises do not have that by default, and the visibility gap varies by agent type. Homegrown agents expose different control surfaces than SaaS agent platforms or local workstation agents, and locally run tools are often the hardest for enterprise controls to observe reliably.
And visibility is not enough. If the agent runtime, orchestration layer, or tool adapter can freely invent the telemetry that the trust layer depends on, the system has recreated self-governance one layer down. Runtime alignment needs integrity as well as visibility. Telemetry must be attributable to a verifiable source, not taken on the agent's word. [SPIFFE/SPIRE](https://spiffe.io/) workload identity provides one foundation: cryptographic attestation of which runtime component produced a given report. [CAEP (Continuous Access Evaluation Protocol)](https://openid.net/wg/sse/) and the Shared Signals Framework provide the transport: a standardized event stream for runtime security events flowing from the components that observe them to the Mission state owner that needs to act on them. Without both integrity and a signal pathway, the runtime alignment layer either trusts what agents report about themselves or polls blindly.
The right model treats residual runtime trust not as binary, but as a continuously scored, consumable resource. The pattern is [just-in-time trust](https://softwareanalyst.substack.com/p/the-future-of-just-in-time-trust) applied to Mission lifecycle. The system grants only the minimum authority needed at the moment it is needed and reconfirms rather than assumes continuity across the task's execution. Recent runtime-governance work, including [MI9](https://arxiv.org/abs/2508.03858), converges on a similar point: semantic telemetry, continuous authorization monitoring, drift detection, and graduated containment become necessary once agent behavior cannot be fully governed at approval time.
Each agent action is evaluated against a weighted intent score: does this align with the approved intent? Is it consistent with the agent's established pattern for this task? How much damage if it results from context taint? How much of the Mission window remains? A low intent score on a high-risk action is grounds for pausing and requiring re-confirmation even when the action falls within the Mission authority envelope.
Each Mission carries a consumable trust budget. Actions that score low on intent alignment consume budget faster. Actions anomalous relative to the behavioral baseline consume faster still. Budget exhaustion triggers re-confirmation or Mission suspension. Budget state belongs with the Mission state owner. Exhaustion is a lifecycle event, not a side-channel signal.
Practical calibration needs a signal model, a baseline, and a cold-start policy. Useful observable signals include resource type distribution relative to the declared purpose, rate of external communication attempts relative to task phase, and semantic similarity of each action to prior approved executions of the same task class. The baseline for a given task class is most reliably established from reviewed template Missions. Prior human-reviewed executions define the expected action distribution that a new run is scored against. The cold-start problem has no clean solution. A reasonable default is to open with the strictest callback thresholds in the relevant template class and relax the budget as an execution baseline accumulates.
Semantic drift detection is the operational form: monitoring whether the agent's actions are drifting from the semantic neighborhood of the approved intent. A board-packet preparation Mission should produce mostly reads, comparisons, and internal assembly operations. If the action log starts showing HR system reads, treasury queries, or external communication attempts, the distribution has shifted outside the expected neighborhood for this purpose class even if each individual call was permitted.
The governance boundary is precise. Shaped authority is authoritative for permit and deny decisions. Trust scores are authoritative for pause, callback, and suspension decisions. Trust scores do not expand Mission scope.
A trust budget is not additional privilege. It is a continued-reliance threshold for exercising already-shaped authority.
This is closer to risk-adaptive and continuous-evaluation control than to entitlement expansion.
> The shaped Mission Authority Model answers "is this action permitted?" The trust layer answers "is the system still willing to rely on this agent's judgment while it does it?"
Staged Mission shaping and trust budgets interact. A Stage 1 discovery envelope carries lower inherent risk (the agent cannot write, publish, or communicate externally), so the trust budget can be calibrated more permissively. When the Mission transitions to Stage 2, the budget policy should tighten: the same semantic drift tolerable during read-only discovery becomes a higher-stakes signal during write-capable execution. The Mission state owner issues a revised budget policy alongside the revised authority artifact at each stage transition.
Trust scoring is probabilistic, not deterministic. A sophisticated adversary may craft injected instructions that score well on intent alignment. The behavioral baseline must be established before drift can be detected, creating a cold-start problem. The scoring model itself becomes an attack surface. These limitations are real. The value of this layer is not certainty. A governed control plane that can act on partial confidence and bounded suspicion is more honest than one that pretends runtime alignment is either fully knowable or irrelevant.
# Design Positions
The following positions have defensible answers. Each has a live counterargument worth naming. The first two concern Mission shaping directly; the last two concern containment and the runtime layer explored in this part.
**LLM-based Mission shaping is acceptable as a proposal step, but should not be the sole authoritative one in high-assurance systems.** The counterargument is that human review of a structured proposal does not actually close the verification gap: if the CFO cannot evaluate the compiled artifact, reviewing the LLM proposal does not help either. That objection is correct about the limits of review, but it argues for better review tooling (progressive disclosure, natural-language summaries of effective permissions), not for removing the human approval step. The LLM proposes. The authorizing system approves. The Mission state owner locks the result. Treating unconstrained LLM output as the authority artifact without that review is inference masquerading as authorization.
**Mission shaping belongs with the Mission state owner, not in the agent.** The counterargument is that the orchestration layer already owns execution state and is better positioned to perform shaping incrementally. That is architecturally attractive but it conflates semantic authority with execution management. If shaping lives in the agent or orchestrator, it is self-governance. The system of record for Mission state should own the authority artifact, staged expansions, and the runtime signals that can suspend or terminate it.
**Confirmation thresholds for irreversible actions should be risk-based, not permission-based.** Whether an action falls within the Mission authority envelope is the wrong question for high-stakes decisions. The right question is whether the action is irreversible and whether the risk magnitude justifies a human checkpoint regardless of authorization status. Financial transactions above a threshold, permanent data deletion, external communications on the user's behalf: these warrant re-confirmation even when they are within authority bounds.
**Containment controls should be first-class design decisions, not deployment accidents.** Most deployments have some containment by accident. Treating containment as an intentional layer (deciding which tool calls are mediated, what credentials are issued, and which boundaries are independently observed) is a different and more defensible posture than hoping accidental constraints are sufficient.
# Open Questions
These questions span both parts of the series.
**Mission shaping**
- What is the minimum viable purpose taxonomy for common agent use cases, and who defines it?
- Can Mission shaping be made deterministic enough to govern without losing the expressiveness that makes natural language intent useful?
- What does a verification UI look like that achieves better bounded comprehension without overwhelming the approver?
- Who can authorize a stage transition, and what mechanism prevents an agent from self-promoting to the next stage?
- At what point do accumulated expansion decisions make the executed authority model so different from the originally shaped one that the original approval no longer meaningfully covers the activity? This boundary defines when re-approval is required rather than another governed expansion; the architecture currently has no answer for it.
**Authorization and enforcement**
- What is the developer-facing API for agent teams to propose Missions, receive a Mission Authority Model, request expansions, and report telemetry? The governance model is described from the operator perspective; the agent side of the interface is not.
- How does Mission governance retrofit onto orchestration frameworks (LangGraph, CrewAI, AutoGen) that already create child agents in production without Mission authority? What is the integration surface?
- What is the right composition between enforcement-point-level authorization (IBAC or CaMeL style at the tool boundary, ASTRA style at token issuance) and Mission-level shaping? Are these complementary layers in the same control plane, or competing designs that reflect different threat models?
**Runtime alignment**
- How should trust budgets be sized and calibrated for different task classes?
- Can trust scoring resist adversarial optimization by an attacker who understands the scoring model?
- What is the right cold-start policy when no behavioral baseline exists for a new agent or task class?
**Interoperability**
- Is cross-organization authority portability achievable, or is it always federation with semantic gaps?
- Which task verticals are positioned to define domain-specific purpose taxonomies first?
**Control-plane architecture**
- Where should the Mission control plane boundary actually sit: inside the authorization server, in a separate authority service, or partially inside the orchestrator that already owns durable execution state?
- What is the governed failure mode when the Mission state owner is unavailable during an active Mission? An active execution that reaches Stage 2 while the Mission state owner loses quorum will continue honoring stale Stage 1 tokens at enforcement points; this is not a hypothetical edge case.
# The Deeper Problem
The field is treating governance problems as engineering problems. Better token formats, tighter delegation chains, more expressive policy languages. Standards bodies are good at this work. But the protocol layer assumes the hard Mission shaping work is solved upstream.
It is not.
Some adjacent proposals, such as [Authenticated Delegation and Authorized AI Agents](https://arxiv.org/abs/2501.09674), try to solve more of the problem at the delegation and credential layer by emphasizing authenticated delegation, agent-specific credentials, and auditable chains of accountability. Those are useful contributions. They still do not remove the need for Mission shaping, because binding a delegation chain to an agent does not by itself tell the system what bounded authority should exist for the task in the first place.
The systems that come closest to solving the enforcement problem are each explicit about this gap in their own limitations sections. IBAC's model is scoped to a single request and a single agent; it does not address multi-agent delegation or long-running missions that span many requests. CaMeL handles data provenance within a single execution but not across delegation hops. The ASTRA work on semantic task-to-scope matching acknowledges directly that "applying delegated authorization in this way across chains of agents demands mechanisms for preserving requester context" that the approach does not yet provide. The gap this series addresses is not a gap the field has missed. It is a gap the field's best current work has named and left open.
> You can have a perfectly specified [Mission-Bound OAuth](/notes/mission-bound-oauth/) deployment with a `mission_ref` on every token, lifecycle management at the AS, and verified actor continuity across every hop. And still have no idea whether the agent is operating within the bounds the user actually intended.
The conclusion is not that compilation is useless. It is that compilation is one disciplined form of Mission shaping, and Mission shaping alone is insufficient, whether formal or informal, without containment. Containment is not a fallback. It is the co-equal operational resilience layer. Better Mission shaping is necessary for structured domains where the task class is well-defined and semantic fidelity is achievable. Containment is equally necessary in open environments where the semantic model will always be incomplete. Mission governance requires both, and architectures should be explicit about which layer is carrying the weight in any given deployment.
The Mission shaping record also has a governance value independent of its safety properties. The Approved Mission is the prerequisite for meaningful audit: without a declared governance record of what was supposed to happen, the system cannot determine whether what happened was authorized, who approved it, and on what basis. In regulated environments where agent actions affect financial records, health data, or external communications, that audit prerequisite is not optional. Mission shaping may not prevent all misalignment. But it is the only mechanism that produces a reviewable approval object that post-hoc investigation can compare against the actual execution record.
The operational goal is survivable incorrectness: a control plane that can remain governable and limit damage even when the semantic model is partial, noisy, or wrong. Perfect semantic fidelity is not achievable at the scale and openness of real agent deployments. Survivable incorrectness is.
Deployment posture also depends on where the organization sits on the human approval spectrum. In practice there is a range: fully automated, human-on-the-loop (reviews batches), supervised autonomy (can interrupt), human-in-the-loop (approves each step). Where an organization sits on that spectrum directly determines how much Mission shaping and how much containment the deployment requires. That calibration is an organizational policy decision, not a protocol question.
That also narrows some of the design uncertainty. The overhead of formal Mission shaping is justified first for structured enterprise workflows with stable systems, legible approval points, and tolerable governance friction. In those domains, Mission shaping may take the form of explicit compilation. In the middle ground between full upfront planning and containment-first execution, the more realistic target is bounded adaptation: the agent can discover, propose, and adjust, but only inside a staged Mission envelope with governed expansion and runtime checkpoints.
For open-world tool-using agents, organizations should expect to lead with containment and treat Mission shaping more as a governance record than as the primary safety guarantee. In some task classes, that should be an explicit policy decision: choose containment-first governance rather than waiting for semantic shaping to mature, because survivability matters more than semantic richness. In other cases, the right policy may be stricter still. Do not allow the task to run agentically at all until the runtime is sufficiently contained and the blast radius is acceptable. The evidence so far also suggests that a shared semantic layer for shaped Mission authority is more likely to emerge as domain-specific families than as one universal model for all agent behavior.
That is a more demanding view of what agent authorization requires. It is also the more operationally honest one.
---
# The Mission Shaping Problem
Canonical URL: https://notes.karlmcguinness.com/notes/the-mission-shaping-problem/
Markdown URL: https://notes.karlmcguinness.com/notes/the-mission-shaping-problem.md
When a CFO asks an agent to assemble the board packet, reconcile the final numbers, and call back for approval before releasing the presentation, they are not specifying an access control list. They are expressing intent.
Something has to turn that intent into something a control plane and its enforcement points can evaluate. That conversion is the Mission shaping problem.
In the vocabulary of this series, human intent becomes a [Mission Proposal](/notes/mission-bound-oauth/#key-terms). Human approval turns that into an [Approved Mission](/notes/mission-bound-oauth/#key-terms). The control plane materializes that as a [Mission Authority Model](/notes/mission-bound-oauth/#key-terms). Enforcement points see the resulting authority artifact.
Mission shaping does not replace policy engines, PDPs, PEPs, token services, or authorization servers. It sits upstream of all of them and produces the task-bounded authority artifact they need to make coherent decisions for agentic work.
Many current deployments do not have a disciplined Mission shaping step. The agent gets a token with broad scopes, infers its own boundaries from context, and the system trusts it to stay within them. That is not governance. That is optimism.
This article focuses on LLM-driven, tool-using agents executing multi-step tasks. This is the deployment class where Mission shaping is hardest, the blast radius is largest, and the governance gap between current practice and what safety requires is widest.
But there is a harder conclusion visible even within that argument. Even if deployments had a rigorous Mission shaping step, shaped authority alone would still not be enough for open-world agents.
Mission shaping is the semantic anchor. Containment is the operational resilience layer that has to carry the safety margin when the semantic model is incomplete. Containment means designing the agent's operating environment so that a failure, compromise, or misaligned action has bounded blast radius through narrow credentials, mediated tools, trusted observation points, and explicit release gates.
So the real question is not just how to shape a Mission from approved intent. It is where the semantic layer is strong enough to bear the weight, and where containment has to carry more of it.
> The gap between what a user approves and what a system can govern is not a configuration problem. It is a semantic one. But semantic problems do not disappear just because you shape them into a more structured artifact.
Mission shaping still matters. It gives the system a declared purpose, a bounded authority artifact, a reviewable approval object, and an auditable explanation of what the task was supposed to be. In structured domains, that shaping can take the form of compilation. But containment is what keeps the system safe when the semantic model is incomplete, the runtime is partially observable, or the agent's behavior drifts inside nominally legitimate authority.
This essay picks up a question left open by [Part 4](/notes/why-mission-bound-oauth-might-be-the-wrong-answer/) of the [Mission-Bound OAuth series](/series/mission-bound-oauth/).
# A Historical Parallel
The browser world navigated a structurally similar problem a generation earlier. Its failure modes are the closest historical precedent for where agent authorization is now.
In the late 1990s, browsers became the first mainstream open-world agents. A browser could navigate anywhere: internal intranet resources, trusted enterprise applications, and the open internet. Each environment carried different risk. Organizations needed a way to let employees use the browser productively without giving the open internet the same authority as internal systems.
Internet Explorer's answer was security zones. Local Intranet, Trusted Sites, Internet, and Restricted Sites each had a default capability envelope: what scripts could run, what plugins could load, what downloads were permitted. Administrators could assign URLs to zones via Group Policy and configure zone-level policy centrally.
The mapping to agent authorization is direct:
- Security zones are authority envelopes. The Internet zone is the minimum-authority default for unknown intent. Trusted Sites is the enterprise template Mission for known, pre-approved task classes.
- Zone assignment is the classification step. Deciding that a URL belongs in the Intranet zone is the same problem as deciding that "assemble the board packet" belongs in the "structured enterprise workflow" authority class.
- Group Policy zone configuration is organizational Mission shaping governance. The admin defining what the Internet zone can and cannot do is the admin defining a purpose taxonomy.
- The default Internet zone posture is the minimum authority principle. Unknown origin, most restricted envelope. Elevate by organizational exception with documented justification.
The browser world also learned the failure modes:
- Everything ends up in Trusted Sites. Every enterprise application that did not work in the Internet zone got added to Trusted Sites because that was easier than fixing the application or narrowing the zone policy. Templates accumulate exceptions for the same reason.
- Zone classification alone was insufficient. Modern browsers do not rely primarily on zones. Process-level sandboxing, Content Security Policy, CORS, and same-origin policy are the actual safety mechanisms. Classification is still there, but containment became the primary layer.
- The escalation mechanism is the attack surface. Users who could manually add sites to Trusted Sites were a persistent governance failure. Agents that can quietly self-promote their authority class are the same failure.
The analogy also shows where agent authorization is harder. URL classification is syntactic: string matching against a domain or pattern. Intent classification is semantic: natural language, open-ended, context-dependent. That difference is not incidental. It is the source of most of what makes agent authorization hard in ways that URL-based zone models never had to confront.
Authority is also time-varying in a way zones never were. A URL stayed in its zone. A Mission Authority Model changes as the task progresses from discovery to execution to external release. The staged, versioned Mission shaping model has no zone equivalent. That is one reason the zone model, however well adapted, cannot be sufficient by itself.
The threat model is inverted. IE's zone model protected the user from external content attacking through the browser. The agent's context integrity problem is the agent using its own legitimate authority in service of external adversarial content it encountered mid-execution. Containment therefore needs to operate differently: not just at the boundary between the agent and external systems, but also on what the agent can do after it has processed external content.
Organizations need some mechanism to manage risk while gaining the benefits of open-world agents. The zone model is the right structural intuition: classify intent into authority classes, apply default-restrictive policy, elevate by governed exception. The browser world shows that this is a necessary first step and an insufficient final answer. Containment is what made browsers survivable at scale, and containment is what will make open-world agents survivable too.
# What Mission Shaping Means Here
The compiler analogy is useful for describing one version of the job, less useful for describing the full problem. In software, a compiler transforms a formal language with defined semantics into machine-executable instructions. Mission shaping is broader. It is the work of taking a high-level approval and producing something the Mission control plane and its enforcement points can apply mechanically across API calls, tool invocations, and delegation boundaries.
In the structured case, Mission shaping can take the form of explicit compilation. In the more open case, the shaping layer is less formal: a bounded purpose statement, a set of allowed tool classes, and an operating envelope rather than an enumerable action list. That coarser output still provides the governance anchor (the approved authority artifact that the control plane and containment layer can reference) even when it cannot enumerate every permitted action. What changes across cases is precision, not the requirement for an artifact.
The structured-case pipeline looks like this:
```
Human intent
โ
Mission Proposal
โ
Approved Mission
โ
Mission Authority Model
โ
Enforcement
```
Each step is a transformation. Each transformation is a place where meaning can be lost, distorted, or never captured.
That is the core Mission shaping problem.
# A Four-Layer Model
Agent authorization is easier to reason about if four distinct concerns are kept separate. From an IAM perspective, the separation is straightforward. Identity answers who is acting. Mission shaping answers what authority should exist for the approved task. Authorization answers whether a requested action is inside that authority. Workflow and runtime governance answer whether execution should continue under current conditions.
In the vocabulary of the XACML and NIST policy model, the Mission state owner corresponds to the Policy Administration Point (PAP). It authors, versions, and lifecycles the authority artifact. Enforcement points at API and tool boundaries are Policy Enforcement Points (PEPs). The authorization layer is the Policy Decision Point (PDP). Runtime alignment has no direct equivalent in the traditional PAP/PDP/PEP decomposition and belongs to the usage-control tradition discussed below.
| Layer | Question | Output | Who |
|---|---|---|---|
| Mission shaping | What authority should exist for this approved task? | Authority artifact: Mission Proposal, Approved Mission, and Mission Authority Model in structured domains; a bounded purpose record and operating envelope in open-world cases | Mission state owner |
| Authorization | Is this specific action inside that authority? | Permit, deny, step-up, or suspend | Enforcement points at API and tool boundaries |
| Containment | If the authority model is wrong, incomplete, or bypassed, how much damage is possible? | Bounded blast radius through narrow credentials, mediated tools, runtime isolation, and explicit release gates | Enforcement architecture, gateways, trusted adapters, and runtime boundaries |
| Runtime alignment | Even if the action is allowed, is the agent still acting in service of the approved intent? | Continue, pause, re-confirm, or terminate | Mission state owner governing lifecycle decisions, fed by distributed observation points such as gateways, tool adapters, and trusted telemetry sources |
Mission shaping creates the authority artifact.
Authorization checks whether a call is inside its bounds.
Containment bounds the damage if the artifact is wrong, incomplete, or bypassed.
Runtime alignment detects the residual risk that remains even when each individual call is technically permitted.
That last layer matters because LLM-driven agents can be corrupted by prompt injection or context taint while still operating inside legitimate authority. Mission shaping defines the ceiling. It does not guarantee the agent is still acting in service of the user's intent.
The semantic challenge of getting that layer right is the Mission shaping problem. Compilation is one disciplined form of Mission shaping, strongest in structured domains and insufficient in most open ones. The more open the environment becomes, the more the architecture has to lean on containment. The goal is not a system that is always semantically correct. It is a system that remains governable when the semantic model is wrong: survivable incorrectness rather than an unachievable semantic guarantee.
The family resemblance to continuity-of-use or usage-control thinking is intentional, including the UCON tradition (Park & Sandhu, 2004). Authority should not be treated as a one-time admission decision, but as something that remains subject to ongoing conditions while the task continues. Agents make that continuity problem harder. The system is not only asking whether a session should continue, but whether an adaptive, tool-using, partially observable actor should continue under the same authority envelope. The trust scores and budgets introduced in [Part 2](/notes/mission-shaping-is-not-enough/) are one operational form of that continuity principle.
UCON was technically coherent but never achieved deployment at scale. The enforcement infrastructure required to act on continuous conditions simply did not exist in 2004. What is different now is that LLMs make natural-language intent tractable as an input to authorization systems, the zero trust and workload identity infrastructure exists as a practical enforcement layer, and regulatory pressure to govern autonomous system behavior is pushing organizations to take runtime authorization seriously in a way that has no parallel in the UCON era.
# Why Mission Shaping Is Hard
## Semantic Ambiguity
Natural language intent is inherently ambiguous. "Assemble the board packet and call me back before release" could authorize pulling draft forecasts, reading prior board materials, reconciling finance data, contacting the FP&A system, or notifying the legal team that a release is pending. Or it could authorize only gathering materials and presenting a summary for review.
What gets approved is rarely just what the user asked for. In enterprise systems it is usually a constrained intersection of requested purpose, system design, business role, and organizational policy.
The shaping step has to convert that ambiguous intent into something a resource server can evaluate deterministically. In the structured case, that means a compilation step. Either way, it requires a purpose taxonomy: a shared vocabulary for what "board packet preparation" means in terms of actual API operations and data access.
No such shared taxonomy exists. Every implementation invents its own.
The failure pattern this creates is familiar from RBAC role explosion and ABAC attribute normalization. Both required organizations to maintain shared vocabulary mappings that accumulated exceptions, became inconsistently applied, and expanded past what any single team could govern. A purpose taxonomy for agent tasks follows the same organizational dynamics.
The taxonomy is also a governance artifact: it encodes organizational policy into machine-evaluable terms and is subject to its own scope creep, organizational drift, and capture by convenience. Who governs the purpose taxonomy itself is a question the architecture must answer.
[OpenID AuthZEN](https://openid.net/specs/authorization-api-1_0.html) is an approved OIDF standard for the PEP-to-PDP query interface. It standardizes how enforcement points ask for authorization decisions once subject, resource, action, and context are already structured enough to evaluate. Mission shaping is the step that produces those stable inputs. For open-world LLM-driven agents, the resources may not be enumerable at policy-writing time, the attributes may not map cleanly to natural-language intent, and the policy for a novel task class may not exist at all. AuthZEN and downstream policy engines still require that upstream work to be done.
## The Verification Problem
Even if you have a Mission shaping step, how do you verify that the resulting authority model represents what the user actually approved?
With code, a compiler translates a formal language with defined semantics. You can inspect the output. You can write tests. You can formally verify properties of the compiled artifact.
With intent, you are translating natural language. The input has no formal grammar. The compiler has no type system to catch semantic errors. The authorizing user cannot inspect the Mission Authority Model in any meaningful way. They approved something in human terms. The compiled artifact is in machine terms. The mapping between them is opaque.
> A CFO who approved "assemble the board packet and call back before release" has no way to verify that the compiled authority model does not also permit the agent to access unrelated HR records, treasury operations, or investor communications, if the purpose taxonomy the system uses is broader than they understood.
The verification gap is not theoretical. It is the difference between informed consent and a terms-of-service checkbox.
You can reduce that gap with better UX:
- a natural language summary generated from the compiled artifact rather than the original proposal
- progressive disclosure of the effective permission envelope
- callback points for the highest-consequence transitions
- a consent receipt that records what the user actually approved
But complex enterprise workflows quickly exceed what an approving human can meaningfully reason about from a summary. So the real goal is not perfect human comprehension. It is better bounded comprehension.
The gap also worsens as adoption scales. The first task templates are reviewable by a careful operator. After a purpose taxonomy grows to hundreds of templates with layered pre-approved expansion paths, verifying that any specific compiled output correctly represents the original approval intent becomes progressively less tractable. Organizations that use the architecture most heavily face the worst verification fidelity, exactly backwards from what a governance model should produce.
## The LLM Trust Problem
Many deployments that do have a shaping step use an LLM to perform it. That is a practical approach. It is also a governance problem. The first point of adversarial influence is wherever the Mission Proposal is generated: whether that is the agent itself, a form-to-proposal translation layer, or an orchestration system.
An LLM-based compiler is not deterministic. The same intent statement can produce different authority models on different runs even in the absence of adversarial input. It can also be influenced through the prompt. It has no formal semantics. And its output is the input to your enforcement system.
That matters even before you get to prompt injection. If two benign runs produce materially different authority envelopes, reproducibility, operator review, and audit all become unstable. Prompt injection makes that worse. For high-assurance governance, LLM-first Mission shaping should therefore remain a proposal step wrapped by bounded review and approval, not the sole authoritative step.
One architectural response is to narrow the input surface of the shaping LLM to trusted sources only. [IBAC](https://ibac.dev/ibac-paper.pdf) takes this approach: the intent parser operates exclusively on the user's message and a pre-resolved trusted contact store, explicitly excluding any external data sources the agent has already touched. That does not make the parser deterministic, but it removes the adversarial content path from the shaping step. Parser errors can then over-scope or under-scope, but they cannot be driven by injected content encountered during execution.
## The Context Integrity Problem
Even with a perfectly shaped Mission Authority Model, an agent operating in an adversarial environment can be redirected: not by exceeding its authority, but by using its legitimate authority in service of someone else's intent.
Prompt injection is the concrete case. The agent is processing a document, an email, or an API response. That content contains adversarial instructions designed to redirect behavior. The enforcement point sees calls that fall within the shaped authority envelope. Nothing is flagged. The user's approved intent has been subverted without a single authorization failure.
This is not a problem that shaped authority models solve. It is a problem they cannot observe.
Several partial defenses are possible. Isolating the shaping step before the agent reads external content prevents adversarial inputs from influencing the authority model, but only for tasks whose execution path is knowable upfront. Intent anchoring cryptographically binds the approved purpose to the authority artifact and creates a reference point for post-hoc analysis. It does not prevent context taint inside that envelope. Call provenance logging helps reconstruct what happened after the fact. Human confirmation gates on irreversible actions limit the damage an injected instruction can accomplish even when it successfully redirects the agent.
But these are not enough on their own. Prompt injection is where containment has to carry more weight than Mission shaping. There is no semantic layer that can observe what happens inside the agent's context window.
Context integrity failures compound the runtime alignment problem. A semantically intact Mission Authority Model offers no signal that the agent has been redirected. Every call looks authorized, every action is within bounds, and the alignment signal remains flat. The only layer that can interrupt is containment: mediated tool calls that block categories of action regardless of the instruction that triggered them, and human confirmation gates that fire on irreversible operations before they complete. An agent that cannot exfiltrate data without going through a logged, rate-limited outbound adapter is less useful to an injected instruction even if the instruction successfully redirected the agent's behavior.
Consider a less structured case than the CFO workflow: an agent asked to research vendors, summarize options, and begin outreach where appropriate. That task crosses external content, third-party tools, evolving selection criteria, and ambiguous action boundaries. A Mission shaped for that task can still provide a governance record and some outer bounds. But the real safety properties are more likely to come from mediated outreach, short-lived credentials, sandboxed browsing, and explicit release gates before any external communication is sent. This is the kind of open-world case where containment is not a complement to Mission shaping so much as the more reliable layer.
# Staged Mission Shaping
Isolating the shaping step is the right principle, but it cannot be applied as a single upfront step for most real enterprise tasks.
```text
+------------------+
| Human intent |
+------------------+
|
v
+------------------+
| Mission Proposal |
+------------------+
|
v
+------------------+
| Approved Mission |
+------------------+
|
v
+------------------+
| Mission state |
| owner |
+------------------+
|
+--> Stage 1: discovery
| |
| +--> enforcement
| |
| +--> discovered facts
| |
+<------------+
|
+--> Stage 2: execution
|
+--> enforcement
|
+--> irreversible action
|
v
+------------------+
| Callback / |
| re-approval |
+------------------+
|
v
+------------------+
| Mission state |
| owner |
+------------------+
```
Many tasks are not fully knowable at the moment of initial approval. An enterprise agent may need to inspect draft spreadsheets, read finance-system metadata, compare version history, or discover which supporting systems are implicated before it can propose the next safe action.
If the entire Mission Authority Model must be shaped before any external content is seen, the result will often be too narrow to be useful. If the agent is allowed to inspect arbitrary external content before shaping, the isolation guarantee collapses.
That tension is why structured enterprise workflows need a staged model rather than a single compile-once event. In the structured case, staged Mission shaping can include staged compilation.
A staged model looks like this:
1. **Shape a narrow discovery envelope.** Enough authority to inspect the immediate problem space: read access to relevant systems, no write operations, no external communications.
2. **Shape a fuller execution envelope from discovered facts.** Still bounded by the original approved purpose, but now informed by what the agent found in Stage 1.
3. **Require explicit expansion, callback approval, or re-confirmation for anything outside the execution envelope.** Novel actions, high-risk operations, and scope expansions all require returning to the Mission state owner.
Each stage transition produces a new versioned authority artifact. The Mission state owner, the authoritative lifecycle service for Mission state, is the only party that can issue a new version. In some deployments that is an AS-resident Mission service. In others it is a separate authority service. The architectural requirement is not placement. It is that one system remains authoritative for Mission versions, stage transitions, and lifecycle state. That centralization makes the Mission state owner both the governance anchor and a single point of failure.
The Mission state owner is the authoritative lifecycle system for Mission state: the component that owns Mission versions, stage transitions, runtime budget state, and suspension or termination decisions.
If it is unavailable during an active Mission, stage transitions stall, [trust budget](/notes/mission-shaping-is-not-enough/#runtime-alignment-trust-scores-and-budgets) exhaustion cannot be acted on, and governed resumption cannot proceed. Availability and resilience requirements for this component are not an afterthought.
The staged model also has to handle re-planning when discovery changes the task materially, downgrade paths when later facts narrow rather than expand the task, and attenuation as well as expansion.
This clarifies where Mission shaping is strongest: structured workflows with clear stages, observable transitions, and legible approval points. In those domains, shaping can take the form of staged compilation. It is conceptual framing, not a full shaping protocol, but it is enough to see where the model holds and where it starts to fray.
Recent work on [semantic task-to-scope matching in delegated authorization](https://arxiv.org/abs/2510.26702) can be read as one concrete instance of this structured case: an authorization server semantically constrains scopes to the task at hand rather than trusting the agent's requested scope set. That is Mission shaping in a narrower, more enumerable form. A complementary approach enforces at the tool-invocation boundary rather than at token issuance: [IBAC](https://ibac.dev/ibac-paper.pdf) and [CaMeL](https://arxiv.org/abs/2503.18813) parse intent into structured permission tuples before execution begins and check every tool call against them deterministically. Token issuance enforcement constrains what authority is granted before the agent runs; tool-invocation enforcement constrains what the agent can do with that authority at every step. Both are instances of the structured case. Neither addresses what happens when authority is shaped correctly but the agent operates across multiple organizations, long-running tasks, or delegation hops where the original approval is long behind the execution.
In the most repeatable cases within that structured domain, shaping can go one step further and emit a governed task artifact: a reviewed script, workflow, or executable routine tied to a specific Mission template and task class. That does not mean the artifact carries standing elevated privilege. It means future Missions can invoke a versioned, signed, pre-approved behavior instead of re-deriving the same logic from natural language every time. The authority still comes from the current Mission context. Reusing the artifact is safe only if its inputs, side effects, and invocation conditions remain narrowly bounded.
Seeing where staged Mission shaping works makes the gap with what most deployments actually do more visible.
# What Most Deployments Do Instead
Relatively few current deployments perform Mission shaping as a first-class step. In practice, many systems fall into one of five patterns, including some that look like compilation but are not.
| Pattern | What it does | Why it falls short |
|---|---|---|
| Scope enumeration | Pre-enumerates allowed scopes or APIs at authorization time | Works only for closed-world tasks where the full execution path is known in advance |
| App-local policy mapping | Maps recognized intents to internal permission bundles | Not portable, usually opaque to users, and breaks at organizational boundaries |
| LLM-generated structured request | Uses an LLM to draft authority inputs | Useful as a proposal step, but probabilistic, non-deterministic, and vulnerable to tainted inputs |
| Broad credential plus prompt discipline | Gives the agent wide authority and relies on the prompt to constrain it | Self-governance, not governance; the agent's own judgment becomes the enforcement boundary |
| Policy languages (XACML, OPA, Cedar) | Express access control policy in a formal language | Designed for enumerable resources and actions; adapting them to open-world intent requires exactly the semantic work that makes Mission shaping hard |
The most common real-world deployments land in the third or fourth pattern. The third pattern has a more disciplined form worth noting separately: systems like [IBAC](https://ibac.dev/ibac-paper.pdf) and [ASTRA](https://arxiv.org/abs/2510.26702) use an LLM to extract intent but enforce the result deterministically, either at the tool boundary or at token issuance. They reduce the taint vulnerability.
They do not address whether the extracted intent correctly represents what the user approved โ that is the shaping problem this article is about. They are structured enforcement architectures that still depend on an upstream intent-extraction step, not substitutes for solving the shaping problem itself.
The majority let the model infer its own boundaries or give the runtime broad authority and rely on local discipline not to misuse it.
When the system never explicitly converts approved intent into a bounded authority artifact, the agent's own judgment becomes the effective policy engine. That is not a governance model. It is the absence of one.
In effect, the agent becomes the place where requested purpose, system design, business role, and organizational policy are reconciled. That reconciliation should be a governance function, not a runtime improvisation.
None of these patterns include containment as an intentional layer either. Containment is usually present as an accident of deployment (the agent happens to be running in a restricted environment) rather than as a first-class design decision.
The concrete example that follows shows the staged model working. The failure modes that arise even within it are the subject of [Part 2](/notes/mission-shaping-is-not-enough/). In deployments running these patterns, they arrive faster and without a governance boundary to limit the damage.
# A Concrete Example
The following example assumes a single organization, known systems, and a structured approval path: the conditions under which staged Mission shaping is most tractable. The interoperability complications that arise at organizational boundaries do not apply here.
The CFO says:
> Assemble the board packet, reconcile the final numbers, and call me back before releasing anything externally.
That is not yet authority. It is intent. It contains at least four different governance questions:
- what systems the agent may inspect
- what changes, if any, it may make while reconciling numbers
- what must wait for callback approval
- what counts as "releasing anything externally"
A plausible Stage 1 Mission Authority Model might look like:
```json
{
"mission_version": 1,
"allowed_resources": [
{ "type": "finance.report", "selector": "period == 2026-Q1" },
{ "type": "finance.forecast_version", "selector": "period == 2026-Q1" },
{ "type": "board.material", "selector": "classification in ['draft', 'internal']" }
],
"allowed_operations": [
"finance.read",
"finance.compare",
"board.read_internal"
],
"constraints": [
{ "type": "callback_required_before_release", "value": true },
{ "type": "mission_stage", "value": "discovery" }
],
"forbidden_operations": [
"board.publish_external",
"notification.send_external",
"finance.approve_final"
]
}
```
If discovery finds only expected variance, the Mission state owner issues version 2. The delta is deliberate. `mission_stage` advances from `discovery` to `execution`, `board.packet_draft` is added as an allowed resource, and write operations (`board.write_internal_draft`, `board.assemble_packet`) are unlocked. The version number increments so enforcement points holding a version 1 token reject it as stale. The CFO's callback requirement remains in force. `board.publish_external` stays forbidden until the CFO confirms. When the callback succeeds, the Mission state owner issues version 3 with external publication unlocked. That is an approval event, not a local agent decision.
This is the domain where Mission shaping is tractable: one organizational trust boundary, known systems, a structured approval path, and a control plane that can tolerate transition friction. Even here, the hidden complexity is real. Selector correctness, stage propagation to distributed enforcement points, token freshness, and sequencing for partially-completed write operations all have to be solved.
One design decision every implementation will face is whether the token carries the Mission Authority Model inline or carries only a `mission_ref` that enforcement points resolve at call time. Inline tokens enable offline enforcement and reduce latency, but they become stale immediately when the Mission state owner issues a new version. Reference-based tokens stay current but create an availability dependency on the Mission state owner at every enforcement point. Neither choice is obviously correct. The right answer depends on revocation latency requirements and the acceptable blast radius if the Mission state owner is temporarily unavailable. Enforcement points resolving a reference token are performing a form of token introspection ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)) against the Mission state owner. The Mission Proposal can also be submitted to the authorization server with integrity guarantees using Pushed Authorization Requests ([RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)) before the interactive approval flow begins.
That tractability does not generalize to open-world environments.
Part 1 stops here because this is the strongest case for Mission shaping: one enterprise, known systems, legible stages, and a control plane that can absorb governance friction.
The harder question starts after that. A well-shaped Mission can still fail under quiet scope expansion, delegation, headless execution, stale state, and open-world runtime redirection. That is where containment, runtime alignment, and survivable incorrectness become the more important story.
[Part 2: Mission Shaping Is Not Enough](/notes/mission-shaping-is-not-enough/) picks up from there.
---
# Why Mission-Bound OAuth Might Be the Wrong Answer
Canonical URL: https://notes.karlmcguinness.com/notes/why-mission-bound-oauth-might-be-the-wrong-answer/
Markdown URL: https://notes.karlmcguinness.com/notes/why-mission-bound-oauth-might-be-the-wrong-answer.md
> **Superseded.** The architecture this post critiques evolved into the
> [Mission-Bound Authorization draft family](https://github.com/mcguinness/mission-bound-authorization),
> which answers the questions raised here. This post remains published
> as history. A definitive series on the current model is in
> preparation.
The previous three posts in this series made the strongest case for Mission-Bound OAuth:
- [Part 1](/notes/mission-bound-oauth/) argued that a durable Mission object is better than token-only continuity for delegated agent authority
- [Part 2](/notes/client-context-and-id-jag-for-mission-bound-oauth/) showed how the authentication layer bootstraps a Mission cleanly and how ID-JAG projects it across same-IdP trust domains
- [Part 3](/notes/mission-architecture-on-aauth/) argued that even AAuth, a cleaner-sheet agent protocol, still appears to need a Mission-like governance layer
That is the positive case.
This post is the negative one.
If I were trying to tear the idea down rather than defend it, I would say this:
> Mission-Bound OAuth may be a clever way to improve OAuth-heavy enterprise IAM, but it may also be the wrong home for the problem, the wrong abstraction boundary for agent governance, and the wrong long-term substrate for open-world systems.
That critique is worth stating directly, because the architecture is only useful if it survives serious skepticism.
The pessimistic argument has five main parts:
1. Mission-Bound OAuth asks the Authorization Server to do too much.
2. It mixes governance state with protocol transport concerns.
3. It depends on an authority-compilation step that is much harder than the OAuth parts.
4. It may still underfit real agent execution, which is messier than the architecture assumes.
5. It may be too broad to standardize well at the semantic layer that actually matters.
If those objections hold, then Mission-Bound OAuth is not the right answer to the full problem. At best, it is a transitional answer for organizations that already live inside OAuth.
# The Authorization Server Becomes Too Important
The architecture looks disciplined because everything routes through the Authorization Server (AS):
- Mission creation
- approval and attenuation
- lifecycle state
- delegation depth
- verified actor binding
- token issuance and exchange
- business-event termination
- introspection
- distributed authority-model retrieval
That is elegant on paper. It is also a warning sign.
The AS stops being only a credential issuer and becomes all of the following at once:
- a durable authority ledger
- a lifecycle state machine
- a delegation graph authority
- an approval evidence store
- a policy compilation checkpoint
- an event-driven execution governor
That concentration creates three distinct problems.
## It creates operational bottlenecks
Every serious control point now depends on AS correctness and AS availability. If the AS or the systems it depends on are slow, mission execution slows. If they fail closed, execution stalls. If they fail open, the governance story collapses.
This is not incidental. It is a direct consequence of choosing OAuth as the home for Mission governance. A typical enterprise OAuth deployment is already a latency-sensitive component in every user-facing flow. Mission-Bound OAuth adds Mission state lookups, lifecycle checks, compiled authority evaluation, and introspection calls on top of that. Each additional dependency is another failure mode.
The staged adoption path in Part 1 acknowledges that the AS may become a bottleneck and calls for a resilience model. But "we need a resilience model" deferred is not the same as "we have a resilience model." For a component that is now the authority source for whether delegated software should continue operating at all, the resilience requirements are much higher than for a standard token service.
## It creates organizational bottlenecks
In most enterprises, the team that runs the IdP or Authorization Server is not the team that owns workflow orchestration, business events, agent runtime safety, or domain-specific approval systems.
Mission-Bound OAuth assumes those worlds can be joined behind one control plane: the IAM team now also owns Mission lifecycle, approval policies, authority compilation, and business-event integration. Sometimes that is realistic. Often it is not. The IAM team has no operational visibility into the business processes that should drive Mission termination, and the workflow team has no way to extend AS policy to reflect domain-specific authority rules.
The result is either a very thin AS-side Mission that only stores lifecycle state and delegates all real governance to downstream systems, or an AS that accumulates domain knowledge it is not structurally suited to own. Neither is the architecture the document describes.
## It creates disproportionate blast radius
If the Mission store or the AS is compromised, the attacker does not just gain token issuance influence. They gain the authority layer that decides whether delegated software should still be operating at all.
That is a qualitatively different prize than a normal token server. A compromised standard AS lets an attacker issue new tokens with elevated scope. A compromised Mission-bearing AS lets an attacker also rewrite the governance record that downstream systems use to determine whether existing execution should continue. They can activate suspended Missions, prevent revocation from propagating, and silently expand authority envelopes. The delta between those two threat surfaces is not marginal.
Mission-Bound OAuth's design compounds this by making the AS the authoritative source for the compiled Mission Authority Model. If that model is stored at the AS and the AS is compromised, an attacker does not just steal tokens. They steal the compiled representation of what each agent is permitted to do, and they can modify it without triggering token rotation.
# Governance May Not Belong Inside OAuth
The deepest critique is not that the architecture is too complicated. It is that it may be putting the durable authority object in the wrong place.
OAuth is fundamentally good at:
- issuing tokens
- narrowing tokens to audiences
- deriving tokens across exchanges
- binding tokens to clients and keys
Mission governance is about something else:
- approval
- durable authority state
- lifecycle transitions
- business-event termination
- delegation policy
- audit evidence
- cross-system authority continuity
Those are governance concerns first and OAuth concerns second.
Mission-Bound OAuth tries to connect those two layers by making the AS authoritative for the Mission itself. That is coherent, but it is not obviously the best separation of concerns. It may be cleaner to let OAuth consume Mission state rather than own it.
There is a simple test for whether an object belongs in a protocol or belongs in a layer that the protocol consumes:
> if the architecture still makes sense when the protocol is removed, then the protocol may not be the right home for the primary object.
The Mission passes this test. A Mission-like durable authority object makes sense whether the downstream execution layer is OAuth, AAuth, a job scheduler, a workflow engine, or a tool broker. The governance concern exists independently of the transport. That is a strong signal that Mission governance belongs in a separate authority layer and OAuth is one consumer of that layer, not its home.
GNAP (Grant Negotiation and Authorization Protocol) is at least suggestive here. One of the recurring adoption questions around GNAP has been whether richer protocol capability and richer governance capability are being introduced together in a way that raises the adoption bar. Mission-Bound OAuth may be taking on a similar risk by coupling the transport improvement and the governance object too tightly.
Mission-Bound OAuth makes a similar choice. It is a real improvement in what the AS governs. It may not be the right place to put the object that drives that governance.
# The Hard Part Is Not OAuth. It Is Compilation
The architecture is at its strongest when it says a Mission is the compilation boundary between ambiguous intent and enforceable authority. That is also where it is weakest.
Part 1 describes the compilation step as follows:
```text
intent
->
Mission Proposal
->
Approved Mission
->
Mission Authority Model
```
That stack is the right conceptual shape. The hard problem is the first arrow: turning open-ended human intent into a bounded, machine-evaluable Mission Proposal without over-delegating or causing constant re-approval failures.
The compilation problem has several hard sub-problems that Mission-Bound OAuth does not directly address.
**Semantic ambiguity.** "Schedule a meeting with the engineering team" could mean: read all engineering team calendars, write a calendar event, send email invitations, record the meeting in a CRM, look up attendees' availability across a directory, and propose a room booking. Or it could mean just the calendar write and the email send. The same natural-language intent compiles to very different authority envelopes depending on who is doing the compilation and what they assume. The Mission Authority Model format defines how to express those envelopes. It does not resolve the ambiguity in choosing which envelope is correct for a given intent.
**Verification.** Even if you compile a Mission Proposal from a user's stated intent, how do you verify that the compiled result actually matches what the user intended? The approval step is the intended answer: the user sees the compiled authority and approves it. But the gap between what a non-expert user reads in an approval screen and what the machine-evaluable authority model actually encodes can be large. Approval UX that accurately represents a complex compiled authority model is an unsolved design problem, not a layout problem.
**Trust in the compilation agent.** If an LLM interprets the user's prompt and proposes the Mission, the compiled authority model is a probabilistic artifact. The same prompt submitted twice may produce different proposals. A prompt injected with adversarial content in a retrieved document may produce a silently expanded proposal. The Mission Authority Model format gives you a way to inspect the result. It does not give you a way to attest that the LLM's interpretation was faithful to the user's intent and free of injection. That attestation problem is pre-OAuth and does not become easier because the result ends up in a well-structured authority model.
**Interoperability collapse at the hardest layer.** Part 1 already acknowledges that compilation may become proprietary, and that if every implementation compiles authority models differently, interoperability may exist only at the token-envelope layer. That is the realistic outcome. The purpose taxonomy, the operation groups, the resource selectors, the relationship predicates: all of these require shared semantics to be interoperable. Shared semantics require coordination. That coordination is harder than defining the schema that carries the semantics.
The practical result is that Mission-Bound OAuth may be disproportionately elaborate around the easier half of the problem, the OAuth protocol mechanics, while still being under-specified around the hardest half: the semantic agreement that makes compiled authority models meaningful across system boundaries.
# The Architecture May Still Underfit Real Agent Execution
Mission-Bound OAuth is built around checkpoints:
- approval
- issuance
- exchange
- introspection
- lifecycle transitions
Real agent systems do not only act at those checkpoints. They:
- pause and resume unpredictably, often across different runtime processes
- switch runtimes when a sub-agent is spun up or a tool call delegates further
- re-plan after partial failure, discovering that the initial plan did not account for what they found
- discover new tools at runtime through MCP or equivalent capability discovery
- accumulate new facts that change what the next step should be
- branch into multiple parallel tasks that may have different authority requirements
- call systems that are not inside the OAuth fabric at all
The MCP tool discovery case is a specific and important example. An agent may be approved for a Mission covering calendar, email, and CRM. During execution, it discovers through MCP that a scheduling assistant tool is available that also touches document storage. The agent never planned to touch document storage. The Mission never anticipated it. But the agent's natural execution path leads there.
Mission-Bound OAuth's answer is `mission_authority_exceeded`: the agent gets a structured error indicating that document storage is outside the compiled authority envelope, and it must either stop that path or trigger a Mission expansion proposal. That is the right governance answer. It is also a significant friction point in agent execution that may, in practice, cause agents to request broader initial authority envelopes, defeating the bounded authority goal.
The re-planning case is equally challenging. An agent plan that looked like three steps may turn into seven steps because the first three uncovered new information. The Mission approved the three-step view. The seven-step execution may be well within the spirit of the original approval but exceed the compiled authority in specific ways. The architecture's answer is expansion proposals. But expansion proposals require human review cycles, and an agent that hits expansion boundaries mid-execution must either stall, abort, or work around the limitation.
The harder challenge is the model-tool boundary. Mission-Bound OAuth governs which API calls an agent is permitted to make. It does not govern what the model does with the response. An agent can read a file it is authorized to read and then include the contents in a message sent to a recipient outside the Mission's authority envelope. The OAuth layer never sees that second action as a distinct authorization event. Mission governance is strong at the resource-access boundary and blind to what happens inside the model's reasoning process.
This creates the illusion of governance:
> the system feels governed because the token path is governed, while the runtime still has many ways to drift, cache, fork, or route around the intended control points.
The architecture acknowledges some of this under practical limits, but the problem is structural, not just a matter of deployment discipline.
# The Model May Be Too Broad To Standardize Well
Mission-Bound OAuth needs a surprisingly large meta-model:
- purpose classes
- templates
- authority models
- resource selectors
- relationship predicates
- operation groups
- lifecycle states
- approval evidence
- delegation chains
- projection policies
- event semantics
Each piece is individually reasonable. Together they are a substantial standardization surface.
XACML is the historical cautionary example. XACML was technically correct. It modeled the right things: attributes, policies, rules, combining algorithms, obligations. It was too complex for most enterprises to author and maintain in practice. Adoption was thin outside specific regulated verticals. The fundamental problem was not that XACML was wrong but that the surface it standardized was too large to be adopted incrementally.
Mission-Bound OAuth faces a version of the same problem. The likely failure mode is:
- the protocol pieces get standardized at IETF: `mission_ref` as a token claim, token exchange extensions, lifecycle error codes, introspection fields
- the semantic pieces remain deployment-specific: purpose taxonomy, compiled authority format, relationship predicates, business-event termination conditions
- every implementation uses different authority compilation logic
- interoperability exists at the token-envelope layer, not at the authority layer the architecture actually cares about
If that happens, Mission-Bound OAuth succeeds procedurally and fails substantively. Operators can exchange `mission_ref`-bearing tokens between any two implementations. But two implementations in the same enterprise still cannot share a Mission, because their compiled authority models are incompatible. The most important interoperability, the kind that makes a Mission's authority meaningful across system boundaries, never arrives.
The staged adoption path helps with this by starting with AS-side lifecycle and deferring semantic richness. But the semantic richness is the point. A `mission_ref` that only carries lifecycle state is useful. It is not the Mission Authority Model the document describes.
# A Stronger Critique
There is an even harsher version of the argument.
Open-world agents are not primarily a delegated authorization problem. They are a runtime control problem. The question is not only "is the agent authorized to take this action?" The question is "is the agent behaving consistently with what was intended, and how would we know if it stopped?"
Mission-Bound OAuth improves the first question. It does not address the second.
Authorization systems govern boundaries. They answer yes or no at defined checkpoints. Runtime control governs behavior between those checkpoints: whether the agent is reasoning correctly, whether it has been manipulated by content in its context window, whether it is producing outputs consistent with the user's intent, and whether human oversight is being exercised at the right frequency.
Prompt injection is a concrete example. An agent executing a Mission can be redirected by adversarial content in a tool response. A malicious calendar entry might contain instructions that cause the agent to take actions outside its intended path. The agent's token requests remain within the Mission's authority envelope, because the agent is still calling the APIs it is authorized to call. The governance layer never sees the manipulation. The audit trail shows a sequence of authorized actions that, taken together, represent behavior nobody intended to authorize.
Mission-Bound OAuth's threat model includes actor substitution, confused deputy, and scope creep. It does not have a good answer for adversarial content injection because that threat crosses the model-tool boundary in a way that authorization infrastructure is not designed to observe.
The stronger critique is not that Mission-Bound OAuth is wrong. It is that it may be misframed. If the real problem is runtime control of autonomous systems, then improving OAuth-level credential governance is necessary but far from sufficient. The right starting point is not:
> how do we make OAuth carry longer-lived governed authority?
It is:
> what authority service governs agent execution, and how do existing protocols consume it?
Those are different architecture questions. The second one does not have OAuth at its center.
# A Better Answer: Make Mission a Separate Authority Service
The most plausible better answer is not "throw away Mission." It is "move Mission out of OAuth."
In that sense this post is also a return to the original Power of Attorney intuition. The earlier series treated the state owner more like a mandate or authority service that sat above IAM, policy, and orchestration rather than inside one protocol component. Mission-Bound OAuth narrowed that thesis into an AS-centered protocol architecture. The MAS alternative reopens the broader control-plane version of the idea.
The cleaner architecture looks like this:
- a dedicated **Mission Authority Service** (MAS) owns Mission state
- the MAS handles approval, lifecycle, delegation policy, compiled authority, approval evidence, and business-event termination
- OAuth Authorization Servers call the MAS at issuance and exchange time to check Mission state and authority bounds
- AAuth authorization servers do the same, because the MAS does not care what protocol is presenting the request
- gateways, schedulers, orchestrators, and tool brokers can query the MAS directly for Mission state, without needing an OAuth token in the path
- OAuth becomes one projection and enforcement mechanism, not the canonical home of the authority object
That produces a cleaner split of responsibilities:
| Layer | Responsibility |
|---|---|
| Mission Authority Service | approval, durable authority state, lifecycle, delegation policy, compiled authority model, evidence, event-driven termination |
| OAuth AS | issue and exchange tokens consistent with Mission state from MAS |
| AAuth AS | issue AAuth auth tokens consistent with Mission state from MAS |
| Runtime / Orchestrator | execute tasks, request new authority from MAS, handle pauses and retries |
| Gateways / PDPs / RSes | enforce resource-specific decisions using Mission-derived context from MAS |
This is not a radical departure from the Mission-Bound OAuth architecture. The Mission object, the authority model, the lifecycle states, the approval flow, and the audit trail are all the same. The main change is where they live and which component is authoritative for them. That shift does not make the hard problems disappear. A MAS still has to solve authority compilation, event integrity, actor continuity, lifecycle coupling, and operational resilience. The claim is narrower: if those hard problems must exist somewhere, they may belong in a dedicated governance service rather than inside the OAuth AS.
**Cleaner separation of concerns.** Mission governance no longer has to be smuggled into OAuth as if it were a token problem. The MAS can be owned by the team that understands workflow and business events. The AS remains the credential issuer it was designed to be. The organizational ownership story becomes much more natural.
**Broader applicability.** Not every enforcement path in agent systems is an OAuth path. Job schedulers, internal queues, workflow engines, tool brokers, and MCP servers may need Mission state even when no OAuth access token is in the path. If Mission state lives in the AS, those systems either become second-class consumers or start depending on IAM endpoints that were not built for them. If Mission state lives in a dedicated MAS, they can consume the same authority record through a purpose-built API.
**Protocol portability.** If OAuth remains the dominant enterprise deployment substrate, the MAS becomes the AS's upstream authority source. If AAuth or something else becomes better suited later, the MAS survives and the transport layer changes underneath it. Part 3 ended with "Mission governance on AAuth" as the cleaner long-term architecture. A Mission Authority Service is what makes that transition possible without rebuilding the governance layer each time.
The practical integration pattern at Stage 1 would look like this: the AS calls `GET /missions/{mission_ref}` on the MAS at exchange time, the MAS returns current lifecycle state and authority bounds, the AS enforces them in the issued token.
That introduces real costs:
- a new network dependency on the MAS for issuance and exchange decisions
- new consistency requirements between AS caches and MAS state
- a new governance service that now needs the same operational hardening the AS would otherwise need
But those costs buy a cleaner separation of concerns. The critique is not that MAS is easy. It is that its difficulty is more honest: it puts the governance complexity in the layer that actually owns governance rather than embedding it inside the credential issuer.
That is the deeper attraction of MAS. It is not only "Mission state moved out of OAuth." It is governance restored as a layer above IAM, policy, and orchestration, with each of those systems consuming Mission state rather than one of them trying to become the whole control plane.
# An Even More Conservative Answer
There is also a safer answer than either Mission-Bound OAuth or a Mission Authority Service, and it deserves honest consideration.
It is less elegant and less autonomous:
- do not let agents hold much durable delegated authority at all
- keep approvals narrow and frequent
- issue per-step credentials
- require renewed justification often
- treat long-running missions as orchestrated job control, not as persistent delegated continuity
This is the principle of least delegation taken seriously: the right amount of authority is the minimum needed for the next specific action, not the maximum that might be needed across the whole intent.
That model is worse for user experience and agent autonomy. An agent that needs approval for each step is not usefully autonomous. It is also worse for agents that operate in low-latency workflows where human approval introduces unacceptable delay. And it is worse for headless or service-to-service workflows where a user is not in the loop at all.
But it may be better for safety in several important cases.
In regulated environments, the compliance risk from a single governance failure may outweigh the UX cost of frequent re-approval. Finance, healthcare, and government deployments where a single unauthorized action triggers audit findings or regulatory penalties may reasonably decide that per-step credentials are worth the friction. The architecture should say so explicitly rather than treating narrow, frequent approval as a degraded mode.
In high-stakes action domains, irreversible actions, large financial transactions, external communications, code deployments, database modifications, the governance value of a brief approval moment before each action may be much higher than the value of seamless autonomous execution. Governance tradeoffs are not always won by the smoother user experience.
For organizations that are still developing their understanding of how their agents actually behave in production, narrow credentials provide a natural forcing function. You cannot have a runaway agent that accumulates unauthorized capabilities if each capability required its own issuance event. That property is valuable during the early deployment period even if it becomes unnecessary later.
The honest conclusion is that Mission-Bound OAuth and the Mission Authority Service architecture described above are both designed around the goal of making autonomous agents work well under governance. The conservative answer is designed around the goal of not having that problem in the first place. Depending on the deployment context, that may be the right engineering decision.
# So Is Mission-Bound OAuth a Bad Idea?
Not necessarily. The honest conclusion is narrower and less flattering than the architecture post itself.
Mission-Bound OAuth may be the wrong answer if the goal is a clean-sheet architecture for agent governance. It may still be a good answer if the goal is to improve governance in OAuth-heavy enterprise infrastructure without replacing the whole stack.
That is a meaningful distinction and it matters for how to read the series.
Mission-Bound OAuth is strongest as:
- a pragmatic retrofit for OAuth-centric environments where replacing the AS is not realistic
- an incremental deployment model where Stage 1 lifecycle and delegation enforcement delivers real value before the full architecture is in place
- an IAM-native improvement to token exchange, lifecycle, and delegated authority continuity that fits existing Okta, Auth0, or Azure AD deployments with extension
It is weakest as:
- a universal architecture for open-world agents operating across heterogeneous systems
- the natural long-term home of the Mission concept, since Mission governance makes sense independent of OAuth
- the only layer at which runtime governance should live, since authorization is necessary but not sufficient for behavioral control
The staged adoption path matters here. A deployment that starts with Stage 1, AS-side Mission lifecycle with a projected `mission_ref` claim, gets the core lifecycle and revocation property with minimal architectural disruption. That is a real improvement over token-only OAuth even if stages 2 and 3 never arrive. But it is also the strongest evidence that the full architecture may be doing too much. If the most adoptable and defensible part of the design is the smallest AS-side lifecycle layer, that may be a sign that the rest belongs elsewhere.
The answer to that question depends on the deployment. For teams running agent workflows today with no Mission-level lifecycle control, Stage 1 is a clear improvement. For teams evaluating whether to build a full governance infrastructure from scratch, the Mission Authority Service alternative may be worth starting with.
# Conclusion
The series has made one consistent claim across all four posts: agent authority needs a durable, governed object, not just a chain of well-formed tokens.
That claim still holds. The critique in this post is not that the claim is wrong. It is that Mission-Bound OAuth may be a reasonable but suboptimal way to realize it.
The most defensible framing for where this lands is:
> Mission is the right object. Mission-Bound OAuth may not be the right home.
That leaves three things true at once:
- the Mission model is directionally right
- extending OAuth may be the most practical near-term path for OAuth-heavy deployments
- a separate Mission Authority Service, and eventually Mission governance on AAuth or a successor, may be the better end state
The series ends here. The argument is not that Mission-Bound OAuth solves agent authorization. The argument is that it identifies the right object and may still put it in the wrong place.
The practical test is simpler: is a deployment with Mission-level lifecycle, authority bounds, and delegation control meaningfully better than a deployment without it?
For the problems agent systems actually face today, the answer is still often yes. That does not make Mission-Bound OAuth the best answer. It makes it a plausible transitional one. If a deployment can start from a cleaner Mission Authority Service architecture instead, that may be the better bet.
---
# Mission Architecture on AAuth
Canonical URL: https://notes.karlmcguinness.com/notes/mission-architecture-on-aauth/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-architecture-on-aauth.md
> **Superseded.** This mapping evolved into the
> [Mission-Bound Authorization draft family](https://github.com/mcguinness/mission-bound-authorization),
> which now carries a dedicated AAuth binding. This post remains
> published as history, and its member names are out of date. A
> definitive series on the current model is in preparation.
This is a follow-up to [Mission-Bound OAuth](/notes/mission-bound-oauth/).
That RFC made one main claim:
> agent systems need a durable, governed authority object that survives across token lifetimes, approval delays, delegation hops, and business-event termination.
The question here is narrower:
If Mission-Bound OAuth is the incremental path for existing OAuth deployments, is [AAuth](https://github.com/dickhardt/draft-hardt-aauth) the clean-sheet path for the same underlying architecture?
AAuth (draft-hardt-aauth) is Dick Hardt's proposed agent-native authorization protocol: conversational, signed-request-first, and designed to depart from OAuth's redirect model rather than extend it. Both Mission-Bound OAuth and AAuth are early-stage proposals, so this comparison is necessarily tentative. Either may change materially before any standardization.
My current answer is: probably yes, but only if AAuth defines a Mission-like governance object rather than treating purpose and chaining alone as sufficient.
If you are skimming, the core claim of this note is simple:
- AAuth looks like a better protocol substrate than OAuth for agent authorization.
- It still does not obviously remove the need for a durable Mission governance object.
- The most plausible long-term comparison is Mission governance on OAuth versus Mission governance on AAuth.
At a glance:
| Question | Mission-Bound OAuth | AAuth | Remaining Mission profile work |
|---|---|---|---|
| Approval and clarification | Added through companion APIs and profiles | Native deferred and conversational flow | Approval evidence retention and tamper-evident audit artifact |
| Agent identity and proof-of-possession | Added through DPoP, mTLS, `act`, and exchange rules | More native to the protocol | Headless and service-to-service agent identity model |
| Chained downstream authorization | Token exchange plus projected Mission context | Native chaining via `upstream_token` | Authority ceiling and bounded expansion rules |
| Authentication-layer bootstrap | Client Context companion profile | Largely native to the protocol's signed and deferred interaction model | Cross-domain reduced projection |
| Durable lifecycle-governed authority object | Explicitly modeled | Not clearly first-class yet | Mission record, lifecycle states, and independent revocation |
| Bounded authority envelope for open-world agents | Explicitly modeled | Not clearly first-class yet | Machine-evaluable Mission Authority Model |
| Verified actor continuity across derivation | Now defined explicitly in Mission-Bound OAuth | Not clearly first-class yet | Binding anchor, key continuity across `upstream_token` hops, projection degradation model |
# What AAuth Already Gives You
One reason AAuth is interesting is that it already has several properties Mission-Bound OAuth has to build indirectly.
From the current draft, AAuth already gives you:
- signed requests using HTTP Message Signatures
- explicit agent identity alongside user identity
- deferred authorization with `202 Accepted`
- pending-state polling
- user clarification during authorization
- direct approval flows
- chained downstream authorization via `upstream_token`
- a first-class `purpose` field
That matters because these are exactly the areas where classic OAuth looks most strained in agent deployments.
Relevant sections in the current AAuth draft (links reference a specific draft iteration and may shift as the spec evolves):
- [Authorization request flow](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-11.2)
- [Deferred responses and pending state](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-10)
- [User interaction](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-11.5)
- [Direct approval](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-4.5.6)
- [Call chaining](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-4.5.8)
- [Purpose](https://dickhardt.github.io/draft-hardt-aauth/draft-hardt-aauth.html#section-14)
# Why This Comparison Matters
Mission-Bound OAuth was never mainly about adding another token claim.
Its real claim was that delegated machine authority needs to be represented as a governed object:
- proposed
- approved or attenuated
- stored durably
- enforced across derivation steps
- revocable independently of token lifetime
- terminable when the business reason ends
AAuth changes the protocol substrate substantially. It is more conversational, more agent-native, and stronger by default on proof-of-possession and chaining.
So the real question is:
Does AAuth already solve the governance problem by virtue of having better protocol structure, or does it still need an explicit Mission-like authority layer?
# The High-Level Mapping
This is the rough mapping I would use today.
| Mission Architecture | AAuth Equivalent | Notes |
|---|---|---|
| Mission Proposal | Signed authorization request to the AAuth authorization server | AAuth already has a stronger request object than OAuth auth requests |
| Approval / attenuation | Deferred approval, pending URLs, clarification, direct approval | AAuth already has the right control-flow shape |
| Mission subject binding | User interaction plus delegate-user binding | Cleaner than stitching this through OAuth redirects and token exchange |
| Mission-bearing runtime artifact | AAuth auth token and chained request context | Better native fit than layering RFC 8693 plus custom rules |
| Sender constraining | HTTP Message Signatures plus key binding | Stronger default than bearer-heavy OAuth deployments |
| Clarification during approval | Native deferred clarification flow | Much more natural for agent systems |
| Mission Authority Model | No exact built-in equivalent yet | This still looks like the main gap |
| Lifecycle-governed authority object | Not clearly first-class in the current draft | This is where Mission semantics still appear useful |
# Where AAuth Looks Better
There are at least five places where AAuth looks cleaner than Mission-Bound OAuth.
## 1. Approval and clarification are native
Mission-Bound OAuth has to introduce proposal endpoints, approval endpoints, pending states, and optional clarification flows around an OAuth core that was not designed for conversational authorization.
AAuth already has the right shape:
- signed request
- `202 Accepted`
- pending URL
- follow-up interaction
- direct approval path
That is a more natural fit for agent authorization than stretching classic OAuth redirects into a multi-step approval conversation.
## 2. Agent identity is part of the protocol
Mission-Bound OAuth has to work hard to distinguish:
- subject
- client
- active actor
- delegation chain
because OAuth starts from the client model, not the agent model.
AAuth is much closer to the reality that software agents are actors in the protocol, not just hidden implementation details behind a client.
One deployment caveat: AAuth identifies agents via HTTPS URLs pointing to published metadata at `/.well-known/aauth-agent.json`. That works well for internet-scale agents with public endpoints. It is a meaningful assumption for enterprise environments where internal microservices, batch jobs, and RPA workflows today register as OAuth clients and often have no public-facing HTTPS endpoints. A Mission profile for AAuth would need to address this, either through an enterprise agent registration companion or a fallback identity model for internally-scoped agents.
## 3. Proof-of-possession is the baseline
Mission-Bound OAuth has to strongly recommend DPoP or mTLS because durable delegated authority on bearer tokens is too risky.
AAuth starts from signed requests. That is a much better default for any protocol that wants to make serious claims about agent governance.
## 4. Chaining is built in
Mission-Bound OAuth relies on token exchange, projected Mission references, and careful downstream derivation rules.
AAuth already has a better native story for chained authorization through `upstream_token`.
One important caveat: Section 4.5.8 of the current draft explicitly flags call chaining as an exploratory feature: "The mechanism described here may change." It is a better starting point than OAuth's approach, but it is not yet a stable protocol surface.
## 5. Authentication-time bootstrap may be less awkward
One thing that has become clearer since the original Mission-Bound OAuth writeup is that classic OAuth needs an authentication-layer companion profile to do this cleanly. In the OAuth world, [Client Context and ID-JAG for Mission-Bound OAuth](/notes/client-context-and-id-jag-for-mission-bound-oauth/) had to define how purpose gets carried into the authentication step and how a reduced Mission view gets projected across same-IdP trust domains.
AAuth likely reduces the need for a separate Client Context-style bootstrap mechanism because signed requests, deferred approval, clarification, and direct approval are already native parts of the protocol flow. That is a real advantage.
But it does not automatically remove the need for the other half of that companion story: a durable Mission record after approval, and a reduced projection model for cases where authority needs to cross domains or be consumed by downstream systems that do not hold the full authoritative state.
In compact form, the comparison now looks like this:
| Concern | OAuth-side answer today | Likely AAuth-side answer |
|---|---|---|
| Authentication-time bootstrap | Client Context companion profile | Mostly native to AAuth interaction flow |
| Durable authority object | Mission-Bound OAuth Mission record | Still needs an AAuth Mission profile |
| Machine-evaluable authority model | Mission Authority Model | Still needs an AAuth Mission profile |
| Cross-domain reduced projection | ID-JAG-style companion profile | Still needs an AAuth projection profile, even if not ID-JAG specifically |
| Verified actor continuity | Mission-Bound OAuth verified agent binding | Still needs an AAuth Mission profile |
# Where The Mission Model Still Adds Something
This is the key part.
Even if AAuth is the better substrate, I do not think it obviously removes the need for the Mission architecture.
## 1. `purpose` is not the same thing as a Mission
AAuth has a `purpose` field, and that is useful. It is also slightly richer than pure display text: AAuth's clarification chat mechanism (Section 11.4) allows users to ask questions about the stated purpose during authorization, so `purpose` is conversationally live during the exchange, not just a consent label.
But `purpose` is still a free-form human-readable string. The spec defines no allowed values, no machine-processable semantics, and no policy-enforcement hooks. It is not automatically:
- a durable authority object
- a versioned authority model
- a lifecycle record
- an audit artifact
- a bounded authority envelope
> Mission-Bound OAuth's strongest idea is not "show more purpose text to the user." It is "treat delegated authority itself as a governed object."
## 2. Deferred approval is not the same as lifecycle governance
AAuth clearly has a better pending/approval story than OAuth.
What is less clear is whether the resulting authorization becomes a durable lifecycle object that can later be:
- suspended
- resumed
- revoked independently of token refresh
- terminated by external business events
- inspected as its own governance record
That is still a separate architectural concern. Notably, the current AAuth draft explicitly puts token revocation out of scope. Section 20.10 states "Token revocation is not defined in this specification." This is not an oversight; it is an explicit deferral. The only termination mechanism is expiry followed by refused renewal, which is reactive rather than proactive. There is also no event notification or business-event hook of any kind.
## 3. Chaining is not the same as a bounded authority envelope
AAuth's chaining model gives provenance, but it introduces a specific least-privilege hazard: `upstream_token` is explicitly "not a scope ceiling" per the spec. Downstream authorization servers evaluate their own policy independently and may grant scope broader than what was authorized upstream. Without an explicit Mission-like envelope, nothing in the protocol prevents that expansion.
> A chain of AAuth hops can expand effective authority, not just narrow it.
That is not a gap in AAuth's design for its stated goals. It is a gap between AAuth's chaining model and what Mission governance requires. The chaining model does not automatically define:
- what downstream discovery is inside approved bounds
- what requires fresh approval
- how jurisdiction, relationship, or business-process constraints are preserved
- how open-world exploration stays governable
That is exactly the job the Mission Authority Model was designed to do.
## 4. Better actor modeling is not yet the same as verified actor continuity
Mission-Bound OAuth now defines `Verified Agent Binding` explicitly: the Mission record is the binding anchor, sender constraining carries key continuity, the AS performs actor verification at exchange time, and cross-domain projection is treated as a degraded case unless live state is shared.
AAuth is directionally better here than OAuth because agent identity and proof-of-possession are closer to the protocol core. But that is still not the same thing as a defined continuity model that answers:
- what is the binding anchor for the approved actor, and how does it survive across AAuth interaction restarts or renewed requests
- how key continuity is enforced across `upstream_token` hops, where each AS is re-evaluating independently
- what exactly crosses a trust boundary in projected form, and what must remain authoritative at the home AS
- where actor continuity degrades to projection-only evidence, and what that means for enforcement at downstream resource servers
Signed requests make actor identity visible at each hop. They do not automatically define whether the actor presented at hop N is still the same actor the Mission originally approved, or whether a delegation event has occurred that requires fresh Mission authorization.
That still looks like Mission-profile work rather than something the current AAuth draft gives you automatically.
## 5. FGA still needs a bounded intermediate artifact
One of the more important arguments in [Mission-Bound OAuth](/notes/mission-bound-oauth/) is that Missions make AuthZEN/FGA-style decisioning practical for agents by narrowing the search space.
That argument does not go away if the protocol substrate changes.
Even on AAuth, you still seem to need something like:
- a Mission Proposal
- an Approved Mission
- a Mission Authority Model
or you still face the same question:
how does open-ended intent become a bounded, evaluable authority context that policy engines can use safely?
## 6. Approval evidence and audit trail
Mission-Bound OAuth eventually had to make approval evidence a first-class artifact: what was shown, who approved it, and what authority model was actually bound to that approval.
AAuth has strong approval mechanics. What is less clear is whether the resulting authorization retains a verifiable approval artifact after the exchange completes. For regulated deployments in finance, healthcare, or government, this is a compliance requirement independent of how good the interaction flow is. A tamper-evident record of what was presented for consent, who approved it, and what authority was bound to that approval is required by regulation regardless of protocol quality. AAuth's conversational approval model is much better than OAuth's redirect, but the retention and verifiability of that evidence needs to be explicit in any Mission profile.
## 7. Headless and service-to-service missions
AAuth identifies agents via HTTPS URLs pointing to published metadata. That works well for internet-scale agents with public endpoints. It is a meaningful assumption for enterprise environments where internal microservices, batch jobs, and RPA workflows often have no public-facing HTTPS endpoints.
A Mission profile would need to address this for deployments where the executing actor is not a public agent with a well-known URL. The Mission record already separates `mission_subject.sub` and `mission_client.client_id` as the binding anchor. An AAuth Mission profile would need an equivalent internal-agent registration model for actors that cannot serve agent metadata at a well-known HTTPS address.
## 8. Authority compilation boundary
One of the stronger arguments in Mission-Bound OAuth is the separation between intent and compiled authority:
```text
intent
->
Mission Proposal
->
Approved Mission
->
Mission Authority Model
```
AAuth's `purpose` field carries intent forward, and its approval flow is much richer than OAuth's. But the compiled authority artifact, the machine-evaluable model that gateways and policy engines reason over, does not appear automatically from a better approval flow.
That compilation step, from human-meaningful approval to machine-evaluable bounds, still looks like explicit profile work. Without it, every enforcement point has to re-derive what the approval meant from the same free-text inputs, with no guaranteed consistency.
## 9. Cross-domain semantics
AAuth is well suited to signed interactions, but that does not automatically answer:
- what authority crosses a trust boundary
- what should be projected versus re-evaluated
- whether the source authority object is preserved or remapped locally
The OAuth companion profile now answers this in one concrete deployment shape: keep the Mission authoritative at home, and use a reduced projection when a downstream same-IdP domain only needs a bounded subset. An AAuth-based architecture may not need the same mechanism specifically, but it would still need to answer the same projection question explicitly.
# The Cleanest Alternative
The cleanest alternative is not:
โreplace Mission-Bound OAuth with AAuth and forget the Mission idea.โ
It is:
โuse AAuth as the protocol substrate and express Mission semantics as an AAuth profile.โ
That profile would likely add:
- a durable Mission record at the authorization server
- a projected Mission reference in auth artifacts
- Mission lifecycle independent of token expiry
- authority-envelope checks on downstream grants
- business-event termination hooks
- Mission-level audit and approval evidence
In that design:
> AAuth replaces the OAuth transport and token choreography. The Mission remains the governance layer.
That may be the cleaner long-term architecture.
# Open Questions for AAuth
**Does AAuth want a durable authority object at all?** If yes, the Mission architecture maps naturally onto AAuth as a profile. If no, AAuth is solving a different problem: cleaner authorization choreography, not governed delegated authority over time. That is worth knowing explicitly, because it determines whether "Mission governance on AAuth" is a natural extension or an awkward graft.
**How does downstream attenuation fit?** Mission-Bound OAuth has an open question around Biscuit-style attenuation and chained restrictions that does not disappear on AAuth. If anything it becomes more pressing, because AAuth is already more comfortable with chained, signed protocol artifacts. The right layering looks similar regardless of substrate: Mission governance decides whether the authority should exist and continue; a downstream attenuation mechanism decides how authority can be further narrowed or chained; the attenuation layer must remain bounded by the Mission. AAuth's native comfort with signed chaining makes it a better place to answer this question.
**Is `purpose` meant to become structured?** `purpose` is conversationally live during authorization, which is a real advantage. But it is not machine-processable or policy-bearing in the current draft. Is that the intended long-term design, or should it become a structured, URI-identified authority claim? Section 9.1 of the draft acknowledges the gap and defers to a future version. The answer to this question determines how much of the Mission Authority Model compilation step an AAuth profile would need to define explicitly.
**Does `upstream_token` imply a scope ceiling?** The current spec is explicit that it does not: downstream authorization servers evaluate their own policy independently. For Mission governance, that is a significant gap. A Mission profile would need to define whether the Mission envelope overrides this behavior, and if so, how downstream ASes learn the ceiling and how violations are handled.
**How should headless missions work?** AAuth's agent identity model assumes a publicly resolvable HTTPS URL. What is the identity and binding model for internal service agents, batch workers, and automation that have no public endpoint? Any enterprise-oriented Mission profile would need to define this, either through a companion registration mechanism or a fallback binding model for non-public agents.
**What would the smallest useful Mission profile actually standardize?** Not a full redesign, just the minimum that makes Mission governance on AAuth viable: a durable Mission record, a projected `mission_ref` claim, lifecycle states independent of artifact expiry, and a bounded expansion rule for `upstream_token` chains. That profile boundary matters because it determines what AAuth itself needs to change versus what lives purely in a companion specification.
# Defining The AAuth Mission Profile
If the right long-term answer is "Mission governance on AAuth," then the useful next step is not another abstract checklist. It is a concrete profile shape.
This is the AAuth Mission profile that seems to fall out of the comparison.
## 1. Mission Creation From Approved AAuth Requests
An approved AAuth interaction should not terminate at "authorization granted." It should produce a durable Mission record at the authorization server.
The profile boundary is:
| AAuth interaction layer | Mission-governance result |
|---|---|
| Signed authorization request | Mission Proposal input |
| Deferred approval / clarification / direct approval | Approval and attenuation step |
| Successful authorization outcome | Approved Mission creation |
| Returned auth artifacts | Projected reference to the durable Mission |
The key rule is simple: the live request is not the durable authority object. It is the input from which the durable authority object is created.
## 2. Mission Record And Mission Reference
The profile should define two distinct things:
- a durable Mission record held by the AAuth authorization server
- a projected `mission_ref` carried in auth artifacts and chained requests
That gives the AAuth-based design the same separation that Mission-Bound OAuth now uses:
- the server governs the authoritative Mission record
- protocol artifacts carry a projected reference
- downstream systems reason over projected facts, not the full internal record
Without that split, AAuth interactions stay cleaner than OAuth, but the architecture still lacks a stable authority object that survives across chained execution.
The service boundary can also stay looser than this post has mostly assumed. One plausible long-term architecture is not only "AAuth plus Mission profile" but "AAuth plus separate Mission authority service," where the AAuth authorization server consumes Mission state instead of owning it directly. That is closer to the control-plane model from the original Power of Attorney series and may be the cleaner separation if AAuth matures into the transport layer while Mission governance remains its own system of record.
## 3. Machine-Evaluable Mission Authority Model
The profile should define a machine-evaluable Mission Authority Model derived from the approved Mission, not just from the current request state.
At a minimum, that model needs to carry:
- purpose class
- bounded resource selectors or equivalent authority envelope
- operation groups or action classes
- lifecycle bounds
- delegation bounds
- approval attenuations
And it needs an explicit access path for enforcement points, for example:
- Mission introspection
- a signed Mission Authority Model artifact
- tightly bounded Mission-derived claims in AAuth auth artifacts
This is the same separation Part 1 had to make on OAuth:
```text
approved request
->
Approved Mission
->
Mission Authority Model
->
projected artifacts for runtime use
```
The protocol substrate may change, but the architecture still needs that artifact stack.
## 4. Lifecycle Independent Of Token Expiry
The profile should define Mission lifecycle as separate from the lifetime of any single AAuth artifact.
That means the Mission can be:
- suspended
- resumed
- revoked
- completed
- expired
and those states must govern whether new artifacts can be issued or chained, independent of whether a previously issued token or authorization artifact is still structurally valid.
This is where AAuth's stronger interaction model stops being enough on its own. Deferred approval is not lifecycle governance. A Mission profile has to add that layer explicitly.
## 5. Approval Evidence As A Retained Artifact
The profile should define approval evidence as something the authorization server retains in a tamper-evident form.
At minimum, that evidence has to answer:
- what the user was shown
- what the user approved
- what authority model was bound to that approval
- what attenuation or clarification happened before approval
That is especially important if AAuth's conversational approval flow becomes one of its main differentiators. The richer the interaction, the more important it becomes to retain the evidence of what actually happened.
## 6. Verified Actor Continuity
The profile should define actor continuity explicitly, not assume it falls out automatically from signed requests.
At minimum, it needs to answer:
- what binds the approved actor to the Mission record
- how that actor is re-verified at each chained derivation step
- when a new actor is treated as a delegation event
- what is authoritative in the home domain
- what degrades to projection-only evidence across trust boundaries
This is where the AAuth profile should parallel the `Verified Agent Binding` work in Mission-Bound OAuth rather than hand-wave past it.
## 7. Cross-Domain Projection Rules
The profile should define what crosses a trust boundary and what must stay local.
That means distinguishing:
- the authoritative Mission record, which stays at home
- the projected Mission reference
- the reduced set of Mission-derived facts another authorization domain may rely on
- the facts that must always be re-evaluated locally
An AAuth Mission profile may not need ID-JAG specifically, but it still needs the same architectural answer the OAuth companion profile now gives: reduced projection, not portable authority.
## 8. Bounded Expansion For Open-World Execution
The profile should define a bounded expansion model that overrides the current `upstream_token` "not a scope ceiling" behavior.
The key question is the same one the OAuth architecture had to answer:
> Is this newly discovered downstream action still inside the approved Mission envelope, or does it require fresh approval?
If the profile cannot answer that mechanically, then AAuth's cleaner chaining model still leaves the core governance problem unresolved.
## 9. Orchestration-Layer Landing
The profile also has to land in the runtime, not just in the authorization server.
That means the AAuth-side architecture needs an answer for:
- how an orchestrator pauses or terminates execution when Mission state changes
- how irreversible steps trigger escalation instead of blind retry
- how compensating or unwind behavior is tied to Mission lifecycle rather than left as local runtime convention
This is the same lesson as in the OAuth architecture: a governed authority object that never reaches execution control is only partially real.
## A Compact Profile Summary
In compact form, the AAuth Mission profile would need to define:
| Profile element | Why it exists |
|---|---|
| Durable Mission record | So authority survives beyond the current AAuth interaction |
| `mission_ref` projection | So artifacts can reference Mission state without exposing the full record |
| Mission Authority Model | So gateways, PDPs, and RSes have something machine-evaluable |
| Lifecycle model | So Mission state can terminate or suspend execution independently of artifact expiry |
| Approval evidence artifact | So approval remains auditable and tamper-evident |
| Verified actor continuity | So the approved actor and the executing actor stay meaningfully linked |
| Cross-domain projection rules | So trust-boundary behavior is explicit |
| Bounded expansion rules | So open-world discovery remains governable |
That is not a full protocol redesign. It is a governance layer on top of a cleaner substrate.
And it still does not eliminate the need for a continuous authority evaluation layer distinct from request authorization. AAuth may be the cleaner transport, but Mission governance still has to answer whether execution should continue as conditions change over time, not merely whether the next signed request is well-formed and in-bounds.
# Conclusion
The framing that matters is not "Mission-Bound OAuth or AAuth." It is "Mission governance on OAuth" versus "Mission governance on AAuth." That separation is more useful because it keeps the governance model distinct from the transport substrate.
AAuth changes the protocol layer substantially, in ways that make it a better substrate for agent authorization. It does not change the underlying requirement: delegated machine authority needs to be represented as a governed object, not just as a better interaction flow.
> If Mission-Bound OAuth is the retrofit, AAuth may be the native habitat.
---
# Client Context and ID-JAG for Mission-Bound OAuth
Canonical URL: https://notes.karlmcguinness.com/notes/client-context-and-id-jag-for-mission-bound-oauth/
Markdown URL: https://notes.karlmcguinness.com/notes/client-context-and-id-jag-for-mission-bound-oauth.md
The [Power of Attorney series](https://notes.karlmcguinness.com/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/) argued that agent authority needs to be bounded by purpose, scope, conditions, and lifecycle, not inferred from credentials alone.
The [Mission-Bound OAuth RFC](/notes/mission-bound-oauth/) made that concrete: the Mission is the durable authority object at the authorization server, tokens and assertions are derived artifacts, and the projected `mission` claim (`id` plus `issuer`) ties them together.
This post is about a narrower question: what role does the *authentication layer* play?
The short answer is that authentication is the one moment in an agent workflow when the user is present: when intent can be confirmed, step-up can happen, and approval evidence can be captured. That makes it the natural bootstrap point for a Mission. [OpenID Connect Client Context](https://github.com/mcguinness/connect-client-context) is the mechanism for doing that cleanly. When Mission approval cannot complete synchronously, the [OpenID Connect Deferred Token Response Flow](https://github.com/gniero/oidc-dtr-resources) (DTR) handles the asynchronous path. When the Mission later needs to cross into another authorization domain inside the same enterprise, [ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) (Identity Assertion Authorization Grant) is how a reduced Mission projection can travel without dragging the full authority model with it.
None of these is an alternative to Mission-Bound OAuth. Together they form an authentication-layer companion profile for it.
# The Audience Problem
Take a concrete scenario. An agent is asked to schedule a meeting with the engineering team, send the invitations, and record the follow-up in the CRM. Three services. Three authorization servers (ASes). OAuth access tokens are audience-bound by design: the token issued for the calendar API is not valid at the CRM. That is intentional, as audience restriction prevents token replay across services, but it creates a structural problem for governed agent execution.
[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange exists for this. Rich Authorization Requests (RAR, [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)) can shape what each token contains. The [OAuth Identity Chaining](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/) draft profiles how identity propagates across AS boundaries. None of these, by themselves, create what a governed agent mission actually needs: a single durable authority object that all three token issuances trace back to, with a unified lifecycle that can be suspended, revoked, or terminated as a unit.
> An agent mission that spans three authorization domains needs more than three shaped tokens. It needs one governed authority object behind them.
The challenge is not that no cross-domain mechanism exists. The challenge is that each target AS needs bilateral trust with the source AS, must understand the specific semantics being propagated, and must apply them consistently. Inside a single organization's infrastructure, this can work. Across the vendor and SaaS ecosystem an enterprise agent actually operates in, it does not scale reliably.
That is exactly the gap Mission-Bound OAuth fills. Once the Mission exists at the AS, token exchange and RAR become derivation mechanisms, ways to mint audience-specific tokens inside the governed envelope. The question this post addresses is earlier in the flow: how does the Mission get started when a user is in the loop?
# The Gap Mission-Bound OAuth Leaves Open
Mission-Bound OAuth defines what governed authority looks like and how it behaves. What it did not yet define directly was the authentication-layer companion path: how an interactive OpenID Connect event binds a Mission to an actual human who explicitly delegated a specific authority to a specific agent for a specific purpose, with identity verified at a known assurance level.
Without an authentication layer, the Mission bootstrap problem has three unsolved parts.
**The Mission knows what was approved, but not that the user actually confirmed it.** Mission-Bound OAuth's interactive approval path shows the user a consent display and captures their response. But without an OpenID Connect (OIDC) authentication event, there is no OpenID Provider (OP)-verified identity assertion binding that approval to a specific authenticated subject at a specific assurance level. The Mission records that *someone* approved it. It does not record *who*, with what authentication strength, or what was cryptographically confirmed at the moment of approval. For regulated or high-stakes delegation, that gap matters: the authority chain cannot be traced back to a verified human principal.
**Open-world agents cannot have their authority envelope set correctly at Mission creation.** The core thesis of the Power of Attorney series is that agents need bounded, governed authority, not just credentials. For open-world agents that discover resource requirements at runtime, this creates a hard problem without an authentication layer: the Mission envelope has to be set at creation time, before the agent knows what it will encounter. The only options without a user-confirmed bootstrap are to make the envelope as broad as the purpose class allows (defeating the governance goal), or to make it narrow and accept constant re-approval failures (defeating the usability goal). Neither is acceptable as a foundation for agents that are supposed to operate autonomously inside governed bounds.
The `params` object in the purpose context helps break this dilemma. The user authenticates at Mission initialization, confirms a purpose, and the agent supplies the specific parameters that bound the task. The AS can use these inputs to compile a Mission Authority Model that is tight enough to be meaningful governance and broad enough to cover the agent's declared intent, without requiring the agent to enumerate every downstream audience in advance. That is the only path to a Mission that governs open-world execution rather than only closed-world workflows.
**Cross-domain projection has no trust foundation without a shared identity anchor.** Mission-Bound OAuth describes cross-domain Mission assertions for reaching authorization domains outside the home AS. But every bilateral trust relationship required to make those assertions work has to be established separately. In most enterprise deployments, the Identity Provider (IdP) is already the trust anchor: every Resource AS already trusts it. ID-JAG leverages that existing trust to project a minimal Mission view across same-IdP boundaries without requiring new AS-to-AS federation agreements. Without the authentication layer, you either accept the bilateral establishment cost for each new domain, or the agent's authority simply cannot cross same-IdP boundaries in a governed way.
These are not gaps that tighter scope strings or stronger credentials can fill. They are gaps in the *trust foundation* of the Mission. A Mission created by a client asserting a purpose, without a user authentication event, without OP-confirmed identity, and without step-up enforcement, is not Power of Attorney. It is a client claiming authority on behalf of a user who was never in the room.
> For Mission governance to be real rather than nominal, the delegation has to trace back to an authenticated human who actually confirmed it. Client Context and ID-JAG are how that traceability is established at the authentication layer.
# Authentication Is the Right Bootstrap Moment
When a user kicks off an agent task through an interactive authentication event, something important is true that will not be true again until the user reappears: the user can read what they are approving, step-up can be triggered if the risk warrants it, and whatever they confirm becomes a tamper-evident approval record. That moment has always been where identity systems do their most important work.
For agent missions, it is also where the Mission can be created or approved, with the user's explicit confirmation of purpose and scope, captured at the one point in the workflow where the human is genuinely present and attentive.
[OpenID Connect Client Context](https://github.com/mcguinness/connect-client-context) is structured context attached to an OIDC authentication request. It is not a new credential type and it is not a replacement for OAuth tokens. It is the mechanism for making authentication intent machine-readable and policy-evaluable, so that the OP and its associated AS can act on it. Specifically, the OP can use the applied context to create or approve a Mission at the AS, establishing the governed authority object before any tokens are issued.
This matters only for user-delegated scenarios where a human is in the loop. Automated workloads with no human initiator, headless services, and pre-provisioned mission types use client credentials rather than interactive OIDC. Client Context is an extension of interactive OIDC, not a general-purpose bootstrap mechanism for every mission type.
That also means Client Context does not replace the broader control plane the older Power of Attorney framing called for. It bootstraps the Mission at the one moment a human is present. It does not answer the later question of whether runtime execution should continue after downstream facts, business events, or orchestration state change.
# The Purpose Context
For agent missions, the context type that matters most is `purpose`. A purpose context for the scheduling scenario might look like this:
```json
{
"kind": "https://example.com/purposes/schedule-meeting",
"display": {
"title": "Schedule Meeting",
"description": "Schedule a meeting with the engineering team and record the follow-up"
},
"params": {
"calendar": "engineering@example.com",
"date_range": "2026-04-01/2026-04-08"
},
"actor": {
"type": "agent",
"id": "assistant-agent-v2",
"sub": "user-123"
},
"constraints": {
"expires_at": "2026-04-01T17:00:00Z",
"max_duration": 900
}
}
```
Each field has a distinct job. `kind` is the stable purpose class the OP uses for policy lookup: it is the join key between the authentication layer and the Mission-creation rules at the AS, stable across deployments and reusable across requests of the same type. `params` are instance-level inputs: the specific calendar, the specific date range. They bound this particular run without carrying authority semantics themselves. `display` is a client-supplied UI hint the OP can present, supplement, or re-render so the user understands the task being approved. `actor` gives the OP advisory context about whether a human, agent, or service initiated the request; unless the client is separately authorized to assert it or the OP verifies it independently, it is not a verified fact. `constraints` bound the authentication event and its immediate outputs.
Together that is enough to bootstrap a Mission. It is not enough to replace one: the ID Token is not the authority artifact; it is evidence that authentication and approval happened. The Mission is the authority artifact, and it lives at the AS.
The `kind` field is architecturally the most important piece. A Mission profile can use it to map into purpose templates, approval workflows, or Mission-creation rules without forcing the base Client Context spec to define any of those things itself. It is the seam between the authentication layer and the authorization governance layer.
# The Handoff
The handoff has five steps: the client sends `client_context` during interactive authentication; the OP evaluates it, applying authentication policy, presenting purpose information to the user, and handling step-up if needed; the OP returns the applied `client_context` in the ID Token and retains the approval evidence; the client verifies the returned `client_context` claim rather than trusting what it originally sent; the OP or its associated AS uses that applied context to create or approve a Mission; from that point, Mission-Bound OAuth takes over.
The easiest way to think about the data model is:
| Form | Where it exists | What it means |
|---|---|---|
| Requested context | Authorization request | What the client asked the OP to evaluate |
| Applied context | ID Token `client_context` claim | What the OP validated and actually used |
| Projected context | ID-JAG or other downstream assertion | The reduced Mission-derived view a specific downstream audience needs |
What the AS receives from the applied Client Context: the purpose class as a stable policy key, the validated instance parameters, the authenticated subject, any actor information the OP has accepted as verified or retained as advisory context, and the retained display evidence for audit. What the AS creates in response: an Approved Mission with a Mission Authority Model, and a projected `mission` claim that downstream token derivation can reference.
The deployment boundary can still vary. In the OAuth-centered model used in this series, the Mission lives at the AS. In a broader control-plane architecture, the same applied context could be handed to a separate authority service that becomes the Mission state owner and the AS becomes one consumer of that state. The authentication-layer handoff is the same either way.
> The ID Token carries evidence that authentication and approval happened. The Mission carries the authority. Those are different jobs and they belong in different artifacts.
Once the Mission exists, the agent can get a calendar token, an email token, and when the time comes, a CRM token. All three trace back to the same Mission. All three are bounded by the same authority model. All three are under lifecycle that the AS can suspend, revoke, or terminate as a unit. The user approved the purpose once. The system enforces it everywhere.
That still leaves one more layer above this post's scope: execution control. Authentication confirms who approved what purpose. It does not by itself ensure that the orchestrator pauses, unwinds, or escalates correctly when the Mission later changes state. That is why this profile is a bootstrap and projection companion, not the whole governance system.
# When Handoff Cannot Complete Immediately
The handoff description above assumes synchronous OIDC: the user completes interaction, the OP issues an ID Token, Mission creation follows. That assumption holds for purpose types that auto-approve from a policy rule or that the user can confirm interactively in a single session. It breaks for purpose types that require more.
High-risk or high-assurance Mission bootstrap may need:
- identity verification beyond what can complete in a session, such as document scanning or biometric review that a human operator evaluates
- human approval of the Mission Proposal before authority is granted, where that approval follows an out-of-band review process
- step-up assurance that depends on an asynchronous channel
For these cases, the [OpenID Connect Deferred Token Response Flow](https://github.com/gniero/oidc-dtr-resources) (DTR) provides the right shape. DTR is an emerging OpenID Connect specification that lets a user initiate an authentication flow, leave, and receive the result asynchronously. The user completes their interactive portion: providing identity information, expressing intent, stepping through any immediate prompts the OP requires. The OP responds not with a redirect containing an authorization code, but with a `deferred_code`. The RP exchanges that code for a `deferred_auth_id`, then polls the token endpoint or waits for a ping callback until the authentication decision is made.
The `deferred_code code` response type is the most useful form for Mission bootstrap. It lets the OP decide at runtime whether the Mission can be bootstrapped immediately (returning a `code` on the redirect) or requires deferred processing (returning a `deferred_code`). The RP handles both paths from the same initial request. Purpose types that auto-approve compile a Mission immediately; purpose types that require human review enter a pending state. The response type is the OP's signal about which path applied.
The mapping between DTR states and Mission states is direct:
| DTR state | Mission lifecycle equivalent |
|---|---|
| `deferred_code` returned on redirect | Mission Proposal submitted; approval pending |
| `deferred_auth_id` received from exchange | Mission Proposal accepted for review |
| `authorization_pending` at token poll | Mission in pending state |
| Successful Token Response with ID Token | Mission approved and activated; Mission creation proceeds |
| `access_denied` error | Mission Proposal denied |
| `expired_token` error | Mission Proposal expired without decision |
The ping callback, sent to the RP's `deferred_client_notification_endpoint` when the authentication decision is made, is the authentication-layer equivalent of Mission lifecycle notification. For deployments that already use SSF/CAEP for Mission lifecycle events, the architectural role is the same: the OP signals completion asynchronously rather than making the RP poll indefinitely.
The `interim_id_token` that DTR can return during the Deferred Code Exchange Response is also worth noting for Mission deployments. When the OP can partially verify identity before the full authentication process completes, it can include an interim ID Token with partially-verified claims. For Mission bootstrap, this opens a preliminary pattern: a partially-verified identity could authorize a provisional Mission Proposal with restricted scope, upgraded to a full Mission once complete identity verification returns. That pattern is optional, but a deployment profile for high-assurance Mission types should address whether provisional Missions are permitted and what their authority bounds look like.
AAuth's `202 Accepted` pending state is the native equivalent of DTR's deferred flow. In AAuth, deferred approval with a polling URL is a first-class part of the core protocol. In the OpenID Connect world, DTR is the spec that adds this capability. Part 3 of this series notes AAuth's native deferred approval as an advantage over standard OAuth for agent Mission bootstrap. DTR is the OIDC ecosystem's answer to that gap. It does not make the OIDC path as seamless as AAuth's native flow, but it does mean the deferred-approval shape exists without requiring a protocol change, for deployments that need to stay in the OpenID Connect world.
# The ID-JAG Payoff
So far the story covers the home domain. The interesting case is when the Mission needs to cross into another authorization domain inside the same enterprise, specifically one that trusts the same OP but issues its own tokens.
In the scheduling scenario, the calendar and email services might sit behind one Resource AS. The CRM sits behind another. Both trust the same enterprise IdP. Token exchange can bridge this gap, but there is a cleaner option when same-IdP trust already exists: the OP can issue an ID-JAG carrying a reduced Mission projection, and the downstream Resource AS can validate it directly.
The ID-JAG is not the Mission traveling across the boundary. It is a signed assertion, issued by the OP the downstream AS already trusts, that carries a minimal projection: the `mission` claim, the applied `purpose.kind`, the relevant expiry, and actor context only if that information is needed and trustworthy enough for the receiving policy. The downstream AS validates the assertion, binds the `mission.id` and `mission.issuer` into its local token or server-side token state, and issues an audience-specific access token scoped to its own resources.
What the projection carries, and what it deliberately omits, matters. It should include `mission.id`, `mission.issuer`, the `purpose.kind`, audience-relevant params, expiry bounds, and actor context only when the receiving AS needs it for policy evaluation and can interpret its trust level correctly. It should not include the full applied `client_context`, UI-only display fields, parameters irrelevant to the receiving audience, or the internal Mission Authority Model. The downstream AS gets what it needs to issue a correctly bounded token. It does not get a copy of the governing Mission.
> The right mental model is not "the mission lives in the ID-JAG." It is "the ID-JAG carries a reduced Mission projection inside a trust domain that already trusts the OP as an assertion source."
This is where the profile becomes compelling as a whole. The user authenticated once, the OP applied a purpose context, and that applied context bootstrapped a Mission. The same-IdP trust that already exists in the enterprise becomes the carrier for a minimal Mission projection. No new bilateral trust establishment. No new protocol. Just the enterprise's existing federation infrastructure carrying a narrowly scoped assertion that lets downstream ASes issue correctly bounded tokens under the same Mission.
# End-to-End Protocol Flow
The scheduling scenario, calendar, email, and CRM, has three authorization domains behind the same enterprise IdP. Here is how the protocol messages flow.
**Phase 1: Authentication and Mission bootstrap**
The agent initiates an OIDC authentication using Pushed Authorization Requests ([PAR, RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126)) to keep the `client_context` payload out of the browser URL. The `client_context` parameter carries the structured purpose context as a JSON object:
```http
POST /par HTTP/1.1
Host: idp.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic
response_type=code
&client_id=ai-assistant
&redirect_uri=https://assistant.example.com/callback
&scope=openid
&nonce=n-0S6_WzA2Mj
&client_context={
"contexts": {
"purpose": {
"kind": "https://example.com/purposes/schedule-meeting",
"display": {
"title": "Schedule Meeting",
"description": "Schedule a meeting with the engineering team and record the follow-up"
},
"params": {
"calendar": "engineering@example.com",
"date_range": "2026-04-01/2026-04-08"
},
"actor": { "type": "agent", "id": "assistant-agent-v2", "sub": "user-123" }
}
}
}
```
The OP evaluates the purpose class against policy, presents purpose information to the user using the supplied `display` values or OP-generated text, applies step-up if required, and completes authentication. After the authorization code exchange, the ID Token confirms the applied context:
```json
{
"iss": "https://idp.example.com",
"sub": "user-123",
"aud": "ai-assistant",
"nonce": "n-0S6_WzA2Mj",
"client_context": {
"contexts": {
"purpose": {
"kind": "https://example.com/purposes/schedule-meeting",
"params": {
"calendar": "engineering@example.com",
"date_range": "2026-04-01/2026-04-08"
},
"actor": { "type": "agent", "id": "assistant-agent-v2" }
}
}
},
"exp": 1773072600
}
```
The OP or associated AS uses the applied `client_context` to create the Mission. Internally the AS records the Mission, and the projected protocol-facing handle is the `mission` object: `{ "id": "msn_7M9Qx2", "issuer": "https://as.example.com" }`.
That verification step matters. A client cannot safely treat the `client_context` it sent in the request as proof that the OP applied it. It has to verify the signed ID Token, check that the `client_context` claim is actually present, and reject the flow if required context is absent or inconsistent. Mission bootstrap only works if the applied context is carried in the signed ID Token and verified by the client.
**Phase 1b (variant): Deferred Mission bootstrap**
When the purpose type requires identity verification or human approval that cannot complete in a single session, the agent uses the `deferred_code code` response type instead. The PAR request is identical except for `response_type`:
```http
POST /par HTTP/1.1
Host: idp.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic
response_type=deferred_code+code
&client_id=ai-assistant
&redirect_uri=https://assistant.example.com/callback
&scope=openid
&nonce=n-0S6_WzA2Mj
&client_context={
"contexts": {
"purpose": {
"kind": "https://example.com/purposes/schedule-meeting",
"display": { ... },
"params": { "calendar": "engineering@example.com", "date_range": "2026-04-01/2026-04-08" },
"actor": { "type": "agent", "id": "assistant-agent-v2", "sub": "user-123" }
}
}
}
```
The OP determines this purpose class requires human approval before Mission authority can be granted. It redirects back with a `deferred_code` instead of a `code`:
```
HTTP/1.1 302 Found
Location: https://assistant.example.com/callback?
deferred_code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
```
The agent exchanges the `deferred_code` for a `deferred_auth_id`:
```http
POST /token HTTP/1.1
Host: idp.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic
DPoP:
grant_type=urn:openid:params:grant-type:deferred
&deferred_code=SplxlOBeZQQYbYS6WxSbIA
&deferred_notification_token=8d67dc78-7faa-4d41-aabd-67707b374255
```
```json
{
"deferred_auth_id": "f4oirNBUlM",
"expires_in": 10800,
"interval": 60
}
```
The agent polls at the configured interval. Until the approval decision is made, the OP returns `authorization_pending`. When the human reviewer approves the Mission Proposal, the next poll returns a successful Token Response:
```json
{
"access_token": "...",
"token_type": "DPoP",
"id_token": "",
"expires_in": 3600
}
```
If the RP registered a `deferred_client_notification_endpoint`, the OP sends a ping callback when the decision is made rather than requiring indefinite polling:
```http
POST /callback HTTP/1.1
Host: assistant.example.com
Authorization: Bearer 8d67dc78-7faa-4d41-aabd-67707b374255
Content-Type: application/json
{ "deferred_auth_id": "f4oirNBUlM" }
```
The agent then makes one final token request to retrieve the ID Token. Both paths, synchronous and deferred, converge here: the returned ID Token contains the applied `client_context` claim in the same structure as Phase 1. The OP or associated AS uses it to create an Approved Mission, exposes its `mission.id` and `mission.issuer` for derivation, and Phase 2 proceeds identically.
**Phase 2: Mission-governed token derivation**
With a `mission` handle in hand from either bootstrap path, the agent requests a calendar token referencing the Mission via `authorization_details`. [DPoP (Demonstrating Proof of Possession, RFC 9449)](https://datatracker.ietf.org/doc/html/rfc9449) sender-constraining is assumed throughout.
```http
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://assistant.example.com/callback
&authorization_details=[{"type":"mission","mission":{"id":"msn_7M9Qx2","issuer":"https://as.example.com"}}]
```
The AS validates Mission state and issues an audience-bound access token:
```json
{
"iss": "https://as.example.com",
"sub": "user-123",
"sub_profile": "user",
"aud": "https://calendar-api.example.com",
"act": { "sub": "assistant-agent-v2", "sub_profile": "ai_agent" },
"mission": {
"id": "msn_7M9Qx2",
"issuer": "https://as.example.com"
},
"scope": "calendar.schedule",
"cnf": { "jkt": "" },
"exp": 1773069300
}
```
The email token follows the same pattern via RFC 8693 token exchange, with `resource=https://email-api.example.com` and the calendar access token as the subject token. The AS checks Mission state at each exchange before issuing.
**Phase 3: ID-JAG projection for the CRM (same-IdP)**
The CRM sits behind `crm-as.example.com`, a separate Resource AS that issues its own tokens but trusts the same enterprise IdP. The agent requests a Mission-projection assertion from the OP using token exchange, identifying the target audience:
```http
POST /token HTTP/1.1
Host: idp.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience=https://crm-as.example.com
&authorization_details=[{"type":"mission","mission":{"id":"msn_7M9Qx2","issuer":"https://as.example.com"}}]
```
The OP confirms the Mission is still active and the CRM audience is within the Mission's bounds, then issues the ID-JAG. This is a signed JWT carrying only what the CRM AS needs: no display fields, no full authority model, no parameters irrelevant to CRM enforcement:
```json
{
"iss": "https://idp.example.com",
"sub": "user-123",
"sub_profile": "user",
"aud": "https://crm-as.example.com",
"resource": "https://crm.example.com/api",
"act": { "sub": "assistant-agent-v2", "sub_profile": "ai_agent" },
"mission": {
"id": "msn_7M9Qx2",
"issuer": "https://as.example.com"
},
"purpose": "https://example.com/purposes/schedule-meeting",
"jti": "id-jag-8f3a2b",
"exp": 1773069600
}
```
The agent presents this ID-JAG at the CRM Resource AS as a JWT bearer grant:
```http
POST /token HTTP/1.1
Host: crm-as.example.com
Content-Type: application/x-www-form-urlencoded
DPoP:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=
&scope=crm.notes.write
```
The CRM AS validates the signature, confirms the same enterprise IdP as issuer, and issues a CRM-scoped token:
```json
{
"iss": "https://crm-as.example.com",
"sub": "user-123",
"sub_profile": "user",
"aud": "https://crm.example.com/api",
"act": { "sub": "assistant-agent-v2", "sub_profile": "ai_agent" },
"mission": {
"id": "msn_7M9Qx2",
"issuer": "https://as.example.com"
},
"scope": "crm.notes.write",
"cnf": { "jkt": "" },
"exp": 1773069900
}
```
The critical point is not the exact wire format. It is that the Mission remains at the AS, the OP projects only the minimum information the same-IdP domain needs, and every issued token still traces back to the same governed Mission.
# Consent Fatigue Is the Failure Mode
This architecture only helps if it reduces repeated user prompts rather than multiplying them.
That is the real danger with any authentication-layer Mission bootstrap model. If every newly discovered tool, every downstream token exchange, or every sub-step in a long-running Mission forces the user back through another interactive approval screen, the result is not stronger governance. It is just consent fatigue.
Consent fatigue breaks the model in predictable ways:
- users stop reading prompts
- agents get broad standing approval just to avoid interruption
- teams weaken the prompts because the workflow becomes unusable
- genuinely high-risk transitions become harder to distinguish from routine noise
That is why this profile only makes sense on top of Mission-Bound OAuth rather than in place of it.
The point of the authentication step is not to ask the user about every downstream token. The point is to create or approve a Mission once, at a moment when the user is present, and then let the authorization layer govern bounded derivations inside that approved Mission.
The practical rule is simple:
- use Client Context when a fresh human authentication or approval event is genuinely needed
- use the Mission to carry that approval forward across bounded downstream actions
- require a new user interaction only when the agent needs to go outside the approved Mission envelope
If the profile cannot do that, it has failed.
# RAR, Token Exchange, Client Context, and DTR
These mechanisms are complementary, not competing.
Client Context explains why the user is authenticating and what they are approving. Mission-Bound OAuth defines the durable authority object and lifecycle. RAR shapes resource-specific authorization requests. Token exchange derives audience-specific tokens. ID-JAG projects reduced Mission context across same-IdP trust domains. DTR makes Mission bootstrap asynchronous when the purpose type requires it.
That is the layered model that makes sense for enterprise agent deployments.
RAR is still the better fit when there is one authorization server, the resource domain is already tightly governed, and the main problem is the shape of the access token.
Client Context plus ID-JAG adds the most value when:
- a user is present
- the OP is the enterprise trust anchor
- the Mission spans multiple authorization domains
- same-IdP trust makes assertion projection practical
- authentication-time step-up and approval evidence matter
DTR adds value on top of that when:
- Mission bootstrap for the given purpose type cannot complete synchronously
- human review of the Mission Proposal is part of the approval flow
- identity verification requirements exceed what a single interactive session can confirm
- the RP needs a notification model rather than a blocking wait
[GNAP (RFC 9635)](https://datatracker.ietf.org/doc/html/rfc9635) is worth acknowledging here too. It addresses multi-resource delegation with a different architectural model and does not depend on the same shared-IdP assumption. For greenfield systems, that may be the cleaner direction. This post is about how to improve the world enterprises actually have today.
# What This Companion Profile Needs To Define
Client Context plus ID-JAG is enough to improve Mission bootstrap, but it only becomes a real companion profile when the handoff and projection rules are explicit.
So rather than leave those pieces as open questions, here is the profile definition that seems to fit this architecture.
## 1. Client Context To Mission Handoff Profile
The base Client Context draft intentionally stops at the authentication layer. It defines requested context, applied context, and client verification, but it does not define the server-side object that applied context might instantiate. This profile closes that seam by treating the Mission as the target object.
### Handoff Preconditions
Before any Mission is created, approved, or resumed from an interactive authentication event, all of the following have to be true:
- The client has validated the ID Token normally, including signature, issuer, audience, expiration, and `nonce`.
This prevents Mission bootstrap from hanging off an untrusted authentication result.
- The ID Token contains a `client_context` claim.
This ensures the OP actually returned applied context.
- The client has verified that the returned `client_context` contains the required applied context for the operation it is about to enable.
This prevents the client from treating requested context as if it were applied context.
- The OP or associated Mission service treats only the applied `client_context` from the ID Token as Mission input, never the raw request value.
This ensures Mission creation is rooted in confirmed, signed context rather than client-supplied request data.
If those conditions are not met, no Mission bootstrap occurs.
### Handoff Modes
This profile defines three handoff modes:
| Mode | When it applies | Result |
|---|---|---|
| `Create` | No active Mission handle is associated with the interaction | The OP or associated Mission service uses the applied context to create a new Mission |
| `Approve` | Local policy requires explicit approval of a pre-created or pending Mission | The OP or associated Mission service uses the applied context and the authenticated subject to mark that Mission approved |
| `Resume` | The client presents an existing `mission.id` and `mission.issuer`, and the Mission is still active | The OP or associated Mission service may treat the authentication event as a Mission re-attestation, but only if the applied context is compatible with the stored Mission record |
The profile should fail closed here: if the Mission service cannot determine whether the event is a create, approve, or resume operation, it rejects the handoff.
In shorthand, the decision flow looks like this:
```text
No Mission handle? -> Create
Pending or pre-created Mission? -> Approve
Existing active Mission handle? -> Resume
Anything ambiguous? -> Reject
```
### Mapping Applied Context Into Mission Inputs
The handoff takes the applied context and maps it into Mission inputs with a strict split between governance inputs and audit-only inputs.
| Category | Contents | How it is used |
|---|---|---|
| Governance inputs | `purpose.kind`, validated `params`, applied tenant or application identifiers, authenticated subject, authentication strength such as `acr` where relevant, and validated temporal bounds such as `constraints.expires_at` or `constraints.max_duration` | Compiles into the Mission Authority Model |
| Audit and evidence inputs | The applied context envelope, authentication time, any rendered or canonicalized display evidence retained by the OP, and any actor information the OP has verified or explicitly retained as advisory | Supports audit, traceability, and evidence retention |
| Never authority by themselves | Raw request `client_context`, unverified `actor` values, and UI-oriented text such as `display.title` or `display.description` | Must not define what the system is authorized to do |
That split matters. `purpose.kind` and validated `params` can shape the Mission Authority Model; the display text can help explain what happened, but it must not define what the system is authorized to do.
### Purpose Catalog Requirement
This profile depends on a purpose catalog keyed by `purpose.kind`.
For each supported `purpose.kind`, the catalog needs to define:
- The schema for `params`.
This defines what instance inputs are valid.
- Which `params` members are Mission-governing versus audit-only.
This separates authority-shaping data from evidence-only data.
- Allowed tenants, applications, or resource classes, where relevant.
This constrains where the Mission may apply.
- Required authentication strength or step-up rules.
This tells the OP what assurance level is needed.
- Maximum lifetime and propagation rules.
This limits duration and downstream spread.
- Conditions for resume versus recreate.
This determines when a Mission can be re-attested rather than re-issued.
This is what allows applied context to compile into a bounded Mission instead of becoming just another bag of metadata.
### Failure Semantics
If authentication succeeds but Mission compilation or policy evaluation fails, the profile should treat that as a Mission bootstrap failure, not as a soft success.
```text
Authentication succeeds
Mission compilation fails
-> No Mission created / approved / resumed
-> Client does not enable the Mission-bound operation
-> Failure is surfaced distinctly from ordinary authentication failure
```
That is the only safe behavior. "The user authenticated successfully" is not equivalent to "Mission bootstrap succeeded."
## 2. Mission Projection Profile For ID-JAG
Once a Mission exists, same-IdP propagation needs a stable projection model. This profile defines ID-JAG as a reduced, audience-bound Mission projection, not as a portable copy of the Mission itself.
### Projection Model
| This projection is | This projection is not |
|---|---|
| A Mission already governed at the home authorization server | The full Mission Authority Model |
| A projection for one specific downstream audience | The full applied `client_context` |
| A reduced set of Mission-derived facts that the downstream AS can evaluate locally | UI text shown to the user |
| An input to local downstream token issuance | Advisory values the downstream AS cannot safely interpret |
### Minimum Projection Claims
At minimum, the projected assertion should carry:
- `iss`: identifies the OP issuing the assertion.
- `aud`: binds the projection to exactly one downstream AS audience.
- `sub`: carries the authenticated user subject.
- `mission`: carries the stable protocol-facing Mission handle as `id` and `issuer`.
- `purpose` or `purpose_kind`: carries the applied `purpose.kind`.
- `iat` and `exp`: bound issuance time and lifetime.
- `jti`: supports replay detection.
The assertion MAY also carry:
- Minimized audience-relevant `params`.
Include these only when they are needed for downstream issuance policy.
- Actor information.
Include it only when the issuer can state whether it is verified or advisory and the receiver has policy for that distinction.
- Sender-constraining information such as `cnf`.
Include it when the deployment binds assertions or derived tokens to a proof-of-possession key.
### Projection Minimization Rules
The profile should define strict minimization rules:
| Do not include | Reason |
|---|---|
| The full applied `client_context` | Too broad for audience-specific projection |
| `display` content | UI evidence is not needed for downstream issuance |
| `params` members irrelevant to the downstream audience | Violates minimization and increases leakage |
| Mission-internal authority structure, workflow state, or extra lifecycle metadata | The downstream AS needs issuance facts, not a copy of Mission internals |
The projection should answer one narrow question for the downstream AS: what Mission-scoped facts does this AS need to issue a correctly bounded local token?
In practice, the boundary looks like this:
| Value | Stay local to home AS / Mission service | May cross domains in projected form |
|---|---|---|
| Mission Authority Model | Yes | No |
| Full applied `client_context` | Yes | No |
| `display` content | Yes | No |
| `mission.id` and `mission.issuer` | No | Yes |
| Applied `purpose.kind` | No | Yes |
| Audience-relevant `params` | Usually | Yes, but only minimized |
| Actor data | Usually | Only when needed and with clear trust semantics |
| Workflow state / internal lifecycle details | Yes | No |
### Downstream AS Processing
The downstream AS validates the assertion and then makes a fresh local issuance decision. At minimum it should:
```text
Validate signature, issuer, audience, time validity, and replay protections
Confirm the issuer is trusted for Mission projection into this audience
Apply local policy to mission.id, mission.issuer, purpose.kind, minimized params, and actor information
Reject if projected values exceed local policy, even if the assertion is otherwise valid
```
The downstream AS does not inherit the Mission wholesale. It receives enough projection to mint its own audience-bound token under local policy.
### Projection Failure Semantics
If the projection is invalid, expired, replayed, insufficiently trusted, or outside local policy, the downstream AS rejects it and issues no token.
That rejection should not be treated as proof that the home Mission is invalid. It only means that this downstream domain will not honor this projection under its own policy.
## What Mission-Bound OAuth Now Defines
The remaining piece, actor verification and execution continuity, now sits where it belongs: in [Mission-Bound OAuth](/notes/mission-bound-oauth/#verified-agent-binding).
The latest Client Context draft is right to treat `actor` as advisory by default. That protects the authentication-layer protocol from over-claiming trust it has not earned. But Mission-Bound OAuth needs more than advisory actor context. It has to answer the separate question of whether the actor executing derived actions is the same actor the Mission approved at bootstrap.
The Mission-Bound OAuth draft now defines that as **verified agent binding**. In that model:
- the Mission record at the AS is the binding anchor
- sender constraining provides key continuity across derivation steps
- the AS performs actor verification at token exchange time
- derived actor claims are AS-authoritative rather than client-self-asserted
- cross-domain projection carries actor context, but live actor continuity across trust domains still depends on introspection or equivalent state sharing
That is the right split. Client Context and ID-JAG handle authentication-time bootstrap and reduced projection. Mission-Bound OAuth handles authority continuity across the Mission lifecycle.
# How This Closes The Security Gaps
Those profile pieces are not just interoperability work. They are how the architecture closes its remaining security gaps.
The main security gaps still open are these:
- **Bootstrap/request confusion.** The client must verify applied context in the ID Token before treating a Mission as user-rooted. The profile now says that explicitly, but the handoff profile must make it impossible for Mission creation to rely on unverified request data.
- **Actor substitution.** Advisory `actor` data is not enough for high-stakes delegation. The Mission-Bound OAuth draft now addresses this with verified agent binding, so the remaining gap here is making sure this companion profile depends on that model rather than trying to redefine it.
- **Projection over-sharing.** ID-JAG needs minimization rules so deployments do not dump the full applied context, UI text, or broad `params` objects into downstream assertions.
- **Replay and confused deputy risk.** Projection claims have to be audience-bound, time-bounded, replay-resistant, and tied to local policy evaluation at the downstream AS. Otherwise one resource domain can be tricked into honoring context intended for another.
- **Lifecycle drift.** If a Mission is suspended, revoked, or terminated, projections and derived tokens need coherent invalidation behavior. Otherwise Mission lifecycle becomes an aspiration rather than an enforcement property. That is fundamentally a Mission-Bound OAuth responsibility, even if this profile depends on it.
These are exactly the kinds of gaps that appear when governance lives in one object, authentication evidence in another, and downstream assertions in a third. They are solvable, but only if the profile makes the joins explicit.
# What Open-World Agents Still Require
The hardest requirement is still the original one: making open-world agents possible without turning the Mission into a standing blanket approval.
That requires more than authentication-time bootstrap. It requires a bounded discovery model.
At Mission creation time, the system has to be able to say something stronger than "this one exact API and scope are allowed" and something narrower than "the agent may do whatever seems related." In practice, that means the profile needs a purpose catalog that defines, for each `purpose.kind`:
- the schema of allowed `params`
- the classes of downstream resources the agent may discover at runtime
- the operations that are in-family for that purpose
- the maximum privilege, lifetime, and propagation rules for those operations
- the boundary where newly discovered work is no longer considered part of the approved Mission
That lets the Mission service compile a Mission Authority Model that is tight enough to govern real execution while still giving the agent room to discover concrete resources at runtime.
The missing rule is the re-approval boundary.
An open-world agent should be able to keep working when it discovers a new calendar folder, a new mailbox, or a new CRM record that is still inside the approved purpose class and parameter bounds. It should not be able to silently expand into a different subject, a different tenant, a stronger operation, a different resource class, or a longer-lived execution than the compiled Mission allows.
At a high level, the boundary should work like this:
| Newly discovered change | Stay inside Mission | Require re-approval |
|---|---|---|
| Another object of the same allowed resource class | Yes | No |
| Same subject, same tenant, same operation class | Yes | No |
| New subject outside approved bounds | No | Yes |
| New tenant | No | Yes |
| Stronger operation than the compiled Mission allows | No | Yes |
| Different resource class outside purpose policy | No | Yes |
| Longer lifetime or broader propagation than allowed | No | Yes |
So the complete model needs a deterministic answer to one question:
> Is this newly discovered action still inside the Mission envelope, or does it require a fresh human interaction?
If the system cannot answer that question mechanically, then open-world execution collapses back into either over-broad standing authority or repeated consent prompts.
That is why the real end state is not "put more data in Client Context." It is a cleaner division of labor:
- Client Context bootstraps purpose at authentication time
- the handoff profile compiles applied context into a bounded Mission
- Mission-Bound OAuth governs that Mission across downstream derivations
- ID-JAG projects only the minimum necessary Mission view across same-IdP trust domains
- Mission-Bound OAuth ensures the executing actor is the actor the system approved
- re-approval triggers fire automatically when discovery crosses the compiled Mission boundary
That is the architecture that closes the remaining security gaps and makes open-world agents plausible without giving them permanent ambient authority.
# The Practical Claim
The claim here is not that Client Context solves agent security.
The claim is narrower:
Client Context is a strong way to capture and confirm interactive Mission bootstrap input at authentication time, and ID-JAG is a strong way to project reduced Mission context across same-IdP trust domains, while leaving the durable Mission and its lifecycle where they belong: at the authorization server.
That is a smaller claim than "mission in the ID Token."
It is also a much better architecture.
---
If you work in identity, security architecture, or agent systems and are thinking about how to carry mission context through the authentication layer, the questions I care most about are:
- what should a Client Context to Mission handoff look like in a real profile
- what minimum Mission projection belongs in an ID-JAG
- where same-IdP projection stops being enough and a broader Mission assertion profile is needed
The Client Context draft is at [github.com/mcguinness/connect-client-context](https://github.com/mcguinness/connect-client-context).
---
# Mission-Bound OAuth
Canonical URL: https://notes.karlmcguinness.com/notes/mission-bound-oauth/
Markdown URL: https://notes.karlmcguinness.com/notes/mission-bound-oauth.md
> **Superseded.** This working document was the early exploration that
> became the [Mission-Bound Authorization draft family](https://github.com/mcguinness/mission-bound-authorization),
> where the current architecture lives. It remains published as
> history, and its object model and member names are out of date. A
> definitive series on the current model is in preparation.
This document is a working item for iterating on the architecture before drafting one or more IETF Internet-Drafts. The current working name is `draft-mcguinness-oauth-mission-bound-authorization`.
This is a **RFC**: an end-to-end architecture proposal published to gather feedback before the work is split into standards-track core pieces, companion specs, or deployment profiles.
It is intentionally a **superset architecture document**. It describes the full model, even where different parts may eventually be standardized separately or left as implementation guidance.
The goal is to make the design legible enough for identity and OAuth practitioners to react to the whole system before the work is narrowed into concrete drafts. If the architecture holds up, the likely next step is to split it into a smaller core OAuth draft plus companion profiles.
If you want the shortest version of the argument, it is this:
- OAuth is good at point-in-time authorization.
- Agents need authority that survives across token lifetimes.
- That authority needs purpose, lifecycle, delegation bounds, and independent termination.
- [Mission-Bound OAuth](#architecture-overview) proposes a durable [Mission](#key-terms) object at the Authorization Server to provide that layer.
If you want the shortest version of what is new here, it is this:
- a [Mission](#key-terms) is a durable delegated-authority object at the AS, not just another token claim
- tokens derive from the Mission; they do not define it
- Mission lifecycle is independent of token lifetime
- Mission state can terminate execution because the business purpose ended, not only because a token expired
- Mission-Bound OAuth is designed to fit existing OAuth, policy, gateway, and FGA deployments rather than replace them
If you are skimming, read these sections first:
- [Introduction](#introduction) for the problem and core claim
- [Architecture Overview](#architecture-overview) for the model in one pass
- [Worked Example: Scheduling Assistant](#worked-example-scheduling-assistant) for the end-to-end flow
- [Deployment Pattern Examples](#deployment-pattern-examples) for how this fits gateways, RS policy, and FGA
- [Practical Limits and Realistic Claim](#practical-limits-and-realistic-claim) for where this architecture can fail
- [Open Questions](#open-questions) for what is genuinely unresolved
What this is not:
- not a claim that OAuth should become a workflow engine
- not a replacement for AuthZEN, FGA, MCP, or GNAP
- not a universal policy language
- not a promise of perfect real-time termination
- not a requirement that every resource server become Mission-aware on day one
# Introduction
OAuth is good at issuing tokens that represent authorization at a moment in time for a client, audience, and scope. Agentic systems need something stronger and longer-lived than that, but also more bounded than standing credentials.
An agent may:
- interpret a user prompt
- plan a multi-step mission
- call multiple APIs over time
- delegate sub-tasks to other software components
- resume work after pauses or human approvals
Standard OAuth primitives do not directly model the delegated authority that makes those actions legitimate as a coherent whole. Access tokens are audience-bound and short-lived by design. Refresh tokens extend continuity, but they do not encode mission purpose, delegation boundaries, or lifecycle state in a way that an authorization server can govern as a first-class artifact.
The core design objective is therefore:
> represent agent authority as an approved, bounded, persistent authorization context tied to mission purpose and lifecycle, rather than as raw credential possession.
This is closer to [power-of-attorney](https://notes.karlmcguinness.com/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/) than to passport-style authentication.
OAuth is increasingly being used in environments where software does more than make a single API call on behalf of a user. Agents plan, pause, resume, delegate, and cross service boundaries. That creates a gap between what existing OAuth tokens represent and what operators actually need to control.
What is missing is not another broad token. What is missing is a durable, governable authority object for a delegated mission:
- approved once
- constrained over time
- checked again at each derivation step
- revocable independently of token expiry
- understandable across audit, policy, and delegation boundaries
Mission-Bound OAuth is an attempt to define that object and show how OAuth can use it without abandoning its existing model. In the terminology of the Power of Attorney series, the [Mission](#key-terms) is the architecture and protocol evolution of what the series called an [Execution Mandate](https://notes.karlmcguinness.com/from-passports-to-power-of-attorney/).
The practical claim is intentionally narrow. This RFC is not arguing that OAuth should become a workflow engine, or that [Mission-Bound OAuth](#architecture-overview) solves all of agent security. The claim is that a durable [Mission](#key-terms) object at the AS would let existing OAuth deployments govern delegated machine authority more safely than token-only patterns do today.
# Terminology and Roles
## Key Terms
| Term | Definition |
|---|---|
| **Mission** | An AS-maintained authorization object representing approved delegated authority for a specific purpose. It has lifecycle state, an authority model, delegation bounds, and can be terminated independently of any token. |
| **Mission Proposal** | A candidate authority description submitted to the AS for review. Not yet authoritative. May be produced from a prompt, intent envelope, MCP tool schema extraction, or structured request. |
| **Approved Mission** | The governance artifact created when a proposal is accepted. Authoritative from a business and governance perspective; holds the lifecycle constraints and audit record of who approved what. |
| **Mission Authority Model** | The machine-evaluable authority representation derived from the Approved Mission. Authoritative for token issuance, token exchange, gateway checks, and RS policy decisions. Stored as a signed, versioned artifact at the AS. |
| **Projected Mission artifacts** | Reduced views of Mission state emitted at protocol boundaries: token claims, introspection responses, cross-domain assertions, audit events. Expose only the minimum data needed at each boundary. |
| **`mission_id`** | The AS-internal authoritative identifier of a Mission record. Not exposed at protocol boundaries. |
| **`mission_ref`** | The projected Mission reference handle used at protocol boundaries: tokens, token exchange, introspection, cross-domain assertions. It is derived from the AS-internal Mission record but is treated as a distinct external handle. |
| **Mission Lifecycle** | The set of states a Mission passes through: `active`, `suspended`, `completed`, `revoked`, `expired`. Missions are created in `active` state upon proposal approval. Enforced by the AS before every token issuance, refresh, token exchange, and delegation event. |
| **Authority Region** | The bounded set of resources, operations, selectors, predicates, and constraints represented inside a Mission Authority Model. |
| **Authority Compilation Boundary** | The architectural boundary between ambiguous intent (prompt or plan) and enforceable delegated authority (Mission Authority Model). Mission approval is the trust boundary between the two. |
| **Intent Envelope** | An intermediate structured representation of what an agent intends to do, derived from prompt interpretation. Proposal input, not authority. |
| **Mission Template** | A pre-registered authority pattern for a common mission category. Templates constrain how proposals become Missions and define the default authority model for a purpose class. |
| **Purpose Class URI** | A URI-form stable identifier for a Mission class, used in client registration, policy rules, and audit. Example: `urn:example.com:mission:schedule-meeting`. |
The design currently assumes the following logical roles:
| Role | Responsibility | Notes |
|---|---|---|
| **User** | Principal on whose behalf authority is granted | Often the subject, but not always the only approver |
| **Agent Client** | Software component that interprets the prompt and executes the Mission | May also be the OAuth client |
| **Orchestration Layer** | Platform or runtime that manages agent execution and calls the Mission proposal endpoint on behalf of the agent | May collapse into the Agent Client in simpler deployments |
| **Authorization Server (AS)** | Stores Mission state and issues OAuth artifacts | Remains authoritative for Mission state used by OAuth |
| **Resource Server (RS)** | API being accessed | May be Mission-aware or Mission-agnostic |
| **Resource Authorization Server (Resource AS)** | In cross-domain deployments, validates Mission assertions and issues audience-specific tokens for a governed trust domain | May differ from the AS that issued the original Mission |
| **Approver** | Human or policy actor that can approve, reject, or attenuate a proposed Mission | Can be user, admin, or policy-driven |
| **Policy Decision Point (PDP)** | Optional policy engine used to evaluate constraints and access conditions | Often integrated via AuthZEN-style interfaces |
Some deployments may collapse these roles. The protocol should not assume they are always distinct implementations.
```text
+----------+
| User |
+---+--+---+
| |
interprets | | (may serve
prompt | | as Approver)
v |
+----------+--+ +----------+
|Orchestration | | Approver |
| Layer | +----+-----+
+------+-------+ |
| |
proposal | approval /
submission | attenuation
| |
+-------+-------+
|
v
+---------+---------+
| Authorization |<------>[PDP]
| Server |
+---------+---------+
|
OAuth auth + token exchange
|
v
+---------+---------+
| Agent Client |
+---------+---------+
|
mission-bound access token
|
v
+---------+---------+
| Resource Server |
+----+----------+---+
| |
cross-domain | introspect (optional)
v v
+--------+--+ +---+-----------+
|Resource | |Authorization |
|Auth Server | | Server |
+-----------+ +---------------+
```
## Subject and Actor Model
IAM readers will reasonably ask "who is who?" because Mission-Bound OAuth spans multiple identity and authority roles that normal OAuth deployments often blur together.
At minimum, the model should distinguish:
- **subject**: the principal on whose behalf authority is being exercised
- **client**: the OAuth client or orchestration layer requesting tokens and Mission operations
- **agent runtime**: the software component performing planning or execution for the client
- **active actor**: the component currently using a derived token at a given hop
- **approver**: the principal or policy authority that can approve, attenuate, suspend, resume, or revoke a Mission
The clean mapping is:
- `sub` identifies the subject on whose behalf the Mission exists
- `client_id` identifies the registered client or orchestration layer
- `act` represents the active delegated actor chain for token-based delegation
- Mission audit events capture the approver or administrative actor that changed Mission state
This matters because approval authority, delegated execution, and audit attribution are different concerns. They should not collapse into a single identifier model.
# Background and Related Work
This document does not appear in a vacuum. It builds on a line of argument developed in the three-part series [You Don't Give Agents Credentials. You Grant Them Power of Attorney](https://notes.karlmcguinness.com/series/you-dont-give-agents-credentials-you-grant-them-power-of-attorney/):
- [Agents Don't Need Your Passport. They Need Your Authority](https://notes.karlmcguinness.com/agents-dont-need-your-passport-they-need-your-authority/) frames the structural gap: identity, access control, and delegation can all work correctly while no layer asks whether a delegated mission should still be running at all.
- [From Passports to Power of Attorney](https://notes.karlmcguinness.com/from-passports-to-power-of-attorney/) introduces the authority-governance model behind this work: purpose, scope, conditions, lifecycle, and delegation as a governed artifact rather than something implied by credentials.
- [Governing the Stay, Not Just the Entry](https://notes.karlmcguinness.com/governing-the-stay-not-just-the-entry/) explains why revocation, continuous evaluation, and business-event termination have to be first-class if delegated machine authority is to remain governable over time.
The series used the term `Execution Mandate` for that governed artifact. This RFC uses `Mission` as the protocol-facing term, but the underlying idea is the same: delegated machine authority must be represented as a governed object, not inferred from token possession alone.
This architecture also sits alongside adjacent work in the identity and authorization ecosystem:
- [RFC 9396: OAuth 2.0 Rich Authorization Requests](https://datatracker.ietf.org/doc/html/rfc9396) for structured authorization input at token issuance time.
- [RFC 8693: OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693) for downstream token derivation and delegation checkpoints.
- [RFC 9635: GNAP](https://www.rfc-editor.org/rfc/rfc9635) as the cleaner-sheet alternative to extending OAuth for richer delegated authorization semantics.
- [OpenID Authorization API 1.0](https://openid.net/specs/authorization-api-1_0.html) as the current AuthZEN decision interface for PDP/PEP integration.
- [OpenID Shared Signals Framework 1.0](https://openid.net/specs/openid-sharedsignals-framework-1_0-final.html) and [OpenID Continuous Access Evaluation Profile 1.0](https://openid.net/specs/openid-caep-1_0-final.html) for state change propagation and event-driven termination.
- [Model Context Protocol](https://modelcontextprotocol.io/specification/) for tool and context discovery above the authority layer.
- [OAuth 2.0 Transaction Tokens](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-transaction-tokens-08) for context propagation in trusted domains.
- [NIST NCCoE Software and AI Agent Identity and Authorization](https://www.nccoe.nist.gov/projects/software-and-ai-agent-identity-and-authorization) as evidence that this problem space is active and recognized outside vendor marketing.
Mission-Bound OAuth is intended to complement, not replace, these efforts. The claim is narrower: existing OAuth and policy building blocks still leave a gap around durable delegated mission governance, and that gap becomes visible once agents operate beyond point-in-time authorization decisions.
The existing authorization landscape provides two models, each useful in its domain:
**OAuth scopes** represent flat permissions at a point in time. They are well-understood, widely deployed, and appropriate for single-request authorization. They have no lifecycle concept and no [Mission](#key-terms) identity.
**Rich Authorization Requests (RAR, RFC 9396)** extend scopes with structured permission objects. They allow richer description of what a token permits at a specific audience. They remain point-in-time: each token request is independent. RAR does not define a persistent authority object, so any deployment that wants one invents private, non-interoperable semantics to do so. [Mission-Bound OAuth](#architecture-overview) is standardizing what a well-designed agentic RAR deployment would build anyway, and making that persistent authority model interoperable across implementations.
**[Mission-Bound OAuth](#architecture-overview)** introduces a third model: durable, governed, lifecycle-aware authority for a [Mission](#key-terms) that outlives any individual token.
The critical properties that distinguish a Mission from the existing models:
- A Mission is created once, before token issuance, through an approval process.
- It persists as a first-class artifact at the Authorization Server across the entire Mission lifecycle.
- Tokens derive authority from the Mission; the Mission does not derive from tokens.
- The Mission can be suspended or terminated independently of whether any token is currently valid.
- Termination can be triggered by a business event, not only by a clock.
- Audit trails attach to the Mission, not to individual tokens, giving a complete record of delegated execution.
> A token answers "is this request permitted right now?" A Mission answers "should this delegated mission be running at all?" These are different questions and the existing stack has no standard answer to the second.
The word "durable" is the load-bearing term. A refresh token is also durable in the sense that it persists, but it is a credential artifact, not a governed authority artifact. A Mission is the authoritative record of purpose, constraints, and lifecycle state that governs every token the mission will ever produce.
# Architecture Overview
Mission-Bound OAuth introduces a new authorization object, the **Mission**, maintained by the AS.
A Mission is the authoritative record of:
- what mission purpose was approved
- what bounded authority region the mission is allowed to operate within
- what constraints, selectors, and predicates govern runtime decisions
- how long the authority lasts
- whether delegation is allowed
- what lifecycle state the mission is in
OAuth tokens do not replace the Mission. They derive authority from it and reference it using a projected Mission reference.
The AS remains the system of record for mission state and evaluates that state before issuing, refreshing, or exchanging tokens.
## From Intent to Enforceable Authority
The most important architectural question is how an open-ended user request becomes something a system can actually enforce.
For open-world agents, the answer cannot be:
- interpret the prompt
- guess the exact future API calls
- whitelist them up front
That only works in tightly closed execution patterns. It does not work well when agents can discover tools dynamically, plan adaptively, encounter new resources at runtime, or operate across large enterprise systems.
The architecture therefore needs an intermediate pipeline:
1. **intent input**: prompt, plan, Model Context Protocol (MCP) tool context, and surrounding mission context
2. **mission proposal**: a candidate authority description for the task
3. **constraint shaping**: templates, policy, approval, attenuation, and business rules narrow the proposal
4. **compiled authority model**: the Mission becomes a machine-evaluable authority region
5. **runtime decision inputs**: issuance, exchange, gateway, and RS checks evaluate requests against that region
This is the real architectural job of a Mission. It is not just a label attached to a token. It is the compilation boundary between ambiguous intent and enforceable delegated authority.
```text
prompt / plan / MCP context
|
v
Mission Proposal
|
v
templates + policy + approval + attenuation
|
v
Approved Mission
|
v
Mission Authority Model
|
+-> token issuance / refresh
+-> token exchange
+-> gateway / RS checks
+-> audit / eventing
```
That boundary is especially important for open-world agents:
- they may not know all resources in advance
- they may discover tools at runtime
- they may need to adapt plans based on what they find
- but they still need bounded authority
Mission-Bound OAuth provides that bounded authority model without requiring every future action to be enumerated up front.
## Authority Compilation Boundary
If Missions are the bridge from open-world intent to enforceable authority, then the architecture needs to say where that bridge is built.
There are at least three plausible models:
- **AS-owned compilation**: the Authorization Server itself compiles proposals, templates, policy bindings, and approval outputs into the authoritative Mission representation
- **mission-management service compilation**: a separate control-plane service compiles the Mission and the AS consumes the result as authoritative input for OAuth processing
- **hybrid compilation**: a management service produces an intermediate form and the AS performs final normalization or binding at approval and issuance time
The current bias is that the AS must remain authoritative for the Mission representation that governs token issuance, refresh, exchange, and lifecycle, even if upstream authoring and compilation are delegated to another component.
That gives the architecture a clean trust boundary:
- authoring may happen elsewhere
- proposal shaping may happen elsewhere
- policy evaluation may be externalized
- but the AS remains authoritative for the Mission artifact that OAuth relies on
**Mission management service trust**: when a separate mission-management service compiles Missions and submits them to the AS, the AS MUST NOT accept the proposed authority model as-is without independent validation. Compromise of the mission-management service must not be equivalent to compromise of Mission governance.
The required trust model:
- the mission-management service signs proposed authority-model artifacts using a key registered with the AS at service registration time
- the AS verifies the signature before accepting any proposed Mission Authority Model artifact
- the AS re-derives and re-validates all policy-bound predicates (lifecycle bounds, delegation limits, purpose class authorization) against its own registered policy at the time of acceptance, regardless of what the submitted artifact asserts
- the AS is the final authority for the Mission Authority Model stored at the AS; a submitted artifact is a proposal input, not an authoritative record
This means a compromised mission-management service can submit invalid proposals, which the AS will reject, but cannot unilaterally create an authorized Mission Authority Model.
## Mission Artifact Stack
The architecture is easier to reason about if the different Mission-shaped artifacts are named explicitly.
| Artifact | Purpose | Authority Status | Typical Consumers |
|---|---|---|---|
| **Mission Proposal** | Candidate authority description produced from prompts, plans, templates, MCP-derived tool hints, and surrounding context | Not yet authoritative; subject to approval and attenuation; may be incomplete or ambiguous | approval systems, policy checks, mission management |
| **Approved Mission** | Governance artifact created once a proposal has been accepted by the relevant approver and policy controls | Authoritative from a business and governance perspective; durable record of who approved what and under which lifecycle constraints | Authorization Server, audit, governance systems |
| **Mission Authority Model** | Machine-evaluable authority representation derived from the Approved Mission | Authoritative for issuance, exchange, gateway, and RS policy decisions | Authorization Server, PDPs, gateways, mission-aware resource servers |
| **Projected Mission artifacts** | Reduced views of Mission state emitted at other protocol boundaries | Boundary-specific projections only; should expose the minimum needed data | tokens, introspection responses, cross-domain assertions, audit events |
The relationship between these artifacts is:
```text
Mission Proposal
-> Approved Mission
-> Mission Authority Model
-> projected artifacts for tokens, introspection, assertions, and audit
```
Another view of the same stack is:
```text
authoring
Mission Proposal
|
v
governance
Approved Mission
|
v
enforcement
Mission Authority Model
|
+-> token claims
+-> introspection
+-> assertions
+-> audit events
```
The same stack is easier to scan if you view it as a transformation pipeline:
```text
[Proposal]
requested authority, still ambiguous
|
v
[Approved Mission]
governance record, durable business authority
|
v
[Mission Authority Model]
machine-evaluable enforcement artifact
|
v
[Projected Artifacts]
boundary-specific token, introspection, assertion, and audit views
```
Or in compact form:
| Stage | Main question answered | Output |
|---|---|---|
| Proposal | "What authority is being requested?" | Candidate authority description |
| Approval | "What authority was actually approved?" | Durable Approved Mission record |
| Compilation | "What can enforcement points evaluate mechanically?" | Mission Authority Model |
| Projection | "What does this boundary need to know?" | Minimal token, introspection, assertion, or audit view |
The AS should remain authoritative for the Approved Mission and the Mission Authority Model forms that govern OAuth processing, even if proposal authoring and compilation are delegated to adjacent services.
### Concrete Compiled Authority Example
To make the compilation concept concrete, consider how the scheduling example compiles from proposal inputs into a machine-evaluable authority region.
Authoring-time inputs:
- intent envelope: `schedule_meeting` for participant Dana, next week
- template: `schedule_meeting` with default constraints
- approval attenuation: limit to EU region, no executive calendars, max 15 minutes duration
The AS-internal Mission Authority Model record (uses `mission_id`; enforcement points receive the signed JWS artifact below, which uses `mission_ref` in place of `mission_id`):
```json
{
"mission_id": "m_92f341aa",
"purpose": "urn:example.com:mission:schedule-meeting",
"authority_model": {
"allowed_resources": [
{ "type": "calendar.event", "selector": "user-manageable-calendars" },
{ "type": "crm.account_note", "selector": "accounts-owned-by-requesting-team" }
],
"allowed_operations": [
{ "group": "calendar.schedule" },
{ "group": "crm.record_activity" }
],
"constraints": [
{ "type": "jurisdiction", "value": "EU" },
{ "type": "business_process", "value": "meeting-coordination" },
{ "type": "exclusion", "resource_attribute": "calendar.is_executive", "value": true }
],
"delegation": { "allowed": true, "max_depth": 0 },
"lifecycle": { "expires_at": 1773069300, "state": "active" }
}
}
```
The signed JWS artifact distributed to enforcement points (PDP evaluation inputs, gateways, mission-aware RSes). `mission_id` is replaced by `mission_ref`; `expires_at` uses NumericDate per JWT conventions (RFC 7519 Section 2); `version` enables cache invalidation:
```json
{
"mission_ref": "mr_7M9Qx2",
"version": 1,
"purpose": "urn:example.com:mission:schedule-meeting",
"authority_model": {
"allowed_resources": [
{ "type": "calendar.event", "selector": "user-manageable-calendars" },
{ "type": "crm.account_note", "selector": "accounts-owned-by-requesting-team" }
],
"allowed_operations": [
{ "group": "calendar.schedule" },
{ "group": "crm.record_activity" }
],
"constraints": [
{ "type": "jurisdiction", "value": "EU" },
{ "type": "business_process", "value": "meeting-coordination" },
{ "type": "exclusion", "resource_attribute": "calendar.is_executive", "value": true }
],
"delegation": { "allowed": true, "max_depth": 0 },
"lifecycle": { "expires_at": 1773069300, "state": "active" }
}
}
```
This authority-model form is what the AS evaluates at token exchange time, what a gateway queries at request time, and what an AuthZEN-compatible policy decision point (PDP) uses to answer "is this action on this resource permitted under this mission right now?" The agent does not need to re-enumerate its intended actions; the authority model is the evaluable boundary for whatever the agent discovers at runtime. The scheduling scenario's `mission_id` (`m_92f341aa`) maps to protocol-facing `mission_ref` (`mr_7M9Qx2`); this separation illustrates the AS-internal/protocol-boundary distinction normatively defined in the Mission Identifier and Mission Reference section.
## Architecture Layers
This architecture is intentionally broader than a single OAuth extension. It is easier to reason about if split into layers.
| Layer | Scope | Likely Standardization Target | Representative Capabilities |
|---|---|---|---|
| **Layer 1: Core OAuth Extension Candidate** | Protocol elements most plausibly standardized as the core OAuth contribution | Core OAuth draft/profile | Mission reference in OAuth requests; Mission-bound token semantics; lifecycle enforcement during issuance, refresh, and exchange; delegation bounds in token derivation; resource server processing expectations; sender-constraining requirements |
| **Layer 2: Mission Management Candidate** | Control plane that creates and governs Mission state | Separate management specification or profile | prompt-to-intent interpretation; Mission proposal generation; approval and attenuation; retrieval and management APIs; Mission templates and approval modes; approval UX; enterprise approval integration |
| **Layer 3: Cross-Domain and Operational Profiles** | Optional or deployment-specific capabilities | Companion profiles and deployment guidance | Mission assertions across authorization domains; SSF/CAEP eventing; policy integration; gateway and service mesh enforcement; business-event-driven Mission termination |
The point of this document is to show how the whole system works end to end. It is not a claim that every layer belongs in one standards-track document.
## Candidate Spec Decomposition
This architecture should not be treated as a single protocol surface. The likely standards decomposition is:
- a **core OAuth extension** for Mission reference, lifecycle-aware issuance, and exchange
- a **mission management specification or profile** for proposals, approval, attenuation, and retrieval
- a **cross-domain profile** for mission assertions and trust between authorization domains
- **deployment guidance** for gateways, RS behavior, policy integration, and eventing
This split matters because the end-to-end architecture is broader than what should probably go into a single OAuth specification.
It is also worth being explicit about the architectural choice this series is making. The protocol-facing model here places the Mission at the AS because the goal is to show how Mission governance could fit existing OAuth infrastructure. That is not the only viable system boundary. The deeper governance concept can also be implemented as a separate authority service that the AS consumes. The earlier Power of Attorney series leaned harder in that direction. This document chooses the AS-centered shape because it is the most direct way to make the OAuth implications concrete.
## Design Principle
Agents may interpret prompts, but they do **not** grant authority.
Authority exists only after a Mission has been proposed, reviewed, and approved. This separation is central to the architecture:
- prompt interpretation is probabilistic
- authority grant is governed
- mission approval is the trust boundary between the two
## End-to-End Flow
The intended flow is:
```text
prompt
-> intent interpretation
-> mission proposal
-> approval / attenuation
-> mission created
-> OAuth authorization referencing mission_ref
-> access token issuance
-> token exchange for downstream APIs
```
Viewed by trust boundary, the same path looks like:
```text
[agent / client]
|
| prompt, plan, MCP context
v
[mission management]
|
| proposal, approval, attenuation
v
[authorization server]
|
| mission-bound token
| mission-aware token exchange
v
[gateway / resource servers]
|
| resource operations, audit, policy checks
v
[mission lifecycle state]
active -> suspended / completed / revoked / expired
```
This makes the Mission the stable authority artifact across otherwise separate OAuth transactions.
An end-to-end deployment may therefore involve three linked phases:
1. **Interpretation phase**: prompts or plans become structured candidate authority
2. **Governance phase**: candidate authority is approved, attenuated, or denied
3. **OAuth execution phase**: approved Mission state is referenced by tokens and enforced over time
This should not be confused with ordinary repeated access checks. Continuous authority evaluation is a separate control-plane question: not only whether the next request is permitted, but whether the Mission behind the execution should still be running at all as facts, approvals, and business conditions change over time.
The enforcement picture across those phases looks like this:
| Check / Control | Proposal / approval | Code issuance | Token issuance / refresh | Token exchange | Gateway / RS | Cross-domain projection |
|---|---|---|---|---|---|---|
| Mission lifecycle state | Created or denied | Checked | Checked | Checked | Often checked via introspection or cached state | Reflected at issuance time only unless live state is shared |
| Mission Authority Model bounds | Compiled | Bound to resulting flow | Enforced | Enforced | Enforced directly or via policy inputs | Reduced to projected facts |
| Delegation depth | Declared | N/A | N/A | Enforced atomically | Usually observed, not advanced | Visible only as projected chain state |
| Verified actor binding | Approved actor context anchored | Bound to code / initial flow | Bound to issued token | Re-verified | Usually indirect unless Mission-aware | Degrades to projection unless introspection is available |
| Business-event termination | Declared as terminal condition | N/A | Checked against current state | Checked against current state | Propagated through introspection / eventing | Stale unless the receiving domain has live state |
## Relationship and External Data Sources
The architecture now relies on relationship predicates such as:
- accounts owned by the requesting team
- participants connected to the approved Mission
- calendars the subject is allowed to manage
- records in a specific business process stage
Those predicates depend on external systems of record. Mission evaluation may therefore depend on one or more relationship sources:
- directory or identity graph
- FGA or graph authorization system
- CRM ownership model
- business-process state system
- HR hierarchy
- tenant or organization membership directory
Mission-Bound OAuth does not replace those systems. It carries the governing Mission context that tells the PDP which relationship predicates matter for the current Mission.
# Mission Data Model
## Mission Object
The current model treats a Mission as a persistent authorization context. For interoperability, the eventual spec work should distinguish a **minimal core** from **deployment-specific extensions**.
The minimal interoperable core is roughly:
- `mission_id`
- `mission_state`
- `mission_subject` (required sub-fields: `sub`, `iss`; strongly recommended: `sub_profile`)
- `mission_client`
- `created_at`
- `mission_exp`
- `delegation`
Deployment-specific extensions may include:
- `purpose`
- structured `constraints`
- compiled authority graph or selector set
- enterprise approval metadata
- mission-specific attributes
- policy evaluation hints
A sketch of the object shape:
```json
{
"mission_id": "m_7M9Qx2",
"mission_state": "active",
"mission_subject": {
"sub": "user-123",
"iss": "https://idp.example.com",
"sub_profile": "user"
},
"mission_client": {
"client_id": "assistant-agent"
},
"mission_exp": 1773072000,
"delegation": {
"allowed": true,
"max_depth": 1,
"current_depth": 0
},
"purpose": {
"class": "urn:example.com:mission:schedule-meeting",
"display": "Schedule a Meeting"
},
"authority_model": {
"operation_groups": [
"calendar.schedule",
"crm.record_activity"
],
"selectors": [
{
"resource_type": "calendar.event",
"selector": "user-manageable-calendars"
},
{
"resource_type": "crm.account_note",
"selector": "accounts-owned-by-requesting-team"
}
],
"predicates": [
{
"type": "jurisdiction",
"value": "EU"
},
{
"type": "business_process",
"value": "meeting-followup"
},
{
"type": "exclusion",
"resource_attribute": "calendar.is_executive",
"value": true
}
]
},
"constraints": {
"max_duration": 900,
"rate_limit": {
"count": 10,
"window": 60
},
"checkpoint_policy": "hybrid"
},
"created_at": "2026-03-13T09:00:00Z"
}
```
This example is AS-internal. `mission_exp` uses NumericDate format (integer seconds since Unix epoch) consistent with the JWT `exp` claim convention defined in RFC 7519 Section 4.1.4. `created_at` remains an internal timestamp field here. Protocol projections should use the explicit field formats defined for the relevant projection rather than assuming this internal object shape is emitted directly.
The AS owns this record. Tokens and exchanges are derivatives of it, not substitutes for it.
The proposal and the Mission are separate artifacts. A Mission Proposal is the requested authority object submitted for review. Approval creates an Approved Mission artifact with its own `mission_id`, lifecycle, authority model, and audit trail. This matters for the API surface and for governance:
- `GET /mission-proposals/{proposal_id}` returns the requested authority object and its review state
- `GET /missions/{mission_ref}` returns the approved authority artifact that governs OAuth processing
- denial and attenuation history belong to the proposal record even when no Mission is ever created
At minimum, `mission_id` should be opaque and unguessable. If exposed outside the AS, it should be treated as a reference handle, not as a container for semantics.
## Mission Identifier and Mission Reference
The architecture is cleaner if it distinguishes the AS-internal Mission identifier from the protocol-facing Mission reference.
- **`mission_id`**: the authoritative identifier of the Mission record inside the issuing AS
- **`mission_ref`**: the projected reference handle used at protocol boundaries such as tokens, token exchange, introspection, and cross-domain assertions
In the simplest deployment, `mission_ref` may be a direct opaque projection derived from `mission_id`. In privacy-sensitive or cross-domain deployments, it often should be pairwise or otherwise distinct per boundary.
That distinction separates three concerns that would otherwise get overloaded into one value:
- record identity at the AS
- protocol selection during token exchange and introspection
- correlation visibility at resource servers or other external parties
The practical model is:
- the AS stores and governs the Mission by `mission_id`
- clients and downstream systems work with `mission_ref`
- the AS maps `mission_ref` back to the internal `mission_id`
This is the recommended direction for the eventual specs. The core protocol should standardize the existence of a projected Mission reference. Whether the claim name remains `mission_id`, becomes `mission_ref`, or is profile-dependent can still be decided later.
This distinction is normative. The `mission_id` MUST NOT appear in tokens, authorization requests, introspection requests or responses, cross-domain assertions, or error responses. It MUST NOT appear in any artifact distributed outside the AS boundary, including the signed Mission Authority Model JWS artifact that enforcement points receive (which MUST use `mission_ref` in place of `mission_id`). All protocol-facing and enforcement-point-facing uses MUST use `mission_ref`. The only contexts where `mission_id` may appear are: (1) AS-internal data stores and records, and (2) examples in this document that explicitly label the content as AS-internal representation.
For open-world deployments, the important part of the Mission may not be a static list of resources or operations. It may be the compiled set of selectors, predicates, relationship conditions, and policy bindings that define the bounded authority region for runtime decisions.
> A Mission is not just a record of approved intent; it is the authoritative Mission Authority Model for a delegated mission.
## Mission Authority Model as a Stored, Versioned Artifact
The Mission Authority Model is a stored, content-addressed artifact maintained by the AS. It is not recomputed on demand at each evaluation point. This is a deliberate architectural decision driven by audit and enforcement requirements.
The audit trail depends on replayability: the AS must be able to reconstruct exactly what authority governed any given token issuance or exchange decision. That requires a stored, versioned representation of the compiled authority at the moment of issuance. A computed-on-demand model cannot provide that guarantee because the inputs to compilation (templates, policy, registered predicates) may change between the time of issuance and the time of audit replay.
The AS always stores the Mission Authority Model for audit and issuance purposes. Distribution of that full model beyond the AS is optional and should be treated as a higher-assurance profile rather than the default deployment expectation.
In the default adoption path:
- the AS uses the Mission Authority Model internally for issuance, refresh, exchange, and lifecycle checks
- gateways and RSes use projected claims or the Mission introspection profile
- only designated high-trust enforcement points retrieve the full signed Mission Authority Model
High-assurance deployments may distribute the Mission Authority Model to trusted enforcement points rather than computing it locally:
- the AS issues the Mission Authority Model as a signed artifact alongside the Approved Mission
- enforcement points (gateways, PDPs, resource servers that do full Mission evaluation) retrieve or receive the Mission Authority Model and validate its signature
- cache staleness bounds for the Mission Authority Model at enforcement points must be policy-defined; enforcement points must re-fetch when staleness bounds are exceeded or when a revision event arrives via SSF/CAEP
When a Mission is amended or re-compiled (for example, when the AS updates a predicate in response to a business-graph change), the AS increments a version counter and issues a new signed Mission Authority Model. Enforcement points that cached a prior version must treat a version mismatch as a cache miss and re-fetch. This is the mechanism that keeps distributed enforcement consistent with the AS's authoritative record.
**Cache coordination between Mission Authority Model and introspection**: enforcement points maintain two separate caches: the signed Mission Authority Model artifact and the RFC 7662 introspection response. These must be treated as coherent: a Mission Authority Model version mismatch (detected by comparing the `version` field in a newly fetched artifact against the cached version) MUST be treated as equivalent to an introspection cache miss, triggering a fresh introspection call before any further enforcement decision. The recommended ordering on version mismatch: (1) re-fetch Mission Authority Model, (2) re-introspect to confirm `active: true` and obtain current `mission_state`, (3) enforce against the updated artifact. An enforcement point MUST NOT apply a new Mission Authority Model version while holding a stale introspection `active: true` response from before the version increment.
> The Mission Authority Model is an artifact the AS signs and enforcement points obtain, not a function enforcement points compute locally.
**Mission Authority Model retrieval endpoint**: in deployments that use distributed authority-model enforcement, enforcement points that need to obtain or refresh the Mission Authority Model do so via a dedicated endpoint at the AS. The endpoint pattern is:
```text
GET /missions/{mission_ref}/authority-model
Authorization: Bearer
```
The AS returns the signed JWS artifact. The `enforcement_credential` is a client credentials access token with scope `mission:authority-model:read`, issued to the enforcement point's registered client identity. Enforcement points cache the artifact using standard HTTP caching semantics; the AS MUST include appropriate `Cache-Control` headers. The AS also pushes revision notifications via SSF when the Mission Authority Model version increments, allowing enforcement points to proactively re-fetch. An enforcement point that cannot reach the retrieval endpoint MUST fail closed: it MUST treat the Mission as if it were `revoked` until it can retrieve a fresh artifact.
**Signing format**: the Mission Authority Model is signed as a JWS (RFC 7515) JSON object using the AS's token signing keys. The signing keys are discoverable via AS metadata (RFC 8414) under the `jwks_uri` field. The signed artifact MUST include the following JOSE header claims:
- `typ`: `"mission+json"` (a new media type candidate for registration)
- `alg`: the same algorithm family used for the AS's access token signing (e.g., `RS256`, `ES256`)
- `kid`: key identifier for signature validation
The payload is the Mission Authority Model JSON object including `mission_ref`, `version`, `authority_model`, `lifecycle`, and `delegation` fields. Trusted internal enforcement points MAY maintain a local mapping from `mission_ref` to the AS-internal `mission_id`, but the distributed artifact itself uses only `mission_ref` at its protocol boundary. Enforcement points validate the JWS signature against the AS's published `jwks_uri` before consuming any Mission Authority Model data. A Mission Authority Model with an invalid or unverifiable signature MUST be rejected and treated as if the Mission were revoked.
## Authority Model Distribution
The architecture needs a clear trust boundary for who is allowed to receive the full Mission Authority Model versus a filtered projection.
The recommended distribution model is:
| Caller Class | Allowed View | Typical Use |
|---|---|---|
| **Authorization Server** | Full Mission record and full Mission Authority Model | authoritative issuance, exchange, lifecycle, audit |
| **PDP acting for the AS or gateway** | Full Mission Authority Model or a minimally filtered evaluation view | runtime policy evaluation |
| **Gateway** | Full Mission Authority Model only when it is a designated enforcement point | high-value request enforcement, distributed checkpointing |
| **Mission-aware RS** | Filtered introspection view by default; full model only in high-assurance profiles | request-time authorization decisions |
| **Baseline RS** | Projected Mission reference only | audit and correlation |
| **Cross-domain AS** | Projected Mission assertion only | local re-evaluation and local token issuance |
The default should be conservative:
- full Mission Authority Model distribution is for trusted enforcement points, not every resource server
- filtered introspection is the default RS-facing mechanism
- projected token claims are the minimum distribution mechanism
This is the default trust pattern for the architecture: trusted enforcement points may receive the full Mission Authority Model; resource servers default to filtered introspection or token-level projections.
This keeps the architecture aligned with its own privacy model. A deployment that distributes the full Mission Authority Model broadly should be treated as a high-assurance or tightly trusted profile, not as the default interoperability expectation.
## Stable vs Dynamic Evaluation
One boundary worth making explicit is which parts of Mission evaluation are stable versus dynamic.
| Category | Examples | Changes How? |
|---|---|---|
| **Stable Mission data** (persisted in Approved Mission and Mission Authority Model) | mission identity, purpose, delegation bounds, compiled selectors, operation groups, compliance predicates, expiry, checkpoint policy | Only via amendment or revocation + re-issuance; each change produces a new versioned Mission Authority Model |
| **Mission lifecycle state** (durable AS-maintained state) | `active`, `suspended`, `completed`, `revoked`, `expired` | Transitions over time; affects all downstream decisions without re-authoring the Mission (`pending` is a proposal lifecycle concept, not a Mission state) |
| **Dynamic evaluation inputs** (supplied at runtime) | current resource instance, current actor, current mission stage, current region, current business relationship state, external policy state | Change without modifying the Mission; evaluated at each decision point |
The reason this distinction matters is that not every denial means the Mission changed. A request may be denied because the runtime facts no longer satisfy the predicates of an otherwise unchanged Mission Authority Model.
## Resource and Action Model
Simple string scopes are too weak for the authority model this architecture requires. The current direction is to use typed resources and actions.
Example:
```json
{
"resource": {
"type": "calendar",
"id": "primary"
},
"action": {
"name": "create_event",
"group": "write"
}
}
```
This helps with:
- container-scoped operations
- action grouping
- object-level permissions
- extensibility over time
- compatibility with structured policy systems
This also aligns better with modern policy engines than raw space-delimited scopes.
The likely protocol consequence is that a core spec should standardize only enough structure to make policy evaluation interoperable, while leaving detailed resource taxonomies to deployment profiles.
## Scaling the Resource and Operation Model
This becomes much more important in real systems than it first appears. Real deployments do not have one API and one action. They have:
- containers and objects
- object hierarchies
- bulk operations
- derived operations
- tenant boundaries
- environment-specific identifiers
- operation families that map poorly to raw OAuth scopes
A usable architecture therefore needs a model that can scale beyond flat strings such as `calendar.write`.
The likely shape is:
- **resource type**: what kind of thing is being acted on
- **resource instance or selector**: which thing or set of things is in scope
- **action name**: the concrete operation
- **action group**: a stable grouping used by policy and approval UX
- **attributes**: optional properties used for finer evaluation
A sketch:
```json
{
"resource": {
"type": "calendar.event",
"container": {
"type": "calendar",
"id": "primary"
},
"selector": {
"owner": "user-123",
"time_range": "2026-03-13/2026-03-20"
}
},
"action": {
"name": "create",
"group": "write"
}
}
```
The point is not to standardize every domain taxonomy. The point is to give Mission-Bound OAuth and related policy systems enough structure to express container-scoped authority, object-level authority, grouped approvals, and policy evaluation on typed entities.
## Resource Selectors as an Abstraction
The architecture likely needs the equivalent of XPath, CSS selectors, or jQuery selectors for resources, but not as a single universal syntax.
What it needs is a **resource selector abstraction**: a standard place in the Mission model to describe "which resources or resource sets are in scope" without forcing every deployment into one selector language.
In practice, selectors may need to express things like:
- calendars the subject can manage
- CRM accounts owned by the current team
- documents in a folder but not under legal hold
- records in a given jurisdiction
- tickets connected to a specific business process stage
Those are not all the same kind of selection. Some are:
- attribute filters
- relationship predicates
- container-scoped selectors
- policy references
- graph traversals
The architecture should therefore treat selectors as an abstraction with multiple possible forms, for example:
```json
{
"resource_type": "crm.account_note",
"selector": {
"kind": "relationship",
"relation": "accounts-owned-by-requesting-team"
}
}
```
```json
{
"resource_type": "calendar.event",
"selector": {
"kind": "attribute_filter",
"where": {
"calendar.owner": "user-123",
"calendar.region": "EU"
}
}
}
```
```json
{
"resource_type": "document",
"selector": {
"kind": "policy_ref",
"ref": "policy://missions/docs/editable-by-subject-team"
}
}
```
This keeps the model flexible enough for AuthZEN, Cedar, Rego, FGA, and deployment-specific policy systems without prematurely standardizing a single resource query language.
The important standardization boundary is not the selector syntax. It is the existence of a selector slot in the Mission model and the expectation that Mission Authority Models may carry resource selectors, not just literal resource IDs or flat allowlists.
## Key Abstraction Slots
Resource selectors are not the only place where the architecture needs a stable slot without prematurely standardizing a full language.
Several parts of the Mission model have the same shape:
- the architecture needs a place to carry meaning
- deployments will want different underlying syntaxes or policy engines
- the first spec should standardize the slot and its role before trying to standardize every semantic language
The main abstraction slots are:
| Abstraction Slot | What It Expresses | Likely Backing Models |
|---|---|---|
| **resource selectors** | which resources or resource sets are in scope | attribute filters, relationship predicates, policy refs, graph selectors |
| **operation groups** | stable approval and policy groupings for actions | deployment taxonomies, API-specific action families |
| **relationship predicates** | subject/resource or actor/resource relationships that affect authority | FGA graphs, directories, CRM ownership models, HR hierarchies |
| **process-state predicates** | business workflow or case state that gates authority | BPM systems, ticketing state, CRM stage, case lifecycle |
| **compliance constraints** | jurisdiction, residency, legal, or regulatory boundaries | policy engines, data classification systems, residency controls |
| **authority envelopes** | the bounded region inside which open-world expansion is allowed | Mission Authority Model, policy bindings, approval attenuations |
| **terminal conditions** | which events or states suspend, complete, or revoke a Mission | SSF/CAEP events, business system events, local lifecycle rules |
| **checkpoint policies** | when synchronous checks are required versus when signal-based continuity is acceptable | risk policies, action classes, operational profiles |
| **projection policies** | what Mission data may be exposed in tokens, introspection, assertions, or audit | privacy rules, audience filtering, trust-domain rules |
| **explanation reasons** | machine-readable and human-readable reasons for denial, attenuation, suspension, or revocation | policy reason codes, audit schemas, admin UX models |
This is an important discipline for the eventual specs. If Mission-Bound OAuth tries to standardize all of these semantic languages in one step, it will become too broad to be practical. If it names none of them, deployments will fill the gaps with incompatible private conventions.
The practical middle ground is:
- standardize the slots and processing checkpoints first
- standardize only the smallest common structures needed for interoperability
- leave richer selector, predicate, and explanation languages to profiles or adjacent specifications
# Mission Lifecycle
## Proposal Lifecycle vs Mission Lifecycle
The Mission Proposal and the Mission are separate artifacts with separate lifecycles. The Mission Proposal lifecycle governs what happens before an authorized Mission exists:
```text
+---------+
| pending |<-- proposal submitted
+----+----+
|
+---------+---------+
| |
approve deny / timeout
| |
v v
+---------+ +----------+ +----------+
| Mission | | denied | | expired |
| created | +----------+ +----------+
+---------+ (terminal) (terminal)
```
`denied` is the terminal state when a proposal is explicitly rejected by the approver. `expired` is the terminal state when the proposal approval window elapses without a decision. Both are terminal on the proposal object. Neither creates a Mission. Denial and timeout history are retained on the proposal record for audit even when no Mission is ever created.
The Mission lifecycle begins at activation and is independent of the proposal lifecycle:
## Mission Lifecycle
The current lifecycle model is:
```text
task done /
client signals /
business event
+--------+ |
+---------->| active |-----+-------> completed
| +---+----+
resume |
| suspend / anomaly / precondition change
| |
| v
| +-----------+
+---------| suspended|
+-----+-----+
|
revoke / expire
|
+------------+------------+
| |
v v
+---------+ +---------+
| revoked | | expired |
+---------+ +---------+
```
Missions are created in `active` state when a proposal is approved. For policy-auto-approved missions, activation is immediate. There is no Mission-level `pending` state; pending is a proposal lifecycle concept.
Valid terminal states are `completed`, `revoked`, and `expired`. `suspended` is not terminal: it requires an explicit resumption event to return to `active`, or a revocation or expiry event to terminate.
In the architecture, lifecycle enforcement applies before:
- authorization code issuance
- access token issuance
- refresh token use
- token exchange
- delegation events
The important property is that mission authority can be suspended or terminated independently of token possession.
**Terminal state invariant**: The states `completed`, `revoked`, and `expired` are terminal. An AS MUST NOT accept any lifecycle transition request targeting a Mission in a terminal state. A `completed` or `revoked` Mission cannot be resumed, re-activated, or amended.
**`completed` vs `revoked` semantics**: both states block all further token issuance, refresh, and exchange. The behavioral difference is in the error code returned and in audit interpretation. `mission_completed` is returned when the Mission ended normally (purpose achieved or business event signaled completion). `mission_revoked` is returned when the Mission was terminated by an authorized party before purpose was achieved or due to policy enforcement. Clients receiving `mission_completed` know the mission succeeded; clients receiving `mission_revoked` know it was externally terminated. Both require a new Mission for any further execution.
## Lifecycle Ownership
Mission lifecycle transitions are IAM governance actions and should not be left implicit.
The transition model should distinguish who is allowed to perform which action:
- **proposal submitter / client**: may request creation and may signal completion when permitted by policy
- **subject**: may approve, deny, or revoke Missions that are acting on their behalf when policy allows
- **administrator**: may approve, attenuate, suspend, resume, revoke, or terminate Missions within administrative scope
- **policy engine**: may auto-approve, suspend, or revoke according to registered rules
- **business event processor**: may transition a Mission to `completed` or `revoked` when declared terminal conditions are met
At a minimum, the architecture should assume:
- creation authority does not imply approval authority
- execution authority does not imply expansion authority
- suspension and revocation are privileged lifecycle actions
- business-event termination is authorized only when the Mission declared that event source as authoritative
**Suspended state** deserves explicit treatment. Suspension is not revocation. It is a temporary hold on execution, typically triggered when a human review is required mid-mission, an anomaly is detected, or a precondition the mission depended on changes. When a mission is suspended:
- token exchange is denied until the mission returns to active
- in-flight requests that have already received tokens run to their token expiry unless active invalidation is applied
- the agent should receive a structured error from the exchange endpoint indicating the suspension, not a generic rejection
- resumption is authorized by the same class of principals that can approve the original Mission: the subject (for user-delegated Missions), an administrator within scope, or a policy engine that declared the suspension condition resolved
- the resumption API follows the same endpoint pattern as approval: `POST /mission-proposals/{proposal_id}/approval` for pending Missions maps to a `POST /missions/{mission_ref}/resume` for suspended Missions, requiring the same authentication and authorization as the original approval action
- the AS re-evaluates the Compiled Mission's validity at resumption time; if conditions have materially changed during suspension (for example, a key policy predicate is no longer satisfiable), the AS MAY decline resumption and transition to `revoked` instead
- the AS MUST emit a `mission.resumed` audit event on successful resumption
- **Maximum suspension duration**: a suspended Mission MUST NOT remain suspended indefinitely. The AS MUST enforce a maximum suspension window that is either: (a) declared in the Mission Authority Model at approval time (`max_suspension_seconds`), or (b) a deployment-default suspension limit defined in AS policy. When the maximum suspension window elapses without a resumption event, the AS MUST automatically transition the Mission to `revoked` and MUST emit a `mission.revoked` audit event with reason `suspension_timeout`. Deployments MUST configure an explicit maximum suspension window. An unbounded suspension window is not permitted, as it produces orphaned authority objects with indeterminate audit trails.
Suspension is the primary mechanism for human-in-the-loop intervention after execution has started. Its behavior, error codes, and resumption flow should be normatively defined in the core spec rather than left to implementation.
**Business event termination** is the mechanism that closes the gap the series identified. The CFO scenario (agent still pulling financials at 2:05 PM, five minutes after the board approved the presentation) fails because business completion events do not flow into the authority layer. Mission-Bound OAuth addresses this by making business events first-class inputs to Mission lifecycle transitions.
A Mission may define terminal conditions tied to business events: a procurement program is cancelled, a renewal conversation closes, a task is marked complete in a ticketing system. When those events fire, the Mission transitions to `completed` or `revoked` regardless of how much time remains on the clock. The mechanism for delivering those events is SSF/CAEP, described in the operational model section.
This is the architecture's continuous authority evaluation loop: authority is re-evaluated not only at issuance and exchange time, but also when external events change whether the Mission should continue at all. The point is not only to stop future token issuance, but to give enforcement points enough signal to halt execution that is no longer authorized to continue.
**Business event integrity and ordering** must also be treated as part of the lifecycle model, not as an implementation afterthought. Event-driven termination is only trustworthy if the AS can distinguish authoritative events from stale, duplicated, replayed, or out-of-order ones. A duplicated `task.completed` event should be idempotent; a delayed `task.reopened` event arriving after revocation should not silently reactivate a terminal Mission; an event from an unregistered source should have no lifecycle effect at all.
The architectural expectations are:
- each terminal condition is bound to one or more registered event sources the Mission declared as authoritative at approval time
- lifecycle events are processed idempotently using event identifiers or equivalent replay-detection state at the AS
- the AS records event time and processing time separately so out-of-order delivery is visible in audit
- terminal lifecycle transitions remain terminal even if a later event would otherwise imply an earlier state
- deployments that cannot establish event authenticity and replay protection for a given source SHOULD NOT allow that source to drive Mission termination directly
This is the same trust boundary discipline applied elsewhere in the architecture: external systems may supply lifecycle-driving facts, but the AS remains authoritative for how those facts are validated and turned into Mission state transitions.
**Refresh token and in-flight token behavior** must be addressed explicitly. Refresh tokens issued under a Mission are derivatives of that Mission, not independent credentials. The AS MUST record the `mission_id` associated with any refresh token at the time of issuance. This association is an AS-internal record and MUST NOT rely on the client to supply it at refresh time. When a Mission transitions to `revoked`, `suspended`, or any terminal state, the AS MUST immediately invalidate all refresh tokens associated with that `mission_id` using RFC 7009 token revocation semantics. This applies regardless of the refresh token's remaining lifetime. Allowing a client to exchange a Mission-linked refresh token for a new access token after Mission revocation defeats the lifecycle guarantee the architecture is designed to provide.
For already-issued short-lived access tokens still in the client's possession when a Mission terminates, the architecture cannot retroactively invalidate bearer tokens without active revocation infrastructure. The recommended approach is twofold: keep access token lifetimes short to minimize the exposure window, and emit a CAEP termination event to enforcement point subscribers who can block the token at a gateway or policy enforcement layer. Deployments that require immediate invalidation for all in-flight tokens should use DPoP or mTLS so a stolen or post-revocation token cannot be used without the bound key.
**Mission lifecycle error codes** must be normatively defined in the core spec. When token exchange or refresh fails due to Mission state, the AS should return a structured error that allows the agent to take a meaningful action rather than treating all rejections identically.
| Error Code | Condition | Agent Behavior |
|---|---|---|
| `mission_suspended` | Mission is temporarily held | Halt execution; await resumption signal before retrying |
| `mission_revoked` | Mission permanently terminated | Stop execution; a new Mission is required |
| `mission_completed` | Mission reached its intended terminal state normally | Execution is complete; no further action required |
| `mission_expired` | Mission lifetime elapsed | Stop execution; a new Mission is required |
| `mission_delegation_depth_exceeded` | Requested delegation would exceed `max_depth` | Reject delegation request; escalate or abort |
| `mission_authority_exceeded` | Requested resource or action outside compiled authority region | Abort this action; do not retry with same Mission |
| `mission_not_found` | No Mission matching the provided `mission_ref` | Verify `mission_ref` or request a new Mission |
| `mission_ref_required` | Multiple active Missions exist for this client and no `mission_ref` was provided in `authorization_details` | Include the correct `mission_ref` in `authorization_details` and resubmit |
| `mission_expansion_conflict` | An expansion proposal was evaluated against a prior Mission version; a concurrent expansion was already accepted | Re-fetch the current Mission state and re-evaluate the expansion against the updated version before resubmitting |
These values appear in the `error` parameter of the token error response per RFC 6749 Section 5.2, with a `mission_ref` field in the error response body echoing back the projected Mission reference the client passed. The error response uses `mission_ref`, not the AS-internal `mission_id`, because the client has no visibility into the internal identifier. The distinction between `mission_suspended` (temporary, agent waits) and `mission_revoked` (permanent, agent stops) is operationally critical and must not be collapsed into a single error code.
**Structured error detail for `mission_authority_exceeded`**: this error code alone is insufficient for client recovery because the agent cannot determine which constraint check failed without additional information. When returning `mission_authority_exceeded`, the AS SHOULD include a `mission_error_detail` object in the error response body:
```json
{
"error": "mission_authority_exceeded",
"mission_ref": "mr_7M9Qx2",
"mission_error_detail": {
"constraint_violated": "resource_type",
"detail": "resource type 'finance.report' is not in the compiled authority region"
}
}
```
The `constraint_violated` field uses one of the following machine-readable values:
- `resource_type`: the requested resource type is outside the compiled authority
- `operation_group`: the requested operation is outside the compiled authority
- `relationship_predicate`: a relationship constraint failed (e.g., the account is not owned by the requesting team)
- `compliance_constraint`: a compliance or jurisdiction constraint failed
- `lifecycle_constraint`: a lifecycle-based authority condition in the compiled authority region failed (e.g., the business process stage is not in the approved set, or a time-window constraint in the compiled authority has elapsed). This value is ONLY used within `mission_error_detail` for `mission_authority_exceeded` responses. It does not overlap with the Mission lifecycle state error codes (`mission_suspended`, `mission_revoked`, `mission_expired`) which are returned as top-level `error` values, not as `constraint_violated` values.
The `detail` field is a human-readable diagnostic string intended for logging, administrative display, and debugging. It MUST NOT be machine-parsed; its format is not normative and may change between AS versions. Clients MUST NOT implement logic that branches on the content of the `detail` field. The AS SHOULD provide the `detail` string in the language indicated by the request's `Accept-Language` header when that header is present. Only `constraint_violated` is the machine-readable discrimination signal; `detail` is explanatory context for human operators.
These values allow the agent to determine whether to abort, request a Mission expansion, or attempt a different resource.
## Revocation Semantics Across Derivatives
Mission revocation has to be stronger than "stop issuing new tokens."
Three derivative classes matter operationally:
- **refreshable credentials**: refresh tokens and equivalent long-lived token derivation handles
- **new exchanges**: token exchange requests for new audiences or delegation hops
- **already-issued short-lived access tokens**: existing audience-bound tokens still in circulation
The expected behavior is:
- Mission revocation or suspension immediately blocks refresh and exchange
- associated refresh tokens are revoked or made unusable
- already-issued short-lived access tokens remain usable only until expiry unless active invalidation infrastructure blocks them earlier
- high-assurance deployments use sender-constraining plus gateway or event-driven invalidation to reduce the post-revocation exposure window
This makes the lifecycle story operationally honest: Mission governance is authoritative, but immediate stop across all already-issued tokens still depends on the enforcement profile.
**Mission amendment and narrowing** are distinct from expansion and require an explicit position. The architecture currently defines expansion (adding within existing bounds) as a first-class operation via expansion proposals. The inverse, narrowing an active Mission's authority region, shortening its expiry, or reducing delegation depth, is not yet defined. The two candidate models are: (1) a formal Mission amendment endpoint where authorized principals can tighten constraints on an active Mission without revocation; (2) revocation and re-issuance as the only path for any structural change. The revocation + re-issuance model avoids consistency problems between the AS's Mission record and any cached compiled authority at enforcement points. The amendment model is operationally more convenient for long-running Missions. This is an open design question for feedback.
**OIDC session lifecycle and Mission lifecycle**: for user-delegated Missions, when a user's OIDC session is revoked (CAEP `session_revoked` event, logout, or device credential revocation), active Missions for that user should be affected. The natural default is suspension: all active user-delegated Missions for the revoked session are suspended, giving the orchestration layer an opportunity to cleanly unwind in-flight execution before the Missions are terminated. Deployments with stricter requirements may choose immediate revocation. Regardless, the session-to-Mission propagation behavior should be defined explicitly per deployment profile rather than left to AS-specific behavior.
## Concurrent Missions
The architecture assumes concurrent active Missions are permitted. A client may hold multiple active Missions for the same user simultaneously, each representing a distinct delegated workflow running in parallel.
Concurrent Missions reflect how real agentic deployments work: an assistant agent may be executing a "schedule meeting" workflow while simultaneously running a "prepare expense report" workflow for the same user. Requiring the first to complete before the second can be authorized would unnecessarily serialize independent workflows and force agents to queue tasks rather than interleave them.
The security goal is to avoid implicit Mission selection while also reducing how much fragile bookkeeping the client must do.
The preferred runtime model is:
- each primary mission-bound orchestration token is bound to exactly one `mission_ref`
- the orchestration layer keeps one in-flight mission-bound token per active Mission
- token exchange requests either echo that `mission_ref` explicitly or omit it when the subject token is already uniquely bound to one Mission
The AS does not infer Mission context from the requested resource. It infers only from the subject token when that token is already uniquely bound to one Mission. If the client supplies an explicit `mission_ref`, the AS MUST compare it to the subject token's bound Mission reference and reject the request on mismatch. If the subject token does not uniquely identify one Mission and the request omits `mission_ref`, the AS returns `mission_ref_required`.
This is the safer adoption model:
- the client does not have to maintain a separate out-of-band Mission lookup table for every exchange
- the AS remains authoritative for Mission binding
- accidental Mission-context swaps become detectable server-side rather than silently becoming application bugs
A request that references the wrong Mission for a given resource is rejected with `mission_authority_exceeded` or `invalid_grant`, depending on whether the failure is an authority-envelope violation or a Mission-binding mismatch.
## Mission Audit Trail
Every Mission state transition should produce a signed audit event as a first-class artifact:
- `mission.created`
- `mission.activated`
- `mission.delegated` (with delegation target and depth)
- `mission.suspended` (with reason)
- `mission.resumed`
- `mission.expanded` (with prior and updated authority region)
- `mission.completed`
- `mission.revoked` (with revoking principal and reason)
- `mission.expired`
This event log is a compliance artifact that is independent of access logs, token rotation cycles, and AS internal state. It survives token rotation cycles that would otherwise erase execution history. Audit events should be signed by the AS and should include both `mission_id` (for AS-internal correlation) and `mission_ref` (for cross-boundary audit correlation), the transition timestamp, the triggering principal, and the triggering event (clock, business event, user action, or policy signal). External auditors and cross-domain audit systems cannot resolve the AS-internal `mission_id`; the `mission_ref` is the portable correlation handle that links audit records across the AS, resource servers, gateways, and downstream domains. Positioning the audit trail as a produced artifact, not just a logged side effect, is what makes delegated execution attributable and reviewable.
But audit alone is not enough. Mission governance only becomes real when it lands in execution control. If a Mission is suspended, revoked, or reaches a terminal condition, the surrounding runtime has to know how to pause, unwind, terminate, or escalate before irreversible work continues. A perfect audit trail paired with an orchestrator that keeps running is still a governance failure.
# OAuth Protocol Integration
## Worked Example: Scheduling Assistant
To make the architecture less abstract, consider a scheduling assistant that can use Calendar, Email, and CRM tools.
The user says:
> Schedule lunch with Dana next week, send the invite, and log the follow-up in Salesforce.
### Step 0: User authentication
For user-delegated missions, the user must be authenticated before a Mission can be created on their behalf. The **auth-first model** is the primary pattern: the user authenticates at the IdP via an OIDC flow, and the resulting authenticated session provides the identity context the orchestration layer uses when submitting the Mission proposal. The proposal carries the authenticated user's subject identifier, and the AS validates that the orchestration layer's `client_id` is registered to propose missions on behalf of that user.
The approval step may be separate from authentication (the user approves a pending proposal through an asynchronous consent interaction) or collapsed with it (a combined authenticate-and-approve screen). Either path produces the same outcome: an Approved Mission with a verified `mission_subject`.
For headless missions, this step is replaced by client credential authentication at the orchestration layer with no interactive user authentication required.
### Step 1: Tool discovery and planning
The agent uses MCP to discover available tools and their schemas:
- calendar availability search
- calendar event creation
- email send
- CRM note creation
MCP helps the agent understand what actions are possible and what parameters are needed. It does not grant authority to use any of them.
### Step 2: Prompt interpretation and proposal creation
The agent interprets the prompt into an Intent Envelope and a candidate Mission Proposal:
```json
{
"purpose": {
"class": "urn:example.com:mission:schedule-meeting",
"display": "Schedule Lunch with Dana"
},
"template_id": "schedule_meeting",
"subject": {
"sub": "user-123",
"iss": "https://idp.example.com"
},
"candidate_authority": {
"resource_types": ["calendar.event", "crm.account_note"],
"operation_groups": ["calendar.schedule", "crm.record_activity"],
"constraints": {
"relationship": "accounts-owned-by-requesting-team",
"business_process": "meeting-followup",
"region": "EU"
}
},
"parameters": {
"participant": "Dana",
"time_window": "next week"
},
"display": {
"title": "Schedule a Meeting",
"description": "The assistant will find availability and create a lunch meeting with Dana next week, send the invite, and log a CRM note.",
"actions": ["Check calendar availability", "Create calendar event with Dana", "Send email invitation", "Log CRM note"],
"constraints": ["EU data only", "Expires in 15 minutes"]
}
}
```
The proposal is then submitted to the mission-management layer:
```text
POST /mission-proposals
```
### Step 3: Approval and attenuation
The AS or associated control-plane service evaluates the proposal. A user, enterprise policy, or administrator may attenuate it before approval.
For example, policy might allow:
- meeting-related scheduling actions for calendars the user is allowed to manage
- invite sending only to participants connected to the approved meeting mission
- CRM note creation only for accounts owned by the current team
- execution only within a 15-minute mission lifetime
- no further delegation beyond the assistant runtime
- processing only in approved regional and compliance boundaries
The result is an approved Mission record at the AS, which is then projected into protocol-facing flows through a `mission_ref`.
### Step 4: Policy evaluation with AuthZEN
At approval time, and again later during token derivation, the AS may act as a Policy Enforcement Point (PEP) and call an AuthZEN-compatible PDP.
Conceptually, the question is no longer just "is Calendar on the allowlist?" It is closer to:
```json
{
"subject": {
"type": "client",
"id": "assistant-agent"
},
"action": {
"name": "create_event",
"group": "write"
},
"resource": {
"type": "calendar.event",
"container": {
"type": "calendar",
"id": "primary"
}
},
"context": {
"mission_ref": "mr_7M9Qx2",
"purpose": "urn:example.com:mission:schedule-meeting",
"mission_state": "active",
"relationship": {
"participant_in_mission": true
},
"business_process": {
"type": "meeting-coordination",
"stage": "scheduling"
},
"compliance": {
"region": "EU"
}
}
}
```
Mission-Bound OAuth defines when those checks happen. AuthZEN provides one way to externalize the decision.
### Step 5: OAuth authorization and mission-bound token issuance
Once approved, the client starts OAuth authorization referencing the Mission, preferably through `authorization_details`:
```json
{
"authorization_details": [
{
"type": "mission",
"mission_ref": "mr_7M9Qx2"
}
]
}
```
The authorization request should be submitted via Pushed Authorization Request (PAR, RFC 9126). PAR keeps the Mission reference, delegation state, and any additional context out of browser redirect URIs and server logs.
The AS issues a mission-bound token that carries a projected Mission reference but is not itself a portable mission credential.
### Step 6: Token exchange for resource-specific APIs
When the agent actually needs to call Calendar, it uses token exchange. The AS checks:
- the Mission is still active
- the target calendar resource falls inside the Mission's compiled authority region
- the requested operation fits the Mission constraints
- delegation depth has not been exceeded
The AS then issues a Calendar-specific token carrying a projected Mission reference.
The same process happens again for Email and CRM. This is where mission continuity is preserved without issuing one overly broad token up front, and without requiring the system to pre-enumerate every exact object the agent may touch.
### Step 7: Request-time enforcement
At request time:
- a baseline Resource Server may simply validate the access token and log a projected Mission reference
- a gateway may query policy using Mission-derived context
- a mission-aware RS may use introspection or token claims to apply additional checks
This gives a deployment several enforcement points without requiring every backend service to understand the full Mission object.
### Step 8: Mission completion or revocation
When the task completes, or if the user or enterprise revokes it, the Mission state changes.
After that:
- further token exchange fails
- refresh or re-issuance tied to the Mission fails
- already-issued short-lived tokens expire naturally or are cut short through eventing and revocation mechanisms
That is the core difference from ordinary token-centric patterns: the authority object survives across token lifetimes, and can also end independently of them.
## OAuth Protocol Details
Once approved, OAuth requests reference the Mission.
### Provisional primary path: RAR-based embedding
The preferred direction is to reference an approved Mission through `authorization_details`:
```json
{
"authorization_details": [
{
"type": "mission",
"mission_ref": "mr_7M9Qx2"
}
]
}
```
This reuses an existing OAuth extension point and frames the Mission reference as structured authorization input, which seems more likely to be acceptable to OAuth practitioners.
The `"mission"` type identifier requires IANA registration under the Authorization Details Types registry established by RFC 9396 Section 10. The identifier name is a candidate proposed for community feedback and is subject to change before any formal registration is made. Feedback on whether `"mission"` is the right name for this registry entry is one of the explicit questions for this blog RFC.
### Transport: PAR as the default
Authorization requests carrying a mission reference should be submitted via Pushed Authorization Request (PAR, RFC 9126) by default. PAR submits the request body over server-to-server TLS before the browser redirect, keeping `mission_ref`, purpose parameters, and delegation state out of browser redirect URIs, referrer headers, and server logs. For any deployment that takes Mission-Bound OAuth seriously as a security primitive, PAR should be treated as a baseline requirement rather than an optional hardening measure.
For interactive user-delegated Mission flows, PKCE (RFC 7636) is required for public and native clients. For confidential clients using PAR with private_key_jwt or client_secret authentication, PKCE is strongly recommended but not required, because the authorization code is already bound to an authenticated client credential. This aligns with RFC 9700 (OAuth 2.0 Security Best Current Practice). PAR remains required as a baseline for all client types regardless of PKCE applicability.
**Mission reference binding at authorization code issuance**: when the AS receives a PAR request containing `authorization_details` with a `mission_ref`, the AS MUST bind that Mission reference to the resulting authorization code at the time the code is issued. At code exchange time, the client MUST NOT be permitted to present a different `mission_ref` than the one bound to the authorization code. If the code exchange request includes `authorization_details` with a different `mission_ref`, the AS MUST reject the request with `invalid_grant`. This prevents an adversary from substituting a lower-authority Mission reference for a higher-authority one at the final issuance step. The AS-bound Mission reference from the PAR step governs the issued access token regardless of what the client submits at code exchange.
**Artifact binding**: the authorization code, the resulting primary mission-bound access token, and any refresh token issued from that code SHOULD all be bound to the same Mission version at issuance time. The AS should retain an internal binding record linking:
- authorization code -> `mission_ref`
- primary access token `jti` -> `mission_id`
- refresh token handle -> `mission_id`
- Approved Mission version -> Mission Authority Model version
These bindings make substitution, replay, and audit reconstruction failures easier to detect and prevent.
### Authorization request parameter: not recommended
A `mission_ref` query parameter on the authorization redirect (`GET /authorize?mission_ref=...`) is not a viable alternative for production deployments. Mission references appearing in redirect URIs are exposed in browser history, referrer headers, and server logs. PAR was specifically designed to eliminate this exposure. Deployments MUST use PAR for all Mission-referencing authorization requests. The GET parameter form MUST NOT be used.
## Mission-Bound Token Model
An access token issued under Mission-Bound OAuth is still an audience-specific OAuth access token. It is not a portable mission credential. Its special property is that it is derived from an active Mission and can carry a reference to that Mission.
**Initial mission-bound token audience**: the primary mission-bound access token issued at Step 5 (the OAuth authorization flow, before any token exchange) is an orchestration-layer credential. Its `aud` value is the orchestration layer's registered resource URI or the orchestration layer's `client_id`. This is a deployment requirement: the orchestration layer MUST be registered as an OAuth resource at the AS with a discoverable resource URI, OR the AS must support `client_id` as a valid `aud` value for the primary mission-bound token. The deployment profile MUST specify which model is used. Using `client_id` as `aud` is simpler and avoids requiring a separate resource registration for the orchestration layer; using a resource URI is more consistent with RFC 8707 audience binding conventions and is the recommended approach for multi-tenant or cross-domain deployments. Either way, the primary token is not intended for direct RS consumption. RSes receive mission-authority by way of audience-specific tokens produced through subsequent token exchange, each of which carries a `mission_ref` but targets a specific RS audience. This separates the mission-authority credential (held by the orchestration layer) from resource-access credentials (consumed by RSes and gateways).
The Mission reference may appear:
- inside the access token as a `mission_ref` or profile-equivalent claim
- in introspection metadata
- in exchange inputs and outputs
- in audit and event streams
The AS needs to validate Mission state before every downstream issuance decision.
A Mission reference is useful for continuity, but over-exposing it creates privacy and linkability costs.
The provisional standardized claim name for the projected Mission reference in tokens is `mission_ref`. This is consistent with the IANA registration candidate in the JWT Claims Registry section of this document. The AS-internal identifier remains `mission_id` and is never exposed at protocol boundaries. The claim name `mission_ref` is proposed for community feedback and may be adjusted before formal registration, but within this document it is used consistently as the normative name.
Possession of `mission_ref` alone grants no authority. Every use of `mission_ref` MUST be bound to an authenticated client context and, where token-based authority is being exercised, to the sender-constrained token or token-derived subject through which the request is made.
### Composition with audience-bound tokens
When a mission-bound token is issued for a specific resource, it is still an audience-specific access token. The Mission reference claim is additive: it appears alongside, not instead of, the standard audience-specific token claims. The AS composes both: the token is valid for the target audience AND references the Mission from which its authority derives.
This means a mission-bound calendar token and a mission-bound CRM token produced from the same Mission may carry the same or different projected Mission references depending on deployment policy. They still derive from the same internal Mission, but the externally visible handle may be audience-specific. That is how the architecture balances continuity with privacy.
## Sender Constraining
Sender constraining is a core requirement for Mission-Bound OAuth, not optional hardening.
Without it, Mission-reference continuity becomes replay continuity. A stolen mission-bearing token carries both the audience-specific access permissions and a durable handle that links the token to a broader mission authority chain.
DPoP (RFC 9449) or mutual TLS (mTLS, RFC 8705) binding should be stated as a baseline expectation for any mission-bearing token, including both the primary mission-bound access tokens and any tokens produced by mission-scoped exchange. A deployment profile that does not require sender constraining should make that gap explicit and acknowledge the replay risk it accepts.
## Mission Introspection
Mission introspection is defined as an extension of OAuth Token Introspection (RFC 7662) rather than a new endpoint, to allow reuse of existing introspection client libraries and authorization infrastructure.
A Mission-aware AS MUST support Mission introspection at its RFC 7662 introspection endpoint by accepting a `mission_ref` value as the `token` parameter:
```text
POST /introspect
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer
token=mr_7M9Qx2&token_type_hint=mission_ref
```
The AS recognizes the `mission_ref` token type hint and returns Mission state as additional fields in the standard RFC 7662 introspection response:
```json
{
"active": true,
"mission_state": "active",
"purpose": "urn:example.com:mission:schedule-meeting",
"exp": 1773072000,
"delegation": { "max_depth": 1, "current_depth": 0 },
"authority_model": {
"allowed_operations": ["calendar.schedule", "crm.record_activity"],
"constraints": { "region": "EU" }
}
}
```
**Introspection response tiering**: the `authority_model` field in the introspection response exposes high-value Mission enforcement data and MUST NOT be returned to all introspection callers. The AS MUST require the introspection client to present an access token with scope `mission:introspect:authority` (or an equivalent deployment-defined scope) to receive the `authority_model` section. A caller presenting only a basic introspection credential receives `active`, `mission_state`, `purpose`, `exp`, and `delegation` but not `authority_model`. This prevents RSes that are authorized to check token validity from receiving the full authority region of every Mission they encounter.
**Tenant-scoped introspection**: Mission introspection MUST be tenant-scoped. The AS MUST reject introspection requests where the authenticated caller is outside the tenant or trust boundary associated with the referenced Mission unless the deployment explicitly permits that class of cross-tenant enforcement.
A new `token_type_hint` value `mission_ref` is a candidate for registration in the OAuth Token Type Hints registry. When the AS receives this hint, it MUST look up the Mission record rather than a token record. The `active: false` response MUST be treated as a hard block on further execution at any enforcement point. The `mission_state` field carries the precise semantic: `suspended` means execution is blocked pending resumption (the Mission may recover); `revoked`, `completed`, and `expired` are terminal (a new Mission is required). Enforcement points MUST consult `mission_state` to determine the correct agent behavior. They MUST NOT treat all `active: false` responses as equivalent to `mission_revoked`, as this would cause agents to abandon recoverable suspensions.
**Introspection failure behavior**: enforcement points MUST treat introspection endpoint failures (network errors, 5xx responses, or responses arriving after the enforcement point's configured timeout) as `active: false`. Fail-closed behavior is required. A cached `active: true` response MAY be used only within a policy-defined staleness window; enforcement points MUST re-fetch when the staleness bound is exceeded or when a CAEP `mission-terminated` event is received. The staleness window MUST be defined explicitly in the deployment profile and MUST NOT default to unlimited.
The architectural decision is:
- the core Mission-Bound OAuth story does not require every deployment to expose Mission introspection
- deployments that want RS- or gateway-side live Mission checks should use an introspection profile
- the core protocol still needs to define what Mission state means at issuance, refresh, and exchange checkpoints even if no RS ever introspects it
## Token Derivation and Exchange
Mission-Bound OAuth expects token exchange to be part of the normal mission execution path when agents call multiple downstream APIs or when one service delegates to another.
**Subject token type**: when the subject token is a mission-bound access token, the `subject_token_type` is `urn:ietf:params:oauth:token-type:access_token` per RFC 8693. The AS distinguishes a mission-bound AT from a plain AT by the presence of a `mission_ref` claim in the JWT body. A new `subject_token_type` URI for mission-bound tokens is not proposed; the existing access token type is sufficient.
The key rule is:
> exchanged tokens may only represent authority that is still permitted by the referenced Mission at the moment of exchange.
That means token exchange must evaluate:
- Mission lifecycle state
- Mission expiry
- resource and action constraints against the compiled authority region
- delegation depth
- client identity
- any policy conditions required by the deployment
If the Mission is no longer valid, the exchange fails even if the input token itself is otherwise structurally valid.
**Refresh tokens on exchange-derived tokens**: RFC 8693 does not mandate that token exchange responses include refresh tokens. By default, token-exchange-derived tokens under a Mission MUST NOT include a refresh token unless the AS policy explicitly permits it for the target resource and client combination. If the AS does issue a refresh token for an exchange-derived token, the AS MUST bind that refresh token to the same `mission_id` as the subject token and MUST revoke it when the Mission is revoked, suspended, or completed, following the same lifecycle rules as refresh tokens issued at initial authorization. An exchange-derived refresh token MUST NOT outlive the Mission from which its authority derives.
### Token Exchange Request Format
The preferred exchange model is that the subject token already identifies exactly one Mission through its `mission_ref` claim, and the exchange request MAY echo that Mission reference in `authorization_details` when the client wants explicit binding:
```text
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://calendar.example.com/
&scope=calendar.schedule
&authorization_details=[{"type":"mission","mission_ref":"mr_7M9Qx2"}]
```
Embedding `mission_ref` inside `authorization_details` is preferred over a standalone token exchange parameter because RFC 9396 already defines `authorization_details` as a valid token endpoint parameter. A standalone `mission_ref` parameter would require IANA registration in the OAuth Parameters registry before conformant ASes could rely on it, since RFC 8693 Section 2.1 defines a closed parameter set and unregistered parameters may be silently ignored.
The AS validates the Mission's state, checks the requested resource and scope against that Mission's compiled authority region, and issues a resource-specific token. The resulting token carries a `mission_ref` appropriate for the target audience.
Mission selection rules:
- if the subject token carries exactly one `mission_ref` and the exchange request omits Mission reference input, the AS MAY use the bound `mission_ref` from the subject token
- if the exchange request includes `authorization_details` with `mission_ref`, the AS MUST compare that value to the Mission reference bound to the subject token and reject the request on mismatch
- if the subject token does not uniquely identify one Mission and the request omits Mission reference input, the AS returns `mission_ref_required`
This keeps Mission selection explicit where needed, but avoids forcing well-behaved orchestration runtimes to copy the same `mission_ref` through every exchange hop purely for disambiguation.
**Anti-confused-deputy rule**: token exchange MUST validate not only the Mission but also the requesting client's authority to derive a token for the requested target resource under that Mission. A client MUST NOT use another client's mission-bound subject token as exchange input unless that delegation path was explicitly authorized by the Mission and represented in the delegated actor state. Possession of a structurally valid subject token is not sufficient by itself.
The cleanest delegation story is to treat delegation as a constrained form of downstream token derivation, enforced at exchange time by the AS, rather than as an informal client-side action.
## Delegation Model
Delegation is in scope because many missions are not executed by a single software component end-to-end.
The `act` claim defined in RFC 8693 Section 4.1 identifies the current acting party in a delegated token. RFC 8693 defines `act` as a single JSON object, not a recursive structure. Nested `act.act` is not defined by RFC 8693 and is not supported by most JWT libraries.
For multi-hop delegation chains, Mission-Bound OAuth proposes a `delegation_chain` claim as an ordered array of actor identifiers. Index 0 is the most-recently-added actor (the current immediate delegate, matching `act.sub`); the last index is the originating delegating actor. The array grows at the tail as delegation chains extend. This is a proposed extension beyond RFC 8693 and is one of the explicit questions for community feedback.
A two-hop delegation example:
```json
{
"sub": "user-123",
"sub_profile": "user",
"act": {
"sub": "assistant-agent",
"sub_profile": "ai_agent"
},
"delegation_chain": [
{ "sub": "assistant-agent", "sub_profile": "ai_agent" },
{ "sub": "workflow-engine", "sub_profile": "service" }
],
"mission_ref": "mr_7M9Qx2"
}
```
The `act` claim identifies the current actor (the immediate delegate). The `delegation_chain` records the full ordered chain for audit and RS policy evaluation. The AS is authoritative for the chain state; both claims are projections of that state into the token.
Whether to extend RFC 8693 with a standardized multi-hop chain representation or define a Mission-specific claim is an open question for feedback. The `delegation_chain` proposal is one candidate. Feedback on the claim name, structure, and whether this requires a standalone Internet-Draft is explicitly requested.
The current direction for the delegation model:
- delegation is allowed only if the Mission permits it
- each delegation event extends `act` (current actor) and appends to `delegation_chain` (full history); **one delegation event is defined as one token exchange request that introduces a new actor**: the AS increments `current_depth` by exactly one for each exchange that sets or changes the `act` claim in the resulting token; re-exchanges that produce a token for the same actor at the same depth do not increment `current_depth`
- maximum delegation depth is enforced by the AS at exchange time via an atomic increment (see Concurrency Requirements below)
- delegated actors inherit attenuated, not expanded, authority
- the AS is authoritative for delegation state; tokens carry only the minimum chain data needed for the next hop or RS policy
**Offline delegation chain validation limitation**: once issued, the `act` and `delegation_chain` claims in a token are immutable for the token's lifetime. A delegation revocation at the AS does not retroactively alter these claims in already-issued tokens. Resource servers that validate delegation offline cannot detect post-issuance chain revocations. Mission-aware resource servers requiring strong delegation assurance MUST use Mission introspection rather than relying solely on token-embedded chain claims. This is an intentional design constraint of bearer token delegation and is not unique to Mission-Bound OAuth.
## Verified Agent Binding
Authentication establishes who approved the Mission. Verified agent binding is the separate question of proving that the actor executing derived actions is the same actor the Mission approved, continuously, across every token exchange, cross-domain projection, delegation hop, and Mission fork the execution takes.
This is an authority continuity problem, not an authentication syntax problem. Mission approval, whether through an interactive consent flow, a policy-auto-approved proposal, or a pre-provisioned activation, establishes that a specific principal delegated authority to a specific actor for a specific purpose. Verified agent binding ensures that the actor presenting mission-bearing tokens at exchange time is the actor that was approved to do so, and that this binding holds for the full Mission lifecycle rather than only at issuance.
### The Binding Anchor
The Mission record at the AS establishes the binding anchor at approval time. Three fields define the approved actor context:
- `mission_subject.sub`: the human principal on whose behalf authority was delegated
- `mission_client.client_id`: the registered OAuth client or orchestration layer approved to operate under this Mission
- `delegation`: the actors or actor classes permitted to hold derived tokens, and at what depth
The AS is the authoritative source for these bindings. Clients do not self-assert actor identity at exchange time; the AS validates the requesting client against the Mission's recorded approval context at every derivation step.
### Key Continuity via Sender Constraining
Sender constraining (DPoP, RFC 9449, or mTLS, RFC 8705) is the mechanism that binds token authority to a specific key holder across the Mission's execution. For mission-bearing tokens, key continuity is the protocol expression of actor continuity.
The binding rules across the Mission's execution:
- **Primary mission-bound token**: the orchestration layer's DPoP key thumbprint (`cnf.jkt`) is bound to the primary access token at issuance. This establishes the key identity of the approved executor.
- **Exchange-derived tokens**: when the orchestration layer exchanges the primary token for an audience-specific token, the AS binds the resulting token to the requesting client's DPoP key. An exchange presented with a different key signals a different actor. The AS MUST reject exchanges where the DPoP key does not match the key bound to the subject token unless the Mission's delegation model explicitly permits delegation to a new key holder at that depth.
- **Delegated actors**: when the Mission permits delegation and a new actor is introduced via token exchange, the AS records the new actor in `act` and appends to `delegation_chain`. The delegated token is bound to the delegate's DPoP key. That delegate's key identity must be stable across subsequent re-exchanges at the same depth.
The practical consequence is that a stolen mission-bearing token cannot be used to derive downstream tokens by an unauthorized actor, because each exchange requires proof of possession of the bound key. Key continuity is actor continuity for every derivation step the Mission takes.
### Actor Rebind, Key Rotation, and Executor Failover
The architecture also needs a legitimate continuity story for when the approved executor changes keys or moves runtimes without changing authority. Long-running Missions will eventually encounter key rotation, process restart, regional failover, HSM rollover, or planned migration from one orchestration instance to another. If the architecture treats every new key as a new actor with no rebind path, Missions become too brittle for real deployments. If it silently accepts key changes, verified agent binding collapses.
The right model is **controlled rebind**, not silent continuity:
- the Mission keeps an approved executor identity anchored in `mission_client.client_id`
- sender-constrained keys are continuity artifacts for that executor, not the executor identity itself
- rotating or replacing the bound key requires an explicit AS-mediated rebind event
- the AS authenticates the requesting client, verifies it is the approved executor or an authorized delegate at the current depth, records the new key binding, and emits a signed audit event
- a rebind does not expand authority, reset delegation depth, or bypass lifecycle checks; it only updates the continuity anchor for the same approved actor
Architecturally, this should be treated like suspension and resumption: a governed lifecycle-adjacent operation with audit semantics, not an implementation convenience hidden behind token issuance. Deployments may choose whether key rebind requires step-up authentication, administrator approval, or policy-only approval based on Mission sensitivity, but the existence of a controlled rebind path should be explicit.
### Exchange-Time Actor Verification
At every token exchange, the AS performs actor verification against the Mission's approved context. This happens independently of whether the subject token is structurally valid:
1. **Client identity check**: the requesting `client_id` must match either the Mission's `mission_client.client_id` for orchestration-layer exchanges, or a delegated actor identity recorded in the Mission's delegation state for sub-delegated exchanges. An exchange from an unauthorized `client_id` MUST be rejected even if the subject token passes all other validation checks.
2. **Delegation depth check**: the AS verifies the exchange does not introduce a new actor beyond the Mission's `max_depth`. The check is atomic (see Concurrency and Atomicity Requirements). An actor-expanding exchange that would exceed `max_depth` is rejected with `mission_delegation_depth_exceeded`.
3. **Actor chain integrity**: the AS derives `act` and `delegation_chain` from its own Mission state and the authenticated requesting client. Client-supplied actor claims in exchange requests are not authoritative and MUST NOT be accepted as-is. The AS is the only party authorized to set or extend these values in derived tokens.
4. **Anti-confused-deputy enforcement**: the AS validates that the requesting client is authorized to derive a token for the requested target resource under the Mission. Possession of a structurally valid subject token is not sufficient by itself. This check prevents one actor from using another actor's mission-bearing token as exchange input to obtain authority for an audience the original actor did not intend to grant.
These checks compose into a chain: each exchange step verifies the requesting client against the Mission's recorded approval context, verifies key possession, verifies delegation depth, and verifies that the derived authority remains inside the Mission's authority model. No single step alone is sufficient.
### Binding Across Projections
When Mission authority crosses a trust domain boundary via a cross-domain assertion or ID-JAG projection, actor binding must carry with it. The receiving AS does not have access to the originating AS's Mission record, so actor continuity across trust boundaries depends on what the assertion explicitly carries.
The projection carries `act` and `delegation_chain` as they existed at assertion issuance time. The receiving AS uses these to apply local policy. For example, the receiving AS may permit only agent-type actors under a registered `client_id` to derive tokens for its governed resources.
Two limitations are inherent to projection-based actor binding and MUST be acknowledged explicitly in any cross-domain deployment:
- **Stale chain state**: the assertion is a point-in-time projection. Post-issuance delegation chain changes, revocations of specific delegation hops, or suspension of an actor within the chain are not visible to the receiving AS from the assertion alone.
- **No live depth enforcement**: the receiving AS cannot enforce the originating Mission's `max_depth` limit against future exchanges it may authorize, because it does not hold the originating Mission's delegation state.
The trust split is easiest to see directly:
| Guarantee | Home domain with AS access | Cross-domain via projection only |
|---|---|---|
| Mission lifecycle state is current | Yes | Not necessarily; may be stale |
| Actor identity is verified against Mission record | Yes | Only to the extent the projection carries trustworthy actor state |
| Delegation depth can be enforced live | Yes | No |
| Mission Authority Model can be evaluated in full | Yes | No, only projected facts are available |
| Revocation or suspension is visible immediately | Yes, subject to cache bounds | No, unless live introspection or equivalent state sharing exists |
| Anti-confused-deputy checks can use full Mission context | Yes | Only within the reduced context carried across the boundary |
Mission-aware deployments that require live actor verification across trust boundaries MUST use mutual introspection or equivalent live state channels rather than relying on static projection. Deployments that accept projection-only binding must treat cross-domain actor continuity as best-effort rather than authoritative.
### Mission Forks and Sub-Mission Actor Inheritance
When a Mission spawns a parallel sub-task requiring its own Mission record rather than progressive expansion of the existing one, actor binding must be established for the sub-Mission explicitly at proposal time.
Two models apply:
**Sub-Mission under the same orchestration layer**: the same `mission_client.client_id` proposes and holds both Missions. The actor binding is continuous; the same orchestration layer is the approved executor for both. No additional binding ceremony is required.
**Sub-Mission delegated to a different actor**: if the sub-Mission is proposed for a different executing actor (a sub-agent, a downstream service, or a different orchestration instance), that actor must be identified at proposal time and the Mission's delegation bounds must permit it. The sub-Mission's `mission_client` records the sub-actor identity. The parent Mission's delegation chain is not automatically inherited by the sub-Mission; the sub-Mission is a distinct authority object with its own actor binding anchored independently at the AS.
In both cases the key rule holds: actor identity at sub-Mission creation is established explicitly at the AS when the sub-Mission is approved, not inferred from parent Mission delegation state.
### Parent and Sub-Mission Lifecycle Coupling
Sub-Missions also need explicit lifecycle coupling to the parent Mission. Without it, the architecture can accidentally recreate the same overhang problem it is trying to solve: a parent Mission ends, but a child authority object continues executing because it has its own local lifecycle and no normative dependency on the originating business authority.
The safest default is:
- a sub-Mission records the `mission_ref` of its parent as part of its governance metadata
- a sub-Mission MUST NOT outlive the parent Mission's terminal state unless it was approved as an intentionally independent successor Mission
- parent `revoked`, `expired`, or `completed` transitions cascade to dependent sub-Missions unless policy explicitly classified them as independent
- parent `suspended` transitions suspend dependent sub-Missions by default, because the authority question is still unresolved
- resuming a parent Mission does not automatically resume all children unless the deployment chose coupled resumption semantics at approval time
This keeps hierarchy honest. A sub-Mission may be its own authority object, but it is not free-floating by default. If a deployment wants a child workflow to continue after the parent ends, that should be explicit at approval time and visible in audit as a new, independently rooted authority path rather than an implicit inheritance loophole.
### The Binding Invariant
The verified agent binding invariant for Mission-Bound OAuth is:
> For every token derived from an active Mission, the AS can identify exactly which actor is bound to that token, verify that actor against the Mission's approved delegation context, and trace the full actor chain from the approved orchestration layer to the current token holder.
This invariant is maintained by the Mission record as the binding anchor, sender-constraining as the key-identity enforcement mechanism, AS-authoritative actor claims in derived tokens, atomic delegation depth enforcement, and anti-confused-deputy validation at each exchange step.
The invariant degrades under two conditions that deployments must treat as explicit risk decisions: (1) bearer tokens without sender-constraining, which allow any possessor to present the token at exchange time without proving key identity; and (2) cross-domain projection without live introspection, which allows the receiving AS to act on stale actor state. Both are known deployment tradeoffs. The invariant holds for the home domain under sender-constrained, Mission-aware issuance and degrades proportionally as deployments depart from that baseline.
## Resource Server Model
One important question is what a Resource Server should be able to rely on.
A practical processing model is:
- the AS enforces Mission lifecycle and delegation rules at issuance, refresh, and exchange time
- the RS continues to validate audience-specific access tokens as usual
- the RS may receive selected Mission metadata through token claims or introspection
- the RS must not be required to understand the full Mission object to remain interoperable
This keeps Mission-Bound OAuth from turning every RS into a mission engine while still allowing richer enforcement where deployments want it.
If richer RS behavior is desired, it should be profiled separately:
| RS Tier | Behavior | Deployment Requirement |
|---|---|---|
| **Baseline RS** | Validates access token as usual; optionally consumes a projected Mission reference for audit or logging | No Mission-specific changes beyond a scoped token |
| **Introspecting RS** | Queries the AS introspection endpoint using the Mission introspection profile to retrieve live Mission state before processing the request | Network access to the AS introspection profile |
| **Policy-aware RS** | Uses Mission-derived attributes (purpose, authority region, delegation state) in local authorization decisions via AuthZEN or an equivalent PDP | Full Mission metadata integration and PDP access |
This is one of the most important separations in the architecture because it allows incremental deployment. Mission-Bound OAuth should still deliver value when only the AS is mission-aware.
## Mission Authority and Scope Authority
Mission authority is a further constraint applied on top of scope authority, not a replacement for it. The scope model is hybrid:
- the client may request scopes as part of the authorization request, as it does today
- the AS treats client-requested scopes as inputs, not as authoritative claims
- the AS derives the final granted scopes from the Mission's authority model, narrowing or adjusting the client's request to match what the Mission permits
- the resulting token carries scopes that are a subset of both what the client requested and what the Mission's authority model allows
This keeps the architecture backward compatible. A baseline RS that does not understand Mission references still receives standard OAuth scopes in the token and can enforce them as usual. The Mission adds a governed layer above scope: the AS ensures at issuance that no token carries scope authority that exceeds the Mission's authority model, regardless of what the client requested.
The practical implication: if a client requests `calendar:write` but the Mission's authority model only permits write access to user-manageable calendars during the current business process, the AS may issue a more specific scope (or a standard scope paired with constrained audience claims) reflecting the narrower authority. The AS is the final arbiter of what scope appears in the token; the client's scope request is advisory.
**Scope mapping**: the mechanism by which the AS maps authority-model elements (resource type selectors, operation groups, relationship predicates) to OAuth scope strings is not standardized in this version and is implementation-specific. The normative constraint is: the AS MUST NOT issue a scope value that grants authority exceeding the Mission's authority model. The practical implication is that baseline RSes receive AS-derived scopes that conservatively represent Mission-attenuated authority, but the exact scope string for a given authority model is determined by AS implementation and deployment configuration. Interoperable scope mapping is a subject for deployment profile work, not the core spec.
For a baseline RS that does not understand Mission references, scope remains the effective authorization floor. Mission constraints are only as effective as the enforcement points that evaluate them, which is an intentional design tradeoff that enables incremental adoption. It should be stated explicitly: the hybrid model reduces but does not eliminate the over-authorization risk at baseline RSes, because the AS-derived scope still reflects the Mission's authority model.
## Mission-Aware Client Registration
Which Mission types a client is permitted to request should be part of client registration (RFC 7591 extension). A client that has not been registered for `schedule_meeting` cannot request a Mission of that type, regardless of what it asserts at runtime.
Client registration metadata should include the following candidate fields:
- `mission_types`: an array of permitted `purpose` identifiers or purpose classes the client may propose; the AS rejects proposals referencing purposes not in this list
- `mission_delegation_max_depth`: the maximum delegation depth permitted for this client across all Mission types; defaults to `0` (no delegation) if absent
- `mission_approval_mode`: the required approval mode for this client's proposals: `"interactive"` (user consent required), `"policy_auto"` (AS evaluates against registered policy at proposal time), or `"pre_provisioned"` (Mission type is pre-approved and activates immediately on request)
- `mission_resource_categories`: an optional coarse-grained filter on the resource type categories the client may reference in proposals, applied before template-level constraints
These field names are candidates for registration in the OAuth Dynamic Client Registration Metadata registry per RFC 7591 Section 3.2, not as JWT claims. Feedback on naming and structure is part of what this blog RFC is intended to surface.
This is the allow-list mechanism applied at the authorization layer. Without it, any Mission-capable client can request any Mission type and the AS has no normative basis for rejection beyond local policy. Client registration is the provisioning boundary that separates what was pre-authorized by an administrator from what a client can self-assert at runtime.
# Mission Management
The prompt itself is not the authorization artifact. It is an input to interpretation and proposal creation.
The basic architectural assumptions are:
- prompts are untrusted semantic input
- interpreted intent is a proposal, not authority
- the approved Mission is the first artifact with normative authority semantics
## Mapping Pipeline
The prompt-to-mission mapping process consists of:
1. Prompt interpretation
2. Intent extraction
3. Mission template selection
4. Mission proposal generation
5. Mission approval
6. OAuth authorization and token issuance
## Intent Envelope
An agent or client may convert a prompt into an intermediate representation called an **Intent Envelope**.
Example:
```json
{
"intent_type": "schedule_meeting",
"entities": {
"participant": "Dana",
"time_window": "next week"
},
"requested_outcomes": [
"calendar.create_event",
"crm.create_note"
],
"risk_signals": {
"external_communication": false,
"financial_action": false,
"sensitive_data": false
},
"confidence": 0.92
}
```
The Intent Envelope expresses the agent's interpretation of the prompt. It does not itself create authority.
## Mission Templates
Authorization servers or associated control-plane services may maintain a registry of **mission templates** representing common workflow categories.
Example template identifiers:
- `schedule_meeting`
- `customer_followup`
- `expense_submission`
- `travel_booking`
Templates may define:
- resource types and selectors
- operation groups
- default mission constraints
- delegation limits
- approval mode
- step-up or administrator approval requirements
- business-process conditions
- compliance and jurisdiction boundaries
Templates are useful because they constrain how free-form prompts become structured authority requests. They are also the appropriate level at which enterprise security and governance teams author policy, without requiring application teams to define raw resource taxonomies for every Mission type.
**Minimum template schema**: a Mission template is a JSON object with the following structure:
```json
{
"template_id": "schedule_meeting",
"purpose_class": "urn:example.com:mission:schedule-meeting",
"display_name": "Schedule a Meeting",
"authority_model_defaults": {
"allowed_resources": [
{ "type": "calendar.event", "selector": "user-manageable-calendars" }
],
"allowed_operations": [
{ "group": "calendar.schedule" }
],
"constraints": []
},
"delegation": {
"allowed": false,
"max_depth": 0
},
"lifecycle": {
"max_duration_seconds": 900
},
"approval_mode": "interactive",
"requires_step_up": false
}
```
Required fields: `template_id`, `purpose_class`, `display_name`, `authority_model_defaults`, `approval_mode`.
Optional fields: `delegation`, `lifecycle`, `requires_step_up`, and deployment-specific extension fields.
Templates are registered at the AS via a template registration API (a Layer 2 management-plane capability). Clients reference a template in a proposal by `template_id`. The AS MUST validate that the proposal's candidate authority is a strict subset of the template's `authority_model_defaults` before approving. A proposal that exceeds the template's authority bounds MUST be rejected.
## Purpose Taxonomy
Purpose identifiers need a clearer IAM story because they drive templates, client registration, approval policy, and audit semantics.
The taxonomy distinguishes three levels:
| Level | Form | Audience | Stability |
|---|---|---|---|
| **Display purpose** | Human-readable string | Approval UX, end users | Presentational only; may change |
| **Template identifier** | Short deployment-local string (e.g., `schedule_meeting`) | Template selection at the AS | Stable within a deployment |
| **Policy class** | URI (e.g., `urn:example:mission:schedule-meeting`) | Client registration, policy engines, audit, cross-deployment interoperability | Globally stable; registration-controlled |
The policy class URI is the authoritative identifier. It is what appears in `mission_types` client registration metadata, in AS policy rules, and in audit events. URI form is chosen because:
- it provides a namespace that avoids collisions between deployments and organizations
- it enables future standardization of well-known purpose classes without requiring a flat global string registry
- it is consistent with how OAuth, OIDC, and related specs handle stable identifiers (e.g., scope URIs, grant type URIs)
In simple single-deployment scenarios, the template identifier and policy class may be the same string. The display purpose is always separate because it is a UX artifact, not a policy artifact.
Organizations defining their own purpose classes should use domain-based URI namespaces: `urn:example.com:mission:schedule-meeting` or `https://example.com/mission-classes/schedule-meeting`. Standard or widely-shared purpose classes are a future registry concern; the architecture does not attempt to define them now.
The important property is stable policy reference. Clients, administrators, and policy engines need a URI they can register against and reason about consistently, independent of how the display label changes over time.
## Tenant and Organization Boundary
Most IAM deployments are tenant- or organization-scoped. The safest default is to treat a Mission as tenant-bound unless it is explicitly projected across a federation boundary.
A Mission should at least carry:
- tenant or organization identifier
- subject tenancy
- client tenancy
- policy authority or issuer
This matters because:
- relationship predicates often depend on tenant-local graphs
- Mission introspection must not leak Mission state across tenants
- client registration and purpose classes are usually tenant-scoped
- cross-domain Mission assertions are explicit exceptions, not the default
The safest default is:
- Missions are local to one issuer and one tenant boundary
- anything that crosses that boundary does so through an explicit projected artifact and trust decision
Operationally, this means `mission_ref` resolution must also be tenant-scoped:
- the AS MUST reject `mission_ref` lookups, introspection requests, authority-model retrieval, and exchange requests that originate outside the tenant or issuer context that owns the Mission unless an explicit federation profile permits that access
- a `mission_ref` that is valid in one tenant MUST NOT be assumed valid or meaningful in another tenant, even if the string value collides
## Proposal and Approval
### Endpoint Candidates
Current endpoint candidates:
```text
POST /mission-proposals
POST /mission-proposals/{proposal_id}/approval
GET /mission-proposals/{proposal_id}
GET /missions/{mission_ref}
POST /missions/{mission_ref}/resume
POST /missions/{mission_ref}/expand
```
The current intent is:
- `POST /mission-proposals` creates a Mission Proposal based on interpreted intent
- `POST /mission-proposals/{proposal_id}/approval` approves, rejects, or attenuates the proposal
- `GET /mission-proposals/{proposal_id}` returns proposal state for polling or review
- `GET /missions/{mission_ref}` returns the approved Mission artifact (authorization required; scoped to principals with administrative or subject authority over the Mission)
- `POST /missions/{mission_ref}/resume` resumes a suspended Mission; requires the same authentication and authorization as the original approval action
- `POST /missions/{mission_ref}/expand` submits an expansion proposal against an active Mission; see Progressive Mission Expansion
### Proposal Request Schema
`POST /mission-proposals` accepts `application/json` with the following structure:
```json
{
"purpose": {
"class": "urn:example.com:mission:schedule-meeting",
"display": "Schedule a Meeting with Dana"
},
"template_id": "schedule_meeting",
"subject": {
"sub": "user-123",
"iss": "https://idp.example.com"
},
"candidate_authority": {
"resource_types": ["calendar.event", "crm.account_note"],
"operation_groups": ["calendar.schedule", "crm.record_activity"],
"constraints": {
"region": "EU",
"business_process": "meeting-followup"
}
},
"parameters": {
"participant": "Dana",
"time_window": "next week"
},
"delegation": {
"allowed": false
},
"lifecycle": {
"max_duration_seconds": 900
},
"display": {
"title": "Schedule a Meeting",
"description": "The assistant will check calendar availability and create a meeting with Dana.",
"actions": ["Check your calendar for availability", "Create a calendar event with Dana"],
"constraints": ["Expires in 15 minutes", "EU data only"]
}
}
```
Required fields: `purpose.class`, `subject`, `candidate_authority`.
Optional fields: `template_id` (if omitted, the AS selects a template by purpose class), `parameters`, `delegation`, `lifecycle`, `display`.
**Success response** (HTTP 201 Created):
```json
{
"proposal_id": "prop_Kx29Mf",
"state": "pending",
"created_at": 1773072000,
"approval_uri": "https://as.example.com/mission-proposals/prop_Kx29Mf/approval",
"status_uri": "https://as.example.com/mission-proposals/prop_Kx29Mf"
}
```
When the proposal is approved, `GET /mission-proposals/{proposal_id}` returns the updated proposal record with `"state": "approved"` and a `mission_ref` field containing the projected Mission reference for the newly activated Mission:
```json
{
"proposal_id": "prop_Kx29Mf",
"state": "approved",
"created_at": 1773072000,
"approved_at": 1773072120,
"mission_ref": "mr_7M9Qx2",
"mission_uri": "https://as.example.com/missions/mr_7M9Qx2"
}
```
Clients MUST poll `status_uri` to detect approval. The AS MAY also support a `callback_uri` field in the proposal request body that it calls upon approval with the same response body. The `mission_ref` returned here is the value the client uses in subsequent `authorization_details` when starting the OAuth authorization flow.
**Failure responses**: 400 for malformed requests, 401 for unauthenticated callers, 403 for unauthorized mission types (client not registered for the requested `purpose.class`), 422 for proposals that exceed template bounds.
The approval step is where requested authority becomes real authority. Approval results in one of three outcomes:
- denial, with no Mission created
- attenuation, with an Approved Mission narrower than the proposal
- approval, with an Approved Mission created from the proposal
That approval may be performed by:
- the end user
- an administrator
- enterprise policy
- an external approval system
- a combination of human and policy controls
An attenuation step is explicitly in scope. The approved Mission may be narrower than the proposed one.
### Single-Party and Multi-Party Approval
The document often talks about "the approver" for readability, but the architecture should not imply a single-actor approval model. Some Missions are low-risk and can be approved by the subject alone. Others require separation of duties: user plus administrator, administrator plus policy engine, or two human approvers from different roles.
The architectural point is that approval is a governed decision procedure, not a single UI click. A Mission may therefore define:
- one required approver class
- multiple required approver classes
- policy preconditions that must succeed before any human approval is sufficient
- ordered approval stages for regulated workflows
The output is still one Approved Mission artifact, but its approval evidence may record multiple approving principals, policy decisions, and timestamps. Separation-of-duties logic does not need to live in the core OAuth extension, but the architecture should acknowledge that the approval boundary may be multi-party for privileged or regulated Missions.
There is a second distinction that matters in the same way: the delegating principal's intent and the resource owner's authority are not always the same thing. A manager may be authorized to start an HR-related workflow involving data that is ultimately governed by a separate data owner, compliance function, or regulatory policy authority. For low-risk workflows those inputs may collapse into one approval path. For sensitive data and regulated actions, they may need to remain distinct and both be reflected in Mission approval policy.
The proposal model should allow input from:
- a raw prompt
- an intent envelope
- a structured plan
- an MCP tool schema extraction
- a direct structured authority request
That flexibility matters because not every deployment wants the AS itself to process natural language.
### Mission Proposal Authorization
Who is the authorized principal to call `POST /mission-proposals` is a security-critical question. The design principle that agents interpret prompts but do not grant authority needs a concrete protocol expression at the proposal endpoint.
The authorized callers and their trust levels:
- **Orchestration layer or agent platform**: the system that manages agent execution, acting as an authenticated OAuth client; the AS authenticates the caller by `client_id` using standard OAuth client authentication; the platform is trusted to submit proposals on behalf of its managed agents but not to approve them
- **User session**: an authenticated user may directly author or authorize a proposal through a consent flow; the AS authenticates the user through a standard OAuth authorization flow
- **Administrator**: an authenticated administrator acting under elevated policy authority, able to pre-approve mission types or approve proposals for regulated workflows
The agent runtime is not an authorized proposal caller. An agent should not be able to submit a proposal under its own identity without passing through an orchestration layer or authenticated user session. The proposal endpoint should reject unauthenticated or agent-self-authenticated requests.
The critical separation: proposal submission is client-authenticated at the orchestration layer. Approval is user-authenticated or policy-evaluated. These are distinct trust steps enforced by separate authorization checks at the AS. A client registered for `schedule_meeting` missions can propose one; only the authorized approver can activate it.
**User identity binding in `mission:propose` tokens for user-delegated missions**: a `mission:propose` access token obtained via client credentials allows the orchestration layer to submit proposals. But for user-delegated missions, the `subject` field in the proposal must be bound to a verified user identity. The AS MUST NOT accept a `subject.sub` claim in a proposal on the basis of the client's self-assertion alone.
Three acceptable binding mechanisms:
1. **Session-bound `mission:propose` token**: the orchestration layer obtains the `mission:propose` token through an authorization code flow that authenticates the user. The AS records the authenticated user subject in the issued token. At proposal time, the AS validates that `proposal.subject.sub` matches the subject in the `mission:propose` token.
2. **JWT subject assertion**: the orchestration layer includes a signed JWT subject assertion in the proposal request, issued by the IdP that authenticated the user. The AS validates the assertion and binds the subject to the proposal.
3. **Pre-bound session reference**: the orchestration layer includes a session reference (e.g., an OIDC `id_token` `jti` or session ID) in the proposal. The AS resolves the authenticated subject from its own session store.
A `mission:propose` token obtained via client credentials alone MUST only be used for headless missions where no interactive user subject is involved.
For headless workflows, the orchestration layer or an administrative process is the authorized proposal submitter, and approval is delegated to policy rather than an interactive consent step.
**Proposal endpoint authentication**: `POST /mission-proposals` is a protected resource requiring an access token with scope `mission:propose`, issued by the AS to the orchestration layer's `client_id`. The endpoint is not an OAuth grant endpoint; it does not accept client credentials directly. The AS MUST authenticate the caller as a registered OAuth client and validate that the client's registration includes the proposed mission type in `mission_types`.
The grant type used to obtain the `mission:propose` token depends on the mission type:
- **Headless missions**: the orchestration layer obtains the token via the client credentials grant (RFC 6749 Section 4.4). The resulting token carries no user subject and MUST only be used for headless mission proposals. The AS MUST reject any user-delegated proposal submitted under a client-credentials-derived `mission:propose` token.
- **User-delegated missions (Mechanism 1)**: the orchestration layer obtains the token via the authorization code flow that authenticates the user. The AS records the authenticated user subject in the issued token. At proposal time, the AS validates that `proposal.subject.sub` matches the subject bound in the `mission:propose` token. This is the most direct binding mechanism.
- **User-delegated missions (Mechanisms 2 and 3)**: the orchestration layer may obtain the `mission:propose` token via client credentials but must supplement the proposal with either a signed JWT subject assertion (Mechanism 2) or a pre-bound session reference (Mechanism 3) to establish the user subject. The AS validates the supplemental binding independently before accepting the proposal subject claim.
The AS MUST publish the proposal endpoint URL in its authorization server metadata (RFC 8414) under a `mission_proposal_endpoint` metadata field. Clients discover the endpoint from AS metadata; the endpoint URL MUST NOT be hardcoded.
The following scopes are candidates for registration. Feedback on scope naming and whether per-mission-type variants (e.g., `mission:propose:schedule_meeting`) are preferable to umbrella scopes is an explicit question for this blog RFC.
| Scope | Grant Type | Purpose |
|---|---|---|
| `mission:propose` | client credentials or auth code | Submit Mission proposals at `POST /mission-proposals` |
| `mission:activate` | client credentials | Activate pre-provisioned mission types at `POST /missions/activate` |
| `mission:expand` | client credentials | Submit expansion proposals at `POST /missions/{mission_ref}/expand` |
| `mission:authority-model:read` | client credentials | Retrieve signed Mission Authority Model artifacts |
| `mission:introspect:authority` | client credentials | Receive `authority_model` in Mission introspection responses |
### Approval Authority Model
Approval authority also needs a clear precedence model.
The approval model should assume these authority sources, from strongest to weakest:
- **hard policy deny**: cannot be overridden by user approval
- **administrator approval or attenuation**: may grant or narrow within administrative scope
- **subject approval**: grants delegated authority within the limits policy and registration allow
- **policy auto-approval**: grants within pre-registered constraints for trusted clients
Some consequences follow:
- a user cannot approve what policy forbids
- an administrator may attenuate a Mission even if the subject would have approved a broader one
- policy auto-approval only works within pre-registered Mission classes and authority envelopes
- external approval systems act as delegated approvers; they do not bypass issuer policy
## Approval UX and Consent Display
The approval step is only meaningful if the approver understands what they are approving. Structured Mission proposals with resource types, operation groups, and relationship predicates are not legible to most users.
The Mission proposal should carry a human-readable display representation alongside the machine-evaluable authority structure. This display representation is used in the approval UI, not in enforcement:
```json
{
"display": {
"title": "Schedule a Meeting",
"description": "The assistant will check calendar availability, create a meeting with Dana, send an email invitation, and log a CRM note. This access expires in 15 minutes.",
"actions": [
"Check your calendar for availability next week",
"Create a calendar event with Dana",
"Send an email invitation to Dana",
"Log a follow-up note in Salesforce for Dana's account"
],
"constraints": [
"EU data only",
"Accounts you own",
"Expires in 15 minutes"
]
}
}
```
The `display` object is the consent primitive. Users approve the display representation; the system enforces the [Mission Authority Model](#mission-authority-model-as-a-stored-versioned-artifact). That alignment between what the user sees and what the system enforces is a security property, not a UX choice. Without it, the approval step is a checkbox users do not understand and the "human in the loop" guarantee is nominal.
**Template-driven display validation** is the mechanism that preserves this alignment. The orchestration layer may supply a candidate `display` object in the proposal, but the AS validates it against the selected mission template before presenting it to the approver. The validation checks that:
- every claimed action in `display.actions` maps to an operation group present in the authority model
- every claimed constraint in `display.constraints` is actually present in the authority model
- the display description does not omit material authority that the authority model grants
If the AS cannot verify alignment, it either regenerates the display from the authority model (template-driven generation) or rejects the proposal. Accepting an unverified display representation is a social engineering attack surface: a malicious or compromised orchestration layer could describe a benign operation while the authority model permits something broader. Template-driven generation or AS-side validation is therefore a security requirement, not a UX convenience.
## A2UI as the Mission Interaction Layer
The plain `display` object is a useful minimum, but it is not the only possible interaction model.
For more agent-native deployments, [A2UI](https://a2ui.org/) is a strong fit as an optional Mission interaction layer:
- the Mission Proposal remains the authority candidate
- the Mission Authority Model remains the enforcement artifact
- A2UI becomes the rendering and interaction layer for review, clarification, attenuation, approval, suspension, and remediation flows
That is a better fit than a static `display` blob when the approval flow is conversational or multi-step:
- the AS may need to ask clarification questions
- the approver may want to narrow the Mission before approval
- the client may need a structured remediation or resume action after suspension
- different clients may want native UI rendering without executing arbitrary remote code
The right architectural boundary is:
- `display` is the minimum portable summary every deployment can support
- A2UI is an optional richer interaction profile for agent-native clients
- neither `display` nor A2UI is the authority itself
- the Approved Mission and Mission Authority Model remain authoritative
AAuth-like approval experiences become much easier to imagine in this model: the authorization server can drive a structured interaction surface while still keeping authority semantics at the Mission layer rather than in the UI layer.
That separation matters. If the UI protocol becomes the authority artifact, the system becomes harder to audit and explain. If the UI protocol is only a presentation and interaction surface over Mission semantics, then:
- approval remains governable
- clarification remains structured
- remediation can be standardized
- the AS can validate what is shown against what is actually enforced
The practical recommendation is therefore:
- require a simple `display` summary in the base architecture
- allow an A2UI interaction surface as an optional profile for richer clients
- require AS-side validation that the A2UI view, like the `display` view, accurately represents the authority actually being approved
## Approval Evidence Artifact
From an IAM governance perspective, it is not enough to know that a Mission was approved. The system also needs to retain what the approver was actually shown.
The approval evidence artifact should include at least:
- the Approved Mission identifier and version
- the Mission Authority Model version or content hash that was approved
- the approver identity
- the approval timestamp
- the template or policy version used to generate the approval view
- the exact approved presentation view:
- validated `display` object, or
- validated A2UI interaction payload, or
- a canonical AS-generated rendering derived from the Mission Authority Model
This matters for:
- audit replay
- human-readable explainability
- dispute resolution
- regulated approval processes
The architectural rule should be:
- approvers approve a presentation artifact
- the AS retains that approved presentation artifact as evidence
- the AS must be able to prove that the retained presentation artifact was validated against the approved Mission Authority Model
- the evidence record should carry a stable digest of the approved presentation artifact and a stable digest or version reference to the Mission Authority Model so that later auditors can verify that the rendered approval surface and the enforced authority model were the same approval event
Without that evidence layer, "human approval" is harder to defend later because the system can show what authority was enforced but not what authority was represented to the approver at decision time.
When approval is delegated to an external approval system, the external system must return enough information for the AS to preserve the same evidence standard. At minimum, it must return one of:
- the final approved presentation artifact
- a stable reference and integrity-protected hash of the approved presentation artifact
- an AS-resolvable identifier that allows the AS to retrieve the exact approved presentation artifact later
An external approval callback that returns only "approved" or "denied" without preserving the approved presentation artifact is insufficient for high-assurance or regulated approval flows.
## Enterprise Approval System Integration
Many enterprises already have approval workflows: ServiceNow, Jira Service Management, enterprise HR systems, or custom approval portals. The Mission proposal and approval model should be designed to delegate to these systems rather than requiring a new approval UI.
The approval endpoint should support an async delegation model:
```text
POST /mission-proposals/{proposal_id}/approval
-> 202 Accepted
Location: /mission-proposals/{proposal_id}
Body:
{
"approval_mode": "external",
"external_system": "https://servicenow.enterprise.example/api/approval",
"external_ticket_id": "REQ0012345",
"callback_uri": "https://as.enterprise.example/mission-proposals/callback"
}
```
The AS holds the proposal in `pending` state, the external system delivers its decision via callback or webhook, and the AS transitions the Mission accordingly. This allows Mission-Bound OAuth to sit above existing enterprise governance infrastructure rather than replacing it. For regulated enterprises, this is the adoption path: Mission proposals become work items in the existing approval system, and the AS acts on the approval output.
**Callback authentication**: the approval callback endpoint (`https://as.enterprise.example/mission-proposals/callback`) MUST be authenticated. Unauthenticated callbacks that can transition a Mission from `pending` to `active` are a privilege escalation attack surface. Three acceptable models:
1. **Callback credential**: when the AS delegates approval to an external system, it issues a short-lived, audience-specific callback credential bound to the `proposal_id`. The external system presents this credential in the callback request. The AS validates it before accepting the approval decision.
2. **Webhook signing**: the external system signs callback payloads using a pre-registered key. The AS verifies the signature before processing. Key registration is part of the external system configuration at the AS.
3. **AS polling**: instead of receiving callbacks, the AS polls the external system for approval decisions. The AS authenticates to the external system using a registered client credential. No inbound callback endpoint is exposed.
Deployments MUST implement at least one of these models. An unauthenticated callback endpoint MUST NOT be deployed.
**Callback re-validation requirement**: authenticating the callback source is necessary but not sufficient. Between the time the AS delegated approval to an external system and the time the callback arrives, conditions may have changed: the proposal's approval window may have elapsed, the subject's organizational membership or authorization may have changed, or a hard policy deny that was not in effect at proposal time may now apply. The AS MUST re-run applicable policy checks at the time of callback receipt before transitioning the Mission to `active`. Specifically, the AS MUST re-validate: (1) the proposal has not expired, (2) the proposed mission type is still permitted for the requesting client, (3) no hard policy deny now applies to the subject or client. If any check fails, the AS MUST reject the callback with an appropriate error and MUST NOT activate the Mission, regardless of the callback credential's validity. The audit event for a rejected callback MUST record the reason for rejection.
## MCP Tool Metadata to Mission Proposal Mapping
Many agent deployments use MCP as the tool and context plane. MCP tool schemas expose resource types, required parameters, and operation descriptions. A standard extraction of this metadata into Mission Proposal candidate authority would give agent platforms a concrete starting point without requiring bespoke proposal generation logic per tool.
The mapping concept:
```json
{
"mcp_tool": {
"name": "calendar_create_event",
"description": "Creates a calendar event",
"input_schema": {
"calendar_id": "string",
"title": "string",
"attendees": ["string"],
"start": "datetime",
"end": "datetime"
}
},
"derived_authority_candidate": {
"resource_type": "calendar.event",
"operation_group": "calendar.schedule",
"required_parameters": ["calendar_id"]
}
}
```
A standard extraction profile for MCP-to-Mission Proposal mapping would allow agent frameworks to auto-generate the candidate authority section of a Mission Proposal from the tools the agent has discovered, rather than requiring the agent to construct it from scratch.
But the boundary has to stay explicit:
- MCP tool names are not stable authorization identifiers
- MCP schemas are not policy vocabulary
- MCP metadata is proposal input, not authority
The extraction step is therefore a translation into Mission vocabulary, not a direct reuse of tool metadata as policy semantics. That auto-generated candidate is then attenuated through templates and policy before becoming a real proposal. This is one of the most practical things the architecture can do to lower the entry cost for agent platform developers who are already using MCP without letting tool schemas silently become the authorization model.
## Handling Ambiguity
When the prompt is incomplete or ambiguous, the architecture should bias toward narrower authority:
- generate a narrower Mission Proposal
- request clarification
- request additional authority later through a new proposal
Ambiguity should reduce scope rather than expand it.
## Progressive Mission Expansion
Progressive expansion is an optional capability for deployments that need open-world adaptation after a Mission has started. It is not required for the minimum viable Mission-Bound OAuth profile. Simpler deployments may choose the stricter rule: if additional authority is needed, create a new Mission rather than expanding the existing one.
When enabled, progressive expansion is the architecture's answer to the open-world bootstrap problem. Agents may discover additional authority requirements while executing a Mission. Rather than requiring all future resources to be enumerated up front, the model allows an agent to submit a constrained expansion proposal that references the current Mission.
The key concept should be a **bounded authority envelope** rather than literal pre-enumeration of every future object. The parent Mission carries:
- approved resource classes and selectors
- approved operation families
- approved relationship predicates
- approved business-process and compliance bounds
An expansion is allowed when the newly requested authority can be shown to fall **inside that approved envelope**, even if the exact runtime object or tool was not named earlier.
That means:
- a newly discovered email tool may be acceptable if `communication-with-approved-participants` is already inside the parent Mission envelope
- a newly discovered CRM object may be acceptable if it matches the approved `accounts-owned-by-requesting-team` selector
- a new operation outside the approved operation families still requires a fresh Mission proposal and approval cycle
The envelope does not make expansion unconstrained. It simply moves the decision boundary from literal membership to policy-evaluable containment.
This should be treated as a later profile for easier adoption:
- baseline deployments can omit expansion entirely
- deployments that need open-world adaptation can add expansion once their Mission templates, policy, and audit model are stable
Containment should be evaluated across at least four dimensions:
- **resource containment**: the requested resource type and selector fall within an approved resource class or selector family
- **operation containment**: the requested action belongs to an approved operation group or operation family
- **predicate containment**: the request does not introduce new relationship, process-state, compliance, or jurisdiction predicates outside the approved set
- **risk containment**: the expansion does not exceed approved delegation depth, duration, checkpoint policy, or other execution bounds
An expansion should be considered **inside the authority envelope** only when all four containment checks pass. If any one of them fails, the request is outside the approved envelope and requires a new Mission proposal or new approval step.
```json
{
"parent_mission_ref": "mr_7M9Qx2",
"requested_extension": {
"candidate_authority": {
"resource_types": [
"email.message"
],
"operation_groups": [
"email.send"
],
"constraints": {
"relationship": "participants-in-approved-meeting"
}
},
"display": {
"title": "Send Meeting Invitation",
"description": "Send an email to Dana confirming the scheduled meeting.",
"actions": ["Send one email to Dana with meeting details"]
}
}
}
```
This directly addresses the question "what happens when the agent discovers a tool it did not know about at startup?" The answer is not necessarily a new full authorization flow with the user. It is a scoped expansion request against the existing Mission, which the AS evaluates against the original approved authority envelope. If it fits inside that envelope, the expansion is approved. If it exceeds those boundaries, a new Mission proposal or a new approval step is required.
This model keeps the user in control without requiring them to pre-enumerate every tool at consent time.
### Expansion Proposal Protocol
Expansion proposals are submitted to `POST /missions/{mission_ref}/expand`. The endpoint requires a client credentials access token with scope `mission:expand`, issued to the same `client_id` that holds the active Mission. The `parent_mission_ref` in the body must match the `{mission_ref}` path parameter; mismatches MUST be rejected with HTTP 400.
**Success response** (HTTP 202 Accepted): the expansion proposal is queued for evaluation. If the AS can evaluate envelope containment synchronously, it MAY return HTTP 200 with the expansion decision inline.
```json
{
"expansion_id": "exp_Hq37Wz",
"state": "pending",
"mission_ref": "mr_7M9Qx2",
"status_uri": "https://as.example.com/missions/mr_7M9Qx2/expansions/exp_Hq37Wz"
}
```
When the expansion is approved, the AS updates the Mission's compiled authority region (incrementing the Mission version counter) and emits a `mission.expanded` audit event. The client discovers approval by polling `status_uri`. The AS MAY also support a `callback_uri` in the expansion request body, following the same callback authentication requirements as the enterprise approval system integration.
**Expansion does not produce a new `mission_ref`.** The existing Mission's compiled authority region is widened; the same `mission_ref` governs subsequent token exchanges. After an approved expansion, enforcement points that cached the prior Mission Authority Model version MUST re-fetch, as the version counter will have incremented.
**If the requested extension exceeds the authority envelope**, the AS MUST return HTTP 422 with error `mission_expansion_outside_envelope` and a `mission_error_detail` object identifying which containment check failed (using the same `constraint_violated` vocabulary as `mission_authority_exceeded`). In this case the client must submit a new full Mission proposal and approval cycle.
**Concurrency**: concurrent expansion proposals against the same Mission version MUST be serialized. An expansion evaluated against a prior version is rejected with HTTP 409 and error `mission_expansion_conflict`, requiring the client to re-fetch the current Mission state and re-evaluate before resubmitting.
## Headless and Service-to-Service Missions
Not all agent workflows involve interactive users. Automation pipelines, scheduled reconciliation jobs, and system-level agents operate as service clients using client credentials (RFC 6749 Section 4.4) or pre-authorized grant flows with no consent screen. Mission governance still applies to these workflows; the approval model is different.
**Subject binding for headless missions**: the proposal schema requires a `subject` field. For headless service-to-service missions, the subject is the service principal on whose behalf the automation runs, not an interactive user. Two cases apply:
- **Client-as-subject**: the orchestration layer is itself the subject. The `subject.sub` is the client's registered service identity and `subject.iss` is the AS or IdP that issued the service identity. This is the appropriate model when the agent is acting on behalf of a system or process rather than a human.
- **Service-identity subject**: the client is acting on behalf of a distinct service identity (e.g., a batch job identity registered in a service account directory). `subject.sub` and `subject.iss` refer to that identity, and the AS validates the orchestration layer's authority to propose Missions on behalf of that subject via client registration metadata.
If `subject` is omitted from a headless mission proposal, the AS MUST treat the `client_id` as both client and subject. Deployments that require explicit subject tracking for audit purposes SHOULD require the `subject` field even for headless missions.
For headless missions, the architecture provides two models:
**Policy-auto-approved missions**: at Mission proposal time, the AS evaluates the request against a registered policy for the client and mission type. If the client is registered for the requested mission type and the policy permits it, the AS activates the Mission immediately at creation without an interactive consent step. The audit trail records the policy evaluation as the approval event. This is appropriate for well-defined, low-risk automated workflows where a security team has pre-vetted the mission type for specific client identities.
**Pre-provisioned mission types (standing mission authorizations)**: a security team or administrator registers approved mission types for specific service clients at provisioning time, analogous to client registration for OAuth scopes. At runtime, a service client activates a Mission of a pre-approved type by calling `POST /missions/activate` with the following request body:
```json
{
"mission_type": "urn:example.com:mission:nightly-reconciliation",
"client_id": "reconciliation-service"
}
```
The endpoint requires a client credentials access token with scope `mission:activate`, issued to the requesting `client_id`. The AS validates that the `mission_type` is in the client's `mission_types` registration with `approval_mode: pre_provisioned` and activates the Mission immediately. The response is HTTP 201 Created with the same body format as an approved proposal response, including `mission_ref` and `mission_uri`.
No runtime proposal or approval cycle is required. The pre-provisioned type defines the compiled authority region, delegation bounds, lifecycle constraints, and business event terminal conditions that apply to every instance of that mission type for that client. The AS MUST record the activation as an audit event (`mission.created`) with the registration record as the authorization evidence.
Lifecycle governance applies identically to headless missions. Business event termination, suspension, delegation bounds, and the audit trail all apply. The only difference from interactive missions is that initial activation is delegated to policy evaluation or pre-registration rather than a user consent flow.
A service client that has not been registered for a mission type cannot request one regardless of its client credentials. The registration boundary for headless clients is the same provisioning-time authority check that client registration provides for interactive clients.
## Mission Authoring and Policy Authoring
One thing this architecture needs to make clearer is the difference between **mission authoring** and **policy authoring**.
Mission authoring is about describing a proposed unit of authority for a specific Mission instance:
- what the agent is trying to do
- on whose behalf it is acting
- what authority region appears necessary
- what parameters and time bounds apply
Policy authoring is about defining the standing rules that govern whether a proposed Mission is acceptable:
- which mission templates are allowed
- which authority regions, selectors, and operation groups are valid for a purpose
- when step-up or admin approval is required
- how delegation depth is bounded
- what environmental conditions change the decision
In other words:
- a **Mission** is an instance
- **policy** is what decides whether the instance is allowed, and in what form
That distinction matters operationally. It means an enterprise can let application teams define mission templates and Mission parameters without also giving them the ability to redefine global authorization policy.
## Prompting as a Policy Authoring Front-End
One practical question for enterprises is whether prompting and intent systems can help close the policy-authoring gap.
The answer is yes, but only if they are treated as an authoring front-end rather than as the governance authority.
Prompting and intent are useful for:
- drafting Mission templates from business-language descriptions
- proposing operation groups, selectors, and constraint candidates
- translating messy enterprise process language into structured Mission vocabulary
- helping platform and security teams bootstrap a first policy model in domains where raw resource/action inventories are too complex to author directly
A useful mental model is:
- prompts and intent act as a semantic compiler front-end
- Mission templates and authority models are the compiled artifacts
- policy review and approval remain the governance checkpoint
In enterprise terms, that means a team might start with a policy prompt such as:
> Sales assistants may prepare renewal packages for accounts owned by their team, only during active renewal stages, only in EU-hosted systems, and only for the current quarter.
The system can then generate a draft template containing:
- a purpose class
- operation groups
- resource selector families
- relationship predicates
- process-state predicates
- compliance constraints
- lifecycle defaults
That draft still needs human and policy-system review before it becomes an approved Mission template or organization policy binding.
This distinction matters because prompting can make policy authoring more legible, but it does not make policy governance optional.
The practical enterprise pattern is:
1. prompting and intent generate candidate templates or authority-model fragments
2. security and platform teams review and normalize them
3. approved templates become reusable Mission authoring inputs
4. runtime prompts only instantiate within those approved templates and authority envelopes
That is how prompting can help solve the enterprise policy-authoring gap without letting runtime language generation silently become the authorization system.
## Mission Templates, Profiles, and Authoring Boundaries
In practice, large deployments will need more than raw per-request proposal JSON. They will likely need:
- reusable mission templates
- purpose catalogs
- operation groups
- resource-type definitions
- approval profiles
- organization-level policy bindings
In practice, at least three authoring layers are needed:
- **runtime mission proposals** created by clients or agents
- **mission templates** managed by platform or application teams
- **authorization policy** managed by security or governance teams
That separation gives a path to scale. Without it, every Mission becomes a bespoke authority object and policy quickly becomes unmanageable.
## Beyond Allowlists: Constraint-Driven Mission Authoring
One of the biggest open issues in this architecture is that explicit allowlists do not scale far enough for real deployments.
It is easy to model a Mission as:
- allowed resource regions
- allowed operation groups
- max duration
- max delegation depth
That is useful, but it is not enough. Real authorization decisions often depend on constraints such as:
- jurisdiction or data residency
- compliance regime
- business process stage
- relationship to the subject or record
- organizational role
- customer or tenant boundary
- risk posture
- time, location, or execution environment
In many cases, those constraints are more important than a literal allowlist of resource names. A scheduling assistant may be allowed to create events, but not for executive calendars, not for regulated accounts, not outside a region, and not outside an approved business process.
That suggests the Mission model needs to support **constraint-driven authority**, not just explicit allowlists.
## Missions as Guardrails for Open-World Agents
This is where Missions become more than a label. For open-world agents, the problem is not just "what exact API calls are pre-approved?" It is "what bounded space is the agent allowed to explore while still remaining safe?"
Mission-Bound OAuth can help by making the Mission the **guardrail object** for open-world execution:
- the agent may discover tools dynamically
- the agent may discover new candidate resources at runtime
- the agent may plan adaptively
- but every derivation still has to stay inside Mission constraints
The Mission does not need to enumerate every future action up front. Instead, it defines a bounded authority region within which planning and execution are allowed to adapt. Progressive expansion provides the escape valve for genuinely novel requirements that appear at runtime.
## Why This Matters for FGA
Fine-grained authorization (FGA) has often been hard to apply to agent missions not because FGA models are weak, but because the mapping problem is too large:
- too many resources
- too many operations
- too much runtime ambiguity
- too little structure around agent intent
Missions can help unblock that by introducing a bounded intermediate layer between open-ended intent and concrete policy evaluation.
Instead of asking policy systems to solve arbitrary agent behavior directly, the system can:
1. interpret intent into a Mission Proposal
2. constrain it through templates, policy, and approval
3. compile it into an evaluable authority region
4. use FGA, AuthZEN, or policy engines to answer specific runtime questions inside that region
That makes fine-grained authorization more tractable for open-world agents because the Mission narrows the search space and carries the governing context forward.
## Deployment Pattern Examples
The easiest way to understand the FGA and policy gap is to look at a few existing deployment patterns and ask what the Mission contributes in each one.
### Pattern 1: Gateway Enforcement with FGA Behind It
**Example**: a procurement assistant can create or update vendor onboarding records for vendors in procurement cycle `P-4421`.
Without Missions:
- OAuth gets the agent a token for the procurement APIs
- FGA can answer object-level questions such as "may this team update vendor `V-8821`?"
- but nothing cleanly carries the approved purpose, procurement-cycle boundary, jurisdiction limits, or terminal conditions across the run
With Missions:
- the Mission bounds the authority region to vendor-onboarding actions in procurement cycle `P-4421`
- the gateway uses the projected Mission reference to fetch or cache Mission context
- the gateway asks the PDP or FGA layer whether the requested vendor record is inside the approved selectors and relationships
- when the procurement cycle closes, a business event terminates the Mission and the gateway stops forwarding further writes
What the Mission adds is not another object-level policy decision. It adds the bounded authority context that tells the gateway and FGA system which object-level questions are even legitimate to ask.
### Pattern 2: Resource Server with Local Policy
**Example**: a CRM API already has local authorization logic and ownership checks, but now an agent is asked to "prepare renewal notes for accounts my team owns this quarter."
Without Missions:
- the RS sees a broadly scoped token such as `crm.write`
- the RS can apply its normal local policy
- but it has no standard way to know whether this write is part of the approved renewal mission, whether the mission is still active, or whether the write falls inside the approved business process and time bounds
With Missions:
- the token carries a projected Mission reference
- the RS either consumes Mission-derived claims or calls the Mission introspection profile
- local policy continues to decide object-level access, but now it can combine that with Mission state, Mission purpose, and Mission constraints
- the RS can deny writes when the Mission is suspended, expired, or outside the approved renewal process even if ordinary OAuth scope would otherwise allow the call
What the Mission adds is a portable governance layer above existing RS-local authorization logic.
### Pattern 3: FGA as the Runtime Decision Engine
**Example**: a support assistant is asked to update tickets related to incident `INC-4421`.
Without Missions:
- FGA can model relationships between users, teams, incidents, and tickets
- but the agent may discover relevant tickets, notes, and linked records dynamically at runtime
- the policy problem becomes open-ended because the system has no approved authority envelope for what counts as "part of this incident mission"
With Missions:
- the Mission expresses the approved authority envelope:
- ticket and note resource types
- incident-linked selectors
- allowed operation groups such as `ticket.update` and `note.append`
- process-state predicates such as "incident still active"
- FGA answers the narrower runtime questions:
- is ticket `T-5512` linked to incident `INC-4421`?
- is the current team allowed to update it?
- is the requested action inside the approved operation family?
What the Mission adds is the intermediate layer that makes open-world FGA queries tractable for agents rather than arbitrary.
### Pattern 4: Mixed Estate with Mostly Legacy Resource Servers
**Example**: an enterprise has many legacy internal APIs behind a gateway and cannot make every backend Mission-aware.
Without Missions:
- teams either issue broad workflow tokens or build ad hoc gateway metadata
- revocation and termination are mostly tied to token lifetime
- audit correlation across the mission is inconsistent
With Missions:
- the AS becomes the authoritative Mission lifecycle control point
- token exchange gates downstream token issuance on Mission state
- the gateway can enforce Mission-aware checks for high-value operations
- legacy backends continue to validate normal tokens without understanding the full Mission model
What the Mission adds is a practical migration path: stronger authority governance at the AS and gateway without requiring every RS to adopt a new model on day one.
Across all four patterns, the recurring gap is the same:
- OAuth provides token transport and audience binding
- FGA, AuthZEN, and local policy provide object-level decisions
- the Mission provides the bounded delegated authority context that makes those decisions coherent over time
# Deployment and Operational Model
## Cross-Domain Mission Authority
This section is included for architectural completeness, but it should be treated as a later profile rather than a near-term core standardization target.
The single-domain and same-trust-boundary story is the part of the architecture that is ready to be pressure-tested first. Cross-domain Mission transport, trust establishment, projection rules, and revocation propagation are all materially less settled and should not block the core Mission-Bound OAuth story.
Agent missions do not stay within a single authorization domain. An agent may authenticate at an enterprise Identity Provider (IdP), execute steps against internal APIs, and then need to reach APIs governed by a partner organization or SaaS provider.
The cross-domain case has two distinct problems:
**Same-IdP trust domain**: multiple resource servers share a common Identity Provider. In this case, a projected Mission-bearing assertion from the originating trust domain can act as the trust anchor for downstream token exchange, with the IdP validating Mission scope before issuing assertions for each Resource AS. This is the Identity Assertion Authorization Grant (ID-JAG) pattern and it works well within an enterprise where all resource servers federate to the same IdP.
**Different-IdP trust domain**: the receiving AS has no trust relationship with the issuing AS. The architecture needs a portable Mission assertion that the receiving AS can validate independently.
For the cross-domain case, the Mission reference needs a portable form:
- a signed Mission assertion (analogous to how ID-JAG packages an authentication event for cross-domain use) carries the mission purpose, constraints, and subject identity in a verifiable artifact
- the receiving AS validates the assertion against the issuing AS's published keys
- the receiving AS applies its own policy to determine whether the asserted mission scope is acceptable within its domain
The normative definition of this assertion format, filtering rules for which mission claims cross a trust boundary, and the federation trust mechanism connecting the two ASes are the open problems for the cross-domain profile. OpenID Federation is the natural trust infrastructure for the key discovery and trust chain validation. SSF/CAEP is the natural mechanism for propagating mission revocation across domains once the Mission terminates at the issuing AS.
The privacy concern applies here directly: subject identifiers in cross-domain Mission assertions may need pairwise forms, and mission semantics should be projected to only what the receiving domain needs for enforcement. A receiving AS should not receive the full compiled authority region from the issuing AS, only the subset relevant to the resources it governs.
### Cross-Domain Mission Assertion: Candidate Claim Structure
A Mission assertion crossing a trust boundary is a signed JWT (JWS) with the following candidate claim set:
```json
{
"iss": "https://as.example.com",
"sub": "user-123",
"sub_profile": "user",
"aud": "https://partner-as.example.org/token",
"act": {
"sub": "assistant-agent",
"sub_profile": "ai_agent"
},
"delegation_chain": [
{ "sub": "assistant-agent", "sub_profile": "ai_agent" }
],
"mission_ref": "mr_7M9Qx2",
"purpose": "urn:example.com:mission:schedule-meeting",
"mission_exp": 1773072000,
"projected_authority": {
"allowed_operations": ["calendar.schedule"],
"constraints": { "region": "EU" }
},
"exp": 1773072300,
"jti": "a8f3c91b-4d2e-4f7a-b5e6-9c0d1f2e3a4b"
}
```
Key projection rules:
- `sub` and `sub_profile` identify the user on whose behalf authority is asserted; pairwise subject identifiers SHOULD be used when crossing domains without an established federation agreement
- `mission_ref` is projected as-is or as a pairwise handle; the receiving AS maps it back to a locally-issued `mission_ref` for tokens it issues
- `projected_authority` is a filtered subset of the compiled authority region relevant to the receiving domain's resources; the issuing AS MUST NOT include authority elements outside the receiving domain's registered resource types
- `mission_exp` is the Mission's expiry at the issuing AS; the receiving AS MUST issue tokens that do not outlive this expiry
- The assertion's own `exp` SHOULD be short (e.g., 5 minutes) to limit replay; `mission_exp` governs the Mission's actual authority window
- OpenID Federation is the trust infrastructure for key discovery and trust chain validation between the two ASes
## Continuous Evaluation Checkpoint Model
Continuous evaluation does not mean a synchronous round-trip to the AS on every API call. That model does not scale at production volumes and creates a single point of failure when the AS is unavailable.
The recommended model is hybrid:
**Synchronous Mission check** before:
- token exchange for any new resource or delegation hop
- high-value or irreversible actions (defined by the Mission's risk classification)
- any action that modifies authority scope or delegation state
**Asynchronous signal-based check** for:
- high-volume, low-risk, reversible operations where the Mission is assumed valid until a termination signal arrives
The AS pushes Mission state change events via the Shared Signals Framework (SSF) and Continuous Access Evaluation Protocol (CAEP). Enforcement points subscribe to these events and act on them. When a termination event fires, enforcement points block further execution at the next checkpoint.
Which action type falls into which category should be policy-defined at Mission approval time, not left to ad hoc runtime behavior. A Mission that does not specify its evaluation checkpoint policy has not fully specified its governance model.
The revocation lag window between a termination signal and its enforcement at all checkpoints must also be policy-defined. Tighter lag reduces exposure but increases availability dependency. That tradeoff should be explicit.
## Business Event to Mission Termination
Business events are the terminal conditions that make the architecture real. The expired-mandate scenario (an agent still pulling sensitive data after its governing business event has closed) is not solved by better token expiry. It is solved by the procurement system publishing a "program closed" event that flows into the Mission layer and terminates the Mission independently of clock state.
The integration model:
1. At Mission approval, terminal conditions are declared: "this Mission terminates when event type `procurement.program.closed` is received for program `PRG-4421`"
2. Business systems publish state change events to an SSF event stream
3. A Mission evaluation service subscribes to that stream and evaluates incoming events against declared terminal conditions
4. When a match fires, the Mission transitions to `completed` or `revoked` and the AS stops issuing derivatives
5. A CAEP `mission-terminated` event propagates to subscribers so enforcement points can halt in-flight execution
For conditions that cannot be instrumented against machine-observable events, the Mission falls back to hard expiry. Even in the hard-expiry fallback, the Mission carries what tokens alone cannot: an authoritative mission record the rest of the security stack can query, a cascade revocation chain, and an audit trail attributing execution to a specific delegated purpose. As instrumentation improves, the condition model grows with it without requiring architectural changes.
**Event source authentication**: event sources that can trigger Mission termination are high-value attack targets. An adversary who can publish a fabricated terminal event for a known program identifier can terminate Missions they should not touch.
Event sources delivering terminal conditions MUST be pre-registered at the AS as part of Mission approval metadata. Registration includes:
- the event source identifier
- the event types the source is authorized to publish
- the SSF transmitter authentication credentials (as defined in SSF 1.0) used to authenticate the source's event stream
The AS MUST validate that an incoming terminal event originates from a pre-registered, authenticated transmitter before transitioning a Mission. Unauthenticated terminal events MUST be discarded.
**Replay protection**: terminal events are high-value single-fire triggers. The AS MUST implement replay protection for terminal events:
- Each event MUST carry a unique event identifier (`jti` or equivalent) and a timestamp.
- The AS MUST maintain a short-lived record of processed event identifiers (at minimum, for the duration of the event stream's configured delivery window).
- An event with a duplicate identifier or a timestamp outside the AS's configured acceptance window MUST be discarded.
- Event timestamps MUST be monotonically non-decreasing within a single event stream; out-of-order events that predate the most recently processed event for the same program identifier MUST be rejected.
## Transaction Token Integration
OAuth Transaction Tokens (Txn-Tokens, [draft-ietf-oauth-transaction-tokens](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-transaction-tokens-08)) are designed to propagate trusted workload identity and call context across microservice boundaries inside a single enterprise. They complement Mission-Bound OAuth by providing the in-network context propagation layer that mission-bearing access tokens are not designed for.
The architectural relationship:
- **Mission-bearing access tokens** govern delegated authority at the OAuth boundary: what the agent is authorized to do, under which Mission, on whose behalf. They cross trust boundaries and are validated by RSes using standard JWT validation.
- **Txn-Tokens** propagate workload context within a trusted service mesh: who called whom, with what initiating token, and for what transaction. They are typically scoped to a single enterprise network boundary.
A Txn-Token issued inside an enterprise may carry a `mission_ref` claim linking the in-flight transaction to its governing Mission. This allows internal services that do not receive the original mission-bearing access token to still have access to the Mission reference for audit, policy evaluation, or context propagation.
The integration model:
1. An external or orchestration-layer request arrives at the edge with a mission-bearing access token containing `mission_ref`
2. The edge service creates a Txn-Token for the internal call chain, carrying workload identity, the initiating token reference, and the `mission_ref` from the incoming access token
3. Internal microservices that receive only the Txn-Token can resolve Mission state via the AS introspection endpoint using the Mission introspection profile and the `mission_ref` the Txn-Token carries
4. Audit records from internal services carry `mission_ref`, linking all steps in the call chain to the governing Mission
This is the correct model for trusted-network deployments where not every internal service hop involves a full OAuth token exchange. The Txn-Token carries the Mission reference as context, not as authority. Authority for each internal service remains governed by its own policy decision, informed by the Mission context the Txn-Token provides.
The `mission_ref` claim in a Txn-Token should be treated as informational context, not as a delegated authority grant. The Txn-Token model does not replace Mission lifecycle enforcement: services that need to enforce Mission state must still check it via introspection, not by trusting the `mission_ref` claim alone.
Deployments MUST NOT silently downgrade from Mission-aware enforcement to token-only enforcement because an introspection or authority-model retrieval path is unavailable. Any fallback to weaker enforcement semantics must be an explicit deployment policy decision with documented risk, not an automatic runtime behavior.
## Policy Integration
Mission-Bound OAuth is not trying to replace policy engines. Its role is to define mission authority semantics and how they integrate with OAuth.
Constraint evaluation may be delegated to a policy system such as the AuthZEN Authorization API. In that model:
- Mission-Bound OAuth defines the lifecycle and authority object
- AuthZEN evaluates whether a specific action on a specific resource is allowed under current policy
That separation matters because it keeps the architecture focused on authority semantics and processing points rather than on a single PDP technology.
## Policy Conflict Precedence
IAM systems need a deterministic answer when multiple control sources disagree.
The precedence model should be:
1. issuer and tenant boundary checks
2. Mission lifecycle state
3. client registration and permitted Mission classes
4. hard policy denies
5. Mission Authority Model authority region
6. runtime relationship and environmental predicates
7. local RS or gateway policy narrowing
This means:
- if the Mission is revoked, everything stops regardless of lower-layer policy
- if registration does not permit the Mission class, approval cannot create it
- if hard policy denies a resource or action, user approval cannot override it
- if the Mission Authority Model allows a region but runtime predicates fail, the request is denied without changing the Mission
- RS and gateway policy may narrow or block execution further, but should not widen authority beyond the Mission
## How AuthZEN Fits
OpenID AuthZEN fits naturally into this architecture as the **decision interface** between policy enforcement points and policy decision points. It does not replace [Mission-Bound OAuth](#architecture-overview), and Mission-Bound OAuth does not replace it.
The clean division is:
- Mission-Bound OAuth defines the Mission authority object, lifecycle, delegation semantics, and OAuth processing checkpoints
- AuthZEN provides a way for an AS, gateway, or resource server acting as a PEP to ask whether a specific action on a specific resource is allowed under current policy
In that model:
- the AS is often a PEP at issuance and token exchange time
- a gateway may be a PEP at request time
- an RS may also be a PEP when it has sufficient context
- the PDP evaluates mission-derived context, actor context, resource context, and environmental context
That means the architecture can support both:
- **embedded decisioning**, where the AS evaluates policy internally
- **externalized decisioning**, where the AS or gateway calls an AuthZEN-compatible PDP
In more advanced deployments, the Mission may need to contribute not just literal attributes but a compiled set of constraints, selectors, and relationship predicates that the PDP can evaluate at runtime. The compiled authority example from the Authority Compilation Boundary section is the expected input to an AuthZEN-style query.
## Mission Evaluation Input Model
To make the open-world model credible, the architecture needs a clearer statement of what a Mission-aware runtime decision consumes.
A Mission-aware decision input should include at least:
- **actor context**: client, user, delegated actor, authentication strength
- **mission context**: mission identifier, state, purpose, expiry, delegation state
- **authority context**: compiled selectors, operation groups, relationship predicates, compliance constraints
- **resource context**: target resource type, instance, container, attributes
- **action context**: requested operation, operation group, execution mode
- **environment context**: time, region, network, tenant, process stage
A sketch:
```json
{
"actor": {
"client_id": "assistant-agent",
"subject": "user-123",
"delegation_depth": 0
},
"mission": {
"mission_ref": "mr_7M9Qx2",
"state": "active",
"purpose": "urn:example.com:mission:schedule-meeting",
"exp": 1773075600
},
"authority": {
"operation_groups": [
"calendar.schedule",
"crm.record_activity"
],
"selectors": [
"calendar:user-manageable",
"crm:accounts-owned-by-requesting-team"
],
"constraints": {
"region": "EU",
"business_process": "meeting-followup"
}
},
"resource": {
"type": "crm.account_note",
"account_id": "acct_123"
},
"action": {
"name": "create",
"group": "crm.record_activity"
},
"environment": {
"region": "EU",
"workflow_stage": "post-meeting"
}
}
```
## Explainability and Administrative Visibility
IAM governance is not complete if a Mission can be denied, attenuated, or suspended without intelligible reasons.
At minimum, the model should support two explanation views:
- **machine-readable** reasons for clients and agents
- **human-readable** reasons for administrators, approvers, and auditors
Machine-readable reasons should identify:
- which Mission was evaluated
- which checkpoint failed
- whether the failure is temporary or terminal
- which policy or predicate class caused the denial
Human-readable reasons should identify:
- the attempted Mission action
- the governing policy or lifecycle rule
- who or what caused the state change
- what remediation path exists, if any
This is particularly important for:
- mission proposal denial
- mission attenuation
- mission suspension
- token exchange denial under `mission_authority_exceeded`
- cross-domain assertion rejection
## How MCP Fits
The document also needs to say how MCP fits, because many agent deployments now use it as the tool and context plane.
MCP is not an authority protocol. It helps agents discover tools, exchange structured context, and invoke capabilities. In this architecture, MCP sits **above** Mission-Bound OAuth.
The clean relationship is:
- MCP exposes tools, schemas, and invocation surfaces to the agent
- the agent uses MCP to understand what tools exist and what inputs they need
- Mission-Bound OAuth determines whether the agent is actually authorized to use those tools in the current Mission
- OAuth tokens and token exchange provide the concrete access artifacts for the underlying APIs
MCP can be relevant in at least three places:
- **tool discovery**: which tools and operations are even available to plan against
- **proposal construction**: mapping tool schemas and operation metadata into Mission Proposal candidates
- **runtime enforcement hints**: attaching operation metadata that helps the AS, gateway, or PDP map a tool call to resource/action policy
The architectural boundary should be explicit: MCP is the tool-interaction layer; Mission-Bound OAuth is the delegated authority layer.
## Discovery and Metadata
Any eventual spec work will need discovery metadata so clients can determine whether an AS supports Mission-Bound OAuth.
Likely areas for discovery include:
- mission proposal endpoint support
- approval endpoint support
- supported Mission reference mechanisms
- token exchange behavior
- delegation capabilities
- policy integration hints
- mission introspection endpoint
For the umbrella architecture, it is useful to distinguish:
- OAuth AS metadata for core protocol support
- mission management metadata for proposal, approval, and template features
- resource metadata for mission-aware enforcement capabilities
# Security Considerations
## Threat Model
The threat model is aimed at reducing:
- **ghost execution**: agents acting without approved Mission authority
- **privilege creep**: Missions accumulating more authority than was approved
- **uncontrolled delegation**: one actor silently widening a chain of authority
- **token replay**: stolen artifacts being reused outside intended conditions
- **actor substitution**: a different actor than the one the Mission approved presenting mission-bearing tokens at exchange time, either through token theft, confused-deputy exploitation, or forged actor chain claims
Mission-Bound OAuth does not eliminate normal OAuth threats. It adds controls for mission continuity and bounded authority on top of the existing security baseline.
## Security Properties to Preserve
For the proposal to be credible, it should preserve the following:
- access tokens remain audience-bound
- mission state is authoritative at the AS
- downstream issuance is denied when mission state is invalid
- delegation is explicit and bounded, represented via `act` (current actor) and `delegation_chain` (full ordered history)
- approval and execution remain separate trust steps
- policy decisions are explainable and auditable
- mission audit events are signed and independently verifiable
## AS as Critical Security Infrastructure
The AS holds authoritative Mission state and Mission Authority Model artifacts for every active agent execution it governs. Compromise of the AS or its Mission store is compromise of execution governance across every Mission it controls. Hardening requirements mirror those of authorization servers generally. Mission Authority Model artifacts must be signed so enforcement points can validate them without a live callback. Fail-closed behavior is required: an unavailable AS halts token exchange and Mission introspection rather than defaulting to open. Every Mission issuance, amendment, and revocation is a first-class security event with a full audit record.
## Sender Constraining Requirements
Sender-constraining using DPoP (RFC 9449) or mTLS (RFC 8705) is a baseline expectation, not optional hardening. A deployment that accepts unsigned bearer mission-bearing tokens at token exchange is accepting meaningful replay risk against a durable authority chain. At least one deployment profile should treat sender-constraining as a core assumption that applies to both primary mission-bound access tokens and any tokens produced by mission-scoped exchange.
## Display Object Validation
Template-driven display validation is a security requirement, not a UX convenience. An orchestration layer that supplies a `display` object claiming a benign action while the compiled authority permits something broader has created a social engineering attack surface that defeats the human-in-the-loop guarantee. The AS must validate or regenerate the display object from the compiled authority before presenting it to an approver. A display object that cannot be verified against the compiled authority region must be rejected or overwritten by the AS.
## Concurrency and Atomicity Requirements
Mission-Bound OAuth places the AS as the authoritative state owner for Mission lifecycle, delegation depth, and compiled authority. Under concurrent load, several AS operations must be atomic to preserve correctness guarantees.
**Mission lifecycle state transitions** (Issue 7.2): the AS MUST evaluate Mission lifecycle state and issue a token within the same atomic operation. A Mission suspension event and a concurrent token exchange request MUST be serialized. The AS MUST NOT issue a token for a Mission that is in `suspended`, `revoked`, `completed`, or `expired` state at the moment of issuance, regardless of the order in which the suspension and exchange requests arrived.
**Delegation depth enforcement** (Issue 7.3): `current_depth` increments MUST be performed atomically using a compare-and-increment operation. The AS MUST reject any token exchange that would result in `current_depth >= max_depth` at the time the increment is committed, even if the exchange request was received when `current_depth` appeared valid. Concurrent delegation exchanges MUST be serialized per Mission.
**Concurrent expansion proposals** (Issue 7.1): expansion proposals submitted concurrently against the same parent Mission MUST be serialized using the Mission's version counter. The AS increments the version counter on each accepted expansion. A concurrent expansion proposal that was evaluated against a prior version MUST be rejected with a concurrency error, requiring the submitter to re-evaluate against the updated Mission. This prevents two independently-valid expansions from collectively exceeding the authority envelope.
**CAEP-triggered Mission suspension** (Issue 7.4): when the AS receives a CAEP `session_revoked` event for a user session, it must suspend all active Missions for that session. This suspension MUST be treated as a lifecycle state transition and MUST be serialized with any concurrent token exchange requests against the affected Missions. The AS MUST NOT issue a token for a Mission that is simultaneously being suspended by an incoming CAEP event. The implementation model is the same as for lifecycle transitions: the CAEP handler acquires the same per-Mission lock before transitioning state, and any concurrent exchange request that arrives during suspension processing receives `mission_suspended`.
**Implementation guidance**: optimistic locking with a version counter on the Mission record is the recommended implementation pattern. The AS should expose the Mission version in `GET /missions/{mission_ref}` responses so clients can detect version changes between reads and submissions.
# Privacy Considerations
## Privacy and Linkability
Mission-Bound OAuth creates a risk of durable mission correlation across APIs and domains if Mission references and attributes are exposed too broadly.
The privacy model should therefore assume:
- the projected Mission reference is opaque and unguessable
- Mission-derived claims are minimized per audience
- resource servers receive only the Mission data needed for enforcement
- introspection and token exchange avoid exposing Mission semantics unnecessarily
- cross-resource correlation is limited where possible by deployment policy or identifier strategy
- subject identifiers in cross-domain Mission assertions should use pairwise forms where appropriate
This has to be explicit, otherwise the design looks like a powerful coordination primitive but also a powerful tracking primitive.
**Per-audience Mission references** address the correlation concern more directly. A single visible Mission handle across multiple resource servers creates a cross-service tracking handle even when the identifier reveals no Mission semantics. RS-A and RS-B can correlate all workflow traffic using the shared handle without any explicit data-sharing agreement.
Three deployment approaches are available, each with different tradeoffs:
1. **Single handle with audience-isolation policy**: use one projected Mission reference but control which resource servers receive it in their access tokens via audience-specific claim filtering at the AS. Resource servers that do not need cross-Mission correlation receive tokens without the Mission reference claim.
2. **Per-audience Mission references**: the AS maintains one internal Mission record and maps it to per-audience handles in issued tokens, analogous to pairwise subject identifiers in OpenID Connect. Each resource server receives a different handle that the AS maps back to the internal Mission for introspection and revocation. Cross-RS correlation is possible only through the AS, not by comparing token claims directly.
3. **Claim-free enforcement via introspection**: the Mission reference claim is omitted from audience-specific access tokens entirely. Resource servers that need Mission state use the introspection profile with a session-scoped reference that does not expose the Mission correlation handle in tokens.
Deployments that require cross-RS audit correlation (compliance, regulated workflows) may use option 1 or 2. Deployments where cross-RS linkability is a privacy concern should prefer option 2 or 3. The architecture does not mandate a single approach, but a deployment profile should specify which model it uses and what the privacy implications are.
## Retention and Governance
Enterprise IAM deployments will also care about retention classes for Mission artifacts.
Retention will likely differ for:
- Mission Proposals, including denied proposals
- Approved Missions
- Mission Authority Model representations
- Mission audit events
- cross-domain Mission assertions
- introspection records or access logs
A sensible default model is:
- Mission Proposals: retained for governance and forensic review, especially when denied or attenuated
- Approved Missions: retained for the duration of their lifecycle plus policy-defined audit retention
- Mission Authority Model representations: retained at least as long as needed for replayable audit and explainability
- Mission assertions: retained according to federation and compliance policy, usually shorter than the Mission itself
The main architectural point is that Mission governance creates artifacts with different sensitivity and retention needs. Treating them all as generic auth logs would be an enterprise design mistake.
# Practical Limits and Realistic Claim
This architecture is intentionally ambitious. It is also easy to be too optimistic about where the hard parts really are. The protocol surfaces are only one part of the problem. Real deployments fail for data, governance, operational, and organizational reasons long before they fail for lack of a token claim.
## Policy Authoring May Be Harder Than the Protocol
The architecture assumes enterprises can define:
- Mission templates
- operation groups
- authority envelopes
- relationship predicates
- approval policies
- business event termination conditions
In practice, many organizations struggle to maintain even scope inventories and role mappings. Mission-Bound OAuth will fail if policy authoring becomes too complex for platform teams to manage coherently.
## Relationship and Business Data May Be Wrong
Much of the promise of Mission-bound enforcement depends on external data being accurate:
- who owns an account
- which calendars a user can manage
- what tenant a record belongs to
- which process stage a task is in
- whether a compliance boundary applies
If those sources are stale or inconsistent, the Mission will be evaluated incorrectly. The architecture should assume imperfect upstream systems and treat some predicates as high-assurance only when backed by reliable sources of record.
## Open-World Envelopes Are Hard to Tune
The bounded authority envelope is the right model, but tuning it is difficult.
- if the envelope is too narrow, agents fail constantly and users are pushed into repeated re-approval
- if the envelope is too broad, the system recreates over-delegation with more structure but not more safety
The practical challenge is not just defining the envelope format. It is defining envelopes that are operationally usable and still meaningfully bounded.
## Approval UX May Collapse into Rubber Stamping
If Mission approvals are too frequent, too technical, or too hard to understand, users and administrators will approve them mechanically. At that point, the architecture still has lifecycle and audit value, but the human-governance story becomes much weaker than the document implies.
Approval is only a security control if:
- the approver understands what is being approved
- the approval frequency is tolerable
- the attenuation path is usable in practice
## The Authorization Server May Become a Bottleneck
Mission-Bound OAuth places more responsibility on the AS:
- Mission storage
- lifecycle transitions
- approval integration
- token exchange enforcement
- compiled authority evaluation or policy calls
- introspection
That increases both scaling pressure and blast radius. If the AS or its dependent policy systems become slow or unavailable, mission execution may stall. A practical deployment needs a clear resilience model, not just a correctness model.
## Cross-Domain Adoption Will Likely Be Slow
The single-domain and same-trust-domain cases are the most realistic near-term targets. Cross-domain Mission assertions require:
- trust establishment
- constrained claim projection
- policy compatibility between issuers
- revocation propagation
- privacy-safe subject handling
That is possible, but not likely to arrive quickly across a broad SaaS ecosystem. The architecture should be practical about this and avoid over-claiming cross-domain near-term interoperability.
## Resource and Operation Normalization May Never Fully Converge
The document assumes useful structure for:
- resource types
- selectors
- operation groups
- relationship predicates
That structure is necessary, but real ecosystems are heterogeneous. Different vendors will name and group actions differently. The practical goal is not perfect semantic portability. It is enough structure to make Mission evaluation tractable within a deployment and partially interoperable across similar deployments.
## Agents Can Still Route Around Governance
Mission governance only helps if execution actually goes through Mission-aware issuance and exchange paths. In the real world, agents and automation systems often have:
- legacy service credentials
- direct backend access paths
- side-channel APIs
- cached tokens or unmanaged connectors
If those paths remain, an agent may still act outside Mission governance. This architecture is strongest when paired with credential minimization and a policy that delegated machine access must flow through Mission-aware infrastructure.
## Revocation and Termination Are Not Instant
The architecture improves lifecycle control, but it does not eliminate lag:
- already-issued short-lived access tokens may remain usable until expiry
- event propagation may be delayed
- gateways may not all receive updates at the same time
- introspection may be cached or temporarily unavailable
This should be described as a bounded-exposure model, not as perfect real-time stop semantics.
## Compilation May Become Proprietary
One of the strongest ideas in the document is the Mission Authority Model as the boundary between open-ended intent and enforceable authority. That is also where vendor-specific divergence is most likely.
If every implementation has a radically different compiled authority model, interoperability may collapse at the most important architectural layer. The practical response is to standardize interfaces and processing checkpoints first, and be cautious about over-standardizing the compiled representation too early.
## Practical Claim
The realistic claim for Mission-Bound OAuth is not:
> this architecture solves open-world agent authorization.
The realistic claim is:
> this architecture gives IAM systems a governed authority object that makes bounded delegation, lifecycle control, auditability, and policy-backed token derivation materially better than token-only OAuth for agent systems.
That is strong enough to matter, and narrow enough to be credible.
# Staged Adoption Path
The pragmatism intent of this work is right, but the adoption path needs to be concrete. A staged deployment model avoids the "all or nothing" objection and shows how existing infrastructure can participate without a full replacement.
| Stage | Required Components | What a Deployment Gets |
|---|---|---|
| **Stage 1: Mission-Lite (AS-only, no management plane)** | Simple Mission record at the AS (`mission_id`, `state`, `exp`, `delegation.max_depth`); one additional validation step at token exchange; a projected Mission reference as an optional claim in issued access tokens; deployment-specific binding of requests to one Mission at a time | Mission authority that can be revoked independently of token expiry; a correlation key across all tokens produced by a Mission; bounded delegation enforcement at the AS |
| **Stage 2: Mission Management Plane** | `POST /mission-proposals` and approval endpoints; Mission templates; display representation for approval UX; AuthZEN integration for proposal-time policy evaluation; enterprise approval system integration | Governed proposal and approval flow with attenuation; human-readable consent display; policy-controlled Mission issuance without per-client custom code; reusable templates for common Mission types |
| **Stage 3: Cross-Domain and Continuous Evaluation** | Cross-domain Mission assertions; SSF/CAEP event stream; business event integration for condition-based termination; Mission introspection for live state queries; progressive Mission expansion for open-world agents | Mission governance across organizational boundaries; termination triggered by business events, not just clocks; resource servers that can make Mission-aware access decisions without full AS integration |
An existing Okta, Auth0, or Azure AD deployment can approximate Stage 1 today with custom authorization server policy, one-Mission-per-runtime-token discipline, and a lookup table or token-bound Mission reference. That approximation does not deliver the full protocol described earlier in this RFC: it is a deployment pattern that captures the core AS-side lifecycle and exchange checks before standardized proposal APIs, PAR-based Mission authorization requests, or Mission introspection are introduced.
Stage 1 should be understood for what it is: primarily AS-side Mission governance at issuance and exchange time, not full downstream Mission-aware object-level authorization at every resource server.
This is the entry point the architecture should lead with for any team evaluating adoption.
Each stage delivers independent value. A team that deploys only Stage 1 gets the core lifecycle and revocation property. Each subsequent stage adds governance depth. This is the pragmatic path that meets the ecosystem where it is today.
# Open Questions
Rather than an undifferentiated list, the open questions now fall into a few decision clusters. The priority order here reflects what seems most likely to determine whether this architecture becomes practical and spec-worthy.
## Priority 1: Core Viability
These questions decide whether the core Mission-Bound OAuth idea is coherent enough to standardize at all.
- what is the minimum standard Mission schema, and what remains deployment-specific
- should Mission authority be defined as a strict constraint layered on top of scope authority
- what is the right projected Mission reference model through RFC 8693 token exchange, especially when the subject token is already bound to one Mission
- what normative error model is needed for Mission lifecycle failures at token exchange
- whether the first serious profile should require sender-constraining rather than treat it as a strong baseline expectation
- what normative requirements the core spec should place on verified agent binding: specifically, whether exchange-time client identity checks, DPoP key continuity across delegation hops, and AS-authoritative actor chain derivation should be MUST-level requirements in the core spec or left to a security profile
- how Mission-Bound OAuth should relate to downstream capability attenuation and chaining mechanisms, such as Biscuit-style restriction-bearing tokens, without taking on that whole problem in the core Mission model
- whether `mission_ref` and `delegation_chain` should be formally proposed for registration in the IANA JSON Web Token Claims registry (RFC 7519 Section 10.1), and whether a standalone Internet-Draft is needed for the `delegation_chain` claim given its proposed deviation from RFC 8693 `act` nesting semantics
## Priority 2: Open-World Practicality
These questions decide whether the architecture actually helps with adaptive agents rather than rebranding closed-world allowlists.
- how Missions should express constraint-driven authority beyond explicit allowlists
- whether a compiled graph or DAG form should exist between mission authoring and runtime enforcement
- how relationship-, process-, and jurisdiction-based constraints should be represented
- how bounded authority envelopes should be defined and tuned in practice
- whether MCP-derived tool metadata should have a standard mapping into Mission proposals
## Priority 3: Governance and IAM Model
These questions decide whether the architecture works as a governance system rather than only a protocol sketch.
- how mission authoring, template authoring, and policy authoring should be separated
- whether Mission approval belongs inside the OAuth protocol family or in an adjacent management profile
- how headless and service-to-service Missions should be provisioned and activated
- whether the authorized caller model for `POST /mission-proposals` is the right trust boundary
- what is the right normative behavior when CAEP or session-level revocation intersects active Missions
- whether Mission amendment (narrowing scope, reducing delegation depth, shortening expiry on an active Mission) should be a first-class operation via a dedicated endpoint, or whether revocation and re-issuance is the only supported path for structural changes
- how approval evidence should be represented if approval is delegated to an external workflow or ticketing system
## Priority 4: Enforcement and Resource Server Semantics
These questions decide how much of the model resource servers and gateways need to understand.
- what parts of Mission state should be exposed to resource servers
- how the Mission introspection profile should surface lifecycle and compiled authority
- whether the projected Mission reference should be single-handle, pairwise per-audience, or omitted from audience-specific access tokens
- what the baseline RS behavior should be when token scope is broader than the Mission's compiled authority region
- whether full Mission Authority Model retrieval should remain purely a high-assurance profile or eventually become a more general interoperability mechanism
- how deployments should prevent silent downgrade from Mission-aware enforcement to token-only enforcement during outages
## Priority 5: Cross-Domain and Standardization Boundary
These questions matter, but they should not block the more local single-domain story.
- how cross-domain Mission assertions should be structured and what claim set should cross a trust boundary
- whether cross-domain assertions should preserve source Mission references or map to local identifiers
- whether the staged adoption model changes what belongs in the core spec versus deployment profiles
- which parts belong in a core OAuth draft versus companion specs
- how Mission-Bound OAuth relates to GNAP and whether extending OAuth is still the right strategic choice
## Lower-Level Naming and Registration Questions
These matter for a draft, but they should be downstream of the architectural decisions above.
- how discovery metadata should be named and registered
- whether the `authorization_details` type identifier should be `"mission"`
- whether the projected Mission reference should be a registered JWT claim and whether it should be string or structured
- whether mission templates should be standardized or remain deployment-local
- how much prompt-processing behavior should be specified versus treated as implementation guidance
# FAQ
## Is this just RAR with more state?
No. RAR structures authorization input for a token request. Mission-Bound OAuth adds a durable, lifecycle-governed authority object that survives across token issuance, exchange, suspension, revocation, and business-event termination.
## Why isn't a refresh token enough?
A refresh token extends credential continuity. It does not represent a governed authority object with purpose, lifecycle, terminal conditions, approval evidence, and bounded delegation semantics. Mission-Bound OAuth is trying to govern delegated authority, not just prolong token issuance.
## Why not just use GNAP?
GNAP is the cleaner architecture for new systems. The practical bet here is that many enterprises will try to solve this inside existing OAuth infrastructure first. Mission-Bound OAuth exists to test whether that incremental path is viable.
## Why isn't AuthZEN or FGA enough by itself?
AuthZEN and FGA answer runtime authorization questions. They do not by themselves define the durable authority object that says why this execution exists, how long it should continue, when it must terminate, or what bounded envelope it may explore. The Mission is the missing middle layer between open-ended agent intent and runtime policy decisions.
## What about Biscuit-style attenuation and chained restrictions?
That is a real adjacent gap, but it is not the same layer. Mission-Bound OAuth governs whether a delegated Mission should exist, continue, expand, suspend, or terminate. Biscuit-style capability tokens are more about downstream cryptographic attenuation, offline verification, and chained restriction accumulation after authority has already been granted.
The important relationship is that any downstream attenuation or chaining mechanism should remain bounded by the Mission and its authority model. Mission-Bound OAuth does not need to solve that whole problem in the first core spec, but it should leave room for separate profiles or companion work that bind attenuation-capable artifacts to `mission_ref`.
## Isn't this too much responsibility for the Authorization Server?
Possibly. That is a real operational risk. The architecture is practical only if there is a staged adoption path, selective externalization of policy, and a resilience model that avoids synchronous dependency on the AS for every request.
## Does every deployment need Mission proposals and approval APIs?
No. The architecture includes them because they matter for the end-to-end story, but the staged adoption path explicitly allows simpler deployments to start with AS-side Mission records, provisioning-time Mission types, or admin-configured Mission activation before adopting a full management plane.
## Aren't you just inventing another policy language?
No. The architecture assumes policy languages and PDPs already exist. The Mission is the governed authority object and compiled context that makes those policy systems usable for delegated agent execution.
## Is the Mission Authority Model a new universal policy language?
No. The current design is to standardize the slots, checkpoints, and protocol boundaries, not a universal selector or policy syntax. Different deployments may use different underlying policy engines and compilation strategies.
## Does this require every resource server to become Mission-aware?
No. A baseline deployment can keep Mission enforcement mostly at the AS and token-exchange layer. Mission-aware gateways and RSes add stronger runtime guarantees, but they are not the only deployment model.
## Why keep both `mission_id` and `mission_ref`?
Because they serve different trust boundaries. `mission_id` is the AS-internal authoritative identifier. `mission_ref` is the projected protocol-facing handle used in tokens, token exchange, introspection, audit, and cross-domain assertions. Separating them improves privacy, tenant isolation, and future cross-domain flexibility.
## Why is `delegation_chain` needed if OAuth already has `act`?
`act` identifies the current acting party. It does not give a full ordered multi-hop history in a form that is easy to audit or evaluate across longer agent chains. `delegation_chain` is proposed because Mission-bound delegation is one of the main use cases here, but whether it belongs in the first core spec is still a legitimate question.
## Why not let the AS infer the Mission from the requested resource?
Because implicit Mission selection is a confused-deputy risk. The safer model is that the subject token is already bound to one Mission, and the AS uses that binding or verifies an explicitly echoed `mission_ref`. Resource-based guessing would create ambiguity and make cross-mission mixups easier to miss.
## Does this require progressive Mission expansion?
No. Expansion is now treated as an optional later capability. A simpler deployment can adopt the stricter rule: if additional authority is needed, create a new Mission.
## Does this solve open-world agent authorization?
Not completely. The realistic claim is narrower: it provides a bounded authority object and a governance model that makes open-world agent execution more controllable than token-only OAuth.
## Is `mission_ref` itself sensitive?
Yes, at least as operational metadata. It is not an authority credential by itself, but it is still a durable correlation handle. That is why the document treats projection, pairwise handles, tenant-scoping, and controlled exposure as security and privacy concerns rather than implementation details.
## What happens when Mission-aware enforcement is unavailable?
The architecture prefers fail-closed behavior for Mission introspection and authority-model retrieval. It should not silently degrade into weaker token-only enforcement unless a deployment explicitly chooses that risk as policy.
## Is cross-domain federation realistic in the near term?
Only in limited settings. Same-trust-domain and single-enterprise deployments are the realistic starting point. Cross-domain Mission assertions are likely a later profile, not the first thing the ecosystem will adopt.
## Is this trying to turn OAuth into a workflow engine?
No. The goal is narrower: add a governed Mission object that can bind delegated authority to purpose, lifecycle, delegation bounds, and termination semantics. The workflow or agent runtime still lives elsewhere.
# IANA Considerations
This section documents the IANA registration candidates proposed by Mission-Bound OAuth. All entries are provisional and subject to change based on community feedback.
## OAuth Extensions Error Registry (RFC 6749 Section 11.4)
The following error codes are candidates for registration in the OAuth Extensions Error Registry:
| Error Code | Usage Location | Description |
|---|---|---|
| `mission_suspended` | Token endpoint error response | The referenced Mission is in `suspended` state. |
| `mission_revoked` | Token endpoint error response | The referenced Mission has been permanently terminated. |
| `mission_expired` | Token endpoint error response | The referenced Mission lifetime has elapsed. |
| `mission_completed` | Token endpoint error response | The referenced Mission reached its normal terminal state. |
| `mission_delegation_depth_exceeded` | Token endpoint error response | The requested delegation would exceed the Mission's `max_depth`. |
| `mission_authority_exceeded` | Token endpoint error response | The requested resource or action is outside the Mission's compiled authority region. |
| `mission_not_found` | Token endpoint error response | No Mission matching the provided `mission_ref` exists for this client. |
| `mission_ref_required` | Token endpoint error response | Multiple active Missions exist for this client and the request did not specify which Mission governs the exchange. |
| `mission_expansion_conflict` | Expansion endpoint error response (HTTP 409) | An expansion proposal was evaluated against a prior Mission version; a concurrent expansion was already accepted. Client must re-fetch and resubmit. |
| `mission_expansion_outside_envelope` | Expansion endpoint error response (HTTP 422) | The requested extension exceeds the parent Mission's approved authority envelope. A new Mission proposal is required. |
## JSON Web Token Claims Registry (RFC 7519 Section 10.1)
**Claim name**: `mission_ref`
**Claim description**: Projected Mission reference handle. Identifies the Mission governing the token's authority derivation. Opaque string; value is audience-specific in privacy-sensitive deployments.
**Change controller**: IETF
**Specification document**: draft-mcguinness-oauth-mission-bound-authorization
**Claim name**: `delegation_chain`
**Claim description**: Ordered array of actor identity objects representing the full delegation chain. Index 0 is the current immediate delegate (matching `act.sub`); the last index is the originating delegating actor (the first principal to receive delegated authority for the subject). The array grows at the tail as delegation chains extend. Each element contains at minimum `sub` and optionally `sub_profile` and `iss`.
**Change controller**: IETF
**Specification document**: draft-mcguinness-oauth-mission-bound-authorization
## Authorization Details Types Registry (RFC 9396 Section 10)
**Type**: `mission`
**Description**: References an approved Mission governing the delegated authority for this authorization or token exchange request.
**Required fields**: `type` (string, value `"mission"`), `mission_ref` (string, opaque projected Mission reference)
**Optional fields**: none in the core type; deployment profiles may define extensions
**Change controller**: IETF
**Specification document**: draft-mcguinness-oauth-mission-bound-authorization
## OAuth Parameters Registry (RFC 6749 Section 11.2)
No new standalone OAuth token endpoint parameters are proposed. Mission reference is carried via `authorization_details`.
## OAuth Dynamic Client Registration Metadata Registry (RFC 7591 Section 3.2)
| Metadata Field | Type | Description |
|---|---|---|
| `mission_types` | array of strings (URI) | Purpose class URIs the client is permitted to propose |
| `mission_delegation_max_depth` | integer | Maximum delegation depth; defaults to 0 |
| `mission_approval_mode` | string | `"interactive"`, `"policy_auto"`, or `"pre_provisioned"` |
| `mission_resource_categories` | array of strings | Coarse-grained resource type filter applied before template constraints |
## Authorization Server Metadata Registry (RFC 8414)
| Metadata Field | Type | Description |
|---|---|---|
| `mission_proposal_endpoint` | URL | Endpoint for submitting Mission proposals (`POST /mission-proposals`) |
| `mission_activate_endpoint` | URL | Endpoint for activating pre-provisioned mission types (`POST /missions/activate`); present only when `pre_provisioned` is in `mission_approval_modes_supported` |
| `mission_supported` | boolean | Whether the AS supports Mission-Bound OAuth |
| `mission_approval_modes_supported` | array of strings | Approval modes supported by this AS: `"interactive"`, `"policy_auto"`, `"pre_provisioned"` |
| `mission_purpose_classes_supported` | array of URI strings | Purpose class URIs the AS accepts in Mission proposals; clients SHOULD check this list before submitting a proposal to avoid 403 failures |
| `mission_scopes_supported` | array of strings | Mission management scopes the AS supports; expected values include `mission:propose`, `mission:activate`, `mission:expand`, `mission:authority-model:read`, `mission:introspect:authority` |
Mission introspection reuses the existing RFC 7662 introspection endpoint (discovered via the standard `introspection_endpoint` AS metadata field) with `token_type_hint=mission_ref`. No new introspection endpoint URI is needed.
# Appendix: Expected Draft Structure
If this architecture survives feedback, I expect it to split into something like:
- a core Mission-Bound OAuth draft covering Stages 1 and the token model
- a mission management and proposal profile covering Stage 2
- a cross-domain mission assertion profile
- operational guidance for gateways, eventing, business event integration, and policy integration
That is one of the main reasons to publish this as a blog RFC first: to get feedback on where the boundaries should actually be, which parts of the staged model belong in which document, and whether the core claim is actually strong enough to justify standardization.
The goal of this work is not to turn OAuth into a workflow engine. The goal is to give OAuth a first-class way to bind delegated machine authority to an approved Mission with purpose, constraints, delegation bounds, and lifecycle.
The key claim is narrow and testable: a durable, AS-maintained Mission object that governs token derivation produces meaningfully stronger Mission governance than RAR at individual token issuance points, because it makes Mission authority independently revocable, auditable at the Mission level rather than the token level, and terminable by business events rather than only by clocks.
If that claim holds up under feedback, the next step is to extract the pieces that deserve standardization and draft them separately with narrower scope, starting with the Stage 1 core.
If you work in OAuth, IAM, policy systems, agent platforms, or enterprise security architecture, the most useful feedback is on the [Priority 1](#priority-1-core-viability) and [Priority 2](#priority-2-open-world-practicality) questions above: whether the core [Mission](#key-terms) object is the right primitive, and whether the bounded authority envelope model is practical enough for real open-world agent systems.
---
# Agents Don't Need Your Passport. They Need Your Authority.
Canonical URL: https://notes.karlmcguinness.com/notes/agents-dont-need-your-passport-they-need-your-authority/
Markdown URL: https://notes.karlmcguinness.com/notes/agents-dont-need-your-passport-they-need-your-authority.md
You've registered your agents as principals in your Identity Provider. You've federated workload credentials, deployed Non-Human Identity controls, and tightened scopes. The agent has an identity, its secrets are vaulted, its access is governed. And your CFO's research agent is still running at 2:05 PM, pulling pre-IPO financials, on a mandate that expired when the board approved the presentation at 2:00 PM.
Check every control: the token is valid, authorization policy permitted the request, Just-In-Time (JIT) provisioning and permissions granted time-based least privilege access, and [Continuous Access Evaluation Protocol (CAEP)](https://openid.net/specs/openid-caep-specification-1_0.html) is publishing revocation events. Every layer shows green. The breach is structurally invisible, not because the stack failed, but because no layer of it was built to ask whether the mission behind the request should still be running.
When an agent acts on your behalf, it does not need your identity. It needs your authority. Those are not the same thing, and enterprise security stacks were built as if they were. Every IAM control in that stack is optimized for a request-scoped question: may this action proceed right now? The question agents expose is different in kind. It is mission-scoped: should this execution still be running at all? No widely adopted IAM pattern owns that question as a first-class concern.
The IAM systems governing enterprise access today were designed around a foundational assumption: humans execute actions. A person authenticates, crosses a boundary, acts, and stops. The security boundary and the execution boundary were the same thing because presence and pacing enforced both.
Agents remove those constraints. They don't stop when the task ends. Credentials don't expire when purpose does. The result is ghost execution: an agent executing with valid credentials in service of a mandate that has already expired. NIST's National Cybersecurity Center of Excellence has [identified the gap](https://www.nccoe.nist.gov/projects/software-and-ai-agent-identity-and-authorization) as an active research area, noting in a concept paper that "existing enterprise practices were not built with AI agents in mind."
The industry is responding. [Market analysis of the emerging agentic identity space](https://softwareanalyst.substack.com/p/emerging-agentic-identity-access) maps investment flowing toward centralized brokering, Zero Standing Privileges, JIT provisioning, and intent-aware authorization. Better passports, more precisely stamped. That direction correctly addresses the problem the access layer owns. What agents expose is not a flaw in its design. It is what happens when the human who was implicitly governing mission scope is removed from the loop. *Should this execution still be running at all?*
---
# ๐ Identity vs. Authority
Agents force a distinction that has always existed, but was easy to ignore:
- **Identity**: establishes who an actor is at a boundary
- **Access**: determines whether a specific request may proceed
- **Delegation**: governs what an actor may do on behalf of another
- **Authority (Power of Attorney)**: governs whether and how an actor may continue to act
A passport lets you cross a border. A Power of Attorney (PoA) lets you act after crossing it. Modern systems have more mature coverage of the first two. Delegation is partially addressed but not standardized end-to-end. Authority, **a purpose-scoped, independently revocable right to act on behalf of another principal**, has no widely adopted equivalent in current enterprise security stacks. That is the gap agents are exposing.
Throughout this series, these terms carry specific meanings. **Authority** is the conceptual domain. **Mandate** is the concrete artifact that carries and enforces it. **Power of Attorney** is the legal analogy that makes both legible.
| Feature | Identity | Access | Delegation | Authority |
|---|---|---|---|---|
| Core Question | Who are you? | May this request proceed? | What may this actor do on behalf of another? | Should this execution still be running? |
| Duration | Bound to the authentication event | Bound to the token or session lifetime | Bound to the proxy grant | Bound to the mission or purpose |
| Enforcement | At authentication | At resource decision points | At delegation points | Throughout the execution lifecycle |
| Failure Mode | Impersonation | Unauthorized access | Privilege escalation | Scope creep / ghost execution |
# ๐ Identity Is Not Authority
Enterprise identity infrastructure is mature at both ends of the principal spectrum. Human identity is well-served by decades of federation, authentication, and lifecycle controls. Workload identity is likewise mature, with services authenticating to each other using cryptographically verifiable identities that require no human in the loop.
When agents arrived, the natural response was to fit them into one of those two boxes. The first approach is impersonation, in which the agent authenticates as, or with a delegated token from, the user it is acting for. If the user has access, the agent acting as the user has access. The second approach is credential vaulting, which involves provisioning the agent as a non-human principal, vaulting and rotating its credentials, and managing its lifecycle through PAM and NHI tooling. Both approaches have real value. Both fail to reach the problem agents actually introduce.
Impersonation conflates identity with authority. The agent does not need to be the user. It needs a bounded mandate to act for the user, scoped to a specific purpose, revocable independently of the user's credentials, and traceable as a distinct principal in audit logs. Impersonating the user gives the agent the full blast radius of the user's identity with no representation of the mission that justified the delegation. Audit logs record the action as the user's. The agent as a distinct accountable principal disappears from access reviews and entitlement audits.
Credential vaulting addresses a genuine vulnerability. Standing, unrotated credentials are a real risk. But it addresses the wrong layer. What the agent holds is not the problem. The problem is that the credential carries no connection to the purpose that justified issuing it. PAM and NHI controls govern the credential. Nothing governs the mission.
Neither model fits because agents combine properties that span both categories in ways neither was built to accommodate. They operate under delegated human authority, but the access layer records consent without the mission context that makes it meaningful. They discover resources dynamically, which makes pre-enumerated permissions either too restrictive to be useful or too broad to be safe. They chain across principals and trust domains, passing authority in ways the original grantor never explicitly authorized. And they must remain governable for the duration of a specific mission, not just at the moment a token was issued.
The question is not what kind of principal an agent is. It is what kind of execution model it requires.
## ๐ค Why Treating Agents as Humans Fails
The first instinct is to provision agents as human users. That pattern is widespread across enterprise deployments. [Cloud Security Alliance Non-Human Identity guidance](https://cloudsecurityalliance.org/research/working-groups/non-human-identity-iam) documents service accounts managed with human-oriented controls across enterprise environments. It is also structurally wrong, and the failure mode reveals something important.
The human was not merely the subject of identity controls. The human was the governance layer those controls never had to model. A person authenticates, acts within the scope of their current intent, and stops when the task is done. Sessions terminate because humans close their laptops. Behavioral baselines calibrate to human-paced access patterns. Access reviews reflect what a person needs right now. The controls held not because they were particularly sophisticated, but because the human brought presence, pacing, and natural scope-limiting that the controls never had to formally represent.
Agents provide none of that. They **do not stop when the mission ends**. There is no closing-the-laptop signal for a completed task. Behavioral baselines calibrated to human access volumes generate constant noise or, worse, learn to ignore agent activity entirely, removing the last behavioral fallback. When an agent spawns sub-agents, access passes implicitly and in full with no concept of attenuation. Human identity systems were never designed for principals that chain. Provisioning an agent as a human transfers the entitlements without the presence, pacing, or accountability those controls were built around.
Agents remove that dependency and make the gap visible. A better human control does not close it. An explicit mandate does.
## โ๏ธ Why Treating Agents as Workloads Fails
The workload model extends this instinct. Govern the agent's credential lifecycle through PAM and NHI tooling, assign it a service identity such as a [SPIFFE](https://spiffe.io/) SVID, and enumerate its permissions in advance. It trades one set of assumptions for a different set of failures. Workload identity systems assume you know every system the workload will call before it runs.
Agents operate in open worlds. Given a destination rather than a track, an agent discovers tools, APIs, and data points that were unknown when the mission started. That creates a paradox: restrict it to pre-mapped resources and it cannot do the job it was built for. Give it broad credentials to navigate freely and the exposure scales with every action it takes.
| | Workload (Train) | Agent (Taxi) |
|---|---|---|
| **Path** | Pre-defined tracks | Dynamic, real-time navigation |
| **Permissions** | Fully enumerated before departure | Just-in-time, context-dependent |
| **Identity** | What it is determines what it can do | Establishes the driver, not the trip |
| **Discovery** | Pre-enumerated at deployment | Continuous: the source of its value |
> You cannot rely on pre-authorization alone to solve an open-world problem.
And even when dynamic policy bridges the discovery gap by evaluating each newly discovered resource against context and purpose at request time, the agent still has no mission object. It knows what it is permitted to do. Nothing tells it when to stop. The discovery problem and the lifecycle problem are separate: solving the first does not solve the second.
Identity was designed to answer a different question. Authority requires a different primitive.
# ๐ Access Is Not Authority
Modern security stacks have deep access-layer coverage. Identity providers, authorization servers, and resource servers govern whether an actor may cross a boundary and whether a request is permitted at that point. What happens after falls outside their scope, including how long execution runs, under what conditions, and whether it should continue.
Three structural gaps explain why tightening these controls does not resolve the problem:
- **Access tokens capture access, not purpose.** They determine whether a request is accepted at a boundary. They do not carry why execution was initiated or when it should end.
- **Credential lifecycle is not mission lifecycle.** Credential rotation and token refresh reset the credential clock. The mission clock does not reset with it.
- **JIT governs credential windows, not mission state.** JIT credentials and JIT privileges reduce standing exposure. The time window estimates how long the mission will run. When the mission ends before the window closes, or runs longer than expected, the mismatch is invisible to the access layer.
Every control in these sections asks whether a specific action is permitted right now. The gap agents expose asks something different. Should this execution still be running at all?
An Execution Mandate is stateful in a different dimension. It persists mission context across requests, token refreshes, service hops, and delegation chains. No combination of access-layer controls can answer a question that requires mission state, not because the question is unanswerable, but because answering it requires a component that holds mission state as a first-class artifact. No standard access-layer control is that component.
## ๐ช Access Tokens Capture Access, Not Purpose
An OAuth access token represents an authorization grant. It encodes what the client is permitted to do, on whose behalf, and for how long. Resource servers evaluate those claims (scopes, audience, subject, expiry) to decide whether a specific request may proceed. What access tokens do not carry is mission context. They record nothing about why the authorization was granted, what conditions must remain true throughout execution, or when it should end.
OAuth Rich Authorization Requests (RAR, [RFC 9396](https://datatracker.ietf.org/doc/html/rfc9396)) allow clients to send structured authorization details at request time, specifying not just `scope=finance:read` but a specific purpose (`quarterly_financial_analysis`), resource (`finance/q3-2025`), and authorizing principal (`cfo@example.com`). The authorization server can issue a token scoped precisely to that task. That is the intent-aware authorization the industry has been building toward, and it is meaningful progress. But RAR's declaration is made once, at the moment of the request. It is not a living record of mission conditions across execution.
When a user authorizes an agent, OAuth records the grant: the agent's client identity, the user's identity, and the approved scopes. It captures that consent was given. It does not capture why.
Consider a code agent authorized to access a production database to investigate a reported incident. The grant records `db:read` on the engineer's behalf. The incident is resolved and the ticket is closed. The grant still shows approval. Nothing in the authorization server knows the ticket was closed. The agent's next request looks identical to its first: a valid client, an approved scope, a consenting user. The context that made the consent meaningful has no formal representation in the grant record.
Authorization servers can revoke tokens and invalidate grants, but revocation requires something to trigger it. A board meeting conclusion is not a system event. Short-lived credentials reduce reliance on explicit access revocation, and that is genuine value. Mission revocation is a different problem. It still requires an authoritative lifecycle owner that knows the mission ended and propagation semantics that reach every enforcement point. A shorter token window does not change what triggers it to close.
RAR, short-lived tokens, and CAEP can be composed to improve the picture, with intent declared at issuance, tokens expiring quickly, and risk signals triggering re-evaluation. That composition is often proposed as the solution. It is not. Composing components that each answer "is this request currently permitted?" does not produce a component that can answer "should this execution still be running?" RAR captures declared purpose at issuance but does not persist it. Short-lived tokens constrain the entry window but do not track why entry was granted. CAEP propagates signals when events fire but has no standard authoritative source of mission state to emit from.
**Tokens enable request-scoped access decisions. Authority governs whether mission-scoped execution should continue.**
## ๐๏ธ Credential Lifecycle Is Not Mission Lifecycle
PAM and NHI tooling is essential. It enforces Zero Standing Privileges, vaults secrets, governs non-human credential issuance, and rotates credentials on schedule. These controls manage the credential lifecycle. They govern when credentials exist, how long they last, and how they are refreshed.
But the credential lifecycle is not the mission lifecycle. A scheduled rotation fires at 3 AM. The agent now holds fresh credentials for a mission that was cancelled at 2 PM. The credential clock resets. The mission clock does not. PAM governs the credential precisely. Whether the purpose that justified provisioning it still holds is a different question entirely, and no part of the credential lifecycle owns it.
## โฑ๏ธ JIT and Approval Flows Govern Access, Not Mission State
When practitioners encounter the mandate continuity problem, the instinct is often to reach for Just-In-Time provisioning or Human-in-the-Loop approval flows. Both reduce credential exposure. Neither tracks whether the mission behind those credentials is still active.
Approval flows assume a human-paced world. Agents operate at machine speed. A single agent can invoke dozens of tools per minute, each potentially warranting review under a strict gate model. The volume creates fatigue: reviewers either rubber-stamp requests or respond by broadening grants and reducing gate frequency, which reintroduces the standing access problem JIT was meant to solve. Even when approval flows work as intended, they address individual actions, not mission lifecycle. A human authorizing each tool call is not the same as a human governing whether the mission behind those calls should still be running.
JIT credentials and JIT privileges are distinct mechanisms that address different risks. JIT credentials, the PAM and vault pattern, control when a token or secret is issued, ensuring no standing credential exists when the agent is not active. JIT privileges, access activation patterns like Microsoft Entra ID Privileged Identity Management, control which permissions are elevated and for how long, ensuring elevated access is not permanently assigned. Both reduce standing risk. Neither governs whether delegated mission authority should continue after the context that justified it has changed.
Both are bounded by a time window estimated to match mission duration. That estimate is a proxy, not a measure. When a mission ends early, the window stays open. When it runs long, credentials refresh without re-authorizing the mission.
> A time window is a proxy for mission duration, not a measure of it.
Behavior-bounded access models go further. Rather than a fixed window, trust becomes a consumable resource that depletes per action, with continuous intent scoring re-evaluating alignment before each request. That is a meaningful improvement over a timer. It is still not a mission clock. A trust budget depletes when actions are taken, not when the purpose that justified those actions has ended. A cancelled procurement program does not register as budget exhaustion. Behavioral monitoring can flag when an agent's actions diverge from its declared objective. It cannot flag when that objective is no longer valid.
# ๐ Delegation Is Not Authority
Getting identity right resolves who the agent is. Getting access right resolves what it may do at any given boundary. Neither addresses the handoff. The moment an agent delegates to another agent or spawns a sub-agent, the context established at the original grant does not travel with the authority. That loss of context at the handoff is the delegation problem.
The mandate addresses this failure across two distinct dimensions. Mission authority asks whether the purpose and conditions that justified the original grant still hold. Delegation authority asks whether the chain from the current agent to the originating human grant is intact and unattenuated. They fail independently. A structurally valid chain can carry a mission that has already lapsed, and a valid mission can be served by a chain whose originating principal has changed. The three gaps below are delegation authority failures.
Standard OAuth delegation defines the first-hop mechanism: a principal authorizes an agent, an authorization server records consent, and a scoped token governs what the agent may do on the principal's behalf. That establishes *what* is permitted, not *why* the grant was made or *when* authority should end.
The problem compounds the moment that agent delegates further. Orchestrators spawn sub-agents that invoke tools and call APIs, which delegate to other agents in turn. Authority flows through chains the original principal never explicitly contemplated. In any well-governed delegation model, sub-delegation requires explicit authorization in the original grant: a delegate cannot further delegate absent that, and a grant of authority does not automatically carry the right to extend it.
Three structural gaps explain why current systems cannot govern these chains:
- **Scope amplifies without attenuation.** Authority passes implicitly at each hop. Even where hierarchical token chains and depth limits constrain chain length, they govern privilege inheritance, not mission scope at each hop. A sub-agent may access more than the mission that justified the original grant.
- **Authority chains lack standardized traceability.** There is no standardized way to verify that authority in a chain traces back to an explicit human grant. If the trace breaks, no system can see it.
- **Context is not re-evaluated at each hop.** Each delegation point passes authority based on the conditions at the original grant, not current context. The purpose may no longer justify extending it.
## ๐ Scope Amplifies Without Attenuation
When a sub-agent is spawned without mandate context, it operates under its own service role and defaults. It has no knowledge of why it was invoked, what purpose the originating mandate was serving, or what conditions must remain true. It cannot enforce what it cannot see.
Hierarchical delegation-token chains with depth limits exist in some frameworks. Even with those controls, the model delegates privileges, not mission intent or execution legitimacy. Cross-system enforcement is difficult because attenuation semantics are not standardized across orchestrators, tools, and trust domains.
Token Exchange ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693)) is the closest existing mechanism for propagating delegated authority across service boundaries. A client presents a token to an authorization server and receives a new one, scoped for a downstream service, with an `act` claim recording who is acting on whose behalf. The `act` claim is an advisory record of the delegation chain, not an enforced constraint on what that chain may do or how long it may run. Each exchange is a bilateral transaction between one client and one authorization server. Token Exchange moves authority across a boundary. It does not govern whether that authority should still exist.
A sub-agent can only receive authority equal to or less than what the delegating agent holds. You cannot delegate what you do not have. With that constraint enforced, the sub-agent cannot reach resources outside the parent mandate's scope.
**Without enforced attenuation, every delegation hop is a potential amplification point.**
## ๐ Authority Chains Don't Trace Back
Without standardized traceability requirements, each additional hop adds a link that no audit log can reconstruct. From the final service's perspective, a request arrived from an authenticated agent with valid credentials. The delegation has no memory of where it started.
The problem has a direct analogy in software supply chain security. [SLSA (Supply Chain Levels for Software Artifacts)](https://slsa.dev/) defines provenance requirements so a build artifact can be traced back through its pipeline to a trusted source, with each step attesting to what it received and what it produced. An authority chain has exactly the same requirement. A mandate trace from the current agent to the originating human grant is the identity equivalent of SLSA provenance: every delegation hop attests to the authority it received and what it passed forward. Valid credentials tell you the current agent is authenticated. They do not tell you whether any human authorized the chain of execution that produced it.
Authority in any chain must trace back to an explicit human grant. If the trace breaks, the delegation is invalid. [South et al.](https://arxiv.org/abs/2501.09674) (2025) address this requirement directly, proposing cryptographic attestation at each delegation hop to make the chain from sub-agent to originating human grant verifiable without trusting intermediate links to have enforced it.
**A delegation chain with no traceable human grant is not governance. It is inherited trust.**
## ๐ Context Doesn't Travel With Authority
At every delegation point, authority passes based on the conditions that existed at the original grant. The question each hop should ask is not only *what authority do I hold?* It is *does the original purpose still justify extending this authority, to this actor, right now?* Short-lived credentials and scoped permissions do not encode any of this. Each delegation decision should reflect current conditions, not those that existed when the original mandate was issued.
Without context re-evaluation, revocation does not cascade. When Agent A's mandate expires or is revoked, Agent B's authority continues. The chain outlives its origin. With context re-evaluation enforced, Agent B's authority terminates when Agent A's mandate expires. Short-lived credentials limit how long a window stays open. Context re-evaluation ensures authority cannot outlive the purpose that justified it.
Consider the failure in full. A procurement officer delegates a vendor sourcing task to Agent A. Agent A calls a third-party vendor-analysis service, which delegates to Procurement Agent B to retrieve supplier risk scores. The CFO then puts the sourcing program on hold. Agent A's mandate should expire. Agent B has no visibility into Agent A's mandate or the condition that ended it. The vendor-analysis service has no knowledge the program was paused. Agent B continues pulling supplier risk scores, authorized at every access layer, acting on a mandate whose originating purpose no longer holds. Each hop amplified scope. Each hop lost a piece of the trace. Each hop passed authority based on conditions that no longer existed.
**Delegation without attenuation, traceability, and context re-evaluation is not governance. It is accidental trust propagation.**
---
The gap is not in your controls. The gap is in what your controls were built to govern. Identity, access, and delegation each answer their question correctly, and none of their questions is the one agents expose.
Closing it requires a control that holds four properties as a persistent, independently revocable artifact. The purpose for which authority was granted. The conditions under which it remains valid. The lifecycle events that end it. And the provenance tracing it back to a human decision. Identity establishes who, not why. Access governs the request, not the mission. Delegation carries authority forward but holds no record of the mission that justified it or the conditions under which it should terminate. None of the three owns all four properties. The missing layer must exist as a distinct first-class artifact, not as an extension of any existing one.
We have been building better passports. What agents need is Power of Attorney. That is the artifact the next post defines.
---
# From Passports to Power of Attorney
Canonical URL: https://notes.karlmcguinness.com/notes/from-passports-to-power-of-attorney/
Markdown URL: https://notes.karlmcguinness.com/notes/from-passports-to-power-of-attorney.md
Governing the stay requires an artifact that no current control provides. For delegated authority to remain trustworthy throughout execution, it must carry four things. The purpose for which it was granted. The conditions that must remain true while it runs. The lifecycle events that end it. And the provenance that traces it back to a human decision.
Identity controls establish who the agent is, not why. Tokens govern what is permitted, not the conditions under which permission holds or the business milestones that close it. Policy engines evaluate each request against current policy but in standard deployments maintain no record of the mission that originated the request, or whether that mission remains valid. Together they answer every question except the one that matters. Should this execution still be running?
No widely adopted primitive holds purpose, conditions, lifecycle, and provenance as a first-class artifact. That is what the Execution Mandate is.
# โ ๏ธ The Enforcement Gap
What is missing from the current stack is not an enforcement mechanism. Policy engines can evaluate mandate conditions once the state is present. What is missing is a state owner. No widely adopted control-plane component holds mission authority as a first-class, independently revocable artifact, and without that, every enforcement point in the stack is answering the wrong question.
In enterprise operations, a badge proves you can pass checkpoints. A work order defines what job is authorized, under what constraints, and when that job is complete.
**[Zero Trust](https://csrc.nist.gov/publications/detail/sp/800/207/final) is the badge. Power of Attorney is the work order.**
The badge can stay valid after the work order is closed. Zero Trust requires that every request be re-evaluated against current identity and policy. That verification is request-scoped. It checks the request, not the mission. The failure takes four forms:
| Failure Mode | Category |
|---|---|
| Mission ends but access persists | Mission authority failure |
| A conflict emerges that did not exist at issuance | Mission authority failure |
| Authority transfers to a principal who no longer holds it | Delegation authority failure |
| Delegation amplifies scope beyond the original grant | Delegation authority failure |
The two cases below are one of each kind, and the subtlest, because in each the mission itself is still running.
- **The "Conflicted Reviewer" (Mission Authority Failure):** An expense audit agent is authorized by an analyst to review the team's travel submissions. During execution, the analyst is added as a required approver for one of the expense reports in scope, creating a separation-of-duty conflict that did not exist at issuance. The agent is now auditing a record its principal must approve. The mission is still active. A reviewer assignment is not an IAM event. Nothing in the stack can see it.
- **The "Delegated Approver" (Delegation Authority Failure):** A legal due diligence agent is authorized by General Counsel to review acquisition target contracts. Midway through the review, GC recuses herself due to a personal conflict of interest and formally delegates her authority to outside counsel. The agent's mandate traces back to a principal who no longer holds authority over that decision. The mission is still active. The token is still valid. A recusal is not an IAM event. Nothing in the stack can see it.
Authorization is a decision mechanism. Whether role-based, attribute-based, policy-based, or relationship-based, access control models evaluate whether a specific request is permitted under current policy. They can encode context and purpose attributes, but each decision is local to a single request. The question they cannot ask is **"Should this actor still be acting at all?"**
Consider a CFO who authorizes a research agent to access pre-IPO financial data to analyze board exposure and prepare a presentation. The board approves the draft at 2:00 PM. The mission is complete. Check every control five minutes later:
- Access Token: valid โ
- Fine-grained authorization (FGA): agent has financial read access โ
- Just-In-Time (JIT): credential was legitimately provisioned โ
- [Shared Signals Framework (SSF)](https://openid.net/specs/openid-sharedsignals-framework-1_0.html)/[Continuous Access Evaluation Protocol (CAEP)](https://openid.net/specs/openid-caep-specification-1_0.html): no identity risk event to propagate โ
- Security Information and Event Management (SIEM): no anomalous behavior โ
- **Mandate: expired at 2:01 PM โ.** Nothing in the stack can see it.
No alert fires. No log entry looks wrong. The breach is structurally invisible.
The response to this scenario is often to point to delegation-token chains with depth limits and reduced scope. If the chain is shallow and the token is narrow, surely the blast radius is contained. Even with those controls, the problem persists. A depth limit governs how far authority propagates. A reduced scope governs which resources the agent may access. Neither provides a canonical place to represent that this mission ended at 2:00 PM and to force every enforcement point to converge on that fact. The chain has no concept of the purpose that justified it.
The more sophisticated objection is that existing primitives, composed correctly, could approximate the same result. [Rich Authorization Requests (RFC 9396)](https://datatracker.ietf.org/doc/html/rfc9396) carry structured purpose declarations at token issuance. Short-lived tokens limit credential exposure. CAEP propagates revocation signals. A fine-grained PDP evaluates contextual conditions per request. Each addresses a real problem. The composition still fails on the same point.
RAR declares purpose at issuance but has no revocation semantics tied to whether that purpose remains valid. Short-lived tokens refresh independently of mission lifecycle. A 15-minute token does not terminate when the mission does. It gets refreshed. CAEP can propagate a mandate-expiry signal, but without a standardized source of mission state there is nothing to propagate. A PDP evaluates whether each request is permitted, not whether the mission that originated the request is still valid.
Composed correctly, these controls close access gaps and reduce blast radius. None of them creates a state owner for mission authority. That is the role that has no current occupant.
The breach also leaves a clean audit trail. Every request was authorized. Every control showed green. Post-incident, the access logs contain no evidence of what the agent's mission was, when it was supposed to end, or which actions exceeded it. The failure is invisible not just at the moment it occurs but in the reconstruction.
A mandate record changes this. Every action executed under a mandate is attributed to a specific delegated purpose, the grant that authorized it, and the agent instance that executed it. Mandate expiry is a first-class event in the audit log. An investigator can ask whether this action was taken while the mandate was valid or after it expired. That question has no answer in today's access logs. It is the difference between a record of what happened and a record of what was authorized to happen. Those two things diverge the moment a mission ends and the agent keeps running.
That distinction matters not just in incident response but in routine governance. A mandate record gives reviewers and auditors what they currently lack. A structured attestation linking every action to a named human grant, a declared purpose, and a defined lifecycle. Compliance reviews can validate not just that access was assigned but that it was used within its authorized scope and terminated when that scope ended.
The gap manifests across every dimension of agentic execution:
| Failure Mode | Current Stack | With a Mandate Layer |
|---|---|---|
| Mission Completion | Agent continues acting while token and policy checks pass | Authority expires when the objective is met, independently of token validity |
| Tool Discovery | Either block the agent or grant broad credentials | Scoped, one-time authority derived from the active mission |
| Sub-Delegation | Agent passes full credentials to sub-agents | Agent passes an attenuated mandate, never more than it holds |
| Compromise | Attacker gains the full scope of the agent's credentials | Attacker is constrained to the active mission's scope |
# ๐ Authority Is Power of Attorney
We have been issuing passports. The right instrument has always been Power of Attorney.
A passport establishes who you are and authorizes you to cross a boundary. A visa goes further, declaring a purpose and duration, but it still governs only the entry. It does not evaluate whether the purpose that justified the entry remains valid. It was the right instrument for a world where the entity crossing the boundary would stop when the task was done.
It is the wrong instrument for an agent that will keep running until something tells it to stop.
The industry has responded to agent security by building better passports and better visas: tighter scopes, shorter lifetimes, faster rotation, more precise intent declarations at issuance. These are improvements. They are improvements to the wrong instrument.
When an agent acts on your behalf, you are not giving it your identity or your credentials. You are granting it **Power of Attorney**.
A Power of Attorney (PoA) is a delegated mandate to act on behalf of another. It is bounded by **purpose, scope, time, and conditions**. It can be limited, revoked, and evaluated independently of identity. You might grant medical Power of Attorney, but not financial. You might grant limited financial authority, but not real-estate authority. The authority is scoped to *why* someone may act, not merely *which actions* are technically possible. The Execution Mandate is the artifact that carries and enforces it as a signed, independently revocable record.
The same dimensions apply directly to agents. An agent authorized to source a backup logistics vendor holds authority for a specific purpose (vendor sourcing under pre-approved terms), a specific scope (that vendor market and contract templates), a defined time window (the active contingency program), and defined conditions (the program remains active). When any dimension changes, the authority terminates, regardless of whether the agent's token remains valid.
One dimension the PoA analogy surfaces that the access model often obscures is that you can only grant authority you actually hold. In enterprise practice, this breaks down more often than the access layer reflects. A manager who authorizes an agent to process HR records may have read access to those records without owning them. The data governance function that does own them has independent policy interests the delegating principal's grant does not represent. Resource owner consent and delegating principal intent are distinct inputs to authorization. Treating them as one is a meaningful gap in the consent model, and for sensitive or regulated data the mandate should reflect both.
Mandates originate in human decisions. In the simplest case, the consent moment looks similar to an OAuth authorization flow: the principal reviews the agent's declared purpose, scope, and conditions before approving. The difference is what gets recorded. An OAuth grant records that consent was given. A mandate grant records the purpose, the conditions under which it holds, and the events that will end it. The richer record is what makes the mandate evaluable throughout execution rather than only at issuance.
This distinction has roots in capability-based security. Object-capability systems addressed what Norm Hardy described as the [confused deputy problem](https://dl.acm.org/doi/10.1145/54289.871709) in 1988. A confused deputy misuses authority granted for one purpose to serve another. The agent mandate problem is the temporal extension of that failure. The mandate layer closes the temporal dimension. It restricts not just what authority may be exercised at any moment but how long that authority runs and under what conditions it remains valid.
The theoretical grounding for continuous authority evaluation comes from Park and Sandhu's [Usage Control (UCON) model](https://dl.acm.org/doi/10.1145/984334.984339) (2004), which distinguished between conditions evaluated at the moment of access and conditions that must remain true *throughout* an ongoing action (what they called continuity obligations). The Execution Mandate operationalizes that distinction. Conditions are not checked once at grant time. They are evaluated per action throughout the mission lifecycle. What UCON established as a theoretical property, agentic systems make a deployment requirement.
The theory was right in 2004. The urgency is new.
# ๐ป Case Study 1: Code Agent and Ghost Execution
**The Scenario:** A team grants a code agent access to GitHub and Slack to monitor PR status and notify when a merge completes.
**The Access Layer Failure:** The PR is merged at 2:00 PM. The mission is over, but the token remains valid until 2:00 AM. The agent continues polling the private repository for 12 hours, still authorized at the access layer but no longer authorized by its mandate.
**The PoA Solution:** The agent holds an Execution Mandate with a condition bound to PR state: *"Authority valid only while PR #123 is pending merge."* When status changes to merged, the mandate is invalidated. Even if the token remains valid, subsequent requests are denied. The mission is complete, so execution stops.
# ๐ Case Study 2: The Taxi and Unchecked Discovery
**The Mission:** "Source and onboard a backup logistics vendor under pre-approved terms, then prepare the purchase order package for review."
**The Workload Failure:** If the agent discovers a supplier portal or risk-screening API that was not in its original pre-approved list, the request is blocked. The agent fails because it cannot leave the tracks.
**The Access Layer Failure:** To prevent this type of failure, teams grant agents broad procurement roles or admin credentials. If the agent touches a malicious integration or compromised endpoint, it has the tokens and permissions to take actions far beyond the original sourcing task.
**The PoA Solution:** The agent is governed by its mandate. When it discovers a new supplier portal or sanctions-screening API, the authority layer asks: *"Does calling this further the `backup_vendor_sourcing` mission and stay within approved terms?"* If yes, a one-time attenuated Execution Mandate is issued specifically for that endpoint, scoped to one resource, one action, for the duration of that call. The sub-agent cannot further delegate, because its mandate is derived from the original mission scope and cannot exceed it. If the procurement program is cancelled, the parent mandate is revoked and the sub-agent's authority terminates with it, regardless of whether any individual credential remains valid.
The two failure modes above are not edge cases. They are the only options the pre-authorization model offers. Restrict the agent to pre-enumerated resources and it cannot execute in an open world. Grant broad credentials and every discovery is an expansion of blast radius. The mandate breaks that binary. Discovery is evaluated against mission scope, not against a pre-approved list. Execution governance is not a more sophisticated form of pre-authorization. It is a different control operating at a different layer.
# ๐ The Execution Mandate in Practice
Those two scenarios converge on a common set of requirements. For mandate governance to hold, delegated authority must be:
- explicitly represented
- bounded by purpose, scope, time, and conditions
- inspectable as execution unfolds
- governable independently of credentials
- terminable when the mandate expires, even if access would still succeed
The concrete form of that authority is an **Execution Mandate**: a signed, inspectable record that runtime systems can evaluate and revoke independently of whether the underlying token remains valid.
At minimum it should carry:
- **principal**: who granted it
- **agent**: who holds it
- **purpose**: why it exists
- **scope**: what it covers
- **conditions**: what must remain true
- **lifecycle**: when it ends
- **delegation**: how authority passes to sub-agents
- **delegation chain**: full provenance from the originating human grant to this agent
The mandate can be structured in several formats. [Macaroons (NDSS 2014)](https://research.google/pubs/macaroons-cookies-with-contextual-caveats-for-decentralized-authorization-in-the-cloud/) enforce attenuation structurally through HMAC-chained caveats. [Biscuit](https://www.biscuitsec.org/) uses Datalog-based logic to express richer conditions directly in the token. Both are promising primitives for mandate attenuation but have limited enterprise adoption and production track record today. A signed [JSON Web Token (JWT, RFC 7519)](https://datatracker.ietf.org/doc/html/rfc7519) has the broadest enterprise tooling support and deployment experience, making it the practical starting point for a minimum viable mandate implementation, with online/offline attenuation tradeoffs handled at the control plane layer.
For the mandate to be enforceable rather than advisory, it must be cryptographically bound to the token it governs, either by embedding a mandate reference in a token claim or by including a token hash in the mandate, so that presenting a valid mandate alongside an unrelated token is detectable at any enforcement point.
This authority must remain anchored to the initial grant. An agent operating in an open world will inevitably discover new tools and data, but it must never be allowed to self-authorize into new permissions. The mandate can be amended only by the granting principal or explicitly delegated administrators. The agent executing under it has no amendment rights.
Authority over the mandate's lifecycle is held by the granting principal. The mandate continues until a defined terminal event fires, the granting principal revokes it, or its lifecycle expires. The agent governs none of these.
> An agent that can rewrite its own mandate has no mandate at all.
## ๐ซ What the Mandate Carries That a Token Cannot
For the procurement agent in Case Study 2, a mandate structured as a signed JWT would look like this:
```json
{
"jti": "mnd-logistics-sourcing-7f2a",
"iss": "https://mandate.example.com",
"iat": "2025-10-01T08:00:00Z",
"exp": "2025-10-15T17:00:00Z",
"sub": "urn:agent:procurement/vendor-sourcing@3.2",
"aud": ["https://enforcement.example.com", "https://vendor-portal.example.com"],
"principal": {
"sub": "vp-supply-chain@example.com",
"iss": "https://idp.example.com",
"grant_ref": "gnt-oauth-c9d3e1"
},
"agent": {
"instance": "inst-7f2a-b8c4"
},
"purpose": {
"objective": "Source and onboard a backup logistics vendor under pre-approved terms",
"business_context": "Contingency supplier program, primary vendor SLA breach Q4-2025"
},
"authorization_details": [
{
"type": "vendor_portal",
"locations": ["https://vendor-portal.example.com"],
"resource": { "category": "logistics", "region": "north-america" },
"actions": ["search", "view_profile", "initiate_onboarding"],
"constraints": {
"contract_template": "standard-logistics-v3",
"spend_tier": "pre-approved"
}
}
],
"conditions": [
{
"id": "program-active",
"expression": "procurement.contingency_program.status == 'active'",
"evaluation": "per_action",
"on_failure": "suspend_and_notify"
},
{
"id": "sod-clear",
"expression": "principal.conflicted_vendors.contains(request.vendor_id) == false",
"evaluation": "per_action",
"on_failure": "halt_and_escalate"
}
],
"lifecycle": {
"hard_expiry": "2025-10-15T17:00:00Z",
"terminal_events": ["vendor_onboarding.completed", "contingency_program.cancelled"],
"on_expiry": "revoke_and_cascade"
},
"delegation": {
"permitted": true,
"max_additional_hops": 1,
"attenuation": "strict_subset_of_parent",
"cascade_revocation": "immediate"
},
"delegation_chain": []
}
```
The specific field names are illustrative. The structural requirements are what matter:
| Field | What it carries that an access token cannot |
|---|---|
| `principal` | The human identity that authorized this execution: traceable, auditable, independent of the agent's own identity |
| `agent` | The specific agent instance holding this mandate: a distinct principal in audit logs, not the user it acts for |
| `purpose` | Evidence traceable to a human grant: objective and business context, not a scope string |
| `authorization_details` | Typed resource objects with per-resource actions and constraints, not scope strings |
| `conditions` | Checks evaluated per action throughout execution; if the contingency program is cancelled, execution suspends before the next call |
| `lifecycle.terminal_events` | Business milestones that end the mission, not only a timer |
| `delegation` | Attenuation rules every child mandate must satisfy: strictly less scope, strictly sooner expiry, no further delegation |
| `delegation_chain` | The full chain of mandate grants from the original human authorization to this agent, inspectable at any point during execution |
The payload serves both dimensions. The purpose, conditions, and lifecycle fields govern mission authority. They ask whether the mandate's conditions and purpose remain valid throughout execution. The principal, delegation, and delegation_chain fields govern delegation authority. They ask whether the chain from this agent to the originating human grant is intact and unattenuated. A mandate can fail on either dimension independently of the other.
A mandate record also transforms the audit trail and the governance record. Every action is attributed to a specific delegated purpose, the expiry of that purpose is a first-class event, and post-incident review can distinguish authorized access from authorized access *after mandate expiry*, a distinction invisible in today's access logs. The same record gives access certifications, compliance audits, and regulatory reviews what they currently lack. A structured attestation linking agent activity to a human decision, a declared scope, and a defined end.
---
The Execution Mandate is the artifact the existing stack does not yet hold. It is what makes delegated execution governable rather than merely authorized. Building the layer that enforces it is where the next post goes.
---
# Governing the Stay, Not Just the Entry
Canonical URL: https://notes.karlmcguinness.com/notes/governing-the-stay-not-just-the-entry/
Markdown URL: https://notes.karlmcguinness.com/notes/governing-the-stay-not-just-the-entry.md
The previous post defined the Execution Mandate. This post builds the layer that makes it operational. A Power of Attorney document sitting in a filing cabinet grants nothing. The document establishes the authority. The system that issues, enforces, propagates, and terminates it is what makes that authority real.
The Execution Mandate is the document. The system that makes it real has four working parts.
1. **Mandate service**: owns mission state as an authoritative, independently revocable artifact evaluable throughout the execution lifecycle
2. **Continuous authority evaluation**: a policy layer that asks whether execution should continue, not just whether a specific request is permitted
3. **Cross-domain mandate propagation**: portable mandate authority that crosses organizational and trust boundaries with verifiable chain of custody
4. **Orchestration-layer enforcement**: authority governance embedded in how workflows manage execution lifecycle, not only at access gates
They are not independent improvements. The mandate service creates the state owner. Continuous evaluation consumes it. Cross-domain propagation extends it. Orchestration enforcement lands it.
Each primitive addresses a different failure mode. The first two enforce mission authority. They ask whether the mandate's purpose, conditions, and lifecycle remain valid. The third enforces delegation authority across organizational boundaries. The fourth lands both at the execution layer. Together they make it possible to delegate authority to autonomous systems with the same confidence you delegate it to humans, with defined scope, revocation rights, and a clear end.
---
# ๐ Mandate Service
The mandate service is what turns the Execution Mandate from a document into an enforced constraint. What follows covers its architecture and integration, lifecycle and revocation, authority and delegation, and hardening as critical infrastructure.
## ๐๏ธ Architecture and Integration
The mandate service is an architectural pattern that can be deployed as a standalone component, a capability added to an existing authorization server, or a module embedded in the orchestration layer. What matters is the function. Authoritative mission state ownership with clear enforcement integration points.
The mandate service does not replace existing Identity and Access Management (IAM) infrastructure. It sits above it. The Identity Provider (IdP) establishes identity. The authorization server governs token issuance. Both feed into the mission record the mandate service holds, which can terminate execution independently of whether the token remains valid. Enforcement can sit at the orchestration layer, an authorization sidecar, or a gateway. The integration point is not where authentication happens but where execution continues.
Clarity about which component owns which question matters. The IdP answers questions of identity. The authorization server answers whether a token grant is valid. The policy engine answers whether a specific request is permitted under current policy. The orchestrator answers what an agent is doing at any given moment. The mandate service answers whether the mandate behind a request is still valid and whether that execution should continue. Each is authoritative in its domain. None substitutes for the others.
Mandate governance is a distinct concern from the two systems it sits between. Workflow state tracks what is happening in execution right now. Policy engines evaluate whether a specific request is permitted under current policy. Mandate governance asks whether the mission behind those requests should continue at all. That question cannot be derived from workflow state or from policy decisions. It requires a separate artifact that holds mission authority as a first-class, independently revocable record.
The trust directions within that architecture matter as much as the boundaries. The policy engine reads from the mandate service as a context source. The mandate service does not defer to policy decisions or to the orchestrator's local execution state. Business systems emit terminal events and the mandate service owns the revocation decision. Writes to the mandate service are restricted to the granting principal and explicitly authorized policy administrators. Mission state authority is held by the mandate service alone. The others enforce, inform, or execute. None governs.
For teams with existing policy infrastructure, the mandate service does not need to replace the Policy Decision Point (PDP). It can function as a context source, with the Policy Enforcement Point (PEP) querying the mandate service as part of its decision inputs and the PDP evaluating mandate state alongside identity claims and resource policy. This allows mandate governance to integrate with [Open Policy Agent (OPA)](https://www.openpolicyagent.org/), [Cedar](https://docs.cedarpolicy.com/), or [OpenID AuthZen](https://openid.net/wg/authzen/) deployments as a context source rather than requiring a separate enforcement path. The enforcement timing is fixed regardless of architecture. Mandate checks must happen before tool invocation and before any irreversible action. A post-action check is not enforcement.
## ๐ Lifecycle and Revocation
The mandate service is the authoritative system of record for mission state. It owns whether a mandate was issued, under what terms, and whether it remains valid. Business systems are the sources of terminal events. A procurement platform signals a program was cancelled, a CRM records a renewal conversation closed, a ticketing system marks a task complete. Those signals flow to the mandate service, which evaluates them against lifecycle conditions and triggers revocation when they match. Every revocation is a first-class security event, recording who revoked, when, under which terminal condition, which child mandates cascaded, and what in-flight actions were halted. That record is reviewable independently of the underlying access logs and survives the token rotation cycles that would otherwise erase execution history.
Three authority questions govern the mandate lifecycle. The mandate is issued by the granting principal through the consent flow. The mandate service owns its ongoing state. Revocation rights belong to the granting principal, delegated administrators, and the terminal events defined in the mandate's lifecycle conditions. Business systems report what happened. The mandate service decides whether it constitutes a terminal condition.
## ๐ค Authority and Delegation
Policy authority over mandates is not held by the agent. It belongs to the granting principal, the human who authorized the delegation. Organizations may extend this authority to additional parties, including security teams, compliance officers, and designated administrators, each with defined scope over issuance, amendment, and revocation. When mandates conflict, the more restrictive constraint governs. Conflicts that cannot be resolved by scope intersection require human escalation before execution continues.
When an agent needs to delegate to a sub-agent, the mechanics of attenuation are worth making explicit. The delegating agent cannot self-issue a child mandate. It submits a delegation request to the mandate service, declaring the sub-agent's identity, the scope it requires, and the conditions that should apply. The mandate service validates that the requested scope is a strict subset of the parent's scope, that the requested expiry falls within the parent's lifecycle, and that the parent mandate explicitly permits further delegation. The mandate service records the parent-child relationship, which is what makes cascade revocation possible when the parent mandate terminates.
This is where the tradeoff between online and offline attenuation surfaces. Offline attenuation (the [Biscuit](https://www.biscuitsec.org/) and [Macaroons](https://research.google/pubs/macaroons-cookies-with-contextual-caveats-for-decentralized-authorization-in-the-cloud/) pattern, where the agent adds caveats directly to its own token without a round-trip) enforces strict-subset semantics structurally and is useful for latency-sensitive cross-domain calls. The cost is auditability. The mandate service has no record of what was delegated, to whom, and under what conditions, which makes cascade revocation across unknown chains unreliable. For deployments where auditability and cascade termination are first-class requirements, online attenuation through the mandate service is the right model. For teams starting with [JWT](https://datatracker.ietf.org/doc/html/rfc7519)-based mandates, conditions live outside the token and attenuation requires a service round-trip rather than in-token caveats. Those constraints are real and worth understanding before choosing a format.
Mandate issuance is the point at which Separation of Duties (SoD), dual control, and break-glass requirements must be explicitly modeled. A mandate cannot confer authority the granting principal is themselves prohibited from exercising under SoD policy, and SoD conflicts that emerge during execution should be expressible as conditions evaluated continuously, not only at issuance. High-value mandates may require co-authorization at the consent flow. Break-glass access should be modeled as a separate, time-bounded, heavily audited emergency mandate rather than a silent widening of an existing one, keeping the governance record intact and the deviation visible.
## ๐ก๏ธ Critical Security Infrastructure
Like any authoritative control-plane component, the mandate service must be treated as a high-value target. Compromise of the mandate service is compromise of execution governance across every agent it controls. Hardening requirements mirror those of authorization servers. Artifacts must be signed so enforcement points can validate mandates without a live callback. Fail-closed behavior is required so that an unavailable mandate service halts execution rather than defaulting to open. Operational policy must define explicit cache staleness bounds, local verification of signed artifacts to reduce live-callback dependency, and revocation lag tolerance calibrated to the risk profile of the actions governed. Every mandate issuance, amendment, and revocation is a first-class security event with a full audit record.
# ๐ Continuous Authority Evaluation
Getting issuance right is necessary. It is not sufficient. The authority layer must ask not *is this request permitted?* but *should this execution still be running?*, evaluated against current context at runtime, not only at the moment a token was issued or a gate was crossed.
> A mandate that is checked only at issuance is not a mandate. It is a declaration.
Park and Sandhu's [UCON model](https://dl.acm.org/doi/10.1145/984334.984339) made this precise in 2004. Continuity obligations must hold throughout an ongoing action, not only at access time. The insight did not translate to deployment. Request-scoped controls were sufficient when humans were managing execution and closing the continuity gap themselves. Agents remove that assumption. CAEP and Zero Trust provide continuous access evaluation, re-evaluating whether a specific credential or request is still valid as conditions change. Behavior-bounded trust models add a third layer, tracking whether each action aligns with the stated objective and consuming a finite trust budget rather than relying on a static grant. The mandate layer provides continuous mission evaluation. It asks whether the execution behind those requests should still be running at all.
Continuous evaluation does not mean a synchronous round-trip to the mandate service on every API call. That model does not scale at production volumes and creates a single point of failure. The operational approach is hybrid. High-value or irreversible actions trigger a synchronous mandate check before execution. High-volume low-risk calls assume the mandate valid until a revocation signal arrives via [Shared Signals Framework (SSF)](https://openid.net/specs/openid-sharedsignals-framework-1_0.html)/[Continuous Access Evaluation Protocol (CAEP)](https://openid.net/specs/openid-caep-specification-1_0.html). The mandate service pushes revocation when a terminal event fires, and ghost execution stops at enforcement points without gating every API call. Which action type applies should be policy-defined by risk and reversibility, not left to ad hoc runtime behavior.
The semantic substrate for continuous evaluation is an open problem. The mandate service needs to know what "the contingency program is active" means at runtime, but business vocabulary does not map cleanly to discrete IAM events. Rather than waiting for system events that may never arrive, one promising approach is to maintain a structured, queryable artifact that captures the ongoing state of the world the mandate was issued into (negotiations active, renewal open, procurement program running). The mandate service evaluates conditions against that artifact rather than raw system signals. [Context graphs](https://foundationcapital.com/context-graphs-ais-trillion-dollar-opportunity/) formalize this pattern, making the graph the semantic bridge between business vocabulary and policy-evaluable state.
For conditions that cannot be instrumented against machine-observable signals, the mandate shifts from condition-based to event-based expiry. A mandate can terminate when the CFO approves the disbursement, when the renewal conversation closes, or when the partner organization's data-processing agreement lapses. The agent stops precisely when the human task completes or the governing relationship changes, without requiring manual security reviews during execution. The re-authorization trigger is the business event, not the clock.
When conditions cannot be instrumented against machine-observable signals, the mandate falls back to hard expiry. That might seem to reduce it to Just-In-Time (JIT) with extra steps. It does not. JIT issues a credential window. A mandate issues a governed mission. Even in the hard-expiry fallback, the mandate carries what JIT cannot. It holds an authoritative mission record the rest of the security stack can query, a cascade revocation chain that fires when the mandate terminates, attenuation semantics that govern every delegation hop, and an audit trail that attributes execution to a specific delegated purpose rather than a credential issuance. As instrumentation improves and event surfaces expand, the condition model grows with it without requiring architectural changes.
The hybrid model surfaces tradeoffs that have no universal answer. Revocation signals via SSF/CAEP are not guaranteed delivery, and stricter fail-closed behavior reduces exposure but creates availability risk. The right balance depends on the risk profile of the actions involved, the reversibility of side effects, and the availability cost the organization will accept when the mandate service is unavailable. What the model requires is that these tradeoffs be made explicitly and policy-defined, not left to ad hoc runtime behavior. A mandate layer that does not specify its failure semantics is not a governance layer.
# ๐ Cross-Domain Mandate Propagation
The two primitives above describe what governance looks like within a single deployment. Agent chains do not stay within one. [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers are third-party. [Agent-to-Agent (A2A)](https://google.github.io/A2A/) protocols cross organizations. Tool ecosystems span multiple vendors and trust domains.
The mandate design has cross-domain portability built in. A mandate structured as a signed [JSON Web Token (JWT, RFC 7519)](https://datatracker.ietf.org/doc/html/rfc7519) or [Biscuit](https://www.biscuitsec.org/) token can be verified at any enforcement point without a callback to the issuing authority, and attenuation semantics ensure each delegation hop can only narrow scope, never amplify it. A receiving system can validate an incoming mandate at its boundary before accepting delegated authority, without requiring shared infrastructure with the sender.
Mandate payloads may carry sensitive business context. In cross-domain scenarios, downstream enforcement points do not always need the full payload to enforce it. A mandate can carry a reference handle in place of the full purpose description, with the authoritative record held by the issuing domain's mandate service. Sensitive fields can be audience-restricted so only designated enforcement points can inspect them.
Full cross-domain governance requires infrastructure that current standards do not yet provide. Federated trust hierarchies, distributed revocation via SSF/CAEP across organizational boundaries, and interoperable condition schemas remain open problems. A single organization can issue, enforce, and revoke mandates within its own deployment with no cross-domain infrastructure at all. At the boundary, the portable mandate design described above provides the necessary tools. Signed artifacts enable validation without shared infrastructure. Attenuation semantics prevent scope amplification at each hop. Reference handles keep sensitive context on the correct side of the boundary. [OpenID Federation](https://openid.net/specs/openid-federation-1_0.html) and SSF/CAEP provide the trust and revocation infrastructure the cross-domain case will eventually require. Most deployments can achieve substantial mandate governance before that infrastructure arrives.
# โ๏ธ Orchestration-Layer Enforcement
At the orchestration layer, the three primitives above become operational. When a mandate expires, orchestrators must be able to pause, unwind, or terminate execution, not merely block at the next gate. The [saga pattern](https://dl.acm.org/doi/10.1145/38713.38742), which coordinates distributed transactions through compensating actions that undo partial work if a step fails, is a useful analogy here, not a direct implementation model.
What the analogy captures is the key operational requirement: not every in-flight action can be undone, and the orchestrator must know the difference before execution begins. If the vendor sourcing mandate expires while the agent is retrieving a supplier profile, halting the retrieval is straightforward. If it expires after an onboarding workflow has already been initiated, unwinding is not possible without human review. The mandate layer makes this distinction explicit at authorization time, defining which actions within the mission scope are reversible. That is what transforms mandate termination from an access control event into a governed unwinding of execution state.
Reversibility is declared at mandate issuance, not inferred by the orchestrator at runtime. The mandate defines the unwinding contract before execution begins.
In practice this requires explicit handling for revocation lag, in-flight requests, and compensating transactions. The hybrid evaluation model described above (synchronous checks for high-value actions, signal-based revocation for high-volume calls) maps directly onto orchestration-layer enforcement. The orchestrator knows which actions require a mandate check before proceeding and which can proceed under the assumption of a valid mandate until a revocation signal arrives.
For agent teams, the operational payoff is concrete. Instead of debating how long a Just-In-Time (JIT) window should be, teams define the terminal events for a mission at authorization time. The security team can audit those definitions. Compliance can validate them against policy. The agent executes until a defined business milestone, not until a timer fires.
[Faramesh](https://arxiv.org/abs/2601.17744) (Fatmi, 2026) proposes a complementary research architecture for this enforcement layer. Authority Attestation Bundles bind signed mission authority to each execution step, Continuous Authority Revocation propagates termination signals across execution boundaries, and Decision Artifacts provide the structured record linking each action to the authority that governed it. The primitives align closely with the mandate service components described in this post and represent the closest published academic framework for execution-layer mission governance in AI agent systems.
# ๐ฎ The Next Control Plane
The gap was invisible because humans were closing it. Presence, pacing, and natural scope-limiting were doing governance work that no formal layer had to model. Agents remove all of that. When we hand execution to agents, we inherit the obligation to replace what humans provided implicitly. A governed mandate with a defined purpose, bounded scope, and a clear end.
Agents are not the first systems to create this mismatch. Long-running workflows and autonomous integrations face the same dynamic. Agents are where it becomes impossible to ignore. The shift is structural. Request-scoped controls answer each boundary correctly and leave the mission ungoverned. The current stack has not needed a mission-scoped layer until now.
This does not require discarding what works. [OAuth](https://oauth.net/2/), [OpenID Connect (OIDC)](https://openid.net/connect/), [Zero Trust](https://csrc.nist.gov/publications/detail/sp/800/207/final), and fine-grained authorization all remain foundational. What is missing is the layer above them that holds mission authority as a first-class, independently revocable artifact and governs whether execution should continue, independent of token validity.
The four primitives described above are the components of that layer, each composing with what already exists. The mandate service sits above the authorization server, evaluation feeds existing PDPs, propagation runs over SSF/CAEP rails, and enforcement embeds in existing agent frameworks.
When this layer exists, agents can be deployed at scale without choosing between control and utility. Authority can be delegated explicitly, constrained to purpose, and terminated cleanly when purpose expires. The blast radius of a compromised or runaway agent shrinks to the scope of its active mission rather than the full reach of its identity.
> We spent the last decade building better ways to verify who is at the door. The next decade is about governing what they are authorized to do once they are inside.
The security of the agentic enterprise depends on governing the stay, not just the entry. Identity controls establish who the agent is. Access controls govern what it may do at each boundary. The mandate layer governs whether the agent should still be acting at all. That is the missing primitive. These are its four components. And that is the next control plane.
---
# About
Canonical URL: https://notes.karlmcguinness.com/about/
Markdown URL: https://notes.karlmcguinness.com/about.md
{{< profile-card src="/images/me.jpg" >}}
I'm [Karl McGuinness](https://karlmcguinness.com), a product and technology leader with 25+ years of experience building mission-critical, internet-scale identity and infrastructure platforms. At Okta, I spent over a decade helping modern enterprises and the broader industry treat identity as foundational infrastructure.
I specialize in product architecture, the intersection of product strategy and system design. I translate ambiguous requirements into durable product structures: domain boundaries, APIs, platform extensibility, and investment sequencing that keep teams fast today and options open later.
This site, **Control Plane**, is where I work through the harder questions: not just how identity works today, but what it needs to become as autonomous agents, delegated authority, and machine-speed decisions replace the human-centric models we built everything on.
## Focus Areas
- **Agentic identity**: how authentication and authorization must evolve as agents act on behalf of principals and operate without human supervision
- **Governance and authority**: delegation chains and accountability structures that make autonomous systems trustworthy
- **Trust and Identity infrastructure**: protocols, assertions, and verification mechanisms that let systems make access decisions with confidence
- **Enterprise IAM**: SSO/Access Management, IGA, PAM and the interoperability gaps that still make enterprise identity harder than it should be
## Standards Work
I contribute to standards that make identity systems interoperable in practice.
- **IETF OAuth WG**: [ID-JAG](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) and [OAuth Resource Parameter in Token Response](https://datatracker.ietf.org/doc/draft-mcguinness-oauth-resource-token-resp/)
- **OpenID Foundation**: [Provider Commands 1.0](https://openid.github.io/openid-provider-commands/main.html), [OIDC Enterprise Extensions 1.0](https://openid.github.io/connect-enterprise-extensions/main.html), and [IPSIE](https://github.com/openid/ipsie)
For full background, see [karlmcguinness.com](https://karlmcguinness.com).
## Background
Before Okta, I worked across enterprise healthcare software, developer tools, and internet infrastructure. That cross-domain background is where my lens comes from: aligning product intent, architecture boundaries, and execution sequencing under real constraints.
## Get in Touch
I work with teams on product architecture, identity strategy, and standards-informed design reviews. I also speak with product and engineering organizations on agentic identity, delegated authority, and trust infrastructure.
[LinkedIn](https://www.linkedin.com/in/karlmcguinness) ยท [Email](mailto:public@karlmcguinness.com) ยท [GitHub](https://github.com/mcguinness)
---
# Welcome to Control Plane
Canonical URL: https://notes.karlmcguinness.com/notes/welcome-to-control-plane/
Markdown URL: https://notes.karlmcguinness.com/notes/welcome-to-control-plane.md
Identity is getting weird again, and in a good way.
This blog is where I post hot takes, field notes, and analysis on identity, security, and agentic systems. Some posts will be tactical. Some will be opinionated. Some will be me zooming out and asking, "are we solving the right problem at all?"
Lately I keep coming back to one thing: most of our stack is great at deciding who can get in, and still pretty weak at governing what autonomous systems should keep doing over time.
So expect writing on:
- Identity and access patterns that hold up in real environments
- Where agent security models break under real runtime conditions
- Delegation, authority, and accountability across multi-hop systems
- Standards progress, standards gaps, and implementation reality
- The occasional spicy take when the industry starts hand-waving
If you build or run identity and security systems, youโre in the right place.