Skip to content

v2.0.4

Latest

Choose a tag to compare

@HellFelix HellFelix released this 10 Apr 14:54

Release Notes — v2.0.4

Release date: 2026-04-10
Promoted from v2.0.4-beta.
Status: stable.


Overview

v2.0.4 is the first stable release of the 2.0 series. It is promoted from v2.0.4-beta, which passed testing with no further changes required. No code has been modified since the beta tag.

The 2.0 series represents a complete overhaul of the system, a new two-service architecture, a redesigned frontend, encrypted tally persistence, structured logging, and a substantially more robust codebase. The five beta releases that preceded this stable tag addressed concurrency bugs, layout regressions, SSE correctness issues, a missing security control (rate limiting), and a host management gap, all found during controlled testing.

What changed in the beta series (v2.0.0-beta → v2.0.4-beta)

  • v2.0.0-beta — Architectural split into rustsystem-server / rustsystem-trustauth, full frontend redesign, encrypted tally persistence, BBS crypto in TypeScript, meeting deletion, vote state endpoints, per-field AsyncRwLock, comprehensive error handling, structured logging, and documentation.
  • v2.0.1-beta — README lock-ordering correction, global footer.
  • v2.0.2-beta — Race condition fix in start-vote, mobile layout fixes, CONTRIBUTE.md, load test.
  • v2.0.3-beta — Invite-watch SSE correctness fix (wrong name, all-hosts broadcast), CI pipeline for stage.
  • v2.0.4-beta — Rate limiting on public endpoints, hosts can remove other hosts, admin page session guard, spurious SSE event fix.

The sections below document the full feature set of the 2.0 release for reference.


Breaking Changes

  • The monolithic backend has been replaced by two separate services (rustsystem-server and rustsystem-trustauth). Deployment now requires both services to be running. See the updated Dockerfile and deployment instructions.
  • rustsystem-client has been removed. All cryptographic operations on the client side are now performed in native TypeScript. WASM has been scrapped.
  • The api-core crate has been renamed to rustsystem-core.
  • Several environment variables have been renamed or added. See .env.
  • Tally saving has moved to POST /api/host/tally (was /api/host/get-tally).

Architecture: Server / Trustauth Split

The headline change of the 2.0 series. The backend is now split into two services that communicate over mutual TLS (mTLS):

Service Role
rustsystem-server Meeting management, vote rounds, tallies, and the frontend SPA
rustsystem-trustauth Blind-signing authority — issues BBS blind signatures and stores voter credentials

This split provides a cryptographic separation of concerns: trustauth knows who is eligible but never sees the vote; the server records what was voted but never learns who submitted it.

  • Trustauth's start-vote endpoint is accessible only from the server via mTLS.
  • PEM certificates are embedded directly into the executables via include_bytes!.
  • Inter-service URLs are configured at compile time via environment variables.

Security

Rate Limiting

Both services apply IP-based rate limiting via tower-governor (10 requests/second sustained, burst of 30) when running in HTTPS (production) mode. Limiting is disabled in HTTP mode so that integration tests can run freely. A background task cleans up stale entries every 60 seconds.

Service Limit Mode
rustsystem-server 10 req/s, burst 30 HTTPS only
rustsystem-trustauth 10 req/s, burst 30 HTTPS only

Host Removal

Hosts can now be removed by other hosts mid-session. The voter list remove button is visible for all voters except the currently logged-in user. The admin page detects removal via a session guard that polls /api/common/vote-progress every 10 seconds; on a 401 it shows a "Not an administrator" panel instead of the normal admin UI.


Frontend

Stateless Client

Voting credentials are stored in trustauth, not in the browser. The frontend is fully stateless: session information is fetched from the server on demand. A voter can clear their browser without losing credentials.

Redesigned UI

  • New design system with reusable components (size: s/sm/m/ml/l/xl, color: primary/secondary/accent).
  • Multiple themes, persisted in local storage.
  • Global navbar and theme switcher.
  • Preview page (/preview) showing every component in all size and colour combinations.
  • VoteSection / VoteOption components (checkboxes replacing radio buttons).
  • Is-authorized indicator — voters can confirm they are logged in to both services.
  • Voter list search field.
  • Rejoin buttons on the landing page.
  • Persistent global footer on every page (navigation, attribution, current release tag).

BBS Crypto in the Browser

The BBS blind-signature protocol (@noble/curves) is implemented entirely in TypeScript. A signature-dev developer page lets contributors simulate the full cryptographic flow without a running backend.

Download All Tallies at Meeting Close

The close-meeting panel includes a Download tallies section. The host can download and decrypt every tally from the meeting in one step, producing a single tallies.json file. Decryption happens entirely in the browser; the private key is never transmitted.


Encrypted Tally Persistence

Vote tallies are saved to disk as encrypted files:

  • Encryption: X25519 ECDH + HKDF-SHA256 + ChaCha20-Poly1305 (ECIES).
  • The public key is derived from the meeting password at creation time; the private key never reaches the server.
  • Files are written to meetings/{meeting-id}/tally-{datetime}.enc.

New Endpoints

Endpoint Method Description
/api/meeting DELETE Delete a meeting and discard all in-memory state
/api/host/tally POST Finalise and save the encrypted tally (replaces /api/host/get-tally)
/api/common/vote-active GET Query current vote state
/api/common/vote-state-watch GET (SSE) Stream vote state changes

Performance & Reliability

Per-Field AsyncRwLock

Mutex has been replaced throughout with per-field AsyncRwLock. A two-level locking strategy (outer map lock + independent per-field locks inside each Meeting) allows concurrent requests on different fields to proceed without blocking. Lock ordering rules are documented in the README.

Concurrency Fix: start-vote Race

Concurrent POST /api/host/start-vote requests could race to initialise the BLS12-381 keypair, leaving the server and trustauth with inconsistent keys. An exclusive lock is now held for the duration of start-vote, ensuring exactly one keypair is generated per round.

Error Handling

All fallible panics have been removed. The entire codebase uses APIError / APIErrorCode result types, with no anyhow, Box<dyn Error>, or io::Result in handler code.


SSE Fixes

Two SSE correctness bugs were found and fixed during the beta series:

Stream Problem Fix
invite-watch Fired on all hosts' screens when any voter logged in; showed the wrong voter's name Channel now carries Option<String> (voter name); each host sees the name of the voter they invited
invite-watch Emitted a spurious event immediately on subscription Switched from WatchStream::new to WatchStream::from_changes

Structured Logging

  • A custom MeetingLogLayer writes events containing a muuid field to per-meeting log files at meetings/{muuid}/log.
  • The main server log is a daily-rolling file at logs/server.log.
  • error! is reserved for genuine server faults; info! is used for all normal flow events.

Testing

Area Coverage
Frontend signatures BBS blind-signature workflow in TypeScript
Multi-profile E2E Multiple simultaneous browser profiles
Server unit tests Refactored server endpoints
Trustauth unit tests Trustauth handlers
Server ↔ trustauth E2E Full flow over mTLS
Frontend integration Stateless architecture
Load test Concurrent meeting and voting operations

Playwright-based tests have been removed as incompatible with the two-service architecture.


Documentation

  • Full README covering quick-start, architecture (Mermaid diagrams), cryptography, RwLock structure, and deployment.
  • In-app Guide and Cryptography pages for end-users.
  • CONTRIBUTE.md covering branching model, versioning rules, and release note format.
  • Dev pages (/preview, /signature-dev) are gated in production builds.