Skip to content

Align uniWAHL production inputs and source controls with offline evaluation - #2335

Draft
edulix wants to merge 2891 commits into
mainfrom
feat/meta-10548/main
Draft

edulix wants to merge 2891 commits into
mainfrom
feat/meta-10548/main

Conversation

@edulix

@edulix edulix commented Jan 22, 2026 •

Copy link
Copy Markdown
Contributor

Aligns the uniWAHL TOE source and production definitions with the offline build and CM procedures of the companion ALC PR sequentech/beyond#615, and adds the public vulnerability disclosure policy.

  • Software base: ovcs is merged in (5a6164f2e2fe) and is the base of the evaluated configuration; on top of it this branch keeps what PP compliance needs. The parts keep the names of the Security Target: braid is the trustee binary, which builds the protocol from the packages/wbraid crates and whose verify command is the election verifier, and b4 is the b4v6 binary of the b4 crate (packages/wbraid/crates/b4), built in the packages/wbraid workspace from its own Cargo.lock. vsc draws its randomness from SysRng (packages/wbraid/PROVENANCE.md), and scripts/check-unsafe.py checks both workspaces. The PR targets main, so its diff includes ovcs; against ovcs it changes 362 files.
  • Production inputs: the seven TOE production definitions pin base images and take the prepared OS, Cargo, Yarn, Maven and Wasm inputs as build contexts, in place of the VENDOR_IMAGE of ovcs, and then build offline; keycloak leaves out the React login themes and the scanovate-authenticator module, which the parent POM selects only where its directory exists (e9f7bc566d); temenos does not contain it. First-party WebAssembly builds from source. Miru integration is excluded, export files are encrypted in process (PBKDF2) rather than with the openssl command, and AWS/Vault CLI use is gated. Five Rust Dockerfiles accept the CM helper's per-invocation Cargo cache. wasm-bindgen-cli is 0.2.128, the version of the crate.
  • Credentials: the copied development credentials in the tree are left empty or read from the environment: the airgap .env and Compose file, packages/windmill/.env.example, the step-cli election-event fixtures and the COMELEC janitor templates. The token examples of the documentation and the JWT tests are synthetic. .devcontainer is that of ovcs, with its development certificates and keys; it is not in the TOE. The test keys that ovcs adds (jwt_verification/fixtures/test-key.pem, student-council-issuer.key, packages/trustee/dev/trustee{1,2}.toml) remain until the P08 re-scan decides them. No deployed credential is changed.
  • Source corrections: Keycloak token responses and credential failures stay out of error messages, failed Keycloak operations are reported, ballot decoding is bounded, malformed vault master secrets are rejected without key material, and temporary files are written completely. Strict lint cleanup uses the item-specific justifications below.
  • Consumer guidance: the four AGD handbooks export as searchable PDFs with an index and a source/hash manifest, verified offline. The Deployment Guide opens with "Verifying the Delivery", the commands of the consumer's delivery checks.
  • Vulnerability disclosure policy 1.1 and security.txt; publication needs separate authorization.
  • Agents: the bsi-certification skill and its entry in the AGENTS.md of ovcs (formerly chore: AGENTS.md as the shared coding-agent instructions (main) #3879 and chore: agent skill for the BSI certification #3882).

The CI is that of ovcs.

Documentation

Validation

On the merged state (77995aa6fc):

  • No unmerged paths or conflict markers. cargo check passes in the packages and packages/wbraid workspaces, the latter without warnings. scripts/check-unsafe.py packages packages/wbraid reports 52 unsafe uses and no problems. reuse lint reports only packages/step-cli/src/load/reference.rs, which is the same in main and ovcs.
  • cargo check --workspace --all-targets reports 704 compiler warnings in the packages workspace, all in code that ovcs already has. The Clippy step of the temenos CI denies warnings, so it fails until they are cleaned up in ovcs (ALC internal to-do §8.6, item 2).
  • A scan with the private-key patterns finds keys only in the test files above and in .devcontainer. Gitleaks has not been run on the merged state (§8.6, item 9).
  • Not yet done on the new base: the seven image builds and their inventories (§8.6, item 1), x86 evaluated-target validation, existing vulnerability findings and the accepted release.

With scanovate-authenticator left out (e9f7bc566d): the temenos snapshot 44665ef55d34, assembled from step e9f7bc566dc6 and beyond 3c0081d3f500, passes the bootstrap check (8,128 entries compared), and ALC-02 Annex A is regenerated from it in sequentech/beyond#615. No Maven build has run on this state yet; the keycloak image build of §8.6, item 1, is its first.

Before the merge, and kept as historical: strict native Clippy and format checks across all 17 workspace members; 13 PDF exporter tests; the bootstrap matched 3,559 Step entries and 2,852 package entries, and Gitleaks 8.30.1 reported zero findings; all seven component images and their source/runtime inventories were built natively on ARM at bootstrap 32cd2529b14e867edf19743446cdf8ffd7090c11 (Step ffb9a5e1cf), sealed closeout alc-evidence/2026-09-07-native-arm-closeout.

Item-specific lint expectations and SDLC §3.2 justifications

The following #[expect] attributes address only the identified style diagnostic while preserving the existing interface. No blanket suppression or production-gate change is introduced. The merge keeps 51 of them; the files it takes from ovcs carry the lint attributes of ovcs.

Item Diagnostic Recorded justification
types/hasura/extra.rs::TasksExecutionStatus non_camel_case_types Preserve the public status variants and their default serialized names
services/s3.rs::upload_multipart_data_to_s3 clippy::too_many_arguments Preserve the multipart upload API with explicit object location, access and content metadata
sqlite/results_area_contest.rs::update_results_area_contest_documents_sqlite clippy::too_many_arguments Keep each tenant, event, election, contest and area identity explicit in the existing SQL update API
sqlite/results_election_area.rs::create_results_election_area_sqlite clippy::too_many_arguments Keep the existing results-row API with explicit scope identities, documents and blank-ballot values
velvet/cli/mod.rs::cli clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/config/mod.rs::config clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/fixtures/mod.rs::fixtures clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/ballot_images/mod.rs::ballot_images clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/decode_ballots/mod.rs::decode_ballots clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/do_tally/counting_algorithm/mod.rs::counting_algorithm clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/do_tally/mod.rs::do_tally clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/generate_db/mod.rs::generate_db clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/generate_reports/mod.rs::generate_reports clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/mark_winners/mod.rs::mark_winners clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/pipes/mod.rs::pipes clippy::module_inception Preserve the existing module layout, re-exports and caller paths during the construct review
velvet/fixtures/fixtures.rs::create_area_config clippy::too_many_arguments Keep the fixture API explicit about tenant, election, area, census and audit counts
velvet/pipes/do_tally/tally.rs::create_candidate_results clippy::too_many_arguments Preserve the existing tally-result API and its explicit vote-count and percentage inputs
velvet/pipes/do_tally/tally.rs::create_contest_result clippy::too_many_arguments Preserve the existing contest-result API and its explicit process, candidate and ballot totals
velvet/pipes/generate_reports/generate_reports.rs::read_breakdowns clippy::too_many_arguments Keep report breakdown scope, names, result paths and inclusion decisions explicit
velvet/pipes/generate_reports/generate_reports.rs::make_report clippy::too_many_arguments Preserve the report assembly interface with explicit election, contest, area and tally-sheet context
velvet/pipes/generate_reports/generate_reports.rs::write_report clippy::too_many_arguments Preserve the report output interface with explicit scope, formats, hashes and consolidation controls

The Windmill cleanup keeps these 26 scoped expectations. The single create_ballot_receipt task module also covers its generated constructor.

Item Diagnostic Recorded justification
windmill/src/types/documents.rs::EDocuments non_camel_case_types Preserve the public document identifiers and their existing derived string representations used by export/import callers.
windmill/src/types/documents.rs::ETallyDocuments non_camel_case_types Preserve the public tally-document variant API used by the existing import and export routines.
windmill/src/types/tasks.rs::ETasksExecution non_camel_case_types Preserve the task identifiers and derived strings stored in task-execution records and consumed by existing callers.
windmill/src/postgres/ballot_style.rs::insert_ballot_style clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/keys_ceremony.rs::insert_keys_ceremony clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/results_area_contest.rs::update_results_area_contest_documents clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/results_election_area.rs::insert_results_election_area_documents clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/tally_session_execution.rs::insert_tally_session_execution clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/tally_sheet.rs::insert_tally_sheet clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/tally_sheet_import.rs::insert_tally_sheet_import clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/postgres/tasks_execution.rs::insert_tasks_execution clippy::too_many_arguments Preserve the existing database API argument order for transaction, record scope and column values.
windmill/src/services/certificate_authority.rs::import_certificate_authority clippy::too_many_arguments Preserve the existing certificate operation API and its transaction, scope and actor inputs.
windmill/src/services/certificate_authority.rs::delete_certificate_authority clippy::too_many_arguments Preserve the existing certificate operation API and its transaction, scope and actor inputs.
windmill/src/services/consolidation/eml_generator.rs::render_eml_file clippy::too_many_arguments Preserve the existing EML renderer inputs and output contract used by report generation.
windmill/src/services/documents.rs::upload_and_return_document clippy::too_many_arguments Keep document identity, storage metadata and visibility explicit across the existing upload callers.
windmill/src/services/documents.rs::upload_and_return_document_with_annotations clippy::too_many_arguments Keep document identity, storage metadata and visibility explicit across the existing upload callers.
windmill/src/services/documents.rs::upload_and_return_document_inner clippy::too_many_arguments Keep document identity, storage metadata and visibility explicit across the existing upload callers.
windmill/src/services/documents.rs::upload_and_return_public_event_document clippy::too_many_arguments Keep document identity, storage metadata and visibility explicit across the existing upload callers.
windmill/src/services/documents.rs::get_upload_url clippy::too_many_arguments Keep document identity, storage metadata and visibility explicit across the existing upload callers.
windmill/src/services/external/api_datafix.rs::replace_voter_pin clippy::too_many_arguments Preserve the PIN replacement API with its separate transaction, voter, election and annotation inputs.
windmill/src/services/tally_sheet_import/application.rs::preview_tally_sheet_import clippy::too_many_arguments Keep source identity, canonical bytes, actor and validation context explicit in the existing tally-import API.
windmill/src/services/tally_sheet_import/application.rs::create_tally_sheet_import clippy::too_many_arguments Keep source identity, canonical bytes, actor and validation context explicit in the existing tally-import API.
windmill/src/tasks/mod.rs::create_ballot_receipt (single generated task) clippy::too_many_arguments Preserve this single Celery task and its generated constructor argument names and order used by existing producers and serialized messages.
windmill/src/tasks/generate_reconciliation_patches.rs::write_envelope_tail clippy::too_many_arguments Keep the existing serialized envelope fields explicit in their writer helper; changing grouping has no format benefit.
windmill/src/services/ess_xml_converter.rs::resolve_contest_max_votes clippy::result_large_err Keep structured validation errors consistent with the importer aggregation API; boxing would change its error contract solely for a size heuristic.
windmill/src/services/vault/mod.rs::vault clippy::module_inception Preserve the existing services::vault::vault module path used by vault callers.

The CLI cleanup preserves three existing argument lists and one serialized tally-status enum:

Item Diagnostic Recorded justification
step-cli/src/commands/create_applications.rs::set_applications clippy::too_many_arguments Preserve the existing application insertion inputs and their explicit transaction, tenant, election and column context.
step-cli/src/commands/export_election_event.rs::export_election_event clippy::too_many_arguments Preserve the command export switches and their existing argument order across callers.
step-cli/src/commands/update_voter.rs::edit_voter clippy::too_many_arguments Preserve the current CLI voter-edit interface and explicit user, election and credential fields.
step-cli/src/types/tally.rs::TallyExecutionStatus clippy::upper_case_acronyms Preserve the tally status names and their existing serde and strum representations consumed by the API.

step-cli denies unsafe_code; only load/executor.rs::identity, from main, allows it, with a // SAFETY: comment for its geteuid/getegid calls.

Out of scope: the pre-existing dependency CSV, Miru packaging and non-TOE image cleanup.

Related: https://github.com/sequentech/meta/issues/10548

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jan 22, 2026 •

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://docs.sequentech.io/docusaurus/pr-preview/pr-2335/

Built to branch doc-previews at 2026-10-14 10:02 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@edulix edulix changed the title 📖 [doc] Application for BSI Certification Align uniWAHL production inputs and source controls with offline evaluation Sep 5, 2026
edulix and others added 27 commits September 26, 2026 23:01
compile-plan kept read_plan's plan and passed sources: None, so a version 3
plan, whose members migrate_v3 lifts into ReadPlan::sources, compiled with
an empty census and no warning. Open the file through
election_config::open like the wizard does and hand its sources to
compile_plan; a save-file zip or a delivery now compiles with its census
and files too, and open's warnings are reported and count for --strict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In demo mode the confirmation screen has no tracker URL, so the ballot ID
was an anchor without href: out of the tab order, so only a mouse could
open the dialog explaining a demo has no tracker. ovcs behaved the same
way (since #542). ConfirmationLayout now renders the ID as a button when
it has a click handler and no href, and as a link or plain text otherwise.
Giving the demo anchor the tracker href instead would let a middle click
or "open in new tab" skip the handler and open a locator page for a
ballot that was never cast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erates

updateBallotStyleAndSelection stores the election event and then iterates
documents, elections and support_materials. The file check only looked at
ballot_styles and election_event, so a file without one of those lists
left half an event in the store behind "… is not iterable". Refuse it
up front with the list named, and apply the same check to the copy kept
in sessionStorage across sign-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ac562e6 exported every material row so a hidden material's file would
find its id on re-import. But rows were then written even when the archive
carries no export_S3_files/ (s3_files off), and for voter-secret documents
that an export without voter secrets leaves out. Import inserted those rows
pointing at documents that were never created, silently.

Select rows by the same get_exportable_document_ids set the S3 export uses,
and none with a document when s3_files is off. Hidden materials' rows still
travel with their files, so process_s3_file keeps finding every id; a
material with no document always travels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
meta#12769 moved Question, Answer, AnswersList, InvalidErrorsList, the
presentation helpers and the two host ports from voting-portal into
ui-essentials, but their tests stayed in voting-portal. They still run
there, over the portal's redux adapter, yet ui-essentials measured its
new ballot at zero, so its coverage gate fell from 75.8% to 63.1% of lines.

A package-local harness mounts a contest over a plain selection port and a
deterministic engine, with no store or router. The tests cover marking,
radio and over-vote disabling, explicit blank and invalid options,
write-ins and their character budget, ranked contests, grouped lists and
subtypes, collapsing, review, decline and blank ballots, acclamation, the
warning policy matrix and the ports' refusals without a provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CodeRabbit fixes changed sequent-core sources, so the committed
sequent-core-0.1.0.tgz no longer matched the checkout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lice

Every ballot test mounts its own port over a plain record, so none ran the
portal's BallotSelectionAdapter: seven of its members were uncovered and a
mis-wired forward (setBlank dispatching the wrong action, say) would pass.
The adapter is now mounted over a real store and each member checked
against the slice it replaced, including the new port object that makes the
ballot re-render after a mark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The keypad's placeholder was its only accessible name, and IvrCall drops
the placeholder when no input is expected, so a failed call left an
unlabelled form field and the admin-portal CallFailure story failed its
axe check. IvrCall takes an inputLabel, and the emulator passes one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rets (#3359)

Restore the Keycloak header language selector and inherited version/hash. Remove authentication secrets from the identified logs and events, fix related OTP/CAPTCHA error handling, and ensure both Maven runners discover all 367 tests.

Validated with 367 Java tests, 30 stories, 15 local live-authentication checks, and current hosted checks including all 11 backend journeys.

Parent issue: sequentech/meta#13610

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
Upload the declared step-cli binary and fail when the artifact is missing. Update the feedback-loop design record with measured results and their validation limits.

All current hosted checks pass. The downloadable CLI artifact contains the expected 33,286,368-byte x86-64 binary and its archive integrity was verified.

Parent issue: sequentech/meta#13610

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
The rand_core 0.10 impls for StrandRng, added for curve25519-dalek 5.0
and the released RustCrypto crates, had no direct test: the dalek paths
only reach fill_bytes. Check that each method draws fresh values from
the OS source and that the type satisfies the 0.10 CryptoRng bound.

The dependency upgrade also changed which ristretto trait instantiations
the protocol tests reach, so check the group and scalar-field laws the
generic protocols rely on through the traits, plus malformed-byte and
wrong-length rejection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-core

The Election Architect modules (builder, planner, preview, archive and
workbook reader/writer) sit behind election_config_templates,
election_config_archive and election_config_xlsx. Neither sequent-core
coverage profile enabled them, so every one of those files reached the
no-decrease gate as a new unmeasured source. Enable the gates in both
profiles, matching the Rust CI feature selection in scripts/dev/ci.py,
and add areas explicitly so base and head build the same area code.

Their standalone *_tests.rs modules are excluded like validate_tests.rs
(cargo-llvm-cov never exports them), and election_config/wasm.rs is a
scope exception: it only compiles with the wasm feature.

A comparison base predates these features and Cargo rejects a feature
its manifest does not declare, so run.py now builds a base with only
the head's features it declares, records the rest as
features_absent_from_base and prints them in the summary. The recorded
profile features stay the head's, so the ratchet still compares them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m the page's Add

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

The side menu's create button and the election event list's Add button were both named "Add", so the two could not be told apart by name. The side menu button is now "Add: Election Events".
The list query builder dropped every filter key that was not a column name,
so the area parent search's `name@_ilike` filter never reached Hasura and
typing a name no longer narrowed the choices. Keys are now checked by the
column before the operator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… open support form

Notifications show their creation date since the Invalid Date fix, and a
rejected support material update keeps its form open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both commands arrived with the Election Architect stack with only their
path-safety and migration tests, which left most of their filesystem and
terminal handling unexercised. Build a synthetic workbook with the
core's own xlsx writer and drive each command through its outcomes: a
clean build that replaces a stale directory, --check-only writing
nothing, --strict refusing warnings, validation failures leaving no
output, unknown auth presets, template overrides and typos, base
exports from JSON and export zips (including the refusals), client
profiles, the ballot preview, and unreadable inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e labels

The signature and destination server chips put white labels on the warning,
info and brand success colours, below the 4.5 contrast minimum. Their labels
are now black. Whether axe reported the contrast depended on the browser's
layout, so the stories' expected failure passed in the CI image and failed
locally; with the defect fixed they check it the same way everywhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ded runner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The admin widget catalogue grew to about 250 story files and 1,360 stories;
run in one browser they take 16 minutes, so with setup and the catalogue
build the job passed its 20-minute limit. The stories matrix now comes
from story_shards, admin-portal runs as three shards, only the first
type-checks, checks the inventory and builds the catalogue, and the
safety-net report unites the three coverage artifacts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and RFC 3339 read-back

Exercise every per-sheet refusal in plan_from_workbook (missing and
duplicate identifiers, dangling references, unknown message kinds, bad
ceremony values), the hand-typed cell readers, the IVR columns written
as text, the SheetCensus source and Timestamp::from_rfc3339/Display.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
census_csv::DERIVED dropped enabled and email_verified as if the
platform regenerated them, but build_tables reads both from the census
row and the Census screen offers them as account columns. A census
marking a member suspended (enabled=false) therefore imported them as
active, with only an "Ignoring" note. Only id and
authorized-election-ids are platform-generated. Mirrors beyond 108e6e28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iters

Every branch of the v1/v2 migrations, an unreadable plan, a census source
that fails on rewind or mid-read, contests that offer or elect nobody,
areas with no identifier, unnamed materials, the permission label and
write-in columns, sign-in wording with nowhere to go, and a profile's
warnings reaching the compiled report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A document that is not an export, blank translations, unparseable IVR
annotations, a missing parent area, the flat contest description, the
photographs and support materials the archive does or does not carry,
unreadable censuses and realm profiles in unexpected shapes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oughs

A base realm with no, empty, textual or unreadable user profile under a
census column or the date-of-birth preset, a caller-named or unknown
preset, contradicting realm and event parameters, long permission-label
and inherited-branding lists, spoken prompts on candidates, carried and
tenant parameters, the event's IVR columns, and an area with no id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…refusals and inline images

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eselimsen and others added 7 commits October 12, 2026 11:02
…ts (1/4) (#3863)

Parent issue: sequentech/meta#13755

Shared messaging contracts, provider adapters, ledger and retry
handling, OTP capacity, account readiness, Hasura permissions/migration,
and the public callback relay. Harvest remains internal; stored data
remains readable across the transition.

Stack 1/4 (142 changed paths). Base: `ovcs`. Review the layers in order;
deploy the complete stack.

Validation: prior full Rust, Java and frontend results and their limits
are recorded in `INFRA-MSG-HANDOVER.md`. Final messaging UI checks
passed 42 Chromium interaction tests across four story files at
wide/narrow widths; focused ESLint/Prettier and REUSE passed. The
Keycloak Docker-context regression passed all six tests. Final local
checks passed 1,832 admin unit tests, journey/story type checks, package
lint/formatting, 595 Python tests (unsigned fixture commits), Ruff
across scripts/dev and REUSE.

Manual channel acceptance, saturated OTP latency and real Meta
credential checks remain pending. Broad CI for the entire 487-file
integration change has not been rerun; the current local validation
targets the outstanding changes. No remote branch or PR is created or
updated by this preparation.

Documentation: messaging architecture, deployment and development under
`docs/docusaurus/docs/07-developers/14-messaging/`, manager reference
`docs/docusaurus/docs/02-election_managers/02-reference/13-messaging-channels.md`,
and `INFRA-MSG-USER-TESTING.md`.

<!-- SPDX-FileCopyrightText: 2026 Sequent Tech Inc <legal@sequentech.io>
-->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added messaging insights for delivery statistics, voter counts,
audience previews, and monthly volumes.
  * Added support-reference lookup for applications.
* Added account callback readiness details, credential checks, and
Messenger Page-change impact information.
* Messenger links can now be completed through Get Started or a
referral, without entering a link word.
* **Improvements**
* Messaging delivery now supports scheduled retries and clearer tracking
of pending or unresolved outcomes.
* Messages use each voter’s recorded contact channel, and masked contact
details show more consistent country-code information.
  * Electoral-log filters now support messaging channel and outcome.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ts (2/4) (#3865)

Parent issue: sequentech/meta#13755

Keycloak authentication and enrollment use the selected channel for
codes and notices, preserve resend and abuse limits, and bind Messenger
references to the account and session. Login and enrollment portals show
the channel-specific flow, including neutral recovery responses. The
user menu offers Contact method with consistent menu colors; account
details remain static text.

Stack 2/4 (141 changed paths). Base:
`feat/meta-13755-msg-1-backend/ovcs`. Review the layers in order; deploy
the complete stack.

Validation: prior full Rust, Java and frontend results and their limits
are recorded in `INFRA-MSG-HANDOVER.md`. Final messaging UI checks
passed 42 Chromium interaction tests across four story files at
wide/narrow widths; focused ESLint/Prettier and REUSE passed. The
Keycloak Docker-context regression passed all six tests. Final local
checks passed 1,832 admin unit tests, journey/story type checks, package
lint/formatting, 595 Python tests (unsigned fixture commits), Ruff
across scripts/dev and REUSE.

Manual channel acceptance, saturated OTP latency and real Meta
credential checks remain pending. Broad CI for the entire 487-file
integration change has not been rerun; the current local validation
targets the outstanding changes. No remote branch or PR is created or
updated by this preparation.

Documentation: messaging architecture, deployment and development under
`docs/docusaurus/docs/07-developers/14-messaging/`, manager reference
`docs/docusaurus/docs/02-election_managers/02-reference/13-messaging-channels.md`,
and `INFRA-MSG-USER-TESTING.md`.

<!-- SPDX-FileCopyrightText: 2026 Sequent Tech Inc <legal@sequentech.io>
-->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->

Contact-menu follow-up validation: two desktop/phone Chromium
interactions, five shared Header stories, 20 existing portal unit tests,
shared/voter types, focused lint/format and REUSE passed.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added contact-channel selection during enrollment, tailored to each
available option, with consent and channel-specific guidance.
* Voters can change their contact channel from the portal and see
success, cancellation, or error feedback.
* Added Messenger connection and reconnection guidance, including QR
codes and device-specific instructions.
* Enrollment completion can display the notification channel, masked
contact details, and a copyable support reference.

* **Bug Fixes**
* OTP attempt limits now persist across resends and prevent further code
requests when exhausted.
* Password resets use consistent, account-neutral responses and route
messages through the selected contact channel.
* Sensitive contact details are masked or omitted from logs and event
records.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ts (3/4) (#3867)

Parent issue: sequentech/meta#13755

Administrators configure accounts and event channels, bind provider
templates, review delivery state and monthly volumes, and inspect
fake-provider messages for development testing. Messaging descriptions
expand across the available width, including Settings and the event tab.
Viber approved-template fields adapt to the drawer width in both create
and edit forms. Sent messages refreshes automatically every five seconds
while open, with manual Refresh retained.

Stack 3/4 (146 changed paths). Base:
`feat/meta-13755-msg-2-authentication/ovcs`. Review the layers in order;
deploy the complete stack.

Validation: prior full Rust, Java and frontend results and their limits
are recorded in `INFRA-MSG-HANDOVER.md`. Final messaging UI checks
passed 42 Chromium interaction tests across four story files at
wide/narrow widths; focused ESLint/Prettier and REUSE passed. The
Keycloak Docker-context regression passed all six tests. Final local
checks passed 1,832 admin unit tests, journey/story type checks, package
lint/formatting, 595 Python tests (unsigned fixture commits), Ruff
across scripts/dev and REUSE.

Manual channel acceptance, saturated OTP latency and real Meta
credential checks remain pending. Broad CI for the entire 487-file
integration change has not been rerun; the current local validation
targets the outstanding changes. No remote branch or PR is created or
updated by this preparation.

Documentation: messaging architecture, deployment and development under
`docs/docusaurus/docs/07-developers/14-messaging/`, manager reference
`docs/docusaurus/docs/02-election_managers/02-reference/13-messaging-channels.md`,
and `INFRA-MSG-USER-TESTING.md`.

<!-- SPDX-FileCopyrightText: 2026 Sequent Tech Inc <legal@sequentech.io>
-->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->

Viber follow-up validation: 15 account-editor Chromium stories, 32
account-editor unit tests, story types, focused lint/formatting and
REUSE passed.

Automatic refresh validation: five tab browser stories, 16 related unit
tests, story types, focused lint/formatting and REUSE passed.

WhatsApp viewer follow-up: wider template bubbles and an integrated
copy-code footer, with desktop/phone geometry, clipboard, focus and
accessibility checks. Ten chat browser stories, 23 related unit tests,
story types, focused lint/format and REUSE passed.

Demo viewer follow-up: legacy captured message senders display Demo
labels, retaining original capture data; all eight languages use
Demo-provider wording. 39 UI unit tests, six tab browser stories, types,
lint, formatting and REUSE passed.

Voters > Columns includes an optional Messaging channel column with
translated icons/names and legacy-channel handling; contacts stay
private. Eight voter-list browser stories and 38 related unit tests
passed, with story types, focused lint/format and REUSE.

Send Notification checks missing content against the audience channels
before scheduling, and confirms scheduling rather than delivery. The
WhatsApp-only audience regression and 25 send/audience browser stories
passed, as did 17 related unit tests, types and focused lint/format.

The All view expands complete captured messages inline, keeps them
expanded across refreshes, and reuses the sandboxed email reader. All 14
affected browser stories, 33 related unit/translation tests, types,
focused lint/format and REUSE pass; desktop and phone layouts were
inspected.

Send Notification uses collapsed channel sections like Templates, with
visible arrows and preserved send overrides. Email/SMS/Messenger edits
apply only to this send; approved WhatsApp/Viber text stays read-only.
All 18 drawer browser stories and 17 related unit tests pass, with
types, focused lint/format, REUSE and desktop/phone inspection.

The event Messaging tab now shows compact binding summaries and mounts
only an opened editor, retaining edits and saving unopened bindings. The
40-binding regression, all 19 event-tab browser stories and 55 unit
tests pass. Three warm Chromium profiles reduce the longest blocking
task from about 1.64 s to 0.51 s; types, lint/format, REUSE and
desktop/phone inspection pass.

Approved WhatsApp/Viber sends show static wording previews and a
translated approval notice. Provider IDs/languages are configured in
saved templates or event bindings, with no send-drawer overrides. Exact
approved language codes (`en` and `en_US`) are retained. All 19 drawer
browser stories, 41 related unit/translation tests, types, style and
REUSE pass; desktop/phone previews were inspected.

Approved event templates are grouped in initially collapsed channel
accordions with binding counts. Closed groups avoid mounting rows and
retain edits; approval errors stay visible and incomplete rows open on
Save. All 20 event browser stories and 57 related unit tests pass, with
types, style, REUSE and desktop/phone inspection.

Message types use translated labels in the event binding editor and
collapsed summaries, preserving routing identifiers. The field is
Message type or template, with Default for this purpose when empty. 21
browser stories and 81 focused unit tests passed, plus story/journey
types, focused lint/format and REUSE.

Known demo templates show complete verification and voting reminder copy
in chat previews, conversation lists and expanded All entries, in the
captured language. Captured text remains authoritative and unknown
templates retain identifiers and parameters. 28 browser stories and 36
admin unit tests passed, plus story/journey types, focused lint/format
and REUSE.

Voter selection replaces the regular top actions with bulk actions,
preventing overlapping bars; clearing selection restores the regular
actions. Ten ListUsers browser stories, story/journey types, focused
lint/format and REUSE passed.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* View sent-message conversations and email content, including delivery
reports and refresh status.
* Review messaging delivery and voter counts, recount audiences, and see
which channels and senders are available before sending.
* Inspect messaging account status, callback details, monthly volumes,
and voter delivery issues.
* Search approvals by support reference and view messaging channels in
voter lists.
* **Improvements**
* Logs now support channel and outcome filters and show current delivery
status when available.
  * Messaging channels appear in a consistent order across the portal.
* **Behavior Changes**
* Notifications use event-configured channels rather than manual channel
selection or fallback ordering. Custom HTTP API and Console providers
are no longer supported.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ts (4/4) (#3868)

Parent issue: sequentech/meta#13755

Wire the callback relay into deployment and offline bundles, retain
generated GraphQL/WASM contracts, and provide synthetic scenarios, OTP
benchmark tooling and deployment/development/manager guides. Scenario
setup also repairs missing service-account tenant attributes in existing
local realms; all ten configured accounts and all five fake-provider
delivery paths passed across both demo tenants. The final tree matches
the integration branch; generated contracts remain in their own commit.

Stack 4/4 (62 changed paths). Base: `feat/meta-13755-msg-3-admin/ovcs`.
Review the layers in order; deploy the complete stack.

Validation: prior full Rust, Java and frontend results and their limits
are recorded in `INFRA-MSG-HANDOVER.md`. Final messaging UI checks
passed 42 Chromium interaction tests across four story files at
wide/narrow widths; focused ESLint/Prettier and REUSE passed. The
Keycloak Docker-context regression passed all six tests. Final local
checks passed 1,832 admin unit tests, journey/story type checks, package
lint/formatting, 598 Python tests (unsigned fixture commits), Ruff
across scripts/dev and REUSE.

Manual channel acceptance, saturated OTP latency and real Meta
credential checks remain pending. Broad CI for the entire 487-file
integration change has not been rerun; the current local validation
targets the outstanding changes. No remote branch or PR is created or
updated by this preparation.

Documentation: messaging architecture, deployment and development under
`docs/docusaurus/docs/07-developers/14-messaging/`, manager reference
`docs/docusaurus/docs/02-election_managers/02-reference/13-messaging-channels.md`,
and `INFRA-MSG-USER-TESTING.md`.

<!-- SPDX-FileCopyrightText: 2026 Sequent Tech Inc <legal@sequentech.io>
-->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->

Demo fixture follow-up: existing accounts are renamed in place,
preserving credentials and settings; all ten local account checks and
all five channels passed in both tenants. 599 Python tests passed.

Manager documentation includes enabling Messaging channel in the voter
list column picker.

Demo scenarios seed one Demo notification template per organization with
all five channel bodies and approved instant-message names, preserving
existing templates. All 601 Python tests, focused Ruff and REUSE pass. A
new local WhatsApp manual notice reached DELIVERED and appeared in the
Sent Messages query; prior captures remain.

The manager reference explains collapsible notification sections and
which edits override a send without changing the saved template.

The manager reference documents on-demand event template editors,
preserved changes and validation of closed bindings.

The manager guide documents static approved send previews and the exact
approved provider language code, linking Meta's language reference.

The manager guide explains collapsed approved-template channel groups,
binding counts, preserved changes and validation feedback.

Demo notification now uses a complete voting reminder with voter name,
event name and portal link on all five channels. Setup upgrades only the
untouched demo-owned seed, with a conditional update preserving
customizations. 112 scenario tests and focused Ruff passed; both tenant
seeds and local WhatsApp delivery verified.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added messaging webhook support for provider callbacks, with
configurable public endpoints, request limits, and secure relay
settings.
* Added development scenarios for messaging across two organizations,
including fake-provider setup and OTP capacity benchmarking.
* Added deployment, development, and user-testing guidance for
messaging.
* **Bug Fixes**
* Updated notification journeys to use both email and SMS without
requiring a channel selection.
  * Corrected results queries to use UUID identifiers.
* **Documentation**
* Updated messaging guidance for channel enrollment, delivery,
readiness, and account management, including changes to supported
provider behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Codex <noreply@openai.com>
Parent issue: sequentech/meta#13833

- #3876 typed `SettingsIntegrations` as `React.FC<void>`
so it fits the settings tab story helper, but its own test renders
`<SettingsIntegrations />`, which `FC<void>` rejects (TS2322), breaking
the admin-portal build and the real-stack journeys. The component is now
a parameterless function, which fits both. Typecheck (app and stories)
is clean for it and its 10 tests pass.
- #3875 changed
`packages/sequent-core/tests/keycloak_http.rs`, an input of the
committed WASM package, so "Committed WASM source freshness" failed.
Regenerated; `--check-package` passes and `yarn install
--frozen-lockfile` succeeds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Parent issue: sequentech/meta#13833

#3877 seeds `publication-access.spec.ts` through a
trusted-write `run_sql`, but `INSERT ... SELECT * FROM
jsonb_populate_recordset(null::t, ...)` sets every column the rows omit
to NULL, so `ballot_publication.created_at` (NOT NULL DEFAULT now())
failed with 23502 in the real-stack journeys. The inserts now name only
the columns the rows set, so table defaults apply.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Parent issue: sequentech/meta#13833

The `frontend-ui / admin-portal stories (1/3)` job on the ovcs tip fails
`yarn stories:inventory --check` with two kinds of problem:

- Four widgets had no story: `MessageDetails` and `MessageRow`
(`SentMessages.tsx`), `SelectionAwareListActions` (`ListUsers.tsx`) and
`SendChannelSection` (`SendTemplate.tsx`). None is exported, so they
cannot have their own section. Their parent screens' existing stories
already render and assert them. They are now declared with
`parameters.widgets` on those stories, as other widgets in the package
are: `SelectionReplacesTopActions` (the toolbar is hidden while rows are
selected), `CollapsibleChannelsPreserveSendOverrides` (the collapsible
channel sections keep their edits), and `ExpandMessage` /
`ExpandEmailAndTemplate` (the message row and its expanded details).
- `LedgerStatus.stories.tsx` was titled `Admin/Logs/LedgerStatusField`
but its `meta.component` is `LedgerStates`; it is retitled
`Admin/Logs/LedgerStates`. `LedgerStatusField` stays covered through the
stories' `widgets` parameter.

No widget is exempted from the gate.

Verification: `yarn stories:inventory --check` now reports 501 covered,
0 remaining, no problems. `test:story` passes for the four story files
(LedgerStatus 2, SendTemplate 19, ListUsers 10, SentMessages 12),
`typecheck:stories` and prettier are clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
edulix and others added 5 commits October 12, 2026 12:29
.agents/skills holds the skill read by Codex; .claude/skills links to it
for Claude Code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bring the BSI branch up to date with main so that #2335 stays mergeable
and its CI runs. main replaces immudb with the PostgreSQL/Trellis board
(#3420) and RabbitMQ with PGMQ (#3422): the BSI changes to the removed
immudb crates and callers are dropped, and main's new code follows the
BSI lint policy (scoped expectations, unsafe_code deny with a SAFETY
comment). The offline Dockerfiles and the source-built sequent-core
WebAssembly package are kept.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The workspace packages now depend on the sequent-core package built from
source in rust/pkg, so the frontend tests, lint and SonarQube jobs build
it with the shared composite action, and build_wasm checks that no
prebuilt archive is committed. ESLint ignores the generated package.

Also add the missing REUSE headers and annotations, apply rustfmt, and
compare the vault error test against the full error chain.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pure rename, content unchanged, so history follows the file.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md is read by Codex and other agents; CLAUDE.md now only imports it
(@AGENTS.md), so Claude Code loads the same instructions and there is a
single file to maintain. References in the IVR design document now point
to AGENTS.md.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
edulix and others added 16 commits October 12, 2026 13:04
The lint cleanup changed the native deserializer to Error::other and
dropped the ErrorKind import, but the sequential WebAssembly variant
still used ErrorKind, so the from-source WebAssembly build failed. Use
Error::other there too.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Artifact uploads fail while the account's artifact storage quota is
exhausted. Hand the sequent-core package from the build job to the
frontend test jobs through the Actions cache, keyed by the run, instead.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Verifying the Delivery" gives the commands of the consumer's checks of
a delivery, in the order of the signed delivery note; it is the first
section of the Deployment Guide (AGD-04). The production Dockerfile
fetches wasm-bindgen-cli 0.2.128, the version of the wasm-bindgen crate,
which wasm-pack requires.

Related: sequentech/meta#10548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ask queues (#3883)

Parent issue: sequentech/meta#14197

Merges all of `main` into `ovcs`: ovcs stops using RabbitMQ and ImmuDB,
and every ovcs feature and fix is kept, ported onto main's PostgreSQL
electoral log, per-event ballot box and PGMQ task queues. ovcs keeps its
own Harvest token verification (#3554). The image builds keep ovcs's
reproducibility rules.

The merge went in stages, one merge commit per group of main PRs,
followed by a catch-up with ovcs's own new commits and the fixes from
review. Each merge commit's message says what was ported and decided.

## What changes for ovcs

- **Electoral log** (main #3420, #3499, #3534): records are in
PostgreSQL with Trellis Merkle logs, in a database per election event.
ImmuDB, immu-board and immudb-rs are gone.
- **Cast votes** move from Hasura's `cast_vote` table into each event's
ballot box. ovcs features built on cast votes are ported onto it:
- **Ballot-box seals.** The guard is a trigger on `ballot_box_ballot` in
the event database. The seal and its signed record are written in one
transaction there, after the sequencer has appended the box. The tally
re-verifies each box against its seal. Services own the event databases,
so this detects tampering rather than preventing it; the seal docs say
so.
- **Received ballots and signed receipts** (#3457) live in
`ballot_box_received`. Voters see contents and Ballot IDs only through
the checks-period-gated paths.
  - **Datafix** keeps ovcs's processor, on the ballot box.
- **Ballot checks.** The census, reconciliation, voter deletion, event
deletion, batch anonymity and messaging recount checks read the ballot
box.
- **Permission labels.** ovcs's Hasura grants on `cast_vote` (#3776)
became Harvest-side scoping of the reads that replaced them. The
electoral-log list, console and proofs honour permission labels (#3833).
  - **Monitoring** reads first votes from the ballot box incrementally.
- **Electoral-log writers** use stable delivery IDs, so a retried append
records each entry once. This covers the signing outbox, approval
matrix, monitoring config, configuration packages, report manifests,
messaging and seals.
- **Statement numbering.** main's statement variants keep their released
numbers and ovcs's follow. Activity-log CSVs whose rows don't match
their statement kind are refused.
- **Task queues** (main #3422, #3509): PGMQ in each environment's own
database.
- Every ovcs queue, task and beat schedule runs on it, including
`monitoring_queue` and messaging.
  - An unreachable queue database is a failed send, not a panic.
  - The seal dispatcher sends each seal with its ETA at the deadline.
  - The electoral-log archives keep every message that did not succeed.
- **main's other PRs:**
- private S3 publications (#3380): ovcs keeps its voting-window
projection, and its migration moved to `1788765000003`;
  - S3 voting content (#3414);
  - load tooling (#3415);
  - tally key restore outcomes (#3290, #3293);
  - auditable ballot ciphertext check (#3300);
  - accessibility and i18n (#3169, #3401, #2952–#2964);
  - voting channels (#3530).
- **Main's #3859** is recorded as merged without its code. ovcs already
verifies every bearer token.
- **Election import** (ovcs #3796 / step#3607, publication half).
Imported publications are restored unpublished. Generated ballots are
kept only if every ballot carries its election's verified key or a demo
key.
- **Bug found while porting:** the delegate-weight count (#3743) read a
status column the tally ballot file doesn't have. It now reads the voter
IDs.
- **Generated files.** The WASM package, `yarn.lock` and GraphQL types
are regenerated. `--check-package` and `yarn install --frozen-lockfile`
pass. The GraphQL types come from a Hasura that applied every migration
to an empty database, with consistent metadata.

## Deployment

- **This is for new environments, like main's ballot box.** Main's
migration drops `cast_vote`, ImmuDB history is not imported, RabbitMQ
messages are not moved, and events created before the upgrade have no
electoral-log database. Before upgrading an installed site, finish its
elections and export what must be kept.
- **ovcs's own unreleased migrations were edited in place**, so
development databases need a reset.
- **New settings:**
- the task-queue database (`QUEUE_DB__*` and its roles), with `windmill
main setup-queue-database` run before the services start;
- the electoral-log database (`ELECTORAL_LOG_PG_*` and its roles, plus
the checkpoint bucket).
- **Existing Keycloak tenant realms** don't get the new task-queue and
electoral-log console roles automatically.

## Testing

- Rust: the whole workspace compiles with tests.
- Windmill: 1661 unit tests, plus the database suites for seals, the
sealed-box tally, received ballots, ceremonies, events, messaging,
monitoring, PGMQ and publications.
  - electoral-log and pgmq-broker: unit tests and database suites.
  - sequent-core: 780 tests.
  - Harvest: 514 tests against PostgreSQL 16.
  - step-cli: 222 tests and the acceptance probe.
  - The database tests used throwaway PostgreSQL.
- Hasura: every migration applies to an empty database.
  - An upgrade from main's released set gives the same schema.
  - The recent migrations go down and up again cleanly.
  - The metadata is consistent.
- Frontends: all six packages type-check, and their Jest suites pass:
  - admin-portal 2044;
  - voting-portal 944;
  - ui-core 480;
  - ui-essentials 532;
  - ballot-verifier 133;
  - results-portal 40.
- Scripts: scripts/dev, scripts/e2e and the voting-flow suites pass.
Every compose combination passes `docker compose config`.
- Reviews: four independent reviews covered voting integrity, the
electoral log and queues with authorization, merge fidelity, and
reproducibility/infrastructure. Their findings are fixed in this PR.

## CI

Everything passes except six coverage "no decrease" gates. These compare
against ovcs as the base, so they can't pass on this PR and need an
admin waiver:
- **Windmill and wrap-map-err:** the base measurement exits early,
because ovcs's base needs ImmuDB (`IMMUDB_USER must be set`), which this
CI no longer provides.
- **Harvest:** every metric went up. The gate still fails on "new
unmeasured file" `src/types/resources.rs`, which lost its only code
(main removed the ImmuDB `TryFrom`). The base still measures it, so a
scope exception is refused.
- **electoral-log-native:** main's PostgreSQL adapters are covered only
by `#[ignore]` tests that need PostgreSQL. CI runs them in the Windmill
job, but the coverage profile can't, without breaking the base's ImmuDB
measurement.
- **sequent-core:** lines and regions went up. Functions dropped only by
main's closures in `util/aws.rs` (which need AWS) and one in
`encrypt.rs`.
- **braid:** source and tests are unchanged. One function lost its hit
when llvm-cov discarded mismatched data after the lockfile change.

These go away once this merge is the base. Main never ran these gates,
so none of main's code had been measured before.

Not run in CI: the Storybook play tests outside the selected shards, and
the two-node air-gapped installation.

## For reviewers

- **Behaviour change in the voting portal:** on a direct ballot link
after the checks period, `BallotLocator` now waits for the event before
asking `LocateBallot`. Main moved the locator out of the
published-ballot route. If the event can't be read, the API still
decides.
- **Backend import journey:** an imported event whose electoral log has
no board now reads as an empty log. That is main's per-event store
behaviour; the journey used to expect an error.
- **Possible bug in main's code, not changed here:** on the Electoral
Log Console's last records page, "next page" stays enabled
(`rowCount=-1`).

## Follow-ups

- The air-gapped installation is updated in sequentech/beyond#995.
- #3884 points the `beyond` submodule at it. It is
stacked on this PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0129Cs8ygVE7UH6N1XzXk8WP

---------

Co-authored-by: Xavier Alsina <xavier.alsina@sequentech.io>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Beltrán Rodríguez <beltran.rodriguez@sequentech.io>
Co-authored-by: Eduardo Robles <edu@sequentech.io>
Co-authored-by: Eduardo Robles <edulix@users.noreply.github.com>
Co-authored-by: Eduardo Robles <edulix@gmail.com>
Parent issue: sequentech/meta#14197

Points step's `beyond` submodule at `4ba21394c`, beyond's `ovcs` with
sequentech/beyond#995 merged: the air-gapped installation that runs
step's `ovcs` with `main` merged in (PGMQ task queues, the PostgreSQL
electoral log, no RabbitMQ or immudb).

The pointer changes from beyond `a0ee42e104` (2026-09-04) to
`4ba21394c`. It replaces #3884, whose branch still
carried #3883's commits after #3883 was squash-merged.

Once sequentech/beyond#997 merges, this moves to that merged commit, so
that beyond's `step` submodule points at a commit on step's `ovcs`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0129Cs8ygVE7UH6N1XzXk8WP

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Parent issue: sequentech/meta#14203

### Description

Migrate `ovcs` to the VoteSecure cryptographic backend and updated Braid
protocol in `packages/wbraid`. The migration covers ballot encryption
and verification, trustee key ceremonies, mixing, threshold decryption
and tallying across backend services and browser WASM.

### Changes

- **Shared cryptography:** Introduce `voting-crypto` and migrate
`sequent-core` to native ciphertext formats, proofs and encryption
domains.
- **Voting and verification:** Update ballot encryption, admission
checks, receipts and audited-ballot verification for single and combined
contests.
- **Protocol integration:** Centralize board configuration, trustee
identities and ceremony state in `protocol-board`.
- **Trustees:** Support native and browser participants, automatic and
manual ceremonies, custody proofs, session recovery and browser backups.
- **Tallying:** Integrate native mixing and decryption, including
weighted combined ballots and tally review corrections.
- **Election recovery:** Preserve protocol messages, manager identities,
publication metadata and immutable cryptographic domains during export
and import.
- **Deployment:** Replace retired backend components and update service
configuration, WASM packages, CI and operator documentation.

### Validation

- Recorded browser trustee journeys pass for manual and automatic key
ceremonies, voting and tallying.
- Add regression coverage for malformed ciphertexts, foreign encryption
domains, weighted ballots, archive recovery and verifier rejection
cases.
- Add browser coverage for session reload, backup restoration and
voting-portal audit downloads.

Checks were not rerun while preparing this description.

### Compatibility

Legacy browser trustee configurations and protocol stores use
incompatible cryptography and require new identities and a new key
ceremony. Existing application secret envelopes remain readable.

---------

Co-authored-by: ruescasd <david@sequentech.io>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Enes <enes@sequent-devenv>
…3891)

Parent issue: sequentech/meta#14253

`beyond/airgap/prepare.sh` failed at the current `ovcs` tip, and two
builds of one commit made on different days gave different
harvest/windmill images.

- `Dockerfile.offline-dependencies`: the `keycloak-m2` stage now copies
`sequent-core/src/types/messaging.rs` and `dial_codes.rs`, which the OTP
contract tests read since #3865 (as `Dockerfile.keycloak` already does).
Without them Maven failed with `NoSuchFile` and `prepare.sh` built no
image.
- harvest and windmill runtime stages declare `ARG SOURCE_DATE_EPOCH`
before the Chromium apt layer, so `useradd` records the commit's day in
`/etc/shadow` instead of the day of the build.

**Validation**
- `python3 -m unittest scripts.dev.test_keycloak_image
scripts.dev.test_product_images`: the two new tests failed before the
change and pass after it (8 tests OK).
- Unmodified `beyond/airgap/prepare.sh` (beyond `4ba21394`), each pass
in a fresh Docker-in-Docker daemon with an empty Nix store, passes run
one after the other: before, it fails in
`Dockerfile.offline-dependencies`. After, two builds give the same
installer ISO (`e00f8150…95e2`), image archive, NixOS NAR hashes, all 87
`checksums.txt` files, and the same manifest, config and layer digests
for all 11 Sequent images. Built on 2026-10-13, harvest/windmill record
day 20738 (`SOURCE_DATE_EPOCH`), not 20739.
- The images then ran the full synthetic election (two-trustee ceremony,
voting, tally through the trustees, Admin Portal, Voting Portal and
Ballot Verifier journeys).

**Documentation**: no change; `beyond/airgap/README.md` "Reproducible
builds" already states the rule these Dockerfiles follow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The ovcs line of step becomes the software base of the evaluated
configuration. The merge uses origin/main (3a1adf7) as its base:
ovcs already contains all of main through #3883.

Conflict policy, ovcs plus what the evaluated configuration needs:
- Files whose only changes on this branch were review and lint commits
  take ovcs.
- Functional fixes of this branch are ported onto the ovcs code where
  that code still exists: the bounded decoding (a65c7c7), the
  Keycloak credential diagnostics (e3275d6) and the malformed vault
  keys (ad9c598). The other fixes are already in ovcs or merged
  cleanly; the Keycloak pagination helper and the tally CSV export they
  touched are no longer used and are removed.
- Kept from this branch: Miru and the ECIES tool off by default, with
  the in-process export encryption; unsafe_code = "forbid" in the crate
  manifests; the WASM package built from source; the controlled build
  model of the production Dockerfiles (named stages replaced with
  --build-context, offline cargo, digest-pinned bases); the credential
  hygiene of the files in the TOE tree.
- The development environment (.devcontainer) is ovcs's. It is outside
  the TOE tree.
- braid, b4 and strand give way to packages/wbraid, which keeps its own
  Cargo.lock. The b4v6 board image is built from it by
  packages/wbraid/crates/b4/Dockerfile.prod. The celery-codegen patch
  is dropped.
- vsc draws its randomness from the operating system (SysRng instead of
  ThreadRng), as packages/wbraid/PROVENANCE.md records.
- scripts/check-unsafe.py covers both Cargo workspaces.
- The copied reCAPTCHA secret of two step-cli election events and the
  trustees' Keycloak client secret of the airgap template, which ovcs
  adds, are blanked as on this branch's other templates.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
beyond feat/meta-10548/main (2c42561ebddc) records the merge of ovcs,
identifies the TOE by the temenos snapshot assembled from it
(d85de9cd8d1c) and carries the revised OR ALC 1.0 package.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The evaluated configuration does not use the scanovate-authenticator
module that ovcs adds to packages/keycloak-extensions. The parent POM now
selects it with a file-activated profile, as it does the beyond modules:
development builds keep it, and the temenos tree, which the bootstrap
assembles without its directory, builds the keycloak image without it.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…henticator

beyond feat/meta-10548/main (2cb806efb524) identifies the TOE by the
temenos snapshot 32eb13071aba, assembled from e9f7bc5, which leaves
scanovate-authenticator out, and carries the revised OR ALC 1.0 package.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The BSI pages are back in the sidebar of the beyond docs site.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
beyond keeps the part names of the Security Target, `braid` and `b4`, on
the ovcs base, with Annex A of temenos 1087b8448b44 and the OR ALC 1.0
package exported from 863113edeccb.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
beyond has the response of the building's security staff to the site
alarm (ALC-04), with Annex A of temenos 3f7dc70cbe4e and the OR ALC 1.0
package exported from a4728ff3b250.

Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Related: sequentech/meta#10548

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

5 participants