<?xml version="1.0" encoding="utf-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     docName="draft-schrock-ep-quorum-04"
     category="info" ipr="trust200902" submissionType="IETF"
     version="3" tocInclude="true" sortRefs="true" symRefs="true">
  <front>
    <title abbrev="EP Multi-Party Quorum">Multi-Party Quorum Authorization for High-Risk Agent Actions (EP-QUORUM)</title>
    <seriesInfo name="Internet-Draft" value="draft-schrock-ep-quorum-04"/>
    <author fullname="Iman Schrock">
      <organization>EMILIA Protocol, Inc.</organization>
      <address>
        <postal>
          <country>US</country>
        </postal>
        <email>team@emiliaprotocol.ai</email>
      </address>
    </author>
    <date year="2026" month="September" day="6"/>
    <area>sec</area>
    <keyword>AI agents</keyword>
    <keyword>authorization</keyword>
    <keyword>multi-party</keyword>
    <keyword>quorum</keyword>
    <keyword>two-person rule</keyword>
    <keyword>separation of duties</keyword>
    <keyword>WebAuthn</keyword>
    <abstract>
      <t>This document defines a multi-party approval predicate over
      action-bound human signoffs: valid signatures, admitted roles, distinct
      approvers and keys, threshold, and an optional ordered trail. The relying
      party pins the governing policy and approver directory independently.
      Passing the predicate is approval evidence, not a complete authorization
      decision, proof of execution, or proof of unused authority.</t>
      <t>This revision repairs the strong ordered profile. A successor signs a
      digest of the completed predecessor signoff, including its signature,
      rather than a precomputable context. The versioned profile establishes
      causal dependence on a completed prior proof under the cryptographic
      assumptions; it does not establish trusted wall-clock time or human
      comprehension. Legacy context-only chains cannot satisfy it. JavaScript,
      Python, and Go reference verifiers share a corpus in one repository.
      Agreement is a same-team consistency check, not independent
      interoperability evidence or a formal proof of the new construction.</t>
    </abstract>
  </front>
  <middle>
    <section anchor="introduction">
      <name>Introduction</name>
      <t>The base EP authorization receipt closes the gap between "is this
      actor authorized in general?" and "should this exact action happen,
      and which accountable human said yes?" by binding one named
      approver's device-held signature to one exact action (see
      <xref target="EP-RECEIPTS"/>). For the
      highest-consequence actions, one approver is not the right control.
      The discipline that governs nuclear release, large-value treasury
      movement, and production-credential change is the
      <em>two-person rule</em>: no single human -- however
      well-authenticated, however senior -- can unilaterally cause the
      action. Two or more distinct, accountable humans must each
      independently authorize, and the action proceeds only when all of
      them have.</t>
      <t>As autonomous agents acquire credentials sufficient for
      irreversible operations, the two-person rule is exactly the control
      that lets an organization grant an agent real authority without
      creating a single point of failure: a compromised, misaligned, or
      prompt-injected agent cannot act alone, and neither can a single
      compromised or coerced approver. EP-QUORUM specifies how to express
      that control as a cryptographic predicate over EP signoffs and how to
      enforce it both at the moment each approver signs and at the moment
      the action would execute.</t>
      <t>The base draft already contemplates multi-approver policies
      (<xref target="EP-RECEIPTS"/>): each approver signs an
      individual Authorization Context sharing the same action hash, and
      commitment occurs only when k valid, distinct signoffs exist before
      expiry. This document makes that sketch normative and testable. It
      adds: ordered approval trails (<xref target="approval-modes"/>); an
      explicit role roster and admission semantics
      (<xref target="quorum-policy"/>); a bounded approval window with an
      asserted-time consistency constraint for ordered mode
      (<xref target="approval-modes"/>); an incremental server-side
      admission rule that keeps a non-conforming signer out of the trail in
      the first place (<xref target="incremental-admission"/>); the
      consolidated fail-closed quorum predicate
      (<xref target="quorum-gate"/>); and an adversarial conformance suite
      (<xref target="conformance"/>).</t>
      <section anchor="design-goals">
        <name>Design Goals</name>
        <t>EP-QUORUM inherits design goals G1-G7 of the base draft and
        adds:</t>
        <ul>
          <li><strong>Q1 -- Additivity.</strong> A quorum is a set of
          native signoffs over the same action hash. No new signature
          algorithm is introduced. The completed-signoff chain is an explicit
          signed-context profile extension; it does not silently extend a
          closed base receipt schema. A single-approver policy is the
          one-member quorum.</li>
          <li><strong>Q2 -- Fail-closed.</strong> The quorum predicate is not
          satisfied unless <em>every</em> element of the predicate holds.
          Absence of evidence, an unparseable member, a malformed policy,
          or any single failed check yields "not satisfied," never
          "satisfied."</li>
          <li><strong>Q3 -- Distinctness (separation of duties at the human
          level).</strong> A quorum of size k requires k pairwise-distinct
          human approvers, each distinct from the initiator. One human
          <bcp14>MUST NOT</bcp14> fill two slots.</li>
          <li><strong>Q4 -- Incremental enforcement.</strong> The protocol
          enforces conformance as each approver signs, not only at consume
          time, so that a wrong-action, wrong-role, duplicate,
          out-of-order, stale, or invalid signature never becomes part of
          the trail.</li>
          <li><strong>Q5 -- Offline-verifiable quorum.</strong> The
          satisfied/not-satisfied judgment is computable from the receipt's
          members and the policy alone, under the same offline verification
          model as the base draft (<xref target="EP-RECEIPTS"/>).</li>
        </ul>
      </section>
    </section>

    <section anchor="terminology">
      <name>Terminology</name>
      <t>In addition to the terminology of <xref target="EP-RECEIPTS"/>:</t>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
      "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>",
      "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>",
      "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>",
      "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and
      "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
      described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/>
      when, and only when, they appear in all capitals, as shown here.</t>
      <dl>
        <dt>Quorum.</dt>
        <dd>The set of distinct approver signoffs required to satisfy one
        human-authorization evidence requirement for an action under a Quorum
        Policy.</dd>
        <dt>Quorum Policy.</dt>
        <dd>A named, versioned rule set declaring the approval mode, the
        required count, the roster of eligible (role, approver) slots, the
        distinct-humans rule, and the approval window. Carried in the
        policy that governs the action; see
        <xref target="quorum-policy"/>.</dd>
        <dt>Member.</dt>
        <dd>One element of a candidate quorum: a (role, approver public
        key, signoff) triple, where the native signoff uses the verification
        machinery of <xref target="EP-RECEIPTS"/> over the Authorization
        Context the approver signed.</dd>
        <dt>Trail.</dt>
        <dd>The ordered sequence of members admitted so far for one action
        -- the partial quorum under construction.</dd>
        <dt>Quorum Gate.</dt>
        <dd>The fail-closed predicate (<xref target="quorum-gate"/>) that
        decides whether a trail is a satisfied quorum. The Verifying
        Executor (<xref target="EP-RECEIPTS"/>)
        <bcp14>MUST</bcp14> consult it before performing the action.</dd>
      </dl>
    </section>

    <section anchor="quorum-policy">
      <name>The Quorum Policy</name>
      <t>A Quorum Policy is a JSON object:</t>
      <sourcecode type="json"><![CDATA[
{
  "mode": "ordered",
  "required": 3,
  "approvers": [
    {
      "role": "program_officer",
      "approver": "ep:approver:po_rivera"
    },
    {
      "role": "authorizing_official",
      "approver": "ep:approver:ao_chen"
    },
    {
      "role": "inspector_general",
      "approver": "ep:approver:ig_okafor"
    }
  ],
  "distinct_humans": true,
  "window_sec": 900
}
]]></sourcecode>
      <t>Members:</t>
      <table>
        <thead>
          <tr><th>Field</th><th>Required</th><th>Type</th><th>Meaning</th></tr>
        </thead>
        <tbody>
          <tr>
            <td><tt>mode</tt></td>
            <td><bcp14>REQUIRED</bcp14></td>
            <td>string (enum)</td>
            <td><tt>threshold</tt> or <tt>ordered</tt>
            (<xref target="approval-modes"/>).</td>
          </tr>
          <tr>
            <td><tt>required</tt></td>
            <td><bcp14>REQUIRED</bcp14></td>
            <td>integer &gt;= 1</td>
            <td>The quorum size k.</td>
          </tr>
          <tr>
            <td><tt>approvers</tt></td>
            <td><bcp14>REQUIRED</bcp14></td>
            <td>array</td>
            <td>The roster of eligible {role, approver} slots. <bcp14>MUST</bcp14>
            be non-empty.</td>
          </tr>
          <tr>
            <td><tt>distinct_humans</tt></td>
            <td><bcp14>OPTIONAL</bcp14> (default true)</td>
            <td>boolean</td>
            <td>When true, no approver identifier may fill more than one
            slot. Device-key distinctness is unconditional
            (<xref target="quorum-gate"/>, checks 5 and 5a). Implementations
            <bcp14>MUST</bcp14> treat a missing value as true.</td>
          </tr>
          <tr>
            <td><tt>window_sec</tt></td>
            <td><bcp14>OPTIONAL</bcp14> (default 900)</td>
            <td>integer &gt; 0</td>
            <td>Maximum span, in seconds, between the first and any later
            admitted signature.</td>
          </tr>
          <tr>
            <td><tt>ordered_chain</tt></td>
            <td><bcp14>OPTIONAL</bcp14> (default false)</td>
            <td>boolean</td>
            <td>When true in ordered mode, requires the completed-signoff
            chain in <xref target="approval-modes"/>. Context-only links do
            not satisfy this requirement.</td>
          </tr>
          <tr>
            <td><tt>ordered_chain_profile</tt></td>
            <td>Required when <tt>ordered_chain</tt> is true</td>
            <td>string</td>
            <td>Exactly <tt>EP-QUORUM-SIGNOFF-CHAIN-v1</tt>. A missing or
            unrecognized profile fails the strong-chain check.</td>
          </tr>
        </tbody>
      </table>
      <t>Rules:</t>
      <ul>
        <li>An <tt>approver</tt> slot identifies an eligible approver in the
        Approver Directory (<xref target="EP-RECEIPTS"/>). The
        <tt>role</tt> is the organizational role under which that approver
        is admitted to this quorum; it is the unit of role eligibility
        (<xref target="quorum-gate"/>, check 4).</li>
        <li>A member is admitted only if its (role, approver) pair is
        present in the roster. A correct signature by a real, enrolled
        approver who is not on the roster for this action
        <bcp14>MUST</bcp14> be rejected (<tt>wrong_role</tt>).</li>
        <li><tt>required</tt> <bcp14>MUST NOT</bcp14> exceed the number of
        distinct human approvers the roster can supply under
        <tt>distinct_humans</tt>. A policy that cannot be satisfied is a
        misconfiguration; verifiers treat an unsatisfiable trail as not
        satisfied, as always.</li>
        <li>The Quorum Policy is part of the action's governing policy and
        is therefore committed by the <tt>policy_hash</tt> of every
        member's Authorization Context (<xref target="EP-RECEIPTS"/>). A signature collected under one Quorum Policy version
        <bcp14>MUST NOT</bcp14> satisfy a requirement evaluated under
        another. The relying party <bcp14>MUST</bcp14> obtain the complete
        expected policy, including the chain profile, from an authenticated
        source outside the supplied quorum. It <bcp14>MUST</bcp14> reject a
        mismatching supplied policy before crediting the quorum; a matching
        artifact-supplied policy is not itself an authority source.</li>
      </ul>
    </section>

    <section anchor="approval-modes">
      <name>Approval Modes</name>
      <t><strong>Threshold mode.</strong> Any <tt>required</tt> distinct
      eligible approvers satisfy the count requirement. Presentation order
      has no causal meaning.</t>
      <t><strong>Plain ordered mode.</strong> Each presented member matches
      the corresponding roster slot in role and approver. The number
      presented is at least <tt>required</tt> and no greater than the roster
      size. Thus ordered k-of-n admits the first k slots, not automatically
      all n. Presented <tt>issued_at</tt> values <bcp14>MUST</bcp14> strictly
      increase and their span <bcp14>MUST</bcp14> fit <tt>window_sec</tt>.
      These are checks on asserted metadata, not proof of actual signing
      order or trusted elapsed time.</t>
      <t><strong>Completed-signoff chain.</strong> A relying party requiring
      causal dependence between approvals <bcp14>MUST</bcp14> pin
      <tt>mode: "ordered"</tt>, <tt>ordered_chain: true</tt>, and
      <tt>ordered_chain_profile: "EP-QUORUM-SIGNOFF-CHAIN-v1"</tt> in the
      governing policy. It <bcp14>MUST NOT</bcp14> infer this requirement
      from whichever fields the supplied artifact happens to retain.</t>
      <t>Let S[i] be the complete predecessor signoff JSON object, including
      its signed context and native proof fields, with the actual signature
      encoded exactly as carried. Define:</t>
      <sourcecode type="pseudocode"><![CDATA[
D = UTF8("EP-QUORUM-SIGNOFF-CHAIN-v1") || 0x00
link(S) = lowercase_hex(SHA-256(D || UTF8(JCS(S))))
]]></sourcecode>
      <t>JCS is the JSON Canonicalization Scheme <xref target="RFC8785"/>.
      The separator is one zero octet, not printable backslash and zero.
      Hash the complete signoff, not the member wrapper, the predecessor
      context alone, or a reconstructed proof that drops fields.</t>
      <t>The first signed context <bcp14>MUST</bcp14> omit
      <tt>prev_signoff_hash</tt>. Each successor signed context
      <bcp14>MUST</bcp14> carry <tt>prev_signoff_hash = link(S[i-1])</tt>.
      Every context <bcp14>MUST</bcp14> omit the legacy
      <tt>prev_context_hash</tt> field; mixed links and a null first
      predecessor are invalid. Every native signature and the full quorum
      predicate <bcp14>MUST</bcp14> also verify. A digest match alone is
      insufficient.</t>
      <t>The successor challenge depends on a completed predecessor proof,
      not merely its public context. Substituting a different valid
      predecessor signature over the same context changes the link and
      requires a new successor signature. This establishes a dependency
      between proof artifacts under signature-unforgeability and hash
      preimage/collision-resistance assumptions. It does not identify a
      trusted signing time, establish that the human saw the prior proof,
      or prove understanding or voluntary approval.</t>
      <section anchor="chain-migration">
        <name>Migration from context-only links</name>
        <t>Revisions through -03 used <tt>prev_context_hash</tt>. All such
        contexts can be prepared before any predecessor signs, then signed
        in reverse order and presented in roster order. They do not prove
        the causal property previously claimed. Under this revision's
        strong requirement a verifier <bcp14>MUST</bcp14> reject missing or
        unknown profiles and every context-only chain, even when all
        member signatures verify.</t>
        <t>An existing chain <bcp14>MUST NOT</bcp14> be upgraded by changing
        its policy label or inserting unsigned hashes. Fresh contexts and
        signatures are required. Historical evidence may be evaluated
        under an explicitly permitted weaker threshold or plain ordered
        policy, but <bcp14>MUST NOT</bcp14> be credited as a completed-signoff
        chain. This compatibility break does not change the native
        signature algorithms or threshold checks.</t>
      </section>
    </section>

    <section anchor="quorum-gate">
      <name>The Quorum Gate (fail-closed predicate)</name>
      <t>A trail is a <strong>satisfied quorum</strong> for an action with
      hash H under policy P if and only if ALL of the following hold. A
      verifier <bcp14>MUST</bcp14> return "satisfied" only when every check
      passes, and <bcp14>MUST</bcp14> return "not satisfied" on the first
      failure, on a malformed policy or member, or on any unrecognized
      condition (Q2):</t>
      <ol>
        <li><strong>Well-formed policy.</strong> P has a recognized
        <tt>mode</tt>, an integer <tt>required &gt;= 1</tt>, and a non-empty
        <tt>approvers</tt> roster. Otherwise: not satisfied.</li>
        <li><strong>All signatures valid.</strong> For every member, the EP
        signoff verifies under <xref target="EP-RECEIPTS"/> -- the WebAuthn <xref target="WEBAUTHN"/>
        assertion (Class A) verifies against the member's
        <tt>approver_public_key</tt>, with the
        assertion challenge equal to the member's context hash and user
        verification asserted. One invalid signature
        (<tt>one_bad_signature</tt>) fails the whole quorum.</li>
        <li><strong>Action binding.</strong> Every member's Authorization
        Context carries <tt>action_hash == H</tt>
        (<tt>action_mismatch</tt>). A member bound to any other action does
        not count.</li>
        <li><strong>Role admission.</strong> Every member's (role,
        approver) pair is present in the roster
        (<tt>wrong_role</tt>).</li>
        <li><strong>Distinct humans.</strong> When <tt>distinct_humans</tt>
        is true (the default), approvers are pairwise distinct and (per the
        base draft's SelfApprovalImpossible) distinct from the initiator
        (<tt>duplicate_human</tt>).</li>
        <li><strong>Distinct keys (check 5a).</strong> Regardless of
        <tt>distinct_humans</tt>, the admitted members' canonically decoded
        <tt>approver_public_key</tt> values are pairwise distinct
        (<tt>duplicate_key</tt>). This defends against a single device key
        enrolled under two approver identifiers, which would otherwise pass
        the distinct-humans check by name while being one signer.</li>
        <li><strong>Threshold.</strong> At least <tt>required</tt> admitted
        members exist (<tt>under_threshold</tt>).</li>
        <li><strong>Order (ordered mode only).</strong> The i-th admitted
        member matches <tt>approvers[i-1]</tt>; signature times are strictly
        increasing (<tt>out_of_order</tt>,
        <tt>non_increasing_time</tt>).</li>
        <li><strong>Ordering chain (strong ordered mode only, check 7a).</strong>
        When <tt>ordered_chain</tt> is true, the exact pinned profile and
        every completed-signoff link satisfy <xref target="approval-modes"/>
        (<tt>broken_chain</tt>). A missing or unknown profile, a legacy link,
        a null first link, or a substituted predecessor proof fails. When
        <tt>ordered_chain</tt> is false this check supplies no causal claim.</li>
        <li><strong>Window.</strong> Every admitted member's
        <tt>issued_at</tt> is within <tt>window_sec</tt> of the first
        member's <tt>issued_at</tt> (<tt>window_exceeded</tt>).</li>
      </ol>
      <t>The predicate is the same whether computed by the orchestrating
      operator before consumption or by an independent Verifying Executor or
      auditor offline (Q5): it is a pure function of (P, H, members).
      Organizational acceptance additionally requires that P, H, and the
      approver directory are authenticated inputs, not arbitrary values
      chosen by the artifact supplier.
      Check 2 invokes the appropriate native signoff verifier per member;
      EP-QUORUM adds the set-level checks. A successful standalone wrapper
      check does not substitute for a carrier's own schema and signature
      requirements (<xref target="member-representation"/>).</t>
    </section>

    <section anchor="incremental-admission">
      <name>Incremental Admission (canAccept)</name>
      <t>To keep a non-conforming signer out of the trail rather than
      discovering it only at consume time (Q4), an orchestrator
      <bcp14>MUST</bcp14> evaluate an incremental admission rule before
      recording each new signoff. Given the policy P, the action hash H, the
      already-admitted trail, and one incoming candidate member, the rule
      ADMITS the candidate only if all of the following hold, and otherwise
      REJECTS it with the named reason:</t>
      <ol>
        <li>P is well-formed and its roster is non-empty (else
        <tt>no_policy</tt> / <tt>no_eligible_approvers</tt>).</li>
        <li>The candidate's context carries <tt>action_hash == H</tt> (else
        <tt>action_mismatch</tt>).</li>
        <li>The candidate's (role, approver) is on the roster (else
        <tt>ineligible_role</tt>).</li>
        <li>When <tt>distinct_humans</tt> is true, no already-admitted
        member shares the candidate's approver (else
        <tt>duplicate_human</tt>).</li>
        <li>In ordered mode, the candidate matches the next unfilled roster
        slot (<tt>approvers[len(trail)]</tt>) in both role and approver
        (else <tt>out_of_order</tt>).</li>
        <li>If the trail is non-empty, the candidate's <tt>issued_at</tt> is
        within <tt>window_sec</tt> of the first member's <tt>issued_at</tt>
        (else <tt>window_exceeded</tt>); and in ordered mode it is strictly
        greater than the last admitted member's <tt>issued_at</tt> (else
        <tt>non_increasing_time</tt>).</li>
        <li>When strong ordering is required, the exact chain profile is
        recognized and the incoming signed predecessor link satisfies
        <xref target="approval-modes"/> against the last admitted complete
        signoff (else <tt>unsupported_chain_profile</tt> /
        <tt>broken_chain</tt>).</li>
        <li>The candidate's signature verifies (else
        <tt>invalid_signature</tt>).</li>
      </ol>
      <t>A rejected candidate <bcp14>MUST NOT</bcp14> be written into the
      trail. Incremental admission is an enforcement convenience and an
      early-rejection UX; it is not a substitute for the Quorum Gate. A
      conforming Verifying Executor <bcp14>MUST</bcp14> re-evaluate the full
      Quorum Gate (<xref target="quorum-gate"/>) over the assembled trail
      before performing the action, regardless of incremental admission,
      because the executor does not trust the orchestrator to have applied
      admission honestly (this mirrors the base draft's execution-side
      enforcement, <xref target="EP-RECEIPTS"/>).</t>
    </section>

    <section anchor="composition">
      <name>Multi-Handshake Composition</name>
      <t>This section is informative. It describes, end to end, the process
      by which the normative machinery of <xref target="quorum-policy"/>
      through <xref target="incremental-admission"/> composes individual
      signing ceremonies into one multi-party decision. Every property named
      here is required elsewhere in this document or in the base draft; this
      section introduces no new requirements.</t>
      <t><strong>The handshake is the unit of approval.</strong> Each quorum
      member is one complete run of the base draft's single-approver signing
      ceremony (<xref target="EP-RECEIPTS"/>): a fresh, one-time
      challenge is issued for that approver against a verified handshake,
      bound to the exact action bytes through the action hash and binding
      hash it commits to, and bounded by an explicit validity window; the
      approver answers it with a user-verified, device-held signature whose
      WebAuthn assertion challenge is the member's context hash (the
      per-member requirement is check 2 of the Quorum Gate,
      <xref target="quorum-gate"/>). Nothing about the ceremony is weakened
      or amortized when the action needs several approvers: there is no
      batch signature and no shared signing session; k approvals are k
      separate ceremonies, each independently verifiable and each
      independently refusable.</t>
      <t><strong>Admission rejects known-invalid candidates early.</strong> Composition is
      incremental and server-side. Each candidate member is evaluated
      against the admission rule of <xref target="incremental-admission"/>
      before it is recorded, so a wrong-action, off-roster, duplicate,
      out-of-order, stale, or invalidly signed member handshake is refused
      at the moment it is presented and never enters the trail. The stored
      trail has passed the listed admission checks. This does not establish
      every final set-level check or the authenticity of the supplied policy
      and enrollment data. The Verifying
      Executor still re-evaluates the full Quorum Gate over the assembled
      trail, as <xref target="incremental-admission"/> requires, precisely
      because it does not trust the orchestrator to have admitted
      honestly.</t>
      <t><strong>The next proof depends on the completed prior proof.</strong>
      In the versioned strong profile, a successor signs the digest of the
      predecessor's complete signoff, including its actual signature. The
      chain transitively commits to prior proof artifacts. It does not
      establish trusted wall-clock order, backdating resistance, rendered
      pixels, or comprehension. Plain ordered mode checks only roster
      position and asserted timestamp consistency.</t>
      <t><strong>Denial is a first-class terminal outcome.</strong> Any
      approver in the trail can refuse, and a refusal is not the mere
      absence of an approval. Under the base draft a denial is signed over
      the same context hash with a decision of "denied" and is terminal: a
      denied authorization can never later become approved
      (<xref target="EP-RECEIPTS"/>). At the
      composition level, one dissenting approver leaves durable,
      attributable evidence of the dissent, which a relying party can weigh
      as adverse evidence rather than as a gap in the trail.</t>
      <t><strong>The composed decision is accepted at most once.</strong> A
      satisfied quorum is authorization evidence for one exact action and is
      consumable at most once within its atomic consumption domain; the relying
      party separately decides AUTHORIZED. Each
      member's signoff individually retains the base draft's
      one-time-consumption rule (<xref target="sec-window"/>), and
      consumption of the composed decision is refused until the Quorum Gate
      is satisfied; a partial trail confers no partial authority
      (<xref target="sec-partial"/>).</t>
      <t><strong>The whole composition is verifiable offline.</strong>
      Because the satisfied/not-satisfied judgment is a pure function of
      the policy, the action hash, and the members
      (<xref target="quorum-gate"/>, Q5), an auditor holding the members
      and authenticated policy and enrollment material can recompute the
      checks -- per-member
      signatures, action binding, distinctness of humans and keys, roster
      admission, threshold, order and ordering chain, and window -- without
      contacting the operator. As in the base draft, such offline
      verification establishes integrity relative to the pinned keys and
      policy, not a trusted signing time; it does not by itself
      establish current validity, such as the present enrollment or
      revocation status of the approvers involved.</t>
    </section>

    <section anchor="member-representation">
      <name>Member Representation in the Receipt</name>
      <t>The quorum computation uses native signoffs, an authenticated
      action hash, an independently pinned policy, and enrolled approver
      keys. The following is the repository's standalone member wrapper.
      It is not an instruction to add an inline policy or other extension
      to the closed Trust Receipt schema of <xref target="EP-RECEIPTS"/>.
      A carrier profile using a base receipt <bcp14>MUST</bcp14> preserve
      that carrier's extension rules and authenticate any external policy
      or member mapping. The completed-signoff chain is an explicit signed
      context profile, not a claim that this wrapper by itself conforms to
      every current base-receipt wire requirement. Each member is the triple:</t>
      <sourcecode type="json"><![CDATA[
{
  "role": "program_officer",
  "approver_public_key": "<SPKI of the approver's enrolled key>",
  "signoff": {
    "@type": "ep.signoff",
    "context": {
      "context_type": "ep.signoff.v1",
      "action_hash": "...",
      "approver": "...",
      "issued_at": "...",
      "...": "..."
    },
    "webauthn": {
      "authenticator_data": "...",
      "client_data_json": "...",
      "signature": "..."
    }
  }
}
]]></sourcecode>
      <t>The <tt>context</tt> and <tt>webauthn</tt> members are exactly as
      verified by the native signoff verifier. The new chain adds a
      domain-separated hash over the complete prior signoff without
      changing the native signature algorithm. The
      <tt>role</tt> and <tt>approver_public_key</tt> are the join keys
      against the Quorum Policy roster and the Approver Directory.</t>
    </section>

    <section anchor="conformance">
      <name>Conformance</name>
      <t>An implementation conforms to EP-QUORUM if, for the published
      adversarial conformance vectors, it returns the expected
      satisfied/not-satisfied verdict for every vector and rejects every
      non-conforming candidate at incremental admission with the expected
      reason. The reference suite (EP-QUORUM-v1) comprises the following
      vectors, each carrying real Class-A WebAuthn assertions:</t>
      <table>
        <thead>
          <tr><th>Vector</th><th>Expect</th><th>Exercises</th></tr>
        </thead>
        <tbody>
          <tr><td><tt>accept_ordered_3of3</tt></td><td>satisfied</td><td>Ordered PO, AO, IG; distinct; increasing time; all action-bound</td></tr>
          <tr><td><tt>accept_threshold_2of3</tt></td><td>satisfied</td><td>Any 2 distinct approvers from a 3-slot roster</td></tr>
          <tr><td><tt>reject_under_threshold</tt></td><td>not satisfied</td><td>Fewer than <tt>required</tt> valid members</td></tr>
          <tr><td><tt>reject_duplicate_human</tt></td><td>not satisfied</td><td>One human filling two slots</td></tr>
          <tr><td><tt>reject_out_of_order</tt></td><td>not satisfied</td><td>Ordered mode, members out of roster order</td></tr>
          <tr><td><tt>reject_action_mismatch</tt></td><td>not satisfied</td><td>A member bound to a different action hash</td></tr>
          <tr><td><tt>reject_expired_window</tt></td><td>not satisfied</td><td>A member outside <tt>window_sec</tt></td></tr>
          <tr><td><tt>reject_one_bad_signature</tt></td><td>not satisfied</td><td>One invalid member signature</td></tr>
          <tr><td><tt>reject_wrong_role</tt></td><td>not satisfied</td><td>A correct signature by an off-roster approver</td></tr>
          <tr><td><tt>reject_duplicate_key</tt></td><td>not satisfied</td><td>One device key signing under two approver identifiers (check 5a)</td></tr>
          <tr><td><tt>reject_broken_chain</tt></td><td>not satisfied</td><td>Strong ordered mode, a member committing to the wrong completed predecessor signoff hash (check 7a)</td></tr>
        </tbody>
      </table>
      <t>The reference suite is maintained such that three cross-language
      reference verifiers (JavaScript, Python, Go), which share one
      repository and are therefore a consistency check rather than
      clean-room independent implementations, <bcp14>MUST</bcp14> agree on
      every vector; divergence is a conformance defect in at least one
      verifier. Independent implementations remain future interoperability
      evidence. The "accept" vectors guard against a verifier that is
      too strict (denying valid quorums); the "reject" vectors guard against
      a verifier that is too lenient (the security-critical direction).</t>
      <t>The updated corpus additionally includes an ordered 2-of-3
      positive, reverse-signed context-only chains with and without the new
      profile label, missing and unknown profiles, and a null first
      predecessor. Unit tests separately exercise substitution of a different
      valid predecessor signature, stripping or mutation of an expected
      policy, and incremental admission. The corpus version is 1.1.0.</t>
      <t>Earlier symbolic evidence over a fixed 2-of-2 instance does not
      establish the new completed-signoff construction. No formal proof of
      this revision's causal-chain profile is claimed. Neither old symbolic
      results nor same-team cross-language tests prove general k-of-n
      security, WebAuthn internals, directory correctness, parser behavior,
      collusion resistance, human comprehension, or deployment behavior.</t>
    </section>

    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>EP-QUORUM inherits all Security Considerations of
      <xref target="EP-RECEIPTS"/> and adds the following. Several restate,
      honestly, what a quorum does <em>not</em> buy.</t>
      <section anchor="sec-prevent">
        <name>What multi-party authorization does and does not prevent</name>
        <t>With authenticated enrollment and policy inputs, a satisfied
        distinct-human quorum establishes valid, action-bound signatures
        under k distinct enrolled keys and the declared quorum checks.
        Timestamp checks apply to asserted values; only the completed-signoff
        profile adds causal proof dependence. Under the signature assumptions
        the orchestrator cannot forge an uncompromised approver's signature
        (<xref target="EP-RECEIPTS"/>). It raises the cost of
        unilateral action: a single compromised agent, a single stolen key,
        a single coerced or malicious approver is insufficient. It does
        <em>not</em> defeat collusion among the required number of
        distinct humans, nor one human who controls multiple enrolled
        identities (an enrollment control -- the base draft's Approver Directory requirements), nor
        simultaneous coercion of a full quorum. As in the base draft,
        EP-QUORUM makes such events <em>attributable</em> -- named, signed,
        and evidenced for every member -- which is a deterrent and an audit
        primitive, not an impossibility proof. Implementations
        <bcp14>MUST NOT</bcp14> claim a quorum is collusion-proof.</t>
      </section>
      <section anchor="sec-failclosed">
        <name>Fail-closed is the only safe default</name>
        <t>The dangerous error in a multi-party gate is to treat ambiguity
        as approval. EP-QUORUM is specified so that a malformed policy, a
        missing or unparseable member, a partial trail, or any single failed
        check yields "not satisfied." A verifier <bcp14>MUST NOT</bcp14>
        default to satisfied on any unrecognized condition. The "reject"
        conformance vectors exist to catch a regression in this
        direction.</t>
      </section>
      <section anchor="sec-partial">
        <name>Partial trails confer no authority</name>
        <t>A trail short of the threshold, or one in which incremental
        admission has accepted some but not all required slots, authorizes
        nothing. A Verifying Executor presented with a partial trail
        <bcp14>MUST</bcp14> refuse, exactly as it refuses a missing single
        signoff. This is the multi-party form of the base draft's
        NoBypassWrite invariant.</t>
      </section>
      <section anchor="sec-window">
        <name>Window and replay</name>
        <t>The approval window (<tt>window_sec</tt>) bounds how long a
        partial quorum remains completable, limiting the value to an
        attacker of compromising a <em>remaining</em> approver after some
        approvals already exist. Each member's signoff retains the base
        draft's one-time-consumption nonce (G3); the window is an
        additional, quorum-level constraint, not a substitute for
        per-signoff replay protection. Comparing asserted timestamps
        does not prevent backdating or establish freshness. The relying
        party separately enforces expiry, a trusted current-time policy,
        and replay state. The completed-signoff chain does not supply
        those properties.</t>
      </section>
      <section anchor="sec-misinform">
        <name>Divide-and-misinform across members</name>
        <t>Because each approver signs their own Authorization Context, a
        malicious orchestrator can attempt to show different approvers
        different renderings or different initiator attestations
        (<xref target="EP-RECEIPTS"/>) while each individual
        signature remains valid. EP-QUORUM does not change the base draft's
        cross-context consistency requirement; verifiers
        <bcp14>SHOULD</bcp14> surface per-member context differences, and
        high-value ordered policies <bcp14>SHOULD</bcp14> render the prior
        approvers' decisions to each subsequent approver so that the trail is
        a chain of informed approvals rather than parallel ones. The
        presentation-attack mitigations of the base draft's Security Considerations apply per
        member.</t>
      </section>
      <section anchor="sec-fatigue">
        <name>Approver fatigue, at quorum scale</name>
        <t>Requiring more humans does not help if each rubber-stamps; it can
        hurt, by diffusing responsibility across a group in which no member
        feels decisive (the base draft's Security Considerations). Quorum policies
        <bcp14>MUST</bcp14> be scoped to genuinely high-consequence,
        low-frequency actions, and deployments <bcp14>SHOULD</bcp14> monitor
        per-role time-to-sign and deny rates rather than assume that more
        signers means more scrutiny.</t>
      </section>
      <section anchor="sec-rubberstamp">
        <name>Rubber-stamping and review-latency evidence</name>
        <t>Signing-ceremony telemetry -- the times at which each member's
        challenge was issued, first viewed, and approved --
        <bcp14>SHOULD</bcp14> be retained alongside the trail as evidence of
        how much review each approval actually received. A relying party
        <bcp14>MAY</bcp14> apply a minimum-review-latency policy when
        weighing an approval: an approval recorded a very short interval
        after the challenge was first viewed is evidence of rubber-stamping
        rather than review, and a relying party may discount it or treat the
        bundle as containing conflicting evidence accordingly. This raises
        the probability that rubber-stamping is detected after the fact and
        makes it attributable to a named approver; it does not prevent
        rubber-stamping, and it does not defeat collusion or coercion, for
        which <xref target="sec-prevent"/> applies. Ceremony telemetry
        originates with the orchestrator, so a relying party that depends on
        it should weigh unsigned telemetry with the same skepticism as any
        other operator-supplied metadata; deployments that need it to serve
        as evidence rather than log data can have the operator sign the
        ceremony record so that its integrity is independently
        checkable.</t>
      </section>
      <section anchor="sec-threshold-custody">
        <name>Human quorum is not threshold secret custody</name>
        <t>EP-QUORUM verifies distinct enrolled approvers, action-bound
        signatures, roles, order, and timing. Reconstructing a capability
        holder secret from m-of-n shares proves only that enough shares were
        combined. Unless each share holder separately performs the EP-QUORUM
        ceremony, threshold reconstruction does not prove that distinct
        humans reviewed or approved the action. Implementations and public
        claims <bcp14>MUST NOT</bcp14> describe threshold secret custody as
        EP-QUORUM authorization.</t>
      </section>
    </section>

    <section anchor="changes-04">
      <name>Changes from -03</name>
      <t>Replaced the unsound context-only chronology claim with the
      versioned EP-QUORUM-SIGNOFF-CHAIN-v1 completed-proof dependency profile.
      Specified the domain-separated digest, first-member and mixed-link
      rejection, authenticated policy pin, and explicit compatibility break.
      Clarified ordered k-of-n, unconditional key distinctness, asserted
      timestamp limits, carrier boundaries, and the absence of a formal
      proof for the new chain.</t>
    </section>

    <section anchor="changes-03">
      <name>Changes from -02</name>
      <t>Clarified that EP-QUORUM's distinct-human, action-bound approval
      ceremony is not interchangeable with m-of-n reconstruction of a
      capability holder secret.</t>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>Normative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8785.xml"/>
      <reference anchor="EP-RECEIPTS" target="https://datatracker.ietf.org/doc/html/draft-schrock-ep-authorization-receipts-12">
        <front>
          <title>Authorization Receipts for High-Risk Agent Actions (EP)</title>
          <author fullname="Iman Schrock">
            <organization>EMILIA Protocol, Inc.</organization>
          </author>
          <date year="2026" month="August" day="16"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-schrock-ep-authorization-receipts-12"/>
      </reference>
      <reference anchor="WEBAUTHN" target="https://www.w3.org/TR/webauthn-2/">
        <front>
          <title>Web Authentication: An API for accessing Public Key Credentials, Level 2</title>
          <author><organization>W3C</organization></author>
          <date year="2021" month="April"/>
        </front>
      </reference>
    </references>
  </back>
</rfc>
