✨ Private S3 ballot publications and scoped cast validation (main) - #3164
✨ Private S3 ballot publications and scoped cast validation (main)#3164edulix wants to merge 8 commits into
Conversation
|
Warning Review limit reachedNext included review available in 25 minutes. View limit detailsLimit details: You’ve used the included review currently available. This review ran on the open-source allowance, not this organization's plan, because the pull request author doesn't have an assigned seat. Waiting won't change this — ask an organization admin to assign them a seat, or add seats in Billing if every seat is already assigned, then retry. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (65)
Comment |
There was a problem hiding this comment.
🟡 Changes recommended
The disposable SQL fixture and voting-window migration contain correctness issues that can break local validation (missing pgcrypto for gen_random_uuid) and can allow malformed schedule configs through (NULL invalid_dates logic).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Implements private S3-backed ballot publications (served via authorized URLs), reduces cast-vote DB work by moving schedule scanning into a transactionally maintained projection, and adds tenant-deletion plumbing plus local regression/benchmark harnesses to validate the new SQL contracts.
Changes:
- Publish immutable ballot-style/election/event JSON artifacts to private S3 and return short-lived presigned URLs to authorized voters.
- Materialize voting windows in
election_voting_window(maintained via triggers) and use a narrowedcast_vote_configurationquery during cast validation. - Add tenant deletion task/route/permission, plus SQL and authorization regressions/benchmarks.
File summaries
| File | Description |
|---|---|
| scripts/voting_flow/schema.sql | Disposable DB schema fixture for local regressions/benchmarks. |
| scripts/voting_flow/schedules.py | Measures schedule query/update latency with/without indexes. |
| scripts/voting_flow/rust_tests.py | Runs focused Rust DB-helper tests against disposable DB. |
| scripts/voting_flow/regression.py | Python DB invariants/regressions for migration + triggers. |
| scripts/voting_flow/fixtures.py | Deterministic SQL fixtures for elections/schedules/votes. |
| scripts/voting_flow/database.py | Spins up isolated Postgres cluster and applies migrations. |
| scripts/voting_flow/benchmark.py | Benchmarks “before vs after” cast-vote SQL path. |
| scripts/test_voter_context_authorization.py | Local Hasura voter-context scope/permission checks. |
| scripts/test_cast_vote_scalability.py | Runner for voting-flow regressions + optional benchmark. |
| scripts/test_ballot_publication_lifecycle.py | Validates publication event lock semantics. |
| scripts/test_ballot_files.py | Runs Rust S3 publication-file regression + index migration check. |
| scripts/postgres/cast_vote_covering_index.sql | Concurrent covering-index migration script for cast_vote. |
| packages/windmill/src/types/tasks.rs | Adds DELETE_TENANT task execution type. |
| packages/windmill/src/tasks/mod.rs | Registers new delete_tenant task module. |
| packages/windmill/src/tasks/delete_tenant.rs | Celery task to delete tenant after event-count guard. |
| packages/windmill/src/services/mod.rs | Exposes delete-tenant service module. |
| packages/windmill/src/services/insert_cast_vote.rs | Uses scoped configuration read + DB-enforced eligibility; audit prep reuse. |
| packages/windmill/src/services/insert_cast_vote_tests.rs | Extracted/expanded unit tests for cast-vote status/policy parsing. |
| packages/windmill/src/services/insert_cast_vote_database_tests.rs | Real-DB integration tests for projection + trigger error mapping. |
| packages/windmill/src/services/electoral_log.rs | Adds constructors that reuse already-loaded signing key. |
| packages/windmill/src/services/delete_tenant.rs | Deletes tenant-scoped S3 documents + Keycloak realm. |
| packages/windmill/src/services/celery_app.rs | Wires delete-tenant task into Celery app and queues. |
| packages/windmill/src/services/ballot_styles/publication_files.rs | Uploads/verifies immutable publication objects to private S3; presigned URL assembly. |
| packages/windmill/src/services/ballot_styles/mod.rs | Exposes publication_files module. |
| packages/windmill/src/services/ballot_styles/ballot_style.rs | Serializes ballot-style generation under event lock; prepares S3 objects. |
| packages/windmill/src/services/ballot_styles/ballot_publication.rs | Prepares S3 objects during publication; adds deletion guard. |
| packages/windmill/src/postgres/tenant.rs | Adds tenant deletion that cleans up tenant-only rows + tenant record. |
| packages/windmill/src/postgres/sql/lock_publication_event.sql | SQL row-lock used to serialize publication work per event. |
| packages/windmill/src/postgres/sql/cast_vote_configuration.sql | Narrow query for cast-vote policy + materialized window dates. |
| packages/windmill/src/postgres/election.rs | Adds get_cast_vote_configuration helper returning policy + dates. |
| packages/windmill/src/postgres/election_event.rs | Adds count_tenant_election_events guard query. |
| packages/windmill/src/postgres/cast_vote.rs | Avoids rereading large content from TOAST; preserves API response. |
| packages/windmill/src/postgres/ballot_publication.rs | Adds lock_publication_event helper using FOR UPDATE. |
| packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs | Adds tenant-delete role to Keycloak template. |
| packages/windmill/examples/prepare_ballot_files.rs | Example CLI to prepare publication S3 objects under lock. |
| packages/windmill/Cargo.toml | Enables serde_json/raw_value for precise EML slicing. |
| packages/sequent-core/src/wasm/wasm_permissions.rs | Adds TENANT_DELETE permission to WASM enum. |
| packages/sequent-core/src/util/external_config.rs | Adds username_start_number to voter generator config. |
| packages/sequent-core/src/types/permissions.rs | Adds TENANT_DELETE to Rust permissions enum. |
| packages/keycloak-extensions/action-token-login-bridge/src/test/java/sequent/keycloak/realm/RealmNamesTest.java | Tests tenant-realm extraction rules. |
| packages/keycloak-extensions/action-token-login-bridge/src/main/java/sequent/keycloak/realm/RealmNames.java | Adds tenantIdFromRealmName for administrative tenant realms. |
| packages/harvest/src/routes/mod.rs | Registers new delete_tenant + ballot_files routes. |
| packages/harvest/src/routes/insert_cast_vote.rs | Passes preferred_username to windmill cast-vote service; adds phase timing logs. |
| packages/harvest/src/routes/delete_tenant.rs | Adds delete-tenant HTTP endpoint wiring Celery task + authorization. |
| packages/harvest/src/routes/ballot_files.rs | Adds voter-scoped endpoint to fetch presigned ballot file URLs. |
| packages/harvest/src/main.rs | Mounts delete-tenant and ballot-files routes. |
| hasura/migrations/backend-db/1788808561206_ballot_style_voter_reference_index/up.sql | Adds covering index for voter ballot-style lookup. |
| hasura/migrations/backend-db/1788808561206_ballot_style_voter_reference_index/down.sql | Drops the voter ballot-style index. |
| hasura/migrations/backend-db/1788765000002_materialize_voting_windows/up.sql | Adds election_voting_window projection + triggers + backfill + schedule index. |
| hasura/migrations/backend-db/1788765000002_materialize_voting_windows/down.sql | Removes projection, triggers, and index. |
| hasura/migrations/backend-db/1788765000001_cast_vote_external_storage/up.sql | Sets cast_vote.content storage to EXTERNAL for new rows. |
| hasura/migrations/backend-db/1788765000001_cast_vote_external_storage/down.sql | Restores cast_vote.content storage to EXTENDED. |
| hasura/migrations/backend-db/1788765000000_serialize_cast_vote_area_checks/up.sql | Adds per-voter advisory lock + cross-area exclusivity in trigger. |
| hasura/migrations/backend-db/1788765000000_serialize_cast_vote_area_checks/down.sql | Restores previous trigger behavior. |
| hasura/metadata/databases/backend-db/tables/sequent_backend_election.yaml | Tightens user select permissions to scoped election/event/IDs. |
| hasura/metadata/databases/backend-db/tables/sequent_backend_election_event.yaml | Tightens user select permissions to scoped tenant+event. |
| hasura/metadata/databases/backend-db/tables/sequent_backend_cast_vote.yaml | Tightens user select permissions to scoped event. |
| hasura/metadata/databases/backend-db/tables/sequent_backend_ballot_style.yaml | Adds relationship to election + tightens user select permissions. |
| hasura/metadata/actions.yaml | Adds ballot-files query action and delete-tenant mutation action. |
| hasura/metadata/actions.graphql | Adds action GraphQL schema for ballot-files and delete-tenant. |
| docs/permissions.md | Documents new tenant-delete permission. |
Review details
Suppressed comments (1)
packages/windmill/src/services/delete_tenant.rs:22
- Grammar/casing in the error message is off; prefer "Error deleting … from S3" for clarity and consistency.
- Files reviewed: 61/61 changed files
- Comments generated: 5
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| CREATE EXTENSION IF NOT EXISTS pg_stat_statements; | ||
| CREATE SCHEMA sequent_backend; | ||
|
|
| bool_or(cron_config IS NOT NULL AND ( | ||
| jsonb_typeof(cron_config) <> 'object' | ||
| OR jsonb_typeof(cron_config -> 'cron') NOT IN ('string', 'null') | ||
| OR jsonb_typeof(cron_config -> 'scheduled_date') NOT IN ('string', 'null') | ||
| )) |
| error_msg: Some(format!( | ||
| "Error sending Delete Tenant task: ${error}" | ||
| )), |
| s3::delete_files_from_s3(bucket, documents_prefix.clone(), s3::S3Endpoint::Server) | ||
| .await | ||
| .map_err(|err| anyhow!("Error delete private files from s3: {err:?}"))?; | ||
|
|
There was a problem hiding this comment.
🟡 Changes recommended
Two verified issues can break the new regression harness and cast-vote error classification (missing pgcrypto in the fixture schema and brittle error downcasting that won’t match context-wrapped DB errors).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
scripts/voting_flow/schema.sql:5
gen_random_uuid()is used as a default in this schema, but the fixture cluster only enablespg_stat_statements. Withoutpgcrypto,initdbclusters will fail to execute this schema withfunction gen_random_uuid() does not exist.
- Files reviewed: 65/65 changed files
- Comments generated: 1
- Review effort level: Lite
| fn map_insert_error(error: anyhow::Error) -> CastVoteError { | ||
| let message = error | ||
| .downcast_ref::<tokio_postgres::Error>() | ||
| .and_then(|error| error.as_db_error()) | ||
| .filter(|error| error.code() == &tokio_postgres::error::SqlState::RAISE_EXCEPTION) | ||
| .map(|error| error.message()); |
There was a problem hiding this comment.
🟡 Changes recommended
The disposable voting-flow schema is missing the required pgcrypto extension for gen_random_uuid(), and Harvest’s cast-vote route phase logging currently emits a literal {ROUTE_PHASE_COMPLETED} instead of the intended message.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
packages/harvest/src/routes/insert_cast_vote.rs:333
- This log message won’t interpolate
ROUTE_PHASE_COMPLETED;"{ROUTE_PHASE_COMPLETED}"is emitted literally. Use a format placeholder (or inline the string) so the constant’s value is logged.
info!(
phase = "enqueue",
duration_us = enqueue_start.elapsed().as_micros() as u64,
"{ROUTE_PHASE_COMPLETED}"
);
scripts/voting_flow/schema.sql:5
gen_random_uuid()is used as a DEFAULT in this fixture schema, butpgcryptoisn’t enabled. On a fresh disposable cluster this will fail when executing schema.sql (missing function). AddCREATE EXTENSION IF NOT EXISTS pgcrypto;before creating tables that usegen_random_uuid().
- Files reviewed: 65/65 changed files
- Comments generated: 1
- Review effort level: Lite
| info!( | ||
| phase = "authorization", | ||
| duration_us = start.elapsed().as_micros() as u64, | ||
| "{ROUTE_PHASE_COMPLETED}" | ||
| ); |
There was a problem hiding this comment.
🟡 Changes recommended
The disposable SQL schema is missing a required extension for gen_random_uuid(), and cast-vote error auditing appears to use the system audit identity instead of the voter identity, which likely breaks audit semantics.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
scripts/voting_flow/schema.sql:6
gen_random_uuid()is used as a DEFAULT (e.g.,scheduled_event.id,cast_vote.id), but the fixture schema never enablespgcrypto, so initializing the disposable cluster will fail with “function gen_random_uuid() does not exist” on a fresh Postgres instance.
- Files reviewed: 65/65 changed files
- Comments generated: 1
- Review effort level: Lite
| @@ -577,7 +542,7 @@ pub async fn try_insert_cast_vote( | |||
| ip, | |||
| country, | |||
| voter_id.to_string(), | |||
| username, | |||
| username.clone(), | |||
| area_id.to_string().clone(), | |||
Parent issue: sequentech/meta#12767 One complete k6/Chromium guide covers setup, workload sizing, local/Docker/Kubernetes workers, reports and troubleshooting. Includes synchronized engine examples, CLI reference, a real report screenshot and a separate telephone guide. Validation: Docusaurus build, Markdown-transform tests and Chromium checks for synchronized tabs, persistence, keyboard navigation, mobile layout and report images. ### Stack 1. #3163 — documentation and code tabs 2. #3164 — publications and cast backend 3. #3165 — voting portal and browser adapters 4. #3149 — native Rust load tooling ### Documentation - **Voting load-testing guide** — [Docusaurus](https://docs.sequentech.io/docusaurus/pr-preview/pr-3163/docs/developers/voting-portal/voter-status-performance) · [GitHub](https://github.com/sequentech/step/blob/feat/meta-12767/main/docs/docusaurus/docs/07-developers/05-voting-portal/voter-status-performance.md) - **CLI setup** — [Docusaurus](https://docs.sequentech.io/docusaurus/pr-preview/pr-3163/docs/developers/cli/cli) · [GitHub](https://github.com/sequentech/step/blob/feat/meta-12767/main/docs/docusaurus/docs/07-developers/02-cli/01-cli_cli.md) - **Load CLI reference** — [Docusaurus](https://docs.sequentech.io/docusaurus/pr-preview/pr-3163/docs/developers/cli/voting-load-reference) · [GitHub](https://github.com/sequentech/step/blob/feat/meta-12767/main/docs/docusaurus/docs/07-developers/02-cli/voting-load-reference.md) - **Telephone load-testing guide** — [Docusaurus](https://docs.sequentech.io/docusaurus/pr-preview/pr-3163/docs/developers/ivr/telephone-load-testing-guide) · [GitHub](https://github.com/sequentech/step/blob/feat/meta-12767/main/docs/docusaurus/docs/07-developers/12-ivr/telephone-load-testing-guide.md) --------- Co-authored-by: Eduardo Robles <edulix@users.noreply.github.com>
Parent issue: https://github.com/sequentech/meta/issues/12767
Publish immutable ballot content to private S3 and return authorized publication URLs through minimal voter status. Cast validation uses scoped policy reads, serialized eligibility and materialized voting windows. Includes tenant cleanup and SQL regression coverage.
Stack
Documentation