Summary
The README states that audit logs record event counts only — no
identity, no token values. GET /api/audit/:ballotId returns audit
event counts. This is correct for the privacy model but insufficient
for tally verification. When a dispute arises about whether a specific
ballot's result is correct, the only verifiable source of truth is the
Stellar ledger — but there is no way to cross-reference a specific
Stellar transaction ID against a specific ballot event in the audit log
because the log stores counts, not structured event records.
The resultEngine.ts calls the tally and publishes results but the
Stellar transaction ID returned from the manageData write is not
stored anywhere accessible. It needs to be stored per ballot event so
it can be surfaced on the public verification page.
Scope
- Add a
ballot_events table to the Prisma schema with fields:
id, ballot_id, event_type (enum: ballot_created,
token_issued, vote_cast, result_published), stellar_tx_id,
created_at
- Add a compound index on
(ballot_id, created_at) to the table for
efficient retrieval of all events for a ballot in chronological order
- In each of the four service files, after a successful Stellar
manageData write, insert a row into ballot_events with the
returned transaction ID — wrap the Stellar write and the database
insert in a single operation so they succeed or fail together
- Update
GET /api/audit/:ballotId to return both the existing event
counts and an array of structured event records with event_type,
stellar_tx_id, and created_at — the stellar transaction IDs
must be included so anyone can verify each event independently on
the Stellar explorer
- Add a unit test confirming a
ballot_created event record is
written after ballot creation with a non-null stellar_tx_id
- Add a unit test confirming the audit route returns structured event
records alongside the existing counts
Relevant Files
backend/prisma/schema.prisma
backend/src/services/ballotEngine.ts
backend/src/services/identityManager.ts
backend/src/services/privacyEngine.ts
backend/src/services/resultEngine.ts
backend/src/routes/audit.ts
Acceptance Criteria
Out of Scope
- Switching from
manageData to Soroban contract calls
- Frontend audit display — a separate issue
Note for Contributors
Do not store any voter identifier, token value, or vote option in the
ballot_events table under any circumstances. The only data stored is
the event type, the Stellar transaction ID, and the timestamp. If you
find yourself adding any other field, stop and check against the privacy
model first.
Summary
The README states that audit logs record event counts only — no
identity, no token values.
GET /api/audit/:ballotIdreturns auditevent counts. This is correct for the privacy model but insufficient
for tally verification. When a dispute arises about whether a specific
ballot's result is correct, the only verifiable source of truth is the
Stellar ledger — but there is no way to cross-reference a specific
Stellar transaction ID against a specific ballot event in the audit log
because the log stores counts, not structured event records.
The
resultEngine.tscalls the tally and publishes results but theStellar transaction ID returned from the
manageDatawrite is notstored anywhere accessible. It needs to be stored per ballot event so
it can be surfaced on the public verification page.
Scope
ballot_eventstable to the Prisma schema with fields:id,ballot_id,event_type(enum:ballot_created,token_issued,vote_cast,result_published),stellar_tx_id,created_at(ballot_id, created_at)to the table forefficient retrieval of all events for a ballot in chronological order
manageDatawrite, insert a row intoballot_eventswith thereturned transaction ID — wrap the Stellar write and the database
insert in a single operation so they succeed or fail together
GET /api/audit/:ballotIdto return both the existing eventcounts and an array of structured event records with
event_type,stellar_tx_id, andcreated_at— the stellar transaction IDsmust be included so anyone can verify each event independently on
the Stellar explorer
ballot_createdevent record iswritten after ballot creation with a non-null
stellar_tx_idrecords alongside the existing counts
Relevant Files
backend/prisma/schema.prismabackend/src/services/ballotEngine.tsbackend/src/services/identityManager.tsbackend/src/services/privacyEngine.tsbackend/src/services/resultEngine.tsbackend/src/routes/audit.tsAcceptance Criteria
ballot_eventstable added to Prisma schema and migratedballot_eventsrow afterevery successful Stellar write
stellar_tx_idis never null in aballot_eventsrowGET /api/audit/:ballotIdreturns structured event recordswith
stellar_tx_idincludedOut of Scope
manageDatato Soroban contract callsNote for Contributors
Do not store any voter identifier, token value, or vote option in the
ballot_eventstable under any circumstances. The only data stored isthe event type, the Stellar transaction ID, and the timestamp. If you
find yourself adding any other field, stop and check against the privacy
model first.