Skip to content

Releases: fsek/rustsystem

v2.0.4-beta

10 Apr 14:38
6e4a64c

Choose a tag to compare

v2.0.4-beta Pre-release
Pre-release

Release Notes — v2.0.4-beta

Pre-release date: 2026-04-10
Built on top of v2.0.3-beta.
Status: pre-release — requires testing before promotion to full release.


Overview

v2.0.4-beta is a small patch release addressing two security gaps and one reliability bug found during testing of v2.0.3-beta.

  • Rate limiting added to all public endpoints (server and trustauth)
  • Hosts can now be removed by other hosts; admin page gains session guard
  • invite-watch SSE fixed to suppress spurious events

Changes

Rate Limiting on Public Endpoints

Prior to this release, public-facing endpoints on both the server and trustauth had no protection against high request volumes. A client could flood the registration or login endpoints without restriction.

Both services now apply IP-based rate limiting via tower-governor (10 requests/second sustained, burst of 30) when running in HTTPS (production) mode. In HTTP mode (local development and integration tests) limiting is intentionally disabled, since tests do not provide ConnectInfo and should be able to send requests freely. A background task runs every 60 seconds on each service to evict stale entries and prevent unbounded memory growth.

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

Hosts Can Remove Other Hosts; Admin Page Session Guard

Two related gaps existed in host management:

  1. A host could not be removed mid-session. The voter list remove button was hidden for all users with host status, including those who were not the currently logged-in user. The button is now visible for every voter except the currently logged-in user. Hosts can be kicked by other hosts, but cannot kick themselves.

  2. The admin page had no session invalidation handling. If a host was removed by another host, their admin page would continue to render and operate normally as it never detected that their session was gone. The admin page now runs the same sessionValid guard already used on the meeting page: a refreshSession callback polls /api/common/vote-progress every 10 seconds and on a 401 marks the session invalid. The initial data load also detects 401 immediately. When the session is marked invalid, a "Not an administrator" panel replaces the normal admin UI.

Behaviour Before After
Remove button shown for other hosts No Yes
Remove button shown for self N/A No
Admin page detects own removal No Yes (10 s polling + on load)
getSessionIds on non-OK response Silent Throws (consistent with other helpers)

invite-watch SSE: Suppress Spurious Events

The invite-watch SSE stream used WatchStream::new, which emits the current value immediately upon subscription in addition to subsequent changes. This caused the host-side invite watcher to fire as soon as a client connected, before any real state change had occurred.

Switched to WatchStream::from_changes, which only emits on actual state transitions. The stream now stays silent until the invited voter genuinely completes login.

v2.0.3-beta

20 Mar 15:43
b3f535b

Choose a tag to compare

v2.0.3-beta Pre-release
Pre-release

Release Notes — v2.0.3-beta

Pre-release date: 2026-03-20
Built on top of v2.0.2-beta.
Status: pre-release — requires testing before promotion to stable.


Overview

v2.0.3-beta is a small patch release. It fixes a bug in the invite-watch SSE that caused incorrect behaviour when multiple hosts were inviting voters simultaneously, adds a CI pipeline for the stage branch, and improves documentation for the APIHandler trait and the versioning section of the contributing guide.

  • Fix: invite-watch SSE fires on all hosts and shows the wrong voter name
  • Add CI pipeline for stage
  • Documentation: updated APIHandler usage guide
  • Documentation: versioning wording in CONTRIBUTE.md

Changes

Fix: Invite-Watch SSE Fires on All Hosts and Shows Wrong Name

When multiple hosts were inviting voters at the same time, two problems occurred:

Problem Cause
The QR code panel was dismissed on all hosts' screens when any voter logged in invite_auth broadcast a plain bool; all subscribers reacted identically
The "has logged in" alert showed the wrong voter's name The frontend read the name from its own local qrInfo state, not from the SSE event

Both problems are fixed together. The invite_auth channel now carries Option<String> — the name of the voter who just logged in — instead of a boolean. The SSE event data is the voter's name directly, so every host sees the correct name regardless of who they were inviting. The QR code panel no longer auto-dismisses on login at all; hosts dismiss it manually. This also removes a spurious initial "Ready" event that the old start-invite endpoint sent unconditionally.


Add CI Pipeline for stage

A CI pipeline has been added for the stage branch. It runs automatically on pushes and pull requests targeting stage.


Documentation: Updated APIHandler Usage Guide

The documentation explaining how to use the APIHandler trait has been updated to better reflect current usage patterns.


Documentation: Versioning Wording in CONTRIBUTE.md

Minor wording improvements to the versioning section of the contributing guide for clarity.

v2.0.2-beta

20 Mar 13:35

Choose a tag to compare

v2.0.2-beta Pre-release
Pre-release

Release Notes — v2.0.2-beta

Pre-release date: 2026-03-20
Built on top of v2.0.1-beta.
Status: pre-release — requires testing before promotion to v2.0.2.

Overview

v2.0.2-beta is a small patch release addressing two bugs discovered during testing of v2.0.1-beta — one a correctness issue in concurrent vote initialisation, the other a mobile layout regression. It also adds a contributor guide, an end-to-end load test, and minor housekeeping.

  • Fixed concurrency race condition in start-vote that could corrupt round keypairs
  • Fixed mobile navbar tabs pushing the theme toggle button off-screen
  • Fixed feature pills on the landing page overlapping the scroll cue
  • Added CONTRIBUTE.md contribution guide
  • Added end-to-end load test in the frontend
  • Removed TODO.md in favour of GitHub issues
  • Minor cleanup in start_vote.rs

Changes

Concurrency race condition in start-vote

Concurrent POST /api/host/start-vote requests could race to initialise the BLS12-381 keypair for a voting round. Because the keypair generation was not mutually exclusive, two callers could produce different RoundState values — leaving trustauth and the server with inconsistent keys and causing all vote proofs submitted in that round to fail verification.

A forced exclusive lock is now held for the duration of start-vote, serialising any concurrent calls so that exactly one keypair is generated per round.


Mobile layout issues on navbar and landing page

Two layout problems were present on portrait mobile viewports:

Element Problem Fix
Navbar tabs Pushed the theme toggle button off-screen to the right Hidden below the sm breakpoint (640 px)
Landing page scroll cue Obscured by the feature pills above it Hidden below the sm breakpoint (640 px)

Both elements are decorative at small screen sizes and are hidden rather than reflowed, keeping the mobile layout clean without restructuring the page.


CONTRIBUTE.md

A contribution guide has been added to the repository root. It covers the branching model, versioning rules, and release note format expected for all contributors.


End-to-end load test

A load test has been added to the frontend test suite. It exercises the full voting flow under concurrent load, and is intended to catch concurrency regressions (such as the start-vote race above) before they reach production.


Removed TODO.md

TODO.md has been removed. Outstanding tasks are now tracked as GitHub issues, which provides better visibility, assignability, and linkability from PRs and commits.


Minor cleanup in start_vote.rs

An unnecessary intermediate variable was removed and formatting was improved in rustsystem-server/src/api/host/start_vote.rs. No behaviour change.

v2.0.1-beta

04 Mar 16:28

Choose a tag to compare

v2.0.1-beta Pre-release
Pre-release

Release Notes — v2.0.1-beta

Pre-release date: 2026-03-04
Built on top of v2.0.0-beta.
Status: pre-release — see v2.0.0-beta for overall pre-release status.


Overview

v2.0.1-beta is a small patch release containing two changes. No breaking changes have been made and no endpoints have been modified.

  • README lock ordering correction
  • Global footer added to the frontend

Changes

README: Lock Ordering Correction

The documented lock ordering in the README was wrong. The previous version listed voters before vote_auth, but start-vote — the only endpoint that holds both locks simultaneously — always acquires vote_auth first. Documenting the wrong order could mislead future contributors into introducing a deadlock.

Lock Correct position in ordering
vote_auth Before voters
voters After vote_auth
invite_auth Last (no simultaneous-hold relationships)

Two additional clarifications were made: login holds its locks sequentially rather than simultaneously, and invite_auth has been moved to the end of the ordering table to reflect that it is never held at the same time as any other lock.


Global Footer

A persistent footer is now rendered at the bottom of every page via the root layout. Previously the landing page had its own local footer that was not visible elsewhere in the application.

The footer contains:

  • Navigation links to the Guide and Cryptography pages.
  • An attribution line for F-sektionen · Lund University.
  • The current release tag, injected at build time using git describe --tags --abbrev=0 and displayed as the version string.

v2.0.0-beta

04 Mar 16:02

Choose a tag to compare

v2.0.0-beta Pre-release
Pre-release

Release Notes — v2.0.0-beta

Pre-release date: 2026-03-03
Covers all changes merged since 2026-02-16.
Status: pre-release — requires stress testing before production use.


Overview

v2.0.0-beta is a pre-release. It is currently being stress tested and will not be put into full production use until that process is complete. The beta label reflects this: the feature set is complete and the system is stable enough for controlled testing, but it has not yet been validated under real meeting load.

The headline change is a fundamental architectural split of the backend into two independent services — rustsystem-server and rustsystem-trustauth — which enforces the zero-knowledge anonymity guarantee at the infrastructure level rather than by policy. Beyond that, the entire frontend has been redesigned, encrypted tally persistence has been added, and the codebase now has comprehensive tests, structured logging, and proper error handling throughout.

Pre-release status

This release includes a dedicated stress test that exercises concurrent meeting and voting operations (see Testing). The results of that test will inform whether any changes are needed before v2.0 is tagged as stable.

The system should not be used to run official FSEK votes until the stress test has passed and v2.0 is promoted to a full release.


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's side are now perfomed 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.

Architecture: Server / Trustauth Split

The most significant change in this release. Previously, all server logic was combined in a single binary. 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. Neither service can reconstruct the other half of the picture, even in principle.

  • Trustauth's start-vote endpoint is accessible only from the server via mTLS, preventing any public caller from triggering round creation.
  • PEM certificates are embedded directly into the executables via include_bytes! so they are never stored on the build machine.
  • Inter-service URLs are configured at compile time via environment variables (API_ENDPOINT_SERVER_TO_TRUSTAUTH, API_ENDPOINT_TRUSTAUTH_TO_SERVER).

Frontend: Stateless Client

Voting credentials (blind signatures and related state) are now stored in trustauth, not in the browser's local storage. The frontend is fully stateless: session information is fetched from the server on demand rather than persisted in the browser. This eliminates an entire class of client-side state management bugs and makes it impossible for a voter to lose credentials by clearing their browser.

Session IDs have been removed from local storage entirely.


Frontend: Complete Redesign (Spring 2026)

The entire UI has been redesigned from scratch:

  • New design system — reusable components live in frontend/src/components/, each with a size prop (s, sm, m, ml, l, xl) and a color prop (primary, secondary, accent).
  • Multiple themes — theme selection is persisted in local storage.
  • Navbar and global theme switcher.
  • Preview page (/preview) — shows every component in all size and color combinations, making it easy to review the design system.
  • VoteSection / VoteOption — vote options use checkboxes (was radio buttons). Multiple options are grouped in a VoteSection component.
  • Is-authorized indicator — voters can see on the meeting page that they are correctly logged in to both server and trustauth.
  • Search field on the voter list panel.
  • Rejoin buttons on the landing page so returning users can quickly navigate back to their meeting.

New Features

BBS Crypto in the Browser

The BBS blind-signature protocol (@noble/curves) is now implemented entirely in the TypeScript frontend. The signature-dev developer page lets you simulate the full meeting workflow — from host keypair generation through voter registration and vote submission — entirely in the browser, which is useful for verifying the cryptographic flow without a running backend.

Encrypted Tally Persistence

Vote tallies are now saved to disk on the server as encrypted files:

  • Encryption uses 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. Timestamps replace the earlier epoch-based naming for readability and uniqueness.
  • Tally saving has been moved to /api/host/tally (was /api/host/get-tally).

Download All Tallies at Meeting Close

A Download tallies section on the close-meeting panel lets the host 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.

Meeting Deletion

A new endpoint (DELETE /api/meeting) allows the host to delete a meeting and discard all in-memory state.

Vote State Endpoints

New state-check endpoints let the client query the current vote state directly from the server. This removes the need for a client-side state machine and eliminates a whole category of state synchronisation bugs.


Performance & Reliability

Per-Field AsyncRwLock

Mutex has been replaced throughout rustsystem-server and rustsystem-trustauth with per-field AsyncRwLock. The two-level locking strategy (outer map lock + independent per-field locks inside each Meeting / RoundState) allows concurrent requests on different fields of the same meeting to proceed without blocking each other. Lock ordering rules are documented in the README to prevent deadlocks.

Error Handling

All fallible panics (unwrap, expect on non-trivial paths) have been removed. The entire codebase now uses the APIError / APIErrorCode result types throughout, with no anyhow, Box<dyn Error>, or io::Result leaking into handler code.


Testing

Test area What was added
Frontend signatures Unit tests for the BBS blind-signature workflow in TypeScript
Multi-profile E2E End-to-end tests exercising multiple browser profiles simultaneously
Server unit tests Additional unit tests covering the refactored server endpoints
Trustauth unit tests Basic coverage of trustauth handlers
Server ↔ trustauth E2E Integration tests exercising the full server-trustauth flow over mTLS
Frontend integration Integration tests updated and fixed for the new stateless architecture
Stress test Load test for concurrent meeting and voting operations

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


Logging

Structured logging has been added throughout the backend:

  • A custom MeetingLogLayer (tracing Layer) writes log events that contain 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 (I/O, crypto failures); info! is used for all normal flow events.

Documentation

  • A proper README has been added, covering the quick-start guide, architecture (with Mermaid diagrams), cryptography, RwLock structure, and deployment.
  • In-app Guide and Cryptography pages explain the system to end-users and technically curious voters.
  • Dev pages (/preview, /signature-dev) are locked behind a flag in production builds.

Infrastructure

  • The Dockerfile has been updated for the new two-service project structure.
  • Deployment scripts (deploy.sh) have been updated and tested for the new architecture.