| Internet-Draft | PQST Cohort | November 2026 |
| Henry | Expires 6 May 2027 | [Page] |
This document specifies the cohort layer of Post-Quantum Secure Transport (PQST). With it, PQST provides independently migrating and recoverable post-quantum sessions within a persistent cohort authorization context. A cohort member's sessions move, sleep, recover and re-key on their own, and none of those events by itself requires a change of the cohort's membership. One sequencer commits the membership, the policy, and a membership epoch together with that epoch's key, and only on security events; members receive them inside their pairwise sessions, keep them through partitions, and return after dormancy or a restart without readmission, unless they were away longer than the group's expiry. This version specifies the group's control plane: membership, policy, reachability and member discovery. It describes, as the direction of the next version, a group data plane under keys derived from the epoch key, which would give the members one secure session among themselves.¶
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 6 May 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.¶
A PQST session [PQST] survives the loss of every path, dormancy and the restart of either endpoint, and resumes with a fresh post-quantum exchange as the same session. The cohort applies the same idea one level up: the cohort is to its members' sessions what a session is to its paths. A member may change paths, go dormant, re-anchor, restart, or return through another gateway of the group, and the cohort exists unchanged. Its state advances only on security events: admission, removal, revocation and scheduled rotation.¶
Two consequences are emphasized here due to their divergence from MLS and IPsec. A removal from the cohort closes that member's sessions with the sequencer; it does not close that member's pairwise sessions with other members. A re-anchor, a restart or a path change of a member's session does not advance the membership epoch.¶
The purpose of the layer is a group whose members hold a unified session relative to each other: one membership, one policy, one epoch and, in the data plane this version leaves for the next, one group secret under which members communicate. Such a group can outlive its infrastructure. Members cut off from the sequencer keep their last epoch and go on, and when the partition heals they return to the current epoch by the ordered acceptance of Section 5.1, with no histories to merge; the sequencer's commits are the only history. This is the setting PQST is designed for: disconnected, intermittent and limited networks, radio nets among them, where a group service that dies with its server is of no use.¶
The cohort layer is an upper layer of [PQST] and changes nothing in it.¶
It runs on the reserved stream STREAM_COHORT inside each member's pairwise session with the sequencer.¶
What must arrive travels as protocol objects, with the delivery, versioning and acknowledgement of Section 19.3 of [PQST].¶
The sequencer is a gateway group's identity (Section 16.9 of [PQST]), so a member's session to it is portable across the group's gateways.¶
Every member session requires identity proof on re-anchor (Section 16.3.3 of [PQST]).¶
The codepoints this document uses are reserved for it in Section 26 of [PQST]: object kinds 0x10 to 0x1F, the stream 0xE0, capability bit 0, packet type 3, and the derivation labels beginning "group ". The group data plane, when it is specified, will be an extension in the sense of Section 21 of [PQST]: a packet type enabled by a capability bit, which a pairwise endpoint that does not implement it drops without a trace.¶
The reader is assumed to know [PQST]. Section numbers without a document name refer to this document; a reference into PQST is written "Section N of [PQST]".¶
This version specifies the control plane (Sections 3 to 12): what the reference implementation does and what the cohort simulator has exercised (Section 17). It carries no group data. Every datagram between members travels in a pairwise session between them, and the epoch key protects only the group's own control (Section 6). Section 13 describes the group data plane the next version is intended to add, and lists the questions that remain open; it is informative.¶
Scope of this version. This document specifies a sequencer-managed membership, policy-distribution, reachability and discovery control plane for identities that communicate over independently authenticated PQST sessions. It establishes no native multiparty encrypted channel: member-to-member application traffic travels in ordinary pairwise PQST sessions, or in a transport specified elsewhere. The membership epoch key serves the control plane's proof of possession and does not, by itself, authenticate any sender's application data. The group data plane of Section 13 is an informative design whose cryptographic constructions are preliminary; its packet format, replay semantics, sender authentication and confidentiality properties are not conformance requirements of this version.¶
MLS ([RFC9420]) lets a group agree its keys among its members, without a trusted party for confidentiality. The cohort layer does not: a sequencer the members trust delivers the key, and post-compromise security comes from rotation, at once on removal. A deployment without a trusted sequencer would use MLS for the group key, with the sequencer as its delivery service; everything else in this document would stand.¶
G-IKEv2 ([RFC9838], which obsoletes GDOI) distributes group keys from a key server over IKEv2. The cohort layer has the same shape, a key server over authenticated pairwise channels, with two differences. The pairwise channels are PQST sessions, so a member's dormancy, restart or movement is not an event for the group. And the group itself has continuity rules (Section 3, Section 8 and Section 10) for members cut off from the server.¶
TESLA ([RFC4082]) gives source authentication from hash chains with delayed key disclosure. Section 13 considers it for broadcast flows of the data plane.¶
Three statements bound what this document claims.¶
A deployment gets one history, CG2, only if it supplies the authority of Section 4: a store with the properties that section lists, by a consensus or transactional system of the deployment's choosing. This document specifies those properties and includes no such system; the reference models one.¶
The cohort's continuity is the continuity of its members' pairwise sessions to the sequencer, with the limits [PQST] states for them: a session that comes back as a new session after a lost race on a slow link is a new session here too, bound to the cohort by its first REPORT.¶
This version is a control plane. It gives a group no native channel, no roster and no sender authentication among members; member-to-member traffic is pairwise PQST, and the group data plane of Section 13 is a direction, not a specification.¶
An implementation conforms to the cohort control plane of this version if it implements, as this document specifies: the three objects COHORT, GROUP_KEY and POLICY with their encodings (Section 5), acknowledging and ignoring ROSTER and MEMBER_EVENT; the four frames REPORT, LOCATE, LOCATE_RESP and EPOCH_PROOF with their fixed sizes (Section 6, Appendix A); the acceptance procedure of Section 5.1, with the persisted high-water marks and the two conflicts; standing confirmed only by an answer to the member's own report on the same session and measured from the report's sending, and its lapse (Section 8 and Section 10); the member states, the membership lookup on return and the one close reason per cause (Section 7); the duplicate-session rule and the identifier order (Section 9); the reporting of Section 11; and the rule that the epoch key is kept in memory only, never persisted, and protects no application data (Section 4 and Section 7).¶
A sequencer implementation conforms if, in addition, it commits as Section 8 says against an authority with the properties of Section 4 and issues nothing it has not committed.¶
Conformance does not include the group data plane of Section 13, a roster service, discovery policy beyond the closed default, or any consensus system; Section 17 reports what the reference implements against this list.¶
Section 1.5 is the applicability of this document and Section 1.6 its conformance clause.¶
Sections 4 to 12 are the control plane: roles and state, objects, frames, member states, epochs and delivery, duplicate sessions, partitions, services and parameters.¶
Section 13 is the intended data plane (informative).¶
Sections 14 to 17 are the security, privacy, IANA and implementation status sections.¶
Appendix A collects the wire formats; it is generated from Section 5, Section 6 and Section 16.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The terms of [PQST] are used as defined there: session, path, generation, re-anchor, FRESH, DORMANT, RECOVERING, ESTABLISHED, protocol object, gateway group, transport-class and security-class events. In addition:¶
Authority. The replicated state of the gateway group, which admits one commit at a time by a conditional transaction (Section 4); the consensus or transactional system that provides it is the deployment's.¶
Cohort. A group of PQST identities held together by one sequencer, identified by a 16-octet cohort_id. Its cohort identity, for the freshness rules of Section 5.1, is the cohort_id together with the sequencer's identity fingerprint.¶
Commit. One successor state of the authority (Section 4), durable before anything of it leaves a gateway: a rotation, which advances the epoch with a new key; an admission or a policy change, which does not (Section 8).¶
Epoch key. A 32-octet random key per membership epoch, delivered by the sequencer to ACTIVE members.¶
Member. An identity admitted to the cohort, with a member_index unique within an epoch and a member_state (Section 7).¶
Membership epoch. A 32-bit counter of the cohort, starting at 1, that advances only on a commit (Section 8). In this document epoch alone means the membership epoch.¶
Partition. The condition of a member whose session to the sequencer is RECOVERING or DORMANT, whatever the cause.¶
Revision. The position of a commit in the cohort's single history, assigned by the authority; it advances with every commit, a rotation, an admission or a policy change alike, where the epoch advances with rotations only.¶
Term. The authority's current grant of commit authority to one gateway, which every commit names and a superseded term cannot pass (Section 4).¶
Sequencer. The one party that commits the cohort's state: a gateway group's identity (Section 16.9 of [PQST]), reachable through any gateway of the group.¶
Session-class event. An event of a member's session that is not a security-class event: dormancy, re-anchor, restart, recovery by a new session, return through another gateway. The cohort layer absorbs these as the pairwise layer absorbs transport-class events.¶
Standing. A member's cohort authorization as the member itself holds it: the epoch key and COHORT object it has accepted, confirmed by the sequencer. Standing is confirmed by a COHORT object for the epoch the member holds or a newer one, on its session with the sequencer, and lapses group_suspend after the sending of the last report the sequencer answered (Section 10); a member whose standing has lapsed presents no EPOCH_PROOF, accepts none and sends no group traffic, while its sessions continue.¶
The membership epoch is distinct from everything that changes keys inside a member's sessions, which this document calls the session's own key changes: the epochs of a PQST session (Section 14 of [PQST]) and its generations (Section 16 of [PQST]).¶
| Session epochs and generations | Membership epoch | |
|---|---|---|
| Governs | one session's traffic keys and replay state | the authorized members and the cohort-wide policy |
| Advances on | a key update (epoch) or a re-anchor after recovery, migration or a scheduled ratchet (generation) | a commit: a removal, a forced or scheduled rotation |
| Scope | one session between two endpoints | one logical context shared by all members |
| Effect of a change | none on any other session or member | delivered to every ACTIVE member |
The cohort is to its members' sessions what a session is to its paths (Section 3 of [PQST]). The pairwise layer absorbs transport-class events inside the session, and the cohort layer absorbs session-class events inside the group. A cohort member may change paths, go dormant, re-anchor, restart or return through another gateway of the group while the cohort exists unchanged. Cohort state advances only on security-class events: admission, removal, revocation and scheduled rotation.¶
This version of the cohort layer is membership, policy, reachability and member discovery, run by one sequencer over the members' pairwise sessions to it, with one epoch key delivered to members for the authentication of group control. It carries no data: every datagram of a cohort travels in a pairwise session, and the epoch key encrypts nothing. The group data plane is the subject of Section 13.¶
The layer runs as an upper layer on the stream STREAM_COHORT = 0xE0 that [PQST] reserves for it (Section 16) inside each member's pairwise session with the sequencer. A member's session with the sequencer serves one cohort: the member names the cohort in its first REPORT on the session (Section 6), the first COHORT object delivered on it, which must agree with that name, fixes the session's cohort (Section 5.1), every later object and frame on the session belongs to that cohort, an object naming another cohort_id is refused, and a member of several cohorts of one sequencer holds a session per cohort. REPORT carries the cohort identifier, because a session is bound to no cohort until its first report; GROUP_KEY, POLICY and LOCATE carry none, because they travel only after that binding, on a session that serves one cohort; EPOCH_PROOF, which travels between members who may share more than one cohort, carries one (Section 6).¶
Whatever arrives travels as a protocol object of Section 19.4 of [PQST], in the kinds of Section 5, with the delivery, versioning and acknowledgement that the object mechanism provides; whatever may be lost travels as plain stream data, in the frames of Section 6. Members do not run the cohort protocol with each other in this version except the EPOCH_PROOF frame; member-to-member sessions are ordinary pairwise sessions whose addresses the discovery service supplies (Section 11). No capability bit is negotiated: a member and its sequencer are provisioned with each other's identities as any two peers are (Section 9.1 of [PQST]), and the stream is reserved.¶
The four invariants.¶
CG1, decoupling. A member's transport or cryptographic session transition does not, by itself, require a membership transition. No transport-class or session-class event of a member's session (a path change or loss, dormancy, recovery, a re-anchor or key update, a restart, a return through another gateway) changes the membership epoch, the member list, the policy version or any other member's cohort state. Only a security-class event may lead to a membership transition, and only through the sequencer's decision (Section 7). Measured: group-state churn per transport-class event is zero; commits per security-class event are at most one; and the sessions among members that an event did not touch see no transition and lose no datagram (Section 17).¶
CG2, single authoritative history. The commits of a cohort form one total order, the revisions of its authority (Section 4); each membership epoch has exactly one epoch key; two commits never produce two successor states for one revision. A partition leaves members holding epochs the cohort has left and creates no history: a gateway without current commit authority defers every commit, admission, removal and rotation alike, and the members keep their last epoch and their pairwise sessions under Section 10.¶
CG3, revocation is not restorable by resumption. Three statements, one per observer. At the sequencer: no resumption, re-anchor or new session restores cohort standing revoked while the member was away; a member removed during an outage that returns with its identity and its continuity secret may resume its pairwise sessions with other members, as CG1 intends, and receives no key or COHORT object of any epoch after its removal. At a peer that has applied the removal's commit: the previous epoch's key is erased and no EPOCH_PROOF under it is accepted, from the removed member or from anyone (Section 10). At a peer that has not applied it: the peer continues on the state it holds, as a partition permits, only within its own standing, which lapses group_suspend after the sending of the last report the sequencer confirmed (Section 10); the removed member's residual acceptance is therefore confined to peers that have not received the commit, and ends at their lapse, at most group_suspend after the sending of a report whose confirmation preceded the removal. Measured: keys delivered to a member removed while away are zero; proofs accepted from it by a peer that has applied the removal are zero; no peer accepts a proof, or sends a group packet, past its standing.¶
CG4, cohort state never rolls back. For a cohort identity, no authenticated object, new session, gateway or restart lowers the membership epoch or the policy version a member has accepted (Section 5.1). Staleness is permitted: a member cut off from the sequencer stays at the epoch it holds; a return to an epoch or a policy it has already left is not, whatever authenticated session delivers it.¶
The sequencer is the gateway group's identity (Section 16.9 of [PQST]): one replicated identity key, reachable through any gateway of the group, so that a member's session to the sequencer is portable across gateways like any session (Section 16.9 of [PQST]). Two kinds of state meet in it, and the authority of Section 4.2 holds only the first.¶
Authoritative, replicated: the cohort_id (16 octets, chosen at creation); the member list, for each member its identity fingerprint, its member_index and whether it is REMOVED and awaiting its drop; the epoch (32-bit, starting at 1) and the current epoch key (32 octets); the policy and its version; and the revision, the commit identifier and the term of Section 4.2. A commit is durable in that state before anything of the new epoch leaves the sequencer (Section 8).¶
Observed, held by the gateway that serves the member: the session the member last used and the sessions it holds; its latest REPORT, the addresses it named and the order of its reports; the time its session went DORMANT and the SUSPENDED and EXPIRED states that timers over that session produce (Section 7); what has been issued to it on each session; and the discovery answers made from its report (Section 11). None of this is a commit or passes through the authority; a deployment that replicates it among its gateways does so by means of its own, outside the authority; and an observation changes the authoritative state only through a commit a gateway makes on its own account, the drop at a rotation of a member it holds EXPIRED (Section 8).¶
The sequencer is the only source of the objects of Section 5; a member never forwards one.¶
The replicated state is an authority (Section 2) that admits one commit at a time. The deployment MUST provide it with the following properties, by a consensus or transactional system of its choosing (Section 14).¶
A commit is a conditional, atomic transaction that names the revision it succeeds and the term under which the gateway writes. The authority applies it whole or not at all, only while that revision is the latest and that term is current. A write under a superseded term is refused however late it arrives, so that a gateway that has lost its authority cannot commit, whatever it believes.¶
An acknowledgement means the commit is durable under the deployment's fault model and cannot be superseded by an earlier or a conflicting commit.¶
A read made for a decision that grants or restores standing returns the latest commit (Section 10), and a gateway MUST have established the currency of the state it issues from. The mechanisms differ in what they establish:¶
A fenced lease from the authority establishes currency for its validity, when the authority commits nothing conflicting while the lease holds.¶
A linearizable read establishes currency at its linearization point only, and any interval a deployment allows between such a read and an issuance is a staleness allowance, not currency, which the deployment MUST add to the revocation exposure it states (Section 12 and Section 14).¶
Past either, a lease's validity or a read's allowance, the gateway does not know, however current its last read was.¶
The authority keeps the commit identifier of every revision for the recovery horizon of the deployment, so that a gateway can resolve an operation whose acknowledgement it lost by its identifier at any revision, after later commits too. The lookup has three outcomes:¶
A committed operation is never retried, even by a gateway that has meanwhile lost its term. An unknown outcome is never taken for a failure, and in particular never licenses the operation again under a new identifier, which for a rotation would mint a second key for one epoch. A retry is the same transaction: the same commit identifier, the same predecessor revision and the same successor, under the gateway's current term. The authority MUST refuse a commit identifier presented with another predecessor or another successor, or resolve it to the one record it holds for that identifier, so that a retry, whatever term it is made under, can never become a second logical operation.¶
A lease held in a gateway's memory is a belief, not authority; only the authority's acceptance of the transaction is.¶
A gateway begins a term, at its grant or at its own restore after a crash, by reading the authority's latest revision and continuing from that record. It commits nothing under the term before that read, so that its first write names a current predecessor and a refusal can mean only one thing. A term is never resumed: a restored gateway is granted a new one, so that its own writes from before the crash, under the old term, are refused however late they arrive.¶
A gateway without current authority serves members' sessions and defers commits (Section 10).¶
A member holds what the sequencer last told it: its COHORT object, the epoch key, the policy; and the sessions it has to the sequencer. It keeps the epoch key in memory only and MUST NOT persist it; it MAY persist the policy body, under the protection its deployment gives it, and need not. What it holds of either after a restart it says in its REPORT, the epoch of the key and the version of the policy body, each 0 when it holds none (Section 6.1), and the sequencer issues what the report lacks (Section 8); the object layer's delivery of a key or a policy before the restart implies nothing about what the member holds after it (Section 19.3.4 of [PQST]). It MUST persist its freshness state, the cohort identity with the highest epoch and the highest policy_version it has accepted (Section 5.1), before it acts on a newer one, so that a restart cannot take it back past them. After a restart it holds no key, and no policy unless it persisted the body, and says so (Section 8); it applies no object naming an epoch or a policy version older than its persisted state, and takes the current key or policy issued to it again, under a new object version, for what it is: the value for an epoch or a version it has already accepted, neither a rollback nor a conflict (Section 5.1). Without that state, on a first start or after a record lost or not matching the cohort identity, it accepts what the sequencer delivers and is protected only by the sequencer's own freshness (Section 10). A member's cohort state changes only when an object arrives or its session to the sequencer is closed by a security-class reason (Section 7); the metric of CG1 counts exactly these changes.¶
Session cohort context. In this version a member-sequencer session carries the control state of exactly one cohort, fixed by the first authenticated COHORT object delivered on it and bound to the session for its life (Section 3). A GROUP_KEY, POLICY or other cohort object delivered on the session MUST be interpreted within that cohort only, and an endpoint MUST NOT apply an object of one cohort to another, whatever the sequencer identity and the epoch number. Membership in several cohorts of one sequencer needs a session per cohort, the member naming the cohort in its first REPORT on each (Section 6) and the sequencer treating a session as belonging to no cohort until then (Section 9); a later version that multiplexes cohorts on one session must add the identifier to its objects and change the scope of object versioning with it.¶
Kinds 0x10 to 0x14, in the range 0x10 to 0x1F that [PQST] reserves for this document (Section 19.4 of [PQST]), registered in Section 16. An endpoint hands the kinds of that range to the layer above the session (Section 19.3.4 of [PQST]), held with its version, and acknowledges it as the object mechanism requires; the cohort layer never sees a partial or unauthenticated object, because the session sealed it.¶
0x10. Name: COHORT; Body: cohort_id(16) sequencer_fp(48) policy_version(4) epoch(4) member_index(2) member_state(1) in_reply_to(4) (79 octets); Delivered to: each member; When: on a new session, on every change of the member's own state, and whenever anything else is delivered¶
0x11. Name: GROUP_KEY; Body: epoch(4) flags(1) key(32) (37 octets); Delivered to: each ACTIVE member; When: when the member's key is older than the epoch: on a new session, on rotation, on a report naming an older epoch¶
0x12. Name: POLICY; Body: policy_version(4) body(*); Delivered to: each ACTIVE member; When: when the member's policy version is older than the current one¶
flags bit 0, REMOVAL, marks a key whose commit dropped a REMOVED member (Section 8); a holder erases its previous epoch's key on receiving it and accepts no proof for that epoch (Section 10). Senders MUST set every bit other than bit 0 to zero; receivers MUST ignore every bit other than bit 0, and MUST still honour bit 0, so that a later version can extend the octet. Kinds 0x13 ROSTER and 0x14 MEMBER_EVENT are registered (Section 16) for a roster service and have no body in this version; a member MUST accept and ignore them. member_state is 1 ACTIVE, 2 SUSPENDED, 3 EXPIRED, 4 REMOVED (Section 7); ACTIVE and REMOVED are the authority's, SUSPENDED and EXPIRED the issuing gateway's observations (Section 4.1). in_reply_to is the report_id of the REPORT the object answers (Section 6), or 0 when the object is unsolicited: only an object answering, on the same session, a report the member sent within report_interval confirms the member's standing, from the report's sending, and carries its own member_state (Section 5.1, Section 8 and Section 10). member_index is 16 bits and unique within an epoch (Section 7). A POLICY object is bounded by MAX_OBJECT (16,384 octets, Section 19.3.2 of [PQST]), carried in segments when it exceeds a packet, and a sequencer MUST refuse a larger policy. The body of POLICY is opaque to this layer: an application-defined document the member applies, versioned by the sequencer alone. Every object is issued by the sequencer, versioned per session as Section 19.4 of [PQST] requires, and, when it names an epoch and a policy version no lower than the holder's (Section 5.1), replaces the holder's previous version of its kind.¶
The object version of Section 19.3 of [PQST] orders the objects of one kind within one session and says nothing across sessions: a new session's versions start again, and an older sequencer state delivered on it is as well-formed and as authentic as a newer one. Freshness is the cohort layer's own judgement, made after the session has delivered an object and apart from it.¶
A member keeps, per cohort identity (Section 2), the highest epoch and the highest policy_version it has accepted, and persists both (Section 4). It judges every object delivered on a session to the sequencer by the following steps, in this order; the table of Section 8 renders the same steps.¶
A COHORT object naming a cohort_id other than the one the member named in the session's first REPORT (Section 6), or a sequencer_fp other than the identity the session authenticates, is refused: nothing is applied, the objects staged for the session included. The first COHORT fixes the session's cohort only by agreeing with the member's nomination, and one session serves one cohort (Section 3).¶
Before the first accepted COHORT the session belongs to no cohort, and a GROUP_KEY or POLICY carries no cohort identifier: the member stages it, at most one object per kind, the latest, and applies the staged objects under the steps below when the first COHORT is accepted, so that a key can never be taken for another cohort's.¶
A COHORT or GROUP_KEY naming an epoch below the member's highest, or a COHORT or POLICY naming a policy_version below its highest, is applied to nothing: the object is acknowledged at the object layer, so that its sender stops sending it, and the member sends a REPORT naming the epoch it holds (Section 8), so that a sequencer behind it learns so. An acknowledgement of an object is therefore delivery, never acceptance; a sequencer that needs to know what a member holds reads the member's report.¶
A GROUP_KEY for the highest epoch the member has accepted, when it holds no key for that epoch, after a restart or a loss of the key, installs that key once the session's cohort is fixed (step 2): it neither lowers nor advances the persisted high-water mark and confirms no standing, which step 7 alone does. One for the epoch the member holds a key for, carrying the same key, is idempotent and applies nothing; one carrying a different key is a conflict: two keys for one epoch cannot both come from a sequencer that committed as Section 8 says. The member keeps the key it holds, applies nothing, reports, and raises a security-class event to the layer above; it MUST NOT replace the key.¶
A COHORT naming policy version N and the POLICY carrying N arrive in either order: the POLICY whose version equals the member's highest installs when the member holds no body for it yet, and a second body under a version already installed is a conflict like a second key, kept out and reported.¶
A COHORT or GROUP_KEY for a higher epoch, and a POLICY for a higher version, are applied as they arrive, whichever of COHORT and GROUP_KEY comes first, the member persisting the new high-water mark before it acts on the object: a GROUP_KEY is issued only to a member the sequencer holds ACTIVE in that epoch (Section 8), so a key arriving before the COHORT object for its epoch says what that object will say of the member's state in the list, and confirms no standing, which step 7 alone does; and a policy change is independent of the epoch by design. A COHORT for the epoch the member holds is taken as well, since the member's own member_state changes within an epoch (Section 7); what it may change is decided by step 7 alone.¶
The member's own member_state, and the standing it confirms, come only from a COHORT whose in_reply_to names a REPORT the member sent on the same session within group_suspend, the standing a report's sending gives, and only when that report was sent, by the member's own clock, after the report whose answer set the state the member holds, whatever the sessions involved: such an object answers that report and is as fresh as it. Standing then runs from the report's sending, not from the answer's arrival, so that an answer delayed confirms what the report asked and grants no lifetime for arriving late (Section 10), and the states the sequencer assigns are ordered by the member's own reports across sessions, so that no delayed object, on any session, can take the member back to an earlier one. A COHORT naming the member SUSPENDED, EXPIRED or REMOVED confirms no standing, whatever it answers.¶
Any other object fresh under steps 3 to 6, unsolicited, late, or answering a report sent on another session, synchronises the cohort identity, the epoch, the index and the policy version and nothing else: it renews no standing and changes no member_state.¶
The rules are the same on every session with the sequencer, on a new session (Section 9), after a re-anchor and after a restart: the session says who delivered an object; the cohort layer says whether it is still current.¶
Stream data on STREAM_COHORT, one frame per DATA payload, typed by its first octet. Frames are plain data: a lost frame is not retransmitted by this layer, and the sequencer treats a frame as data from the member, never as an instruction; in particular a REPORT naming an epoch above a gateway's own is a signal to read the authority, never an instruction to stop issuing (Section 10).¶
REPORT = 0x01 cohort_id(16) report_id(4) epoch(4)
policy_version(4) path_count(1) addr_count(1)
*( len(1) addr(len) )
LOCATE = 0x02 request_id(4) fingerprint(48)
LOCATE_RESP = 0x03 request_id(4) fingerprint(48)
[ age(4) *( len(1) addr(len) ) ]
EPOCH_PROOF = 0x04 cohort_id(16) epoch(4) proof(16)
addr = family(1) address(4|16) port(2)
¶
addr is a transport address, family as in INNER_ADDR (Section 19.4.4 of [PQST]), so len is 7 or 19, and nothing follows a frame's last addr; the member reports the addresses it can be reached at. The fixed sizes are collected in Appendix A.4.¶
cohort_id names the cohort the session serves (Section 3): a sequencer of another cohort ignores the report, and a session's first report binds it (Section 9). report_id is a counter the member keeps per session, increasing, never 0 and never reused within the session, persisted before a report leaves so that a restart that restores the session continues it: a COHORT object answering the report (in_reply_to, Section 5) can then be told from one answering an earlier report or none, and an answer to a report sent before a restart, whose ledger the restart lost, confirms nothing.¶
The counter is 32 bits so that a session's life exhausts it under no reporting interval in use; a member that has nonetheless used every identifier opens a new session. epoch is the epoch whose key the member holds, or 0 when it holds none; policy_version is the version of the policy body it holds, or 0 when it holds none, so that a member whose restart lost the body, which it need not persist (Section 4.3), is issued it again; policy versions start at 1 and 0 is reserved for this meaning, as epoch 0 is for the key: a sequencer that has issued no policy holds version 0 and issues no POLICY. path_count has no semantics in this version: senders MUST set it to 1, receivers MUST ignore it, and it is independent of addr_count. The member sends a REPORT when Section 11 says, and the sequencer answers it as Section 8 says.¶
Bound and validation. addr_count is at most 8, the pairwise max_paths (Section 15 of [PQST]), so that a report never needs more than one packet: 191 octets with eight IPv6 addresses, 95 with eight IPv4. A receiver validates a frame before acting on any of it: the frame type is known, the fixed fields are present, the address count is at most 8, each address has a length of 7 or 19 that matches its family octet, and no octet follows the last address; a frame that fails any of these is discarded whole, nothing of it applied, and counts as a protocol violation of the carrying session (Section 20.3 of [PQST]). LOCATE_RESP carries at most the eight addresses of the report it answers and is validated the same way (Section 6.2).¶
LOCATE carries a request_id the requester chooses as a 32-bit counter never reused within the session, never 0, a requester that has used every identifier opening a new session, and the fingerprint it asks about; a request is 53 octets. LOCATE_RESP echoes both, and the requester matches an answer to an outstanding request by both and discards any other answer, keeping per subject the highest identifier answered and discarding an answer to a lower one, so that requests complete in any order, an answer arriving after its request was given up is attributed to no later one, and no answer changes the addresses held for an identity it does not name. An answer with no octets after the fingerprint, 53 octets (1 + 4 + 48), says only that the requester is not to find that identity: unknown, not ACTIVE and not discoverable by this requester are one answer; a 51-octet buffer, the empty answer's size when the identifier was 16 bits, is not an answer. age is the seconds, at the gateway answering, since the latest report it holds from that member (Section 11); an answer carries at most the eight addresses of that report (Section 6.1). What an answer means, and under what policy one is given, is Section 11.¶
proof is the first 16 octets of HMAC-SHA-384(key, label("group epoch proof") || cohort_id || epoch(4) || binding) under the epoch key named, with HMAC-SHA-384 as in Section 6.5 of [PQST] and label as in Section 7 of [PQST], where binding is the 32 octets Export(lineage, "PQST group epoch proof", cohort_id || epoch(4), 32) of the pairwise session the frame travels in (Section 8.6 of [PQST]): both ends of that session derive it, it changes at every re-anchor, and nobody else holds it. The proof names its cohort, since two members may share more than one, and a verifier accepts it only for a cohort it holds and the proof names. It says that the session's peer holds the epoch key, at least as recently as the session's current generation; it is bound to that session and generation, so that a proof taken from another session, or from an earlier generation of this one, fails verification, and no challenge travels on the wire. It encrypts nothing.¶
What a proof means. A valid EPOCH_PROOF demonstrates possession of the named epoch key by the authenticated peer of the carrying session, and nothing more: not current membership, not the absence of a removal since the key was issued, and no authorization for any operation. A member MUST NOT infer current cohort authorization from a proof alone: a cohort-dependent decision also requires that the verifier's own standing has not lapsed and that the epoch named is one the verifier accepts (Section 10). A member presents a proof when the layer above asks it to; this layer defines what a proof means and when one is accepted, not when one is sought. A member whose standing has lapsed presents none and accepts none.¶
A proof is a statement of possession, not a challenge and its response: it carries no freshness beyond the generation it is bound to and needs none inside an authenticated session, whose own replay protection excludes a copy injected by anyone but the peer; a proof repeated within a generation is the same statement made again. Nor does a proof say when its maker received the key, or that it has not since expired or been removed: an expired member that kept a copy of the key makes the same proof as a newly admitted one. A membership decision about another member comes from the sequencer, never from a proof: in this version LOCATE (Section 11) answers only for an ACTIVE member, under the discovery policy, as fresh as its age, and only an asker whose own standing holds can act on the answer; offline, a member has nothing but its own standing and its peers' statements of possession, and decides accordingly. A roster object is the next version's (Section 5).¶
admit
(none) -------> ACTIVE <----------------------+
| | returns: re-anchor
dormant longer | | or FRESH, re-proof,
than group_suspendv | membership lookup
SUSPENDED ---------------------+
|
dormant longer |
than group_expire v
EXPIRED --> re-admission; dropped at the next
rotation (scheduled, batched)
from any state, on revocation, a failed re-proof or suspected
compromise: REMOVED (one commit at once; the member's sessions
closed with AUTHZ_REVOKED)
¶
ACTIVE: a member with a live or recoverable session to the sequencer; it holds the current epoch key.¶
SUSPENDED: the member's session to the sequencer has been DORMANT or closed longer than group_suspend.¶
Entitlement. It is entitled to return without re-admission. It receives no new group material while suspended; a rotation made meanwhile reaches it when it returns.¶
Never a commit. Suspension is a fact about a friendly device and is never a commit. The announced suspension of Section 19.5 of [PQST] moves a member to SUSPENDED at once.¶
The member's clock. The member's own view runs on the same clock: its standing lapses group_suspend after the sending of the last report the sequencer confirmed (Section 10), so that the member's lapse precedes the sequencer's suspension by at most half of group_suspend, the confirmation interval of Section 8, plus the time each takes to notice the outage and their clocks' drift; nothing the member does on its own, and nothing from a peer, renews its standing.¶
EXPIRED: dormant longer than group_expire.¶
Re-admission. At the gateway that holds it expired it MUST be re-admitted, and its return there is answered by closing the session with AUTHZ_EXPIRED (Section 20.2 of [PQST]).¶
Not a rotation of its own. The next rotation drops every expired member in the same commit.¶
A gateway's own observation. Until that commit, expiry is a gateway's own observation, from its own timers over its own sessions: another gateway of the group, which has not observed the dormancy, holds the member ACTIVE or SUSPENDED and serves it on its return as Section 10 says, and the authority settles the matter only by the commit that drops the member. A member is denied the key by a commit, a removal or that rotation, and by nothing else; re-admission after expiry is housekeeping, not a security boundary.¶
REMOVED: a security decision of the sequencer's operator or policy.¶
At once. The sequencer closes the member's sessions with AUTHZ_REVOKED and rotates at once (Section 8), and that rotation's commit drops it from the list.¶
Every return before the drop. Every return of the member before the drop, by a new session, a re-anchor of a session it had, or a path of that session validated again (S14 of Section 18.2 of [PQST]), is refused with the same close, whether or not the rotation that carries the removal has committed yet: a removed member's session is never left open and unserved. The same holds of an EXPIRED member's return at the gateway that holds it expired, with AUTHZ_EXPIRED.¶
One close reason per cause. A return is answered with one reason, the same on the session the member had and on a new one, so that an application that branches on the reason, re-admission against staying out, branches the same way for one identity whichever session came back:¶
| Return | Reason |
|---|---|
REMOVED, before or after the rotation that drops the member, on the session it had or on a new one |
AUTHZ_REVOKED
|
| an identity not on the list, dropped at a rotation or never admitted, on any session |
AUTHZ_REVOKED
|
EXPIRED, at the gateway that holds the member expired: its return before the drop, and the close of its session at the drop |
AUTHZ_EXPIRED
|
A rotation that drops a member changes no reason: once dropped, the identity is not on the list. The member treats either reason as the end of its membership (below).¶
Reachability is never an input to a security decision. The only transitions a timer makes are ACTIVE -> SUSPENDED -> EXPIRED, and neither is a commit. The suspension clock starts when the sequencer's session to the member goes DORMANT or is closed for any reason other than the sequencer's own decision (AUTHZ_REVOKED, AUTHZ_EXPIRED), and stops when the session is ESTABLISHED again.¶
Return. A member returns when a session between it and the sequencer is ESTABLISHED: the restored session re-anchored (Section 16 of [PQST]), a new FRESH exchange, a RECOVERING session back on a validated path (transition S14 of Section 18.2 of [PQST]), or a DORMANT one re-anchored (S17).¶
Proof at every re-anchor. Every re-anchor of a member's session proves the requester: the requirement is the sequencer identity's, not a session's. Every gateway of the group requires proof of the requester in every re-anchor it serves under that identity (Section 16.3.3 of [PQST]), from its own state or from a sealed record (Section 16.6.3 of [PQST]), and before it serves the request, so that the requirement rests on nothing a record carries, nothing the first REPORT says and no state of the gateway that established the session: a gateway that has never seen the session enforces it as that one did (Section 9). A CONTINUE therefore carries ct_req and a CONTINUE_0RTT is answered PROOF_REQUIRED and repeated as CONTINUE, after which the member offers the proof in every request of that session (Section 16.3.3 of [PQST]); a return from dormancy and a return after a restart prove the device's identity key before anything is delivered, and a FRESH exchange proves it by construction. The cost is one encapsulation per re-anchor at each side.¶
Membership lookup. The sequencer then performs a membership lookup by the session's peer identity, and the member's state decides what happens: ACTIVE or SUSPENDED becomes ACTIVE and receives whatever it lacks (Section 8); EXPIRED is closed with AUTHZ_EXPIRED; REMOVED, or an identity not in the list, is closed with AUTHZ_REVOKED. No credential is carried by the member: identity re-proof plus the lookup is the whole admission check.¶
The member's side. On its side the member treats a session the sequencer closes with AUTHZ_REVOKED or AUTHZ_EXPIRED as the end of its current membership: it discards the epoch key and reports the corresponding state to the layer above, REMOVED for the first reason and EXPIRED for the second, so that an application can tell staying out from re-admission. A session the member closes itself, as the grantor of a grant that lapsed (Section 17 of [PQST]), is a closed session and nothing more: the member infers no removal from it, keeps its key and its standing under the rules of Section 10, which lapse on their own clock, and regains standing only through a new session, the membership lookup at its return and a confirmation (Section 8).¶
Admission is out of band in this version: the member's identity is registered with the sequencer as a peer, with its initial policy, and it is ACTIVE from the first session it opens.¶
No epoch advance. Admission in this version advances no epoch: a new member receives the key existing members hold, so possession of the epoch key says nothing about when its holder was admitted or whether it is still a member (Section 6), and the key in this version serves EPOCH_PROOF alone and MUST NOT protect application data; a data plane deriving traffic keys from the epoch key gets no backward access control from an admission that does not rotate, which is why the group data plane rotates on admission (Section 13).¶
The admission commit. The sequencer's record of an admission, the member's member_index and ACTIVE state, is a commit (Section 8), a revision that advances no epoch, made before the member is served, so that two gateways cannot allocate one index and a restart cannot forget an admission. The sequencer allocates the lowest member_index not held; an index is unique within an epoch and returns to the pool at the rotation that drops its holder; a cohort holds at most 65,535 members, and an admission beyond that is refused (the index will partition the data plane's nonce space, where reuse within an epoch would be fatal). Re-admission of an EXPIRED member is the same administrative act.¶
Rotation on admission. A sequencer MAY nonetheless rotate on an admission, for one admission or for the admissions of an admission_batch window as Section 13.4 describes, under its own policy: a rotation is a commit it may make at any time (Section 8), and a deployment that wants the admission boundary the group data plane will need has it in this version without a change to the member's state machine; the default is not to rotate.¶
The membership epoch advances only on a commit that rotates, and only the sequencer commits; an admission or a policy change is a commit too, one that advances no epoch (below). The rotations:¶
the scheduled rotation, every rotation_interval;¶
a security event: a removal, or a forced rotation by the operator.¶
A commit is one successor state, the next revision of the authority (Section 4):¶
the epoch, advanced by a rotation and unchanged by an admission or a policy change;¶
the epoch key, new and random when the epoch advances and unchanged otherwise;¶
the member list with its member_index assignments, without the REMOVED members and the members the committing gateway holds EXPIRED by its own timers (Section 7), which is how an observation enters the authority:¶
whether a REMOVED member was dropped;¶
the policy_version; and¶
a commit identifier the sequencer chooses for the logical operation, a rotation, a removal, an admission or a policy change, and keeps across its retries.¶
The sequencer carries it out in this order, as the anchor exchange of Section 9.3 of [PQST] is ordered:¶
Submit. The commit is one conditional transaction, naming the revision it succeeds and the sequencer's term; the sequencer proceeds only when the authority acknowledges it, and the epoch is part of the record, never advanced apart from it.¶
Refusal. A transaction the authority refuses, because the revision or the term is no longer current or the replication is unavailable, leaves the epoch, the key and every member exactly as they were; the operation is retried on the sequencer's timer, under its current term, until it succeeds or the sequencer learns that it has lost its authority.¶
Retry. A retry is the same transaction, the same commit identifier, predecessor revision and successor, which the authority holds it to (Section 4.2), so that no retry is a second operation.¶
Not deferred. A removal's close of the member's sessions is not deferred with its rotation.¶
Read on an unknown outcome. When the outcome of a transaction is unknown, because the acknowledgement was lost or the connection failed, or when the authority refuses it, the sequencer reads the latest revision before anything else:¶
Its own identifier. A successor carrying its commit identifier is its own commit, succeeded, acknowledged late or made by an earlier attempt, and the sequencer continues from the authority's record of it.¶
Another identifier. A successor carrying another identifier means another sequencer committed under a later term, and this one has lost its authority, which says nothing of whether its own operation committed before that: it resolves that by the lookup of Section 4.2 with its commit identifier,¶
A refusal of its term. A refusal while the revision it named is still the latest is a refusal of its term, and it has lost its authority likewise.¶
Cannot be read. While the authority cannot be read the sequencer retries with the same identifier, and it never derives a second successor for one operation (Section 4).¶
One operation at a time. An admission or a policy change arriving while a rotation's outcome is unknown, or a rotation while an admission's is, waits until that outcome is known, since a second transaction submitted beside an unresolved one could not tell its own refusal from another sequencer's commit.¶
Issue only once the commit is known durable, and never twice for one commit. The dropped members' indices return to the pool, and GROUP_KEY, with REMOVAL set when the commit dropped a REMOVED member, with COHORT are issued to every member that is ACTIVE and reachable at that moment, over its session, each delivery acknowledged by the object mechanism.¶
No member ever holds a revision the authority has not seen, which is the measurable form of CG2 under gateway failure. A member that is ACTIVE but RECOVERING or briefly DORMANT receives the objects when it returns: delivery is the session's business, and the cohort layer never retries on its own. Nothing is issued to SUSPENDED or EXPIRED members.¶
Delivery on return (what a returning or newly connected member lacks, as the REPORT it sends on every return says, Section 11):¶
What it lacks:¶
What decides nothing. What the sequencer issued on the session before the member left, and what the member acknowledged, decides nothing, since the member may hold neither.¶
Not issued again. An object issued before the member left and not yet acknowledged is not issued again: PQST sends it again when the session is back, whether by a re-anchor or on its generation (Section 19.3.3 of [PQST]). A new session with a member starts with nothing delivered on it.¶
A report behind the current state. A REPORT naming an epoch below the current one, or a policy version other than the current one, from a reachable ACTIVE member, is answered at once with a COHORT object naming the report and the current GROUP_KEY or POLICY again, as a new object version (one naming an epoch above the current is a signal to read the authority, Section 10).¶
After a restart. A restarted member, which holds no key and no policy body unless it persisted one, reports epoch 0 and policy version 0 and so receives both on the session it kept, a session whose re-anchor after the restart proved the requester, and its standing with them, from that report's sending.¶
The report, not the record. The object layer's record that a key or a policy was delivered before the restart says nothing of what the member holds after it (Section 19.3.4 of [PQST]); the report does.¶
Confirmation. A COHORT object for the epoch a member holds, or a newer one, that answers, on the same session, a REPORT the member sent within group_suspend (in_reply_to, Section 5) and names it ACTIVE confirms the member's standing from the report's sending (Section 10).¶
What confirms nothing. An object answering no such report confirms nothing, so that a delayed object, however authentic, renews no standing the sequencer has stopped vouching for, and one delayed confirms only what its report asked for, with the lifetime that report's time gives.¶
When a gateway answers. A current gateway answers a REPORT from an ACTIVE member with a COHORT object naming the report:¶
at once when the report names an epoch or a policy version other than the current one, or when it is the first report on a session the gateway has bound;¶
and otherwise when the last report it answered on this session is older than group_suspend/2 - report_interval/2, three eighths of group_suspend at the largest interval, so that the first report at or past that age is answered whatever the jitter of the reports' arrival, every other report at the largest interval, and never one arriving a moment short of a threshold of half.¶
A rate control, not a condition. The interval is a rate control for a member already confirmed, never a condition for confirming one that has just returned, restarted or lost its standing, which the first answered exchange confirms.¶
Unasked objects. Objects it issued unasked in the meantime, on a rotation, a policy change or the member's return, name no report, however many await an answer, confirm nothing at the member, and do not postpone the next answer.¶
As it arrives. A report is answered as it arrives or not at all, and an answer the object layer's retransmission delays confirms from the report's sending for as long as that standing lasts, so that a reachable member reporting every report_interval is confirmed at least twice within its standing with one report lost.¶
The cost. One small object per member per group_suspend/2 - report_interval/2, beyond what the cohort's own changes send.¶
A gateway not known current. A gateway that does not know its replica to be current issues none (Section 10), and a member parked on such a gateway lapses, as it should.¶
Acceptance of what arrives, the steps of Section 5.1 rendered, the same on the session the member had and on a new one:¶
| What arrives | Against what the member holds | The member |
|---|---|---|
COHORT or GROUP_KEY for a lower epoch
|
a higher epoch accepted | applies nothing, acknowledges, reports its epoch |
GROUP_KEY for the highest epoch it has accepted, holding no key for it (after a restart) |
the epoch accepted, no key | installs the key, the high-water mark unchanged, no standing confirmed |
GROUP_KEY for the epoch it holds, the same key |
the key | applies nothing (idempotent) |
GROUP_KEY for the epoch it holds, another key |
the key | a conflict: keeps its key, reports, raises a security-class event |
COHORT for the epoch it holds |
its standing | applies the index and the policy version when they are fresh; applies member_state, and confirms standing, only under step 7 of Section 5.1
|
COHORT or GROUP_KEY for a higher epoch |
a lower epoch | applies it, persists the epoch first, in whichever order the two arrive |
POLICY, or a COHORT, with a lower policy_version
|
a higher version accepted | applies nothing, acknowledges, reports |
POLICY with a higher policy_version
|
a lower version | applies it, persists the version first |
Sessions of dropped members. A session the sequencer closed, or that belonged to a member dropped at a rotation, that comes back (a path validated, a re-anchor, an establishment) is closed again with the reason of Section 7, AUTHZ_REVOKED for an identity no longer on the list; the tombstone of Section 19.6 of [PQST] serves the same purpose on the wire.¶
The reconnect storm is a non-event. N suspended members returning at once each come back, by a re-anchor or on a validated path, re-prove if restarted, are looked up and receive the objects they lack: zero commits, zero deliveries to other members.¶
Both sides may recover a lost session by a FRESH exchange at the same moment (the member's recovery schedule and the sequencer's both interleave one, Section 16.7 of [PQST]), and concurrent sessions between two peers are permitted (Section 9.6.2 of [PQST]); the two endpoints may then see the two sessions established in opposite orders.¶
The lower identifier is kept. The sequencer keeps the session with the lower identifier and closes the other, lower in the unsigned big-endian order of the 16 octets of the session identifier, Export(session, "session id", "", 16) of Section 8.6 of [PQST], the comparison Section 9.6.1 of [PQST] makes of identity fingerprints, which the member uses as well when it chooses which live session to report on.¶
One session per member and cohort. A session a member had is closed when a new one with the member is established and kept, so that the sequencer has one session per member and cohort.¶
Scoped to the cohort. The rule is scoped to the cohort: a new session belongs to no cohort until the member's first REPORT on it names one (Section 6), and until then it displaces nothing and is served nothing.¶
A session re-anchored toward another gateway. A session re-anchored toward a gateway of the sequencer's identity that has not served it (Section 10), which that gateway sees arrive as a re-anchor rather than an establishment, is new to it in the same way:¶
proof of the requester is required of it as of a new session, whether that gateway serves it from replicated state or from a sealed record (Section 7);¶
it belongs to no cohort there until the member's first REPORT on it, which the member sends on every re-anchor (Section 11);¶
and that report binds and serves it.¶
Once bound. Once a session is bound, the member's reports, and so its reachability record (Section 11), come from that session alone, a report on a session it superseded updating nothing.¶
Different cohorts. Two sessions of one member for different cohorts coexist, and duplicates are resolved among the sessions of one cohort only; a sequencer looks the member up in the cohort its report names before it issues anything, and a member's new session for another cohort sends its first REPORT whatever sessions the member holds for other cohorts, since the choice among duplicate sessions is made within a cohort.¶
The member follows the objects. The member follows the session the sequencer's objects arrive on and treats every session to the sequencer that is ESTABLISHED as reachability; among several live sessions it reports on the one with the lowest identifier until an object names another.¶
Never ESTABLISHED. A session that was never ESTABLISHED (a half-open exchange left by an abandoned recovery attempt) is not reachability, whatever paths it validates, and is not followed.¶
A member whose session to the sequencer is RECOVERING or DORMANT is in a partition from the cohort's point of view, whatever the cause. In it:¶
its pairwise sessions with other members continue and carry data; the cohort layer does not touch them;¶
it keeps its last epoch, epoch key and policy and acts on them while its standing holds:¶
Standing. Standing is confirmed by a COHORT object for the epoch held or a newer one that answers the member's own recent report on the same session and names it ACTIVE (Section 5.1 and Section 8), and lapses group_suspend after the sending of the last report so confirmed: an answer arriving after that deadline restores nothing, within the acceptance window or not, so that the bound CG3 states holds from the last report the sequencer answered, not from the arrival of its answer.¶
Proofs within standing. Within its standing the member may present EPOCH_PROOF to peers and accept theirs. A peer with a newer epoch accepts a proof for the previous epoch only within group_suspend of the moment it received its own epoch, and only when its own key did not arrive with REMOVAL set; when that window ends it erases the previous key. The reasons:¶
The time bound. A member's legitimate lag is bounded by its partition, and past group_suspend it is suspended and owed nothing.¶
The REMOVAL flag. The member a commit removed holds exactly the key before it, so a peer that has received that commit erases the previous key at once and accepts no proof for it (CG3).¶
A skipped epoch. A peer that skipped an epoch holds no previous key either, so it can accept no proof for the previous epoch.¶
After the lapse. Once its standing has lapsed the member presents no proof, accepts none, and sends and accepts no group traffic (Section 13), while every session of its continues; at group_expire after the last confirmation it erases its epoch key.¶
Nothing else renews standing. Nothing but the sequencer's COHORT renews standing: not a re-anchor or a new session, not pairwise traffic, not a peer's proof, and not an object already accepted or an older one, which Section 5.1 refuses.¶
A removed member's residual standing. A removed member's residual standing is thus confined to peers that have not received the removal, that is, to a partition, and ends at those peers' lapse, at most group_suspend after the sending of a report whose confirmation preceded the removal.¶
The cost to the honest. The removal rule has a cost to the honest: a member one epoch behind, whose peers' keys arrived with REMOVAL, is refused by them until its own key arrives, at its return; the rule is epoch-wide rather than per member because this version has no roster, and Section 17 measures the cost;¶
it sends reports when it can and discards all but the latest;¶
it cannot admit, remove or rotate, and does not elect a sequencer: two members in one partition gain nothing from each other but data and discovery hints.¶
Healing is the member's session becoming ESTABLISHED again (Section 7, return). The member sends its report with the epoch it holds; the sequencer answers with what it lacks. An epoch the member missed is not merged: the member committed nothing. A sequencer gateway cut off from the authority, or without the term to commit, serves members' sessions, issues the objects of Section 5 only while it knows its replica to include the latest commit (below), and defers commits until it holds a current term and a current read of the authority (Section 4); a member it cannot confirm lapses as the standing rule above says, and a removal's sessions are closed at once even while its rotation waits.¶
Gateways and freshness. A session's authentication proves which gateway group answered, not that the gateway's replica of the group's state is current: a gateway behind its replicas holds authentic objects of an epoch the cohort has left.¶
Only from a current replica. A gateway issues the objects of Section 5 only from a replica it knows to include the group's latest commit, a property the deployment's replication MUST provide to each gateway (Section 14).¶
How long it knows. The knowledge is a fenced lease's for its validity, or a linearizable read's at its linearization point with whatever staleness allowance the deployment states and counts in its exposure (Section 4.2), and ends with it: a gateway cut off from the authority knows its replica current until that lease or allowance runs out and no longer, whatever its last read showed, and from then issues nothing that confirms or restores standing, so that a gateway isolated with its members renews their standing from a revision the group has left for at most that allowance.¶
When it does not know. A gateway that does not know this serves the member's session like any other but issues no cohort object, and the member stays on what it holds, as in a partition.¶
A report above the gateway's epoch. A REPORT naming an epoch above the gateway's own is a signal to read the authority before the next issuance, not proof: the session authenticates the member, not the truth of its statement.¶
If that read shows the replica behind the latest commit, the gateway issues no cohort object until a read shows it caught up, and continues to serve the member's session;¶
if the read shows the replica current, including when the reported epoch is above the authority's own latest, the gateway discards the signal, counts it, and continues to issue, so that no member can stop a current gateway's confirmations by a claim.¶
A proof is not a signal. An EPOCH_PROOF is not such a signal: a proof for an epoch the gateway does not hold cannot be verified, and one it can verify names an epoch it already holds.¶
The member's own enforcement. A member enforces Section 5.1 whichever gateway it reaches, so that a gateway that is behind without knowing it can take the member nowhere; what it cannot do is bring a member the commit it has not seen, which only a current gateway can.¶
Without commit authority. A gateway without current commit authority (Section 4) admits, removes and rotates nothing, and changes no policy: those are commits, and it defers them; for a returning member it serves the session and delivers the objects of a revision it knows current, and no more.¶
Fails closed. Anything that grants or restores standing fails closed; the members' sessions and their own behaviour in a partition, above, are untouched.¶
Reachability. The sequencer keeps the latest report per member with its time, the latest being the one with the highest report_id on the session (Section 6), which the member increases with every report, persists, and never reuses, and which the session authenticates: a report delivered after a later one is ignored, so that an address the member has left cannot replace the one it reported since.¶
Binding is not selection. A session's first report binds it to the cohort and does not by itself make it the member's session: among the bound sessions of one member and cohort only the one selected under Section 9, the lower identifier, updates the member's record, the report that binds a session the selection does not keep counts for nothing and that session is closed, and when the selected session changes the order starts again from the newly selected session's reports.¶
Other sessions. A report arriving on a session the sequencer has not selected, has superseded or has closed updates nothing, however it authenticates, so that an address reported on an old session cannot replace the one reported since on the selected one.¶
Data, not instructions. Reports are data to the sequencer, never instructions.¶
Reporting. A member sends a REPORT on its session to the sequencer when the session is established, when it re-anchors, when a path of it is validated again after a loss (S14 of Section 18.2 of [PQST]), and every report_interval while the session is up, RECOVERING or DORMANT (Section 12).¶
The first report. The first report on a session names the cohort and binds the session (Section 6 and Section 9); the later ones are answered by the confirmation of Section 8. A gateway the session arrives at by a re-anchor sees that re-anchor's report as the session's first (Section 9).¶
On a DORMANT session. A report sent on a DORMANT session is what starts the session's fresh recovery schedule (Section 16.7 of [PQST]): a member whose network has returned is back within report_interval and the recovery schedule's first exchange that reaches the sequencer, not after dormant_retry.¶
Addresses. A member reports only the addresses its policy allows.¶
Discovery. A member sends LOCATE with another member's fingerprint; the sequencer answers LOCATE_RESP from that member's latest report only when it is ACTIVE and policy allows the requester to find it, by role or by name; otherwise the answer is empty, indistinguishable from an unknown member.¶
On the authority. The answer rests on the authority as standing does: a gateway gives the addresses only when the subject's ACTIVE state and the requester's permission are those of a state it knows current under Section 10, and otherwise the empty answer, so that a gateway cut off from the authority discloses no address under a policy a later commit may have withdrawn; the report it answers from is its own (below).¶
The membership lookup. LOCATE is also this version's membership lookup (Section 6): an answer says the member is ACTIVE now, as fresh as age; an empty one says only that the asker is not to find it.¶
The default. The default policy allows no discovery.¶
From its own reports. A gateway answers from the reports it holds: reports are not commits (Section 3, CG1) and are not replicated through the authority, so a LOCATE at a gateway the subject does not report to is answered empty, and age is the time elapsed at the answering gateway since the report it holds, with no meaning across gateways; a deployment that needs discovery across its gateways replicates reports among them by means of its own, outside the authority.¶
Several requests. The answer echoes the request's identifier and the fingerprint it is for, so a member may have several requests outstanding (Section 6).¶
The trust anchor. The fingerprint is discovery's trust anchor, and the only one it supplies: a member that lacks the peer's identity key obtains it by DISCOVER (Section 9.7 of [PQST]) at an address the answer gave and accepts it only when its fingerprint is the one the sequencer named, or holds it from provisioning.¶
A reachability service. Discovery is a reachability service: admission to a cohort provisions no identity keys, supplies no relay or routing, and guarantees no connectivity between members; it gives address hints and preserves the security context of the sessions members hold.¶
Hints, not routes. The requester opens or re-anchors a pairwise session with the member on the addresses as externally supplied hints (Section 15.3 of [PQST]), subject to the same validation and budget as any candidate. The sequencer never relays data.¶
Forwarding members. A member that forwards datagrams by routing key for others (Section 15.8 of [PQST]) is a path to both endpoints and nothing more: its dormancy changes their paths, never their sessions. Policy names which members may forward.¶
| Parameter | RECOMMENDED | Where |
|---|---|---|
group_suspend
|
the session's max_dormancy, or less by policy |
sequencer policy |
group_expire
|
7 days | sequencer policy |
rotation_interval
|
24 h | sequencer policy |
report_interval
|
the minimum of 60 s and a quarter of group_suspend
|
member policy; at most a quarter of group_suspend, so that a reachable member is confirmed at least twice within its standing with a report's delay and loss allowed for (Section 8) |
| previous-epoch proof window |
group_suspend
|
member |
| standing, from the sending of the last confirmed report to its lapse |
group_suspend
|
member |
| residual acceptance of a removed member's proofs at a peer that has not received the removal's commit (CG3, Section 10) | until that peer's own standing lapses: at most group_suspend after the sending of its last confirmed report, up to 24 h at the pairwise default max_dormancy and less by policy, plus the staleness allowance of Section 4.2 where the deployment issues on reads rather than a lease |
member |
| an answer accepted as confirming the report it names | within group_suspend of the report's sending |
member |
| confirmation of a reachable member | in answer to the first REPORT after the last answered is group_suspend/2 - report_interval/2 old |
sequencer |
| epoch key erased without confirmation |
group_expire
|
member |
Relations. 0 < group_suspend < group_expire, so that the states of Section 7 are entered in their order whatever max_dormancy a deployment sets, and 0 < report_interval <= group_suspend/4; an implementation MUST refuse a configuration that violates either. A deployment that issues on linearizable reads with an allowance between read and issuance (Section 4.2) adds that allowance to the residual acceptance above, since a confirmation issued within it may rest on a revision a removal has left.¶
This version carries no group data. This section describes the data plane the next version is intended to add, so that the shape of the unified session is visible, and lists what is still open. Nothing in it is normative, and nothing in it changes [PQST].¶
A PQST session has an identity-bound secret that only its endpoints hold, traffic keys and a data plane under it, continuity across path loss and restarts, generations that ratchet, replay protection per direction, and an exporter. The control plane of this document already gives the group its continuity (CG1, CG2) and a secret that rotates on schedule and at once on removal: the epoch key. What it lacks is a data plane under that secret, per-sender replay protection, and a group exporter. One property has no pairwise counterpart: in a session, a packet that opens came from the one other holder of the key; in a group, every member holds it.¶
The constructions of this section and the next are preliminary. The specification of that data plane will have to state the sender authentication and the replay-commit ordering its threat model needs, and will have to refrain from claiming individual sender integrity, malicious-member-resistant replay protection or confidentiality against a member wherever it has not provided them. The sender-key derivation and the packet format below are not final until the open question of Section 13.4 is decided.¶
The group secret is derived from the delivered epoch key, which itself still protects nothing directly: group_secret(e) = Expand(epoch_key(e), label("group traffic") || cohort_id || epoch(4), 48), with Expand as in Section 6.5 of [PQST] and label as in Section 7 of [PQST]. The sequencer makes and delivers the keys; members do not agree them among themselves as in MLS ([RFC9420]), and post-compromise security comes from rotation.¶
A native group packet under packet type 3 of [PQST], enabled by capability bit 0: one seal by the sender, opened by every receiver, on any carrier (unicast to each member, a relay that forwards by routing key without opening, multicast, or a radio net), in the protected-packet format unchanged:¶
the routing key is a group routing key that rotates per epoch;¶
epoch_id carries the cohort epoch;¶
seq carries the sender's member_index in its high 16 bits and the sender's counter in the low 48 (Section 12 of [PQST]).¶
Keys per sender, derived from the group secret and the sender's index, so that senders have disjoint nonce spaces and receivers one replay window per sender, with no coordination. This separates senders; it does not authenticate them.¶
Sender authentication per flow, where a flow needs it.¶
For small groups, a MAC per recipient, keyed from the sender's pairwise session with that recipient (its exporter, Section 8.6 of [PQST]).¶
For broadcast feeds, delayed key disclosure from hash chains in the manner of TESLA ([RFC4082]), whose anchors the sequencer distributes.¶
Traffic that must be attributable, such as a command, stays in pairwise sessions.¶
Partitions keep the frozen epoch of Section 10: members cut off from the sequencer go on under the last epoch they hold while their standing holds, group_suspend from the sending of the last confirmed report (Section 10, decision 2 of Section 13.4), after which they fall back to pairwise sessions, which never need the sequencer.¶
Where group packets may go. A member sends group packets only to addresses validated in a pairwise session with the recipient, or within the reflection budget of Section 15.4 of [PQST] toward any other, so that a member cannot aim the group's traffic at a victim.¶
A group exporter Export(group, ulabel, context, L) from the group secret, rotating with the epoch, under the "PQST group" exporter labels that [PQST] reserves.¶
The receive path would be the pairwise one, with a sender index in place of a lineage: header protection, the pre-authentication filter, one AEAD, the two-phase replay check. A group packet that fails would leave no trace in authoritative state and never create or change pairwise state, as Section 21 of [PQST] requires of any packet type an extension defines.¶
A member's compromise exposes the group's traffic until the next rotation; removal rotates at once, so the window is the time to detect the compromise.¶
Without a per-flow sender authentication, members can forge each other within the group.¶
The network sees the group: within an epoch every group packet carries the same routing key, so an observer links them to one group and sees its volume. Rotating the key per epoch bounds the linkage; nothing hides a group from a listener on its radio net.¶
One replay window per sender heard from, bounded by the membership.¶
A group packet proves possession of the epoch key, not membership (Section 6); sender authentication is per flow where a flow needs it, as Section 13.2 describes, and traffic that must be attributable stays pairwise.¶
A partitioned group sends under its frozen epoch while its members' standing holds, group_suspend from the sending of each one's last confirmed report (Section 10), then falls back to its pairwise sessions; a member erases the key at group_expire.¶
All three carriers are in scope from the first: unicast fan-out, a relay at the gateway group that forwards by routing key without opening, and broadcast media.¶
An inner address space per cohort is in scope: the sequencer assigns each member an address from the cohort's prefix; an inner packet to a member travels in the pairwise session with it, one to the cohort's broadcast address as a group packet.¶
An admission under the group data plane is a commit that advances the epoch with a new key, as a removal is: the new member receives nothing from which earlier traffic keys derive, and sends or receives group packets only under the epoch of its admission, so that the admission boundary is a cryptographic one (backward access control, Section 14).¶
Batching. Admissions arriving within admission_batch of one another are one commit; the recommended value is 0, one commit per admission, and a deployment with high churn raises it.¶
The previous key. Receivers keep the previous epoch's key for receiving only until group_suspend after the successor arrived, unless the successor came with REMOVAL, in which case the previous key is erased at once (Section 10): a member partitioned from the sequencer stays audible until its standing lapses, the connected members having one rule for packets and proofs, while the new member, which never held the previous key, reads nothing from before its admission.¶
Expiry. An expired member receives no later key and is dropped at the next rotation, as now.¶
The group plane authenticates the group, not the sender: every member derives every sender's key, so a malicious member can forge another member's index, advance the receivers' replay window for that sender so that the honest sender's next packet is refused as a replay, and repeat that sender's key and nonce.¶
Verify before the replay state moves. A receiver that needs to know the sender therefore verifies the per-flow sender authentication of Section 13.2, the per-recipient MAC or the delayed-disclosure scheme, before the claimed sender's replay state moves, and never on the group AEAD alone.¶
Without it. A flow without it gets what the group key gives, which is that an outsider forged nothing, and traffic that must be attributable or must survive a malicious member stays pairwise.¶
Stated before the receiver is written. These limits are stated in Section 14 of the data-plane version before its receiver is written.¶
Open. Whether receivers must constrain each individual sender against a malicious member, or whether a member is trusted because it holds the current group key. The answer decides whether native cohort traffic suits only telemetry of low impact or also security-critical control, and it decides the sender-key derivation and the replay-commit ordering; until it is taken, nothing in Section 13.2 and Section 13.3 is final, and decision 6 states the limit of the shared-key construction.¶
The security considerations of [PQST] apply to every session this layer uses; those below are the cohort layer's own. Section 13.3 states what the intended data plane would cost.¶
A malicious member with its keys in hand can read the control objects it receives, present EPOCH_PROOF, and send on its own sessions. It cannot admit, remove or rotate, alter another member's state, forge the sequencer's objects (sealed by that member's own session with the sequencer, whose identity it does not hold), or impersonate another member (pairwise identities, Section 9 of [PQST]).¶
Expiry is not revocation. A gateway's EXPIRED is its own observation of dormancy, and another gateway that has not made it serves the member as before until the commit that drops the member (Section 7). An operator who means to exclude a device removes it, which is a commit, closes its sessions with AUTHZ_REVOKED and rotates the key.¶
A captured dormant device can re-anchor as the device until re-proof is demanded and present the last epoch it saw.¶
No current key after a restart. It cannot obtain the current key after a restart without re-proof, because every re-anchor of a member session proves the requester and no key is on disk, only the freshness state of Section 4, which lets the device refuse an older epoch and obtain nothing.¶
No access after removal. Nor can it keep access after removal: removal rotates and closes with AUTHZ_REVOKED.¶
A member removed while disconnected cannot recover its cohort standing from the sequencer when it returns: its sessions to the sequencer were closed with AUTHZ_REVOKED at the removal, the tombstone of Section 19.6 of [PQST] answers a re-anchor with its old secret, and the membership lookup refuses a new session (Section 7).¶
Its pairwise sessions. Its pairwise sessions with other members are ordinary PQST sessions and may resume, as the decoupling of CG1 intends.¶
Its previous-epoch proof. Its previous-epoch proof is refused by every member that has received the removal's commit, whose key carries REMOVAL (Section 10); only a member in a partition, which has not received it, accepts that proof, and only within its own standing, which lapses group_suspend after the sending of the last report the sequencer confirmed, before the removal (CG3).¶
Validated. The rule is validated by the scenario of Section 17: a member removed during a ten-minute outage returns with its identity and its continuity secret while the others continue.¶
A compromised gateway of the group holds the group identity and can do everything the sequencer can; containment is re-keying the group identity and re-admitting members, as Section 23.3 of [PQST] states for gateway groups.¶
Jamming one member makes it SUSPENDED and then EXPIRED; it causes no commit, no delivery to anyone else, no rotation, and no loss of its sessions, which go DORMANT and recover.¶
Jamming the sequencer freezes admissions, removals and rotations; it breaks no pairwise session and produces no second epoch anywhere (CG2).¶
The epoch proof proves possession of a key by the peer of the session it travels in, bound to that session's current generation through the exporter (Section 6): not identity beyond the session's, not current membership, and no authorization. A peer MUST NOT act:¶
Independently authorized pairwise sessions. Removing a member from a cohort closes its sessions with the sequencer and ends its standing; it does not close its pairwise sessions with other endpoints, which are governed by their own authorization (CG1).¶
Standing, not survival. An application that requires cohort membership as a condition of a pairwise relation checks standing, through the proof and its own lapse, and does not infer it from the session's survival.¶
When a removal reaches a peer. A removal is enforced at a peer when that peer applies the commit or when its standing lapses; until one of these an application MUST NOT assume that every peer has stopped honouring the removed member.¶
No immediate termination. The control plane of this version does not promise immediate termination of all communication with a removed identity.¶
Removal while the authority is unavailable. The removed member's sessions with the sequencer close at once; the rotation that invalidates its key waits for the authority (Section 8). Until the commit lands the removed member holds the current key and other members accept its proof: a bounded exposure, ended by the commit or by the members' lapse, and the alternative, an epoch the authority has not seen, would be worse (CG2).¶
The bound is the deployment's. group_suspend is at most the session's max_dormancy (Section 12), so standing cannot be configured to last indefinitely; a deployment that needs long disconnected operation raises max_dormancy and accepts, in the same figure, that a removal reaches a disconnected peer no sooner than its lapse. A member measures its standing with the clock it measures dormancy with; after a restart it holds no key, and so no standing, until the sequencer confirms it (Section 4).¶
Backward access control. In this version an admission advances no epoch: the admitted member receives the current key and can compute proofs under it, and the key protects no application data.¶
Under a data plane. A data plane deriving traffic keys from the epoch key alone would let a member admitted mid-epoch derive the keys of traffic recorded before its admission; the next rotation ends the key's future use and takes nothing back, which is why the group data plane rotates on admission (Section 13).¶
Possession is not history. Neither possession of the key nor a valid proof establishes an identity's admission history or current standing; those come from the sequencer (Section 6).¶
Durability. No member ever holds an epoch the replicated state has not seen: a gateway that cannot replicate commits nothing (Section 8).¶
Freshness is not authentication. An authenticated session delivers an object that a gateway of the group holds; whether the object is current is the cohort layer's judgement (Section 5.1; CG4):¶
The rules. A member never lowers the epoch or policy version it has accepted, keeps the two across restarts, treats a second key for one epoch as a conflict, and a gateway behind its replicas issues no cohort objects (Section 10).¶
Replication is a security assumption. The replication between gateways is therefore a security assumption of this layer: it must let each gateway know whether its replica includes the latest commit.¶
Each rule's limit. A gateway that is behind and does not know it is held off by the members' own rule, which cannot, however, bring them a commit the gateway has not seen; a member that has lost its persisted freshness state is protected by the gateways' rule alone.¶
A gateway cut off from the authority holds authentic objects and live member sessions, and nothing in a session tells the member that the gateway's replica has been left behind.¶
What stops it. What stops it renewing standing from an obsolete revision is the validity of its knowledge of currency (Section 4.2 and Section 10): a fenced lease is good for its validity, a linearizable read for its linearization point and the staleness allowance the deployment states and counts, and no longer, so that an isolated gateway's confirmations stop within that validity and its members lapse as in a partition, within group_suspend of the last confirmation.¶
Bounded staleness, not currency. The allowance is a bounded staleness, not currency, a confirmation issued within it resting on a revision a removal may have left, by at most the allowance, which Section 12 adds to the exposure.¶
A read trusted thereafter. A replication that gave a gateway no such validity, a read once made and trusted thereafter, would let it renew standing against a removal for as long as the isolation lasted, and does not meet Section 4.2.¶
Storage rollback. The persisted freshness state protects against crash recovery and stale gateways, not against an attacker who restores a member's storage to an earlier image, which erases the record as it erases everything else; a deployment that needs that protection gives the record a monotonic counter or protected storage, outside this document.¶
Clocks and report cadence. A member orders its reports and measures its standing by its own clock (step 7 of Section 5.1), and the sequencer paces its answers by its own (Section 8); no clock is shared.¶
A forward jump. A member clock that jumps forward makes the member refuse an answer it should have accepted and lapse early, which fails toward the safe side.¶
A backward jump. One that jumps backward extends, by the size of the jump, the standing the member believes it has, and with it the window in which it accepts proofs under the epoch it holds, since the bound of CG3 at a peer is measured by that peer's clock.¶
Cadence. A member that reports less often than report_interval is confirmed less often and lapses sooner; one that reports more often is answered no more often than Section 8 paces.¶
What a deployment sees. A deployment whose members' clocks are not monotonic, or that cannot keep the report cadence, sees lapses it did not expect and, at the members with clocks that went back, acceptance longer than it expected, never standing the sequencer did not grant: the sequencer's view, which governs what is issued, is measured by its clock alone.¶
The authority is a dependency. The properties Section 4 requires of the replicated state are what the single history of CG2 rests on.¶
A fork. A deployment whose gateways write to a store without conditional commits and fenced terms can fork the history, two gateways each minting a successor to one revision with the sequencer's one identity, and no member can tell the two apart by cryptography; the members' one-key-per-epoch check (CG4) shows the symptom, not the cause.¶
Systems that qualify. Systems with the properties exist, a consensus-backed store with conditional writes and leases carrying fencing tokens, or a transactional database at serializable isolation; which one is the deployment's choice and outside this document.¶
While unavailable. While the authority is unavailable, no new epoch, admission or policy exists anywhere: revocation is then not globally effective, the limit Section 10 states for partitions, and the choice is consistency over availability for the control plane alone, since the data plane and the pairwise sessions need none of it.¶
What the simulation shows. The reference's gateway group is a model, one logical store with faults, refusal, a lost acknowledgement and a superseded term, behind the contract of Section 4 (Section 17). Its censuses show the properties the model represents under those faults; they are not evidence of the consistency of a deployment's authority, which the deployment establishes for the system it chose.¶
Cohort control is stream data inside pairwise packets of the same shape as any other (Section 24 of [PQST]): an observer cannot tell group control from data, and the privacy properties of Section 24 of [PQST] carry over unchanged. A forwarding member does not learn the content, nor the authenticated PQST sender identity, from the protected packet's encrypted contents alone; it can observe routing identifiers, packet sizes, timing and whatever the carrier shows it, a source address or a link identity among them, so that PQST gives no sender anonymity against a forwarding member.¶
Discovery is closed by default and opened by policy, and a member reports only the addresses its policy allows: the sequencer, which sees a member's transport address anyway, learns nothing more unless the member's policy says so, and no member learns another's addresses unless the policy names it. Nothing in a pairwise session's wire data shows whether its endpoints are members (Section 24.6 of [PQST]); the data plane of Section 13 would change that for the group's own packets (Section 13.3).¶
The assignments in this section are requested for the publication of this document as an RFC. At the Internet-Draft stage none has been allocated, and implementations use the values here provisionally until then.¶
A new registry. This document establishes a "Cohort Frames" registry in the "PQST Parameters" group that [PQST] creates. The field is one octet. Its ranges and policies, in the terms of [RFC8126]: 0x00 Reserved; 0x01-0x3F IETF Review; 0x40-0x7F Reserved; 0x80-0xBF Specification Required, under the guidance to designated experts of Section 25 of [PQST]; 0xC0-0xFF Private Use. Initial contents:¶
| Value | Name | Reference |
|---|---|---|
| 0x01 |
REPORT
|
Section 6 |
| 0x02 |
LOCATE
|
Section 6 |
| 0x03 |
LOCATE_RESP
|
Section 6 |
| 0x04 |
EPOCH_PROOF
|
Section 6 |
Registrations in the PQST registries. [PQST] reserves the following for this document. IANA is asked to record the assignments with this document as the reference, and to mark the reservations as reservations.¶
| Registry | Value | Name | Status |
|---|---|---|---|
| Object Kinds | 0x10 |
COHORT
|
assigned (Section 5) |
| Object Kinds | 0x11 |
GROUP_KEY
|
assigned (Section 5) |
| Object Kinds | 0x12 |
POLICY
|
assigned (Section 5) |
| Object Kinds | 0x13 |
ROSTER
|
Reserved: no payload semantics in this version |
| Object Kinds | 0x14 |
MEMBER_EVENT
|
Reserved: no payload semantics in this version |
| Object Kinds | 0x15-0x1F | -- | Reserved for later versions of this document |
| Stream Identifiers | 0xE0 |
STREAM_COHORT
|
assigned (Section 3) |
| Capability Bits | 0 | -- | Reserved for a future group data-plane specification (Section 13); not set by implementations of this version |
| Packet Types | 3 | -- | Reserved for a future group data-plane specification (Section 13); no processing semantics in this version |
The derivation labels beginning "group " that [PQST] reserves for this document are internal to the key schedule and are not a registry: this document uses label("group epoch proof") (Section 6) and intends label("group traffic") and label("group sender") for the data plane. The exporter labels beginning "PQST group" are reserved likewise (Section 8.6 of [PQST]).¶
This section records the status of known implementations of the protocol defined by this specification at the time of posting of this Internet-Draft, and is based on a proposal described in [RFC7942]. The description of implementations in this section is intended to assist the IETF in its decision processes in progressing drafts to RFCs. Please note that the listing of any individual implementation here does not imply endorsement by the IETF. Furthermore, no effort has been spent to verify the information presented here that was supplied by IETF contributors. This is not intended as, and must not be construed to be, a catalog of available implementations or their features. Readers are advised to note that other implementations may exist. According to [RFC7942], "this will allow reviewers and working groups to assign due consideration to documents that have the benefit of running code, which may serve as evidence of valuable experimentation and feedback that have made the implemented protocols more mature. It is up to the individual working group to use this information as they see fit".¶
Reference implementation. A Go implementation of Sections 3 to 12 by the author, over the reference implementation of [PQST], covering every item of the conformance clause of Section 1.6 for both roles, the member and the sequencer, the authority being a model (below):¶
Maturity: a research prototype. Revision: the one this draft was built from, recorded in the manifest of the implementation's repository (pqst/validation/EVIDENCE.md), which is available from the author on request until it is published; the test names in this section are the names of the checks the manifest and the traceability index list. Licensing Declaration to Be Provided Later. There is no independent implementation of this layer yet: the independent implementation of [PQST] is to be extended to it after this revision, and nothing here is reported as interoperated.¶
Simulation. A cohort harness runs N members and a model sequencer on the pairwise simulator of the PQST reference.¶
Operations. Its operations are down, up, restart, jam, unjam, partition, heal, rotate, policy, remove, announced suspension and store faults.¶
Pairwise sessions among members. The members also hold pairwise sessions among themselves, in a ring with chords, and send on them every window; between two members disturbed by none of the operations, and not within 150 seconds of a return, the harness requires that their session sees no transition, that every send is accepted and that every datagram is delivered by the next window, in the pairwise sessions between them, which is the transport every figure of this section exercises and the objective of Section 1 measured among members.¶
The monitors. It checks:¶
CG1, CG2, CG3 and CG4 after every operation (for CG3: a member whose key arrived with REMOVAL, that is, one that has applied the removal's commit, holds no previous key, and a removed member holds no key of a later epoch; the census does not count proofs at peers that have not applied the commit, which Section 10 bounds by their standing)¶
that no member holds an epoch the replicated state has not seen¶
and standing (Section 10): a member reachable for longer than group_suspend has been confirmed and has not lapsed, and no member holds standing longer than group_suspend after the sending of the report its last confirmation answered.¶
Two directed tests of the objective. Two directed tests measure the objective: while one member goes down, restarts, is removed and returns, the sessions among the other three see no transition and lose nothing of 210 datagrams each way, the cohort commits once, and the removed member's own pairwise session resumes while its standing does not.¶
The adversarial scenario of CG3. The adversarial scenario of CG3 runs as the other: a member down for ten minutes is removed one minute in and returns with its identity and continuity secret, in one run from a restart with its record; the sequencer refuses it, it holds no later key or COHORT object, every other member, having applied the removal's commit, refuses its proof for the old epoch within group_suspend, and the others see one commit and no session transition of their own.¶
Directed tests of standing (Section 10).¶
A member removed while it and a peer sit in a partition together is accepted by that peer, uninformed, within the peer's standing, which lapses group_suspend after the sending of the peer's last confirmed report with no new epoch in sight; from then the peer accepts and presents no proof, pairwise traffic from the removed member renews nothing, and the sequencer's COHORT on healing renews standing at the new epoch, under which the old proof is refused by the removal rule.¶
A member reachable for three lifetimes never lapses, confirmed by the sequencer's answers to its reports.¶
Keys are erased at group_expire without a confirmation, and a restart holds none.¶
A proof of an earlier generation fails after the session re-anchors, and a fresh one verifies.¶
The removal rule's cost, an honest member one epoch behind refused until its return, is shown and lasts exactly that long.¶
An expired member that kept a copy of its key, as a dishonest device would, makes a proof its peer accepts as possession, while the sequencer closes its return with AUTHZ_EXPIRED and answers LOCATE for it with nothing: a proof restores no membership.¶
The sequencer's replicated state. The sequencer's replicated state is a model: one in-memory store behind the authority contract of Section 4, with faults for a refused write, a lost acknowledgement and a superseded term; the reference includes no consensus or transactional store, and the censuses show the properties the model represents, not the consistency of a deployment's authority.¶
The gateway-group harness. A second harness models a gateway group over that authority (Section 10): two or three gateways with one sequencer identity, each reading the authority through a replica that can be frozen behind it and thawed, the term granted to one gateway at a time and moved by the deployment, and members that open a replacement session to another gateway or re-anchor toward it.¶
Its monitors. Its monitors are CG4 at the members, one key per epoch across replicas and members, one history (no two commits at one revision, no epoch minted twice), and the gateway rule of Section 10: a gateway behind the authority issues nothing while behind, and a superseded gateway issues nothing once it learns it.¶
Its directed tests. Directed tests run the cases the security review named:¶
a member at a later epoch opening a replacement session to a frozen gateway (which issues nothing, or whose stale object the member refuses and reports);¶
a removal kept by a member reconnecting through a gateway behind it, the removed member's proof refused;¶
a gateway that knows it is behind serving the session and no cohort state until it catches up;¶
the member's restart between the two sessions with and without its freshness record;¶
two gateways committing from one predecessor revision, two minting different keys for one epoch, a delayed write after the term moved, the old holder resuming after a freeze, and a partition between gateways healing to one history.¶
Its share of the census. The census runs a tenth of its scenarios on the group harness, with gateway freezes, thaws, term grants and members moving between gateways among the operations.¶
Censuses. Censuses, each one generated distribution on one revision, are evidence about the model sequencer described above, not about a deployment's authority. On the code of this revision (pkg/cohort and pkg/endpoint as committed through 5f02c7c), the census of 2026-11-02, in eight shards:¶
both families clean, 0 failures in 100,002 cohort scenarios and in 10,000 gateway-group scenarios;¶
continuity 100,002 of 100,002;¶
group-state churn 0;¶
10,981,128 of 10,981,128 datagrams between undisturbed members delivered by the next window;¶
739,003 commits for 913,095 security-class events;¶
413,907 returns hinted by the host resumed in 100 ms at the median and 1.1 s at the 95th percentile;¶
88,392 silent returns in about 4 s and about a minute, the dormant retry.¶
Earlier censuses. The censuses of earlier revisions found defects in both families: two of the dormant member on the code before the external reviews; 41 of the cohort family and 1,871 of the 10,000 gateway-group scenarios on the code after them; and one gateway-group failure after those corrections. Each was reduced to a minimal scenario kept as a directed test, and the rules they forced are in Section 7, Section 8, Section 9, Section 10 and Section 11:¶
a removed member's return by a validated path;¶
a session re-anchored toward a gateway that never served it;¶
the pacing of confirmations;¶
the disruption monitor's attribution of a returned member's own timers;¶
a gateway behind its replica recording nothing as issued;¶
and the prompt of a dormant member.¶
Every one of those seeds passes on the code of this revision. The changes of the eighth, ninth and tenth reviews each have a directed test in this layer (TestLosingSessionReportDoesNotUpdate, TestLocateEmptyWhenBehind, TestMemberReportsCloseReason, TestParamsRefused) and are within the census above.¶
Four events kept apart. These figures keep apart four events that differ in cost:¶
the recovery of an ordinary pairwise session, by the core's rules;¶
the return of the member's session to the sequencer, which proves the requester (Section 7) and is therefore one round trip, never the core's zero-round-trip form;¶
the delivery of the current epoch key and policy on that return, reported separately above;¶
and the establishment or resumption of a member-to-member session, which is the core's pairwise recovery again.¶
No single figure is a group recovery latency.¶
Regression tests of the reviews. The rules the external reviews forced each have a directed test in the reference, named in the reviews' own numbering and kept as regressions, the manifest mapping each name to its file:¶
CR-13 to CR-15 (standing confirmed only by an answer to the member's own recent report, on the same session, and running from that report's sending);¶
CF-13 to CF-16 (objects staged until the first COHORT; the member's state ordered by its reports across sessions; the report counter across a restart);¶
MC-01 to MC-06 (sessions of several cohorts under one sequencer; the first COHORT agreeing with the nomination and the sequencer);¶
CI-11 and CI-12 (LOCATE identifiers and report ordering);¶
CS-16 and CS-17 (outcomes resolved from the authority's history, and left unknown past its horizon);¶
and in the core's reference GC-19 and GC-20 (the partial-response grace bounded by the attempt's life; the sealed record persisted with the commit, at both boundaries of the write).¶
The manifest named above records each test's file and the revision it ran on, and the traceability index beside it (pqst/validation/TRACEABILITY.md) links CG1 to CG4 and each review's property to its directed tests, monitors, censuses, models and what nothing covers.¶
Not implemented. The data plane of Section 13, and the ROSTER and MEMBER_EVENT kinds.¶
Known limitations.¶
The authority of Section 4 is a model in the simulator, one in-memory store with faults, not a consensus system, and the gateway group of Section 10 is modelled with a replicated log in memory, not a consensus system;¶
the census figures above are of the control plane and of pairwise datagrams, and each census is evidence for the distribution it generated on the revision it ran on, not a proof of the invariants;¶
the open question of Section 13.4 is undecided.¶
The reproduction data is in the manifest named above.¶
This appendix collects in one place the layout of every structure the cohort layer puts on the wire, inside the pairwise session's stream and objects. It is generated from the sections that define them and adds nothing to them: Section 5 for the objects, Section 6 for the frames, Section 16 for the codepoints. Everything travels inside a PQST session, so the packet formats are those of the wire-format appendix of [PQST].¶
Object bodies (Section 5), carried by the object mechanism of Section 19.4 of [PQST]:¶
0x10. Name: COHORT; Body: cohort_id(16) sequencer_fp(48) policy_version(4) epoch(4) member_index(2) member_state(1) in_reply_to(4) (79 octets); Delivered to: each member; When: on a new session, on every change of the member's own state, and whenever anything else is delivered¶
0x11. Name: GROUP_KEY; Body: epoch(4) flags(1) key(32) (37 octets); Delivered to: each ACTIVE member; When: when the member's key is older than the epoch: on a new session, on rotation, on a report naming an older epoch¶
0x12. Name: POLICY; Body: policy_version(4) body(*); Delivered to: each ACTIVE member; When: when the member's policy version is older than the current one¶
Frames on STREAM_COHORT (Section 6), one per DATA payload, typed by the first octet:¶
REPORT = 0x01 cohort_id(16) report_id(4) epoch(4)
policy_version(4) path_count(1) addr_count(1)
*( len(1) addr(len) )
LOCATE = 0x02 request_id(4) fingerprint(48)
LOCATE_RESP = 0x03 request_id(4) fingerprint(48)
[ age(4) *( len(1) addr(len) ) ]
EPOCH_PROOF = 0x04 cohort_id(16) epoch(4) proof(16)
addr = family(1) address(4|16) port(2)
¶
proof is the first 16 octets of HMAC-SHA-384(key, label("group epoch proof") || cohort_id || epoch(4) || binding) under the epoch key named, with HMAC-SHA-384 as in Section 6.5 of [PQST] and label as in Section 7 of [PQST], where binding is the 32 octets Export(lineage, "PQST group epoch proof", cohort_id || epoch(4), 32) of the pairwise session the frame travels in (Section 8.6 of [PQST]): both ends of that session derive it, it changes at every re-anchor, and nobody else holds it.¶
The Cohort Frames registry (Section 16):¶
| Value | Name | Reference |
|---|---|---|
| 0x01 |
REPORT
|
Section 6 |
| 0x02 |
LOCATE
|
Section 6 |
| 0x03 |
LOCATE_RESP
|
Section 6 |
| 0x04 |
EPOCH_PROOF
|
Section 6 |
Registrations in the PQST registries (Section 16):¶
| Registry | Value | Name | Status |
|---|---|---|---|
| Object Kinds | 0x10 |
COHORT
|
assigned (Section 5) |
| Object Kinds | 0x11 |
GROUP_KEY
|
assigned (Section 5) |
| Object Kinds | 0x12 |
POLICY
|
assigned (Section 5) |
| Object Kinds | 0x13 |
ROSTER
|
Reserved: no payload semantics in this version |
| Object Kinds | 0x14 |
MEMBER_EVENT
|
Reserved: no payload semantics in this version |
| Object Kinds | 0x15-0x1F | -- | Reserved for later versions of this document |
| Stream Identifiers | 0xE0 |
STREAM_COHORT
|
assigned (Section 3) |
| Capability Bits | 0 | -- | Reserved for a future group data-plane specification (Section 13); not set by implementations of this version |
| Packet Types | 3 | -- | Reserved for a future group data-plane specification (Section 13); no processing semantics in this version |
| Quantity | Octets | Where |
|---|---|---|
COHORT body |
79 | Section 5 |
GROUP_KEY body |
37 | Section 5 |
proof of EPOCH_PROOF
|
16 | Section 6 |
| identity fingerprint | 48 | Section 6 and [PQST] |
addr (IPv4, IPv6) |
7, 19 | Section 6 |
cohort_id
|
16 | Section 4 |
| epoch key | 32 | Section 4 |
member_index
|
2 | Section 7 |
largest POLICY, MAX_OBJECT of [PQST]
|
16384 | Section 5 |