| Internet-Draft | OAuth Mission Containment | August 2026 |
| McGuinness | Expires 8 February 2027 | [Page] |
Mission-Bound Authorization for OAuth 2.0 commits a Mission's
authority at a single approval event: the approved Authority Set and
its integrity anchors never change. This document defines Mission
Containment, an optional layered extension for narrowing a live
Mission without ending it. When a declared protected event fires (a
tainted read, an anomaly signal, an open-world discovery
tainted-session event), the Mission Issuer commits a contain
transition: an issuer-held, versioned overlay removes capability from
the Mission's effective authority while the Mission stays active
and the approved anchors stay immutable. Containment is monotonic and
removal-only. Token derivation, child delegation, cross-domain
projection, and offline attenuation are gated on the effective
authority; a derivation that asks only for contained capability fails
with authority_contained. Removed authority returns only through a
new approval, as a successor Mission under the expansion profile,
with the predecessor's containment history disclosed to the Approver.
A deployment that never contains a Mission is unaffected by this
document.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://mcguinness.github.io/mission-bound-authorization/draft-mcguinness-oauth-mission-containment.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-mcguinness-oauth-mission-containment/.¶
Source for this draft and an issue tracker can be found at https://github.com/mcguinness/mission-bound-authorization.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 8 February 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Mission-Bound Authorization for OAuth 2.0 [I-D.draft-mcguinness-oauth-mission] (the "issuance profile") makes a Mission a first-class OAuth artifact: a structured, human-approved, integrity-bound task whose authority bounds and outlives every token an agent derives. It commits the Authority Set once, at the approval event, and gates every derivation on Mission state.¶
Incident response against a live Mission has, under the profiles so far, two grains: revoke the Mission and end one body of work across every resource, or suspend it and pause all of it. A mid-task signal is often finer than either. A session reads tainted content and the right response is to remove the Mission's egress capability, not its read capability; an anomaly fires against a payment action and the right response is to remove that action, not the reconciliation work around it. This document defines that finer control: capability kill within one Mission, with the Mission still running.¶
The mechanism is a narrowing overlay, not a change to the Mission.
The approved Authority Set and its intent_hash and authority_hash
anchors are immutable; whether capability is contained is evaluated
state, not part of authority_hash, and folding it into the anchor
would make the committed authority time-varying. This is the same
argument by which the Status profile keeps an entry's fired
completion status out of the anchor
([I-D.draft-mcguinness-oauth-mission-status]). The overlay is held
by the Mission Issuer, versions independently, and only ever grows.¶
This document is optional. It is a layered extension to the issuance
profile, not a change to it. A deployment that implements
[I-D.draft-mcguinness-oauth-mission] and never contains a Mission
is fully conformant to that profile and is unaffected by this
document: it holds no containment overlay, commits no contain
transition, and never emits the authority_contained denial reason.
No lifecycle state is added; a contained Mission is an active (or
suspended) Mission with a non-empty overlay.¶
A Mission Issuer claims conformance to this document only when it contains a Mission; otherwise it remains a plain issuance-profile Mission Issuer. Nothing here places a new requirement back on the issuance profile.¶
This document depends normatively on the issuance profile and is not
implementable alone. It reuses, without restating, that profile's
Mission, Mission Intent, Authority Set and entry shape, integrity
anchors, mission claim, subset rule, and lifecycle gating, and the
Status profile's state version and event-source verification
([I-D.draft-mcguinness-oauth-mission-status]). It uses the terms
Agent (Client), Subject, Approver, Mission Issuer, Authority Set,
Mission, and derived token as defined in the issuance profile.¶
Where this document refers to "the issuance profile" without a section, it means [I-D.draft-mcguinness-oauth-mission] as a whole.¶
This document defines:¶
the containment overlay: containment_version and the contained
set (Section 6);¶
the contain transition and its monotonicity, idempotency, and state-legality rules (Section 7);¶
protected events and the fail-closed rule for indeterminate reports (Section 8);¶
derivation gating on the Effective Authority Set (Section 9);¶
the authority_contained denial reason (Section 10);¶
visibility of containment on status and introspection surfaces (Section 11);¶
propagation over the existing state machinery (Section 12);¶
restoration, only through an approved successor Mission (Section 13);¶
the containment policy and its consent seam (Section 14); and¶
the Containment Evidence object (Section 15).¶
This document does NOT define:¶
constraint tightening within an entry (for example, lowering a
max_amount ceiling); the overlay is removal-only (Section 6.1);¶
any transition that reverts containment in place; restoration is a successor Mission (Section 13);¶
a dedicated containment event type for Signals consumers; the metadata-only lifecycle event carries the change in the interim (Section 12);¶
the content of the controls.containment Mission Intent member; it
is named as an extension point only (Section 14); or¶
how a deployment detects a tainted read or an anomaly; detection belongs to the harness, runtime, and discovery layers (Section 8).¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
This document uses the terms Mission, Mission Intent, Authority Set, Mission Issuer, Mission-bound token, and delegation from [I-D.draft-mcguinness-oauth-mission], and state version from [I-D.draft-mcguinness-oauth-mission-status].¶
The issuer-held, versioned record of capability removed from a Mission's effective authority (Section 6).¶
A {resource, actions} pair in the overlay's contained set. An
entry with no actions member contains the whole Authority Set
entry for that resource.¶
The approved Authority Set minus contained capability and, where the Status profile's completion capability runs, minus discharged entries. Every authority that leaves the Mission is bounded by it (Section 9).¶
An event class the containment policy declares as a containment trigger, whose verified occurrence commits a contain transition (Section 8).¶
The committed, metadata-only change that adds capability to the
contained set and increments containment_version
(Section 7).¶
The issuance profile uses "containment" for set containment in its subset rule; this document uses Containment for the incident-response control. Context distinguishes them.¶
A Mission participating in this profile carries a containment overlay on its Mission record:¶
containment_version:REQUIRED. An integer, 0 at the approval event, incremented by each
contain transition, strictly monotonic per Mission. A Mission with
containment_version 0 is uncontained.¶
contained:REQUIRED when containment_version is greater than 0. An array of
contained-capability entries. Each entry is a JSON object
[RFC8259] with resource (REQUIRED, the resource of an
approved Authority Set entry) and actions (OPTIONAL, an array of
that entry's action values). An entry with no actions member
contains the whole Authority Set entry.¶
The overlay is held by the Mission Issuer. It is not part of the
Mission Intent and not part of the Authority Set, and it never enters
intent_hash or authority_hash: the approved Authority Set and its
anchors are immutable. Whether capability is contained is evaluated
state, not part of authority_hash; folding containment into the
anchor would make the committed authority time-varying, exactly as
folding an entry's fired completion status into it would
([I-D.draft-mcguinness-oauth-mission-status]).¶
The overlay removes capability; it expresses nothing else. Constraint
tightening within an entry (for example, lowering a max_amount
ceiling) is a named deferral: removal keeps monotonicity trivially
decidable, set union over entries, where tightening would need a
per-constraint comparator to prove each successive overlay no broader
than the last. A deployment that needs a tighter constraint contains
the affected actions and re-approves them, tightened, through a
successor Mission (Section 13).¶
Containment composes with the Status profile's entry discharge and
duplicates none of it. Discharge retires an entry because the task
the entry was granted for is done, under a terminal_when condition
the Approver committed inside the Authority Set; containment removes
capability because a protected event fired, under issuer-held policy
the anchor never carried. Both are evaluated state; both commit as
metadata-only state-version increments; derivation excludes both
(Section 9).¶
A contain transition commits additions to the contained set and
increments containment_version. Its rules:¶
Monotonic. A contain transition MUST only add contained capability: the new contained set MUST be a superset of the prior one, and a transition MUST NOT remove an entry, restore an action, or otherwise revert containment. No operation of this profile shrinks the overlay (Section 13).¶
Idempotent per event. Each contain transition is attributed to
a protected event by its event_id. A duplicate report of the same
event_id MUST NOT commit a second transition; the Mission Issuer
answers it with the already-committed result.¶
Legal states. A contain transition is legal from active and
from suspended, and MUST be refused in every terminal state. A
resume does not clear the overlay: a Mission suspended, contained,
and resumed returns to active still contained.¶
No state change. The transition changes no lifecycle state: a
contained active Mission remains active. Containment narrows
what the Mission can do, never whether it runs.¶
Each contain transition is a committed metadata-only change for the purposes of the state version, which the Status profile defines as "incremented on each committed lifecycle transition (the approval event is version 1) and each committed metadata-only change" ([I-D.draft-mcguinness-oauth-mission-status]): the Mission's state version increments at the commit, so a materialized policy view that commits a state version ([I-D.draft-mcguinness-mission-runtime]) is detectably obsolete after a containment.¶
The containment policy (Section 14) declares the event classes that trigger containment. Representative classes, defined by their own layers, not here: a tainted read under the harness taint policy ([I-D.draft-mcguinness-mission-harness]), an anomaly signal from the deployment's monitoring, and the tainted-session event of open-world discovery ([I-D.draft-mcguinness-mission-discovery]).¶
A protected event is a trusted input to authority: the Mission Issuer
MUST authenticate and integrity-verify an event source outside its
own trust domain before acting on its report. The Status profile's
signed event-source profile, a JWS status document verified against
the source identity rather than the transport origin, is the
interoperable mechanism
([I-D.draft-mcguinness-oauth-mission-status]).¶
A protected event whose authenticity or applicability is indeterminate fails closed. The Mission Issuer MUST either commit the narrowing the policy maps to the event or withhold derivation of the implicated capability until the report is resolved; it MUST NOT ignore the event. An unverifiable report can cost the Mission authority early; it can never preserve authority the policy would have removed.¶
A derived or delegated credential MUST NOT carry contained capability. Token derivation under a contained Mission MUST evaluate the request against the Effective Authority Set, with the issuance profile's subset rule unchanged:¶
a derivation request whose authority lies entirely within the
contained set MUST fail, with the authority_contained denial
reason (Section 10);¶
a derivation request covering both contained and uncontained capability MUST omit the contained capability: the issued token carries only the uncontained remainder; and¶
tokens already issued before the transition are the same residual
that revocation carries, bounded the same way by their own exp
and the deployment's freshness rules (Section 12).¶
The same bound applies wherever authority leaves the Mission. A Child Mission attenuates from the parent's Effective Authority Set, so a contained capability never reaches a sub-agent ([I-D.draft-mcguinness-oauth-mission-child-delegation]). A cross-domain projection projects the Effective Authority Set, so a contained capability never crosses the trust boundary ([I-D.draft-mcguinness-oauth-mission-cross-domain]). An offline attenuation root is derived from the Effective Authority Set, so no offline chain can narrow its way back to contained capability ([I-D.draft-mcguinness-oauth-mission-attenuation]).¶
authority_contained:The requested capability was approved for this Mission and is currently contained.¶
The distinction from the runtime enforcement layer's
out_of_authority, the action not within the Authority Set
([I-D.draft-mcguinness-mission-authzen]), is approval history:
out_of_authority reports capability that was never approved, and
authority_contained reports capability the Approver granted and the
issuer then contained. That history is what makes the denial
expansion-eligible: once-approved authority is a candidate for
re-adjudication (Section 13), where never-approved authority is
an ordinary out-of-authority denial.¶
At the token endpoint the denial uses the issuance profile's error
vocabulary for a request outside the derivable authority and MAY
additionally carry authority_contained in the shared
mission_denial_reason member the expansion profile registers
([I-D.draft-mcguinness-oauth-mission-expansion]). A runtime
enforcement deployment carries it as a denial-reason identifier
alongside the classification set of
[I-D.draft-mcguinness-mission-authzen]. A consumer MUST treat an
unrecognized reason code as a denial with no further semantics.¶
Consistent with the expansion profile, the classification of a denial
as expansion-eligible belongs to the component that denies the
action; this document classifies authority_contained as
expansion-eligible, and eligibility is not an authorization in favor
of expansion.¶
A contained Mission MUST NOT present contained capability as live. When this profile runs:¶
the mission object of the Mission Status Response and the token
introspection Mission projection MUST carry containment_version
([I-D.draft-mcguinness-oauth-mission-status]); and¶
any surface of those profiles that discloses Mission authority MUST omit contained capability or annotate it as contained.¶
A consumer that does not understand containment_version ignores it;
the state version already makes the change observable
(Section 7). A consumer that reads it holds a precise
question: whether the authority view it materialized predates the
current overlay.¶
Containment rides the existing state-propagation machinery and adds none:¶
Pull. The contain transition's state-version increment moves
the Mission Status Response: the next read reports the new
version and containment_version, and a materialized view naming
an older version is detectably obsolete (Section 7).¶
Status List. A contained active Mission stays active, so
its Status List bit is unchanged: the list carries reliance bits
only, and containment detail, like the state version, stays on the
authoritative surfaces
([I-D.draft-mcguinness-oauth-mission-status]). Containment binds
derivation at the issuer, so a list-only consumer's staleness never
widens what a contained Mission can issue.¶
Push. Where Signals runs, the commit is emitted as a
mission.lifecycle-change whose state equals its prior_state,
that profile's metadata-only shape, incrementing version like any
other committed change
([I-D.draft-mcguinness-oauth-mission-signals]). A dedicated
containment event type is deferred; the version movement is what
makes the change observable in the interim.¶
How quickly a consumer must adopt the narrowed view is bounded by the freshness and staleness rules it already operates under ([I-D.draft-mcguinness-oauth-mission-status], [I-D.draft-mcguinness-mission-runtime]); this document does not restate them.¶
Contained authority returns only as a new approval. No transition of
this profile removes capability from the overlay; the path back is a
successor Mission adjudicated under the expansion profile
([I-D.draft-mcguinness-oauth-mission-expansion]), whose authority
comes only from its own approval event. An authority_contained
denial is the expansion-eligible signal for that path
(Section 10).¶
Two rules close the laundering gap between containment and expansion:¶
Containment MUST NOT propagate to a successor: the successor's
overlay starts empty, containment_version 0, because its
authority is whatever its own approval yields, not the
predecessor's minus a memory.¶
An expansion whose predecessor has a non-empty containment overlay MUST surface the predecessor's containment history in the expansion consent disclosure: at minimum the contained capability and the event class that contained each entry.¶
Together they keep the successor clean without letting it launder: capability removed for cause can return, but only past an Approver who saw the cause.¶
The map from protected event class to narrowing is the containment policy. It is issuer-held, parallel to the derivation policy by which the issuance profile derives the Authority Set: an input the issuer applies at a committed change, never content of the anchor. The client does not supply it, the Mission Intent does not carry it, and an intent-shaping component does not write it: a compromised agent, client, or shaping component MUST NOT be able to select which events contain or how far a containment narrows.¶
A deployment may want the Approver to consent to tightened
containment behavior at approval time. controls.containment, a
member of the Mission Intent's controls object under the issuance
profile's extensibility rules, is named here as that extension point;
its content is explicitly not defined by this document. An
Approver-consented member can only tighten what the issuer-held
policy would do, never loosen it.¶
The Mission Issuer MUST record a Containment Evidence object for each contain transition. This evidence is audit material and does not grant authority.¶
A Containment Evidence object is a JSON object [RFC8259] with:¶
evidence_id:REQUIRED. Unique identifier.¶
mission:REQUIRED. Mission reference: id, issuer, and authority_hash.¶
event:REQUIRED. The protected event: type, source, observed_at (an
RFC 3339 [RFC3339] date-time), and event_id.¶
policy:REQUIRED. Identifier of the containment policy rule applied.¶
prior_version, new_version:REQUIRED. The Mission's state version before and after the transition.¶
prior_containment_version, new_containment_version:REQUIRED. The overlay's version before and after the transition.¶
removed:REQUIRED. The contained-capability entries this transition added (Section 6).¶
created_at:Example:¶
{
"evidence_id": "cnt_4Tq9mV2xLp",
"mission": {
"id": "msn_8RfX2Lqv9TqMv4z7sA2bN1k0YpEdHc9-",
"issuer": "https://as.example.com",
"authority_hash":
"sha-256:tY2nD9bM7sX1cF8gH2vJ4kE5pNQl3KvZ4mP5x0wQrR6"
},
"event": {
"type": "session-taint",
"source": "https://harness.example.com",
"observed_at": "2026-11-02T09:41:00Z",
"event_id": "evt_7pQ4mK1c"
},
"policy": "contain-egress-on-taint-v3",
"prior_version": 4,
"new_version": 5,
"prior_containment_version": 0,
"new_containment_version": 1,
"removed": [
{ "resource": "https://mail.example.com",
"actions": ["messages.send"] }
],
"created_at": "2026-11-02T09:41:02Z"
}
¶
A Containment Evidence object's canonical bytes are its JCS
[RFC8785] canonicalization, and its type identifier is
application/mission-containment-evidence+json, used by local
agreement pending registration. An audit or transparency profile
registers the object by these values.¶
An implementation claims conformance to this document only in the Mission Issuer role and only when it contains a Mission. A conforming containment-capable Mission Issuer MUST:¶
hold the containment overlay per Mission and commit contain transitions under the monotonicity, idempotency, and state-legality rules of Section 7;¶
verify protected events and fail closed on an indeterminate report (Section 8);¶
gate every derivation, delegation, projection, and attenuation root
on the Effective Authority Set, and deny an all-contained request
with authority_contained (Section 9,
Section 10);¶
carry containment_version on its status and introspection
surfaces and never present contained capability as live
(Section 11);¶
record a Containment Evidence object per contain transition (Section 15); and¶
where it also adjudicates expansion, start the successor uncontained and surface the predecessor's containment history at the expansion consent (Section 13).¶
A containment-capable Mission Issuer is also a conforming issuance-profile Mission Issuer ([I-D.draft-mcguinness-oauth-mission]). A Resource Server requires no new behavior: a token it sees never carries contained capability (Section 9).¶
Containment's guarantee is directional: every rule in this document moves authority in one direction, down. The risks specific to it are who can force that movement and what the movement discloses.¶
Containment is monotonic by construction: it only removes capability, so it is not a path to escalation, and a compromised or injected agent cannot use a protected event to widen authority. The worst a forged or forced event achieves is a forced premature containment, a denial-of-service on the task: authority the task still needs is withdrawn. This is the same residual the Status profile carries for forced premature discharge, bounded the same way: the event-source authentication, integrity, and fail-closed rules of Section 8 bound who can force it, and restoration remains available through a fresh approval (Section 13).¶
Whatever surface applies contain transitions, the issuer's event intake or an operator plane, holds a denial capability over live work. It is operator trust: a deployment MUST authenticate and authorize callers of that surface as it does lifecycle mutation, and SHOULD govern bulk containment with the same dry-run-first posture as bulk lifecycle operations ([I-D.draft-mcguinness-oauth-mission-management]).¶
A compromised event source can fire protected events at will. Monotonicity bounds its blast radius: every transition it forces yields too little authority, never too much, and the overlay it inflates cannot be spent, delegated, or projected. Recovery is bounded too: the poisoned Missions are enumerable from their Containment Evidence, and each restores only through an approval that sees the containment history (Section 13).¶
The disclosure rule of Section 13 is load-bearing: without it, expansion is a laundering path in which capability removed for cause returns through a successor the Approver believes is clean. An implementation that omits the predecessor's containment history from the expansion consent defeats containment for any adversary patient enough to ask again.¶
A containment overlay discloses incident posture: that an event
fired, which capability it implicated, and when. The Mission Status
Response and introspection surfaces carry containment_version and
any containment annotations only to callers those profiles already
authenticate and authorize; this document adds no anonymous surface
and preserves the Status profile's anti-oracle posture
([I-D.draft-mcguinness-oauth-mission-status]). Containment Evidence
carries event detail (type, source, event_id) and is audit
material: a deployment retains and discloses it under the same access
rules as its other Mission evidence, and SHOULD NOT copy event detail
onto broader surfaces than the audit trail requires.¶
This document requires no IANA action.¶
The containment_version member and any containment annotation ride
surfaces whose defining profile establishes no per-member registry
(the mission object of the Mission Status Response and the
introspection Mission projection,
[I-D.draft-mcguinness-oauth-mission-status]); consistent with those
surfaces' rules, a consumer ignores an unrecognized member.¶
authority_contained extends, by specification, the closed set of
reason codes carried in the shared mission_denial_reason member;
that member's registrations are the expansion profile's, and this
document creates no registry for the codes, consistent with that
profile's plan for a future shared "Mission Denial Reason" registry
([I-D.draft-mcguinness-oauth-mission-expansion]).¶
The Containment Evidence type identifier
application/mission-containment-evidence+json is used by local
agreement pending registration (Section 15.2).¶
This document is part of the Mission-Bound Authorization for OAuth 2.0 set and defines the in-Mission narrowing complement to Mission Expansion's widening.¶