Skip to content

audit-service Agent Tasks

Agent workflow: follow README.md for Audit -> Investigate -> Code -> Test -> Fix; keep this pack's writable scope and verification commands authoritative.

Dispatch type: per-service

Dispatch ID: SVC-021

Current implementation: services/audit-service foundation exists for internal append-only audit/security events. Admin audit compatibility still exists in services/admin-service.

Owns: compliance/security append-only event history, source idempotency, retention/legal-hold metadata, and compliance query filters.

Must read:

  • docs/agents/handoff-phase-9-admin-foundation.md
  • legacy audit/admin operation log modules: /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/admin/operations.controller.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/admin/feature-maintenance.service.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma.

Legacy source evidence:

  • /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/admin/**
  • audit/security/event rows in /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma.

Writable files:

  • services/audit-service/**
  • contracts/openapi/services/audit-service.yaml
  • docs/api/audit-service.md

Orchestrator-owned files:

  • producer event contracts in owner services, admin-service compatibility routes, gateway route cutovers, and shared deploy manifests unless delegated.

Delegated gateway verification scope:

  • Agents may maintain only the non-default audit compatibility rehearsal in deploy/gateway/routes.audit-admin-compat-native-example.json and deploy/gateway/routes.audit-admin-compat-native-localhost-example.json; the default /api/admin/operations/audit route remains admin-service backed until orchestrator promotion evidence exists.
  • Route proof must cover GET /api/admin/operations/audit to /v1/admin/operations/audit-compat through scripts/test/audit-admin-compat-route-coverage.sh, with QA evidence under docs/qa/audit-admin-compat-*.md.
  • Makefile target: test-audit-admin-compat-routes.

Initial tasks:

  • Done: define event ingestion, retention metadata, and native OpenAPI contract.
  • Done: add audit/security append/list APIs, source event replay handling, and Postgres append-only triggers.
  • Done: add optional IAM security-event projection sink wiring. IAM posts redacted security events to audit-service with sourceService=iam-service, sourceEventId=<iam security event id>, metadataSchemaVersion=1, and retentionPolicy=security while keeping IAM local logs authoritative and best-effort projection non-blocking.
  • Add remaining producer adapter slices for organization membership sync and admin broadcast/gateway route operations. Each producer must send versioned payloads with sourceService, sourceEventId, request/correlation ids, actor/account ids, action/entity/outcome, and metadata schema version.
  • Done: add the first admin operations producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, admin-service posts new native admin audit logs to /v1/audit-events using sourceService=admin-service, sourceEventId=<admin audit log id>, category=ADMIN, severity=INFO, retentionPolicy=admin-operations, metadataSchemaVersion=1, and createdAt -> occurredAt without blocking the admin write path.
  • Done: add the first wallet producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, wallet-service posts successful ledger entries to /v1/audit-events using sourceService=wallet-service, sourceEventId=<ledger entry id>, category=DOMAIN, severity=INFO, outcome=SUCCESS, retentionPolicy=wallet-ledger, metadataSchemaVersion=1, and createdAt -> occurredAt without blocking wallet balance movements.
  • Done: add the first billing producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, billing-service posts billing state events to /v1/audit-events using sourceService=billing-service, sourceEventId=<billing event id>, category=DOMAIN, severity=INFO, outcome=SUCCESS, retentionPolicy=billing-events, metadataSchemaVersion=1, and createdAt -> occurredAt. This stays separate from the usage/wallet outbox so audit-service failures do not trigger entitlement or wallet delivery retries.
  • Done: add the first payment producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, payment-service posts persisted payment events to /v1/audit-events using sourceService=payment-service, sourceEventId=<payment event id>, category=DOMAIN, severity=INFO, retentionPolicy=payment-events, metadataSchemaVersion=1, and createdAt -> occurredAt. This stays separate from the billing/wallet event outbox so audit-service failures do not retry monetization fanout.
  • Done: add the first notification producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, notification-service posts newly persisted notification events to /v1/audit-events using sourceService=notification-service, sourceEventId=<notification event id>, category=DOMAIN, severity=INFO, retentionPolicy=notification-events, metadataSchemaVersion=1, and createdAt -> occurredAt. Source event replays return the original notification event and do not publish another audit row; provider delivery attempts stay separate diagnostics.
  • Done: add the first profile producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, profile-service posts audit-shaped profile mutation events to /v1/audit-events using sourceService=profile-service, sourceEventId=<profile event id>, category=DATA, severity=INFO, retentionPolicy=profile-events, metadataSchemaVersion=1, request/correlation ids, optional gateway actor context, and local occurredAt. The slice adds profile.snapshot.upserted without making profile role/status projections authoritative for access control.
  • Done: add the first gateway route-operation producer slice. When configured with AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL, api-gateway posts matched client-facing route outcomes to /v1/audit-events using sourceService=api-gateway, sourceEventId=gateway:route:<requestId>, category=OPERATIONS, action=gateway.route.request, entity=GatewayRoute, severity=INFO|WARNING|ERROR, outcome=SUCCESS|DENIED|FAILED|ERROR, retentionPolicy=gateway-route-operations, metadataSchemaVersion=1, and redacted route metadata containing query keys only. Disabled or failed audit projection does not block gateway responses, and shadow reads record only the client-facing route decision.
  • Done: add a native legacy AuditLog compliance projection surface before public route migration. POST /v1/audit-events/legacy-admin-log maps action, entity, entityId, organizationId, actorId, metadata, and createdAt -> occurredAt into audit_events, uses sourceService=admin-service plus sourceEventId=<legacy AuditLog id> for idempotency, and keeps the legacy database read-only.
  • Done: add a legacy backfill report wrapper around the projection route. POST /v1/audit-events/legacy-admin-log/batch accepts already-shaped legacy rows, reports processed/created/replayed/failed counts and per-row failures, and never mutates the legacy database.
  • Done: add the external legacy reader command services/audit-service/cmd/legacy-admin-audit-backfill. It reads legacy "AuditLog" rows in read-only transactions, posts already-shaped rows to audit-service, prints the batch report, supports -dry-run, and never mutates the legacy database.
  • Done: add a separate admin audit compatibility preview before moving /api/admin/operations/audit from admin-service to audit-service. GET /v1/admin/operations/audit-compat preserves action, entity, limit, skip, newest-first {items,total,skip,limit}, legacy/source id, Vietnamese metadata labels, and createdAt mapped from occurredAt while the public route still rolls back to the existing admin-service rehearsal.
  • Done locally: add a non-default audit-service route-table and gateway rehearsal for GET /api/admin/operations/audit to /v1/admin/operations/audit-compat. make test-audit-admin-compat-routes validates the Compose and localhost examples, keeps the default route table legacy, requires an ADMIN gateway principal, and leaves writes/nested admin operations routes on legacy. Backfill evidence and rollback rehearsal are still required before promoting the public route to audit-service.
  • Done locally: GET /v1/admin/operations/audit-compat now accepts only api-gateway with the shared internal token and a forwarded ADMIN principal. make test-admin-audit-owner-browser creates a fresh audit-only Postgres cluster, runs temporary audit-service/Gateway/Admin Vite processes, proves one ADMIN row reaches the Admin Operations audit tab through the exact candidate route, rejects STUDENT and anonymous Gateway requests, and removes the temporary resources on exit. This guard is intentionally limited to the compatibility read; producer write routes are unchanged in this slice.
  • Keep future IAM/security/gateway projection slices focused on outbox-backed retry, richer actor/request context, organization-membership audit semantics, backfill reports, and runtime evidence; the current sinks are best-effort and do not replace those cutover requirements.
  • Add backfill/report tasks for legacy security-event rows without mutating the legacy database.
  • Done: add retention/legal-hold scan filters separately from public admin route compatibility. GET /v1/audit-events and GET /v1/security-events accept retentionBefore and legalHoldBefore for compliance review jobs without mutating append-only rows.

Acceptance:

  • Audit is append-only and queryable by actor/entity/action/date.
  • Domain services emit audit events without sharing DBs.
  • Split from admin-service has a migration/compatibility plan.
  • Producer adapters are idempotent by sourceService + sourceEventId, and replay does not mutate existing audit/security rows.
  • Admin-service can retain temporary compatibility reads, but compliance-grade audit storage remains audit-service-owned.
  • IAM-sourced security events never include raw passwords, reset tokens, Google tokens, refresh tokens, or provider secrets.
  • Public /api/admin/operations/audit remains on admin-service compatibility routes until audit-service has rollback evidence, legacy response parity, and real or production-like backfill/report evidence for the promoted slice.

Verification:

  • Current: GOTOOLCHAIN=go1.25.11 go test ./services/audit-service/...
  • make test-audit-admin-compat-routes
  • make test-admin-audit-owner-browser
  • AUDIT_REPLAY_RETENTION_POSTGRES_CONFIRM=disposable-postgres make test-audit-replay-retention-postgres
  • The confirmed Postgres runner retains only a focused Go test log and non-secret summary.json under output/qa/audit-replay-retention-postgres-*.
  • Optional Postgres: set AUDIT_SERVICE_POSTGRES_TEST_DATABASE_URL, then run GOTOOLCHAIN=go1.25.11 go test ./services/audit-service/internal/repository -run TestPostgresAuditAndSecurityIntegration -count=1 -v
  • Future producer/backfill/compatibility slices must add focused replay and filter tests plus route-table guards before any public audit route cutover.

Go-platform documentation is generated from repository Markdown.