NIP-CONSENSUS: Multi-Party Consensus
NIP-CONSENSUS
Multi-Party Consensus
draft optional
Two addressable event kinds for gathering agreement from multiple parties on Nostr — a proposer publishes a consensus proposal listing required voters and a threshold, and each voter casts a signed vote.
Design principle: Consensus proposals are a coordination primitive for group decisions. They record who agreed and who did not — they do not enforce outcomes. The consuming application decides what happens when consensus is reached or fails.
Standalone. This NIP works independently on any Nostr application.
Motivation
Nostr has mechanisms for individual expression (notes, reactions, zaps) but no standard for structured group decisions. Many collaborative workflows require agreement from multiple independent parties before proceeding:
- DAOs and governance — proposals requiring a quorum of members to pass
- Group purchases — splitting a purchase across friends who must all agree
- Multi-sig coordination — human-readable proposals preceding on-chain multi-sig actions
- Community moderation — multiple moderators must agree before taking action
- Event planning — all vendors must confirm availability for a proposed timeline
NIP-57 zaps express support with money, and NIP-25 reactions express sentiment, but neither models threshold-based consensus with identified voters. See [Relationship to Existing NIPs](#relationship-to-existing-nips) below for a detailed comparison. NIP-CONSENSUS fills this gap with a minimal, composable primitive for structured group decision-making.
Relationship to Existing NIPs
- NIP-25 (Reactions): Reactions express sentiment (
+/-) but lack the structure needed for threshold-based governance. A reaction-based approach would require each client to independently maintain the voter set, implement threshold arithmetic, track abstentions, enforce deadlines, and handle reaction updates, all without relay-side filtering support. Kind 30574 (Proposal) encodes the voter set, threshold, quorum type, and deadline in a single event. Kind 30575 (Vote) is relay-filterable by the proposal'satag, so clients fetch only votes for a specific proposal rather than scanning all reactions. - Concrete example: A DAO with 5 board members needs 3/5 approval within 48 hours. With NIP-25 reactions, any pubkey can react to the proposal note; the client must cross-reference a separately maintained board list, ignore non-member reactions, track abstentions against the deadline, and determine when the threshold is unreachable. With NIP-CONSENSUS, the proposal event declares
voters: 5,threshold: 3, andexpiration: <timestamp>. The relay returns only kind 30575 events matching the proposal, each from a pubkey in the declared voter set. - NIP-57 (Zaps): Zaps express economic support but not structured agree/disagree/abstain decisions with quorum semantics.
Kinds
| kind | description |
|---|---|
| 30574 | Consensus Proposal |
| 30575 | Consensus Vote |
Both kinds are addressable events (NIP-01). The d tag format ensures each event occupies a unique slot, allowing updates via republication.
Consensus Proposal (kind:30574)
Published by a proposer to initiate a multi-party decision. Lists all required voters via p tags and specifies the consensus threshold.
{
"kind": 30574,
"pubkey": "<proposer-hex-pubkey>",
"created_at": 1698768000,
"tags": [
["d", "dao_budget_2026:consensus:q1_allocation"],
["t", "consensus-proposal"],
["alt", "Consensus proposal: allocate 500000 sats to relay infrastructure"],
["p", "<member_a-hex-pubkey>"],
["p", "<member_b-hex-pubkey>"],
["p", "<member_c-hex-pubkey>"],
["p", "<member_d-hex-pubkey>"],
["threshold", "3"],
["expiration", "1698854400"],
["consensus_type", "budget"]
],
"content": "Proposal: Allocate 500,000 sats from the community treasury to fund relay infrastructure for Q1 2026. Breakdown: 300,000 sats for hosting, 200,000 sats for development bounties.",
"id": "<32-bytes lowercase hex>",
"sig": "<64-bytes lowercase hex>"
}Tags:
d(REQUIRED): Addressable event identifier. RECOMMENDED format:<context_id>:consensus:<sequence>. Applications MAY use any d-tag format that ensures uniqueness.t(REQUIRED): Protocol family marker. MUST be"consensus-proposal".p(REQUIRED, multiple): Oneptag per required voter. Each voter's hex pubkey.threshold(REQUIRED): Integer string — minimum number ofagreevotes required for the proposal to pass.expiration(RECOMMENDED): Unix timestamp — voting deadline. Clients SHOULD use NIP-40expirationfor relay-level enforcement.consensus_type(RECOMMENDED): Category of decision. Primary values:budget,governance,moderation. Applications MAY define additional types such astimeline,scope,terms,design, or other domain-specific values.ref(OPTIONAL): External reference.e(OPTIONAL): Event ID of the item requiring consensus (e.g. the content, transaction, or action being voted on).
Content: Plain text or NIP-44 encrypted JSON describing the proposal. SHOULD include enough detail for all voters to make an informed decision.
Consensus Vote (kind:30575)
Published by a voter to cast their vote on a consensus proposal. The d tag format allows one vote per voter per proposal. Addressable, so voters can change their vote before the deadline.
{
"kind": 30575,
"pubkey": "<member_a-hex-pubkey>",
"created_at": 1698769000,
"tags": [
["d", "dao_budget_2026:consensus:q1_allocation:vote:<member_a-hex-pubkey>"],
["t", "consensus-vote"],
["alt", "Consensus vote: agree on Q1 budget allocation"],
["e", "<consensus-proposal-event-id>", "wss://relay.example.com"],
["vote", "agree"],
["p", "<proposer-hex-pubkey>"]
],
"content": "Agreed. The relay infrastructure is critical for our community's growth.",
"id": "<32-bytes lowercase hex>",
"sig": "<64-bytes lowercase hex>"
}Tags:
d(REQUIRED): Format<proposal_d_tag>:vote:<voter_pubkey>. One vote per voter per proposal.t(REQUIRED): Protocol family marker. MUST be"consensus-vote".e(REQUIRED): Event ID of the Kind 30574 proposal being voted on.vote(REQUIRED): The voter's decision. One of"agree","disagree", or"abstain".p(RECOMMENDED): Proposer's pubkey (for notification).condition(OPTIONAL): Conditional agreement text (e.g. "agree if budget stays under 500,000 sats").
Content: Plain text with the voter's rationale or conditions.
Protocol Flow
Proposer Relay Voters
| | |
|-- kind:30574 Proposal ---->| |
| (threshold: 3, | |
| voters: V1, V2, V3, V4) | |
| |------- notification ------>| V1
| |------- notification ------>| V2
| |------- notification ------>| V3
| |------- notification ------>| V4
| | |
| |<-- kind:30575 Vote --------| V1 (agree)
| |<-- kind:30575 Vote --------| V2 (agree)
| |<-- kind:30575 Vote --------| V3 (agree)
|<------ notification -------| |
| | |
| Threshold (3) reached | |
| Consensus passed | |
| | |- Proposal: Proposer publishes
kind:30574listing all required voters viaptags and thethresholdfor passage. - Voting: Each voter evaluates the proposal and publishes
kind:30575with theirvote. - Vote changes: Voters MAY update their vote by republishing
kind:30575(addressable event replacement) before the deadline. - Resolution: The proposal passes when the number of
agreevotes meets or exceeds thethreshold. It fails if the deadline passes without meeting the threshold, or if enoughdisagreevotes make the threshold unreachable.
Quorum Rules
- Threshold: The
thresholdtag defines the minimum number ofagreevotes required. It MUST be at least 1 and MUST NOT exceed the number ofp-tagged voters. - Unanimous: Set
thresholdequal to the number of voters for unanimous consent. - Simple majority: Set
thresholdtoceil(voters / 2). - Abstentions: An
abstainvote counts as participation but not towards the threshold. - Deadline: If
expirationpasses without the threshold being met, the proposal is considered failed.
The following diagram illustrates how votes are tallied and consensus is resolved:
flowchart TD
classDef green fill:#1b3d2d,stroke:#16c79a,color:#f0f0f0
classDef yellow fill:#2d2d1b,stroke:#f5a623,color:#f0f0f0
classDef blue fill:#1b2d3d,stroke:#0f3460,color:#f0f0f0
classDef red fill:#3d1b1b,stroke:#e94560,color:#f0f0f0
PROPOSAL([Proposer publishes<br/>kind:30574 Proposal<br/>voters: N, threshold: T]):::blue
PROPOSAL --> VOTING[VOTING OPEN<br/>Voters cast kind:30575]:::yellow
VOTING --> VOTE{New vote<br/>received}:::blue
VOTE --> VALID{Voter pubkey<br/>in proposal p tags?}:::yellow
VALID -- "No" --> IGNORE([Ignore vote]):::red
VALID -- "Yes" --> TALLY[Update tally:<br/>agree / disagree / abstain]:::blue
TALLY --> AGREE_CHECK{agree count<br/> >= threshold T?}:::yellow
AGREE_CHECK -- "Yes" --> PASSED([CONSENSUS PASSED]):::green
AGREE_CHECK -- "No" --> DISAGREE_CHECK{disagree count ><br/>N minus T?}:::yellow
DISAGREE_CHECK -- "Yes" --> FAILED_MATH([CONSENSUS FAILED<br/>Threshold unreachable]):::red
DISAGREE_CHECK -- "No" --> DEADLINE{Expiration<br/>reached?}:::yellow
DEADLINE -- "Yes" --> FAILED_TIME([CONSENSUS FAILED<br/>Deadline passed]):::red
DEADLINE -- "No" --> VOTINGUse Cases
DAO Governance
Nostr-native DAOs can use consensus proposals for treasury management, membership decisions, and policy changes. Each proposal lists the DAO members as voters, with the threshold set per the DAO's bylaws. Votes are cryptographically signed and publicly verifiable, providing transparent governance without a custom voting platform.
Group Purchase Coordination
Friends splitting a group purchase (concert tickets, shared subscriptions, group gifts) can use a consensus proposal to confirm everyone is in before committing. The proposer lists all participants and sets threshold to the total count — ensuring unanimous agreement before money moves.
Community Moderation Decisions
Nostr communities with multiple moderators can use consensus proposals for moderation actions (banning users, pinning content, changing community rules). The threshold model ensures no single moderator can act unilaterally, and the signed vote trail provides accountability.
Multi-Party Contract Agreement
Before formalising a contract or agreement between multiple parties (e.g. a joint venture, a shared workspace lease, a collaboration agreement), a consensus proposal can confirm that all parties agree to the terms. Each party's signed vote serves as a cryptographic record of their consent.
Test Vectors
All examples use timestamps around 1709280000 (2024-03-01) and placeholder hex pubkeys.
Kind 30574 — Consensus Proposal
A budget approval proposal listing 3 voters with a threshold of 2.
{
"kind": 30574,
"pubkey": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"created_at": 1709280000,
"tags": [
["d", "treasury_2026:consensus:q1_budget"],
["t", "consensus-proposal"],
["alt", "Consensus proposal: allocate 250000 sats to relay hosting"],
["p", "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3"],
["p", "c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"],
["p", "d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5"],
["threshold", "2"],
["expiration", "1709366400"],
["consensus_type", "budget"]
],
"content": "Allocate 250,000 sats from the community treasury to fund relay hosting for Q1 2026.",
"id": "<32-byte-hex>",
"sig": "<64-byte-hex>"
}Kind 30575 — Consensus Vote
An "agree" vote from one of the listed voters on the above proposal.
{
"kind": 30575,
"pubkey": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3",
"created_at": 1709283600,
"tags": [
["d", "treasury_2026:consensus:q1_budget:vote:b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3"],
["t", "consensus-vote"],
["alt", "Consensus vote: agree on Q1 budget"],
["e", "aaaa1111bbbb2222cccc3333dddd4444eeee5555ffff6666aaaa1111bbbb2222", "wss://relay.example.com"],
["vote", "agree"],
["p", "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"]
],
"content": "Agreed. Relay infrastructure is essential for the community.",
"id": "<32-byte-hex>",
"sig": "<64-byte-hex>"
}Security Considerations
- Voter verification. Implementations MUST verify that Kind 30575 votes are signed by a pubkey listed in the corresponding Kind 30574's
ptags. Votes from unlisted pubkeys MUST be ignored. - Vote manipulation. Since votes are addressable events, a voter can change their vote by republishing. Clients SHOULD display vote change history (by tracking
created_attimestamps) to prevent hidden vote switching. - Threshold validation. Clients MUST verify that the
thresholdvalue is at least 1 and does not exceed the number ofp-tagged voters. Proposals with invalid thresholds SHOULD be rejected. - Deadline enforcement. Votes published after the
expirationtimestamp SHOULD be ignored. Clients MUST check timestamps when tallying votes. - Content encryption. When proposals contain sensitive information, the proposal SHOULD be delivered via NIP-59 gift wrap (one copy per voter) with the content NIP-44 encrypted pairwise to each recipient. A single NIP-44 ciphertext cannot be decrypted by multiple keys.
- Sybil resistance. The voter list is explicitly defined by the proposer via
ptags. This prevents Sybil attacks but requires trust in the proposer's voter selection. Applications MAY use NIP-02 contact lists or NIP-58 badges to verify voter eligibility.
Dependencies
- NIP-01: Basic protocol flow, addressable events
- NIP-40: Expiration timestamps (voting deadlines)
- NIP-44: Versioned encrypted payloads (sensitive proposal content)
Reference Implementation
No public reference implementation exists yet. Implementors SHOULD refer to the kind definitions above.
A minimal implementation requires:
- A Nostr client that supports addressable event publishing.
- Vote tallying logic that counts
agreevotes against thethreshold, respecting voter eligibility and deadline enforcement. - Voter verification to ensure only
p-tagged pubkeys' votes are counted.