Skip to content

✨ Private S3 ballot publications and scoped cast validation (main) - #3164

Open
edulix wants to merge 8 commits into
mainfrom
feat/meta-12767/main-backend
Open

✨ Private S3 ballot publications and scoped cast validation (main)#3164
edulix wants to merge 8 commits into
mainfrom
feat/meta-12767/main-backend

Conversation

@edulix

@edulix edulix commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

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

  1. 📚 Voting load guides and synchronized code examples (main) #3163 — documentation and code tabs
  2. ✨ Private S3 ballot publications and scoped cast validation (main) #3164 — publications and cast backend
  3. ✨ Load voting content from S3 with minimal voter status (main) #3165 — voting portal and browser adapters
  4. ✨ Native Rust voting load preparation, workers and reports (main) #3149 — native Rust load tooling

Documentation

Copilot AI lite review requested due to automatic review settings September 8, 2026 09:22
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit 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.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 509f6f19-69e6-43dd-ac9d-81ce1aa5fb89

📥 Commits

Reviewing files that changed from the base of the PR and between fe395d4 and b14b8db.

📒 Files selected for processing (65)
  • docs/permissions.md
  • hasura/metadata/actions.graphql
  • hasura/metadata/actions.yaml
  • hasura/metadata/databases/backend-db/tables/sequent_backend_ballot_style.yaml
  • hasura/metadata/databases/backend-db/tables/sequent_backend_cast_vote.yaml
  • hasura/metadata/databases/backend-db/tables/sequent_backend_election.yaml
  • hasura/metadata/databases/backend-db/tables/sequent_backend_election_event.yaml
  • hasura/migrations/backend-db/1788765000000_serialize_cast_vote_area_checks/down.sql
  • hasura/migrations/backend-db/1788765000000_serialize_cast_vote_area_checks/up.sql
  • hasura/migrations/backend-db/1788765000001_cast_vote_external_storage/down.sql
  • hasura/migrations/backend-db/1788765000001_cast_vote_external_storage/up.sql
  • hasura/migrations/backend-db/1788765000002_materialize_voting_windows/down.sql
  • hasura/migrations/backend-db/1788765000002_materialize_voting_windows/up.sql
  • hasura/migrations/backend-db/1788808561206_ballot_style_voter_reference_index/down.sql
  • hasura/migrations/backend-db/1788808561206_ballot_style_voter_reference_index/up.sql
  • packages/harvest/src/main.rs
  • packages/harvest/src/routes/ballot_files.rs
  • packages/harvest/src/routes/delete_tenant.rs
  • packages/harvest/src/routes/insert_cast_vote.rs
  • packages/harvest/src/routes/mod.rs
  • packages/keycloak-extensions/action-token-login-bridge/src/main/java/sequent/keycloak/realm/RealmNames.java
  • packages/keycloak-extensions/action-token-login-bridge/src/test/java/sequent/keycloak/realm/RealmNamesTest.java
  • packages/sequent-core/src/services/authorization.rs
  • packages/sequent-core/src/types/permissions.rs
  • packages/sequent-core/src/util/external_config.rs
  • packages/sequent-core/src/wasm/wasm_permissions.rs
  • packages/windmill/Cargo.toml
  • packages/windmill/examples/prepare_ballot_files.rs
  • packages/windmill/external-bin/janitor/templates/COMELEC/keycloak.hbs
  • packages/windmill/external-bin/janitor/templates/COMELEC/keycloakAdmin.hbs
  • packages/windmill/src/postgres/ballot_publication.rs
  • packages/windmill/src/postgres/cast_vote.rs
  • packages/windmill/src/postgres/election.rs
  • packages/windmill/src/postgres/election_event.rs
  • packages/windmill/src/postgres/sql/cast_vote_configuration.sql
  • packages/windmill/src/postgres/sql/lock_publication_event.sql
  • packages/windmill/src/postgres/tenant.rs
  • packages/windmill/src/services/ballot_styles/ballot_publication.rs
  • packages/windmill/src/services/ballot_styles/ballot_style.rs
  • packages/windmill/src/services/ballot_styles/mod.rs
  • packages/windmill/src/services/ballot_styles/publication_files.rs
  • packages/windmill/src/services/celery_app.rs
  • packages/windmill/src/services/delete_tenant.rs
  • packages/windmill/src/services/electoral_log.rs
  • packages/windmill/src/services/insert_cast_vote.rs
  • packages/windmill/src/services/insert_cast_vote_database_tests.rs
  • packages/windmill/src/services/insert_cast_vote_tests.rs
  • packages/windmill/src/services/mod.rs
  • packages/windmill/src/tasks/delete_tenant.rs
  • packages/windmill/src/tasks/mod.rs
  • packages/windmill/src/types/tasks.rs
  • scripts/postgres/ballot_style_voter_reference_index.sql
  • scripts/postgres/cast_vote_covering_index.sql
  • scripts/postgres/drop_ballot_style_voter_reference_index.sql
  • scripts/test_ballot_files.py
  • scripts/test_ballot_publication_lifecycle.py
  • scripts/test_cast_vote_scalability.py
  • scripts/test_voter_context_authorization.py
  • scripts/voting_flow/benchmark.py
  • scripts/voting_flow/database.py
  • scripts/voting_flow/fixtures.py
  • scripts/voting_flow/regression.py
  • scripts/voting_flow/rust_tests.py
  • scripts/voting_flow/schedules.py
  • scripts/voting_flow/schema.sql

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 narrowed cast_vote_configuration query 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.

Comment on lines +4 to +6
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE SCHEMA sequent_backend;

Comment on lines +77 to +81
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')
))
Comment thread packages/harvest/src/routes/delete_tenant.rs Outdated
Comment on lines +89 to +91
error_msg: Some(format!(
"Error sending Delete Tenant task: ${error}"
)),
Comment on lines +15 to +18
s3::delete_files_from_s3(bucket, documents_prefix.clone(), s3::S3Endpoint::Server)
.await
.map_err(|err| anyhow!("Error delete private files from s3: {err:?}"))?;

Copilot AI review requested due to automatic review settings September 8, 2026 09:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 enables pg_stat_statements. Without pgcrypto, initdb clusters will fail to execute this schema with function gen_random_uuid() does not exist.
  • Files reviewed: 65/65 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +1050 to +1055
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());
Copilot AI review requested due to automatic review settings September 8, 2026 10:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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, but pgcrypto isn’t enabled. On a fresh disposable cluster this will fail when executing schema.sql (missing function). Add CREATE EXTENSION IF NOT EXISTS pgcrypto; before creating tables that use gen_random_uuid().
  • Files reviewed: 65/65 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +62 to +66
info!(
phase = "authorization",
duration_us = start.elapsed().as_micros() as u64,
"{ROUTE_PHASE_COMPLETED}"
);
Copilot AI review requested due to automatic review settings September 8, 2026 10:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 enables pgcrypto, 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

Comment on lines 535 to 546
@@ -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(),
Findeton pushed a commit that referenced this pull request Sep 8, 2026
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>
Base automatically changed from feat/meta-12767/main-docs to main September 8, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants