Skip to main content

Audit Service — Local Dev Setup

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template

1. Prerequisites

ToolVersionPurpose
Node.js22 LTSRuntime
pnpm9.xPackage manager
Docker Desktop4.xCompose stack
nats-clilatestStream inspection
MinIO Client (mc)latestObject storage inspection

2. docker-compose.yml (audit-service dev stack)

version: "3.9"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: audit_dev
POSTGRES_USER: audit_app
POSTGRES_PASSWORD: dev_password
ports:
- "5433:5432"
volumes:
- audit_pg_data:/var/lib/postgresql/data
- ./docker/init.sql:/docker-entrypoint-initdb.d/init.sql

nats:
image: nats:2.10-alpine
command: ["--jetstream", "--store_dir=/data"]
ports:
- "4222:4222"
- "8222:8222"
volumes:
- audit_nats_data:/data

minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data

volumes:
audit_pg_data:
audit_nats_data:
minio_data:

The docker/init.sql script creates the audit_app role with INSERT-only permissions on audit_entries (mirrors production).

3. Environment variables (.env.example)

NODE_ENV=development
PORT=3006
LOG_LEVEL=debug

DATABASE_URL=postgresql://audit_app:dev_password@localhost:5433/audit_dev
DATABASE_SCHEMA=audit

NATS_URL=nats://localhost:4222
NATS_CONSUMER_NAME=audit-service-dev

OBJECT_STORAGE_ENDPOINT=http://localhost:9000
OBJECT_STORAGE_ACCESS_KEY=minioadmin
OBJECT_STORAGE_SECRET_KEY=minioadmin
OBJECT_STORAGE_BUCKET=audit-exports

KEYCLOAK_REALM_URL=http://localhost:8080/realms/dev
JWT_PUBLIC_KEY_PATH=./docker/dev-jwt-public.pem

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=audit-service

CHAIN_INTEGRITY_JOB_CRON=0 2 * * *
EXPORT_SIGNED_URL_TTL_SECONDS=3600

4. Start the dev stack

# 1. Start dependencies
docker compose up -d postgres nats minio

# 2. Create MinIO bucket
mc alias set local http://localhost:9000 minioadmin minioadmin
mc mb local/audit-exports

# 3. Install dependencies
pnpm install

# 4. Apply migrations
pnpm db:migrate

# 5. Seed with test events
pnpm db:seed

# 6. Start service in watch mode
pnpm dev

5. Database commands

CommandDescription
pnpm db:migrateApply Drizzle migrations
pnpm db:migrate:dryShow SQL without executing
pnpm db:seedInsert synthetic audit entries across 2 test tenants
pnpm db:verify-chainRun chain-hash integrity check against dev DB
pnpm db:studioOpen Drizzle Studio at localhost:4983

6. Common development commands

CommandDescription
pnpm devNestJS watch mode
pnpm buildTypeScript compile
pnpm testAll tests
pnpm test:unitUnit only
pnpm test:integrationIntegration (requires compose stack)
pnpm test:coverageCoverage report
pnpm lintESLint
pnpm typechecktsc --noEmit

7. Seed data overview

EntityCountNotes
Tenants2ten_dev_a, ten_dev_b for isolation testing
AuditEntry rows500 per tenantMix of all event types; realistic timestamps over 30 days
AuditExport jobs2One completed, one queued

8. Publish a test event (NATS)

# Simulate a USER_SUSPENDED event from identity-service
nats pub com.ghasi-ehr.iam.user.suspended '{
"id": "evt_test_001",
"type": "com.ghasi-ehr.iam.user.suspended",
"source": "identity-service",
"specversion": "1.0",
"time": "2026-04-18T10:00:00Z",
"data": {
"tenantId": "ten_dev_a",
"actorId": "usr_admin001",
"resourceId": "usr_test_001",
"reason": "Policy violation"
}
}'

# Verify ingestion
nats stream view AUDIT_DEV --count 1

9. Troubleshooting

IssueResolution
INSERT denied on audit_entriesCheck docker/init.sql creates audit_app role with INSERT-only
Chain-hash mismatch in devRun pnpm db:verify-chain; likely seed order issue; run pnpm db:reset
MinIO bucket not foundRun mc mb local/audit-exports manually
NATS subscription not receivingCheck wildcard consumer is created: nats consumer ls AUDIT_DEV