Inkress ADRsarchitecture decisions
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 like kyc.approved) + record / record_id (the polymorphic target) + a JSON data payload, 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_at and 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 modify data. (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.
← 0036 Media is stored in S3 behind cdn.inkress.com, uploaded via the API with content-addressed dedup 0038 Inventory is an advisory checkout-time gate, never reserved or decremented →