| Internet-Draft | EP Multi-Party Quorum | July 2026 |
| Schrock | Expires 7 January 2027 | [Page] |
This document defines EP-QUORUM, a multi-party authorization profile for the EMILIA Protocol (EP) authorization receipt (draft-schrock-ep-authorization-receipts). Where the base receipt binds a single accountable human to one exact high-risk action, EP-QUORUM binds a set of distinct accountable humans -- the "two-person rule," generalized to M-of-N and to ordered approval trails -- to one exact action, such that no action is authorized until the full quorum holds. The profile is purely additive: each quorum member is an unmodified EP signoff over the same action hash, and a single-approver policy is the degenerate one-member case. EP-QUORUM specifies a fail-closed quorum predicate (all member signatures valid, every member bound to the exact action, approvers pairwise distinct, every approver admitted by role, the threshold met, the declared order respected, and all signatures within a bounded time window), an incremental server-side admission rule that rejects a non-conforming signer before it enters the trail, and a set of adversarial conformance vectors. The predicate is offline-verifiable under the base draft's verification model and is maintained as cross-language conformance vectors that three reference verifiers (JavaScript, Python, Go) are required to agree on. Those verifiers live in one repository -- a cross-language consistency check, not clean-room independent implementations; independent implementations remain future interoperability evidence. The document also describes, informatively, the multi-handshake composition process: how individual signing ceremonies -- fresh one-time challenge, exact-action binding, user-verified device signature -- compose into one ordered, offline-verifiable, once-consumable multi-party decision.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 7 January 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
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 [EP-RECEIPTS], Section 1). 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 two-person rule: 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.¶
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.¶
The base draft already contemplates multi-approver policies ([EP-RECEIPTS], Section 7): 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 (Section 4); an explicit role roster and admission semantics (Section 3); a bounded approval window with a monotonic-time constraint for ordered mode (Section 4); an incremental server-side admission rule that keeps a non-conforming signer out of the trail in the first place (Section 6); the consolidated fail-closed quorum predicate (Section 5); and an adversarial conformance suite (Section 9).¶
EP-QUORUM inherits design goals G1-G7 of the base draft and adds:¶
In addition to the terminology of [EP-RECEIPTS]:¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHOULD", "MAY" are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.¶
A Quorum Policy is a JSON object:¶
{
"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
}
¶
Members:¶
| Field | Required | Type | Meaning |
|---|---|---|---|
mode
|
REQUIRED | string (enum) |
threshold or ordered
(Section 4). |
required
|
REQUIRED | integer >= 1 | The quorum size k. |
approvers
|
REQUIRED | array | The roster of eligible {role, approver} slots. MUST be non-empty. |
distinct_humans
|
OPTIONAL (default true) | boolean | When true, no approver identifier may fill more than one slot, AND no single device key may fill more than one slot (Section 5, checks 5 and 5a). Implementations MUST treat a missing value as true. |
window_sec
|
OPTIONAL (default 900) | integer > 0 | Maximum span, in seconds, between the first and any later admitted signature. |
ordered_chain
|
OPTIONAL (default false) | boolean | Strong ordered mode (Section 4). When
true (ordered mode only), each signoff after the first
MUST commit, inside its own signed context, to the
hash of its predecessor's context (prev_context_hash), so
approval order is proven by the signatures rather than by
issued_at timestamps. |
Rules:¶
approver slot identifies an eligible approver in the
Approver Directory ([EP-RECEIPTS], Section 5.2). The
role is the organizational role under which that approver
is admitted to this quorum; it is the unit of role eligibility
(Section 5, check 4).¶
wrong_role).¶
required MUST NOT exceed the number of
distinct human approvers the roster can supply under
distinct_humans. A policy that cannot be satisfied is a
misconfiguration; verifiers treat an unsatisfiable trail as not
satisfied, as always.¶
policy_hash of every
member's Authorization Context ([EP-RECEIPTS],
Section 4). A signature collected under one Quorum Policy version
MUST NOT satisfy a requirement evaluated under
another.¶
Threshold mode ("threshold"). Any
required distinct approvers drawn from the roster, in any
order, satisfy the quorum. This is the classic M-of-N rule.¶
Ordered mode ("ordered"). Approvals
MUST occur in the roster's listed order: the i-th
admitted member MUST match approvers[i-1] in
both role and approver. In addition, signature times
MUST be strictly increasing -- each member's
issued_at MUST be later than the previous
admitted member's issued_at (non_increasing_time).
Ordered mode expresses escalation chains in which a later authority
signs after, and in knowledge of, an earlier one (e.g.,
Program Officer, then Authorizing Official, then Inspector
General).¶
In both modes, every admitted member's issued_at
MUST fall within window_sec of the first
admitted member's issued_at (window_exceeded). The
window bounds the lifetime of a partial quorum so that a long-dormant
partial trail cannot be completed much later by an attacker who has
since compromised a remaining approver.¶
Strong ordered mode ("ordered_chain": true).
Ordering by issued_at alone is asserted by whoever stamps the
time. To prove order by the signatures themselves, a policy
MAY require a cryptographic chain: each signoff after the
first carries, inside its signed Authorization Context, a
prev_context_hash equal to the SHA-256 of the canonical context
of its predecessor in the trail; the first signoff carries none. Because
each approver's device signs over its own context, the i-th approver
thereby attests to having signed after the exact (i-1)-th
approval, and no party -- including the operator -- can reorder, insert,
or backdate a signoff without invalidating a signature
(broken_chain). This is RECOMMENDED for the
highest-consequence ordered actions. Implementations that do not set
ordered_chain retain timestamp ordering and remain
conformant.¶
A trail is a satisfied quorum for an action with hash H under policy P if and only if ALL of the following hold. A verifier MUST return "satisfied" only when every check passes, and MUST return "not satisfied" on the first failure, on a malformed policy or member, or on any unrecognized condition (Q2):¶
mode, an integer required >= 1, and a non-empty
approvers roster. Otherwise: not satisfied.¶
approver_public_key, with the
assertion challenge equal to the member's context hash and user
verification asserted. One invalid signature
(one_bad_signature) fails the whole quorum.¶
action_hash == H
(action_mismatch). A member bound to any other action does
not count.¶
wrong_role).¶
distinct_humans
is true (the default), approvers are pairwise distinct and (per the
base draft's SelfApprovalImpossible) distinct from the initiator
(duplicate_human).¶
distinct_humans is true, the admitted members'
approver_public_key values are pairwise distinct
(duplicate_key). 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.¶
required admitted
members exist (under_threshold).¶
approvers[i-1]; signature times are strictly
increasing (out_of_order,
non_increasing_time).¶
ordered_chain is true, the first admitted member carries
no prev_context_hash and every later member's
prev_context_hash equals the SHA-256 of the canonical context
of its predecessor (broken_chain). When ordered_chain
is false this check is not applicable.¶
issued_at is within window_sec of the first
member's issued_at (window_exceeded).¶
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). Because each member is an unmodified EP signoff, check 2 is exactly the base draft's verifier invoked per member; EP-QUORUM adds the set-level checks 1, 3-10 on top.¶
To keep a non-conforming signer out of the trail rather than discovering it only at consume time (Q4), an orchestrator MUST 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:¶
no_policy / no_eligible_approvers).¶
action_hash == H (else
action_mismatch).¶
ineligible_role).¶
distinct_humans is true, no already-admitted
member shares the candidate's approver (else
duplicate_human).¶
approvers[len(trail)]) in both role and approver
(else out_of_order).¶
issued_at is
within window_sec of the first member's issued_at
(else window_exceeded); and in ordered mode it is strictly
greater than the last admitted member's issued_at (else
non_increasing_time).¶
invalid_signature).¶
A rejected candidate MUST NOT 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 MUST re-evaluate the full Quorum Gate (Section 5) 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, [EP-RECEIPTS], Section 9).¶
This section is informative. It describes, end to end, the process by which the normative machinery of Section 3 through Section 6 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.¶
The handshake is the unit of approval. Each quorum member is one complete run of the base draft's single-approver signing ceremony ([EP-RECEIPTS], Section 5.3): 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, Section 5). 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.¶
A malformed trail never exists. Composition is incremental and server-side. Each candidate member is evaluated against the admission rule of Section 6 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 is therefore, at every instant, a prefix of a potentially satisfiable quorum; there is no later cleaning pass that removes bad members, because bad members are never written. The Verifying Executor still re-evaluates the full Quorum Gate over the assembled trail, as Section 6 requires, precisely because it does not trust the orchestrator to have admitted honestly.¶
Order is a signed commitment, not a claim. In strong ordered mode (Section 4), each member's signed context after the first commits to the hash of its predecessor's context. Because that commitment sits inside the bytes the approver's device signed, and each predecessor in turn committed to its own predecessor, the i-th signature transitively covers the entire prior trail: the order of approvals is non-repudiable, and no party -- including the operator -- can reorder, insert, or backdate a member without invalidating a signature (check 7a, Section 5). Plain ordered mode retains the weaker, timestamp-based ordering described in Section 4.¶
Denial is a first-class terminal outcome. 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 ([EP-RECEIPTS], Sections 5.3 and 6.1). 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.¶
The composed decision is consumed once. A satisfied quorum authorizes exactly one execution of exactly one action. Each member's signoff individually retains the base draft's one-time-consumption rule (Section 10.4), and consumption of the composed decision is refused until the Quorum Gate is satisfied; a partial trail confers no partial authority (Section 10.3).¶
The whole composition is verifiable offline. Because the satisfied/not-satisfied judgment is a pure function of the policy, the action hash, and the members (Section 5, Q5), an auditor holding only the quorum receipt can recompute every check above -- 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 the integrity and authenticity of the recorded composition as of signing time; it does not by itself establish current validity, such as the present enrollment or revocation status of the approvers involved.¶
A quorum receipt is an ordinary EP Trust Receipt
([EP-RECEIPTS], Section 6.2) whose contexts
and signoffs arrays carry one entry per admitted member, plus
the Quorum Policy (by reference via policy_hash, and
optionally inline for convenience). For the offline
quorum computation, each member is the triple:¶
{
"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": "..."
}
}
}
¶
The context and webauthn members are exactly as
defined by the base draft; EP-QUORUM does not alter their
canonicalization, hashing, or signature verification. The
role and approver_public_key are the join keys
against the Quorum Policy roster and the Approver Directory.¶
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:¶
| Vector | Expect | Exercises |
|---|---|---|
accept_ordered_3of3
|
satisfied | Ordered PO, AO, IG; distinct; increasing time; all action-bound |
accept_threshold_2of3
|
satisfied | Any 2 distinct approvers from a 3-slot roster |
reject_under_threshold
|
not satisfied | Fewer than required valid members |
reject_duplicate_human
|
not satisfied | One human filling two slots |
reject_out_of_order
|
not satisfied | Ordered mode, members out of roster order |
reject_action_mismatch
|
not satisfied | A member bound to a different action hash |
reject_expired_window
|
not satisfied | A member outside window_sec
|
reject_one_bad_signature
|
not satisfied | One invalid member signature |
reject_wrong_role
|
not satisfied | A correct signature by an off-roster approver |
reject_duplicate_key
|
not satisfied | One device key signing under two approver identifiers (check 5a) |
reject_broken_chain
|
not satisfied | Strong ordered mode, a member committing to the wrong predecessor context hash (check 7a) |
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, MUST 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).¶
EP-QUORUM inherits all Security Considerations of [EP-RECEIPTS] and adds the following. Several restate, honestly, what a quorum does not buy.¶
A satisfied quorum proves that k pairwise-distinct enrolled approvers each produced a valid, action-bound, in-window, in-order (where required) signature with their own device-held keys, and that the orchestrator could not have forged any of them ([EP-RECEIPTS], Section 11.1). 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 not defeat collusion among the required number of distinct humans, nor one human who controls multiple enrolled identities (an enrollment control -- base draft Section 5.2), nor simultaneous coercion of a full quorum. As in the base draft, EP-QUORUM makes such events attributable -- named, signed, and evidenced for every member -- which is a deterrent and an audit primitive, not an impossibility proof. Implementations MUST NOT claim a quorum is collusion-proof.¶
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 MUST NOT default to satisfied on any unrecognized condition. The "reject" conformance vectors exist to catch a regression in this direction.¶
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 MUST refuse, exactly as it refuses a missing single signoff. This is the multi-party form of the base draft's NoBypassWrite invariant.¶
The approval window (window_sec) bounds how long a
partial quorum remains completable, limiting the value to an
attacker of compromising a remaining 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. The monotonic-time rule in ordered
mode additionally prevents back-dating a later authority's signature
to appear to precede an earlier one.¶
Because each approver signs their own Authorization Context, a malicious orchestrator can attempt to show different approvers different renderings or different initiator attestations ([EP-RECEIPTS], Section 11.9) while each individual signature remains valid. EP-QUORUM does not change the base draft's cross-context consistency requirement; verifiers SHOULD surface per-member context differences, and high-value ordered policies SHOULD 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 base draft Section 11.3 apply per member.¶
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 (base draft Section 11.8). Quorum policies MUST be scoped to genuinely high-consequence, low-frequency actions, and deployments SHOULD monitor per-role time-to-sign and deny rates rather than assume that more signers means more scrutiny.¶
Signing-ceremony telemetry -- the times at which each member's challenge was issued, first viewed, and approved -- SHOULD be retained alongside the trail as evidence of how much review each approval actually received. A relying party MAY 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 Section 10.1 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.¶
This document has no IANA actions.¶