feat(ledger): write audit_events row per ledger write - #135
Merged
Conversation
added 3 commits
June 15, 2026 11:20
Write one immutable platform.audit_events row inside the business transaction for account create, rename, status change, transaction post and reversal. Capture actor (JWT subject), correlation id (MDC), action, resource type and id, result and the SHA-256 request hash; the reversal row carries the reason and compensating transaction id in a new nullable JSONB payload column (migration 019). The audit write reuses the active-transaction guard so a rolled-back operation leaves no row. Failure and denied auditing are tracked separately. Closes #49
…lation Import AuditTrailWriterImpl into the @DataJpaTest contexts that pull in AccountServiceImpl or TransactionPoster (they now require the audit writer), adding an ObjectMapper bean to the idempotency IT. Stop deleting platform.audit_events rows directly: the append-only trigger rejects DELETE, so the writer ITs assert per-action row counts under auto-rollback and the standalone writer IT truncates between tests. Replace the unfinished retry-topology stub with a real happy-path drive through the idempotency to poster chain.
Integration tests run in parallel forks against one shared Postgres container, so audit rows committed by the standalone writer IT and the full-context API ITs are visible to the auto-rollback ITs reading in parallel. Assert by the unique resource id (and action) of each case instead of global or action-only counts, parse the JSONB payload rather than matching its exact text (Postgres reformats it on read), and stop truncating in the writer IT to avoid cross-fork lock contention.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Writes one immutable
platform.audit_eventsrow inside the business transaction for each state-changing ledger operation.What
@Transactionalboundary, so a rolled-back operation leaves no row (atomic).actor_id(JWT subject),correlation_id(from MDC, generated fallback when absent),action,resource_type,resource_id,result(SUCCESS),request_hash(SHA-256 of the request body for create/post/reverse, null for the body-less status change and rename).payload JSONBcolumn (migration019) stores the compensating transaction id and the optionalreason; the reverse endpoint gains an optional request body carryingreason.(String) -> StoredResponseso the already-computed request hash reaches the service without recomputation.Scope
Success-path auditing only. Two follow-ups were split out deliberately:
AccessDeniedHandler, a different mechanism).Verification
:test :compileIntegrationTestKotlin :detekt :spotlessCheck :assemble(integrationTest runs on CI).019is idempotent (ADD COLUMN IF NOT EXISTS), nullable, registered once after018; the append-only trigger is unaffected.Closes #49