services: # ───────────────────────────────────────────── # PostgreSQL 16 — primary datastore # ───────────────────────────────────────────── postgres: image: postgres:16-alpine environment: POSTGRES_DB: veylant POSTGRES_USER: veylant POSTGRES_PASSWORD: veylant_dev ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U veylant -d veylant"] interval: 5s timeout: 5s retries: 10 start_period: 10s # ───────────────────────────────────────────── # Redis 7 — sessions, rate limiting, PII pseudonymization mappings # No persistence in dev (AOF/RDB disabled for fast startup) # ───────────────────────────────────────────── redis: image: redis:7-alpine command: redis-server --save "" --appendonly no ports: - "6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 10 # ───────────────────────────────────────────── # ClickHouse 24.3 LTS — append-only audit logs and analytics # Pinned to LTS for stability # ───────────────────────────────────────────── clickhouse: image: clickhouse/clickhouse-server:24.3-alpine environment: CLICKHOUSE_DB: veylant_logs CLICKHOUSE_USER: veylant CLICKHOUSE_PASSWORD: veylant_dev CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1 ports: - "8123:8123" # HTTP interface (used for health check and dashboard queries) - "9000:9000" # Native TCP (used by Go driver) volumes: - clickhouse_data:/var/lib/clickhouse - ./deploy/clickhouse/listen-ipv4.xml:/etc/clickhouse-server/config.d/listen-ipv4.xml:ro healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1:8123/ping || exit 1"] interval: 5s timeout: 5s retries: 20 start_period: 15s ulimits: nofile: soft: 262144 hard: 262144 # ───────────────────────────────────────────── # Keycloak 24 — IAM, OIDC, SAML 2.0 # start-dev: in-memory DB, no TLS — development only # Realm is auto-imported from deploy/keycloak/realm-export.json # ───────────────────────────────────────────── keycloak: image: quay.io/keycloak/keycloak:24.0 command: ["start-dev", "--import-realm"] environment: KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin KC_DB: dev-mem KC_HEALTH_ENABLED: "true" ports: - "8080:8080" volumes: - ./deploy/keycloak:/opt/keycloak/data/import:ro healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:8080/health/ready || exit 1"] interval: 10s timeout: 10s retries: 20 start_period: 30s # Keycloak takes ~20s to start in dev mode # ───────────────────────────────────────────── # Veylant proxy — Go application # ───────────────────────────────────────────── proxy: build: context: . dockerfile: Dockerfile ports: - "8090:8090" environment: VEYLANT_SERVER_PORT: "8090" VEYLANT_SERVER_ENV: "development" VEYLANT_DATABASE_URL: "postgres://veylant:veylant_dev@postgres:5432/veylant?sslmode=disable" VEYLANT_REDIS_URL: "redis://redis:6379" VEYLANT_KEYCLOAK_BASE_URL: "http://keycloak:8080" VEYLANT_KEYCLOAK_REALM: "veylant" VEYLANT_KEYCLOAK_CLIENT_ID: "veylant-proxy" VEYLANT_PII_ENABLED: "true" VEYLANT_PII_SERVICE_ADDR: "pii:50051" VEYLANT_PII_TIMEOUT_MS: "100" VEYLANT_PII_FAIL_OPEN: "true" VEYLANT_LOG_FORMAT: "console" VEYLANT_LOG_LEVEL: "debug" # Provider API keys — set via a .env file or shell environment. # Only providers with an API key set will be enabled at runtime. VEYLANT_PROVIDERS_OPENAI_API_KEY: "${OPENAI_API_KEY:-}" VEYLANT_PROVIDERS_ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY:-}" VEYLANT_PROVIDERS_MISTRAL_API_KEY: "${MISTRAL_API_KEY:-}" # Azure OpenAI requires resource name + deployment ID + API key. VEYLANT_PROVIDERS_AZURE_API_KEY: "${AZURE_OPENAI_API_KEY:-}" VEYLANT_PROVIDERS_AZURE_RESOURCE_NAME: "${AZURE_OPENAI_RESOURCE_NAME:-}" VEYLANT_PROVIDERS_AZURE_DEPLOYMENT_ID: "${AZURE_OPENAI_DEPLOYMENT_ID:-}" # Ollama — defaults to localhost:11434 (use host.docker.internal in Docker Desktop). VEYLANT_PROVIDERS_OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://host.docker.internal:11434/v1}" VEYLANT_METRICS_ENABLED: "true" # ClickHouse audit log (Sprint 6). VEYLANT_CLICKHOUSE_DSN: "clickhouse://veylant:veylant_dev@clickhouse:9000/veylant_logs" # AES-256-GCM key for prompt encryption — generate: openssl rand -base64 32 # In production, inject via Vault or secret manager. Leave empty to disable. VEYLANT_CRYPTO_AES_KEY_BASE64: "${VEYLANT_CRYPTO_AES_KEY_BASE64:-}" depends_on: postgres: condition: service_healthy redis: condition: service_healthy clickhouse: condition: service_healthy # distroless/static has no shell or wget, so Docker health checks via CMD-SHELL # cannot run. The proxy exposes /healthz at :8090 — healthy when it starts. # ───────────────────────────────────────────── # PII detection service — Python (Sprint 3: full pipeline) # Layer 1: regex (IBAN/email/phone/SSN/CB) # Layer 2: Presidio + spaCy NER (PERSON/LOC/ORG) # Pseudonymization: AES-256-GCM in Redis # ───────────────────────────────────────────── pii: build: context: ./services/pii dockerfile: Dockerfile ports: - "50051:50051" # gRPC - "8000:8000" # HTTP health environment: PII_GRPC_PORT: "50051" PII_HTTP_PORT: "8000" PII_REDIS_URL: "redis://redis:6379" # PII_ENCRYPTION_KEY must be set to a 32-byte base64-encoded key in production. # The default dev key is used if unset (NOT safe for production). PII_ENCRYPTION_KEY: "${PII_ENCRYPTION_KEY:-}" PII_NER_ENABLED: "true" PII_NER_CONFIDENCE: "0.85" PII_TTL_SECONDS: "3600" depends_on: redis: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8000/healthz || exit 1"] interval: 10s timeout: 5s retries: 10 start_period: 60s # spaCy fr_core_news_lg model load takes ~30s on first start # ───────────────────────────────────────────── # Prometheus — metrics collection # Scrapes the proxy /metrics endpoint every 15s # ───────────────────────────────────────────── prometheus: image: prom/prometheus:v2.53.0 ports: - "9090:9090" volumes: - ./deploy/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--web.console.libraries=/etc/prometheus/console_libraries" - "--web.console.templates=/etc/prometheus/consoles" depends_on: proxy: condition: service_started # ───────────────────────────────────────────── # Grafana — metrics visualisation # Auto-provisioned datasource (Prometheus) + Veylant dashboard # Default credentials: admin / admin # ───────────────────────────────────────────── grafana: image: grafana/grafana:11.3.0 ports: - "3001:3000" environment: GF_SECURITY_ADMIN_PASSWORD: admin GF_USERS_ALLOW_SIGN_UP: "false" volumes: - ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro - ./deploy/grafana/dashboards:/var/lib/grafana/dashboards:ro depends_on: - prometheus # ───────────────────────────────────────────── # Veylant Dashboard — React SPA (Sprint 7) # Dev server only — production uses dist/ served by nginx # ───────────────────────────────────────────── web: image: node:20-alpine working_dir: /app command: sh -c "npm install && npm run dev -- --host" ports: - "3000:3000" volumes: - ./web:/app - /app/node_modules environment: VITE_AUTH_MODE: "dev" VITE_KEYCLOAK_URL: "http://localhost:8080/realms/veylant" depends_on: proxy: condition: service_started volumes: postgres_data: clickhouse_data: