Ledger audit trail¶
Backend domain: app/graphql/ledger_audit/
Migration: add_ledger_audit_logs
RBAC Path: AUDIT_LOG
Overview¶
Every LedgerEntry POST and REVERSE now writes a row into ledger_audit_logs. Rows are hash-chained: each row's row_hash is sha256(prev_hash || canonical_json(payload) || at || actor_id). A broken link or altered payload makes ledgerAuditChainVerify return isValid: false.
payload is a JSON object snapshot of what changed (entry number, date, memo, source type/id, and the line breakdown for new entries).
Setup prerequisites¶
None — the audit is emitted automatically by LedgerService. Migrations must be applied per tenant.
GraphQL surface¶
Queries¶
ledgerEntryHistory(entryId: UUID!): [LedgerAuditLog!]!
ledgerAuditChainVerify(
startSequence: Int = 1,
endSequence: Int = null
): LedgerAuditChainVerification!
LedgerAuditLog fields: id, sequenceNumber, entryId, lineId, operation (POSTED | REVERSED | EDITED_MEMO | CLEARED | UNCLEARED), actorId, at, payload (JSON scalar), prevHash, rowHash.
LedgerAuditChainVerification: isValid, startSequence, endSequence.
Mutations¶
None. The audit is write-only via the ledger service.
Behaviour to handle in the UI¶
- On any journal-entry detail page, add a History tab that calls
ledgerEntryHistory(entryId)and renders a timeline. Each row shows operation, actor (resolve viausersquery if you need full name), timestamp, and a pretty-printedpayload. payloadis a JSON scalar — render it with a collapsible JSON viewer rather than trying to map every field.- Add an Audit integrity badge in the global header for admin/accountant roles. Poll
ledgerAuditChainVerifyonce per session (or on demand from a Settings → Audit page). Show green whenisValid: true, red badge + actionable banner when false.
Suggested UX flow¶
- Entry history drawer — opens from any ledger entry showing the chain of POSTED → REVERSED events.
- Audit dashboard (admin only) — verify chain integrity, show last verified timestamp, expose a "Verify now" button that re-runs
ledgerAuditChainVerify. If invalid, surface the sequence range to investigate.
Permissions¶
Both queries require Path.AUDIT_LOG. Limit menu visibility to ACCOUNTANT and ADMINISTRATOR roles.
Operation coverage¶
| Operation | Emitter |
|---|---|
POSTED |
LedgerService.create_entry (every JE creation) |
REVERSED |
LedgerService.reverse_entry |
CLEARED |
BankReconciliationService.mark_cleared_ledger_lines |
UNCLEARED |
BankReconciliationService.unmark_cleared_ledger_lines |
EDITED_MEMO |
reserved — no current emitter; will fire from a future memo-edit mutation |
Open follow-ups (not yet implemented)¶
- No CSV export of the audit log yet; if a regulator asks, the data is already in
ledger_audit_logs— easy add.