Appearance
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-safesourceEventId, redacted metadata,metadataSchemaVersion=1, andretentionPolicy=security. - Admin operations audit projections sent by the optional admin-service sink with
sourceService=admin-service, replay-safesourceEventId=<admin audit log id>,category=ADMIN,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=admin-operations. - Wallet ledger projections sent by the optional wallet-service sink with
sourceService=wallet-service, replay-safesourceEventId=<ledger entry id>,category=DOMAIN,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=wallet-ledger. - Billing event projections sent by the optional billing-service sink with
sourceService=billing-service, replay-safesourceEventId=<billing event id>,category=DOMAIN,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=billing-events. - Payment event projections sent by the optional payment-service sink with
sourceService=payment-service, replay-safesourceEventId=<payment event id>,category=DOMAIN,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=payment-events. - Notification event projections sent by the optional notification-service sink with
sourceService=notification-service, replay-safesourceEventId=<notification event id>,category=DOMAIN,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=notification-events. - Profile event projections sent by the optional profile-service sink with
sourceService=profile-service, replay-safesourceEventId=<profile event id>,category=DATA,severity=INFO,metadataSchemaVersion=1, andretentionPolicy=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
| Method | Path | Purpose |
|---|---|---|
POST | /v1/audit-events | Append an audit event idempotently by id or (sourceService, sourceEventId). |
GET | /v1/audit-events | Query audit events by org, actor, account, source, category, severity, action, entity, outcome, date, retention, and legal hold. |
POST | /v1/audit-events/legacy-admin-log | Project 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/batch | Project already-read legacy AuditLog rows and return processed/created/replayed/failed counts plus per-row failures. |
GET | /v1/admin/operations/audit-compat | Internal 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-events | Append a security/auth event idempotently by id or (sourceService, sourceEventId). |
GET | /v1/security-events | Query 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-emptyretentionUntilat or before the timestamp.legalHoldBefore=<date-time>returns rows with non-emptylegalHoldUntilat 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-logis an internal projection/backfill route for a single already-read legacyAuditLogrow.POST /v1/audit-events/legacy-admin-log/batchaccepts up to 500 already-read legacy rows and reportsprocessed,created,replayed,failed, projecteditems, and per-rowfailures.It maps
id -> sourceEventId,organizationId,actorId,action,entity,entityId,metadata, andcreatedAt -> occurredAt.It writes
sourceService=admin-service,category=ADMIN,severity=INFO, andmetadataSchemaVersion=1.Replaying the same legacy
idreturns the original projected audit event and does not mutate it. Batch replay counts these rows asreplayed.GET /v1/admin/operations/audit-compatreads ADMIN audit events through audit-service and returns the legacy admin operations page shape withaction/entityfilters,limit/skip, newest-first rows, andcreatedAtmapped fromoccurredAt. For projected legacy rows,idis the original legacy/source id. The read requires api-gateway internal authentication plus a forwardedADMINprincipal; 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.jsonmapsGET /api/admin/operations/auditto audit-service/v1/admin/operations/audit-compatfor Compose.deploy/gateway/routes.audit-admin-compat-native-localhost-example.jsonmaps the same public route tohttp://localhost:8101for localgo run.make test-audit-admin-compat-routesverifies the non-default route examples, gateway behavior,ADMINrole guard, exact-method capture, query forwarding, and legacy fallback for writes/nested admin operations routes.make test-admin-audit-owner-browserstarts 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.jsonremains 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 200The 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=1Optional 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