Files
govoplan-voting/docs/VOTING_DOMAIN.md
T

5.6 KiB

Voting domain, assurance, and operations

Ownership boundary

Voting owns the protocol and authoritative state of a governed ballot:

  • immutable ballot definitions after opening
  • frozen electorate entries, weights, and provenance
  • eligibility enforcement, vote replacement, and receipts
  • deterministic tally, quorum, threshold, closure, and certification
  • challenge, annulment, command replay, and lifecycle evidence
  • provider-neutral handoff for confidential, secret, or externally certified voting

Committee owns the body, meeting, agenda, deliberation, and minute context. It stores a Voting ballot reference and a verified aggregate result for meeting records. Poll owns informal preference, response, and availability collection. Decisions owns the resulting formal institutional outcome. Encryption and Identity Trust own cryptographic key and device primitives rather than the voting ceremony.

Assurance profiles

The native recorded profile stores each current vote with its elector, selection, weight, generation, and receipt hash. Superseded votes remain available for authorized reconstruction. It is auditable but not secret.

The confidential, secret, and external_certified profiles require a provider exposed as voting.provider.<provider-id>. Voting refuses to open the ballot when that provider is unavailable. The provider keeps raw ballots and credentials inside its own assurance boundary and returns aggregate counts, weighted counts, a result hash, and evidence. GovOPlaN does not claim that a provider or deployment satisfies legal or certification requirements merely because the adapter contract is implemented. Core bounds provider evidence to JSON, 64 items and 64 KiB and rejects fields that can carry credentials, private key material, plaintext, or raw selections before Voting or Committee can persist the projection.

Bundled local confidential provider

local_confidential is an interactive reference provider for the confidential profile. Opening a ballot creates a ballot-scoped Encryption vault using the configured local_aesgcm provider. Casting stores the elector, weight, receipt, and replacement generation as provider metadata while the selection payload is stored only as authenticated ciphertext. Finalization opens active cast envelopes inside the provider boundary and exports only the aggregate and sanitized evidence.

Encryption records a context-bound keyed commitment rather than an enumerable plaintext digest, so low-entropy choices are not exposed to an offline database reader through idempotency metadata.

This profile is server-readable. It does not provide voter anonymity, unlinkability, coercion resistance, client-held keys, an HSM/KMS claim, or external certification. It therefore cannot be selected for secret or external_certified ballots. A provider ballot reference is generated when the ballot opens; externally hosted providers may continue to require a pre-existing reference.

Lifecycle and concurrency

Ballots move through draft -> open -> closed -> certified. A closed or certified result can be challenged. An administrator can annul an invalid ballot from any non-annulled lifecycle state. Opening freezes SHA-256 hashes of the definition and electorate. Each state change creates a new immutable revision and lifecycle event. Mutating commands require an expected revision and an idempotency key; a key replay returns the first outcome and a key reused with another payload is rejected.

Recorded voters can replace a vote only when the frozen definition allows it. The previous generation is retained and excluded from the active tally. Lifecycle events contain the receipt hash and generation, not the selection.

Operations and recovery

Back up these tables as one consistency unit:

  • voting_ballot_revisions
  • voting_cast_records
  • voting_lifecycle_events
  • voting_command_replays
  • voting_confidential_ballots
  • voting_confidential_casts

A restore is valid only when current revisions are unique per tenant and ballot, cast generations are unique per elector, every active cast references the current frozen definition hash, result hashes can be recomputed, and event sequences are contiguous. Provider-backed restores also require the external provider evidence and ballot reference to remain resolvable. Do not reopen a restored external ballot merely to compensate for missing provider state. For local_confidential, restore the Voting and Encryption tables from the same consistency point and restore the exact deployment MASTER_KEY_B64 from the approved secret store. A database-only restore or a changed/lost master key must fail closed; it cannot reconstruct confidential casts.

Before destructive retirement, stop new casts, retain a verified database snapshot, export result/certification evidence under the applicable retention policy, and reconcile any Committee or Decision references. Horizontal nodes must use the shared database; no authoritative Voting state is stored on a node-local filesystem.

Security checks

  • tenant predicates apply to every ballot, cast, event, and replay lookup
  • the authenticated account must match the frozen elector subject
  • native casting fails closed for non-recorded assurance profiles
  • interactive provider casting validates the frozen tenant, electorate, definition, options, replacement policy, and time window again
  • raw selections are never returned by list, detail, result, or history APIs
  • provider result keys must exactly match frozen options
  • external results require evidence and cannot exceed the frozen electorate
  • certification and annulment use separate permissions