Files
govoplan-voting/docs/VOTING_DOMAIN.md
T

113 lines
5.6 KiB
Markdown

# 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