<?xml version="1.0" encoding="utf-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     docName="draft-schrock-ep-enforcement-point-00"
     category="info" ipr="trust200902" submissionType="IETF"
     version="3" tocInclude="true" sortRefs="true" symRefs="true">
  <front>
    <title abbrev="EP Enforcement Point">An Enforcement-Point Profile for Authorization Receipts</title>
    <seriesInfo name="Internet-Draft" value="draft-schrock-ep-enforcement-point-00"/>
    <author fullname="Iman Schrock">
      <organization>EMILIA Protocol, Inc.</organization>
      <address>
        <postal>
          <country>US</country>
        </postal>
        <email>team@emiliaprotocol.ai</email>
      </address>
    </author>
    <date year="2026" month="June" day="28"/>
    <area>sec</area>
    <keyword>AI agents</keyword>
    <keyword>policy enforcement point</keyword>
    <keyword>authorization</keyword>
    <keyword>receipts</keyword>
    <keyword>fail-closed</keyword>
    <abstract>
      <t>This document defines an Enforcement-Point (PEP) profile that
      composes on the shared verifier core specified in the referenced
      authorization-receipt Internet-Draft
      <xref target="I-D.schrock-ep-authorization-receipts"/>. The profile gives a
      Policy Enforcement Point a small, stable contract for high-risk
      agent actions: a registered decision vocabulary
      (<tt>allow</tt> / <tt>allow_with_signoff</tt> / <tt>deny</tt>,
      with an out-of-band <tt>observe</tt> mode), a decision-request and
      decision-response schema bound to the exact action under
      evaluation, and a requirement that every decision be bound to an
      offline-verifiable authorization receipt (the EP-RECEIPT-v1 wire
      format). It states the conformance requirements an enforcement
      point must meet — fail-closed on uncertainty, honoring of one-time
      consumption, and receipt emission — and the central invariant that
      a rejecting decision must take effect before any approval-bearing
      state mutation.</t>
      <t>This profile <em>consumes</em> a policy decision; it does not
      define a policy language. It is complementary to, not a
      replacement for, the decision-interface and policy-engine work it
      sits behind (AuthZEN, OPA, Cedar), and it does not redefine the
      verifier core, which is specified in
      (draft-schrock-ep-authorization-receipts).</t>
    </abstract>
  </front>
  <middle>
    <section>
      <name>Introduction</name>
      <t>An AI agent or automated process about to perform an
      irreversible operation — releasing a payment, changing a
      beneficiary, dropping a table, force-pushing over history — sits
      in front of a system of record that will, or will not, let the
      operation through. The component that makes that go/no-go call at
      the moment of execution is a Policy Enforcement Point (PEP). This
      document profiles how an EP enforcement point behaves so that its
      decisions are interoperable, fail-closed, and backed by evidence a
      third party can verify without trusting the enforcement point
      itself.</t>
      <t>The authorization-receipt work
      (<xref target="I-D.schrock-ep-authorization-receipts"/>) defines
      the shared verifier core: the canonical Action Object and action
      hash, the human-signed Authorization Context, one-time
      consumption, separation of duties, and the offline verification
      algorithm over a signed receipt. This document does not restate
      or redefine any of that. It defines the much narrower thing that
      sits at the enforcement boundary: the vocabulary an enforcement
      point speaks, the request it answers, the response it returns, and
      the binding of that response to a receipt the core can verify.</t>
      <t>Two boundaries are deliberate and load-bearing:</t>
      <ol>
        <li><strong>The policy boundary.</strong> An enforcement point
        does not decide policy <em>semantics</em>. It poses an action in
        context to a Policy Decision Point (PDP) and consumes the
        decision. Whether the PDP is backed by Cedar, OPA/Rego, an
        AuthZEN exchange, or an in-process rule engine is out of scope;
        this profile defines only the shape and meaning of the decision
        once it crosses back to the enforcement point.</li>
        <li><strong>The verifier boundary.</strong> An enforcement point
        does not invent a receipt format or a verification algorithm. It
        emits receipts in the format the shared core verifies, and a
        relying party verifies them with the core's offline algorithm.
        This profile defines only the binding between a decision and the
        receipt that records it.</li>
      </ol>
      <t>The result is a thin, honest contract: an enforcement point is
      conformant when it speaks the registered vocabulary, fails closed,
      honors one-time consumption, and emits a verifiable receipt for
      every decision that reaches an approval-bearing state — and it
      makes no claim about deciding policy or about owning the trust
      core.</t>
      <section>
        <name>Design Goals</name>
        <ul>
          <li><strong>G1 — Stable vocabulary.</strong> The decision
          terms are a small, registered, stable enum. An enforcement
          point and a relying party <bcp14>MUST</bcp14> agree on their
          meaning across versions and operators.</li>
          <li><strong>G2 — Action-bound decisions.</strong> Every
          decision is bound to one exact action via the action hash of
          the shared core. A decision for action A
          <bcp14>MUST NOT</bcp14> authorize action B.</li>
          <li><strong>G3 — Fail-closed.</strong> Uncertainty,
          transport failure to a PDP, an unverifiable receipt, or any
          unrecognized state <bcp14>MUST</bcp14> resolve to a
          non-permissive outcome. The safe default is to withhold, not
          to allow.</li>
          <li><strong>G4 — Reject-before-mutation.</strong> A rejecting
          decision <bcp14>MUST</bcp14> take effect before any
          approval-bearing state mutation occurs
          (<xref target="reject-before-mutation"/>). The enforcement
          point sits in front of the write, never beside it.</li>
          <li><strong>G5 — Receipt-bound.</strong> Every decision that
          reaches an approval-bearing state
          (<xref target="positive-states"/>) <bcp14>MUST</bcp14> be
          bound to an offline-verifiable authorization receipt produced
          in the shared core's format.</li>
          <li><strong>G6 — Consume policy, not author it.</strong> The
          enforcement point <bcp14>MUST NOT</bcp14> embed an
          authorization-policy language; it consumes a PDP decision
          (<xref target="policy-deferral"/>).</li>
          <li><strong>G7 — Honest enforcement class.</strong> The
          deployment topology that determines whether the gate is
          bypassable is declared, not implied
          (<xref target="conformance"/>); claims
          <bcp14>MUST NOT</bcp14> overstate it.</li>
        </ul>
      </section>
      <section anchor="scope">
        <name>Scope</name>
        <t>In scope: the decision vocabulary; the decision request
        (action context plus presented evidence) and decision response
        schemas; the binding of a decision to an EP-RECEIPT-v1 receipt;
        the reject-before-mutation invariant at the enforcement point;
        and conformance requirements for an enforcement point.</t>
        <t>Out of scope, by reference to other work and not restated
        here:</t>
        <ul>
          <li>The receipt format internals, the canonical Action Object,
          the Authorization Context, the human signoff signature, the
          Approver Directory, and the offline verification algorithm —
          all defined by the shared verifier core
          (<xref target="I-D.schrock-ep-authorization-receipts"/>). This
          profile references them; it does not redefine them.</li>
          <li>The authorization-policy <em>language</em> and decision
          <em>semantics</em> — defined by policy engines and decision
          interfaces (Cedar, OPA/Rego, AuthZEN), which this profile
          consumes (<xref target="policy-deferral"/>).</li>
          <li>The human-approval ceremony itself (how a named human is
          reached and how they sign) — defined by the core; the
          enforcement point only observes its result through receipt
          state.</li>
          <li>Risk signaling and advisory inputs, which may inform a PDP
          but are never the sole gate; an enforcement point
          <bcp14>MUST NOT</bcp14> treat an advisory as a decision.</li>
        </ul>
      </section>
    </section>
    <section anchor="terminology">
      <name>Terminology</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
      "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>",
      "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>",
      "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>",
      "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and
      "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
      described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/>
      when, and only when, they appear in all capitals, as shown here.</t>
      <t><strong>Enforcement Point (PEP).</strong> The component that, at
      the moment of execution, permits or withholds a high-risk action.
      It poses the action to a PDP, consumes the decision, binds the
      decision to a receipt, and gates the action accordingly. It is the
      subject of this profile.</t>
      <t><strong>Policy Decision Point (PDP).</strong> The component that
      evaluates authorization policy and returns a decision. Its policy
      language and evaluation are out of scope; this profile consumes its
      output. See <xref target="policy-deferral"/>.</t>
      <t><strong>Action.</strong> A single proposed operation with
      concrete parameters, represented by the shared core's canonical
      Action Object and identified by its action hash. This profile uses
      the action hash as the binding key for a decision; it does not
      redefine the Action Object.</t>
      <t><strong>Decision.</strong> A member of the registered decision
      vocabulary (<xref target="decision-vocabulary"/>) returned for one
      action in one context.</t>
      <t><strong>Decision Request.</strong> The action context and
      presented evidence an enforcement point evaluates
      (<xref target="decision-request"/>).</t>
      <t><strong>Decision Response.</strong> The decision and its
      supporting fields the enforcement point returns
      (<xref target="decision-response"/>).</t>
      <t><strong>Authorization Receipt.</strong> The offline-verifiable
      evidence artifact, in the EP-RECEIPT-v1 wire format, that records a
      decision and (where applicable) its signed human approval. Its
      structure and verification are defined by the shared core; this
      profile defines its binding to a decision
      (<xref target="receipt-binding"/>).</t>
      <t><strong>EP-RECEIPT-v1.</strong> The wire-format tag of the
      receipt the shared core verifies. Used in this document to name the
      on-the-wire artifact; the buyer-facing term for the same thing is
      "authorization receipt."</t>
      <t><strong>Approval-bearing state mutation.</strong> Any write that
      records, or relies upon, an authorization having been granted —
      the state change the action exists to make. The
      reject-before-mutation invariant
      (<xref target="reject-before-mutation"/>) governs its ordering
      relative to the decision.</t>
      <t><strong>Enforcement mode.</strong> The operational posture of
      the enforcement point for a given decision —
      <tt>enforce</tt>, <tt>warn</tt>, or <tt>observe</tt>
      (<xref target="enforcement-modes"/>) — distinct from the decision
      vocabulary itself.</t>
    </section>
    <section anchor="decision-vocabulary">
      <name>The Decision Vocabulary</name>
      <t>An enforcement point speaks a small, registered, stable
      vocabulary. The <tt>decision</tt> value of a Decision Response
      (<xref target="decision-response"/>) <bcp14>MUST</bcp14> be exactly
      one of the following enum values. Producers <bcp14>MUST NOT</bcp14>
      emit, and consumers <bcp14>MUST</bcp14> reject as malformed, any
      <tt>decision</tt> value outside this set.</t>
      <table>
        <thead>
          <tr><th>Value</th><th>Enforcement outcome</th><th>Meaning</th></tr>
        </thead>
        <tbody>
          <tr>
            <td><tt>allow</tt></td>
            <td>Proceed</td>
            <td>Execute without further gating. No human approval is
            required by policy for this action in this context.</td>
          </tr>
          <tr>
            <td><tt>allow_with_signoff</tt></td>
            <td>Withhold pending approval</td>
            <td>The action is permitted by policy but
            <bcp14>MUST NOT</bcp14> execute until a named, accountable
            human approves it through the shared core's signoff. The
            enforcement point withholds execution until a verifiable
            positive-state receipt
            (<xref target="positive-states"/>) exists, then proceeds.</td>
          </tr>
          <tr>
            <td><tt>deny</tt></td>
            <td>Refuse</td>
            <td>The action is blocked and <bcp14>MUST NOT</bcp14>
            execute. <tt>deny</tt> is terminal for the attempt and has no
            signoff path: a denied action cannot be rescued by human
            approval. It is reserved for hard failures (for example
            sanctions or embargo hits, or device/posture signals an
            operator designates non-overridable).</td>
          </tr>
        </tbody>
      </table>
      <t>The three values above are the decision vocabulary proper:
      every decision an enforcement point makes is one of
      <tt>allow</tt>, <tt>allow_with_signoff</tt>, or <tt>deny</tt>.
      They map to exactly three enforcement outcomes — proceed, withhold
      pending approval, and refuse — and that mapping is stable.</t>
      <t><tt>observe</tt> is not a fourth decision but an enforcement
      <em>mode</em> (<xref target="enforcement-modes"/>). In
      <tt>observe</tt> mode a decision that would otherwise withhold or
      refuse is recorded but not enforced. To keep the recorded decision
      faithful, an enforcement point in <tt>observe</tt> mode
      <bcp14>MUST</bcp14> report the decision it would have enforced in a
      separate field (<tt>observed_decision</tt>,
      <xref target="decision-response"/>) rather than overwriting
      <tt>decision</tt> with a permissive value; the
      <tt>observe</tt> token <bcp14>MAY</bcp14> appear as the effective
      <tt>decision</tt> only to signal that no enforcement occurred. This
      preserves the invariant that the substantive decision is always one
      of the three vocabulary values.</t>
      <t>The vocabulary is closed for v1. Future versions
      <bcp14>MAY</bcp14> register additional values only through the
      registry contemplated in <xref target="iana"/>; an enforcement
      point that receives an unrecognized value
      <bcp14>MUST</bcp14> fail closed (<xref target="fail-closed"/>) and
      <bcp14>MUST NOT</bcp14> treat it as <tt>allow</tt>.</t>
    </section>
    <section anchor="decision-request">
      <name>The Decision Request</name>
      <t>A Decision Request poses one action, in its context, with the
      evidence the enforcement point presents for evaluation. It is the
      input to the enforcement point; the enforcement point in turn poses
      the policy-relevant subset to a PDP
      (<xref target="policy-deferral"/>). The request carries two parts:
      the <strong>action context</strong> (who is doing what, to what,
      under which policy) and the <strong>presented evidence</strong>
      (the risk and posture signals offered for the decision).</t>
      <sourcecode type="json"><![CDATA[
{
  "ep_version": "1.0",
  "request_type": "ep.decision.request.v1",
  "organization_id": "ep:org:acme",
  "action": {
    "action_type": "wire.release",
    "action_hash": "sha256:9f2c...",
    "target": { "system": "treasury.example",
                "resource": "wire/8841" },
    "amount": "2400000.00",
    "currency": "USD",
    "target_changed_fields": ["beneficiary_account"]
  },
  "actor": {
    "initiator": "ep:entity:agent-recon-7",
    "actor_role": "treasury-agent",
    "auth_strength": "phishing_resistant_mfa"
  },
  "evidence": {
    "risk_flags": ["new_beneficiary"],
    "advisory_refs": ["ep:advisory:..."]
  },
  "policy_id": "ep:policy:wires-over-100k@v12",
  "enforcement_mode": "enforce",
  "before_state_hash": "sha256:aa01...",
  "after_state_hash": "sha256:bb02..."
}
]]></sourcecode>
      <t>Rules:</t>
      <ul>
        <li><tt>action.action_hash</tt> <bcp14>REQUIRED</bcp14>. It is
        the SHA-256 digest of the shared core's canonical Action Object
        and is the sole binding key for the decision. The enforcement
        point <bcp14>MUST</bcp14> recompute the action hash from the
        canonical action it actually presents and
        <bcp14>MUST</bcp14> reject a request whose recomputed hash does
        not match <tt>action.action_hash</tt>. The fields under
        <tt>action</tt> other than the hash are conveniences for the PDP
        and for rendering; they are not the binding and
        <bcp14>MUST NOT</bcp14> substitute for it.</li>
        <li><tt>policy_id</tt> <bcp14>REQUIRED</bcp14>. Names the policy
        whose decision the enforcement point will consume. The
        enforcement point does not interpret the policy's rules; it
        identifies the policy so the PDP and the receipt can pin the
        exact version evaluated.</li>
        <li><tt>actor.initiator</tt> <bcp14>REQUIRED</bcp14>. Identifies
        the proposing entity. It is carried so the core can enforce
        separation of duties (an initiator must not approve its own
        action); the enforcement point identifies the initiator but
        grants it no approval authority.</li>
        <li><tt>evidence</tt> <bcp14>OPTIONAL</bcp14>. Risk flags,
        advisory references, and posture signals offered to the decision.
        Presented evidence is an <em>input</em> to a decision and
        <bcp14>MUST NOT</bcp14> be treated as a decision: an advisory or
        risk signal is never the sole gate
        (<xref target="advisory-not-a-decision"/>).</li>
        <li><tt>enforcement_mode</tt> <bcp14>OPTIONAL</bcp14>, default
        <tt>enforce</tt>. See <xref target="enforcement-modes"/>.</li>
        <li><tt>before_state_hash</tt> / <tt>after_state_hash</tt>
        <bcp14>OPTIONAL</bcp14>. Digests of the pre- and post-action
        state that the receipt records so a relying party can later
        confirm which mutation the decision governed. They commit the
        decision to a specific state transition; they are evidence, not
        policy.</li>
        <li>Producers <bcp14>MUST NOT</bcp14> place an
        authorization-policy expression (rules, conditions, a policy
        language fragment) anywhere in the request. The request poses an
        action; it does not carry a policy to be evaluated inline.</li>
      </ul>
    </section>
    <section anchor="decision-response">
      <name>The Decision Response</name>
      <t>A Decision Response returns the decision for one action and the
      fields a caller needs to act on it and to locate the bound
      receipt.</t>
      <sourcecode type="json"><![CDATA[
{
  "ep_version": "1.0",
  "response_type": "ep.decision.response.v1",
  "decision": "allow_with_signoff",
  "observed_decision": null,
  "action_hash": "sha256:9f2c...",
  "policy_id": "ep:policy:wires-over-100k@v12",
  "policy_hash": "sha256:77ab...",
  "signoff_required": true,
  "signoff_tier": "single",
  "reasons": ["money_destination_change", "amount_over_threshold"],
  "receipt_id": "ep:receipt:01J...",
  "receipt_status": "pending_signoff",
  "expires_at": "2026-06-09T17:36:05Z",
  "enforcement_class": "EP-Verified-Execution"
}
]]></sourcecode>
      <t>Rules:</t>
      <ul>
        <li><tt>decision</tt> <bcp14>REQUIRED</bcp14>. Exactly one
        vocabulary value (<xref target="decision-vocabulary"/>), or the
        <tt>observe</tt> token when, and only when, the request's
        <tt>enforcement_mode</tt> was <tt>observe</tt> and no enforcement
        occurred.</li>
        <li><tt>observed_decision</tt>
        <bcp14>REQUIRED</bcp14> when <tt>decision</tt> is
        <tt>observe</tt>; otherwise <bcp14>OPTIONAL</bcp14> and
        <tt>null</tt>. When present it
        <bcp14>MUST</bcp14> be the vocabulary value that would have been
        enforced. In <tt>observe</tt> mode the enforcement point
        <bcp14>MUST</bcp14> still set <tt>signoff_required</tt> to the
        value the enforced decision would have carried, so the record is
        faithful to what would have been required.</li>
        <li><tt>action_hash</tt> <bcp14>REQUIRED</bcp14> and
        <bcp14>MUST</bcp14> equal the request's
        <tt>action.action_hash</tt>. This is the response's binding to
        the exact action; a response whose <tt>action_hash</tt> differs
        from the request <bcp14>MUST</bcp14> be rejected.</li>
        <li><tt>policy_hash</tt> <bcp14>REQUIRED</bcp14> for any
        non-<tt>deny</tt> decision. It pins the exact policy version the
        PDP evaluated, mirroring the core's requirement that an approval
        bound to one policy version not satisfy another.</li>
        <li><tt>signoff_required</tt> <bcp14>REQUIRED</bcp14>, boolean.
        <bcp14>MUST</bcp14> be <tt>true</tt> when <tt>decision</tt> (or
        <tt>observed_decision</tt>) is <tt>allow_with_signoff</tt> and
        <tt>false</tt> when it is <tt>allow</tt> or <tt>deny</tt>.</li>
        <li><tt>signoff_tier</tt> <bcp14>OPTIONAL</bcp14>. When signoff
        is required, names the accountability tier the approval flow must
        satisfy (for example a single accountable approver, or two
        independent approvers). The enforcement point reports the tier;
        the shared core enforces distinctness of approvers. The tier is
        a policy output the enforcement point relays, not a rule it
        authored.</li>
        <li><tt>reasons</tt> <bcp14>REQUIRED</bcp14>, an array of
        machine-stable reason codes. It <bcp14>MUST NOT</bcp14> be empty
        for any non-<tt>allow</tt> decision. Reasons are explanatory;
        they are not a policy language and carry no executable
        semantics.</li>
        <li><tt>receipt_id</tt> <bcp14>REQUIRED</bcp14> for any decision
        that reaches an approval-bearing state
        (<xref target="positive-states"/>); it identifies the bound
        authorization receipt (<xref target="receipt-binding"/>).</li>
        <li><tt>receipt_status</tt> <bcp14>REQUIRED</bcp14> when
        <tt>receipt_id</tt> is present. It reflects the receipt's
        lifecycle state as defined by the core (for example
        <tt>issued</tt>, <tt>pending_signoff</tt>, or
        <tt>denied</tt>).</li>
        <li><tt>enforcement_class</tt> <bcp14>REQUIRED</bcp14>. The
        declared conformance class (<xref target="conformance"/>); it
        <bcp14>MUST NOT</bcp14> state a stronger class than deployed.</li>
      </ul>
    </section>
    <section anchor="receipt-binding">
      <name>Binding a Decision to an Authorization Receipt</name>
      <t>Every decision that reaches an approval-bearing state
      (<xref target="positive-states"/>) <bcp14>MUST</bcp14> be bound to
      an authorization receipt that the shared verifier core can verify
      offline. This profile defines the binding; it does not redefine the
      receipt, the signature, or the verification algorithm, all of which
      are specified by
      <xref target="I-D.schrock-ep-authorization-receipts"/>.</t>
      <section>
        <name>The Binding Key</name>
        <t>The binding is the canonical action. The enforcement point
        <bcp14>MUST</bcp14> preserve, at decision time, the exact
        canonical Action Object it evaluated, and the bound receipt
        <bcp14>MUST</bcp14> carry that same canonical action and its
        action hash. The receipt's claim therefore commits to: the
        action type, the decision (carried as the receipt's outcome), the
        enforcement mode, the canonical action, the action hash, the
        pre- and post-state hashes where supplied, and the pinned policy
        identifier and policy hash. Because the canonical action signed
        in the receipt is byte-for-byte the action the enforcement point
        decided on, a receipt for one action cannot be presented to
        authorize another — this is the "what was decided is what was
        signed" property, inherited from the core, not reinvented
        here.</t>
        <t>A schematic of the bound receipt, whose internals are owned by
        the core, is shown for orientation only:</t>
        <sourcecode type="json"><![CDATA[
{
  "@version": "EP-RECEIPT-v1",
  "payload": {
    "receipt_id": "ep:receipt:01J...",
    "claim": {
      "action_type": "wire.release",
      "outcome": "allow_with_signoff",
      "enforcement_mode": "enforce",
      "canonical_action": { "...": "exact action decided on" },
      "action_hash": "sha256:9f2c...",
      "before_state_hash": "sha256:aa01...",
      "after_state_hash": "sha256:bb02...",
      "policy_id": "ep:policy:wires-over-100k@v12",
      "policy_hash": "sha256:77ab..."
    },
    "authorization": {
      "status": "approved_pending_consume",
      "signoff_required": true,
      "approver_id": "ep:approver:jchen-controller",
      "approved_at": "2026-06-09T17:24:40Z"
    }
  },
  "signature": { "algorithm": "Ed25519", "...": "owned by the core" }
}
]]></sourcecode>
        <t>The enforcement point populates the claim from the decision it
        made; the signature, its algorithm, the signer key material, and
        the verification procedure are defined and performed by the
        core. An enforcement point <bcp14>MUST NOT</bcp14> define its own
        signature scheme or its own canonicalization for the bound
        receipt.</t>
      </section>
      <section anchor="positive-states">
        <name>Positive States and the Honesty Gate</name>
        <t>A receipt is bound to a signed, offline-verifiable evidence
        artifact only when the decision has reached an approval-bearing
        positive state — that is, when an approval has genuinely been
        granted and is awaiting consume, or has been consumed. An
        enforcement point <bcp14>MUST NOT</bcp14> cause a signed receipt
        to be emitted for a decision in any non-positive state
        (pending, denied, rejected, or expired) or for a decision whose
        canonical action was not preserved. Where the shared core would
        return no signature for such a state, the enforcement point
        <bcp14>MUST</bcp14> surface the unsigned evidence packet rather
        than synthesize a signature: it <bcp14>MUST NOT</bcp14> assert,
        through a signed artifact, an authorization that was never
        granted.</t>
        <t>Concretely: a <tt>deny</tt> decision is bound to a receipt
        that records the refusal but carries no positive-state signature;
        an <tt>allow_with_signoff</tt> decision is bound to a
        pending receipt that becomes a signed, offline-verifiable
        artifact only once the named human's approval reaches a positive
        state; an <tt>allow</tt> decision is bound to a receipt recording
        that no human approval was required. The honesty gate is a
        property of the core that the enforcement point
        <bcp14>MUST</bcp14> respect, not subvert.</t>
      </section>
      <section anchor="offline-verification">
        <name>Offline Verifiability of the Bound Receipt</name>
        <t>A relying party holding a bound receipt and the signer's
        pinned public key material <bcp14>MUST</bcp14> be able to verify
        it with no network access to the enforcement point, using the
        shared core's offline verification algorithm
        (<xref target="I-D.schrock-ep-authorization-receipts"/>). The
        enforcement point's role is to produce receipts that pass that
        algorithm; it adds no verification step of its own. The signer
        key <bcp14>MUST</bcp14> be pinned to a trust root that does not
        depend on the enforcement point being online or honest at
        verification time. As with the core, offline verification
        establishes authenticity at decision time, not current
        revocation status; a relying party with freshness requirements
        consults current key and log state online.</t>
      </section>
    </section>
    <section anchor="execution-receipt">
      <name>The Post-Execution Receipt</name>
      <t>The bound receipt above proves a decision was authorized <em>before</em>
      the action. To close the loop, once a permitted action actually executes,
      the enforcement point <bcp14>SHOULD</bcp14> emit a <em>post-execution
      receipt</em> (an execution attestation): offline-verifiable proof that the
      authorized action was carried out, and with what outcome.</t>
      <t>A post-execution receipt <bcp14>MUST</bcp14> commit to the authorization
      it discharges — it carries the binding key (<xref target="receipt-binding"/>)
      of the decision whose action it executed, so the authorization and its
      execution form a single verifiable chain. It records an outcome (for
      example <tt>executed</tt> or <tt>failed</tt>). It conveys no new authority:
      it attests that an authorized action ran, never that an action was approved.
      An enforcement point <bcp14>MUST NOT</bcp14> emit a post-execution receipt
      that references a decision which never reached a positive, consumed state
      (<xref target="positive-states"/>).</t>
      <t>The post-execution receipt is verifiable offline on the same terms as
      the bound receipt (<xref target="offline-verification"/>), and
      <bcp14>MAY</bcp14> be anchored for long-term, tamper-evident retention
      (an EP Commit seal; <tt>draft-schrock-ep-evidence-record</tt>). Together the
      bound receipt and the post-execution receipt give a relying party the whole
      account: a named human authorized this exact action, and it was carried out
      — both checkable without trusting the enforcement point.</t>
    </section>
    <section anchor="reject-before-mutation">
      <name>The Reject-Before-Mutation Invariant</name>
      <t>This is the central enforcement-point invariant. A rejecting
      decision — <tt>deny</tt>, or <tt>allow_with_signoff</tt> for which
      a positive-state approval does not yet exist —
      <bcp14>MUST</bcp14> take effect <em>before</em> any
      approval-bearing state mutation occurs. Equivalently: the
      enforcement point sits in front of the write, and no
      approval-bearing mutation is reachable except through a permitting
      decision.</t>
      <t>Requirements:</t>
      <ul>
        <li>An enforcement point <bcp14>MUST</bcp14> evaluate the
        decision and, for any non-permitting outcome, withhold or refuse
        the action <em>before</em> the approval-bearing mutation is
        applied. A design that mutates state and then evaluates — or that
        evaluates beside the write rather than ahead of it — does not
        conform.</li>
        <li>For <tt>allow_with_signoff</tt>, the approval-bearing
        mutation <bcp14>MUST NOT</bcp14> be applied until a verifiable
        positive-state receipt (<xref target="positive-states"/>) exists
        for the exact action. The enforcement point withholds; it does
        not optimistically apply and later reconcile.</li>
        <li>For <tt>deny</tt>, no approval-bearing mutation is permitted
        for the attempt under any subsequent approval; <tt>deny</tt> is
        terminal (<xref target="decision-vocabulary"/>).</li>
        <li>If the enforcement point cannot determine, at the moment of
        the write, that a permitting decision is in force for the exact
        action, it <bcp14>MUST</bcp14> fail closed
        (<xref target="fail-closed"/>) and withhold the mutation.</li>
      </ul>
      <t>The invariant is what makes an enforcement point an enforcement
      point rather than an after-the-fact recorder: the safe state is
      reached by refusing, and the refusal precedes the irreversible
      step. The strength with which the invariant holds against a party
      that controls the system of record is a function of deployment
      topology, stated honestly in <xref target="conformance"/>.</t>
    </section>
    <section anchor="enforcement-modes">
      <name>Enforcement Modes</name>
      <t>Enforcement mode is the enforcement point's posture for a
      decision and is orthogonal to the decision vocabulary. Three modes
      are defined.</t>
      <t><strong><tt>enforce</tt> (default).</strong> The decision is
      returned and the enforcement point <bcp14>MUST</bcp14> honor it:
      <tt>allow</tt> proceeds, <tt>allow_with_signoff</tt> withholds
      until a positive-state receipt exists, <tt>deny</tt> refuses. This
      is the only mode in which the reject-before-mutation invariant
      (<xref target="reject-before-mutation"/>) provides enforcement.</t>
      <t><strong><tt>warn</tt>.</strong> The decision is returned
      verbatim and is advisory: the caller <bcp14>MAY</bcp14> proceed
      against a non-permitting decision. An enforcement point in
      <tt>warn</tt> mode <bcp14>MUST</bcp14> report the true decision and
      <bcp14>MUST NOT</bcp14> represent the action as having been
      enforced.</t>
      <t><strong><tt>observe</tt>.</strong> For staged rollout and audit.
      A decision that would withhold or refuse is recorded but not
      enforced. The enforcement point <bcp14>MUST</bcp14> set the
      effective <tt>decision</tt> to the <tt>observe</tt> token, carry
      the decision that would have been enforced in
      <tt>observed_decision</tt>, and keep <tt>signoff_required</tt> at
      the value the enforced decision would have had
      (<xref target="decision-response"/>). An enforcement point in
      <tt>observe</tt> mode <bcp14>MUST NOT</bcp14> claim any enforcement
      and <bcp14>MUST NOT</bcp14> downgrade a recorded <tt>deny</tt> to
      <tt>allow</tt>; it downgrades only the <em>effect</em>, never the
      recorded decision.</t>
      <t>Modes change whether a decision is acted upon. They never change
      what the decision is: the substantive decision recorded for an
      action is always one of <tt>allow</tt>,
      <tt>allow_with_signoff</tt>, or <tt>deny</tt>.</t>
    </section>
    <section anchor="policy-deferral">
      <name>Policy Semantics Are Deferred, Not Defined</name>
      <t>This profile defines the enforcement point's contract. It does
      not define how authorization policy is written, stored, or
      evaluated. The enforcement point poses an action in context to a
      Policy Decision Point and consumes the decision; the policy
      language and decision semantics live entirely in the PDP and the
      decision interface it speaks.</t>
      <ul>
        <li>An enforcement point <bcp14>MUST NOT</bcp14> embed an
        authorization-policy language, and Decision Requests
        <bcp14>MUST NOT</bcp14> carry inline policy expressions
        (<xref target="decision-request"/>). The enforcement point
        identifies a policy by <tt>policy_id</tt> and consumes the PDP's
        decision and pinned <tt>policy_hash</tt>; it does not interpret
        the policy's rules.</li>
        <li>The decision interface between the enforcement point and the
        PDP <bcp14>MAY</bcp14> be an AuthZEN-style access-evaluation
        exchange (a subject/action/resource/context request to a PDP
        returning a decision) or any equivalent. This profile composes
        with such an interface and does not replace it.</li>
        <li>The PDP <bcp14>MAY</bcp14> be backed by any policy engine —
        for example Cedar or OPA/Rego. The choice of engine and the
        authoring of policy are explicitly out of scope.</li>
        <li>The mapping from a PDP decision to this profile's vocabulary
        is a deployment concern, but it <bcp14>MUST</bcp14> be total and
        fail-closed: any PDP outcome that is not an unambiguous permit
        <bcp14>MUST</bcp14> map to a non-permissive vocabulary value, and
        a PDP that is unreachable or returns an unrecognized result
        <bcp14>MUST</bcp14> resolve to fail-closed
        (<xref target="fail-closed"/>), never to <tt>allow</tt>.</li>
      </ul>
      <t>The division of labor is deliberate: the PDP decides
      <em>whether</em> policy permits the action; the enforcement point
      decides <em>that the action does not proceed unless</em> the
      decision permits it, binds the decision to verifiable evidence, and
      orders the refusal ahead of the write. Only the latter is profiled
      here.</t>
    </section>
    <section anchor="conformance">
      <name>Conformance Requirements and Classes</name>
      <t>An enforcement point is conformant with this profile when it
      meets all of the following requirements.</t>
      <section anchor="fail-closed">
        <name>Fail-Closed</name>
        <t>An enforcement point <bcp14>MUST</bcp14> fail closed. Any of
        the following <bcp14>MUST</bcp14> resolve to a non-permissive
        outcome — withholding the action — and <bcp14>MUST NOT</bcp14>
        resolve to <tt>allow</tt>:</t>
        <ul>
          <li>a PDP that is unreachable, errors, or times out (transport
          failure to a decision source is not a policy decision and
          <bcp14>MUST NOT</bcp14> be interpreted as permission);</li>
          <li>a decision value the enforcement point does not recognize
          (<xref target="decision-vocabulary"/>);</li>
          <li>an action-hash mismatch between request and response, or
          between either and the recomputed canonical action;</li>
          <li>a required field absent or malformed in a Decision Request
          or Decision Response;</li>
          <li>a bound receipt that fails offline verification, or whose
          state is not a positive state where a positive state is
          required to proceed;</li>
          <li>any state the enforcement point cannot map to a defined
          outcome.</li>
        </ul>
        <t>Fail-closed is the default for uncertainty of every kind. An
        enforcement point <bcp14>SHOULD</bcp14> distinguish, in its
        <tt>reasons</tt>, a fail-closed withholding from a policy
        <tt>deny</tt>, but both withhold the action.</t>
      </section>
      <section anchor="consume-once">
        <name>Honoring One-Time Consumption</name>
        <t>The shared core makes an authorization consumable at most
        once, globally. An enforcement point <bcp14>MUST</bcp14> honor
        this: it <bcp14>MUST NOT</bcp14> permit an approval-bearing
        mutation against a receipt whose authorization has already been
        consumed, and it <bcp14>MUST</bcp14> reject a second presentation
        of a once-consumed authorization as a replay. An enforcement
        point <bcp14>MUST NOT</bcp14> re-use a single positive-state
        receipt to authorize more than one execution of the action, and
        <bcp14>MUST NOT</bcp14> treat a receipt in a consumed state as
        re-authorizing. Consume-once enforcement
        <bcp14>SHOULD</bcp14> be backed by a durable, atomic record so
        that concurrent presentations cannot both succeed; an in-memory
        replay guard is insufficient for a production enforcement
        point.</t>
      </section>
      <section anchor="receipt-emission">
        <name>Receipt Emission</name>
        <t>An enforcement point <bcp14>MUST</bcp14> emit a bound
        authorization receipt (<xref target="receipt-binding"/>) for
        every decision that reaches an approval-bearing state, and
        <bcp14>SHOULD</bcp14> record evidence for <tt>deny</tt> and
        fail-closed outcomes as well, so that the absence of an expected
        execution is itself attributable. Emission
        <bcp14>MUST</bcp14> respect the honesty gate
        (<xref target="positive-states"/>): a signed, offline-verifiable
        artifact is emitted only for a decision that genuinely reached a
        positive state; otherwise the enforcement point emits the
        unsigned evidence packet. An enforcement point
        <bcp14>MUST NOT</bcp14> suppress receipt emission for a permitted
        high-risk action: the receipt is the evidence the profile
        exists to produce.</t>
      </section>
      <section anchor="vocabulary-conformance">
        <name>Vocabulary and Schema Conformance</name>
        <t>An enforcement point <bcp14>MUST</bcp14> emit only the
        registered decision values (<xref target="decision-vocabulary"/>),
        <bcp14>MUST</bcp14> populate the required fields of the Decision
        Request and Decision Response
        (<xref target="decision-request"/>,
        <xref target="decision-response"/>), and <bcp14>MUST</bcp14>
        bind every decision to the exact action by action hash. A
        consumer <bcp14>MUST</bcp14> reject a response whose
        <tt>action_hash</tt> does not match the request it answers.</t>
      </section>
      <section anchor="classes">
        <name>Enforcement Classes (declared, not implied)</name>
        <t>Whether the gate can be bypassed depends on where it sits.
        Honesty about deployment topology is a conformance requirement,
        mirroring the shared core. An enforcement point
        <bcp14>MUST</bcp14> declare its class in the Decision Response
        (<tt>enforcement_class</tt>) and in the bound receipt, and
        claims <bcp14>MUST NOT</bcp14> state a stronger class than
        deployed.</t>
        <t><strong>EP-Verified Execution (STRONG).</strong> The system of
        record itself evaluates the decision and refuses to perform the
        approval-bearing mutation without a permitting,
        verifiably-bound decision. The gate cannot be bypassed by a
        party that does not control the system of record.</t>
        <t><strong>EP-Gated Middleware (STANDARD).</strong> An
        interception layer between the agent and the executing credential
        enforces the gate. It protects strongly against agent error and
        prompt injection; an operator with code control over the
        middleware can bypass it. Receipts remain valid evidence of what
        was decided.</t>
        <t><strong>EP-Evidence Only (BASIC).</strong> Actions execute
        independently; the enforcement point produces decisions and
        receipts for audit. No enforcement claim is made; the
        reject-before-mutation invariant is not provided.</t>
      </section>
    </section>
    <section anchor="related-work">
      <name>Relationship to Other Work</name>
      <t><strong>The shared verifier core</strong>
      (<xref target="I-D.schrock-ep-authorization-receipts"/>) defines
      the receipt, the canonical action and action hash, the human
      signoff, separation of duties, one-time consumption, and the
      offline verification algorithm. This profile composes <em>on</em>
      that core by reference: it speaks a vocabulary, defines a
      request/response, and binds decisions to receipts the core
      verifies. It does not redefine the core, and it does not claim to
      own or to be the trust core; the core is the set of shared
      properties this and other profiles verify against, not an artifact
      this profile owns.</t>
      <t><strong>AuthZEN</strong> (<xref target="AUTHZEN"/>) standardizes
      the access-evaluation interface between a PEP and a PDP — the
      request/response by which an enforcement point asks "is this
      permitted?" This profile is a PEP-side profile: it
      <bcp14>MAY</bcp14> use an AuthZEN exchange to obtain the policy
      decision it consumes, and it adds the evidence-binding,
      reject-before-mutation, and fail-closed requirements that a bare
      decision interface does not specify. It does not replace
      AuthZEN.</t>
      <t><strong>OPA/Rego</strong> (<xref target="OPA"/>) and
      <strong>Cedar</strong> (<xref target="CEDAR"/>) are
      policy-language engines that can back the PDP. This profile
      consumes a decision from such an engine; it defines no policy
      language of its own (<xref target="policy-deferral"/>).</t>
      <t><strong>Security Event Token (SET)</strong>
      (<xref target="RFC8417"/>) and the Shared Signals Framework with
      CAEP define an envelope and transport for security events and
      session/posture signals. An enforcement point
      <bcp14>MAY</bcp14> consume such signals as presented evidence
      (<xref target="decision-request"/>); they are inputs to a decision
      and never the decision itself
      (<xref target="advisory-not-a-decision"/>).</t>
      <t><strong>RATS</strong> (<xref target="RFC9334"/>) / <strong>EAT</strong> (<xref target="RFC9711"/>) produce
      attestation results about an entity or device's state. An
      enforcement point <bcp14>MAY</bcp14> consume such results as
      posture evidence; this profile does not define attestation.</t>
    </section>
    <section>
      <name>Security Considerations</name>
      <section anchor="advisory-not-a-decision">
        <name>An Advisory Is Never the Sole Gate</name>
        <t>Presented evidence — risk flags, advisories, posture signals
        — informs a decision; it is not a decision. An enforcement point
        <bcp14>MUST NOT</bcp14> permit or refuse an action solely because
        an advisory said so, and <bcp14>MUST NOT</bcp14> let an advisory
        substitute for the PDP decision or for a required signoff. The
        residual risk if this is violated is twofold: a permissive
        advisory could wave through an action policy would gate, and a
        spoofed or stale advisory could become an unaccountable gate.
        Evidence enters only through the <tt>evidence</tt> block of a
        Decision Request and is weighed by policy; the gate remains the
        decision plus, where required, the human signoff.</t>
      </section>
      <section anchor="fail-closed-security">
        <name>Fail-Open Is the Failure Mode That Matters</name>
        <t>The most dangerous defect in an enforcement point is treating
        uncertainty as permission. A PDP timeout, an unparsed response,
        an unknown decision value, or an unverifiable receipt
        <bcp14>MUST</bcp14> withhold the action
        (<xref target="fail-closed"/>). Designs that "allow on error to
        preserve availability" convert every transient fault into an
        authorization bypass and do not conform. Where availability
        genuinely outranks safety for a class of actions, that class
        does not belong behind this profile.</t>
      </section>
      <section anchor="ordering-security">
        <name>Mutation Ordering and Time-of-Check/Time-of-Use</name>
        <t>The reject-before-mutation invariant
        (<xref target="reject-before-mutation"/>) is only as strong as
        the atomicity between the check and the write. An enforcement
        point <bcp14>MUST</bcp14> ensure that the permitting decision in
        force at the moment of the write is the decision for the exact
        action being written — same action hash, unexpired, not already
        consumed. A gap between checking a decision and applying the
        mutation is a time-of-check/time-of-use vulnerability: an
        attacker who can alter the action between check and write defeats
        the binding. Consume-once (<xref target="consume-once"/>) and
        action-hash binding together close this only if the check-and-
        consume is atomic with respect to the mutation.</t>
      </section>
      <section anchor="observe-misuse">
        <name>Observe-Mode Misrepresentation</name>
        <t>Because <tt>observe</tt> mode records a decision without
        enforcing it, an operator could present an <tt>observe</tt>-mode
        deployment as if it enforced. An enforcement point in
        <tt>observe</tt> or <tt>warn</tt> mode <bcp14>MUST NOT</bcp14>
        claim enforcement and <bcp14>MUST</bcp14> mark the effective
        decision honestly (<xref target="enforcement-modes"/>). A relying
        party <bcp14>MUST NOT</bcp14> treat an <tt>observe</tt>-mode
        record as evidence that an action was gated; it is evidence only
        of what would have been decided.</t>
      </section>
      <section anchor="policy-trust">
        <name>The Enforcement Point Trusts the PDP's Decision, Not Its Inputs</name>
        <t>This profile binds and orders a decision; it does not vouch
        for the policy that produced it. A compromised or misconfigured
        PDP can return <tt>allow</tt> for an action that should be gated,
        and the enforcement point will faithfully permit it. The
        enforcement point's guarantees are conditional on the PDP being
        sound: it guarantees that <em>no action proceeds except under a
        permitting decision, bound to verifiable evidence, with refusal
        ordered ahead of the write</em> — not that the decision was
        correct. Soundness of policy is the PDP's responsibility
        (<xref target="policy-deferral"/>) and is out of scope.</t>
      </section>
      <section anchor="overclaim">
        <name>What This Profile Does and Does Not Establish</name>
        <t>A bound receipt establishes that a specific decision was made
        for a specific action and, for <tt>allow_with_signoff</tt>, that
        a named human approved it — verifiable offline. It does not, by
        itself, establish that the deployment was unbypassable; that
        depends on the declared enforcement class
        (<xref target="conformance"/>). It does not establish that the
        policy was correct (<xref target="policy-trust"/>). And it does
        not establish anything about an AI model's behavior. Where the
        shared core's safety properties are machine-checked, those proofs
        cover the core's authorization state machine, not this profile's
        deployment topology or the PDP; implementations
        <bcp14>MUST NOT</bcp14> represent core proofs as covering the
        enforcement point's deployment. This profile is experimental and
        has not been independently audited; conformance claims should be
        stated as such.</t>
      </section>
    </section>
    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions. A future version may request
      a registry for the decision vocabulary
      (<xref target="decision-vocabulary"/>) so that additional decision
      values, if ever needed, are added under a stable,
      backward-compatible policy in which an unrecognized value
      <bcp14>MUST</bcp14> fail closed.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>Normative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
      <reference anchor="I-D.schrock-ep-authorization-receipts" target="https://datatracker.ietf.org/doc/draft-schrock-ep-authorization-receipts/">
        <front>
          <title>Authorization Receipts for High-Risk Agent Actions</title>
          <author fullname="Iman Schrock">
            <organization>EMILIA Protocol, Inc.</organization>
          </author>
          <date year="2026" month="June"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-schrock-ep-authorization-receipts"/>
      </reference>
    </references>
    <references>
      <name>Informative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8417.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9334.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9711.xml"/>
      <reference anchor="AUTHZEN" target="https://openid.net/specs/authorization-api-1_0-01.html">
        <front>
          <title>Authorization API 1.0</title>
          <author><organization>OpenID Foundation (AuthZEN WG)</organization></author>
          <date year="2024" month="December"/>
        </front>
      </reference>
      <reference anchor="OPA" target="https://www.openpolicyagent.org/docs/latest/policy-language/">
        <front>
          <title>Open Policy Agent: The Rego Policy Language</title>
          <author><organization>Open Policy Agent project</organization></author>
          <date year="2024"/>
        </front>
      </reference>
      <reference anchor="CEDAR" target="https://www.cedarpolicy.com/">
        <front>
          <title>Cedar Policy Language</title>
          <author><organization>Cedar project</organization></author>
          <date year="2024"/>
        </front>
      </reference>
    </references>
  </back>
</rfc>
