A decentralized decision-making protocol implementing Condorcet-style Ranked Choice Voting with IPFS-based data storage and Bitcoin-signed message verification.
Note: Looking for the web application implementation? Check out the Web App README for details on the included web interface and mobile signing app.
The Hivemind Protocol web application is available as a Docker image:
# Pull the Docker image
docker pull valyriantech/hivemind:latest
# Run the container with required ports
docker run -p 5001:5001 -p 8000:8000 -p 8080:8080 valyriantech/hivemind:latest
The web application will be accessible at http://localhost:8000
The Hivemind Protocol is a revolutionary approach to decentralized decision-making that combines:
- Condorcet-style ranked choice voting
- Immutable IPFS-based data storage
- Cryptographic verification using Bitcoin signed messages
- Flexible voting mechanisms and constraints
- Advanced consensus calculation
-
Decentralized & Transparent
- All voting data stored on IPFS
- Complete audit trail of decisions
- No central authority or server
- Cryptographically verifiable results
-
Advanced Voting Mechanisms
- Condorcet-style ranked choice voting
- Multiple ranking strategies (fixed, auto-high, auto-low)
- Support for various answer types (Boolean, String, Integer, Float)
- Weighted voting with contribution calculation
- Custom voting restrictions and rules
- Predefined options for common types
-
Secure & Verifiable
- Bitcoin-style message signing for vote verification
- Immutable voting history
- Cryptographic proof of participation
- Tamper-evident design
- Comprehensive validation checks
-
Flexible Consensus
- Single-winner and ranked consensus types
- Advanced tie-breaking mechanisms
- State management with reset and exclude options
- Dynamic result recalculation
The protocol's architecture is documented through comprehensive diagrams in the diagrams/
directory:
- Class Diagram (
class_diagram.md
): Core components and their relationships - Component Diagram (
component_diagram.md
): System-level architecture and interactions - State Diagram (
state_diagram.md
): State transitions and validation flows - Voting Sequence (
voting_sequence.md
): Detailed voting process flow - Data Flow Diagram (
data_flow_diagram.md
): How data moves through the system - Cache Invalidation Diagram (
cache_invalidation_diagram.md
): Result caching mechanism - Selection Mode Diagram (
selection_mode_diagram.md
): Different selection behaviors - Verification Process Diagram (
verification_process_diagram.md
): Message signing and verification - Ranking Algorithm Diagram (
ranking_algorithm_diagram.md
): Ranking strategies visualization
pip install hivemind-python
An issue represents a decision to be made. It can contain:
- Multiple questions with indices
- Answer type constraints (Boolean/String/Integer/Float)
- Participation rules
- Custom validation rules
- Predefined options for common types
Options can be predefined or submitted dynamically:
- Automatic options for Boolean types (Yes/No)
- Predefined choices for common scenarios
- Dynamic option submission with validation
- Complex type validation support
- Signature and timestamp verification
Participants express preferences through three ranking methods:
-
Fixed Ranking
opinion = HivemindOpinion() opinion.ranking.set_fixed([option1.cid, option2.cid]) # Explicit order
-
Auto-High Ranking
opinion.ranking.set_auto_high(preferred_option.cid) # Higher values preferred
-
Auto-Low Ranking
opinion.ranking.set_auto_low(preferred_option.cid) # Lower values preferred
The protocol maintains state through:
- IPFS connectivity management
- Option and opinion tracking
- Comprehensive validation
- Contribution calculation
- Multiple state transitions
- Result caching for performance optimization
- Author verification for finalization
This package relies on IPFSDictChain, a library that provides a dictionary-like data structure with IPFS-based storage and blockchain-like chaining capabilities. IPFSDictChain enables the Hivemind Protocol to store and retrieve data in a decentralized manner while maintaining data integrity and immutability.
The HivemindIssue
class is the foundation of the Hivemind Protocol, representing a voting issue to be decided by participants.
from hivemind import HivemindIssue
# Create a new issue
issue = HivemindIssue()
issue.name = "Team Laptop Selection"
issue.description = "We need to select a new standard laptop model for the development team"
issue.tags = ["equipment", "technology", "procurement"]
issue.answer_type = "String"
-
questions: List of questions for ranking the same set of options by different criteria
# All questions will use the same set of options (laptop models) # but participants can rank them differently for each question issue.add_question("Which laptop model is best overall?") issue.add_question("Which laptop model is most reliable based on past experience?") issue.add_question("Which laptop model has the best support options?")
-
answer_type: Defines the expected answer format
# Supported types: String, Integer, Float, Bool, Hivemind, File, Complex, Address issue.answer_type = "String" # For laptop model names
-
constraints: Validation rules for answers
# For numeric answers issue.set_constraints({ "min_value": 0, "max_value": 100 }) # For boolean answers issue.set_constraints({ "true_value": "Approve", "false_value": "Reject" }) # For string answers issue.set_constraints({ "min_length": 5, "max_length": 500, "regex": "^[a-zA-Z0-9 ]+$" }) # For predefined choices issue.set_constraints({ "choices": ["Option A", "Option B", "Option C"] })
-
restrictions: Controls who can participate
issue.set_restrictions({ # Only these addresses can vote "addresses": ["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "..."], # Limit options per address "options_per_address": 3 })
Weighted Voting: You can assign different weights to addresses using the
@weight
syntax:issue.set_restrictions({ # Addresses with weights (format: "address@weight") "addresses": [ "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa@2.5", # This address has 2.5x voting power "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", # Default weight of 1.0 "1MzQwSR3s7RqxJPuQzF7Y4iybjhHNV4bZq@0.5" # This address has 0.5x voting power ], "options_per_address": 3 })
Weights affect the influence of each participant's opinion in the final consensus calculation. Higher weights give more influence to certain participants, which can be useful for stakeholder voting or expertise-weighted decisions.
-
on_selection: Action when consensus is reached
# Valid values: None, Finalize, Exclude, Reset issue.on_selection = "Finalize" # Locks the issue after consensus
The HivemindIssue
class enforces strict validation rules:
- Name: Non-empty string ≤ 50 characters
- Description: String ≤ 5000 characters
- Tags: Unique strings without spaces, each ≤ 20 characters
- Questions: Non-empty, unique strings, each ≤ 255 characters
- Answer Type: Must be one of the allowed types
- Constraints: Must match the answer type
- Restrictions: Must follow the defined format
All issue data is stored on IPFS:
# Save to IPFS
issue_cid = issue.save() # Returns the IPFS CID
# Load from IPFS
loaded_issue = HivemindIssue(cid=issue_cid)
# Generate an identification CID for a participant
identification_cid = issue.get_identification_cid("Participant Name")
The HivemindOption
class represents a voting option in the Hivemind protocol, allowing participants to create and validate options for different answer types.
from hivemind import HivemindOption, HivemindIssue
# Create a new option
option = HivemindOption()
# Associate with an issue
option.set_issue(issue_cid)
# Set the option value (type depends on issue.answer_type)
option.set("Dell XPS 13") # For String answer type
option.text = "Latest model with 32GB RAM" # Optional descriptive text
-
value: The actual value of the option (type varies based on answer_type)
# Different value types based on answer_type option.set("Dell XPS 13") # String option.set(True) # Bool option.set(1500) # Integer option.set(4.5) # Float option.set({"cpu": "i7", "ram": 32, "ssd": 1}) # Complex
-
text: Additional descriptive text for the option
option.text = "Detailed description of this option"
-
hivemind_id: The IPFS hash of the associated hivemind issue
option.hivemind_id = issue.cid()
The class validates options based on the issue's answer_type and constraints:
-
String Validation
# Validates against min_length, max_length, and regex constraints issue.set_constraints({"min_length": 5, "max_length": 100}) option.set("Valid string value") # Will be validated
-
Boolean Validation
# Validates boolean values and ensures text matches constraints issue.set_constraints({"true_value": "Yes", "false_value": "No"}) option.set(True) option.text = "Yes" # Must match the true_value constraint
-
Numeric Validation
# Validates against min_value and max_value constraints issue.set_constraints({"min_value": 0, "max_value": 100}) option.set(75) # Will be validated # Float validation also checks decimals issue.set_constraints({"decimals": 2}) option.set(75.25) # Valid: has 2 decimal places
-
Complex Validation
# Validates complex objects against specs issue.set_constraints({ "specs": { "cpu": "String", "ram": "Integer", "ssd": "Integer" } }) option.set({ "cpu": "i7", "ram": 32, "ssd": 1 }) # Will validate all fields against their types
-
File Validation
# Validates IPFS hashes for file options option.set("QmZ9nfyBfBJMZVqQPiTtEGcBXHAKZ4qMtQ5vwNJNrxQZBb")
-
Address Validation
# Validates Bitcoin addresses option.set("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
Options are stored on IPFS for immutability and decentralization:
# Save to IPFS
option_cid = option.save() # Returns the IPFS CID
# Load from IPFS
loaded_option = HivemindOption(cid=option_cid)
# Get information about the option
info_str = option.info()
# Get the answer type
answer_type = option.get_answer_type()
# Get the CID
cid = option.cid()
The HivemindOpinion
and Ranking
classes work together to represent a voter's preferences in the Hivemind protocol, providing flexible ways to express opinions on options.
from hivemind import HivemindOpinion
# Create a new opinion
opinion = HivemindOpinion()
opinion.hivemind_id = issue_cid
opinion.question_index = 0 # First question in the issue
-
hivemind_id: The IPFS hash of the associated hivemind issue
opinion.hivemind_id = issue.cid()
-
question_index: The index of the question this opinion is for
# For multi-question issues, specify which question this opinion addresses opinion.set_question_index(2) # Third question (zero-indexed)
-
ranking: The ranking of options for this opinion
# The ranking object handles the preference ordering # (See Ranking class below for details) opinion.ranking.set_fixed(["option1_cid", "option2_cid", "option3_cid"])
# Save to IPFS
opinion_cid = opinion.save() # Returns the IPFS CID
# Load from IPFS
loaded_opinion = HivemindOpinion(cid=opinion_cid)
# Get a JSON-serializable representation
opinion_dict = opinion.to_dict()
# Get formatted information
info_str = opinion.info()
The Ranking
class provides three different ways to express preferences:
Explicitly specifies the order of preference for options:
# Create an opinion with fixed ranking
opinion = HivemindOpinion()
opinion.ranking.set_fixed([
"QmOption1Cid", # First choice
"QmOption2Cid", # Second choice
"QmOption3Cid" # Third choice
])
Automatically ranks options based on proximity to a preferred value, with higher values preferred in case of ties:
# For laptop selection with numeric performance scores
# Assuming options have numeric values (e.g., benchmark scores)
opinion = HivemindOpinion()
opinion.ranking.set_auto_high("QmPreferredOptionCid")
# When get() is called with a list of options, they will be ranked by:
# 1. Proximity to the preferred option's value
# 2. Higher values preferred in case of equal distance
ranked_options = opinion.ranking.get(available_options)
Similar to auto-high, but prefers lower values in case of ties:
# For laptop selection with price values
# Assuming options have numeric values (e.g., prices)
opinion = HivemindOpinion()
opinion.ranking.set_auto_low("QmPreferredOptionCid")
# When get() is called with a list of options, they will be ranked by:
# 1. Proximity to the preferred option's value
# 2. Lower values preferred in case of equal distance
ranked_options = opinion.ranking.get(available_options)
from hivemind import HivemindIssue, HivemindOption, HivemindOpinion
# Create an issue for laptop selection
issue = HivemindIssue()
issue.name = "Team Laptop Selection"
issue.add_question("Which laptop model is best overall?")
issue.add_question("Which laptop model has the best price-to-performance ratio?")
issue.answer_type = "Integer" # For benchmark scores or prices
issue_cid = issue.save()
# Create options with benchmark scores
option1 = HivemindOption()
option1.set_issue(issue_cid)
option1.set(9500) # High-end model benchmark score
option1.text = "Dell XPS 15"
option1_cid = option1.save()
option2 = HivemindOption()
option2.set_issue(issue_cid)
option2.set(8200) # Mid-range model benchmark score
option2.text = "MacBook Pro 14"
option2_cid = option2.save()
option3 = HivemindOption()
option3.set_issue(issue_cid)
option3.set(7000) # Budget model benchmark score
option3.text = "Lenovo ThinkPad T14"
option3_cid = option3.save()
# Create an opinion for the first question (best overall)
opinion1 = HivemindOpinion()
opinion1.hivemind_id = issue_cid
opinion1.question_index = 0
opinion1.ranking.set_fixed([option1_cid, option2_cid, option3_cid])
opinion1_cid = opinion1.save()
# Create an opinion for the second question (price-to-performance)
# Using auto-low to prefer options with better value
opinion2 = HivemindOpinion()
opinion2.hivemind_id = issue_cid
opinion2.question_index = 1
opinion2.ranking.set_auto_low(option3_cid) # Prefer the ThinkPad's value
opinion2_cid = opinion2.save()
The HivemindState
class is the central component of the Hivemind Protocol, managing the entire voting process, including options, opinions, and result calculations.
from hivemind import HivemindState
# Create a new state
state = HivemindState()
# Associate with an issue
state.set_hivemind_issue(issue_cid)
# Add predefined options (for Bool or choices)
predefined_options = state.add_predefined_options()
-
hivemind_id: The IPFS hash of the associated hivemind issue
state.hivemind_id = issue.cid()
-
option_cids: List of option CIDs in the state
# Access the list of options for option_cid in state.option_cids: option = state.get_option(option_cid) print(option.text)
-
opinion_cids: List of dictionaries containing opinions for each question
# Access opinions for a specific question for participant, opinion_data in state.opinion_cids[0].items(): print(f"Participant: {participant}") print(f"Opinion CID: {opinion_data['opinion_cid']}")
-
selected: List of options that have been selected
# Check which options have been selected for selected_option_cid in state.selected: option = state.get_option(selected_option_cid) print(f"Selected: {option.text}")
-
final: Whether the hivemind is finalized
if state.final: print("This hivemind is finalized - no more changes allowed")
# Add an option with signature verification
state.add_option(
timestamp=int(time.time()),
option_hash=option.cid(),
address="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
signature="IHh9BMpylbu1DsJiMu+dmLHzoR3HHC0/skxNW/LaJi1K3agNb7mTRTNEfXRGHcLr8V0mSGv8c3XyGWU7bCEdhEE="
)
# Add an opinion with signature verification
state.add_opinion(
timestamp=int(time.time()),
opinion_hash=opinion.cid(),
address="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
signature="IHh9BMpylbu1DsJiMu+dmLHzoR3HHC0/skxNW/LaJi1K3agNb7mTRTNEfXRGHcLr8V0mSGv8c3XyGWU7bCEdhEE="
)
The HivemindState implements the Condorcet method for calculating results:
# Get cached results for all questions
all_results = state.results()
# Calculate results for a specific question
results = state.calculate_results(question_index=0)
# Get the score of a specific option
score = state.get_score(option_hash=option.cid(), question_index=0)
# Get options sorted by score
sorted_options = state.get_sorted_options(question_index=0)
# Get the consensus (winning option value)
consensus_value = state.consensus(question_index=0)
# Get all options in ranked order
ranked_values = state.ranked_consensus(question_index=0)
The HivemindState supports different selection behaviors based on the issue's on_selection
property:
# Select the consensus (requires author signature for issues with an author)
selected_options = state.select_consensus(
timestamp=int(time.time()),
address=issue.author,
signature="IHh9BMpylbu1DsJiMu+dmLHzoR3HHC0/skxNW/LaJi1K3agNb7mTRTNEfXRGHcLr8V0mSGv8c3XyGWU7bCEdhEE="
)
# Selection modes:
# - None: No special action
# - Finalize: Locks the hivemind (state.final = True)
# - Exclude: Excludes the selected option of the first question from future results
# - Reset: Clears all opinions
# Update a participant's name
state.update_participant_name(
timestamp=int(time.time()),
name="Alice",
address="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
signature="IHh9BMpylbu1DsJiMu+dmLHzoR3HHC0/skxNW/LaJi1K3agNb7mTRTNEfXRGHcLr8V0mSGv8c3XyGWU7bCEdhEE=",
message="1627184000Alice"
)
# Get the weight of a participant's opinion
weight = state.get_weight(opinionator="1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
# Get participant contributions to the consensus
contributions = state.contributions(results=results, question_index=0)
# Save state to IPFS
state_cid = state.save()
# Load state from IPFS
loaded_state = HivemindState(cid=state_cid)
from hivemind import HivemindIssue, HivemindOption, HivemindOpinion, HivemindState
import time
# Create an issue for laptop selection
issue = HivemindIssue()
issue.name = "Team Laptop Selection"
issue.add_question("Which laptop model is best overall?")
issue.add_question("Which laptop model has the best price-to-performance ratio?")
issue.answer_type = "String"
issue_cid = issue.save()
# Create a state for this issue
state = HivemindState()
state.set_hivemind_issue(issue_cid)
# Create options
option1 = HivemindOption()
option1.set_issue(issue_cid)
option1.set("Dell XPS 15")
option1.text = "High-end model with 32GB RAM"
option1_cid = option1.save()
option2 = HivemindOption()
option2.set_issue(issue_cid)
option2.set("MacBook Pro 14")
option2.text = "Mid-range model with 16GB RAM"
option2_cid = option2.save()
option3 = HivemindOption()
option3.set_issue(issue_cid)
option3.set("Lenovo ThinkPad T14")
option3.text = "Budget model with good value"
option3_cid = option3.save()
# Add options to state (in a real scenario, these would include signatures)
timestamp = int(time.time())
state.add_option(timestamp, option1_cid, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "signature")
state.add_option(timestamp, option2_cid, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "signature")
state.add_option(timestamp, option3_cid, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "signature")
# Create and add opinions (in a real scenario, these would include signatures)
opinion1 = HivemindOpinion()
opinion1.hivemind_id = issue_cid
opinion1.question_index = 0 # First question
opinion1.ranking.set_fixed([option1_cid, option2_cid, option3_cid])
opinion1_cid = opinion1.save()
opinion2 = HivemindOpinion()
opinion2.hivemind_id = issue_cid
opinion2.question_index = 1 # Second question
opinion2.ranking.set_fixed([option3_cid, option2_cid, option1_cid]) # Different ranking for price-performance
opinion2_cid = opinion2.save()
# In a real scenario, add_opinion would include address and signature
state.add_opinion(timestamp, opinion1_cid, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "signature")
state.add_opinion(timestamp, opinion2_cid, "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "signature")
# Calculate results
results = state.results()
print(f"Best overall: {state.consensus(question_index=0)}")
print(f"Best price-performance: {state.consensus(question_index=1)}")
# Save the state
state_cid = state.save()
Detailed examples can be found in the examples/
directory:
- Python 3.10 or higher
- ipfs-dict-chain >= 1.1.0
- A running IPFS node (see IPFS Installation Guide)
- The IPFS daemon must be running and accessible via the default API endpoint
- Default endpoint: http://127.0.0.1:5001
Full documentation is available at https://valyriantech.github.io/hivemind-python/
This project is licensed under the MIT License - see the LICENSE file for details.