regulator-portal-service — Local Dev Setup
Version: 1.0 Status: Draft Owner: Regulator-facing + DevEx Last Updated: 2026-04-21 References: SERVICE_OVERVIEW.md, DEPLOYMENT_TOPOLOGY.md, TESTING_STRATEGY.md
1. Prerequisites
| Tool | Version |
|---|---|
| Node.js | 20 LTS |
| pnpm | 9.x |
| Docker Engine | 24+ |
openssl | 3.x (for test PKI) |
psql | 16 |
nats CLI | 0.1.5+ |
curl with --cert flag support | — |
2. Docker Compose
infra/docker/docker-compose.regulator-portal.yml:
version: '3.9'
services:
postgres-reg:
image: postgres:16-alpine
environment:
POSTGRES_DB: regulator
POSTGRES_USER: regulator
POSTGRES_PASSWORD: regulator
ports: ["5448:5432"]
volumes:
- reg_pg_data:/var/lib/postgresql/data
- ./regulator-portal/seed/schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro
- ./regulator-portal/seed/seed.sql:/docker-entrypoint-initdb.d/002-seed.sql:ro
redis-reg:
image: redis:7-alpine
ports: ["6398:6379"]
nats:
image: nats:2.10-alpine
command: -js -m 8222
ports: ["4226:4222"]
softhsm:
image: ghasi/softhsm-dev:2.6
volumes:
- reg_hsm_data:/var/lib/softhsm
- ./regulator-portal/hsm:/hsm-init:ro
mock-splunk-hec:
image: ghasi/mock-splunk-hec:dev
ports: ["8088:8088"]
mock-qradar:
image: ghasi/mock-qradar:dev
ports: ["514:514/tcp", "6514:6514/tcp"]
mock-logstash:
image: ghasi/mock-logstash:dev
ports: ["5044:5044"]
mock-atra-sftp:
image: atmoz/sftp:latest
command: atra:atra:1001::upload
ports: ["2223:22"]
volumes:
- reg_atra_sftp:/home/atra
mock-national-pki-ca:
image: ghasi/mock-ca:dev
ports: ["8089:8089"]
volumes:
- ./regulator-portal/test-pki:/test-pki:ro
minio-reg:
image: minio/minio:latest
environment:
MINIO_ROOT_USER: regminio
MINIO_ROOT_PASSWORD: regminio
command: server /data --console-address ":9001"
ports: ["9300:9000", "9301:9001"]
volumes: [reg_minio_data:/data]
mock-compliance:
image: ghasi/mock-compliance-engine:dev
ports: ["50055:50055"]
mock-cdr-mediation:
image: ghasi/mock-cdr-mediation:dev
ports: ["50075:50075"]
mock-consent-ledger:
image: ghasi/mock-consent-ledger:dev
ports: ["50076:50076"]
mock-sender-id:
image: ghasi/mock-sender-id-registry:dev
ports: ["50078:50078"]
regulator-portal-api:
image: ghasi/regulator-portal-service:dev
depends_on:
- postgres-reg
- redis-reg
- nats
- softhsm
- mock-atra-sftp
- mock-national-pki-ca
- minio-reg
- mock-compliance
- mock-cdr-mediation
ports: ["3082:3082"]
environment:
NODE_ENV: development
LOG_LEVEL: debug
DATABASE_URL: postgres://regulator:regulator@postgres-reg:5432/regulator
REDIS_URL: redis://redis-reg:6379/0
NATS_URL: nats://nats:4222
S3_ENDPOINT: http://minio-reg:9000
S3_ACCESS_KEY: regminio
S3_SECRET_KEY: regminio
HSM_PKCS11_LIB: /softhsm/libsofthsm2.so
HSM_TOKEN_LABEL: reg-dev
ATRA_SFTP_HOST: mock-atra-sftp
ATRA_SFTP_USER: atra
ATRA_SFTP_PASSWORD: atra
NATIONAL_PKI_CRL_URL: http://mock-national-pki-ca:8089/crl
COMPLIANCE_GRPC: mock-compliance:50055
CDR_GRPC: mock-cdr-mediation:50075
CONSENT_GRPC: mock-consent-ledger:50076
SENDER_ID_GRPC: mock-sender-id:50078
LI_SLA_ACK_HOURS: "1"
regulator-portal-siem:
image: ghasi/regulator-portal-service:dev
depends_on: [nats, mock-splunk-hec, mock-qradar, mock-logstash]
environment:
NODE_ENV: development
NATS_URL: nats://nats:4222
SIEM_DEST_SPLUNK_URL: http://mock-splunk-hec:8088/services/collector
SIEM_DEST_QRADAR_HOST: mock-qradar
SIEM_DEST_LOGSTASH_URL: http://mock-logstash:5044
command: ["node", "dist/apps/siem-forwarder/main.js"]
volumes:
- reg_siem_wal:/var/lib/siem-wal
regulator-portal-web:
image: ghasi/regulator-portal-web:dev
depends_on: [regulator-portal-api]
ports: ["3081:3081"]
environment:
NODE_ENV: development
API_URL: http://regulator-portal-api:3082
volumes:
reg_pg_data:
reg_hsm_data:
reg_atra_sftp:
reg_minio_data:
reg_siem_wal:
Start:
docker compose -f infra/docker/docker-compose.regulator-portal.yml up -d
docker compose exec softhsm /hsm-init/init.sh
# Generate test PKI if not already
cd infra/docker/regulator-portal/test-pki && ./generate-test-pki.sh
3. Test PKI
infra/docker/regulator-portal/test-pki/:
| File | Purpose |
|---|---|
national-pki-root-ca.pem | Root CA for regulator |
regulator-user-atra.{crt,key} | Test ATRA user cert |
regulator-user-atra-revoked.{crt,key} | Revoked test cert |
auditor-user-ca.{crt,key} | Auditor CA (separate from national-PKI) |
auditor-user-test.{crt,key} | Test auditor cert (time-boxed) |
crl.pem | Test CRL containing revoked certs |
Callers use their cert via curl:
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
https://localhost:3081/login
4. Common Commands
4.1 Test login
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
https://localhost:3082/v1/regulator/me
4.2 Submit a test LI request
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
-X POST https://localhost:3082/v1/regulator/li/requests \
-d '{"targetMsisdn":"+93701234567","dateRange":{"from":"2026-04-01","to":"2026-04-20"},"scope":"IRI","legalRef":"WAR-2026-001","signedWarrantHash":"sha256:..."}'
4.3 Advance LI state (dual-control)
# Initiator (legal team)
curl --cert ghasi-legal-test.crt --key ghasi-legal-test.key \
-X POST https://localhost:3082/v1/regulator/li/requests/li_abc/transition \
-d '{"to":"ACK"}'
# Approver (security, within 60 s)
curl --cert ghasi-security-test.crt --key ghasi-security-test.key \
-X POST https://localhost:3082/v1/regulator/li/requests/li_abc/approve \
-d '{}'
4.4 Generate a report
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
-X POST https://localhost:3082/v1/regulator/reports \
-d '{"type":"DAILY_CDR_STATUS","date":"2026-04-20"}'
Polling for completion:
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
https://localhost:3082/v1/regulator/reports/{reportId}
4.5 Ingest a complaint
curl --cert regulator-user-atra.crt --key regulator-user-atra.key \
-X POST https://localhost:3082/v1/regulator/complaints \
-d '{"citizenMsisdn":"+93701234568","complaintType":"UNSOLICITED_MARKETING","summary":"Receiving SMS I did not opt in to","receivedAt":"2026-04-21T10:00:00Z","regulatorRef":"COMP-2026-042"}'
4.6 Publish a test event for SIEM forwarder
nats pub auth.events.user.login '{"userId":"u_123","ts":"2026-04-21T10:00:00Z","ip":"192.0.2.1"}'
Then inspect mock-splunk + mock-qradar + mock-logstash for the relayed event.
4.7 Auditor flow
# Grant time-boxed auditor access (admin)
curl --cert ghasi-admin-test.crt --key ghasi-admin-test.key \
-X POST https://localhost:3082/v1/admin/regulator/auditor-access \
-d '{"auditorCertSubject":"CN=test-auditor","framework":"ISO27001","expiresAt":"2026-05-21T00:00:00Z"}'
# Auditor uses their cert
curl --cert auditor-user-test.crt --key auditor-user-test.key \
https://localhost:3082/v1/auditor/evidence?framework=ISO27001
4.8 Run tests
pnpm --filter @ghasi/regulator-portal-service test
pnpm --filter @ghasi/regulator-portal-service test:integration
pnpm --filter @ghasi/regulator-portal-service test:contract
pnpm --filter @ghasi/regulator-portal-service test:e2e:local
4.9 Reset
docker compose -f infra/docker/docker-compose.regulator-portal.yml down -v
docker compose -f infra/docker/docker-compose.regulator-portal.yml up -d
docker compose exec softhsm /hsm-init/init.sh
5. Environment Variables
Covered in Docker Compose above. Dev-only flags:
| Variable | Purpose |
|---|---|
DEV_DISABLE_OCSP_STAPLE | Skip OCSP staple requirement (dev only) |
DEV_ACCEPT_SELF_SIGNED | For local dev; prod rejects |
SIEM_WAL_PATH | /var/lib/siem-wal |
6. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Login returns 401 despite valid cert | CA trust chain not loaded | Regenerate test PKI + restart |
Login returns CRL_REVOKED unexpectedly | CRL cache stale | curl http://mock-national-pki-ca:8089/refresh-crl |
| SIEM forwarder shows no events | Subject mismatch or subscriber not healthy | nats stream info; check forwarder logs |
| LI delivery hangs | Mock SFTP not running | docker compose ps; restart |
Report generation fails with UPSTREAM_UNAVAILABLE | Mock upstream not running | Check individual mocks |
| HSM sign fails | softhsm not initialised | /hsm-init/init.sh |