# The Ballot Box

**Last Updated:** 2026-01-31
**Confidence:** High
**Status:** Implemented

## Summary

The Ballot Box is Episteme's high-velocity vote ingestion system. It separates votes from assertions to enable thousands of agents to vote simultaneously without lock contention.

**Key Facts:**
- Votes are append-only (immutable)
- Content-addressed by BLAKE3 hash
- O(1) vote counts via cached counters
- O(1) aggregate weights for Materializer
- Decoupled from Assertion mutations

**File Pointer:** `crates/stemedb-storage/src/vote_store.rs`

## Storage Layout

| Key Pattern | Value | Purpose |
|-------------|-------|---------|
| `V:{assertion_hash}:{vote_hash}` | Serialized `Vote` | Individual votes |
| `VC:{assertion_hash}` | `u64` (LE bytes) | Vote count cache |
| `VW:{assertion_hash}` | `f32` (LE bytes) | Aggregate weight cache |

## VoteStore Trait

```rust
#[async_trait]
pub trait VoteStore: Send + Sync {
    /// Store a vote and return its content-addressed hash
    async fn put_vote(&self, vote: &Vote) -> Result<Hash>;

    /// Get a specific vote by hash
    async fn get_vote(&self, assertion_hash: &Hash, vote_hash: &Hash) -> Result<Option<Vote>>;

    /// Get all votes for an assertion (O(n))
    async fn get_votes_for_assertion(&self, assertion_hash: &Hash) -> Result<Vec<Vote>>;

    /// Get vote count (O(1) via cache)
    async fn get_vote_count(&self, assertion_hash: &Hash) -> Result<u64>;

    /// Get aggregate weight (O(1) via cache)
    async fn get_aggregate_weight(&self, assertion_hash: &Hash) -> Result<f32>;

    /// Check if any votes exist
    async fn has_votes(&self, assertion_hash: &Hash) -> Result<bool>;
}
```

## Usage Example

```rust
use stemedb_storage::{HybridStore, GenericVoteStore, VoteStore};
use stemedb_core::types::Vote;

// Create vote store backed by HybridStore (fjall + redb)
let kv_store = HybridStore::open("./data")?;
let vote_store = GenericVoteStore::new(kv_store);

// High-velocity vote ingestion
let vote = Vote {
    assertion_hash: [1u8; 32],
    agent_id: [2u8; 32],
    weight: 0.85,
    signature: sig_bytes,
    timestamp: now,
};
let vote_hash = vote_store.put_vote(&vote).await?;

// O(1) aggregation queries (for Materializer)
let count = vote_store.get_vote_count(&assertion_hash).await?;
let total_weight = vote_store.get_aggregate_weight(&assertion_hash).await?;
```

## Design Rationale

### Why Separate Votes from Assertions?

Traditional databases would store votes as a column or join table:

```sql
-- Naive approach: votes as assertion metadata
UPDATE assertions SET vote_count = vote_count + 1 WHERE hash = ?;
```

**Problems:**
1. Lock contention when many agents vote on same assertion
2. Lost history (can't see who voted when)
3. Violates append-only semantics

**Ballot Box Solution:**
1. Votes are separate, immutable records
2. Each vote is content-addressed
3. Caches enable O(1) aggregation
4. Full audit trail preserved

### Cache Update Strategy

When `put_vote()` is called:
1. Serialize vote with rkyv
2. Compute BLAKE3 hash (content address)
3. Store at `V:{assertion_hash}:{vote_hash}`
4. Increment `VC:{assertion_hash}` counter
5. Add weight to `VW:{assertion_hash}` sum

The caches are updated atomically with the vote write, ensuring consistency.

### Duplicate Vote Handling

The VoteStore does NOT prevent duplicate votes - it stores whatever is submitted. Duplicate detection is a higher-level concern (e.g., "one vote per agent per assertion") that should be enforced at the API layer.

This design choice keeps the storage layer simple and lets policy be defined elsewhere.

## Integration with Materializer

The Materializer (Phase 2) will use the VoteStore to update Materialized Views:

```rust
// Materializer pseudocode
for assertion in new_assertions {
    let votes = vote_store.get_votes_for_assertion(&assertion.hash).await?;
    let weighted_score = calculate_consensus(votes, trustrank);

    if should_update_mv(weighted_score, current_mv) {
        store.put(
            format!("MV:{}:{}", assertion.subject, assertion.predicate),
            assertion.serialize()
        ).await?;
    }
}
```

## Related Topics

- [Storage](./storage.md)
- [Assertion](./assertion.md)
- [Architecture](../../../architecture.md)
- [Vision - The Ballot Box](../../../vision.md#4-2-the-ballot-box-high-velocity-consensus)
