Some checks failed
CI/CD Pipeline / Notifications (push) Has been cancelled
CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
CI/CD Pipeline / Policy Validation (push) Has been cancelled
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-firm-connectors) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-forms) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-hmrc) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ingestion) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-normalize-map) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ocr) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-indexer) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-reason) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rpa) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (ui-review) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (ui-review) (push) Has been cancelled
CI/CD Pipeline / Generate SBOM (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
397 lines
13 KiB
YAML
397 lines
13 KiB
YAML
# FILE: infra/base/infrastructure.yaml
|
|
# Infrastructure Services for AI Tax Agent
|
|
# Environment-agnostic - use with environment-specific .env files
|
|
# Deploy with: ./infra/scripts/deploy.sh <environment> infrastructure
|
|
|
|
networks:
|
|
frontend:
|
|
external: true
|
|
name: apa-frontend
|
|
backend:
|
|
external: true
|
|
name: apa-backend
|
|
|
|
volumes:
|
|
postgres_data:
|
|
neo4j_data:
|
|
neo4j_logs:
|
|
qdrant_data:
|
|
minio_data:
|
|
vault_data:
|
|
redis_data:
|
|
nats_data:
|
|
authentik_data:
|
|
|
|
services:
|
|
# Edge Gateway & SSO
|
|
apa-traefik:
|
|
image: docker.io/library/traefik:v3.5.1
|
|
container_name: apa-traefik
|
|
restart: unless-stopped
|
|
networks:
|
|
- frontend
|
|
- backend
|
|
ports:
|
|
- "8090:80"
|
|
- "8444:443"
|
|
- "8091:8080" # Dashboard
|
|
env_file:
|
|
- ./.provider.env
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- ./traefik/config/traefik.yml:/etc/traefik/traefik.yml:ro
|
|
- ./traefik/config/traefik-dynamic.yml:/etc/traefik/conf.d/01-base.yml:ro
|
|
- ../compose/traefik/traefik-dynamic.local.yml:/etc/traefik/conf.d/02-local.yml:ro
|
|
- ./certs/:/var/traefik/certs/:rw
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.http.routers.dashboard.rule=Host(`traefik.app.harkon.co.uk`)"
|
|
- "traefik.http.routers.dashboard.entrypoints=websecure"
|
|
- "traefik.http.routers.dashboard.tls=true"
|
|
- "traefik.http.routers.dashboard.tls.certresolver=godaddy"
|
|
- "traefik.http.routers.dashboard.service=api@internal"
|
|
- "traefik.http.routers.dashboard.middlewares=authentik-forwardauth@file"
|
|
|
|
# Identity & SSO (Authentik)
|
|
apa-authentik-db:
|
|
image: postgres:15-alpine
|
|
container_name: apa-authentik-db
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- authentik_data:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_DB: authentik
|
|
POSTGRES_USER: authentik
|
|
POSTGRES_PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U authentik"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
apa-authentik-redis:
|
|
image: redis:7-alpine
|
|
container_name: apa-authentik-redis
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
command: --save 60 1 --loglevel warning
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
apa-authentik-server:
|
|
image: ghcr.io/goauthentik/server:2025.8.3
|
|
container_name: apa-authentik-server
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
command: server
|
|
environment:
|
|
AUTHENTIK_REDIS__HOST: apa-authentik-redis
|
|
AUTHENTIK_POSTGRESQL__HOST: apa-authentik-db
|
|
AUTHENTIK_POSTGRESQL__USER: authentik
|
|
AUTHENTIK_POSTGRESQL__NAME: authentik
|
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
|
AUTHENTIK_ERROR_REPORTING__ENABLED: false
|
|
AUTHENTIK_API_CLIENT_SECRET: ${AUTHENTIK_API_CLIENT_SECRET}
|
|
AUTHENTIK_GRAFANA_CLIENT_SECRET: ${AUTHENTIK_GRAFANA_CLIENT_SECRET}
|
|
AUTHENTIK_MINIO_CLIENT_SECRET: ${AUTHENTIK_MINIO_CLIENT_SECRET}
|
|
AUTHENTIK_VAULT_CLIENT_SECRET: ${AUTHENTIK_VAULT_CLIENT_SECRET}
|
|
AUTHENTIK_BOOTSTRAP_FILE: /blueprints/ai-tax-agent-bootstrap.yaml
|
|
depends_on:
|
|
- apa-authentik-db
|
|
- apa-authentik-redis
|
|
volumes:
|
|
- ${AUTHENTIK_BOOTSTRAP_FILE}:/blueprints/ai-tax-agent-bootstrap.yaml
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.authentik.rule=Host(`auth.${DOMAIN}`)"
|
|
- "traefik.http.routers.authentik.entrypoints=websecure"
|
|
- "traefik.http.routers.authentik.tls=true"
|
|
- "traefik.http.routers.authentik.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
|
|
|
|
apa-authentik-worker:
|
|
image: ghcr.io/goauthentik/server:2025.8.3
|
|
container_name: apa-authentik-worker
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
command: worker
|
|
environment:
|
|
AUTHENTIK_REDIS__HOST: apa-authentik-redis
|
|
AUTHENTIK_POSTGRESQL__HOST: apa-authentik-db
|
|
AUTHENTIK_POSTGRESQL__USER: authentik
|
|
AUTHENTIK_POSTGRESQL__NAME: authentik
|
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD}
|
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
|
AUTHENTIK_ERROR_REPORTING__ENABLED: false
|
|
AUTHENTIK_API_CLIENT_SECRET: ${AUTHENTIK_API_CLIENT_SECRET}
|
|
AUTHENTIK_GRAFANA_CLIENT_SECRET: ${AUTHENTIK_GRAFANA_CLIENT_SECRET}
|
|
AUTHENTIK_MINIO_CLIENT_SECRET: ${AUTHENTIK_MINIO_CLIENT_SECRET}
|
|
AUTHENTIK_VAULT_CLIENT_SECRET: ${AUTHENTIK_VAULT_CLIENT_SECRET}
|
|
AUTHENTIK_BOOTSTRAP_FILE: /blueprints/ai-tax-agent-bootstrap.yaml
|
|
depends_on:
|
|
- apa-authentik-db
|
|
- apa-authentik-redis
|
|
volumes:
|
|
- ${AUTHENTIK_BOOTSTRAP_FILE}:/blueprints/ai-tax-agent-bootstrap.yaml
|
|
|
|
apa-authentik-outpost:
|
|
image: ghcr.io/goauthentik/proxy:2025.8.3
|
|
container_name: apa-authentik-outpost
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
environment:
|
|
AUTHENTIK_HOST: http://apa-authentik-server:9000
|
|
AUTHENTIK_INSECURE: true
|
|
AUTHENTIK_TOKEN: ${AUTHENTIK_OUTPOST_TOKEN}
|
|
AUTHENTIK_REDIS__HOST: apa-authentik-redis
|
|
AUTHENTIK_REDIS__PORT: 6379
|
|
depends_on:
|
|
- apa-authentik-server
|
|
- apa-authentik-redis
|
|
|
|
# Secrets Management
|
|
apa-vault:
|
|
image: hashicorp/vault:1.15
|
|
container_name: apa-vault
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
volumes:
|
|
- vault_data:/vault/data
|
|
environment:
|
|
VAULT_DEV_ROOT_TOKEN_ID: ${VAULT_DEV_ROOT_TOKEN_ID}
|
|
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
|
|
command: vault server -dev -dev-listen-address=0.0.0.0:8200
|
|
cap_add:
|
|
- IPC_LOCK
|
|
extra_hosts:
|
|
- "auth.local.lan:host-gateway"
|
|
- "vault.local.lan:host-gateway"
|
|
- "minio.local.lan:host-gateway"
|
|
- "api.local.lan:host-gateway"
|
|
- "traefik.local.lan:host-gateway"
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.vault.rule=Host(`vault.${DOMAIN}`)"
|
|
- "traefik.http.routers.vault.entrypoints=websecure"
|
|
- "traefik.http.routers.vault.tls=true"
|
|
- "traefik.http.routers.vault.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.services.vault.loadbalancer.server.port=8200"
|
|
|
|
# Object Storage
|
|
apa-minio:
|
|
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
|
container_name: apa-minio
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
volumes:
|
|
- minio_data:/data
|
|
environment:
|
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
|
MINIO_BROWSER_REDIRECT_URL: https://minio.${DOMAIN}
|
|
MINIO_IDENTITY_OPENID_CONFIG_URL: "https://auth.${DOMAIN}/application/o/minio-prod/.well-known/openid-configuration"
|
|
MINIO_IDENTITY_OPENID_CLIENT_ID: "minio-prod"
|
|
MINIO_IDENTITY_OPENID_CLIENT_SECRET: ${AUTHENTIK_MINIO_CLIENT_SECRET}
|
|
MINIO_IDENTITY_OPENID_SCOPES: "openid,profile,email,minio"
|
|
MINIO_IDENTITY_OPENID_REDIRECT_URI: "https://minio.${DOMAIN}/oauth_callback"
|
|
MINIO_IDENTITY_OPENID_DISPLAY_NAME: "Login with Authentik"
|
|
command: server /data --address ":9092" --console-address ":9093"
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:9092/minio/health/live"]
|
|
interval: 30s
|
|
timeout: 20s
|
|
retries: 3
|
|
extra_hosts:
|
|
- "auth.local.lan:host-gateway"
|
|
- "minio.local.lan:host-gateway"
|
|
- "api.local.lan:host-gateway"
|
|
- "traefik.local.lan:host-gateway"
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.minio-api.rule=Host(`minio-api.${DOMAIN}`)"
|
|
- "traefik.http.routers.minio-api.entrypoints=websecure"
|
|
- "traefik.http.routers.minio-api.tls=true"
|
|
- "traefik.http.routers.minio-api.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.routers.minio-api.service=minio-api"
|
|
- "traefik.http.services.minio-api.loadbalancer.server.port=9092"
|
|
- "traefik.http.routers.minio-console.rule=Host(`minio.${DOMAIN}`)"
|
|
- "traefik.http.routers.minio-console.entrypoints=websecure"
|
|
- "traefik.http.routers.minio-console.tls=true"
|
|
- "traefik.http.routers.minio-console.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.routers.minio-console.service=minio-console"
|
|
- "traefik.http.services.minio-console.loadbalancer.server.port=9093"
|
|
|
|
# Vector Database
|
|
apa-qdrant:
|
|
image: qdrant/qdrant:v1.7.4
|
|
container_name: apa-qdrant
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
volumes:
|
|
- qdrant_data:/qdrant/storage
|
|
environment:
|
|
QDRANT__SERVICE__GRPC_PORT: ${QDRANT__SERVICE__GRPC_PORT:-6334}
|
|
QDRANT__SERVICE__HTTP_PORT: 6333
|
|
QDRANT__LOG_LEVEL: INFO
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.qdrant.rule=Host(`qdrant.${DOMAIN}`)"
|
|
- "traefik.http.routers.qdrant.entrypoints=websecure"
|
|
- "traefik.http.routers.qdrant.tls=true"
|
|
- "traefik.http.routers.qdrant.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.routers.qdrant.middlewares=authentik-forwardauth@file"
|
|
- "traefik.http.services.qdrant.loadbalancer.server.port=6333"
|
|
|
|
# Knowledge Graph Database
|
|
apa-neo4j:
|
|
image: neo4j:5.15-community
|
|
container_name: apa-neo4j
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
volumes:
|
|
- neo4j_data:/data
|
|
- neo4j_logs:/logs
|
|
environment:
|
|
NEO4J_AUTH: neo4j/${NEO4J_PASSWORD}
|
|
NEO4J_PLUGINS: '["apoc", "graph-data-science"]'
|
|
NEO4J_dbms_security_procedures_unrestricted: gds.*,apoc.*
|
|
NEO4J_dbms_security_procedures_allowlist: gds.*,apoc.*
|
|
NEO4J_apoc_export_file_enabled: true
|
|
NEO4J_apoc_import_file_enabled: true
|
|
NEO4J_apoc_import_file_use__neo4j__config: true
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.neo4j.rule=Host(`neo4j.${DOMAIN}`)"
|
|
- "traefik.http.routers.neo4j.entrypoints=websecure"
|
|
- "traefik.http.routers.neo4j.tls=true"
|
|
- "traefik.http.routers.neo4j.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.routers.neo4j.middlewares=authentik-forwardauth@file"
|
|
- "traefik.http.services.neo4j.loadbalancer.server.port=7474"
|
|
|
|
# Secure Client Data Store
|
|
apa-postgres:
|
|
image: postgres:15-alpine
|
|
container_name: apa-postgres
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_DB: tax_system
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
|
|
command: >
|
|
postgres
|
|
-c shared_preload_libraries=pg_stat_statements
|
|
-c pg_stat_statements.track=all
|
|
-c max_connections=200
|
|
-c shared_buffers=256MB
|
|
-c effective_cache_size=1GB
|
|
-c maintenance_work_mem=64MB
|
|
-c checkpoint_completion_target=0.9
|
|
-c wal_buffers=16MB
|
|
-c default_statistics_target=100
|
|
-c random_page_cost=1.1
|
|
-c effective_io_concurrency=200
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
# Cache & Session Store
|
|
apa-redis:
|
|
image: redis:7-alpine
|
|
container_name: apa-redis
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
volumes:
|
|
- redis_data:/data
|
|
command: >
|
|
redis-server
|
|
--appendonly yes
|
|
--appendfsync everysec
|
|
--maxmemory 512mb
|
|
--maxmemory-policy allkeys-lru
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
|
|
# Message Broker & Event Streaming
|
|
apa-nats:
|
|
image: nats:2.10-alpine
|
|
container_name: apa-nats
|
|
restart: unless-stopped
|
|
networks:
|
|
- backend
|
|
- frontend
|
|
ports:
|
|
- "4222:4222" # Client connections (for local testing)
|
|
volumes:
|
|
- nats_data:/data
|
|
command: >
|
|
--jetstream
|
|
--store_dir=/data
|
|
--http_port=8222
|
|
environment:
|
|
NATS_LOG_LEVEL: ${NATS_LOG_LEVEL:-info}
|
|
healthcheck:
|
|
test:
|
|
[
|
|
"CMD",
|
|
"wget",
|
|
"--no-verbose",
|
|
"--tries=1",
|
|
"--spider",
|
|
"http://localhost:8222/healthz",
|
|
]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.constraint-label=app"
|
|
- "traefik.docker.network=apa-backend"
|
|
- "traefik.http.routers.nats-monitor.rule=Host(`nats.${DOMAIN}`)"
|
|
- "traefik.http.routers.nats-monitor.entrypoints=websecure"
|
|
- "traefik.http.routers.nats-monitor.tls=true"
|
|
- "traefik.http.routers.nats-monitor.tls.certresolver=${TRAEFIK_CERT_RESOLVER}"
|
|
- "traefik.http.routers.nats-monitor.middlewares=authentik-forwardauth@file"
|
|
- "traefik.http.services.nats-monitor.loadbalancer.server.port=8222"
|