<?xml version="1.0" encoding="utf-8"?>
<rfc version="3" docName="draft-janbjer-div-00" category="info" ipr="trust200902" submissionType="IETF" xml:lang="en" tocInclude="true" tocDepth="3" sortRefs="true" symRefs="true">
  <front>
    <title abbrev="DIV">Deterministic Intent Verification (DIV) Protocol Specification</title>
    <seriesInfo name="Internet-Draft" value="draft-janbjer-div-00" />
    <author fullname="Christian Janbjer" initials="C." surname="Janbjer">
      <organization>Janbjer Technologies AB</organization>
      <address>
        <email>hello@intyga.com</email>
      </address>
    </author>
    <date year="2026" month="October" day="4" />
    <keyword>authorization</keyword>
    <keyword>intent</keyword>
    <keyword>non-repudiation</keyword>
    <keyword>WebAuthn</keyword>
    <keyword>AI agents</keyword>
    <abstract>
      <t>This document specifies Deterministic Intent Verification (DIV), a transport-independent format for signed action approvals and their offline verification. A relying party reconstructs the signed payload from its expected execution parameters, verifies witnesses against locally selected trust anchors, and checks the signed approval requirement against any locally configured approval policy. The specification covers ordinary approvals, offline approvals, delegation, agent authority, and platform hash-only intents. Cryptographic verification is stateless; enforcing single-use execution requires stateful nonce redemption. A valid signature establishes approval of the signed bytes under the selected trust policy, not execution of the action or the approver's understanding of it.</t>
    </abstract>
  </front>
  <middle>
    <section anchor="introduction" numbered="false">
      <name>Introduction</name>
      <t>This document specifies DIV; the companion protocol is described in <xref target="DEWP" />. This is an individual Internet-Draft and does not imply IETF endorsement.</t>
      <t>The numbered sections retain the numbering of the source specification, including lettered sections, so existing technical cross-references remain usable.</t>
      <t>The companion schemas and conformance vectors are pinned by <xref target="Artifacts" />. Paths beginning with docs/schemas/dewp/ correspond to schemas/dewp/ in that snapshot; packages/mcp-schemas/vectors/ corresponds to vectors/. Other repository paths are informative implementation locations. Schema identifiers are identifiers, not permission to substitute an unversioned schema for the pinned snapshot.</t>
      <t>Long source-code lines use the reversible folding convention of <xref target="RFC8792" />. Unfold a marked block before parsing it or computing any cryptographic digest.</t>
      <t>Related work includes <xref target="I-D.williams-intent-token" />, which describes a pre-execution authorization token and an audit structure.</t>
      <t>Related identity and authenticator specifications include <xref target="DID-CORE" />, <xref target="SPIFFE" />, and <xref target="FIDO2" />. MCP <xref target="MCP" /> is one possible integration transport.</t>
    </section>
    <section numbered="false">
      <name>Requirements Language</name>
      <t>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 <xref target="RFC2119" />
        <xref target="RFC8174" /> when, and only when, they appear in all capitals, as shown here.</t>
    </section>
    <section anchor="s-1" numbered="false">
      <name>1. Scope &amp; Explicit Non-Goals</name>
      <t>To maintain a minimal trust surface, DIV narrowly defines only the intent object, its proof envelope, its deterministic serialization, and the local verification algorithm.</t>
      <section anchor="s-1-1" numbered="false">
        <name>1.1 Scope</name>
        <t>DIV specifies exclusively:</t>
        <ol type="1">
          <li>
            <t>The canonical data schema for an explicit intent payload and its proof envelope.</t>
          </li>
          <li>
            <t>The deterministic serialization rules adhering to JSON Canonicalization Scheme (JCS) <xref target="RFC8785" />.</t>
          </li>
          <li>
            <t>The local, in-process algorithm executed by a Relying Party to verify an intent proof against runtime parameters.</t>
          </li>
        </ol>
      </section>
      <section anchor="s-1-2" numbered="false">
        <name>1.2 Out-of-Scope (Explicit Non-Goals)</name>
        <t>DIV explicitly does <strong>NOT</strong> define:</t>
        <ul>
          <li>
            <t>
              <strong>Authentication or Identity Management:</strong> DIV assumes identity attestation (e.g., OIDC, SPIFFE <xref target="SPIFFE" />, DIDs) is established independently.</t>
          </li>
          <li>
            <t>
              <strong>Key Distribution or PKI:</strong> Public key discovery, trust anchors, and key rotation mechanisms are deferred to external key-management infrastructure.</t>
          </li>
          <li>
            <t>
              <strong>Transport Protocols:</strong> DIV envelopes MAY be carried over HTTP, gRPC, WebSockets, or file-based IPC.</t>
          </li>
          <li>
            <t>
              <strong>Approval Workflow Orchestration:</strong> Step-up prompting, notification routing, and quorum scheduling are operational concerns outside this specification.</t>
          </li>
        </ul>
      </section>
    </section>
    <section anchor="s-2" numbered="false">
      <name>2. Terminology &amp; Core Definitions</name>
      <ul>
        <li>
          <t>
            <strong>Irreversible Action (IA):</strong> Any state-mutating operation whose execution cannot be completely and atomically rolled back without side effects.</t>
        </li>
        <li>
          <t>
            <strong>Relying Party (RP):</strong> The executing system, application, service, or tool server that receives an execution request and validates the intent proof against its own internal parameter state before invoking the target operation.</t>
        </li>
        <li>
          <t>
            <strong>Issuing Service:</strong> The service that conducts the approval ceremony: it freezes the Intent Payload (including its <tt>requirement</tt>) at issuance, presents it to Approvers, and records the resulting witnesses. Referred to interchangeably in this document as the <em>approval service</em> (§5a) and the <em>issuing deployment</em> (§5b); the client component that drives a WebAuthn <xref target="WebAuthn" /> ceremony on its behalf is part of this role. It is distinct from the Relying Party, and the issuer-side MUSTs of §4.3.3 bind it — not Core Profile verifiers.</t>
        </li>
        <li>
          <t>
            <strong>Approver:</strong> A human authority holding a private signing key who cryptographically attests to an explicit execution payload.</t>
        </li>
        <li>
          <t>
            <strong>Target:</strong> A unique, machine-readable string identifying the specific Relying Party instance or execution environment expected to perform the action.</t>
        </li>
        <li>
          <t>
            <strong>Intent Payload:</strong> The canonical structured object containing the exact execution parameters and contextual metadata subject to signature verification.</t>
        </li>
        <li>
          <t>
            <strong>Proof Envelope:</strong> The top-level cryptographic container carrying the Intent Payload and corresponding signature metadata.</t>
        </li>
        <li>
          <t>
            <strong>Intent Proof:</strong> A successfully verified Proof Envelope satisfying all DIV validation requirements.</t>
        </li>
      </ul>
    </section>
    <section anchor="s-3" numbered="false">
      <name>3. Protocol Invariants</name>
      <t>A compliant DIV implementation MUST satisfy the following structural invariants:</t>
      <ol type="1">
        <li>
          <t>
            <strong>Parameter-Bound Binding</strong>
          </t>
          <t>The signature MUST be computed over the complete Intent Payload containing the exact execution parameters.</t>
        </li>
        <li>
          <t>
            <strong>Local Payload Reconstruction</strong>
          </t>
          <t>The Relying Party MUST NOT trust the payload supplied within the Proof Envelope.</t>
          <t>The Relying Party MUST reconstruct the expected Intent Payload before signature verification, taking the <strong>security-binding fields</strong> — <tt>target</tt>, <tt>actionType</tt>, <tt>params</tt> — exclusively from its own runtime execution parameters, and asserting the <tt>nonce</tt> of the challenge it is redeeming itself. The remaining, <strong>issuance-frozen</strong> fields (<tt>display</tt>, <tt>requester</tt>, <tt>requirement</tt>, <tt>evidence</tt>, <tt>expiresAt</tt>, and any type-specific fields) MAY be taken from the envelope: they are inputs to reconstruction, not trusted facts, because the signature covers them — a forged value changes the reconstructed bytes and fails verification (§4.4.1).</t>
          <t>The signature protects issuance-frozen fields against <strong>third parties only</strong>. They are authored by whoever composed the bytes — the Issuing Service, or any Approver composing a payload of their own — and each signer attests to them. In particular the <tt>requirement</tt> bounds only what the signers themselves stated: it cannot, on its own, stop the Approvers it constrains from stating a weaker one. A Relying Party that holds its own approval policy MUST therefore compare the signed <tt>requirement</tt> against it (§5 step 3d).</t>
        </li>
        <li>
          <t>
            <strong>Offline Relying Party Verification</strong>
          </t>
          <t>The Relying Party MUST verify the signature locally using a trusted Approver public key resolved according to deployment-specific key-management policy.</t>
          <t>Verification MUST NOT require outbound calls to external brokers or verification services.</t>
        </li>
        <li>
          <t>
            <strong>Fail-Closed Execution</strong>
          </t>
          <t>Any Irreversible Action encountering missing, malformed, unverified, expired, or replayed Intent Proofs MUST abort execution before invoking the underlying system operation.</t>
        </li>
        <li>
          <t>
            <strong>Target Isolation</strong>
          </t>
          <t>The Intent Payload MUST explicitly bind the intended Target identifier to prevent cross-service replay attacks.</t>
        </li>
      </ol>
    </section>
    <section anchor="s-4" numbered="false">
      <name>4. Canonical Payload and Proof Envelope Specification</name>
      <section anchor="s-4-1" numbered="false">
        <name>4.1 Serialization Format</name>
        <t>Intent Payloads MUST be serialized into a deterministic byte sequence using JSON Canonicalization Scheme (JCS) <xref target="RFC8785" />.</t>
        <t>Implementations MUST NOT rely on arbitrary JSON serialization behavior.</t>
        <t>Canonicalization accepts JSON data only. Runtime arrays with missing elements (for example a sparse JavaScript array) MUST be refused, rather than collapsed into an empty array or converted to null elements. A present JSON null element is preserved: <tt>[]</tt> and <tt>[null]</tt> have different signed bytes.</t>
        <t>Strings MUST be valid Unicode, as RFC 8785 inherits from I-JSON <xref target="RFC7493" /> §2.1. A string — a value or a member name — containing an unpaired UTF-16 surrogate (a high surrogate not followed by a low one, or a low surrogate not preceded by a high one) MUST be refused by producers and verifiers alike. It MUST NOT be serialized as a <tt>\udXXX</tt> escape, replaced with U+FFFD, or passed through: each of those was the behaviour of some implementation, and a signature over such a string then verified in some languages and not in others. A verifier that parses signed JSON text MUST likewise refuse a <tt>\u</tt> escape naming an unpaired surrogate, and text that is not valid UTF-8, rather than decode a replacement character. Valid text, including characters outside the Basic Multilingual Plane, is unaffected: this rule changes no canonical bytes for any valid input.</t>
        <t>The cryptographic signature MUST cover only the canonical serialized Intent Payload.</t>
        <t>Proof Envelope metadata, transport metadata, and external execution context MUST NOT be included in signature computation.</t>
        <section anchor="s-4-1-1" numbered="false">
          <name>4.1.1 Portable Number Range</name>
          <t>RFC 8785 defines a serialization for every finite double, but independent implementations do not agree in practice: each language's number formatter switches to exponent notation at its own threshold, and <tt>-0</tt> has no single spelling. Because the signature covers the serialized bytes, two parties that format one number differently produce different bytes for the same payload — so the signature fails and the verifier reports what looks like tampering.</t>
          <t>A number appearing anywhere in a signed payload (including inside <tt>params</tt>) is <strong>portable</strong> when it is finite, is not <tt>-0</tt>, and satisfies one of:</t>
          <ul>
            <li>
              <t>it is an integer with <tt>|x| &lt; 1e16</tt>; or</t>
            </li>
            <li>
              <t>it is <tt>0</tt>; or</t>
            </li>
            <li>
              <t>it is a non-integer with <tt>1e-4 &lt;= |x| &lt; 1e16</tt>.</t>
            </li>
          </ul>
          <t>
            <strong>Producers MUST refuse to sign a payload containing a non-portable number</strong>, rather than emitting bytes some verifiers cannot reproduce. Carry such a value as a decimal string, as an integer in smaller units (e.g. minor currency units), or not at all. Verifiers MAY refuse such a payload for the same reason.</t>
          <t>This is stricter than RFC 8785 alone, deliberately: the range is the intersection on which every conformant implementation agrees, and a signature is worth nothing outside it. The reference implementations enforce it at signing time in all five languages, and the conformance vectors (§7a) pin it. DEWP §4.3.1 applies the identical range to committed ledger metadata.</t>
          <t>
            <strong>Integers above 2^53.</strong> The integer clause admits values in <tt>(2^53, 10^16)</tt> that an IEEE-754 double cannot represent exactly. A runtime whose JSON parser preserves big integers (Java, Rust, Python) canonicalizes such a value to its exact digits, while a double-based parser (ECMAScript, Go) rounds it at parse time — the same document then produces different canonical bytes in different languages, and the mismatch reads as tampering. A double-based producer cannot emit such a value in the first place, and the reference producer refuses non-portable content at ingestion, so the case is reachable only from hand-authored or foreign documents. Producers on arbitrary-precision runtimes SHOULD keep integers within <tt>±2^53</tt> and carry larger values as decimal strings; a future revision may tighten the integer bound to <tt>2^53</tt> outright.</t>
          <section anchor="s-4-1-1-1" numbered="false">
            <name>4.1.1.1 Shortest Round-Trip Formatting</name>
            <t>Restricting the range is necessary but not sufficient. <strong>Inside</strong> the portable range an implementation MUST serialize a number as the <strong>shortest decimal string that round-trips to the same IEEE-754 double</strong> — the ECMAScript <tt>Number::toString</tt> behaviour RFC 8785 §3.2.2.3 mandates. A formatter that emits more digits than necessary produces different bytes for the same value, which fails the signature exactly as an out-of-range value does.</t>
            <t>This is called out explicitly because a language's built-in formatter is not automatically conformant, and the failure is silent and version-dependent:</t>
            <ul>
              <li>
                <t>
                  <strong>Java.</strong>
                  <tt>Double.toString</tt> does <strong>NOT</strong> produce the shortest round-trip form before JDK 19 (JDK-4511638); it emits extra digits for some values. A conformant Java implementation MUST therefore implement shortest-round-trip formatting itself rather than delegating to <tt>Double.toString</tt> — otherwise the same receipt canonicalizes differently on JDK 17 and JDK 21, and interoperates with neither. The reference implementation does this in <tt>packages/verify-java</tt> (<tt>Canonical.formatShortestDouble</tt>).</t>
              </li>
              <li>
                <t>
                  <strong>Integers.</strong> A value that is mathematically integral MUST serialize with no decimal point and no exponent (<tt>1</tt>, not <tt>1.0</tt> or <tt>1E0</tt>), for every integral double in the portable range.</t>
              </li>
            </ul>
            <t>An implementation whose standard library already emits the shortest round-trip form (ECMAScript, Go <tt>strconv</tt> with <tt>'g'</tt>/-1, Rust <tt>ryu</tt>, Python <tt>repr</tt>) satisfies this clause without extra work; one whose library does not MUST supply it. The <tt>floats-portable</tt> conformance vectors (§7a) pin the expected strings.</t>
          </section>
        </section>
      </section>
      <section anchor="s-4-2" numbered="false">
        <name>4.2 Intent Payload Schema</name>
        <sourcecode type="json">{
  "v": 1,
  "type": "div-intent-verification",
  "target": "prod-db-cluster-01",
  "actionType": "db:dropTable",
  "display": "Delete production users table",
  "params": {
    "environment": "production",
    "table": "users"
  },
  "evidence": null,
  "requester": {
    "did": "did:example:service:deploy-pipeline",
    "attestation": null
  },
  "requirement": {
    "requiredApprovals": 2,
    "requireHardwareKey": true,
    "allowedAaguids": ["adce0002-35bc-c60a-2b7b-40b2ede212b7"],
    "requesterCannotApprove": true,
    "signerClass": "human"
  },
  "nonce": "c_8f91a2b4c6e8",
  "expiresAt": "2026-07-24T12:05:00Z"
}</sourcecode>
      </section>
      <section anchor="s-4-3" numbered="false">
        <name>4.3 Intent Payload Field Definitions</name>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Type</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>v</td>
              <td>uint8</td>
              <td>REQUIRED</td>
              <td>DIV protocol version. MUST equal 1.</td>
            </tr>
            <tr>
              <td>type</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>MUST equal div-intent-verification.</td>
            </tr>
            <tr>
              <td>target</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Intended execution target identifier.</td>
            </tr>
            <tr>
              <td>actionType</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Machine-readable operation identifier.</td>
            </tr>
            <tr>
              <td>display</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Human-readable approval summary.</td>
            </tr>
            <tr>
              <td>params</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>Exact execution parameters.</td>
            </tr>
            <tr>
              <td>evidence</td>
              <td>null</td>
              <td>REQUIRED</td>
              <td>Reserved for external facts upon which authorization may be conditioned (§4.3.4). MUST be present, and MUST be <tt>null</tt> in this version.</td>
            </tr>
            <tr>
              <td>requester</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>Request context metadata (§4.3.1).</td>
            </tr>
            <tr>
              <td>requirement</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>Approval policy in force at issuance (§4.3.2).</td>
            </tr>
            <tr>
              <td>nonce</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Replay prevention identifier.</td>
            </tr>
            <tr>
              <td>expiresAt</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>RFC3339 <xref target="RFC3339" /> UTC expiration timestamp.</td>
            </tr>
          </tbody>
        </table>
        <t>For an <tt>AI_AGENT</tt> requester, the unpublished v1 format uses the agent extension in §4.3.6. <tt>exp</tt> replaces <tt>expiresAt</tt>; <tt>action</tt>, <tt>agent</tt>, <tt>session</tt> and <tt>nbf</tt> are REQUIRED. The ordinary human/service payload above retains <tt>expiresAt</tt>. A verifier MUST reject an agent payload unless its RP independently supplies the agent context expected for the action it is about to execute.</t>
        <section anchor="s-4-3-1" numbered="false">
          <name>4.3.1 Requester Object</name>
          <t>The <tt>requester</tt> object binds who requested the action:</t>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Type</th>
                <th>Requirement</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>did</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Decentralized identifier of the requesting principal.</td>
              </tr>
              <tr>
                <td>attestation</td>
                <td>object | null</td>
                <td>REQUIRED</td>
                <td>Third-party workload attestation, or the literal <tt>null</tt> when the requester is unattested. The <tt>null</tt> is signed and load-bearing: it distinguishes an attested workload from a bare credential holder.</td>
              </tr>
            </tbody>
          </table>
          <t>When present, <tt>attestation</tt> MUST contain exactly:</t>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Type</th>
                <th>Requirement</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>method</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Attestation method (e.g. <tt>oidc</tt>, <tt>spiffe</tt>).</td>
              </tr>
              <tr>
                <td>issuer</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Trust root that vouched for the workload.</td>
              </tr>
              <tr>
                <td>subject</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Attested workload identity.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="s-4-3-2" numbered="false">
          <name>4.3.2 Requirement Object</name>
          <t>The <tt>requirement</tt> object binds the approval policy that was in force when the challenge was issued. It MUST be frozen at issuance and MUST NOT be recomputed at verification time.</t>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Type</th>
                <th>Requirement</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>requiredApprovals</td>
                <td>uint</td>
                <td>REQUIRED</td>
                <td>Quorum size. The number of distinct approver <strong>identities</strong> that must each contribute a valid witness signature. MUST be an integer ≥ 1, and a verifier MUST reject a payload whose value is absent, non-integral or below 1: §5-step-7 rejects unless the counted identities are <em>at least</em>
                  <tt>requiredApprovals</tt>, so a value of 0 is satisfied vacuously and would admit an envelope carrying no valid witness signature at all. Counting signatures rather than identities is a conformance error — see §4.4.2 and §5-step-7.</td>
              </tr>
              <tr>
                <td>requireHardwareKey</td>
                <td>boolean</td>
                <td>REQUIRED</td>
                <td>Whether the policy demanded an authenticator with verified manufacturer attestation (the reference gateway checks a registration-verified <tt>packed</tt>/<tt>tpm</tt> attestation chain against an independently provisioned hardware-trust root — see its operational docs), not merely a device-bound / non-synced credential: <tt>singleDevice</tt>/<tt>backedUp=false</tt> alone is a backup-flag classification, not evidence of hardware.</td>
              </tr>
              <tr>
                <td>allowedAaguids</td>
                <td>array of string</td>
                <td>REQUIRED</td>
                <td>Authenticator models the policy admitted, as AAGUIDs. MUST be sorted ascending; the empty array means unrestricted.</td>
              </tr>
              <tr>
                <td>requesterCannotApprove</td>
                <td>boolean</td>
                <td>REQUIRED</td>
                <td>Whether four-eyes / separation of duties was demanded, i.e. the approver MUST NOT be the requester.</td>
              </tr>
              <tr>
                <td>signerClass</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>The class of signer the policy requires. <tt>"human"</tt> is the only value this version defines. Verifiers MUST reject a payload whose <tt>signerClass</tt> is absent or is a value they do not recognize (§5-step-3a).</td>
              </tr>
            </tbody>
          </table>
          <t>
            <strong>Signer-class registry.</strong> This version defines exactly one signer class:</t>
          <table>
            <thead>
              <tr>
                <th>Value</th>
                <th>Meaning</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>
                  <tt>human</tt>
                </td>
                <td>Every witness signature counted toward <tt>requiredApprovals</tt> must come from a human identity. The issuing service enforces this at signing time; §5-step-3a defines what a verifier can and cannot re-check.</td>
              </tr>
            </tbody>
          </table>
          <t>The field is a string rather than a boolean so that a future class (for example, an agent signing under a sealed delegation of authority) is a new <strong>value</strong> — one that deployed verifiers refuse until they are explicitly taught its verification semantics — rather than a change to the payload shape. Rejecting unknown values is therefore not defensive pedantry; it is the mechanism that keeps "this receipt is human-approved" a checkable claim as signer classes multiply.</t>
          <t>
            <tt>signerClass</tt> deliberately names the <strong>required class</strong>, not any actual signer: the payload is frozen at issuance, before any witness exists, and an M-of-N quorum's witnesses need not be homogeneous in any future class scheme. Per-witness facts live in the Proof Envelope's witness entries, never in the signed intent.</t>
          <t>
            <strong>Future signer classes (non-normative).</strong> The anticipated second class is an agent approving within authority a human granted it — call it <tt>delegated-agent</tt>. A future version that defines it MUST specify, before any verifier accepts the value:</t>
          <ol type="1">
            <li>
              <t>
                <strong>A delegation-of-authority artifact</strong>: a human-signed statement binding the agent's signing key to the granting human's identity, with an action scope, parameter bounds, and an expiry — the shape §5a.5's Delegation already has, with the delegate being an agent key instead of a human operator. A delegation that merely names an agent DID without binding its key inherits the §4.4.6 identity-association problem.</t>
            </li>
            <li>
              <t>
                <strong>Two-signature verification</strong>: the envelope carries the agent's witness signature over the Intent Payload AND the delegation artifact (or a resolvable reference to it); the verifier checks both, so "the agent approved" is never separable from "a human authorized this agent for exactly this scope". The accountable-human chain must survive offline verification with no issuer secret, exactly as human approvals do.</t>
            </li>
            <li>
              <t>
                <strong>Revocation semantics</strong>: what an offline verifier may assume about a delegation's validity window, mirroring §5a.6's treatment.</t>
            </li>
          </ol>
          <t>Under this scheme the witness ledger records the agent as the signer and the delegation as the authority chain — the human's accountability is cryptographic, not annotated. Deployed verifiers built against this version already refuse <tt>delegated-agent</tt> payloads by the registry rule, which is precisely the intended migration: nothing verifies as agent-approved until a verifier is upgraded to check the delegation chain. The scope-declaration half of that artifact is the Agent Authority (§5b); the key-binding half is what this future class adds.</t>
          <t>
            <tt>allowedAaguids</tt> MUST be sorted because the <strong>set</strong> is the policy: an unordered list would make two identical policies produce different signed bytes depending on the order the rule happened to enumerate them in, and the canonical serialization would no longer be a function of the policy alone.</t>
          <t>Without <tt>requirement</tt> in the signed bytes, a receipt from a 3-of-3 hardware-pinned challenge is byte-for-byte identical to a 1-of-1 one. A Relying Party "verifying offline" would then still have to trust the issuer for the entire policy — the precise dependency offline verification exists to remove. Signing it also means each approver attests to the policy their signature is being counted toward.</t>
          <t>
            <strong>The signed requirement is the signers' own statement.</strong> Signing makes the requirement tamper-evident to third parties; it does not make it binding on the signers. Whoever composes the payload chooses its <tt>requirement</tt>, so a single Approver — including one who is also the requester — can compose <tt>requiredApprovals: 1, requesterCannotApprove: false</tt> for an action the Relying Party's policy gates at 3-of-3 with four-eyes, sign it alone, and produce an envelope that satisfies §5 steps 1–7 against the signed value. A compromised Issuing Service can do the same by freezing a weaker requirement at issuance. Verifying the signed requirement proves that the quorum <em>the signers stated</em> was met, never that the Relying Party's own policy was. A Relying Party that holds that policy MUST compare the two (§5 step 3d).</t>
          <t>
            <strong>Offline checkability differs per field.</strong> A Relying Party MUST NOT assume all five are equally enforceable from a Proof Envelope alone:</t>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Offline verifiable?</th>
                <th>Why</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>requiredApprovals</td>
                <td>Fully</td>
                <td>Count distinct approver identities among the valid witnesses (not signature entries — §4.4.2), which requires an identity-associating trust anchor (§4.4.6).</td>
              </tr>
              <tr>
                <td>requesterCannotApprove</td>
                <td>Fully, under an identity-associating anchor only</td>
                <td>Compare each witness identity against <tt>requester.did</tt>. Under a key-set anchor (§4.4.6) the witness identity IS the key and <tt>signerDid</tt> is an unverified string, so the comparison has nothing to compare: the rule is not verifiable at all and the envelope MUST be rejected (§5 step 3b).</td>
              </tr>
              <tr>
                <td>requireHardwareKey</td>
                <td>Partially</td>
                <td>An assertion proves a WebAuthn credential signed, not that the authenticator carries verified manufacturer attestation — that check is made once, at registration time, against the attestation object an assertion does not carry. It does carry the signed Backup Eligible / Backup State flags, and a witness with either set MUST NOT count (§4.4.5 rule 6); the flags clear is the authenticator's own claim, not attestation.</td>
              </tr>
              <tr>
                <td>allowedAaguids</td>
                <td>Not at all</td>
                <td>The AAGUID appears in registration data, never in an assertion. What a verifier CAN refuse is what could never satisfy it: a non-empty allowlist is treated exactly like <tt>requireHardwareKey</tt> — a bare-key (ES256) witness MUST NOT count, and an offline proof MUST be rejected (§5a.3).</td>
              </tr>
              <tr>
                <td>signerClass</td>
                <td>Partially</td>
                <td>For a WebAuthn witness, the UV flag (§4.4.5) is cryptographic evidence a user-verification ceremony — a human gesture — occurred at signing. A bare-key (ES256) witness carries no signer-class evidence at all: there the class rests on the issuing service's signing-time enforcement, or, for an offline proof (§5a), on the delegation ceremony that named the operators. What a verifier MUST enforce unconditionally is the registry rule: reject absent or unrecognized values.</td>
              </tr>
            </tbody>
          </table>
          <t>A Relying Party that requires enforcement of <tt>requireHardwareKey</tt> or <tt>allowedAaguids</tt> MUST obtain it from enrollment records, not from the envelope. Where no enrollment record is available — an offline proof above all — such a requirement MUST be treated as unsatisfied rather than as satisfied by default. A Relying Party MUST NOT reject an ES256 witness merely because <tt>signerClass</tt> is <tt>"human"</tt> — humans legitimately sign with bare keys (§5a); a deployment wanting cryptographic proof of the ceremony pins <tt>requireHardwareKey</tt>.</t>
          <t>
            <strong>The signed requirement is a projection, not the whole policy.</strong> A deployment MAY enforce additional approval-policy dimensions beyond the five signed fields — the reference gateway, for example, also enforces a named eligible-approver list and requester-attestation constraints (<tt>approverDids</tt>, <tt>requireAttestedRequester</tt>, <tt>allowedIssuers</tt>) when granting an approval. Such fields are deliberately NOT part of the signed <tt>requirement</tt>: they are enforced online by the issuing service at approval time and are therefore invisible to offline verification. A Relying Party MUST NOT read the signed <tt>requirement</tt> as the complete policy in force — it is the offline-checkable projection of it, chosen so that every signed field is one an approver's signature can meaningfully attest to.</t>
        </section>
        <section anchor="s-4-3-3" numbered="false">
          <name>4.3.3 Denial Payload — the decision is signed</name>
          <t>A signature over an Intent Payload (or over a §5a.5 Delegation or §5b Agent Authority payload) attests to <strong>approval of</strong> that payload. Refusal is a different act and MUST be signed over different bytes.</t>
          <t>The <strong>Denial Payload</strong> for a payload <tt>P</tt> is derived from the exact canonical bytes of <tt>P</tt>:</t>
          <ol type="1">
            <li>
              <t>Parse <tt>P</tt>. It MUST be a JSON object carrying a non-empty string <tt>type</tt>.</t>
            </li>
            <li>
              <t>Set <tt>type</tt> to <tt>&lt;P.type&gt; + "-denial"</tt>.</t>
            </li>
            <li>
              <t>Add <tt>decision</tt> with the value <tt>"deny"</tt>.</t>
            </li>
            <li>
              <t>Re-serialize under JCS (§4.1).</t>
            </li>
          </ol>
          <t>Every other field is carried through verbatim, so the denial is bound to the same nonce, target, parameters, requester, requirement and expiry as the approval it refuses. Deriving rather than rebuilding is normative: it makes it structurally impossible for the two to disagree about <em>what</em> is being decided.</t>
          <t>An implementation MUST refuse to derive a denial from a payload whose <tt>type</tt> already ends in <tt>-denial</tt>.</t>
          <t>The derivation is defined for any DIV payload type, but this version requires denial support only for the three service-issued ceremony kinds (<tt>div-intent-verification</tt>, <tt>div-delegation</tt>, <tt>div-agent-authority</tt>), and only those are vectored (§7a). An offline refusal (§5a) produces no signed artifact: the Approver simply declines to sign, and there is no issuing service whose record needs non-repudiable refusal evidence — the Relying Party that constructed the challenge already knows it was not approved. <tt>div-offline-intent-denial</tt> is therefore not defined by this version and MUST NOT be emitted; verifiers refuse it by the ordinary unknown-type rule.</t>
          <t>
            <strong>Why this is a MUST.</strong> An issuing service that verifies both decisions against the approval bytes, and takes the decision from an unauthenticated request field instead, makes one signature valid evidence of two contradictory acts. An approval signature is then replayable as a refusal: the resulting witness carries the approver's real signature, public key and payload, verifies offline, and attests to a denial that human never made. The reference implementation had exactly this defect. Note that replay counters do not mitigate it — a synced platform authenticator reports a counter of <tt>0</tt> indefinitely (§4.4.5), so the same assertion remains presentable for as long as the challenge is open.</t>
          <t>Consequently:</t>
          <ul>
            <li>
              <t>An issuing service MUST select the bytes to verify from the decision being claimed, and MUST record those same bytes as the witness payload for that decision.</t>
            </li>
            <li>
              <t>A client generating a WebAuthn challenge MUST bind the bytes for the decision the user is being asked to make, at the moment the ceremony is created — an assertion produced for an approval is not convertible into a refusal afterwards.</t>
            </li>
          </ul>
          <t>Denial witnesses are ledger entries, not Proof Envelopes: they are verified by recomputing the committed leaf (DEWP §4.1–§4.2), not by rebuilding a canonical payload, so a verifier implementing only the Core Profile needs no Denial Payload support. Conformance vectors for the transform are pinned in §7a alongside the approval payloads.</t>
        </section>
        <section anchor="s-4-3-4" numbered="false">
          <name>4.3.4 Evidence — reserved</name>
          <t>
            <tt>evidence</tt> is part of the canonical Intent Payload and is therefore covered by every witness signature. In this version of the specification its value <strong>MUST</strong> be the literal <tt>null</tt>.</t>
          <t>
            <tt>null</tt> is signed and load-bearing, exactly as <tt>requester.attestation</tt>'s <tt>null</tt> is (§4.3.1): it is the payload's explicit statement that <strong>no external-evidence condition is represented by this authorization</strong>. It is not padding and it is not a default.</t>
          <t>Normative rules:</t>
          <ol type="1">
            <li>
              <t>The <tt>evidence</tt> key <strong>MUST</strong> be present in every Intent Payload and Offline Intent Payload. A payload in which the key is absent <strong>MUST</strong> be rejected.</t>
            </li>
            <li>
              <t>An absent key, a JSON <tt>undefined</tt>, an empty array <tt>[]</tt> and an empty object <tt>{}</tt>
                <strong>MUST NOT</strong> be treated as equivalent to <tt>null</tt>. An implementation that normalizes any of them into <tt>null</tt> — on either the producing or the verifying side — is non-conformant, because it converts a shape it does not understand into an assertion that no condition applied.</t>
            </li>
            <li>
              <t>Non-<tt>null</tt> values are <strong>reserved</strong> for a later version of this specification. An implementation <strong>MUST</strong> reject a payload whose <tt>evidence</tt> is not <tt>null</tt>, and <strong>MUST NOT</strong> treat it as unconditioned. This is the same fail-closed-on-unknown rule as the <tt>signerClass</tt> registry (§4.3.2, §5-step-3a), and for the same reason: an evidence-conditioned authorization that verified as though it were unconditioned would be the one failure this reservation exists to prevent.</t>
            </li>
            <li>
              <t>An implementation <strong>MUST NOT</strong> encode external-evidence commitments in <tt>params</tt> as a substitute for this field. <tt>params</tt> is a security-binding, runtime-owned field (Invariant 2) that a Relying Party reconstructs from the operation it is about to perform, that an Approver interface is expected to render in full (§7), and that participates in the Delegation agreement rule of §5a.6. An evidence commitment satisfies none of those three properties.</t>
            </li>
          </ol>
          <t>
            <strong>Where evidence sits relative to the payload's other fields.</strong> The four are deliberately distinct and a conformant implementation MUST NOT conflate them:</t>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Describes</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>
                  <tt>params</tt>
                </td>
                <td>What will execute. Runtime-owned; reconstructed by the Relying Party.</td>
              </tr>
              <tr>
                <td>
                  <tt>requirement</tt>
                </td>
                <td>Who may approve and how that approval must be produced (§4.3.2).</td>
              </tr>
              <tr>
                <td>
                  <tt>requester.attestation</tt>
                </td>
                <td>The provenance of the requesting principal (§4.3.1).</td>
              </tr>
              <tr>
                <td>
                  <tt>evidence</tt>
                </td>
                <td>External facts upon which the authorization may be conditioned.</td>
              </tr>
            </tbody>
          </table>
          <t>
            <strong>Payload families that do not carry <tt>evidence</tt>, and why.</strong> A Delegation (§5a.5) is sealed before the action it authorizes occurs, so it cannot commit to a fact established at approval time; conditioning a delegated action is a statement about <em>required</em> evidence, not a commitment to particular evidence, and is left to a later version. An Agent Authority (§5b) declares scope for requests, and a request within scope still takes the ordinary approval path, where the Intent Payload carries any conditioning. A Platform Hash-Only Intent (§5c) is issued by a party that never receives the payload and so has verified nothing it could commit to.</t>
        </section>
        <section anchor="s-4-3-5" numbered="false">
          <name>4.3.5 Key Ordering</name>
          <t>Because serialization is JCS, keys in the signed bytes are sorted by UTF-16 code unit (RFC 8785 §3.2.3) at every level (e.g. within <tt>requester</tt>: <tt>attestation</tt> before <tt>did</tt>; within an attestation: <tt>issuer</tt>, <tt>method</tt>, <tt>subject</tt>; within <tt>requirement</tt>: <tt>allowedAaguids</tt>, <tt>requesterCannotApprove</tt>, <tt>requireHardwareKey</tt>, <tt>requiredApprovals</tt>, <tt>signerClass</tt>). The middle pair in that example depends on code-unit order (<tt>H</tt> precedes <tt>d</tt>); implementations MUST NOT use locale-aware or case-insensitive sorting or hand-order keys. The recursive sort is the contract.</t>
        </section>
        <section anchor="s-4-3-6" numbered="false">
          <name>4.3.6 Agent continuity and composition</name>
          <t>The agent extension is part of the <strong>same JCS object and the same WebAuthn challenge bytes</strong> as <tt>target</tt>, <tt>params</tt>, <tt>requester</tt>, <tt>requirement</tt> and <tt>nonce</tt>:</t>
          <sourcecode type="json">NOTE: '\' line wrapping per RFC 8792

{
  "action": { "reversibility": "irreversible", "amount": { "amount"\
  : "4200", "currency": "USD" } },
  "agent": { "label": "payments-agent", "configDigest": "sha256:&lt;64 \
  lowercase hex&gt;", "delegatedBy": null },
  "session": { "id": "sha256:&lt;64 lowercase hex&gt;", "seq": "1", "prev"\
  : null,
    "aggregate": { "amount": "4200", "currency": "USD" } },
  "nbf": "2026-09-20T12:00:00.000Z",
  "exp": "2026-09-20T12:05:00.000Z"
}</sourcecode>
          <t>The nested groups have separate meanings. <tt>action</tt> commits the operation's <strong>effect</strong> as well as the existing exact <tt>params</tt>: <tt>reversibility</tt> is <tt>reversible</tt> or <tt>irreversible</tt>; the latter MUST go through human signing, never policy auto-approval or discovery. <tt>amount</tt> is either <tt>null</tt> or a nonnegative decimal string and ISO 4217-style three-letter currency. The PEP derives it from the operation it will actually perform; requester-provided prose and numeric floats are not evidence of the amount. <tt>agent</tt> commits a non-personal machine label, the RP's <tt>configDigest</tt>, and <tt>delegatedBy</tt> (hash of the complete, signed leaf Agent Authority receipt, or <tt>null</tt>). The label helps the human read the ceremony; the DID in <tt>requester.did</tt> remains the identity binding. <tt>session</tt> commits a SHA-256 digest of an RP-owned, random opaque session ID (never a name or email), positive decimal-string sequence, predecessor receipt hash (<tt>null</tt> only at sequence 1), and running aggregate. These fields are grouped because they form one ordered, per-session statement; they do not add an independent authorization. <tt>nbf</tt>, <tt>exp</tt> and the existing <tt>nonce</tt> limit that statement to one fresh request and at most five minutes. Timestamps MUST be canonical UTC ISO strings. Monetary strings MUST use base-10 digits with at most nine fractional places; a float, exponent, leading zero, or signed number is invalid.</t>
          <t>
            <tt>configDigest</tt> is <strong>an RP assertion, not a self-attestation and not proof of agent integrity</strong>. The reference computation is SHA-256 of UTF-8 bytes: ASCII <tt>intyga-agent-config-v1</tt>, one NUL byte (<tt>0x00</tt>), then JCS of <tt>{model:{provider, version}, tools:[{id, version, schemaDigest}], systemPrompt}</tt>. Sort tools by <tt>id</tt> using UTF-16 code-unit order before JCS and refuse duplicate IDs. Return <tt>sha256:</tt> plus lowercase hex. The RP's policy enforcement point (PEP) MUST recompute it from the live runtime immediately before execution and refuse drift. The gateway cannot see inside that runtime. The raw system prompt, raw tool schemas and personal data MUST NOT be placed in these new receipt fields or audit metadata; use digests and opaque identifiers. The RP must also minimize existing <tt>params</tt> and <tt>display</tt> according to its data policy.</t>
          <t>The complete agent receipt digest uses SHA-256 of UTF-8 bytes: ASCII <tt>intyga-agent-receipt-v1</tt>, one NUL byte (<tt>0x00</tt>), then JCS of <tt>{canonicalPayload,witnesses}</tt>. <tt>canonicalPayload</tt> is the exact signed string. Each witness is projected to exactly six fields: <tt>signerDid</tt>, <tt>signerPublicKey</tt>, <tt>signature</tt>, <tt>sigAlg</tt>, <tt>authenticatorData</tt>, and <tt>clientDataJSON</tt>; absent optional fields become JSON <tt>null</tt>. Sort the projected witnesses by their JCS strings in UTF-16 code-unit order before serializing the outer object. A legacy single-witness receipt supplies its top-level witness as a one-element array; the digest still commits to the complete signed proof. Return <tt>sha256:</tt> plus lowercase hex. Both constructions have pinned <tt>agentDigests</tt> cases in <tt>canonical-vectors.json</tt>.</t>
          <t>The PEP MUST reconstruct the intended target, action, parameters, reversibility, amount, agent identity/configuration and session state from its own protected state, verify the receipt and approver keys, and atomically reserve <tt>nonce</tt>, the per-session head/sequence and any global budget before executing. The reference SDK returns the next receipt hash for such a compare-and-swap; it cannot perform the RP's database transaction. Signing in INTYGA remains asynchronous. Agent drift is checked locally at execution, never by calling the signing service to inspect a live model.</t>
          <t>An offline verifier MUST receive the complete ordered session bundle and a trusted head obtained <strong>outside</strong> that bundle. It verifies each signature, contiguous sequence and predecessor hash, then recomputes each <tt>session.aggregate</tt> from the signed action amounts using integer decimal arithmetic. A gap, branch, duplicate, mixed currency, false aggregate or wrong final head is a verification failure. A verifier that has no implementation of the complete root-to-leaf authority check MUST refuse a delegated agent receipt; validating its human signature alone does not establish the subagent's scope. A single unanchored branch cannot prove that another branch was withheld; the RP must maintain a durable authoritative head and an independently enforced budget across sessions (ten individually approved payments may still exceed a global limit).</t>
        </section>
      </section>
      <section anchor="s-4-4" numbered="false">
        <name>4.4 Proof Envelope Schema</name>
        <t>A DIV Proof Envelope contains:</t>
        <ol type="1">
          <li>
            <t>The canonical Intent Payload, <strong>as a string</strong> — the exact bytes that were signed.</t>
          </li>
          <li>
            <t>One or more witness signatures over those bytes.</t>
          </li>
          <li>
            <t>The metadata a Relying Party needs to resolve keys and recompute the payload.</t>
          </li>
        </ol>
        <t>The payload MUST be carried as the serialized canonical string, not as a nested object. A nested object would have to be re-serialized before verification, reintroducing exactly the serialization ambiguity §4.1 exists to eliminate.</t>
        <section anchor="s-4-4-1" numbered="false">
          <name>4.4.1 Envelope Fields</name>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Type</th>
                <th>Requirement</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>canonicalPayload</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>The exact signed bytes (§4.1 canonical serialization of the Intent Payload).</td>
              </tr>
              <tr>
                <td>signatures</td>
                <td>array of Witness</td>
                <td>CONDITIONAL</td>
                <td>Every witness signature over <tt>canonicalPayload</tt>, one entry per approver (§4.4.2). REQUIRED for a quorum receipt; absent in the single-signature form (§4.4.3).</td>
              </tr>
              <tr>
                <td>verificationCode</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Short human-readable code for out-of-band confirmation (§4.4.4).</td>
              </tr>
              <tr>
                <td>target</td>
                <td>string</td>
                <td>OPTIONAL</td>
                <td>Echo of the payload's target, for display only.</td>
              </tr>
              <tr>
                <td>actionType</td>
                <td>string</td>
                <td>OPTIONAL</td>
                <td>Echo, for display only.</td>
              </tr>
              <tr>
                <td>actionDescription</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Echo of the payload's <tt>display</tt> field.</td>
              </tr>
              <tr>
                <td>params</td>
                <td>object</td>
                <td>REQUIRED</td>
                <td>Echo of the payload's params.</td>
              </tr>
              <tr>
                <td>requester</td>
                <td>object</td>
                <td>OPTIONAL</td>
                <td>Echo of the payload's requester, so a Relying Party can recompute the signed bytes.</td>
              </tr>
              <tr>
                <td>signerDid, signerPublicKey, signature, sigAlg, authenticatorData, clientDataJSON</td>
                <td>—</td>
                <td>CONDITIONAL</td>
                <td>Single-signature form (§4.4.3).</td>
              </tr>
            </tbody>
          </table>
          <t>Envelope fields fall into two classes under Invariant 2 (Local Payload Reconstruction), and the distinction is what makes reconstruction meaningful:</t>
          <ul>
            <li>
              <t>
                <strong>Security-binding fields</strong> — <tt>target</tt>, <tt>actionType</tt>, <tt>params</tt> — MUST come exclusively from the Relying Party's own runtime during reconstruction. Their envelope copies (and the <tt>params</tt> echo) are display/tooling conveniences a Relying Party MUST NOT feed into reconstruction: doing so verifies the envelope against itself and voids the binding.</t>
            </li>
            <li>
              <t>
                <strong>Issuance-frozen fields</strong> — <tt>actionDescription</tt> (the payload's <tt>display</tt>), <tt>requester</tt>, and the <tt>requirement</tt>, <tt>evidence</tt>, <tt>nonce</tt> and <tt>expiresAt</tt> carried inside <tt>canonicalPayload</tt> — are frozen by the Issuing Service before any witness signs, so the Relying Party has no runtime source for them. It takes them from the envelope as reconstruction <em>inputs</em>, which is safe rather than circular: the signature covers them, so a forged value changes the reconstructed bytes and fails verification. The <tt>nonce</tt> is additionally bound by the caller, who MUST assert which challenge is being redeemed and refuse a payload naming a different one. "Forged" here means altered by a third party: the signers author these fields, so the <tt>requirement</tt> is additionally bounded by the caller's own policy where it has one (§5 step 3d).</t>
            </li>
          </ul>
          <t>
            <tt>evidence</tt> has <strong>no envelope echo, deliberately</strong>. <tt>target</tt>, <tt>actionType</tt>, <tt>params</tt> and <tt>requester</tt> are echoed because a Relying Party needs them for display or tooling; <tt>evidence</tt> needs neither. It is issuance-frozen, so the verifier reads it from <tt>canonicalPayload</tt> — where a forged value fails the byte comparison of §5-step-6 — and asserts the expected <tt>null</tt> during reconstruction. Adding an echo would create a second, untrusted copy of a field whose only purpose is to be checked against the signed bytes, which is the circularity §4.4.1 exists to prevent.</t>
          <t>
            <tt>actionDescription</tt> is REQUIRED rather than OPTIONAL despite being an echo, and that requiredness is behaviourally enforced: the reference verifier feeds it into <tt>display</tt> during reconstruction, so omitting it changes the reconstructed bytes and fails the signature check. <tt>params</tt> is REQUIRED for display and tooling interoperability, but reconstruction always uses the Relying Party's own runtime parameters, as Invariant 2 demands; the presence of the envelope's <tt>params</tt> echo is therefore enforced structurally by the schema only, and the echo is never trusted.</t>
        </section>
        <section anchor="s-4-4-2" numbered="false">
          <name>4.4.2 Witness Object</name>
          <table>
            <thead>
              <tr>
                <th>Field</th>
                <th>Type</th>
                <th>Requirement</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>signerDid</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Identifier of the approving principal.</td>
              </tr>
              <tr>
                <td>signerPublicKey</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Base64 SPKI (ES256) or base64 COSE_Key (WEBAUTHN).</td>
              </tr>
              <tr>
                <td>signature</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>Base64 signature over <tt>canonicalPayload</tt> (for <tt>WEBAUTHN</tt> witnesses: unpadded base64url over <tt>authenticatorData ‖ SHA-256(clientDataJSON)</tt> — see the encoding note below).</td>
              </tr>
              <tr>
                <td>sigAlg</td>
                <td>string</td>
                <td>REQUIRED</td>
                <td>
                  <tt>ES256</tt> or <tt>WEBAUTHN</tt>.</td>
              </tr>
              <tr>
                <td>authenticatorData</td>
                <td>string</td>
                <td>CONDITIONAL</td>
                <td>Base64url. REQUIRED when <tt>sigAlg</tt> is <tt>WEBAUTHN</tt>.</td>
              </tr>
              <tr>
                <td>clientDataJSON</td>
                <td>string</td>
                <td>CONDITIONAL</td>
                <td>Base64url. REQUIRED when <tt>sigAlg</tt> is <tt>WEBAUTHN</tt>; its <tt>challenge</tt> MUST equal <tt>base64url(canonicalPayload)</tt>.</td>
              </tr>
            </tbody>
          </table>
          <t>
            <strong>WEBAUTHN witness field encodings.</strong> The browser's assertion API yields <tt>authenticatorData</tt>, <tt>clientDataJSON</tt> and <tt>signature</tt> as <strong>unpadded base64url</strong>, and that is the wire form producers emit (the shared <tt>webauthn-vector.json</tt> pins it). Verifiers MUST accept unpadded base64url for these three fields and SHOULD additionally accept standard base64, padded or not — the two alphabets differ only in characters 62/63, so tolerant decoding is lossless and cannot make an invalid encoding valid. A verifier that decodes only the standard alphabet refuses valid production receipts while appearing to pass a standard-encoded test suite; this exact drift shipped in three of the reference ports and was caught only by re-encoding the golden vector.</t>
          <t>
            <strong>ES256 signature encodings.</strong> For an <tt>ES256</tt> witness the base64-decoded <tt>signature</tt> MAY be either raw IEEE P1363 (<tt>r ‖ s</tt>, exactly 64 bytes for P-256) or ASN.1 DER, and verifiers MUST accept both. The two are encodings of the same <tt>(r, s)</tt> pair, so tolerant decoding cannot widen what verifies — the signature still has to verify under a trusted key. WebAuthn assertions carry DER-encoded ECDSA signatures (that is what the WebAuthn API yields). The §7a receipt fixtures pin one accepted receipt in each encoding.</t>
          <t>Producers MUST emit <tt>sigAlg</tt>. For legacy compatibility, a verifier MUST treat an absent or null witness <tt>sigAlg</tt> as <tt>ES256</tt>, and MUST fall back to ES256 verification for a value it does not recognize; the signature must still verify under a trusted P-256 key, so the fallback can only fail closed — it never widens acceptance. <tt>AUTO_APPROVED</tt> is not an unrecognized value: §4.4.3 defines it, it carries no witness signature to verify, and it MUST NOT fall through to the ES256 path. (Contrast §5-step-3a, where an unrecognized <tt>signerClass</tt> is rejected outright: <tt>sigAlg</tt> names how one signature is checked and the fallback still demands a valid signature, while <tt>signerClass</tt> names <em>what kind of authority</em> the whole receipt claims, which no fallback can safely assume.)</t>
          <t>A quorum receipt MUST carry one entry per approver. Emitting only the first approval makes an M-of-N approval indistinguishable from a 1-of-1 one, so <tt>requirement.requiredApprovals</tt> could not be checked offline at all — the quorum would be unverifiable precisely where it matters most.</t>
          <t>When counting toward <tt>requirement.requiredApprovals</tt>, a Relying Party MUST count <strong>distinct approver identities</strong>, not signature entries. Two signatures from one approver's two registered credentials are one approval.</t>
        </section>
        <section anchor="s-4-4-3" numbered="false">
          <name>4.4.3 Single-Signature Form</name>
          <t>When <tt>signatures</tt> is absent, the flat <tt>signerDid</tt> / <tt>signerPublicKey</tt> / <tt>signature</tt> / <tt>sigAlg</tt> fields MUST be read as a one-element witness list. This form also carries the <tt>AUTO_APPROVED</tt> case, which has no witness at all: <tt>sigAlg</tt> is <tt>AUTO_APPROVED</tt> and there is no human signature. A Relying Party MUST refuse an <tt>AUTO_APPROVED</tt> envelope unless it has explicitly opted in for that specific call site.</t>
          <t>Example (ES256, single signature; required echo fields shown):</t>
          <sourcecode type="json">NOTE: '\' line wrapping per RFC 8792

{
  "canonicalPayload": "{\"actionType\":\"db:dropTable\",\"display\":\
  \"Delete production users table\",…}",
  "signerDid": "did:example:human:alice",
  "signerPublicKey": "base64-spki-p256",
  "signature": "base64-signature",
  "sigAlg": "ES256",
  "verificationCode": "AB12-CD34",
  "actionDescription": "Delete production users table",
  "params": { "environment": "production", "table": "users" }
}</sourcecode>
        </section>
        <section anchor="s-4-4-4" numbered="false">
          <name>4.4.4 Verification Code</name>
          <t>
            <tt>verificationCode</tt> is a short code derived from the canonical payload, formatted <tt>AB12-CD34</tt>. It exists so an approver can confirm out of band that the challenge they are signing is the one the requester raised. It is a human-factors control, not a cryptographic one, and MUST NOT be treated as authentication.</t>
          <t>The derivation is fixed so that both ends of the out-of-band channel compute the same code with no coordination: take <tt>SHA-256(canonicalPayload)</tt> as lowercase hex, keep the first 8 characters, uppercase them, and insert a hyphen after the fourth (<tt>XXXX-XXXX</tt>). Because the input is the exact signed bytes, any change to the action, its parameters, or the signed requirement produces a different code.</t>
        </section>
        <section anchor="s-4-4-5" numbered="false">
          <name>4.4.5 WebAuthn Envelopes</name>
          <t>A Relying Party verifying a <tt>WEBAUTHN</tt> witness MUST:</t>
          <ol type="1">
            <li>
              <t>Pin the expected <tt>origin</tt> and RP ID and reject any assertion that does not match. Without both pinned, an assertion harvested at any other Relying Party verifies.</t>
            </li>
            <li>
              <t>Enforce the User-Present flag, and by default the User-Verified flag.</t>
            </li>
            <li>
              <t>Verify the signature over <tt>authenticatorData || SHA-256(clientDataJSON)</tt>, not over the payload directly.</t>
            </li>
            <li>
              <t>Confirm <tt>clientDataJSON.challenge</tt> equals <tt>base64url(canonicalPayload)</tt>.</t>
            </li>
            <li>
              <t>Reject an assertion whose <tt>clientDataJSON.crossOrigin</tt> is <tt>true</tt>, or whose WebAuthn L3 <tt>topOrigin</tt> is present and differs from <tt>origin</tt>, unless the deployment explicitly opts in. The two are the same embedding reported two ways, and a verifier that checks only <tt>crossOrigin</tt> accepts assertions the issuing service refuses. Origin and RP-ID pinning see the frame's origin inside a cross-origin iframe, so they cannot by themselves detect a third-party embedder driving the ceremony. The hosted INTYGA service applies this rule at ingest, on every ceremony it runs or relays (console, hosted approvals and the platform plane, registration included), with no opt-in: it also refuses a WebAuthn L3 <tt>topOrigin</tt> that differs from <tt>origin</tt>. Every receipt it issues therefore passes a verifier's default for this rule, and a verifier that turns on <tt>allowCrossOrigin</tt> accepts nothing the service would have issued.</t>
            </li>
            <li>
              <t>When the signed <tt>requirement.requireHardwareKey</tt> is <tt>true</tt>, not count a witness whose <tt>authenticatorData</tt> flags byte has Backup Eligible (bit 3, <tt>0x08</tt>) or Backup State (bit 4, <tt>0x10</tt>) set. A backup-eligible credential is by definition not device-bound, and both flags are covered by the assertion signature, so this catches an issuer that let a synced passkey sign a hardware-pinned action. The converse proves nothing: flags that are clear are the authenticator's claim, not attestation (§4.3.2). A non-empty <tt>allowedAaguids</tt> alone does not trigger this rule — an allowlist may legitimately name a synced-passkey provider.</t>
            </li>
          </ol>
          <t>The authenticator's signature counter is not a usable replay control here: a synced platform authenticator (passkey) legitimately reports a counter of <tt>0</tt> on every assertion, so counter monotonicity cannot distinguish a replay from a fresh ceremony. Replay protection comes from the challenge binding (rule 4) plus nonce redemption (§6.1), never from the counter.</t>
          <t>Example (WEBAUTHN, 2-of-N quorum; required echo fields shown):</t>
          <sourcecode type="json">{
  "canonicalPayload": "{\"actionType\":\"db:dropTable\",…}",
  "signatures": [
    {
      "signerDid": "did:example:human:alice",
      "signerPublicKey": "base64-cose-key",
      "signature": "base64-assertion-signature",
      "sigAlg": "WEBAUTHN",
      "authenticatorData": "base64url-authenticator-data",
      "clientDataJSON": "base64url-client-data-json"
    },
    {
      "signerDid": "did:example:human:bob",
      "signerPublicKey": "base64-cose-key",
      "signature": "base64-assertion-signature",
      "sigAlg": "WEBAUTHN",
      "authenticatorData": "base64url-authenticator-data",
      "clientDataJSON": "base64url-client-data-json"
    }
  ],
  "verificationCode": "AB12-CD34",
  "actionDescription": "Delete production users table",
  "params": { "environment": "production", "table": "users" }
}</sourcecode>
        </section>
        <section anchor="s-4-4-6" numbered="false">
          <name>4.4.6 Trust Anchor Modes and Identity Association</name>
          <t>A Relying Party resolves trusted Approver keys from a <strong>trust anchor</strong> it controls (§5 step 3). Three shapes are in common use, and they are not equivalent for quorum:</t>
          <ul>
            <li>
              <t>
                <strong>Identity-associating anchor (REQUIRED for <tt>requiredApprovals</tt> &gt; 1).</strong> The anchor maps an approver <em>identity</em> — a DID, or an equivalent stable subject identifier — to the set of public keys bound to it. This is what makes §4.4.2's rule expressible: several credentials belonging to one person collapse to one approval, exactly as an offline Trust Bundle requires (§5a.4).</t>
            </li>
            <li>
              <t>
                <strong>Key-set anchor.</strong> The anchor is a flat allowlist of trusted public keys with no identity attached. Because nothing binds a key to a person, <strong>each trusted key is necessarily treated as its own identity</strong>, and the envelope's <tt>signerDid</tt> cannot be relied upon to close the gap: in this mode it is an unverified string, and counting it would let one approver claim to be three. The consequence is unavoidable and MUST be understood by anyone configuring one: a deployment using a key-set anchor with <tt>requiredApprovals</tt> &gt; 1 is counting <strong>credentials, not people</strong>, so one approver holding <em>M</em> listed keys satisfies an <em>M</em>-of-<em>N</em> quorum alone. For the same reason a verifier MUST reject a key-set anchor when the signed <tt>requesterCannotApprove</tt> rule is true (§5 step 3b): a receipt-controlled <tt>signerDid</tt> cannot establish separation of duties. Use an identity-associating anchor for that rule.</t>
            </li>
          </ul>
          <t>Therefore a deployment MUST NOT use a key-set anchor when <tt>requiredApprovals</tt> &gt; 1, unless it also guarantees at most one listed key per approver — which is the same requirement stated differently, and is fragile in exactly the way credential rotation and multi-device enrollment make likely.</t>
          <t>
            <strong>One key, one approver.</strong> An identity-associating anchor can itself map one key to two identities — an export error, or one person enrolled under two identifiers. Counting distinct identities alone would then let that key's holder satisfy a 2-of-N quorum alone. A verifier MUST count distinct identities AND distinct keys: once a key has been counted for one identity, a witness verifying under the same key for a different identity MUST NOT count. The reference verifiers compare keys by their decoded bytes; the same key in two different encodings (a COSE_Key and an SPKI) is not detected, so an anchor SHOULD NOT carry one key in two encodings.</t>
          <ul>
            <li>
              <t>
                <strong>Identity-committing anchor (self-certifying identifiers). Support is OPTIONAL.</strong> The pinned identifier itself commits to a key — e.g. <tt>did:intyga:key:&lt;base64url(sha256(key bytes))&gt;</tt> — so the anchor entry needs no key material at all: the verifier accepts the envelope-carried key exactly when it hashes to the pinned identifier. This does not conflict with §5 step 3's prohibition on trusting envelope-carried keys, because the <em>commitment</em> is resolved from the Relying Party's own configuration; the envelope merely transports bytes that are checked against it. <strong>Precedence:</strong> an anchor that additionally maps keys to such an identity takes precedence over the commitment — the explicit mapping must be able to both extend the identity to later-enrolled credentials and <em>narrow</em> it away from a revoked one, neither of which a commitment-always-wins rule can express. A single-key commitment cannot rotate; identities expected to hold several credentials over time SHOULD use a stable identifier under an identity-associating anchor instead.</t>
            </li>
          </ul>
          <t>Delegations (§5a.5) name approver identities in <tt>delegatedTo</tt>, so they need an identity-associating anchor and MUST be refused under a key-set anchor.</t>
          <t>
            <em>(Note for conformance testing: the golden vectors can only demonstrate the distinct-identity rule under an identity-associating anchor, since a key-set anchor has no identities to be distinct about. A vector suite passing under a key-set anchor is not evidence that §4.4.2 is satisfied.)</em>
          </t>
        </section>
      </section>
    </section>
    <section anchor="s-5" numbered="false">
      <name>5. Verification Procedure</name>
      <t>The Relying Party MUST execute verification immediately before performing an Irreversible Action.</t>
      <t>The verification procedure is:</t>
      <ol type="1">
        <li>
          <t>Receive the Proof Envelope.</t>
        </li>
        <li>
          <t>Validate Proof Envelope structure.</t>
        </li>
        <li>
          <t>Resolve the trusted Approver public key(s) according to local policy. The key MUST come from the Relying Party's own key management; a key read from the envelope proves only that the envelope is internally consistent. (An identity-committing anchor — §4.4.6, OPTIONAL — satisfies this rule by pinning a key <em>commitment</em> in the Relying Party's own configuration: the envelope-carried key is accepted only when it matches that commitment.) When <tt>requirement.requiredApprovals</tt> is greater than 1, the trust anchor MUST associate keys with identities (§4.4.6) — a key-set anchor cannot express the distinct-identity rule of §4.4.2.</t>
          <ul>
            <li>
              <t>
                <strong>3a.</strong> Validate <tt>requirement.signerClass</tt> against the registry of §4.3.2, reading the <tt>requirement</tt> from the envelope's <tt>canonicalPayload</tt> (an issuance-frozen field — §4.4.1; a forged value fails the byte comparison in step 6): reject the envelope if the field is absent or carries a value this verifier does not recognize. An unrecognized class MUST NOT be treated as human-equivalent — future signer classes become acceptable only when a verifier is explicitly taught their semantics, never by default.</t>
            </li>
            <li>
              <t>
                <strong>3b.</strong> If <tt>requirement.requiredApprovals</tt> is greater than 1, <strong>or</strong>
                <tt>requirement.requesterCannotApprove</tt> is true, the anchor MUST be identity-associating (§4.4.6); reject the envelope otherwise. For <tt>requesterCannotApprove</tt> the reason is that separation of duties is a statement about <em>identities</em>: under a key-set anchor each key is its own identity and the envelope's <tt>signerDid</tt> is attacker-controlled, so "this signer is not the requester" cannot be established. Reject at this step rather than at step 7 — the failure is that the Relying Party's anchor is the wrong shape for the signed policy, not that a quorum came up short, and reporting it as a shortfall sends an operator looking for missing approvals that were never the problem.</t>
            </li>
            <li>
              <t>
                <strong>3c.</strong> Validate <tt>evidence</tt>, reading it from the envelope's <tt>canonicalPayload</tt> (an issuance-frozen field — §4.4.1; a forged value fails the byte comparison in step 6): reject the envelope if the key is absent, and reject it if the value is anything other than <tt>null</tt>. A non-<tt>null</tt> value MUST NOT be treated as unconditioned — evidence semantics become acceptable only when a verifier is explicitly taught them, never by default (§4.3.4). An implementation MUST distinguish an absent key from a present <tt>null</tt>; collapsing the two turns this step into a no-op.</t>
            </li>
            <li>
              <t>
                <strong>3d.</strong> If the Relying Party holds a local approval policy for the action, it MUST compare the signed <tt>requirement</tt> (read from <tt>canonicalPayload</tt>, as in 3a) against it and reject any weaker value: a <tt>requiredApprovals</tt> below the policy's, <tt>requesterCannotApprove</tt> false where the policy sets it, or <tt>requireHardwareKey</tt> false where the policy sets it. An equal or stricter signed value passes, and steps 3b and 7 then enforce the <em>signed</em> value. The signed requirement alone bounds only what the signers stated (§4.3.2): without this step, one Approver can author and satisfy a quorum of one. Reject here, before any signature is counted — the failure is a policy downgrade, not a shortfall. A local policy the verifier cannot read (for example a quorum below</t>
              <ol type="1">
                <li>
                  <t>MUST be rejected rather than treated as absent. <tt>allowedAaguids</tt> is outside this comparison; a deployment restricting authenticator models expresses that as <tt>requireHardwareKey</tt> here and enforces the model list from enrollment records (§4.3.2).</t>
                </li>
              </ol>
            </li>
          </ul>
        </li>
        <li>
          <t>Construct the expected Intent Payload from local runtime execution parameters.</t>
        </li>
        <li>
          <t>Serialize the expected payload using RFC8785 JCS.</t>
        </li>
        <li>
          <t>Verify each witness signature against the canonical bytes.</t>
        </li>
        <li>
          <t>Validate the approval requirement: count <strong>distinct</strong> approver identities with a valid signature. If <tt>requirement.requesterCannotApprove</tt> is true, a signature from <tt>requester.did</tt> MUST NOT be counted toward <tt>requiredApprovals</tt>; its presence does not by itself invalidate the envelope. (This step is reached only under an identity-associating anchor — step 3b rejects a key-set anchor outright when this rule is set, because there the comparison is not expressible.) Reject unless the remaining count is at least <tt>requirement.requiredApprovals</tt>. A <tt>requiredApprovals</tt> that is absent, non-integral or below 1 MUST have been rejected before this step (§4.3.2): "at least 0" is true with nothing counted, so an implementation that reaches here with a 0 accepts an envelope carrying no valid witness signature.</t>
        </li>
        <li>
          <t>Validate Target binding.</t>
        </li>
        <li>
          <t>Validate expiration.</t>
        </li>
        <li>
          <t>Validate nonce freshness.</t>
        </li>
        <li>
          <t>Record nonce redemption.</t>
        </li>
        <li>
          <t>Permit execution.</t>
        </li>
      </ol>
      <t>If any step fails, execution MUST be denied.</t>
      <t>Step 7 is what makes quorum meaningful offline. A Relying Party that verifies one signature and stops has verified an approval, not <em>the</em> approval the policy required. Step 3d is what makes it <em>the Relying Party's</em> quorum: step 7 counts against the signed value, and a verifier that skips 3d has proved only that the signers met the quorum they chose.</t>
      <t>Steps 1–9 constitute the <strong>stateless cryptographic check</strong> and MAY be implemented by a self-contained offline verifier that holds no state. Steps 10–11 (nonce freshness and redemption) are inherently <strong>stateful</strong>: they require the Relying Party to persist which nonces it has already consumed. A conformant deployment MAY therefore satisfy steps 10–11 in a stateful gatekeeper (which atomically marks a challenge consumed) while running steps 1–9 as a defense-in-depth offline re-verification at the point of execution. Because the stateless verifier cannot itself record redemption, it MUST require the caller to name the nonce being redeemed, so that single-use enforcement remains the caller's explicit responsibility.</t>
    </section>
    <section anchor="s-5a" numbered="false">
      <name>5a. Offline Approval</name>
      <section anchor="s-5a-1" numbered="false">
        <name>5a.1 Motivation</name>
        <t>A DIV deployment is fail-closed (Invariant 4): when the approval service is unreachable, no proof can be obtained and the Irreversible Action does not execute. That is correct, and it places the approval service in the critical path of every governed action.</t>
        <t>An operator therefore needs a mechanism that survives the outage. The naive answer — pre-signing approvals for anticipated actions and holding them until needed — is <strong>NOT RECOMMENDED</strong> by this specification. Such a proof is a bearer capability at rest: possessing the file is sufficient to act, it cannot be revoked at an offline Relying Party, and the human signature attests to a judgment made about a hypothetical rather than about the incident in progress. Narrowing the action and its parameters does not repair this, because the defect is in <em>when</em> the human decided, not in <em>how much</em> they authorized.</t>
        <t>This section specifies the alternative. <strong>Offline Approval moves the signing ceremony off the network rather than earlier in time.</strong> The Relying Party constructs the challenge locally at incident time, Approvers review and sign it on a device with no connectivity, and the Relying Party verifies the result with the same stateless procedure of §5. No capability exists at rest, the humans see the actual incident, and the validity window is minutes rather than weeks.</t>
        <t>Two mechanisms are defined. §5a.2–§5a.4 specify <strong>Offline Approval</strong>, which applies when the approval service is unreachable but the Approvers are not. §5a.5–§5a.6 specify <strong>Delegation</strong>, a narrow pre-signed artifact for the residual case where the Approvers themselves cannot be reached; a Delegation authorizes no action by itself and transfers only the authority to approve.</t>
      </section>
      <section anchor="s-5a-2" numbered="false">
        <name>5a.2 Offline Intent Payload</name>
        <t>An Offline Intent Payload is identical to the Intent Payload of §4.2 except that:</t>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <tt>type</tt>
              </td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>div-offline-intent</tt>.</td>
            </tr>
            <tr>
              <td>
                <tt>challengedAt</tt>
              </td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC timestamp at which the Relying Party constructed the challenge.</td>
            </tr>
          </tbody>
        </table>
        <t>The <tt>type</tt> discriminator is inside the signed bytes. An Offline Intent Proof therefore <strong>MUST NOT</strong> verify as an Intent Proof, and an Intent Proof <strong>MUST NOT</strong> verify as an Offline Intent Proof, even for a byte-identical action. Implementations MUST provide the two canonicalizations as distinct operations; a single operation parameterized by type is NOT RECOMMENDED, because it permits the ordinary path to emit an offline payload by mistake.</t>
        <t>The <tt>nonce</tt> MUST be generated by the Relying Party (§6.1), which is the party that will redeem it. Because the approval service never sees the challenge, no other party can enforce its single use.</t>
        <t>
          <tt>challengedAt</tt> exists so a verifier can bound the validity <strong>window</strong>, not merely the expiry. Without it, a payload minted with an over-long <tt>expiresAt</tt> is indistinguishable at verification time from a correctly minted one.</t>
      </section>
      <section anchor="s-5a-3" numbered="false">
        <name>5a.3 Offline Verification</name>
        <t>A Relying Party verifying an Offline Intent Proof MUST perform the §5 procedure, reconstructing the payload with the offline canonicalization, and MUST additionally:</t>
        <ol type="1">
          <li>
            <t>
              <strong>Refuse by default.</strong> An Offline Intent Proof MUST be rejected unless the caller has explicitly opted in at that call site. A process-wide or default-on opt-in is NOT RECOMMENDED. An Offline Intent Proof with no human signature (<tt>sigAlg: AUTO_APPROVED</tt>) MUST be rejected regardless of any auto-approval opt-in.</t>
          </li>
          <li>
            <t>
              <strong>Bound the window.</strong>
              <tt>expiresAt - challengedAt</tt> has a fixed ceiling of <strong>60 minutes</strong>. A deployment MAY enforce a shorter window and MUST NOT accept a longer one; a proof whose window exceeds the deployment's cap MUST be rejected even when its signature is valid.</t>
          </li>
          <li>
            <t>
              <strong>Reject inverted and forward-dated windows.</strong>
              <tt>expiresAt</tt> earlier than <tt>challengedAt</tt> MUST be rejected. A <tt>challengedAt</tt> later than the verification time plus the §6.2 clock-skew tolerance MUST also be rejected: capping the window's <em>width</em> without bounding its <em>position</em> leaves the window free to slide, so a proof dated years ahead with a compliant 60-minute window would verify today and keep verifying until that date — exactly the pre-signed bearer capability §5a.1 rejects. This rejection is unconditional and is NOT waived by the audit override of §6.2, which exists to re-examine a proof that <em>was</em> valid and has since lapsed and says nothing about one dated in the future.</t>
          </li>
          <li>
            <t>
              <strong>Refuse a hardware-key requirement.</strong> If the signed approval requirement sets <tt>requireHardwareKey</tt>, or carries a non-empty <tt>allowedAaguids</tt> authenticator-model allowlist, the proof MUST be rejected. See §5a.8; this constraint is normative because the requirement cannot be satisfied offline — an offline witness is a bare key, which has no authenticator model at all — and accepting the proof anyway would silently downgrade the policy the Approver attested to. A producer SHOULD refuse to create an offline challenge, or declare an offline runbook, under such a rule.</t>
          </li>
          <li>
            <t>
              <strong>Enforce every other invariant unchanged</strong> — Target Isolation (§3 Invariant 5), parameter binding (§3 Invariant 1), local payload reconstruction (§3 Invariant 2), the signed approval requirement including <tt>requesterCannotApprove</tt> (§4.3.2), the §5-step-3d comparison against the Relying Party's policy — normally the Trust Bundle rule for the action (§5a.4) — and expiry (§6.2).</t>
          </li>
        </ol>
        <t>The approval requirement bound into an Offline Intent Payload MUST be obtained from an authority outside the Relying Party — normally a Trust Bundle (§5a.4). A Relying Party that composes the requirement itself is setting its own quorum, and the resulting proof attests to nothing beyond that Relying Party's own configuration.</t>
      </section>
      <section anchor="s-5a-4" numbered="false">
        <name>5a.4 Trust Bundle</name>
        <t>Offline verification requires the Approver keys to be resolvable locally: Invariant 3 forbids taking them from the proof under verification, and the directory that would ordinarily answer the lookup is by definition unreachable. A <strong>Trust Bundle</strong> is the offline projection of the approval policy, exported while connectivity exists and verified locally thereafter.</t>
        <t>A Trust Bundle MUST carry, at minimum:</t>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <tt>type</tt>
              </td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>div-trust-bundle-v1</tt> for the first public exact action policy profile. The internal tenant policy migration number is separate.</td>
            </tr>
            <tr>
              <td>
                <tt>v</tt>
              </td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>1</tt> for this profile. Unknown versions MUST be refused.</td>
            </tr>
            <tr>
              <td>
                <tt>approvers</tt>
              </td>
              <td>REQUIRED</td>
              <td>Approver identities and the public keys bound to each.</td>
            </tr>
            <tr>
              <td>
                <tt>policy</tt>
              </td>
              <td>REQUIRED</td>
              <td>The tenant baseline and exact action-ID requirements, including eligible Approver identities.</td>
            </tr>
            <tr>
              <td>
                <tt>unmatchedActionPolicy</tt>
              </td>
              <td>REQUIRED</td>
              <td>
                <tt>DENY</tt> or <tt>BASELINE</tt>, covered by the bundle signature.</td>
            </tr>
            <tr>
              <td>
                <tt>issuedAt</tt>
              </td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC timestamp of export.</td>
            </tr>
            <tr>
              <td>
                <tt>expiresAt</tt>
              </td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC timestamp after which the bundle MUST be refused.</td>
            </tr>
          </tbody>
        </table>
        <t>A Trust Bundle MUST be integrity-protected by a signature the Relying Party can verify without network access, using key material pinned at export time. A Relying Party MUST refuse an expired bundle, and MUST NOT fall back to an unverified or unbounded Approver set when no valid bundle is available.</t>
        <t>
          <em>Reference format (informative).</em> The reference implementation (<tt>packages/sdk/src/trust-bundle.ts</tt>, exported with <tt>intyga trust-bundle export</tt>) carries the Trust Bundle as a compact <strong>JWS (RS256)</strong> whose payload is the bundle JSON above, verified against the issuing gateway's public key stored alongside it at export time — the pinned-at-export key material this section requires. The exact policy profile carries complete quorum, hardware, four-eyes, requester-attestation, allowlist, eligibility, escalation and automatic-window metadata. Group membership is expanded at export; escalation signers remain separate from initial eligibility. The selected rule MUST preserve every constraint of the <tt>*</tt> baseline or resolution MUST refuse the action. Unknown actions use the baseline only when the signed <tt>unmatchedActionPolicy</tt> says <tt>BASELINE</tt>; unsupported offline controls MUST be refused. Signed <tt>selectionRank</tt>/<tt>selectionKey</tt> fields are informational, not a substitute for validating the complete constraints. Legacy bundles MUST NOT be used to create new approvals under this profile; ordinary historical receipt verification is unchanged. See the implementation's rollout and compatibility guidance for disconnected consumers. On top of the bundle's own <tt>expiresAt</tt> it enforces a <strong>30-day maximum age</strong> from <tt>issuedAt</tt>, so an operator who sets a distant expiry still cannot keep a stale approver set in service indefinitely. Other formats satisfying the normative requirements above are equally conformant.</t>
        <t>Because an Approver identity may be bound to more than one public key (for example a software key and several registered authenticators), a conformant trust anchor MUST be able to associate multiple keys with one identity, and quorum MUST count distinct <strong>identities</strong> rather than distinct keys. Counting keys would let a single Approver holding several credentials satisfy an M-of-N quorum alone.</t>
      </section>
      <section anchor="s-5a-5" numbered="false">
        <name>5a.5 Delegation of Approval Authority</name>
        <t>Offline Approval requires the Approvers to be reachable out of band. Where that cannot be assumed, a deployment MAY pre-authorize a <strong>Delegation</strong>: a proof, signed in advance by the ordinary quorum, that transfers the authority to approve one pre-declared action to a named set of local operators.</t>
        <t>A Delegation Payload is identical to the Intent Payload of §4.2 except that:</t>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <tt>type</tt>
              </td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>div-delegation</tt>.</td>
            </tr>
            <tr>
              <td>
                <tt>delegatedTo</tt>
              </td>
              <td>REQUIRED</td>
              <td>The identities permitted to approve at incident time. MUST be a set, canonicalized in sorted order.</td>
            </tr>
            <tr>
              <td>
                <tt>delegatedQuorum</tt>
              </td>
              <td>REQUIRED</td>
              <td>How many distinct members of <tt>delegatedTo</tt> MUST sign. MUST be ≥ 1 and ≤ the size of <tt>delegatedTo</tt>.</td>
            </tr>
            <tr>
              <td>
                <tt>sealedAt</tt>
              </td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC time the sealing ceremony was opened; frozen into the bytes every quorum member signs (as in §5b.2).</td>
            </tr>
          </tbody>
        </table>
        <t>
          <strong>A Delegation authorizes no action.</strong> It is not an approval and MUST NOT be accepted as one: an implementation MUST reject a Delegation Payload presented to the approval verification procedure of §5, and MUST expose Delegation verification as a distinct operation. There is deliberately no opt-in flag that would permit the substitution, because a Delegation that could authorize its own action would be exactly the pre-signed bearer capability §5a.1 rejects.</t>
        <t>The <tt>requirement</tt> in a Delegation Payload describes the quorum that signed the <strong>Delegation</strong> and MUST be at least as strict as the ordinary requirement for the delegated action. Delegating authority is never the cheaper path.</t>
      </section>
      <section anchor="s-5a-6" numbered="false">
        <name>5a.6 Delegation Verification</name>
        <t>A Relying Party using a Delegation MUST:</t>
        <ol type="1">
          <li>
            <t>
              <strong>Verify the Delegation itself</strong> against the Trust Bundle's ordinary Approver set, enforcing its signed <tt>requirement</tt> — including the §5-step-3a <tt>signerClass</tt> registry check — and its expiry. <tt>expiresAt - sealedAt</tt> has a fixed ceiling of <strong>72 hours</strong>: a deployment MAY enforce a shorter window and MUST NOT accept a longer one. A <tt>sealedAt</tt> later than the verification time plus the §6.2 clock-skew tolerance MUST be rejected, for the reason §5a.3 rule 3 gives — a ceiling on the window's width bounds nothing about where that window sits — and, as there, unconditionally. A Delegation with no human signature (<tt>sigAlg: AUTO_APPROVED</tt>) MUST be rejected regardless of any auto-approval opt-in. The ordinary Approver set and minimum sealing requirement are those of the action being executed, resolved from the Trust Bundle's policy, and the signed sealing <tt>requirement</tt> MUST be compared against that minimum under §5 step 3d. When the Offline Intent Proof is then verified under the Delegation, the same ordinary rule is the step-3d policy for it: <tt>delegatedQuorum</tt> is already at least as strict (§5a.5). A cached successful verification does not extend the Delegation's life: its expiry MUST be checked again at approval use time under §6.2. Trust Bundle freshness MUST also still hold after collecting incident signatures.</t>
          </li>
          <li>
            <t>
              <strong>Require agreement on the action.</strong> The Delegation's <tt>target</tt>, <tt>actionType</tt> and <tt>params</tt> MUST equal those of the Offline Intent Proof being verified. A Delegation MUST NOT widen the action it was issued for.</t>
          </li>
          <li>
            <t>
              <strong>Substitute, not widen.</strong>
              <tt>delegatedTo</tt> replaces the eligible Approver set and <tt>delegatedQuorum</tt> replaces <tt>requiredApprovals</tt> for that verification, and only for it. The Offline Intent Payload's signed <tt>requiredApprovals</tt> MUST equal <tt>delegatedQuorum</tt>, so the operators still sign the policy their signatures are counted toward.</t>
          </li>
          <li>
            <t>
              <strong>Resolve delegate keys from the Trust Bundle</strong>, never from the Delegation or the Offline Intent Proof. A Delegation names identities; it does not carry key material.</t>
          </li>
          <li>
            <t>
              <strong>Enforce every constraint of §5a.3</strong> on the Offline Intent Proof unchanged.</t>
          </li>
        </ol>
        <t>A Delegation therefore narrows two things and widens none: who may approve, and for which single action.</t>
      </section>
      <section anchor="s-5a-7" numbered="false">
        <name>5a.7 Reconciliation</name>
        <t>An approval obtained offline is invisible to the approval service at the time it is granted. A deployment MUST record every offline approval locally and MUST report it to the approval service when connectivity returns, retaining the local record until the report is definitely acknowledged. An unreported approval is indistinguishable from an unauthorized action.</t>
        <t>A reported offline approval SHOULD be re-verified by the receiving service against its own record of the action and its own Approver key material, rather than accepted on the reporter's assertion. The Approver signatures make the report independently checkable; a report that cannot be checked establishes little.</t>
        <t>An offline approval MUST be surfaced to the caller under a status distinct from an ordinary approval. The prevailing caller guard is a test for the ordinary approved status, so a distinct status ensures that enabling offline approval in an existing service cannot silently begin permitting actions.</t>
      </section>
      <section anchor="s-5a-8" numbered="false">
        <name>5a.8 Security Considerations</name>
        <t>
          <strong>No capability at rest.</strong> The mechanism of §5a.2–§5a.4 leaves nothing on disk that authorizes an action. This is its principal advantage over pre-signing and the reason the remaining considerations are comparatively narrow.</t>
        <t>
          <strong>Hardware-backed authenticators.</strong> A WebAuthn assertion cannot in general be produced offline: the ceremony requires a secure context and binds to a Relying Party identifier that an offline signing surface will not satisfy. Consequently <tt>requireHardwareKey</tt> cannot be honoured offline, and §5a.3 requires such a proof to be rejected rather than accepted under a weaker signature class. A deployment that must retain offline capability for hardware-pinned actions has to provision an attested offline authenticator, which is out of scope here. A non-empty <tt>allowedAaguids</tt> is the same class of requirement: it is uncheckable offline for the reasons given in §4.3.2, and §5a.3 rejects it exactly as it rejects <tt>requireHardwareKey</tt> — an allowlist that a key with no model could satisfy would not be an allowlist.</t>
        <t>
          <strong>Out-of-band channel integrity.</strong> The payload travels to the Approver, and the signature back, across a channel this specification does not define. That channel need not be confidential — the payload carries no secret and the signature is verified cryptographically — but the Approver MUST be able to read the action they are authorizing in full, and SHOULD confirm the verification code (§4.4.4) against the operator's display. An Approver who signs an opaque blob has not approved anything.</t>
        <t>
          <strong>Local single use.</strong> Nonce redemption is stateful and local (§5, steps 10–11). Because the Relying Party generates its own nonce, single use within that Relying Party is enforceable exactly as in the online case. Two Relying Parties cannot observe each other's redemptions, so a deployment sharing one Approver set across several Relying Parties MUST scope nonces per Relying Party (§6.1).</t>
        <t>
          <strong>Delegation is a standing capability.</strong> Everything §5a.1 says about pre-signing applies to a Delegation, with one mitigation: it authorizes no action alone, so possessing the file is not sufficient to act. The residual risk is collusion between a Delegation holder and <tt>delegatedQuorum</tt> of the named operators. Deployments using Delegation SHOULD keep the window short, cap the number of live Delegations, and monitor the ratio of delegated to ordinary approvals.</t>
        <t>
          <strong>Revocation.</strong> Neither mechanism can be revoked at a Relying Party that is offline. For Offline Approval the exposure is bounded by the window cap of §5a.3 and by the fact that a human decides at incident time. For Delegation the window cap of §5a.6 is the only mitigation, which is why it is short. Both caps bound the window's width <em>and</em>, through the forward-dating rule of §5a.3 rule 3, its position — without that second half a cap bounds nothing durable, since an artifact minted today for a window opening years from now would satisfy the width ceiling and still be a capability at rest for its whole wait.</t>
        <t>
          <strong>Unforeseen incidents.</strong> Offline Approval imposes no pre-declaration: any action the Relying Party can describe can be approved offline, because the humans are in the loop when it happens. Delegation does fix the action and its parameters in advance and is therefore limited to anticipated incidents.</t>
        <t>
          <strong>Relying Party compromise.</strong> Out of scope, as in §7. Note that a Relying Party constructs its own offline challenge, so a compromised one can choose the action it asks to have approved — but it cannot obtain a signature over an action the Approvers decline, and it could equally have declined to ask at all.</t>
      </section>
    </section>
    <section anchor="s-5b" numbered="false">
      <name>5b. Agent Authority</name>
      <section anchor="s-5b-1" numbered="false">
        <name>5b.1 Motivation</name>
        <t>As agents take on delegated work, deployments need a governed, verifiable answer to "who authorized this agent to operate in this scope" — an answer that survives offline verification with no issuer secret, exactly as approvals do. An Agent Authority is that artifact: a statement of <strong>standing scope for one named agent</strong>, sealed by a human quorum through the same signing ceremony as an ordinary approval.</t>
        <t>An Agent Authority is deliberately <strong>declarative</strong>: a target, a set of action patterns, a validity window. It defines no evaluation semantics beyond substring matching and carries no expression language. It is also <strong>not a Delegation</strong> (§5a.5): a Delegation pre-authorizes WHO MAY APPROVE at incident time — hence its one-action, no-wildcard, ≤72-hour constraints — while an Authority authorizes nothing at all. Execution always still requires an ordinary Intent Proof (§5). Loosening Delegation to carry scope would have weakened the break-glass invariants; the distinct type keeps both sets of constraints intact.</t>
      </section>
      <section anchor="s-5b-2" numbered="false">
        <name>5b.2 Agent Authority Payload</name>
        <t>The canonical payload has <tt>type</tt>
          <tt>div-agent-authority</tt> and serializes under the same JCS rules as §4.1:</t>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Type</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>v</td>
              <td>uint8</td>
              <td>REQUIRED</td>
              <td>DIV protocol version. MUST equal 1.</td>
            </tr>
            <tr>
              <td>type</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>div-agent-authority</tt>.</td>
            </tr>
            <tr>
              <td>target</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Target identifier the authority is scoped to (Target Isolation).</td>
            </tr>
            <tr>
              <td>actionPatterns</td>
              <td>array of string</td>
              <td>REQUIRED, non-empty</td>
              <td>Case-insensitive substring patterns over the machine action identifier (<tt>actionType</tt>). <tt>"*"</tt> matches all. MUST be sorted ascending by UTF-16 code unit — the SET is the scope. Deliberately NOT matched against the human-readable description: the description is authored by the agent being bounded, so matching it would let an out-of-scope request cover itself by quoting a pattern in its own text.</td>
            </tr>
            <tr>
              <td>display</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Human-readable name of the authority, shown to the sealing quorum.</td>
            </tr>
            <tr>
              <td>agent</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>
                <tt>{ "did": string }</tt> — the agent the authority is ABOUT. Key-binding for the agent is introduced together with the <tt>delegated-agent</tt> signer class (§4.3.2), not here.</td>
            </tr>
            <tr>
              <td>parentReceiptHash</td>
              <td>string or null</td>
              <td>REQUIRED</td>
              <td>Domain-separated SHA-256 of the <strong>complete signed parent authority receipt</strong>, including all witnesses. <tt>null</tt> identifies a root grant. A child grant MUST name a live parent grant and fit inside its target, action scope and lifetime.</td>
            </tr>
            <tr>
              <td>requester</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>Who opened the sealing ceremony (§4.3.1).</td>
            </tr>
            <tr>
              <td>requirement</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>The sealing quorum's policy attestation (§4.3.2), including <tt>signerClass</tt>.</td>
            </tr>
            <tr>
              <td>nonce</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>The ceremony's single-use identifier.</td>
            </tr>
            <tr>
              <td>sealedAt</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC time the ceremony was opened; frozen into the bytes.</td>
            </tr>
            <tr>
              <td>expiresAt</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC end of validity. Renewal is a fresh ceremony.</td>
            </tr>
          </tbody>
        </table>
        <t>There is <strong>no 72-hour window cap</strong>: that cap exists because a Delegation pre-authorizes offline approval and cannot be revoked at an offline Relying Party. An Authority is enforced — and revoked — online by the issuing deployment; its window is deployment policy. A verifier MUST still reject a payload whose <tt>expiresAt</tt> precedes its <tt>sealedAt</tt>, and one whose <tt>sealedAt</tt> is later than the verification time plus the §6.2 clock-skew tolerance — an Authority sealed in the future was not live at that time, and §5b.3's evidence claim is precisely about liveness then (§5a.3 rule 3).</t>
        <t>The issuing deployment SHOULD floor the sealing quorum at the strictest approval rule covering any action the patterns reach, so that sealing standing scope over an action is never cheaper than approving that action once. This is a SHOULD on the issuing deployment, not a verifier check: the artifact does not carry the deployment's approval rules, so a verifier cannot re-derive the floor.</t>
      </section>
      <section anchor="s-5b-3" numbered="false">
        <name>5b.3 Agent Authority Verification</name>
        <t>A verifier MUST expose Agent Authority verification as a function <strong>separate from</strong> Intent Proof verification, and Intent Proof verification MUST reject a <tt>div-agent-authority</tt> payload outright. The result of verifying an Authority is governance evidence — "these named humans granted this agent this scope, and the grant was live at the evaluation time" — never an authorization to execute.</t>
        <t>For a delegated subagent, the verifier MUST receive the complete root-to-leaf authority receipt chain and independently trusted approver keys for every link. Each child <tt>parentReceiptHash</tt> MUST equal the digest of its verified parent receipt; the root MUST carry <tt>null</tt>. Targets MUST match, the child's validity interval MUST fit inside the parent's, and every child substring pattern MUST contain at least one parent pattern (or the parent has <tt>"*"</tt>). This is a conservative, provable subset test: ambiguous patterns are refused. The leaf's agent DID MUST equal the executing agent DID, the action type MUST match a leaf pattern, and the action intent's <tt>agent.delegatedBy</tt> MUST equal the leaf receipt digest. A scope seal still never substitutes for an action approval. An offline verifier cannot learn later revocations; the gateway's online path rejects a child if any ancestor has been revoked or expired.</t>
        <t>Verification proceeds as §5a.6 does for Delegations, with the §5-step-3a <tt>signerClass</tt> registry check and the §5-step-3d comparison against the Relying Party's own sealing policy (how many humans, with what separation of duties, must grant agent scope) applied to the sealing requirement. Without that comparison a seal proves only the quorum its sealers stated. Validate the payload type and version; validate <tt>actionPatterns</tt>, <tt>sealedAt</tt>, <tt>expiresAt</tt>; reconstruct the canonical bytes from the verifier's OWN <tt>target</tt> and <tt>agent.did</tt> (Local Payload Reconstruction — both come from the caller's policy, never from the artifact); verify each witness signature against a trust anchor resolved from local policy; count distinct approver identities against <tt>requirement.requiredApprovals</tt>; reject <tt>AUTO_APPROVED</tt>. Expiry is checked against the evaluation time; an expired Authority MAY be re-verified for audit with an explicit override (§6.2).</t>
        <t>
          <strong>Revocation is authoritative online only.</strong> An offline verifier sees validity, not revocation state. Treat a sealed Authority like a certificate, not a bearer token: the issuing deployment records seals in its witness ledger (the sealing event commits the payload digest, the agent, and the scope), revokes them there, and answers for liveness.</t>
        <t>
          <strong>Request-time enforcement (non-normative).</strong> The issuing deployment MAY use live seals as a request boundary. The reference gateway does, with a deliberately simple rule: <strong>sealing is the switch</strong> — an agent with no live seal is unbounded (every request escalates to a human, unchanged), and an agent with one or more live seals is confined to the union of its sealed scopes, with out-of-scope requests refused before a challenge exists and the refusal witnessed. Coverage is decided from the scope's <tt>target</tt> and the request's <tt>actionType</tt> alone — a bounded agent that omits <tt>actionType</tt> matches nothing but <tt>"*"</tt>. A subagent's seal counts as scope only while every ancestor seal is live; one whose ancestry was revoked or expired still bounds the agent but covers nothing, so a parent's revocation never widens a child. An in-scope request is not thereby approved; it takes the ordinary §5 path. This keeps the Authority's normative claim intact — it authorizes nothing — while making "no agent acts outside human-granted scope" an enforceable, auditable property.</t>
      </section>
    </section>
    <section anchor="s-5c" numbered="false">
      <name>5c. Platform Hash-Only Intent</name>
      <section anchor="s-5c-1" numbered="false">
        <name>5c.1 Motivation</name>
        <t>An integrating platform — a service with its own end customers and its own UI — needs the non-repudiation primitive without handing its payloads to the issuer. Its requests carry financial, personal, or payroll data; transmitting them in plaintext would make the issuer a data processor for the platform's entire customer base while adding no verification value, since the Relying Party (the platform itself, or its auditor) already holds the payload.</t>
        <t>The Platform Hash-Only Intent inverts §4.2's display model: the <strong>platform canonicalizes its own payload</strong> (under the §4.1 rules), renders its approval UI from that one serialization, and submits only the payload's digest. The issuer binds a WebAuthn ceremony to the digest, verifies the assertion against the subject's enrolled credential and the platform's <strong>own registered Relying Party</strong> (rpId/origins), and witnesses the result. The signed bytes never contain the payload.</t>
        <t>
          <strong>What shifts, stated plainly.</strong> In §4.2 the <tt>display</tt> string inside the signed bytes is the What-You-See-Is-What-You-Sign anchor, and the issuer's approval surface renders it. Here the platform's UI is the display authority: the issuer attests that <em>this enrolled key signed this digest at this time on this RP</em>, and cannot attest what the person was shown. A platform that renders one thing and hashes another defeats WYSIWYS for its own users — which is why an integration MUST derive displayed, signed and executed bytes from the single canonical serialization, and MUST NOT rebuild the payload between approval and execution. This is an integration requirement on the platform, verifiable by the platform's auditor against its own codebase, not a property the receipt can carry.</t>
      </section>
      <section anchor="s-5c-2" numbered="false">
        <name>5c.2 Platform Intent Payload</name>
        <t>The canonical payload has <tt>type</tt>
          <tt>div-platform-intent</tt> and serializes under the same JCS rules as §4.1:</t>
        <table>
          <thead>
            <tr>
              <th>Field</th>
              <th>Type</th>
              <th>Requirement</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>v</td>
              <td>uint8</td>
              <td>REQUIRED</td>
              <td>DIV protocol version. MUST equal 1.</td>
            </tr>
            <tr>
              <td>type</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>div-platform-intent</tt>.</td>
            </tr>
            <tr>
              <td>hashAlg</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>MUST equal <tt>SHA-256</tt>.</td>
            </tr>
            <tr>
              <td>payloadHash</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>Lowercase hex SHA-256 (exactly 64 characters) of the platform's canonical payload bytes. Producers MUST refuse any other form — uppercase or mixed-case hex of the same digest would produce different signed bytes for the same payload.</td>
            </tr>
            <tr>
              <td>rpId</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>The WebAuthn RP ID the signing ceremony ran on — the PLATFORM's registered Relying Party, never the issuer's. Binding it into the signed bytes ties the receipt to the surface that performed the ceremony.</td>
            </tr>
            <tr>
              <td>subject</td>
              <td>object</td>
              <td>REQUIRED</td>
              <td>
                <tt>{ "externalId": string }</tt> — the platform's opaque, tenant-scoped subject identifier. Never a global identity claim: binding this key to a legal person is the platform's claim, carried as enrollment metadata, not asserted here.</td>
            </tr>
            <tr>
              <td>signedAt</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC time the issuer froze the challenge. Informative binding — the tamper-evident time authority is the issuer's witness ledger, where challenge creation and receipt issuance are committed and anchored.</td>
            </tr>
            <tr>
              <td>expiresAt</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>RFC3339 UTC end of the challenge's validity.</td>
            </tr>
            <tr>
              <td>nonce</td>
              <td>string</td>
              <td>REQUIRED</td>
              <td>The challenge's single-use identifier (§6.1).</td>
            </tr>
          </tbody>
        </table>
        <t>The WebAuthn assertion challenge is the canonical payload's bytes in base64url, exactly as §4.4.5 defines for other payload kinds.</t>
      </section>
      <section anchor="s-5c-3" numbered="false">
        <name>5c.3 Verification</name>
        <t>A verifier MUST expose Platform Intent verification as a function <strong>separate from</strong> Intent Proof verification (<tt>verifyPlatformReceipt</tt> in the reference implementation), and Intent Proof verification MUST reject a <tt>div-platform-intent</tt> payload outright — the two attest different things, and neither may ever be mistaken for the other.</t>
        <t>The Relying Party supplies, from its own state and never from the receipt: the <tt>payloadHash</tt> it recomputes from its own copy of the canonical payload, its <tt>rpId</tt>, the ceremony <tt>nonce</tt> it is redeeming, its WebAuthn <tt>origin</tt> expectation, and the subject's trusted keys (§4.4.6 trust-anchor modes; the self-certifying DID mode applies unchanged). Verification reconstructs the canonical bytes from those values plus the receipt's <tt>signedAt</tt>/<tt>expiresAt</tt>/<tt>subject</tt>, byte-compares against the signed payload, then verifies each WebAuthn witness under §4.4.5 with the PLATFORM's origin/rpId as the expected values. Every witness MUST be a WebAuthn assertion (<tt>sigAlg</tt>
          <tt>WEBAUTHN</tt>) with user verification asserted — unconditionally: a verifier option that waives the User-Verified flag for ordinary receipts (§4.4.5 rule 2) MUST NOT apply here; <tt>AUTO_APPROVED</tt> MUST be rejected with no override — this plane has no policy pre-approval. At least one distinct verified witness is REQUIRED. Expiry follows §6.2, fail closed.</t>
        <t>
          <strong>Credential revocation is evaluated at signing time.</strong> The issuer refuses a revoked credential in every ceremony from the moment of revocation, and witnesses both the revocation (<tt>CREDENTIAL_REVOKED</tt>) and each refusal. Receipts signed before revocation remain valid; an offline verifier sees validity, not revocation state (the same bound as §5b.3's revocation note).</t>
      </section>
      <section anchor="s-5c-4" numbered="false">
        <name>5c.4 Security Considerations</name>
        <t>The origin binding is the trust boundary <strong>for a browser-mediated ceremony</strong>. A standards-compliant browser is what refuses to let a page assert an origin other than its own when it constructs <tt>clientDataJSON</tt>, so an assertion produced <em>through a browser</em> on one of the platform's registered origins is the only kind whose <tt>clientDataJSON.origin</tt> can be trusted, and §4.4.5 verification rejects anything else the browser could have produced. This binding is a property of the browser as client, not of the authenticator or of WebAuthn as a wire format: a native CTAP2 client that talks to a security key directly (bypassing a browser — e.g. a custom app built on <tt>libfido2</tt> or an equivalent) constructs its own <tt>clientDataJSON</tt> and can put any origin string in it; the key itself does not know or enforce origin, so it signs whatever it is asked to. No offline verifier can distinguish that from a genuine browser assertion after the fact — the guarantee holds only for the class of client that cannot lie about where the interaction happened, and general-purpose end-user devices are not restricted to that class. Enrollment MUST therefore verify the attestation against the registered RP configuration, and registration of that configuration is a privileged, witnessed act; neither closes this residual.</t>
        <t>Because credentials are scoped to the platform's RP ID, one person enrolled by two platforms holds two unrelated keypairs and two subject identities; nothing in this profile links them — deliberate data minimization, and the §1.2 non-goal (DIV is not an identity system) applies with extra force.</t>
        <t>
          <strong>Implementation status.</strong> TypeScript, Go, Rust, Python and Java implement <tt>div-platform-intent</tt> through dedicated platform-receipt verifiers. Their ordinary approval verifiers continue to refuse this type: a platform signature is never an ordinary action approval. Shared verifier parity fixtures pin successful verification and digest/RP/origin/subject/nonce refusals.</t>
      </section>
    </section>
    <section anchor="s-6" numbered="false">
      <name>6. Replay Protection and Expiration</name>
      <section anchor="s-6-1" numbered="false">
        <name>6.1 Nonce Requirements</name>
        <t>The nonce MUST be unique within the replay-protection scope of the Relying Party.</t>
        <t>The Relying Party SHOULD generate the nonce whenever approval requests originate from untrusted requesters.</t>
        <t>A redeemed nonce MUST remain unavailable for reuse until the associated proof expiration time has elapsed. Recording and enforcing redemption is a stateful Relying Party responsibility (see the note on §5 steps 10–11) and is distinct from the stateless cryptographic verification of the Proof Envelope.</t>
      </section>
      <section anchor="s-6-2" numbered="false">
        <name>6.2 Expiration Validation</name>
        <t>The Relying Party MUST reject proofs where the current time exceeds expiresAt.</t>
        <t>
          <strong>Timestamp syntax.</strong> Every signed timestamp (<tt>expiresAt</tt>, <tt>challengedAt</tt>, <tt>sealedAt</tt>, <tt>signedAt</tt>) MUST be an RFC 3339 §5.6 <tt>date-time</tt>, and a verifier MUST refuse any other spelling rather than guess at it: exactly <tt>YYYY-MM-DDTHH:MM:SS</tt>, an optional fraction of one to nine digits introduced by <tt>.</tt>, and a zone of <tt>Z</tt> or <tt>±HH:MM</tt>; <tt>T</tt> and <tt>Z</tt> uppercase; the date MUST exist (30 February and a non-leap 29 February are refused); hours <tt>00</tt>–<tt>23</tt>, minutes and seconds <tt>00</tt>–<tt>59</tt> (no leap second); offset hours <tt>00</tt>–<tt>23</tt> and offset minutes <tt>00</tt>–<tt>59</tt>. A numeric offset denotes the same instant as its UTC equivalent. A bare date, a zone-less time — which a lenient parser reads in the verifier host's own time zone, so the verdict moved with the machine — a space or lowercase separator, a comma fraction and an expanded year are all refused. The reference producer emits <tt>YYYY-MM-DDTHH:MM:SS.sssZ</tt>, which every conformant verifier accepts.</t>
        <t>Implementations SHOULD support configurable clock-skew tolerance.</t>
        <t>A default tolerance of ±30 seconds is RECOMMENDED.</t>
        <t>A verifier MAY support re-verifying an expired proof for post-hoc audit or forensics, behind an explicit per-call override (<tt>allowExpired</tt> in the reference implementation, available on intent, delegation, and agent-authority verification alike). The result of such a re-verification is evidence for the record — "this was validly signed while it was live" — never authorization to execute: Invariant 4's fail-closed rule binds execution regardless of the override.</t>
      </section>
    </section>
    <section anchor="s-7" numbered="false">
      <name>7. Security Considerations</name>
      <t>DIV provides protection against:</t>
      <ul>
        <li>
          <t>Modification of approved execution parameters.</t>
        </li>
        <li>
          <t>Replay of approvals against unintended targets.</t>
        </li>
        <li>
          <t>Unauthorized execution using valid standing credentials.</t>
        </li>
        <li>
          <t>Transport-layer alteration of intent artifacts.</t>
        </li>
      </ul>
      <t>DIV does not provide protection against:</t>
      <ul>
        <li>
          <t>Compromise of the Approver private signing key.</t>
        </li>
        <li>
          <t>Malicious approval by a trusted Approver.</t>
        </li>
        <li>
          <t>A weaker <tt>requirement</tt> authored by the signers themselves, unless the Relying Party supplies its own approval policy (§5 step 3d). Without that policy a verifier proves only the quorum the signers stated.</t>
        </li>
        <li>
          <t>Compromise of the Relying Party execution environment.</t>
        </li>
        <li>
          <t>Incorrect interpretation of valid parameters by the executing application.</t>
        </li>
      </ul>
      <t>The Approver interface SHOULD display the exact execution parameters or an equivalent deterministic rendering before signature generation to reduce blind-signing risk.</t>
    </section>
    <section anchor="s-7a" numbered="false">
      <name>7a. Reference Test Vectors</name>
      <t>Compliant implementations MUST pass the official cross-language golden vectors, published in this repository as <tt>packages/mcp-schemas/vectors/canonical-vectors.json</tt> (the companion DEWP set is <tt>ledger-vectors.json</tt>; see DEWP §10). <tt>verifier-parity-vectors.json</tt> in the same directory is also part of the conformance set: it pins executable <strong>verdicts</strong> rather than bytes — each case fixes the <tt>ok</tt> result and, where present, the signers and a required fragment of the refusal reason, so a refusal that lands for the wrong rule fails visibly instead of reading as green. The shared <tt>webauthn-vector.json</tt> in the same directory is part of the conformance set: it pins the §4.4.5 WEBAUTHN witness path — the unpadded-base64url wire encodings of §4.4.2, origin/RP-ID pinning, and the <tt>clientDataJSON.challenge</tt> binding — for every port that verifies WebAuthn witnesses. <strong>These files are the normative source</strong>, so a port that drifts from them fails visibly rather than at a relying party's site. <tt>canonical-vectors.json</tt> is consumed by the TypeScript canonical implementation and verifier and by the Go, Rust, Python and Java ports. <tt>webauthn-vector.json</tt> is <em>produced</em> by the TypeScript implementation and consumed by the Go, Rust, Java and Python ports; the reference TypeScript verifier implements §4.4.5 but pins it with its own fixtures rather than this file — which is how the §4.4.2 encoding it defines came to be tightened in TypeScript and ship unmirrored in three ports. A TypeScript consumer for the shared WebAuthn vector is a known gap, not an exemption.</t>
      <t>The vectors pin, among other things:</t>
      <ul>
        <li>
          <t>Canonical serialization (<tt>stableStringify</tt>) including the cross-language number-portability rules, UTF-16 key ordering with astral-plane keys, and HTML-sensitive characters.</t>
        </li>
        <li>
          <t>The canonical bytes of all five payload kinds — <tt>div-intent-verification</tt>, <tt>div-offline-intent</tt> (§5a.2), <tt>div-delegation</tt> (§5a.5), <tt>div-agent-authority</tt> (§5b.2) and <tt>div-platform-intent</tt> (§5c.2) — including that no kind can verify as another and that <tt>allowedAaguids</tt>, <tt>delegatedTo</tt> and <tt>actionPatterns</tt> are canonicalized as sorted sets. Every pinned <tt>requirement</tt> block carries <tt>signerClass</tt> (§4.3.2). All five builders are pinned in every port. Dedicated verifiers check §5b and §5c artifacts; the ordinary approval verifier still refuses them, as pinned by the receipt fixtures <tt>agent-authority-refused-by-approval-verifier</tt> and <tt>platform-intent-refused-by-approval-verifier</tt>.</t>
        </li>
        <li>
          <t>
            <strong>Denial payloads</strong> (§4.3.3) for each of the three ceremony kinds, pinning both the derivation and the property the derivation exists for: the denial bytes never equal the approval bytes they negate, so a signature over one cannot be presented as the other. Consumed by the TypeScript implementation only — denial witnesses are ledger entries verified by DEWP leaf recomputation, not Proof Envelopes, so a Core Profile verifier has nothing to check here.</t>
        </li>
        <li>
          <t>Quorum fixtures are evaluated under an <strong>identity-associating</strong> anchor (§4.4.6); a key-set anchor has no identities to be distinct about, so passing them in that mode is not evidence that §4.4.2 is satisfied.</t>
        </li>
        <li>
          <t>Signed <strong>receipt</strong> fixtures a verifier must accept or refuse as committed: single-signature (raw-P1363 and DER ECDSA encodings), tampered parameters, <tt>AUTO_APPROVED</tt> refusal, and a missing requester block. Nine refusal fixtures carry a VALID signature over their own bytes so the rule under test is the only gate: a Delegation presented to the approval verifier (§5a.5), an Agent Authority presented to the approval verifier (§5b.3), a Platform Hash-Only Intent presented to the approval verifier (§5c.3 — pinned in every port, like the §5b case), an intent whose signed <tt>requirement.signerClass</tt> is the unknown <tt>delegated-agent</tt> value (§5-step-3a's registry rule — refused, never treated as human), and five pinning the reserved <tt>evidence</tt> field of §4.3.4 (§5-step-3c).</t>
        </li>
        <li>
          <t>
            <strong>Evidence fixtures</strong> (§4.3.4, §5-step-3c). <tt>evidence-null-verifies</tt> is the positive control; <tt>evidence-missing-refused</tt> strips the key and re-signs, so the refusal is the presence rule and not a broken signature; <tt>evidence-empty-array-refused</tt>, <tt>evidence-empty-object-refused</tt> and <tt>evidence-arbitrary-value-refused</tt> pin that <tt>[]</tt>, <tt>{}</tt> and a populated value are each refused rather than normalized to <tt>null</tt>. <tt>evidence-mutated-after-signing-refused</tt> is the exception that does NOT re-sign: it pins that the check runs before Local Payload Reconstruction, so the refusal names the unsupported payload shape instead of reporting a parameter mismatch. The same six cases appear in <tt>verifier-parity-vectors.json</tt> under <tt>approvals</tt>, where each additionally pins a fragment of the refusal reason.</t>
        </li>
        <li>
          <t>
            <strong>Quorum receipts</strong> pinning §4.4.2/§5-step-7: distinct approver <strong>identities</strong> are counted, never signature entries (one approver's two registered credentials are one approval), and <tt>requesterCannotApprove</tt> excludes the requester's own signature.</t>
        </li>
        <li>
          <t>
            <strong>Offline and delegation receipts</strong> pinning the §5a.3 refuse-by-default opt-in, the 60-minute offline window cap, and the 72-hour delegation window cap — each including a validly signed proof whose signed window exceeds the cap and MUST be rejected anyway. Every case in these two sections carries an explicit <tt>asOf</tt> (RFC3339 UTC) that the consumer MUST pass to its verifier as the evaluation time. The fixtures are dated far in the future so they never expire, which makes them forward-dated relative to a real clock; <tt>asOf</tt> sits between each case's <tt>challengedAt</tt>/<tt>sealedAt</tt> and its <tt>expiresAt</tt>, so one file can pin both the width caps and the §5a.3 rule 3 position rule. Each section also carries a case whose bytes are those of its accepted sibling, evaluated at an <tt>asOf</tt> BEFORE the signed <tt>challengedAt</tt>/<tt>sealedAt</tt> — validly signed, inside every width cap, and MUST be rejected as forward-dated.</t>
        </li>
        <li>
          <t>A <strong>
              <tt>requiredApprovals: 0</tt>
            </strong> intent receipt carrying a valid signature, which MUST be rejected on the §4.3.2 minimum rather than passing §5-step-7 vacuously.</t>
        </li>
        <li>
          <t>
            <strong>Requirement-floor verdicts</strong> (§5 step 3d), in <tt>verifier-parity-vectors.json</tt>. They reuse already signed receipts, because the policy is a verifier input and never signed bytes: the <tt>requirement-floor-*</tt> cases under <tt>approvals</tt> pin that a signed 1-of-1 requirement verifies with no floor (the signers' own quorum), is refused under a floor raising the quorum, requiring four-eyes or requiring a hardware key, and is accepted under an equal floor — with an equal hardware floor accepted for a device-bound WebAuthn witness. <tt>authority-requirement-floor-*</tt> under <tt>agentAuthority</tt> pin the same for a sealing requirement. Every refusal pins the reason fragment <tt>weaker than the relying party's policy</tt>.</t>
        </li>
        <li>
          <t>
            <strong>Verifier input handling</strong>, in the self-contained <tt>verifierInputHardening</tt> section of <tt>verifier-parity-vectors.json</tt> (own keys): each accepted and refused §6.2 timestamp spelling, an offline <tt>challengedAt</tt> without a zone, a <tt>topOrigin</tt> that differs from <tt>origin</tt> (§4.4.5 rule 5), a platform receipt without user verification under a caller waiver (§5c.3), two DIDs sharing one key (§4.4.6) and an agent authority whose signed pattern carries an unpaired-surrogate escape (§4.1). The file stays I-JSON: the escape lives inside the <tt>canonicalPayload</tt> text, never as a raw string.</t>
        </li>
        <li>
          <t>The <tt>verificationCode</tt> derivation of §4.4.4 and the payload digest.</t>
        </li>
      </ul>
      <t>The signing keys and ECDSA signatures inside the file are regenerated whenever the vectors are — they are test fixtures, not trust anchors — but every committed signature remains verifiable against the committed key in the same file. Trust Bundles (§5a.4) are not vectored; their reference format is documented in that section.</t>
    </section>
    <section anchor="s-8" numbered="false">
      <name>8. IANA Considerations</name>
      <t>This document requires no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>Normative References</name>
      <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author fullname="S. Bradner" initials="S." surname="Bradner" />
          <date month="March" year="1997" />
        </front>
        <seriesInfo name="BCP" value="14" />
        <seriesInfo name="RFC" value="2119" />
        <seriesInfo name="DOI" value="10.17487/RFC2119" />
      </reference>
      <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author fullname="B. Leiba" initials="B." surname="Leiba" />
          <date month="May" year="2017" />
        </front>
        <seriesInfo name="BCP" value="14" />
        <seriesInfo name="RFC" value="8174" />
        <seriesInfo name="DOI" value="10.17487/RFC8174" />
      </reference>
      <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785">
        <front>
          <title>JSON Canonicalization Scheme (JCS)</title>
          <author fullname="A. Rundgren" initials="A." surname="Rundgren" />
          <author fullname="B. Jordan" initials="B." surname="Jordan" />
          <author fullname="S. Erdtman" initials="S." surname="Erdtman" />
          <date month="June" year="2020" />
        </front>
        <seriesInfo name="RFC" value="8785" />
        <seriesInfo name="DOI" value="10.17487/RFC8785" />
      </reference>
      <reference anchor="RFC7493" target="https://www.rfc-editor.org/info/rfc7493">
        <front>
          <title>The I-JSON Message Format</title>
          <author fullname="T. Bray" initials="T." role="editor" surname="Bray" />
          <date month="March" year="2015" />
        </front>
        <seriesInfo name="RFC" value="7493" />
        <seriesInfo name="DOI" value="10.17487/RFC7493" />
      </reference>
      <reference anchor="RFC3339" target="https://www.rfc-editor.org/info/rfc3339">
        <front>
          <title>Date and Time on the Internet: Timestamps</title>
          <author fullname="G. Klyne" initials="G." surname="Klyne" />
          <author fullname="C. Newman" initials="C." surname="Newman" />
          <date month="July" year="2002" />
        </front>
        <seriesInfo name="RFC" value="3339" />
        <seriesInfo name="DOI" value="10.17487/RFC3339" />
      </reference>
      <reference anchor="Artifacts" target="https://www.intyga.com/specs/v1.0.0/CHECKSUMS.sha256">
        <front>
          <title>DIV and DEWP version 1.0.0 schemas and conformance vectors</title>
          <author>
            <organization>Janbjer Technologies AB</organization>
          </author>
          <date year="2026" month="October" day="4" />
        </front>
      </reference>
      <reference anchor="WebAuthn" target="https://www.w3.org/TR/2026/REC-webauthn-3-20260825/">
        <front>
          <title>Web Authentication: An API for accessing Public Key Credentials - Level 3</title>
          <author>
            <organization>W3C</organization>
          </author>
          <date year="2026" month="August" day="25" />
        </front>
      </reference>
    </references>
    <references>
      <name>Informative References</name>
      <reference anchor="FIDO2" target="https://fidoalliance.org/fido2/">
        <front>
          <title>FIDO2</title>
          <author>
            <organization>FIDO Alliance</organization>
          </author>
        </front>
      </reference>
      <reference anchor="DID-CORE" target="https://www.w3.org/TR/did-core/">
        <front>
          <title>Decentralized Identifiers (DIDs) v1.0</title>
          <author>
            <organization>W3C</organization>
          </author>
        </front>
      </reference>
      <reference anchor="SPIFFE" target="https://spiffe.io/">
        <front>
          <title>SPIFFE/SPIRE</title>
          <author>
            <organization>SPIFFE</organization>
          </author>
        </front>
      </reference>
      <reference anchor="MCP" target="https://modelcontextprotocol.io/">
        <front>
          <title>Model Context Protocol (MCP)</title>
          <author>
            <organization>Model Context Protocol</organization>
          </author>
        </front>
      </reference>
      <reference anchor="I-D.williams-intent-token" target="https://datatracker.ietf.org/doc/html/draft-williams-intent-token-02">
        <front>
          <title>The Intent Token: A Cryptographic Authorization Primitive for Autonomous Agents</title>
          <author fullname="Jeffrey Williams" initials="J." surname="Williams">
            <organization>Independent</organization>
          </author>
          <date day="4" month="September" year="2026" />
        </front>
        <seriesInfo name="Internet-Draft" value="draft-williams-intent-token-02" />
        <annotation>"The Intent Token: A Cryptographic Authorization Primitive for Autonomous Agents" (Individual Internet-Draft, non-normative). Addresses a related pre-execution authorization problem via a JWT-based token; DIV differs by remaining transport- and identity-agnostic and by requiring local payload reconstruction (§3, Invariant 2) rather than trusting an embedded claim set.</annotation>
      </reference>
      <reference anchor="RFC8792" target="https://www.rfc-editor.org/info/rfc8792">
        <front>
          <title>Handling Long Lines in Content of Internet-Drafts and RFCs</title>
          <author fullname="K. Watsen" initials="K." surname="Watsen" />
          <author fullname="E. Auerswald" initials="E." surname="Auerswald" />
          <author fullname="A. Farrel" initials="A." surname="Farrel" />
          <author fullname="Q. Wu" initials="Q." surname="Wu" />
          <date month="June" year="2020" />
        </front>
        <seriesInfo name="RFC" value="8792" />
        <seriesInfo name="DOI" value="10.17487/RFC8792" />
      </reference>
      <reference anchor="DEWP" target="https://datatracker.ietf.org/doc/html/draft-janbjer-dewp-00">
        <front>
          <title>Deterministic Evidence &amp; Witness Protocol (DEWP) Specification</title>
          <author fullname="Christian Janbjer" initials="C." surname="Janbjer" />
          <date year="2026" month="October" />
        </front>
        <seriesInfo name="Internet-Draft" value="draft-janbjer-dewp-00" />
      </reference>
    </references>
  </back>
</rfc>
