Firestore Database — Schema & Admin Tools
Complete reference for Foundation's Firestore database: collection schemas, validation rules, referential integrity, admin tools, and data management.
Collection Overview
| Collection | Documents | Tenant-Scoped | Aggregates | Description |
|---|---|---|---|---|
proposals |
Governance proposals | Yes | total_votes, options.votes, support_count | Core governance records |
voters |
Registered voters | Yes | — | Voter registry with verification status |
votes |
Individual votes | Yes | — | One doc per vote, references proposal + option |
supporter_signatures |
Support signatures | Yes | — | Pre-vote support for proposals |
voting_rounds |
Voting round metadata | Yes | — | Round type and status tracking |
funds |
Community funds (Pillar 2) | Yes | — | UBI fund definitions and balances |
distributions |
Fund distributions | Yes | — | Distribution records, references funds |
product_requests |
Marketplace items (Pillar 3) | Yes | — | Product listings with supplier bids |
savings_summary |
Aggregate savings | No | — | Singleton doc (ID: "current") |
identity_proofs |
ID verification records | Yes | — | PoH proof records, references voters |
Collection Schemas
proposals
{
title: string, // required, min 1 char
description: string,
status: "round0" | "active" | "passed" | "rejected",
scope: "city" | "regional" | "national" | "global", // optional
pillar: "YourVoice" | "YourShare" | "YourMarket",
support_count: number, // integer >= 0
support_threshold: number, // integer >= 0
total_votes: number, // integer >= 0 — AGGREGATE: actual vote count
options: [
{
id: string,
label: string,
votes: number // integer >= 0 — AGGREGATE: per-option vote count
}
],
created_at: string, // ISO 8601 date
updated_at: string, // ISO 8601 date
tenant_id: string
}
Aggregates verified:
total_votesmust equal count ofvotesdocs whereproposal_id == this.idoptions[n].votesmust equal count ofvotesdocs whereproposal_id == this.id && option_id == options[n].idsupport_countmust equal count ofsupporter_signaturesdocs whereproposal_id == this.id
voters
{
wallet_address: string, // required, min 1 char
status: "active" | "suspended" | "pending",
is_verified: boolean,
is_biometric_verified: boolean,
age: number, // integer >= 0, optional
location: string, // optional
geographic_scope: string, // optional
registered_at: string, // ISO 8601 date
tenant_id: string
}
votes
{
proposal_id: string, // required — REFERENCE → proposals
option_id: string, // required — must exist in proposal.options
anonymous_hash: string, // required — for duplicate detection
timestamp: string, // ISO 8601 date
tenant_id: string
}
Integrity checks:
proposal_idmust reference an existing proposaloption_idmust exist in the referenced proposal'soptionsarray- No two votes with the same
anonymous_hashon the same proposal (duplicate detection)
supporter_signatures
{
proposal_id: string, // required — REFERENCE → proposals
anonymous_hash: string, // required — for duplicate detection
timestamp: string,
tenant_id: string
}
Integrity checks:
proposal_idmust reference an existing proposal- No duplicate
anonymous_hashper proposal
voting_rounds
{
proposal_id: string, // REFERENCE → proposals
round_type: "support" | "voting" | "runoff",
status: "active" | "completed" | "cancelled",
started_at: string,
ended_at: string, // optional
tenant_id: string
}
funds
{
name: string,
description: string,
status: "active" | "paused" | "completed" | "draft",
categories: [
{ name: string, allocation: number }
],
total_balance: number, // >= 0
distributed_amount: number, // >= 0
participant_count: number, // integer >= 0
location: string,
tenant_id: string
}
distributions
{
fundId: string, // REFERENCE → funds
status: "pending" | "processing" | "completed" | "failed",
amount: number,
recipient_count: number,
distributed_at: string,
tenant_id: string
}
product_requests
{
title: string,
description: string,
status: "open" | "bidding" | "fulfilled" | "cancelled",
proposalId: string, // optional — REFERENCE → proposals
bids: [
{
vendorId: string,
price: number,
description: string,
submitted_at: string
}
],
tenant_id: string
}
savings_summary
Singleton document with ID "current":
{
total_savings: number,
total_participants: number,
average_savings: number,
last_updated: string
}
identity_proofs
{
voter_id: string, // REFERENCE → voters
proofType: "self_declared" | "social_vouch" | "biometric" | "government_id" | "manual_review",
trustTier: "basic" | "standard" | "verified" | "certified",
status: "pending" | "approved" | "rejected",
submitted_at: string,
reviewed_at: string, // optional
tenant_id: string
}
Integrity checks:
voter_idmust reference an existing votertrustTiermust be consistent withproofType(e.g.,biometric→verifiedorcertified)
Database Validator
The Database Validator (Admin Panel > Testing tab) runs a 3-phase validation:
Phase 1 — Schema Validation
Checks every document in all 10 collections:
- Required fields present
- Field types match (string, number, boolean, array, map)
- Enum values valid (status, scope, round_type, proofType, trustTier)
- Numeric fields non-negative where required
- Array structures correct (options, categories, bids)
- ISO date format validation
Phase 2 — Referential Integrity
Verifies all foreign-key references:
votes.proposal_id→ existing proposal with validoption_idsupporter_signatures.proposal_id→ existing proposalvoting_rounds.proposal_id→ existing proposaldistributions.fundId→ existing fundidentity_proofs.voter_id→ existing voterproduct_requests.proposalId→ existing proposal (when set)
Phase 3 — Aggregate Consistency
Cross-validates computed fields:
proposal.total_votesvs actual vote countproposal.options[x].votesvs per-option vote countproposal.support_countvs actual supporter signature count- Duplicate vote detection (same
anonymous_hashon same proposal) - Duplicate support detection (same
anonymous_hashon same proposal)
Results Display
- Expandable per-collection sections
- Color-coded severity: red (errors), amber (warnings), green (passes)
- Document count per collection
- Total error/warning counts
Auto-Fix (via @plantagoai/db)
Available fix operations:
| Fix | Description |
|---|---|
| missing-defaults | Add missing fields with schema default values |
| enum-normalize | Fix enum values (trim whitespace, normalize case) |
| orphan-cleanup | Delete documents whose foreign-key targets don't exist |
| aggregate-recount | Recompute aggregate counts from actual document counts |
| timestamp-repair | Fix malformed ISO date strings |
Always run with dryRun: true first to preview changes.
Data Seeding Tools
Seed Demo Data
Populates standard demo dataset:
- 7 community funds (Stockton SEED, Jackson, Denver, Austin, Chicago, Newark, LA)
- 3 distribution history records
- ~50 marketplace products with supplier bids and price comparisons
- Savings summary singleton
- Demo governance proposals
Idempotent — detects existing data and skips.
Population Seeder
Creates demo voters at a specific location:
- Google Places Autocomplete for location picking
- Configurable count: 100 to 100,000
- Auto-inferred geographic scope
- Realistic data: random wallets, ~85% verification rate, ages 18-80, 6-month registration spread
- Batch-processes 50 at a time with progress bar
Vote Seeder
Generates votes on active proposals:
- Filter by pillar (YourVoice / YourShare / YourMarket)
- Select proposal
- Configure count: 100 to 100,000
- Distribution modes:
- Random — even spread
- Landslide Approve — ~80% on first option
- Landslide Reject — ~80% on last option
- Head to Head — top 2 nearly tied (~48% each)
- Creates voter records for audit trail
- Atomic tally updates via
increment()
Voting Simulation
Quick mode: 20 random votes distributed across all active proposals.
Reset Tools
All resets require confirmation dialog.
| Tool | Scope | Behavior |
|---|---|---|
| Reset Vote Counts | Proposals only | Zeros tallies, preserves structure, does NOT delete vote docs |
| Reset Voters | Voters collection | Batch-deletes all voter docs (200 per batch) |
| Reset All & Re-seed | Everything | Phase 1 (0-70%): Clears 8 collections. Phase 2 (70-100%): Re-seeds fresh |
Export
Via @plantagoai/db's exportDb():
- JSON format with optional metadata (
_exportedAt,_collectionName) - Tenant-scoped export option
- Configurable document limit per collection
Security Rules Generation
@plantagoai/db's generateRules() produces Firestore security rules from schema definitions:
- Type validation per field
- Tenant isolation (
tenant_idscoping) - Ring-based access control
- Required field enforcement