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-fieldAsyncRwLock, 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-serverandrustsystem-trustauth). Deployment now requires both services to be running. See the updated Dockerfile and deployment instructions. rustsystem-clienthas been removed. All cryptographic operations on the client side are now performed in native TypeScript. WASM has been scrapped.- The
api-corecrate has been renamed torustsystem-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-voteendpoint 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/VoteOptioncomponents (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
MeetingLogLayerwrites events containing amuuidfield to per-meeting log files atmeetings/{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.mdcovering branching model, versioning rules, and release note format.- Dev pages (
/preview,/signature-dev) are gated in production builds.