drop AQB for simple majority of voters

This commit is contained in:
Alain Brenzikofer
2026-08-02 12:08:01 +02:00
parent 9f3fe2ef2e
commit 9f689ab92e
2 changed files with 297 additions and 160 deletions
@@ -48,11 +48,19 @@ The paper's conclusion is that "a Byzantine admin can exploit concurrency to inf
## 5. The voting rule
Adaptive quorum biasing comes from Polkadot; Burdges et al., ["Overview of Polkadot and its Design Considerations"](https://arxiv.org/abs/2005.13456) describes positive turnout bias as generalizing the notion of a quorum, "where additional turnout always makes change more likely, assuming the same yay-to-nay ratio... in case of low turnout we favour the nay side, or status quo, by requiring a super-majority approval, and as turnout approaches 100% the requirement dials down to majority-carries." Its two stated justifications are that "the status quo tends to be safer than any change", and that low-turnout results are volatile: "a result could be 51% 49% one month and then change to 49% 51%", so enacting them is unwise. The comparison is `against/√turnout < approve/√electorate`, our `B²·E < A²·T`. Polkadot's published illustration (25% turnout requires ≈66% approval, 75% turnout requires ≈54%) is consistent with the RFC's integer-exact figures.
We use **simple majority of the votes cast** (`A > B`). May's theorem characterizes simple majority as the unique rule satisfying anonymity, neutrality and positive responsiveness, and Rae's analysis (Rae 1969; see Berndt Rasmussen on the RaeTaylor theorem) grounds it as the rule minimizing expected disappointment under symmetric preferences. Departures need a justification specific to the setting, and we found none that survives.
**Why this rule rather than a fixed quorum.** Participation quorums are known to invert incentives: opponents of a proposal maximize their chance of defeating it by abstaining rather than voting no, so the rule rewards boycotts and depresses turnout. AQB has no participation threshold, so abstention is never strategically superior to voting nay; a nay always hurts the proposal more than a non-vote. Empirically, fixed quorums would also simply not be met: studies of on-chain governance report very low participation and heavily concentrated voting power (Feichtinger, Fritsch, Vonlanthen & Wattenhofer, ["The Hidden Shortcomings of (D)AOs"](https://arxiv.org/abs/2302.12125); Fritsch, Müller & Wattenhofer, "Analyzing Voting Power in Decentralized Governance: Who controls DAOs?"; Barbereau et al., "DeFi, Not So Decentralized"). A dormant-member-heavy chat group is the same regime.
**Why not a turnout quorum.** Participation quorums invert incentives: opponents maximize their chance of defeating a proposal by abstaining rather than voting no, so the rule rewards boycotts and depresses turnout. Empirically they would also not be met: studies of on-chain governance report very low participation and heavily concentrated voting power (Feichtinger, Fritsch, Vonlanthen & Wattenhofer, ["The Hidden Shortcomings of (D)AOs"](https://arxiv.org/abs/2302.12125); Fritsch, Müller & Wattenhofer, "Analyzing Voting Power in Decentralized Governance: Who controls DAOs?"; Barbereau et al., "DeFi, Not So Decentralized").
**What the rule does not do, and what covers it.** Deviating from simple majority needs justification: May's theorem characterizes simple majority as the unique rule satisfying anonymity, neutrality and positive responsiveness, and Rae's analysis (Rae 1969; see Berndt Rasmussen on the RaeTaylor theorem) grounds majority as the rule minimizing expected disappointment under symmetric preferences. Status-quo bias is defensible only when the costs of erroneous change exceed those of erroneous inaction, which is Polkadot's first justification and is plausible here: a wrongly replaced admin set is disruptive and the group cannot easily undo it mid-flight. But AQB has a property no voting-theoretic argument rescues: with zero nays, any non-empty aye set passes. Our protection is procedural, not arithmetic: the enforced referendum period and challenge window during which a single nay raises the bar steeply. That is why the RFC treats period enforcement as a security mechanism rather than a liveness convenience.
**Why not adaptive quorum biasing, which an earlier draft of this design used.** Burdges et al., ["Overview of Polkadot and its Design Considerations"](https://arxiv.org/abs/2005.13456) describe positive turnout bias as generalizing the quorum, "where additional turnout always makes change more likely, assuming the same yay-to-nay ratio... in case of low turnout we favour the nay side, or status quo, by requiring a super-majority approval, and as turnout approaches 100% the requirement dials down to majority-carries", justified by status-quo safety and the volatility of narrow low-turnout results ("a result could be 51% 49% one month and then change to 49% 51%"). Both justifications transfer to a chat group. The rule does not, for three reasons.
First, it is scale-invariant in the wrong direction. Writing `p` for the opposition share of votes cast, the condition `B²E < A²T` requires ayes amounting to `p²/(1p)` of the *whole* electorate, a quantity in which `E` cancels. At `p = 0.25` that is 8.3% of the electorate; at `p = 0.1`, 1.1%. The rule therefore lets a bloc win in inverse proportion to how few members object, identically at any group size, and no group size makes it prudent.
Second, its safety assumption does not hold here. AQB is *needed* only where a majority of the electorate is unreachable, and *safe* only where opponents reliably vote. Token governance satisfies both: a passive retail mass coexists with delegates and large holders who monitor proposals professionally. A private group has no standing opposition class (the members who would object are drawn from the same attention distribution as everyone else), so the apathy that motivates the rule is the same apathy that disarms it. The DAO turnout evidence cited above describes the first population and does not transfer to the second.
Third, the threshold moves as votes arrive, so a member cannot predict the effect of their own vote and a nay raises the bar for the ayes. There is no honest way to render "how many more ayes are needed" in a UI when the answer is a function of turnout.
**What we lose by dropping it, stated plainly.** At low turnout AQB was the *stricter* rule, so `A > B` is the weaker choice against minority capture: against 40 colluding ayes at `E = 100`, AQB required 35 nays to defeat the certificate and simple majority requires 40. And `A > B` retains AQB's worst property unchanged: with zero nays a single aye passes. Under both rules the protection for a silent electorate is procedural rather than arithmetic (the enforced period and the challenge window), which is why the RFC treats period enforcement as a security mechanism. A minimum-support floor (`A ≥ max(3, ⌈E/3⌉)`) would close the single-aye case without reintroducing an abstention incentive, since a floor on ayes is unaffected by opponents staying home; the RFC carries it as an open question.
**Ballot secrecy: deliberately absent.** Votes are signed and third-party-verifiable within the group, so members can prove to each other how someone voted. The e-voting literature treats this as a serious defect: Juels, Catalano & Jakobsson, ["Coercion-Resistant Electronic Elections"](https://doi.org/10.1145/1102199.1102213) (WPES 2005) formalize why, and Helios (Adida, USENIX Security 2008) explicitly disclaims suitability for coercive settings. The risk here is concrete and small-scale: incumbent admins can identify nay voters and retaliate before the referendum concludes, and the RFC's own removal-deferral rule exists because they will try. We accept it because verifiability is what makes a certificate self-authenticating, which is the property that lets any member carry the result past a hostile forwarder. Coercion-resistant schemes need either a trusted tallier or heavyweight cryptography with a registration phase, neither of which fits. Note also the layering tension worth being explicit about: SimpleX's transport is deliberately deniable, and we are adding application-level non-repudiation on top of it for exactly one message class.
@@ -76,8 +84,8 @@ Our version chain is a reconfiguration sequence, and there is a positive result
## Summary of positions
We adopt: consensus-number analysis as the justification for revisable finality (Frey/Gestin/Raynal); I-confluent vote aggregation with a non-consensus decision rule (Kleppmann & Howard); majority-certificate authority in place of seniority ranking (motivated by Dougal's critique); epoch-style versioning without a serializer (MLS, minus its Delivery Service); flickering-tolerant membership (as DCGKA does); authenticated membership events (Rösler et al.); positive turnout bias (Burdges et al.), with procedural rather than arithmetic protection against the zero-nay case; and social-only accountability (PeerReview's framing, without Casper's stake).
We adopt: consensus-number analysis as the justification for revisable finality (Frey/Gestin/Raynal); I-confluent vote aggregation with a non-consensus decision rule (Kleppmann & Howard); majority-certificate authority in place of seniority ranking (motivated by Dougal's critique); an authenticated hash-linked membership log with witness-based enfranchisement, following the hash-DAG approach Matrix and Keyhive use and Byzantine causal broadcast (Kleppmann & Howard); epoch-style versioning without a serializer (MLS, minus its Delivery Service); flickering-tolerant membership (as DCGKA does); authenticated membership events (Rösler et al.); simple majority of votes cast (May's theorem), with procedural rather than arithmetic protection against the zero-nay case; and social-only accountability (PeerReview's framing, without Casper's stake).
We reject: anti-flickering revocation semantics (consensus number *N*); a finality arbiter (ERA) or delivery-service serializer (MLS), both of which reintroduce a trusted chokepoint; fixed participation quorums (abstention incentives, and DAO turnout empirics); ballot secrecy (incompatible with self-authenticating certificates); social-graph Sybil defence (wrong graph, wrong scale, for v1); and fork consistency as a goal (we repair forks rather than making them permanent).
We reject: anti-flickering revocation semantics (consensus number *N*); a finality arbiter (ERA) or delivery-service serializer (MLS), both of which reintroduce a trusted chokepoint; fixed participation quorums (abstention incentives, and DAO turnout empirics) and adaptive quorum biasing (scale-invariant minority capture, and a safety assumption that holds only where a standing opposition class exists); ballot secrecy (incompatible with self-authenticating certificates); social-graph Sybil defence (wrong graph, wrong scale, for v1); and fork consistency as a goal (we repair forks rather than making them permanent).
Two places where the literature suggests the RFC is currently weaker than it needs to be: acceptance is not a pure function of received updates, which is a real deviation from strong eventual consistency and the root of knife-edge divergence (§2); and the temporal rules in the witnessed chain are backdating mitigations of the kind ERA classifies as insufficient without an arbiter, so their contribution should be described as raising cost rather than establishing a bound (§3).
+284 -155
View File
@@ -1,4 +1,4 @@
# Democratic groups: member referenda with adaptive quorum biasing
# Democratic groups: member referenda over an authenticated membership log
## Motivation
@@ -79,15 +79,14 @@ independently against its own record of members and keys and applies the new adm
governance version, generalizing the version-gated atomic set replacement already implemented for channel rosters
(`applyAtRosterVersion`).
The tally rule is **adaptive quorum biasing** (positive turnout bias), as used in Polkadot governance v1 (Burdges et
al., "Overview of Polkadot and its Design Considerations", 2020): at low turnout a supermajority of votes cast is
required; as turnout approaches the full electorate the threshold approaches simple majority. This avoids fixed quorums
that dormant members would make unreachable. As in Polkadot, the protection against a small minority passing a
referendum unopposed is not the curve alone but the *voting period* during which anyone can cast a nay, so receivers
validate the period structurally, and every member is guaranteed an objection window between seeing a non-unconditional
result and applying it (see "Timing"; a strict-majority result applies immediately, which is safe because no further
vote can change it). A certificate whose ayes exceed half the electorate is immune to vote-withholding attacks and
applies immediately; smaller-turnout certificates apply after a challenge window (see "Certificate soundness").
The tally rule is a **simple majority of the votes cast**: the proposal passes iff `A > B`. There is no turnout quorum,
because participation thresholds reward abstention: an opponent defeats a quorumed proposal more cheaply by boycotting
it than by voting against it, which is the opposite of the behaviour a group wants. The protection against a small
minority passing a referendum unopposed is therefore not the tally rule but the *voting period* during which anyone can
cast a nay, so receivers validate the period structurally, and every member is guaranteed an objection window between
seeing a result and applying it (see "Timing"). A certificate whose ayes exceed half the whole electorate cannot be
changed by any vote still outstanding, so it is immune to vote-withholding and applies immediately; every other
certificate applies only after a challenge window (see "Certificate soundness").
Incumbents cannot block a referendum in progress because: governance events are self-authenticating and accepted from
*any* member or forwarder (exempt from the single-`expectedForwarder` rule); voting rights are fixed at proposal time,
@@ -108,8 +107,8 @@ group layer and stated under limitations.
- One referendum action: replace the set of `GRAdmin` members. Moderator and member roles are untouched (manageable by
the new admins). The action type is a sum type (`GovAction`) so profile changes, governance-parameter changes, and
group deletion can become referendum actions later without protocol redesign.
- Fixed positive turnout bias. Other curves (negative bias, simple majority of votes cast) are parameters left for
later.
- Fixed tally rule: simple majority of votes cast. Turnout-weighted curves and minimum-support floors are deferred (see
open questions).
### Prerequisite: member signing keys in p2p groups
@@ -136,91 +135,157 @@ Preconditions: every current member's negotiated chat version supports governanc
leave or be removed first; a client that silently ignores governance events would diverge), and every current member's
key is known.
An owner initiates enabling with chosen parameters. Enabling is itself a unanimity referendum among the current owners:
the initiating owner collects signatures from all co-owners (trivial for sole-owner groups) and broadcasts the genesis
certificate as `x.grp.gov.enable`:
**A prerequisite bug fix, outside governance.** `xGrpMemIntro` accepts the introduced member's role verbatim: unlike
`xGrpMemNew` and `xGrpMemFwd`, its p2p branch has no `checkHostRole`, so a mere admin can introduce a member carrying
role `GROwner` into a victim's local view at any time. That fake owner then satisfies every owner-gated receiver check
at that victim, including the genesis validation below, which lets an admin bootstrap governance in a group that never
opted in and with parameters of its choosing. This is a pre-existing hole with consequences beyond governance, and
governance must not be built over it. `xGrpMemIntro` must apply the same cap as its siblings, so that the introduced
role can never exceed the introducer's; with that in place an admin cannot introduce an owner at all.
Enabling requires an **authorising set** whose composition depends on whether the group still has owners:
- **Owned groups**: all current owners. The initiating owner collects signatures from all co-owners (trivial for
sole-owner groups).
- **Ownerless groups**, where the last owner has left and no member holds `GROwner`: a strict majority of the current
members, each signing with their member key. This is a founding certificate with the same shape as a referendum
certificate, and it exists because the owner-signature rule is vacuous here: an empty owner set is satisfied by an
empty signer set, so without this case any single member could enable governance unilaterally with parameters of
their choosing. It also retires what earlier drafts deferred as future work, since an ownerless group is precisely
the group that most needs a way to appoint admins.
In both cases the authorising set must be **non-empty**, and every signer must be a member the receiver has itself
connected to rather than merely one it has been told about, so that a minted or announced-but-unmet identity cannot
authorise the group's constitution.
The initiator then broadcasts the genesis certificate as `x.grp.gov.enable`:
- mint a random 256-bit `governanceId`, a group-scoped identifier that exists only inside e2e-encrypted messages (see
metadata note under limitations);
- `params`: `{referendumDays (default 7), challengeHours (default 24), settleSkewDays (default 7)}`;
- `params`: `{referendumDays (default 7), challengeHours (default 24), witnessCount (default 2)}`;
- the initial electorate (list and hash, as defined in the next section);
- signed bytes: `smpEncode ("SXGG", governanceId, params, electorateHash)`, signed by every current owner.
- signed bytes: `smpEncode ("SXGG", governanceId, params, genesisLogHash)`, signed by every current owner, where
`genesisLogHash` is the hash of the sealed initial membership log (see "Authenticated membership").
Receivers validate that the signer set is exactly the set of members they record as `GROwner`, that the parameters lie
within protocol-defined bounds: `1 ≤ referendumDays ≤ 30`, `1 ≤ challengeHours ≤ 24·referendumDays` (an unbounded
window would make every non-unconditional referendum unresolvable, silently reducing governance to
strict-majority-of-electorate forever), `0 ≤ settleSkewDays ≤ referendumDays`, rejected fail-closed otherwise (every
safety property below is a function of these values, and the enabling owner is exactly the actor governance exists to
constrain; a zero period or a vacuously large settlement skew would void the objection guarantees), and validate the
electorate (checks defined below, with the genesis message's broker timestamp standing in for `proposedAt`; fail-closed
on conflict), then apply atomically: store governance state, demote all `GROwner` members (including the sender and
possibly themselves) to `GRAdmin`, set governance version 1.
Receivers validate, fail-closed on any failure, that:
1. the group is **not already governed**. A governed group is owner-free by construction, so its owner set is empty and
the owned-group rule would be vacuously satisfied by an unsigned certificate; without this check an attacker could
re-genesis a governed group at will, resetting its `governanceId`, its parameters and its version chain, and
retiring every honest reserve certificate and `prevProposalHash` anchor. A byte-identical repeat of the stored
genesis is an idempotent no-op; a *different* genesis for a governed group is an attack, and is surfaced rather
than applied. Governance is enabled exactly once per group;
2. the authorising set matches the rule above, is non-empty, and every signer is connected to the receiver: exactly the
recorded `GROwner` members for an owned group, or a strict majority of the recorded current members for an
ownerless one;
3. the parameters lie within protocol-defined bounds: `3 ≤ referendumDays ≤ 30`, `24 ≤ challengeHours ≤
24·referendumDays`, `1 ≤ witnessCount ≤ 5`. Both ends bind. An unbounded window would make every
non-unconditional referendum unresolvable, silently reducing governance to strict-majority-of-electorate forever;
at the other end a one-day period with a one-hour window would let two confederates replace the admin set inside a
day, which a dormant membership routinely misses, and enabling is irreversible so the group could never undo it.
The enabling party is exactly the actor governance exists to constrain, so its discretion over these values is
bounded on both sides;
4. the sealed initial membership is consistent with the receiver's own member list.
It then applies atomically: store governance state, seal the initial membership as the log's genesis entry, demote all
`GROwner` members (including the sender and possibly themselves) to `GRAdmin`, and set governance version 1.
From this point the group is owner-free: no member holds `GROwner`, and governed clients additionally **reject any event
that would create or set a member with role `GROwner`**: `XGrpMemRole` to owner, and owner-role members arriving via
`XGrpMemNew`, `XGrpMemIntro`, `XGrpMemFwd`, `XGrpLinkInv` or `XGrpInv`. The explicit rejection rule matters: the
existing role gates alone do not close this (`xGrpMemIntro` accepts the introduced member's role verbatim from the host
at any time, so a hostile host could otherwise mint a fake "owner" in a victim's local view and then feed it owner-gated
events). With the rule in place, `XGrpDel` (receiver gate: sender must be `GROwner`) is dead in governed groups; nobody
`XGrpMemNew`, `XGrpMemIntro`, `XGrpMemFwd`, `XGrpLinkInv` or `XGrpInv`. This is belt-and-braces alongside the
`xGrpMemIntro` fix above, which closes the same hole for groups that have not opted in. With the rule in place,
`XGrpDel` (receiver gate: sender must be `GROwner`) is dead in governed groups; nobody
can remotely delete the group, which also closes the "delete the group to pre-empt the vote" attack. Owner-gated checks
for `XGrpInfo` and `XGrpPrefs` are relaxed to `GRAdmin` in governed groups on both sides: the receiver gates and the
sender-side assertion in `runUpdateGroupProfile` (otherwise an owner-free group could never edit its profile again);
governance parameters are not carried in the profile and have no update path (v1: governance is a one-way door until a
`GAChangeGovernance` action ships; see open questions).
For newly created groups the creator enables governance at creation (self-signed genesis, electorate of one). Joiners to
a governed group receive the genesis certificate from their host together with the group profile; like everything else a
joiner learns about a p2p group, it is trusted on the host's word (see limitations). To make a forged or divergent
genesis *detectable*, clients must surface governance events carrying a `governanceId` different from their stored one,
rather than silently dropping them; governance events are forwarded by everyone, so a victim of a fake genesis will see
mismatching traffic.
For newly created groups the creator enables governance at creation, a self-signed genesis over a membership of one.
### Electorate
Joiners receive the genesis certificate with the group, and cannot check it against a recorded owner set, because a
governed group has none. What they can check is agreement: the genesis is the root of the membership log, so its hash
is an ancestor of every frontier, and comparing frontiers with any member other than the introducing host establishes
that the joiner is on the same log as the rest of the group. This is the first point at which a host-mediated join
becomes verifiable rather than merely trusted, and it is why joiners should sync frontiers with a second peer before
participating. Until they have, they are in the position every SimpleX joiner is in today. Clients must additionally
surface governance events carrying a `governanceId` different from their stored one rather than silently dropping
them; since governance events are forwarded by everyone, a victim of a fake genesis will see mismatching traffic.
The electorate for a referendum is **all settled current members**, status connected or beyond (`GSMemConnected`,
`GSMemComplete`, `GSMemCreator`), regardless of role and regardless of `blockedByAdmin`, excluding only `GRRelay` (not
applicable to p2p groups, excluded defensively). Members in transient join states are not in the electorate. Basing
eligibility on anything role- or restriction-shaped is deliberately avoided: those are levers incumbents control (demote
dissidents to observer, block them "for all") and would hand them a disenfranchisement tool. The only way to keep
someone out of a future electorate is to remove them from the group before a referendum starts; see limitations.
### Authenticated membership
"Settled" is a per-client observation (each client marks a member connected on its own handshake with them), so honest
members' views of the electorate boundary can differ for recently joined members. The validation rules below use a
settlement-skew tolerance for exactly this reason, and clients record *when* each member became settled.
Everything above is only as trustworthy as the membership set the votes are counted against, and in p2p groups today
that set is whatever each member's admins told them. That is not a residual social risk, it is a live protocol attack:
`xGrpMemNew` gates only on the *sender's* role, so an admin can announce fabricated members carrying keys it generated
itself, and no receiver can distinguish them from real members who have not yet connected. Announce `E + 1` phantoms,
sign their votes, and the resulting certificate is a strict majority of a majority-fabricated electorate. Selective
disclosure is as damaging without any forgery: announce one real member to some peers and not others, and no electorate
list can ever satisfy every honest receiver again. **v1 therefore requires an authenticated membership log; the
referendum machinery is not safe to ship over admin-asserted membership.**
The proposal ships the electorate as a list of `MemberId`s (chunked for large groups) plus `electorateHash` = hash of
the sorted `(memberId, memberKey)` pairs. Each receiver validates it against its own database:
**The log.** Each governed group has an append-only, hash-linked log of membership facts. Each entry is
`{parents :: [Hash], action, subject :: MemberId, author :: MemberId, ts, sig}` where `action` is one of `Added`
(carrying the subject's `MemberKey`), `Removed`, `RoleChanged`, or `Confirmed` (below), `parents` are the hashes of the
entries the author had when writing, and the entry's own hash covers all of it. The log is a DAG rather than a
sequence, because p2p groups have no total order; concurrent entries are siblings and are merged by taking the union.
Entries are gossiped like any other governance event, forwardable by anyone under transport rule 1, and every member
retains the whole log. It is small: its size is proportional to membership *changes*, not to messages.
1. recompute `electorateHash` from the shipped `MemberId` list joined with the receiver's *own* recorded key for each
member, sorted by `memberId`; the result must equal the proposal's `electorateHash` (which is inside the signed
`proposalHash`; the list itself travels outside the signature and could otherwise be padded by a forwarder to
inflate `E` at selected receivers; recomputing with the receiver's keys also binds the proposer's key view to the
receiver's), and the proposer must appear in the list;
2. every listed member must be known to the receiver; an unknown member is ballot stuffing, reject (key divergence has
no separate check: the list carries no keys, so it surfaces as a check-1 hash mismatch; this check exists to give a
precise diagnostic);
3. every member the receiver records as current and settled earlier than `settleSkewDays` before `proposedAt` must be
listed; an omission is disenfranchisement, reject. Members settled more recently may be listed or omitted (their
settlement was plausibly still propagating when the proposal was made), and removals with broker timestamps close to
`proposedAt` are likewise tolerated as omissions. Clients must surface to voters both the recently settled members a
proposal omits and removals adjacent to a referendum; these tolerances are proposer discretion over the margins of
the electorate, and the voters' remedy is a nay.
**Enfranchisement.** A member joins the electorate only when its `Added` entry has been countersigned by at least
`witnessCount` (default 2) distinct already-enfranchised members other than the author, each publishing a `Confirmed`
entry attesting that it completed a connection handshake with the subject. This is the load-bearing rule, and it needs
no new handshake: the mesh already emits `x.grp.mem.con` when two members connect, so `Confirmed` is that existing
signal, signed and logged. Its effect is that enfranchisement cannot be asserted, only demonstrated. A phantom that
never connects to anyone is never enfranchised no matter how many times its author announces it; to enfranchise `k`
phantoms an attacker must complete `2k` real handshakes with honest members, which is expensive, rate-limited by those
members' devices, and above all *visible to the members doing the confirming*.
Rejection is fail-closed: the member will not vote and will not apply the certificate. It is *recoverable*; see
"Catch-up and recovery" below. With membership events additionally signed (see implementation), equivocating membership
state to different members becomes evidence-producing: two contradictory signed membership statements from the same
admin are third-party-verifiable proof of misbehavior.
This does not deliver Sybil resistance, which is unattainable without a central authority (Douceur), and an attacker
willing to run many clients and connect them to honest members can still inflate the roll. What it converts is the
attack's cost and visibility: from a silent, free, unilateral database edit into a public, attributable, interactive
one that every confirming member sees and that leaves permanent signed evidence of who vouched for whom.
Members who join after the proposal are not in the electorate and cannot vote; they accept the referendum outcome on the
same basis as everything else they learn about the group, from their introducing host. The governance guarantees hold
for members present at proposal time.
**Deriving the electorate.** The electorate at a log frontier is computed deterministically: every member with an
enfranchising `Added` (per above) and no later `Removed`, regardless of role and regardless of `blockedByAdmin`,
excluding only `GRRelay`. Eligibility deliberately does not depend on role or restriction, since those are levers
incumbents control. Because the derivation is a pure function of the log, every member holding the same frontier
computes the same `E`, and the per-client `settled_at` observation that previous drafts relied on disappears, together
with its skew tolerances.
Concurrent `Added` and `Removed` for the same subject are resolved as removal-wins within a frontier, and a re-`Added`
subject is enfranchised again: this is the flickering the design already accepts elsewhere, and it is what keeps the
object at consensus number 1 (see "Related work").
**Forks are evidence.** Two honest members exchange frontiers cheaply (a list of head hashes, piggybacked on existing
traffic). If neither frontier is an ancestor of the other after fetching missing entries, the log has forked, which can
only happen if some author signed conflicting histories: an equivocation with a signed, third-party-verifiable proof
attached. Selective disclosure collapses into this case, because withholding an entry from one peer is detectable as
soon as that peer syncs frontiers with anyone else. This is what closes the pincer attack, in which one undisclosed
member made every possible electorate list invalid at someone.
**The proposal cites a frontier, not a list.** `x.grp.gov.propose` carries `logFrontier`, the sorted set of head
hashes, in place of an electorate list and hash. A receiver validates by checking that it holds every entry reachable
from that frontier, fetching what it lacks, and that the frontier is consistent with (not forked from) its own log.
Validation is therefore *repairable rather than fail-closed*: a member behind on the log catches up and proceeds, where
previously an unknown member was an unrecoverable rejection. A member that cannot reconcile the frontier with its own
log has detected a fork, and surfaces it rather than voting.
Members enfranchised after the cited frontier are not in that electorate and cannot vote in that referendum. The
guarantees hold for members enfranchised at proposal time.
**Bootstrapping and cost.** Enabling governance seals the current membership as the log's genesis entry, signed as part
of the genesis certificate; members present at that moment are enfranchised without confirmations, since their
connections predate the log. Joiners receive the log with the group and verify it by comparing frontiers with peers
other than their host, which is the first point at which a host-mediated join becomes checkable rather than merely
trusted. The `witnessCount` default of 2 is a compromise: 1 is forgeable by a single admin, and values above 2 delay
enfranchisement in sparse groups where a joiner connects slowly.
### Referendum protocol
New chat protocol events (JSON, version-gated): `x.grp.gov.enable` (above), and:
- `x.grp.gov.propose`
`{governanceId, govVersion, action, electorate, electorateHash, prevProposalHash, proposedAt, expiresAt, proposer, sig}`,
`{governanceId, govVersion, action, logFrontier, prevProposalHash, proposedAt, expiresAt, proposer, sig}`,
from any electorate member. `prevProposalHash` is the `proposalHash` of the referendum whose certificate the proposer
applied at the previous version (the genesis certificate's hash at `govVersion = 2`), chaining each referendum to the
state it was proposed against. It names the *proposal*, not the certificate, because certificates are not canonical:
@@ -229,7 +294,7 @@ New chat protocol events (JSON, version-gated): `x.grp.gov.enable` (above), and:
proposed member IDs sorted. `proposer` is the proposer's `MemberId`, carried explicitly so that forwarded copies can
be verified without trusting the forwarder's sender claim. `proposalHash` = SHA-256 of the deterministic binary
encoding
`smpEncode ("SXGP", governanceId, govVersion, action, electorateHash, prevProposalHash, proposedAt, expiresAt, proposer)`;
`smpEncode ("SXGP", governanceId, govVersion, action, logFrontier, prevProposalHash, proposedAt, expiresAt, proposer)`;
`sig` is the proposer's signature over it. `govVersion` must be the receiver's stored governance version + 1 for full
processing; a proposal claiming a higher version is not retained and only marks a possible version gap, triggering at
most one rate-limited `x.grp.gov.request` with backoff (otherwise a single forged-version proposal would stampede the
@@ -286,21 +351,26 @@ Transport rules (the anti-censorship core):
voters to silence them mid-vote (today, receiving `XGrpMemDel` about oneself both flips the member's own status to
removed, locking the send path, and, for non-admin members, tears down all group connections immediately).
### Tally: adaptive quorum biasing
### Tally: majority of votes cast
Let `E` = electorate size, `A` = valid aye votes, `B` = valid nay votes, `T = A + B` (turnout). The proposal passes iff
(positive turnout bias, integer-exact form):
```
B² · E < A² · T
A > B
```
equivalently `B/√T < A/√E`. Properties: at full turnout (`T = E`) this reduces to `A > B` (simple majority); at low
turnout a supermajority of votes cast is required. Example, `E = 100`: at `T = 25`, passing requires `A ≥ 17` (68% of
votes cast); at `T = 100`, `A ≥ 51`. Note that with zero nays *any* non-empty aye set passes; this is inherent to
positive turnout bias (Polkadot's included). The defense for the silent majority is not the curve but the voting period
and challenge window, during which nays are cheap and effective: at `E = 100`, a single nay forces `A ≥ 5`, five nays
force `A ≥ 13`, twenty force `A ≥ 29`; resistance scales roughly as `A ≈ ∛(B²·E)`.
Ties fail, so the status quo wins a split vote. Abstention is neutral: not voting neither helps nor hinders a proposal,
which is why there is no turnout quorum. The rule is chosen for a property the protocol cannot supply on its own,
namely that a member can predict the outcome of their own vote without knowing the turnout. Under a turnout-weighted
curve the number of ayes required moves as votes arrive, so "how many more do we need" has no stable answer and a nay
can raise the bar for the ayes; under `A > B` each vote moves the result by exactly one, in the direction the voter
chose.
The rule's weakness is the mirror image of its simplicity: with no nays, a single aye passes. Nothing in the arithmetic
distinguishes a genuine unopposed proposal from one nobody noticed, so the entire defence of a silent electorate is
procedural: the enforced referendum period, the challenge window, and the fact that one nay is enough to force a real
contest. This is a deliberate trade against a minimum-support floor (see open questions), and it is why the period is
validated as a security property rather than a liveness convenience.
### Timing
@@ -334,9 +404,11 @@ against the proposal, so a malicious certificate assembler would omit nay votes.
to selective inclusion:
- A certificate is **unconditional** if it would pass even with every electorate member not in the certificate counted
as nay. Substituting `B ← B + (E T)`, `T ← E` in the condition reduces it to exactly `2A > E`: ayes are a strict
as nay. Substituting `B ← B + (E T)` into `A > B` gives `A > E A`, i.e. exactly `2A > E`: ayes are a strict
majority of the whole electorate. No withheld or future vote can flip such a certificate (only annulment of an
equivocated aye already inside it can; see limitations), and it is applied immediately.
equivocated aye already inside it can; see limitations), and it is applied immediately. Note this test is independent
of the tally rule: it asks whether any outstanding vote could change the outcome, so it survives unchanged if the
rule is later replaced.
- Any other valid certificate starts a local **challenge window** (`challengeHours`) beginning at
`max(certificate receipt, local expiresAt)`. The receiving member rebroadcasts the certificate, any member holding
valid votes absent from it (in particular, nay voters themselves) resends them, and members who first saw the proposal
@@ -362,12 +434,12 @@ Application is a single local transaction, generalizing `applyAtRosterVersion`:
1. check `govVersion` **greater than** the stored governance version, and (for a gap greater than one) that the
**witnessed chain** conditions below hold. A stale version is ignored as replay, with one exception: a valid
same-version certificate for a *different* proposal supersedes the applied one iff it ranks higher in **mandate
order**: more ayes first, then unconditional before non-unconditional, then smaller `proposalHash` as the final
deterministic tie-break. Aye count leads because unconditionality is relative to each proposal's own electorate: a
banked certificate from a small past electorate must not outrank a better-supported live one. All three ranking
components are computed from the canonical certificate bytes and the referenced proposal's electorate, independent of
locally held votes: local annulment governs whether a certificate is *acceptable*, never how it *ranks*, so the order
is total and identical at every member;
order**: larger margin `A B` first, then larger aye count `A`, then smaller `proposalHash` as the final
deterministic tie-break. Margin leads because it is the quantity the tally rule itself decides on, and because it is
absolute rather than relative to each proposal's own electorate: a banked certificate drawn from a small past
electorate must not outrank a better-supported live one. All three ranking components are computed from the
canonical certificate bytes, independent of locally held votes: local annulment governs whether a certificate is
*acceptable*, never how it *ranks*, so the order is total and identical at every member;
2. set every member listed in the action to `GRAdmin`; demote every other current `GRAdmin` to `GRMember`;
3. store the new governance version, winning proposal and certificate;
4. **announce the applied certificate once to all connections** (unconditional certificates included) as a compact
@@ -425,7 +497,7 @@ considering (open question 6); it bounds both this residual and the O (gap) fetc
can compel.
Mandate order makes same-version arbitration substantive: a certificate can displace only one with a weaker showing, and
ayes cannot be ground (they are real member signatures), so the attacker-influenced `proposalHash` decides only ties
margins cannot be ground (they are real member signatures), so the attacker-influenced `proposalHash` decides only ties
between certificates of identical mandate strength. It still cannot elevate an unpassed proposal. Known race: actions
taken by admins of the losing certificate during the overlap are invalid in the winners' view; clients should surface a
"contested result" state, and new admins should avoid destructive actions until their certificate's window (including
@@ -473,11 +545,10 @@ machinery this design generalizes):
a client serves a given requester only versions above what it last served them (as the roster machinery does with
`roster_served_version`), and rate-limits serving per requester over time. The response also includes the *hashes* of
any active proposals at the requester's new current version + 1 (full proposals are fetched on demand by
`proposalHash`, keeping responses bounded; retention allows up to one proposal per proposer, each shipping an
electorate list), so that a member advancing via catch-up regains the ability to vote in the live referendum instead
of being silently disenfranchised (it still counts in `E`, so its forced abstention would raise the bar for everyone).
- The catching-up member validates each bundle: the structural timing check, signatures, and electorate checks evaluated
against its recorded membership history at the bundle's `proposedAt` (settlement and removal times are recorded),
`proposalHash`, keeping responses bounded; retention allows up to one proposal per proposer), so that a member
advancing via catch-up regains the ability to vote in the live referendum instead of being silently disenfranchised.
- The catching-up member validates each bundle: the structural timing check, signatures, and the electorate derived from
the bundle's cited `logFrontier` (fetching any log entries it lacks),
skipping the live plausibility check. The tally is evaluated over the bundle's votes **union** its own locally held
votes for that proposal, without a challenge window; the member is judging evidence of an outcome already final
elsewhere, and *that* premise must be established, not assumed: before adopting a bundle (or a stale certificate
@@ -497,7 +568,7 @@ machinery this design generalizes):
third-party-verifiable record of who vouched for enacting the result. For a genuinely enacted certificate, nay voters
and abstainers applied it too and can attest; for a withheld one, an attester must out itself. Both conditions are
waived only for certificates whose aye set is a strict majority both of the proposal's electorate and of the
receiver's *current* settled membership; a banked majority that has since eroded gets no waiver, and a one-vote
receiver's electorate at its current log frontier; a banked majority that has since eroded gets no waiver, and a one-vote
fabrication never qualifies. Falling short, the client does not apply, surfaces the pending state, and MAY offer
user-confirmed adoption as a fallback. Version skipping means a member that cannot validate version N can still adopt
N+1 directly, provided it holds N's bundle as a witness (step 1), served alongside N+1's in the same catch-up
@@ -525,35 +596,50 @@ to trust in the introducing host, as with all pre-join group state.
infrastructure, not the admins'; see limitations), and are forwardable by *anyone* for pairs lacking direct
connections (transport rules 12).
- **Silence voters**: snapshot voting rights (rule 3) + removal deferral incl. the send-guard exemption (rule 4).
- **Rig the electorate mid-vote**: the electorate is fixed by the proposal at proposal time; role changes and blocks
never affect eligibility.
- **Rig the electorate**: the electorate is derived from the membership log at the frontier the proposal cites, so it is
identical at every member and cannot be conjured, shrunk, or selectively disclosed without forking the log and leaving
signed evidence; role changes and blocks never affect eligibility.
- **Rush the vote**: the referendum period is structurally bound into the signed proposal; directly received proposals
are plausibility-checked against broker timestamps; and every member gets at least the challenge window between seeing
a non-unconditional result and applying it.
- **Mint an owner or pre-empt by deletion**: governed clients reject owner-role members outright, so `XGrpDel` and other
owner-gated events have no valid sender.
- **Turn governance off**: governance parameters have no update path.
- **Mint an owner or pre-empt by deletion**: introduced roles are capped at the introducer's, and governed clients
reject owner-role members outright, so `XGrpDel` and other owner-gated events have no valid sender.
- **Turn governance off or re-run genesis**: parameters have no update path, and a second `x.grp.gov.enable` for an
already-governed group is rejected rather than applied, so the constitution cannot be reset by anyone.
What incumbents *can* still do is act before a referendum exists; see limitations.
## Security analysis and limitations
- **Curated electorate / Sybil.** The electorate is the historically admin-curated member list; admins may have admitted
sock puppets long before any vote ("join the same group several times... and pretend to be different members", threat
model). Opt-in groups accept this; `2024-03-14-super-peers.md` raises the same concern in passing, suggesting voting
power weighted by "community score" to "compensate for anonymous participants who could subvert the vote if plain vote
count was made", an aside it explicitly calls out of scope, as it is here for v1.
- **Sybil.** Enfranchisement requires `witnessCount` confirmations from already-enfranchised members, so identities
cannot be conjured, but they can be *grown*: an attacker who runs real clients and gets them confirmed by honest
members accumulates real votes, and members who have long since joined ("join the same group several times... and
pretend to be different members", threat model) are already enfranchised. This is unavoidable without a central
authority (Douceur), so the design targets cost and visibility rather than prevention: every identity is on the log
with a signed record of who vouched for it, which is exactly the evidence a group needs to notice and to respond with
a referendum. `2024-03-14-super-peers.md` raises the same concern, suggesting voting power weighted by "community
score" to "compensate for anonymous participants who could subvert the vote if plain vote count was made"; that is
the eventual answer and is out of scope here, as it is there.
- **The genesis bootstrap is the one place local knowledge is authoritative.** Every later membership fact is checked
against the log, but the log has to start somewhere, and its root can only be validated against what each receiver
already believes: the recorded owner set, or for an ownerless group the recorded member list. An admin who has
equivocated membership *before* enabling can therefore seal a skewed genesis for the members it lied to, and those
members fail closed against everyone else rather than being silently captured. Requiring signers to be members the
receiver has actually connected to raises the cost, and comparing frontiers with a second peer detects the result,
but the bootstrap cannot be made self-authenticating. Enable governance in a group whose membership you can see is
settled.
- **Pre-proposal purges.** All anti-silencing guarantees are scoped to a referendum in progress. An incumbent who moves
first can remove suspected dissidents *before* any proposal exists, shrinking the future electorate. Removals are
broadcast events, so a purge is visible to the remaining members, who can respond by immediately proposing (any member
can, at any time); but members removed before that proposal are gone. The electorate tolerances (recently settled
members and adjacent removals) widen this margin slightly; clients must surface both to voters, whose remedy is a nay.
The structural fix is an authenticated membership log (step 2).
can, at any time); but members removed before that proposal are gone. The membership log makes a purge undeniable and
attributable rather than merely visible, and removals adjacent to a proposal should be surfaced to voters, whose
remedy is a nay; it does not make the removals reversible.
- **Withheld-proposal partition.** Colluders can distribute a proposal only among themselves and present proposal +
certificate together at expiry. Victims receiving both via forwards accept the proposal (no timing check on forwards)
and, under the late-voting rule, may cast fresh nays until their own challenge windows close. This defense depends on
aggregation: the tally test is nonlinear, so a victim rejects only if the nays *it* holds at window close suffice (at
`E = 100` against 40 colluding ayes, a member must hold ≥ 35 nays; one holding 30 adopts the coup). Aggregation in
aggregation: a victim rejects only if the nays *it* holds at window close outnumber the ayes in the certificate (at
`E = 100` against 40 colluding ayes, a member must hold 40 nays; one holding 39 adopts the coup, and under the
previous turnout-weighted rule 35 would have sufficed, so the simpler tally is the weaker one here). Aggregation in
turn depends on certificate rebroadcast and vote fan-out reaching victims within overlapping windows, which the
window-extension rule exists to maximize, and pre-existing partitions (below) can undermine. The realistic outcome is
therefore a contested result whose boundary tracks connectivity: early isolated evaluators may adopt, later ones
@@ -561,15 +647,15 @@ What incumbents *can* still do is act before a referendum exists; see limitation
nays count nowhere. An unconditional certificate cannot be produced this way without a genuine majority; a large
colluding minority plus engineered isolation of victims is the residual risk, superseded (like all divergence here)
by the next referendum the victims can validate.
- **Membership equivocation.** Admins can have shown different membership states to different members *before* a
proposal; those members then disagree on the electorate and fail closed (refusing the referendum rather than being
manipulated) and recover via catch-up only if their records permit validation. Signing membership events makes
equivocation evidence-producing but not impossible. The proper fix is a channels-style authenticated roster; see
future work.
- **Benign electorate races.** Membership changes in flight when a proposal is made can cause honest members to reject
it (check 3). The settled-member rule plus the settlement-skew tolerance absorb join races (settlement is a per-client
observation and propagates slowly); the removal tolerance absorbs removal races; catch-up repairs stragglers. Residual
rejections cost the proposer a retry at the next version, not a partition.
- **Membership equivocation.** The log converts equivocation from an undetectable divergence into a fork with a signed
proof of who authored the conflicting histories, and selective disclosure into the same case as soon as any two
honest members compare frontiers. It does not prevent an author from equivocating, and a member whose only peers are
the equivocator and its confederates has nobody to compare with; that residual is the pre-existing-partition case
below.
- **Benign membership races.** Membership changes in flight when a proposal is made no longer cause rejections: the
electorate is derived from a cited frontier, so a receiver that is behind fetches the missing entries and proceeds,
and one that is ahead simply evaluates at the older frontier. What remains is that a member enfranchised moments
after the frontier cannot vote in that referendum, which costs a wait rather than a partition.
- **Pre-existing partitions.** Pairs the admins never introduced (including the documented concurrent-invite race in
`2025-11-24-member-relations-vector.md`) still cannot exchange votes directly; the any-member forwarding rule reduces,
but does not eliminate, dependence on connectivity the incumbents shaped.
@@ -604,7 +690,7 @@ What incumbents *can* still do is act before a referendum exists; see limitation
since acceptance beyond a one-version gap is bounded by the frontier independently attested by several members from
outside the certificate's aye set, and a completed referendum retires every reserve strictly below its version. A
banked competitor at the *same* version can contest only that one round; under mandate order it prevails only by
out-polling the live certificate in genuine ayes (equal ayes fall through to the unconditionality tie-break), which a
out-polling the live certificate on margin (equal margins fall through to aye count, then the hash), which a
minority cannot do against a well-supported **reaffirmation referendum** (proposing the current admin set, already
expressible as `GAReplaceAdmins`). Clients should offer reaffirmation; flushing all suspected reserves takes at most
two successive referenda. The freshness horizon and attestation requirement do not make late enactment impossible (one
@@ -621,8 +707,23 @@ What incumbents *can* still do is act before a referendum exists; see limitation
See "Related work" at the end of this document for how these choices sit against the literature, and
`2026-08-01-group-governance-related-work.md` for the full analysis.
- **Fixed majority of the electorate.** Subsumed: it is exactly the "unconditional certificate" case; AQB additionally
lets lower-turnout referenda resolve, at the cost of the challenge window.
- **Adaptive quorum biasing** (Polkadot's positive turnout bias, `B²E < A²T`), which demands a supermajority of votes
cast at low turnout and relaxes to majority as turnout rises. Rejected for this setting. Its required support, as a
fraction of the electorate, is `p²/(1p)` where `p` is the opposition share of votes cast, which is independent of
group size: it lets a bloc win in inverse proportion to how few members object, and at any size a handful of ayes
beats a large silent membership once opposition is thin. It is *needed* only where a majority of the electorate is
unreachable, and *safe* only where opponents reliably vote; those hold together in token governance, which has a
standing class of monitoring delegates, and not in a private group, where would-be objectors are drawn from the same
attention distribution as everyone else. It also imposes a threshold that moves as votes arrive, which cannot be
rendered honestly in a UI. Note the trade is not free: at low turnout AQB is *stricter* than `A > B`, so dropping it
weakens minority-capture resistance and shifts that burden onto the voting period and challenge window.
- **Fixed majority of the electorate** (`2A > E`). Retained, but as the test for applying a certificate *immediately*
rather than as the pass condition; as a pass condition it is unreachable in groups where the transport itself
suppresses turnout (an offline member's queue stalls at `defaultMsgQueueQuota = 128` messages, so members away from
an active group may not receive a proposal within its period, while still counting in `E`).
- **A minimum-support floor** (`A > B` and `A ≥ max(3, ⌈E/3⌉)`), which would remove the single-aye case without
reintroducing an abstention incentive, since a floor on ayes is unaffected by opponents staying home. Deferred to an
open question rather than adopted, to keep v1's rule to one clause.
- **Interactive consensus (BFT / owner DAG voting)** as sketched in `2023-10-20-group-integrity.md`: rejected there
already as impractical for mobile clients; unnecessary here since a one-shot threshold certificate suffices.
- **Approvals among privileged members only** (`2024-04-01-super-peers-2.md`): solves accidental/destructive actions
@@ -632,17 +733,24 @@ See "Related work" at the end of this document for how these choices sit against
## Implementation sketch
- `Protocol.hs`: `GovAction`, enable/proposal/vote/cert/request types, five event tags (+ `isForwardedGroupMsg`);
deterministic binary encodings with domain-separation tags (`SXGG`/`SXGP`/`SXGV`, plus `SXGS` state attestations
served in catch-up responses).
- `Protocol.hs`: `GovAction`, enable/proposal/vote/cert/request types plus the membership-log entry type and a
`x.grp.gov.log` event carrying entries and frontier requests, six event tags (+ `isForwardedGroupMsg`); deterministic
binary encodings with domain-separation tags (`SXGG`/`SXGP`/`SXGV`, `SXGL` for log entries, plus `SXGS` state
attestations served in catch-up responses).
- Membership log: entry validation (parent hashes present, author enfranchised and permitted, signature), DAG merge and
frontier computation, enfranchisement evaluation (`witnessCount` confirmations), fork detection on frontier compare,
and derivation of the electorate at a frontier. `Confirmed` entries are emitted from the existing `x.grp.mem.con`
path, signed; the connection event that already exists becomes the enfranchising witness.
- Key management: per-group member keypair for p2p governed groups (`group_members.member_pub_key` exists; add p2p
private-key storage and population of `memberPubKey` on join/intro; TOFU pinning as in `applyMemberKeyRole`).
- Optionally in the same version: populate and verify signatures on `XGrpMemNew`/`XGrpMemDel`/`XGrpMemRole` in governed
p2p groups via the existing p2p verification branch in `withVerifiedMsg` (equivocation evidence; independently closes
the unsigned-forward forgery hole). In governed groups these events also carry the sender's governance version
(reusing the existing `rosterVersion` field on `XGrpMemRole`/`XGrpMemDel`, plus an equivalent optional field on
`XGrpMemNew`), which is the catch-up trigger for members with no other governance traffic.
- `Subscriber.hs`: handlers for the five events; genesis parameter-bounds validation; proposal timing validation and
- Required, not optional, now that membership is load-bearing: sign and verify `XGrpMemNew`/`XGrpMemDel`/`XGrpMemRole`
in governed p2p groups via the existing p2p verification branch in `withVerifiedMsg`, and mirror each into a log entry
(independently closes the unsigned-forward forgery hole). In governed groups these events also carry the sender's
governance version (reusing the existing `rosterVersion` field on `XGrpMemRole`/`XGrpMemDel`, plus an equivalent
optional field on `XGrpMemNew`), which is the catch-up trigger for members with no other governance traffic.
- `Subscriber.hs`: **the `xGrpMemIntro` role cap (prerequisite, applies to all p2p groups, not only governed ones)**;
handlers for the six events; genesis validation (already-governed rejection, non-empty authorising set, signer
connectedness, parameter bounds on both ends); proposal timing validation and
per-proposer retention cap; certificate validation + challenge-window worker with late-voting support; version-gated
apply with witnessed-chain version skipping, the same-version mandate-order exception, and the post-apply compact
announcement; catch-up serving from stored bundles with per-requester served-version bound and rate limiting;
@@ -659,12 +767,23 @@ ALTER TABLE groups
ADD COLUMN governance TEXT; -- params + governanceId; null = not governed
ALTER TABLE groups
ADD COLUMN governance_version INTEGER;
ALTER TABLE group_members
ADD COLUMN settled_at TEXT;
-- when this client saw the member connect;
-- backfilled from created_at for members already at GSMemConnected or beyond at migration time
ALTER TABLE group_members
ADD COLUMN gov_served_version INTEGER; -- catch-up amplification bound
CREATE TABLE group_membership_log
(
entry_id INTEGER PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups ON DELETE CASCADE,
entry_hash BLOB NOT NULL,
parents BLOB NOT NULL, -- sorted parent hashes
action TEXT NOT NULL, -- added / removed / role_changed / confirmed
subject_id BLOB NOT NULL,
subject_key BLOB, -- MemberKey, on `added`
author_id BLOB NOT NULL,
entry_ts TEXT NOT NULL,
entry_sig BLOB NOT NULL,
UNIQUE (group_id, entry_hash)
);
CREATE INDEX idx_membership_log_subject ON group_membership_log (group_id, subject_id);
CREATE TABLE group_referenda
(
referendum_id INTEGER PRIMARY KEY,
@@ -672,7 +791,7 @@ CREATE TABLE group_referenda
proposal_hash BLOB NOT NULL,
gov_version INTEGER NOT NULL,
action BLOB NOT NULL,
electorate BLOB NOT NULL, -- member id list + hash; retained for catch-up serving
log_frontier BLOB NOT NULL, -- cited membership-log heads; electorate derived from it
prev_proposal_hash BLOB NOT NULL,
proposed_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
@@ -692,7 +811,8 @@ CREATE TABLE group_referendum_votes
);
```
- API: `APIEnableGroupGovernance`, `APIProposeGroupAdmins`, `APIGroupVote`; certificate assembly, application, and
- API: `APIEnableGroupGovernance` (collects owner or founding-majority signatures depending on the group's owner set),
`APIProposeGroupAdmins`, `APIGroupVote`; certificate assembly, application, and
catch-up are automatic. Chat items for proposal / votes / result / contested result, styled like existing group
events.
@@ -704,12 +824,10 @@ CREATE TABLE group_referendum_votes
link-queue `RKEY` authority in simplexmq, where owners are currently ranked so the creator cannot be demoted, and any
single owner key suffices to update the link, multisig having been considered and deferred there. That is the step-2
RFC, and it is where this design meets the project's stated roadmap item "Multisig: M-of-N approval for administrative
actions" (`docs/protocol/channels-overview.md`). An authenticated membership log (roster) would also retire the
electorate-equivocation and pre-proposal-purge limitations above.
actions" (`docs/protocol/channels-overview.md`). The membership log introduced here is the p2p counterpart of the
channel roster, and the two should converge on one representation.
- **More referendum actions**: change governance parameters (including disabling), update profile/preferences, delete
the group, replace moderators.
- **Governance for ownerless legacy groups** (no one left to sign the genesis certificate): possibly unanimous-member
enabling.
- **Reputation-weighted or Sybil-resistant voting**, per `2024-03-14-super-peers.md`.
- **Ballot secrecy** (blind or ring signatures) for groups that need it.
@@ -721,9 +839,9 @@ CREATE TABLE group_referendum_votes
3. Should the genesis certificate require unanimity of owners (current design) or majority of owners?
4. Whether to sign membership events in governed p2p groups in v1 (recommended here) or defer to a separate signing
rollout.
5. The electorate tolerances (settlement skew, in-flight removals) trade benign-race robustness against
proposer/incumbent discretion over the electorate's margins; is surfacing them to voters sufficient, or should the
tolerances be zero (stricter, more benign rejections)?
5. `witnessCount` defaults to 2. Higher values raise the cost of manufacturing an enfranchised identity but delay
enfranchisement of genuine joiners in sparse groups, and can strand a joiner whose introductions stall. Is 2 right,
and should it scale with group size?
6. The tier- (ii) deferral and catch-up fetch budgets are two-sided DoS tuning: too tight, and purges become schedulable
into predictably unprotected gaps; too loose, and reference spam suspends moderation or starves the objection path.
The witnessed chain adds a third: serving and storing O (gap) bundles is itself an amplification surface, which an
@@ -733,6 +851,12 @@ CREATE TABLE group_referendum_votes
7. Should certificate *acceptance* be made a pure function of the certificate, as ranking already is? It is currently
the design's one deviation from strong eventual consistency and the direct cause of knife-edge divergence; see
"Related work".
8. Should the tally carry a minimum-support floor, e.g. `A > B` and `A ≥ max(3, ⌈E/3⌉)`? Plain majority of votes cast
means one aye passes an unnoticed referendum, and the only thing preventing that is whether anyone is paying
attention during the period. A floor closes it, stays reachable at realistic turnout, and unlike a turnout quorum
creates no incentive to boycott. The argument against is that it adds a second clause to a rule whose whole value is
that a member can state it from memory, and that it can deadlock a group whose active population has fallen below
the floor. This is the most consequential open question in the document.
## Related work
@@ -783,17 +907,22 @@ tolerates the same flickering: "a user may be removed and re-added, possibly ind
fills. [More is Less](https://eprint.iacr.org/2017/713) (Rösler, Mainka & Schwenk, EuroS&P 2018) found group management
messages unauthenticated in deployed messengers, which is the empirical case for signing membership events here.
**The voting rule.** Positive turnout bias is from [Polkadot](https://arxiv.org/abs/2005.13456) (Burdges et al.): "in
case of low turnout we favour the nay side, or status quo, by requiring a super-majority approval, and as turnout
approaches 100% the requirement dials down to majority-carries", justified by status-quo safety and the volatility of
narrow low-turnout results. **We prefer it to a fixed quorum** because participation quorums reward abstention
(opponents defeat a proposal more cheaply by boycotting than by voting nay) and because on-chain governance shows
turnout far too low for fixed quorums to be met ([Feichtinger et al.](https://arxiv.org/abs/2302.12125); [Fritsch et
al.](https://arxiv.org/abs/2204.01176)). AQB's weakness is that with zero nays any non-empty aye set passes, so our
protection is procedural: the enforced period and challenge window, during which a single nay raises the bar steeply.
**We reject ballot secrecy** ([Juels, Catalano & Jakobsson](https://doi.org/10.1145/1102199.1102213), WPES 2005) because
verifiable signatures are what make a certificate self-authenticating and able to travel past a hostile forwarder, at
the cost, acknowledged in the limitations, that incumbents can identify nay voters.
**The voting rule.** We use majority of votes cast. May's theorem characterises simple majority as the unique rule that
is anonymous, neutral and positively responsive, so departures from it need a justification specific to the setting.
**We reject a turnout quorum** because participation thresholds reward abstention (opponents defeat a proposal more
cheaply by boycotting than by voting nay), and on-chain governance shows turnout far too low for fixed quorums to be
met in any case ([Feichtinger et al.](https://arxiv.org/abs/2302.12125); [Fritsch et
al.](https://arxiv.org/abs/2204.01176)). **We also reject [Polkadot's](https://arxiv.org/abs/2005.13456) adaptive
quorum biasing** (Burdges et al.: "in case of low turnout we favour the nay side, or status quo, by requiring a
super-majority approval"), despite its appeal, because its required support as a fraction of the electorate is
`p²/(1p)` in the opposition share `p` and therefore independent of group size: it is scale-invariant in exactly the
way that makes it dangerous here, letting a small bloc win whenever opposition is thin. Its safety rests on a standing
class of attentive opponents, which token governance has and a private group does not; the DAO turnout evidence often
cited in its favour describes a population unlike this one and does not transfer. The cost of dropping it is real and
recorded in the limitations: at low turnout AQB was the stricter rule. **We reject ballot secrecy** ([Juels, Catalano &
Jakobsson](https://doi.org/10.1145/1102199.1102213), WPES 2005) because verifiable signatures are what make a
certificate self-authenticating and able to travel past a hostile forwarder, at the cost, acknowledged in the
limitations, that incumbents can identify nay voters.
**Sybil resistance and accountability.** [The Sybil Attack](https://doi.org/10.1007/3-540-45748-8_24) (Douceur, IPTPS
2002) states the limit we inherit: without a central authority, Sybils are always possible, and our electorate is an