All notable changes to AnonVote will be documented here.
-
Multi-Tenant Organization Isolation (#76)
- Row-Level Security (RLS): PostgreSQL RLS policies on all organization-scoped tables
- Tenant Context Middleware: Sets
app.current_organization_idsession variable per request - Per-Organization Encryption Keys: Each organization has unique encryption key for vote data
- Key Rotation: Support for rotating organization keys with versioning
- Audit Segregation: Audit logs isolated by organization with RLS enforcement
- Defense-in-Depth: Multiple security layers (RLS + app filters + encryption)
- Added
OrganizationKeymodel for storing encrypted organization keys - Added
organizationIdfield toAuditEventmodel - Created
backend/src/middleware/tenantContext.tsfor tenant isolation - Created
backend/src/services/organizationKeyService.tsfor key management - Added 25+ tenant isolation tests in
backend/src/tests/tenantIsolation.test.ts - Added 20+ encryption key tests in
backend/src/tests/organizationKeys.test.ts - Comprehensive documentation in
docs/TENANT_ISOLATION.md
-
Encryption Key Management API
POST /api/admin/organizations/:id/encryption-key- Create organization encryption keyPOST /api/admin/organizations/:id/rotate-keys- Rotate organization encryption keysGET /api/admin/organizations/:id/encryption-keys- List organization encryption keys- Organizations can only manage their own keys (enforced)
- Database-Level Isolation: RLS policies prevent cross-organization data access
- Encryption Isolation: Compromising one org's key doesn't expose others' data
- SQL Injection Protection: RLS blocks cross-tenant access even with SQL injection
- Fail-Secure Design: Security failures block requests rather than allowing access
- Compliance Ready: Supports GDPR, HIPAA, SOC 2, ISO 27001 requirements
- Startup Process: Server now ensures all organizations have encryption keys on startup
- Security Architecture: Multiple layers of protection (defense-in-depth)
- Test Coverage: 45+ security-focused test cases for tenant isolation
- Database migration
20260823212624_add_organization_keys_and_rlsadds:OrganizationKeytable for per-organization encryption keysorganizationIdtoAuditEvent(auto-populated from ballot relationship)- RLS policies on 11 tables (
Ballot,Session,AuditEvent,OrganizationKey,Vote,Option,Result,VoterToken,EligibilityList,EligibilityEntry) - Public read policies for voter access where needed
- Backward compatible - no breaking changes
-
Weighted Voting System
- Added
weightfield toEligibilityEntryandVotemodels - Added
allowWeightedVotingflag toBallotmodel - Updated ballot creation to accept weighted voting option
- Updated token issuance to include weight in response
- Updated vote submission to record weight
- Updated result calculation to use weighted votes
- Added UI toggle for weighted voting in CreateBallotPage
- Added
-
Delegated Voting System
- Added
delegatedFromanddelegatedTofields toVoterTokenmodel - Created
delegationManagerservice for vote delegation - Added
/api/delegationsendpoint for delegation operations - Updated privacyEngine to handle delegated votes
- Updated resultEngine to count delegated votes correctly
- Added
-
Multi-round / Ranked-Choice Voting
- Added
rankfield toVotemodel for storing voter rankings - Added
allowRankedChoiceandmaxRankingsfields toBallotmodel - Updated ballot creation with ranked-choice toggle and max rankings input
- Updated privacyEngine to handle ranked votes
- Added
-
Blind Vote Verification
- Created
verificationServicefor generating and verifying vote hashes - Added
/api/verification/generateendpoint for voters to get verification hash - Added
/api/verification/verifyendpoint for public vote verification - Verification hash allows voters to confirm their vote without exposing identity
- Created
-
Soroban Smart Contracts Service
- Created
sorobanServicefor contract deployment and interaction - Added
deployContract,callContract, andgetContractDatafunctions - Ready for future Soroban smart contract implementation
- Created
- Backend compilation — Added
--transpile-onlyflag to ts-node-dev for faster compilation - Logo consistency — Updated all logo references to use the simple 4-circle logo
- Icon visibility — Made calendar icon visible in both light and dark modes
- Prisma type errors — Fixed TypeScript errors in delegationManager.ts by adding type assertions
- IdentityManager weight — Fixed weight property error in identityManager.ts
-
New files:
backend/src/services/delegationManager.tsbackend/src/services/verificationService.tsbackend/src/services/sorobanService.tsbackend/src/routes/delegations.tsbackend/src/routes/verification.ts
-
Updated files:
backend/prisma/schema.prisma— Added weighted voting, delegation, and ranked-choice fieldsbackend/src/types.ts— Added new fields to typesbackend/src/services/ballotEngine.ts— Added weighted voting supportbackend/src/services/identityManager.ts— Added weight to token responsebackend/src/services/privacyEngine.ts— Added weight and rank supportbackend/src/services/resultEngine.ts— Updated to use weighted votesbackend/src/routes/ballots.ts— Added weighted voting optionbackend/src/routes/tokens.ts— Added weight to responsebackend/src/routes/votes.ts— Added rank supportbackend/src/app.ts— Added new route handlersfrontend/src/types/index.ts— Added new fields to typesfrontend/src/api/client.ts— Added new API parametersfrontend/src/pages/CreateBallotPage.tsx— Added weighted voting and ranked-choice UIfrontend/src/pages/VotePage.tsx— Updated to handle weighted votesfrontend/public/favicon.svg— Updated to simple logofrontend/src/components/Navbar.tsx— Updated logofrontend/src/components/PageLoader.tsx— Updated logofrontend/src/styles/theme.css— Fixed icon visibility
-
Migrations:
20260503162041_add_weighted_voting20260503172112_add_delegated_voting20260503173305_add_ranked_choice_voting
- Complete frontend UI redesign — Dark and light mode support with theme toggle
- New design system — Space Grotesk (headings), DM Sans (body), JetBrains Mono (monospace)
- Framer-inspired dark mode — Electric blue accents (#1c7ed6), pure black canvas (#000000), engineered precision
- Apple-inspired light mode — Clean surfaces, premium white space (#f5f5f7), ink type hierarchy
- Improved error and success message components — Consistent design system classes with proper icon sizing
- Sticky navbar — Fixed at top with frosted glass effect (backdrop-filter blur)
- New footer — Two-column layout with copyright/credit and navigation links
- Password visibility toggle — Eye icon on login and registration pages
- Input field icon fixes — Icons now correctly positioned inside fields
- 401 interceptor — No longer redirects on public routes (login, register, token request)
- Theme persistence — Theme toggle now persists across sessions via localStorage
- Dark mode toggle — Manual toggle in navbar with sun/moon icon, persists user preference
- PageLoader component — Animated SVG loader with rose curve pattern and particle trail
- Input field icons now correctly positioned inside fields
- 401 interceptor no longer redirects on public routes
- Theme toggle now persists across sessions via localStorage
- Organization registration and login — Secure authentication with bcrypt password hashing
- Ballot creation — Dynamic options, eligibility list upload, voting deadline
- Anonymous token-based voting — One-time tokens with SHA-256 hashing
- Results page — Vote counts and percentages with visual breakdown
- Stellar blockchain verification — Tamper-proof results with public transaction IDs
- Audit page — Ballot transparency with event tracking
- AES-256-GCM encryption — Vote payloads encrypted with organization-specific key
- SHA-256 voter token hashing — No raw tokens stored in database
- Eligibility list upload — CSV/plain-text validation with injection prevention
- Rate limiting — Strict rate limiter (3 req/min) for vote submission
- Session management — JWT-based sessions with 8-hour expiration
- Audit event tracking — TOKEN_ISSUED, VOTE_CAST, RESULT_PUBLISHED events
- Duplicate attempt detection — Prevents token reuse and vote duplication
- Backend scaffolding — Express.js with TypeScript
- Prisma ORM setup — PostgreSQL with Supabase connection
- Core services — Ballot engine, privacy engine, result engine, identity manager
- Stellar integration — Testnet blockchain for immutable audit trail
- Frontend scaffolding — React with Vite, TypeScript, Tailwind CSS
- API client — TypeScript client with axios interceptors
- Theme context — Dark/light mode toggle with localStorage persistence
-
Notification system — App-wide notification management with
NotificationContextNotificationContext.tsx— Stores notifications in state with types:ballot_created,ballot_closed,results_published,token_requested,warningNotificationDropdown.tsx— Reusable notification dropdown componentuseNotifications()hook — Access notifications, unread count, mark all as read, add notification- LocalStorage persistence for notifications
- 3 seed notifications on first load
-
Navbar redesign — Replaced standalone Logout button and theme toggle with new elements:
- Notification Bell — Bell icon with red dot indicator for unread notifications
- Clicking opens notification dropdown overlay (no page navigation)
- Shows up to 10 most recent notifications
- "Mark all as read" button at top
- Click-outside detection to close dropdown
- User Avatar / Profile Button — Circular avatar showing first letter of org name
- Background:
var(--brand-primary), text: white, font:var(--font-display) - Dropdown with: Profile & Settings, Theme toggle, Logout
- Click-outside detection to close dropdown
- Background:
- Notification Bell — Bell icon with red dot indicator for unread notifications
-
Auth state enhancement — Added
orgEmailtouseAuth()hook- Fetches email from
getMe()API response - Updated all state updates to include
orgEmail
- Fetches email from
-
Consistent dropdown styling — Both notification and profile dropdowns use same base classes:
navbar-dropdown— Shared positioning, z-index, animationnavbar-dropdown-item— Consistent hover effects and transitionsnavbar-dropdown-divider— Uniform divider stylingnavbar-avatar— Circular avatar with hover opacity effectnavbar-bell— Bell icon with consistent hover behaviornavbar-bell-dot— Red dot indicator for unread notifications
-
Profile dropdown — Now uses
orgEmailfrom auth state instead of hardcodedorg@example.com
-
New files:
frontend/src/context/NotificationContext.tsxfrontend/src/components/NotificationDropdown.tsx
-
Updated files:
frontend/src/hooks/useAuth.ts— AddedorgEmailto auth statefrontend/src/components/Navbar.tsx— New notification bell and avatar dropdownfrontend/src/components/Navbar.css— New dropdown classes and stylesfrontend/src/App.tsx— Wrapped withNotificationProvider
-
Settings page — Full settings dashboard at
/settingsroute- Two-column layout: 240px fixed sidebar + scrollable content area
- 6 settings sections: Profile, Appearance, Stellar, Security, Danger Zone, Contact Support
- Active section tracked with
useState- no page navigation, everything renders inline - Responsive: single column on mobile
-
Profile section — Manage organization information
- Profile picture card with 80px circular avatar
- Upload photo and Remove buttons
- Organization Details card with inline editing for name and email
- Account ID with copy button
- Member Since date display
updateOrgAPI function for saving changes
-
Appearance section — Customize AnonVote appearance
- Theme card with Light/Dark mode cards (icon, label, description, checkmark)
- Accent Color card with 6 color swatches (Indigo, Blue, Emerald, Violet, Rose, Amber)
- Font Size card with Small/Default/Large options
- localStorage persistence for theme, accent color, and font size
- Dynamic theming via
document.documentElement.style.setProperty
-
Stellar section — Manage blockchain configuration
- Network card with Testnet/Mainnet toggle (badge-open/badge-closed styles)
- Stellar Expert URL with external link icon
- Transaction Signing status with green dot
- Stellar Account card with Public Key, Last Transaction, Total Transactions
-
Security section — Keep account safe
- Change Password card with 3 fields and validation (min 8 chars, passwords match)
- Active Sessions card with browser info and "Sign out all other sessions" button
- Two-Factor Authentication card with "Not enabled" status and "Enable 2FA" button
changePasswordAPI function
-
Danger Zone section — Irreversible actions
- Delete All Ballots card with inline confirmation
- Delete Account card with inline confirmation
- Red outlined button for "Delete All Ballots"
- Red primary button for "Delete Account"
deleteAccountAPI function- Clears localStorage and navigates to
/loginon success
- AuditTable.tsx — Fixed JSX structure issue with broken
<a>tag
-
New files:
frontend/src/pages/SettingsPage.tsxfrontend/src/pages/SettingsPage.cssfrontend/src/context/NotificationContext.tsxfrontend/src/components/NotificationDropdown.tsx
-
Updated files:
frontend/src/hooks/useAuth.ts— AddedorgEmailto auth statefrontend/src/api/client.ts— AddedupdateOrg,changePassword,deleteAccountfunctionsfrontend/src/App.tsx— Added/settingsroute, wrapped withNotificationProviderfrontend/src/components/Navbar.tsx— New notification bell and avatar dropdownfrontend/src/components/Navbar.css— New dropdown classes and stylesfrontend/src/components/AuditTable.tsx— Fixed JSX structure