Problem
The ballot creation flow is the foundation of everything in
AnonVote and it is incomplete at every layer. The backend route
accepts a title and deadline but does not validate option arrays,
does not enforce minimum or maximum option counts, and does not
anchor the ballot to Stellar on creation — meaning ballots exist
only in the database with no on-chain record. The frontend form
has no dynamic option management, no timezone-aware deadline
picker, and no feedback when creation fails. There are zero tests
covering ballot creation on either the backend or frontend.
Additionally the admin authentication flow has no session
expiry handling — an admin token issued 30 days ago is still
accepted as valid, which is a security gap that needs to be
closed as part of hardening this flow.
Solution
Implement the complete ballot creation and admin setup flow
so an admin can configure a ballot end to end, have it anchored
to Stellar on creation, and manage it from a properly secured
dashboard.
Backend
- Validate all ballot creation inputs — title length, minimum
2 and maximum 10 options, deadline must be at least 1 hour
in the future, all fields required
- Return structured field-level validation errors so the frontend
can highlight exactly which input failed
- Call create_ballot on the Soroban contract via Stellar SDK
immediately after database write — store the returned
transaction ID against the ballot record in the database
- If the Soroban call fails, do not roll back the database
write — instead mark the ballot as pending_anchor and queue
a background retry so ballot creation is never blocked by
a transient Stellar network issue
- Implement session expiry on admin JWT — tokens older than
8 hours must be rejected with a 401 and a SESSION_EXPIRED
error code
- Add GET /admin/ballots route that returns all ballots for
the authenticated admin with status, vote count, and
Stellar anchor status
Frontend
- Build the full ballot creation form — title input with
character count, dynamic option list with add and remove
controls, minimum 2 options enforced client-side, timezone
aware deadline picker that displays the admin's local time
but submits UTC
- Display field-level validation errors inline next to the
relevant input — not as a single generic toast
- Show Stellar anchor status on the ballot detail page —
anchored with transaction link, pending, or failed with
a manual retry button
- Handle SESSION_EXPIRED error globally — redirect to login
with a clear message rather than showing a broken state
- Build the admin ballot list with status tabs — draft,
active, closed — and a search input
Tests
- Unit tests for ballot creation validation — missing fields,
invalid deadline, too few options, too many options
- Unit test for JWT expiry enforcement — expired token returns
401 with SESSION_EXPIRED code
- Unit test for pending_anchor retry queue — Soroban failure
queues retry without rolling back database write
- Component tests for the ballot creation form — validation
errors display correctly, option add and remove work,
deadline picker submits UTC
- Integration test covering full creation flow from form
submission to Stellar anchor confirmation
Acceptance Criteria
Note for contributors
The pending_anchor retry mechanism is important — do not make
ballot creation dependent on Stellar network availability.
The database is the source of truth for ballot state. Stellar
is the auditability layer. A transient Stellar outage must
never prevent an admin from creating a ballot.
Problem
The ballot creation flow is the foundation of everything in
AnonVote and it is incomplete at every layer. The backend route
accepts a title and deadline but does not validate option arrays,
does not enforce minimum or maximum option counts, and does not
anchor the ballot to Stellar on creation — meaning ballots exist
only in the database with no on-chain record. The frontend form
has no dynamic option management, no timezone-aware deadline
picker, and no feedback when creation fails. There are zero tests
covering ballot creation on either the backend or frontend.
Additionally the admin authentication flow has no session
expiry handling — an admin token issued 30 days ago is still
accepted as valid, which is a security gap that needs to be
closed as part of hardening this flow.
Solution
Implement the complete ballot creation and admin setup flow
so an admin can configure a ballot end to end, have it anchored
to Stellar on creation, and manage it from a properly secured
dashboard.
Backend
2 and maximum 10 options, deadline must be at least 1 hour
in the future, all fields required
can highlight exactly which input failed
immediately after database write — store the returned
transaction ID against the ballot record in the database
write — instead mark the ballot as pending_anchor and queue
a background retry so ballot creation is never blocked by
a transient Stellar network issue
8 hours must be rejected with a 401 and a SESSION_EXPIRED
error code
the authenticated admin with status, vote count, and
Stellar anchor status
Frontend
character count, dynamic option list with add and remove
controls, minimum 2 options enforced client-side, timezone
aware deadline picker that displays the admin's local time
but submits UTC
relevant input — not as a single generic toast
anchored with transaction link, pending, or failed with
a manual retry button
with a clear message rather than showing a broken state
active, closed — and a search input
Tests
invalid deadline, too few options, too many options
401 with SESSION_EXPIRED code
queues retry without rolling back database write
errors display correctly, option add and remove work,
deadline picker submits UTC
submission to Stellar anchor confirmation
Acceptance Criteria
error responses
ID stored in the database
creation
SESSION_EXPIRED
for each ballot
of admin timezone
Note for contributors
The pending_anchor retry mechanism is important — do not make
ballot creation dependent on Stellar network availability.
The database is the source of truth for ballot state. Stellar
is the auditability layer. A transient Stellar outage must
never prevent an admin from creating a ballot.