Skip to content

Audit Service

audit-service owns compliance-grade append-only audit and security event history for native Go services. It is a downstream event sink, not a domain decision maker.

Audit-service now has an internal admin operations audit compatibility preview. A non-default gateway route rehearsal maps GET /api/admin/operations/audit to that preview. A local browser proof exists, but the default public route remains on the existing admin-service compatibility path until legacy backfill, response parity, and rollback evidence explicitly promote audit-service.

Boundary

Owns:

  • Append-only audit events with actor, account, action, entity, source service, source event id, outcome, severity, category, request/correlation IDs, metadata schema version, retention policy, legal hold, content hash, and metadata fields.
  • Append-only security events with principal, event type, risk level, success, reason, account, source service, source event id, request/correlation IDs, metadata schema version, retention policy, legal hold, content hash, and metadata fields.
  • IAM security-event projections sent by the optional IAM sink with sourceService=iam-service, replay-safe sourceEventId, redacted metadata, metadataSchemaVersion=1, and retentionPolicy=security.
  • Admin operations audit projections sent by the optional admin-service sink with sourceService=admin-service, replay-safe sourceEventId=<admin audit log id>, category=ADMIN, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=admin-operations.
  • Wallet ledger projections sent by the optional wallet-service sink with sourceService=wallet-service, replay-safe sourceEventId=<ledger entry id>, category=DOMAIN, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=wallet-ledger.
  • Billing event projections sent by the optional billing-service sink with sourceService=billing-service, replay-safe sourceEventId=<billing event id>, category=DOMAIN, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=billing-events.
  • Payment event projections sent by the optional payment-service sink with sourceService=payment-service, replay-safe sourceEventId=<payment event id>, category=DOMAIN, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=payment-events.
  • Notification event projections sent by the optional notification-service sink with sourceService=notification-service, replay-safe sourceEventId=<notification event id>, category=DOMAIN, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=notification-events.
  • Profile event projections sent by the optional profile-service sink with sourceService=profile-service, replay-safe sourceEventId=<profile event id>, category=DATA, severity=INFO, metadataSchemaVersion=1, and retentionPolicy=profile-events.
  • Query filters for compliance review and retention scans.

Does not own:

  • Feature maintenance, moderation, admin dashboards, support workflows, or source domain writes. Those stay in owner services or admin-service.
  • IAM credentials, sessions, roles, or permission checks.
  • Wallet/payment/billing ledger decisions.
  • Public route compatibility until a gateway cutover task explicitly adds it.

Native Routes

MethodPathPurpose
POST/v1/audit-eventsAppend an audit event idempotently by id or (sourceService, sourceEventId).
GET/v1/audit-eventsQuery audit events by org, actor, account, source, category, severity, action, entity, outcome, date, retention, and legal hold.
POST/v1/audit-events/legacy-admin-logProject one read-only legacy AuditLog row into audit-service with sourceService=admin-service, sourceEventId=<legacy AuditLog id>, and createdAt -> occurredAt.
POST/v1/audit-events/legacy-admin-log/batchProject already-read legacy AuditLog rows and return processed/created/replayed/failed counts plus per-row failures.
GET/v1/admin/operations/audit-compatInternal compatibility preview for the legacy admin operations audit list. Requires X-Internal-Service: api-gateway, the shared internal token, and X-User-Id/X-User-Role: ADMIN; preserves action, entity, limit, skip, newest-first {items,total,skip,limit}, legacy/source id, Vietnamese metadata labels, and createdAt mapped from occurredAt.
POST/v1/security-eventsAppend a security/auth event idempotently by id or (sourceService, sourceEventId).
GET/v1/security-eventsQuery security events by principal, account, source, type, risk, success, date, retention, and legal hold.

Legacy Evidence

Legacy AuditLog has stable organizationId, actorId, action, entity, entityId, metadata, and createdAt fields. The current public GET /api/admin/operations/audit list shape supports action, entity, limit, and skip, returns newest-first {items,total,skip,limit}, and is still covered by the admin-service route rehearsal until a separate compatibility task moves it.

Native legacy projection:

Compliance scans:

  • retentionBefore=<date-time> returns rows with non-empty retentionUntil at or before the timestamp.

  • legalHoldBefore=<date-time> returns rows with non-empty legalHoldUntil at or before the timestamp. It is available on both audit and security event list routes for retention/legal-hold review jobs.

  • POST /v1/audit-events/legacy-admin-log is an internal projection/backfill route for a single already-read legacy AuditLog row.

  • POST /v1/audit-events/legacy-admin-log/batch accepts up to 500 already-read legacy rows and reports processed, created, replayed, failed, projected items, and per-row failures.

  • It maps id -> sourceEventId, organizationId, actorId, action, entity, entityId, metadata, and createdAt -> occurredAt.

  • It writes sourceService=admin-service, category=ADMIN, severity=INFO, and metadataSchemaVersion=1.

  • Replaying the same legacy id returns the original projected audit event and does not mutate it. Batch replay counts these rows as replayed.

  • GET /v1/admin/operations/audit-compat reads ADMIN audit events through audit-service and returns the legacy admin operations page shape with action/entity filters, limit/skip, newest-first rows, and createdAt mapped from occurredAt. For projected legacy rows, id is the original legacy/source id. The read requires api-gateway internal authentication plus a forwarded ADMIN principal; direct, forged, missing, or non-ADMIN headers are rejected.

  • It does not read from or mutate the legacy database. Backfill jobs must read legacy rows externally and post them to the single-row or batch projection route.

Gateway rehearsal:

  • deploy/gateway/routes.audit-admin-compat-native-example.json maps GET /api/admin/operations/audit to audit-service /v1/admin/operations/audit-compat for Compose.
  • deploy/gateway/routes.audit-admin-compat-native-localhost-example.json maps the same public route to http://localhost:8101 for local go run.
  • make test-audit-admin-compat-routes verifies the non-default route examples, gateway behavior, ADMIN role guard, exact-method capture, query forwarding, and legacy fallback for writes/nested admin operations routes.
  • make test-admin-audit-owner-browser starts a fresh audit Postgres cluster, temporary audit-service/Gateway/Admin Vite processes, then proves the Admin Operations audit tab renders the owner response through the exact candidate route. It removes the temporary cluster and processes on exit.
  • The default deploy/gateway/routes.json remains legacy until browser/runtime proof, backfill proof, response parity, and rollback evidence are recorded.

Legacy reader command:

bash
LEGACY_DATABASE_URL='postgres://legacy-readonly:...' \
AUDIT_SERVICE_URL='http://localhost:8080' \
GOTOOLCHAIN=go1.25.11 go run ./services/audit-service/cmd/legacy-admin-audit-backfill \
  -batch-size 200

The command reads "AuditLog" in read-only transactions, posts batches to /v1/audit-events/legacy-admin-log/batch, and prints the aggregate report. Use -dry-run to count legacy rows without posting.

Verification

bash
GOTOOLCHAIN=go1.25.11 go test ./services/audit-service/... -count=1

Optional Postgres repository integration:

bash
AUDIT_SERVICE_POSTGRES_TEST_DATABASE_URL='postgres://hoctapaz:hoctapaz@localhost:5433/hoctapaz_platform?sslmode=disable' \
  GOTOOLCHAIN=go1.25.11 go test ./services/audit-service/internal/repository \
  -run TestPostgresAuditAndSecurityIntegration -count=1 -v

Go-platform documentation is generated from repository Markdown.