Overview / Platform / audit
ADR 0037
commerce-api keeps a general, app-written audit trail (append-only by convention)
AcceptedPlatform / audit
Context
Operationally sensitive actions across commerce-api — KYC decisions, fee changes, order and merchant mutations, ledger events — need an audit trail answering who did what, when, to which record. This is distinct from the payments control-plane's tamper-evident hash-chained ledger (ADR 0007) and from the money ledger (ADR 0029); it is the business/operational trail.
Decision
Write a single, general audit_logs table explicitly from application code, append-only by convention, with a polymorphic shape and auto-captured request context.
- One polymorphic table. Each entry is
event(a string likekyc.approved) +record/record_id(the polymorphic target) + a JSONdatapayload, tenant-scoped. Any record type can be audited without a per-type table. - Explicit, app-level writes. Domains call the audit writer at the moments that matter (KYC, fees, orders, merchant, ledger), so each entry captures business intent (the event name), not just a raw row diff.
- Append-only by convention. The table has no
updated_atand the normal path only inserts; request IP and user-agent are captured once per request and stamped on the entry.
Consequences
- Positive: a uniform, queryable audit trail spanning domains, keyed by meaningful event names, with actor context attached automatically.
- Neutral / to revisit: append-only here is a convention, not a database guarantee — there is no trigger and no revoked
UPDATE/DELETE, and a privileged (secure: true) changeset can still modifydata. (Contrast the payments ledger, ADR 0007, where append-only is enforced and hash-chained.) If the commerce-api trail ever needs to be tamper-evident, that enforcement would have to be added. - Neutral / to revisit: because writes are explicit, a code path that forgets to log leaves no trail — coverage is as good as the call sites.
Alternatives considered
- DB triggers / change-data-capture for automatic row auditing: rejected — automatic capture records raw diffs, not business intent, and hides the logic in the database; explicit app writes name the event, at the cost of relying on discipline.
- DB-enforced immutability (revoke UPDATE/DELETE, hash chain): not adopted for this trail (it is used for the payments ledger, ADR 0007) — the operational trail is convention-only for now.
- Reuse the money ledger as the audit log: rejected — the ledger records money movements; a general audit trail is a different concern with different shape and consumers.