Files
dd0c/products/docker-compose.yml
Max Mayfield eb953cdea5
Some checks failed
CI — P2 Drift (Go + Node) / agent (push) Successful in 43s
CI — P2 Drift (Go + Node) / saas (push) Failing after 5s
CI — P3 Alert / test (push) Failing after 4s
CI — P4 Portal / test (push) Failing after 4s
CI — P5 Cost / test (push) Failing after 4s
CI — P6 Run / saas (push) Failing after 5s
CI — P2 Drift (Go + Node) / build-push (push) Failing after 7s
CI — P3 Alert / build-push (push) Has been skipped
CI — P4 Portal / build-push (push) Has been skipped
CI — P5 Cost / build-push (push) Has been skipped
CI — P6 Run / build-push (push) Failing after 5s
Security hardening: auth encapsulation, pool restriction, rate limiting, invites, async webhooks
Phase 1 (Security Critical):
- Auth plugin encapsulation: replaced global addHook with Fastify plugin scope
- Removed startsWith URL matching; public routes registered outside auth scope
- JWT verify now enforces algorithms: ['HS256'] (prevents algorithm confusion)
- Raw pool no longer exported from db.ts; systemQuery() + getPoolForAuth() instead
- withTenant() remains primary tenant-scoped query path

Phase 2 (Infrastructure):
- docker-compose.yml: all secrets via env var substitution (${VAR:-default})
- Per-service Postgres users (dd0c_drift, dd0c_alert, etc.) in docker-init-db.sh
- .env.example with all configurable secrets
- build-push.sh uses $REGISTRY_PASSWORD instead of hardcoded
- .gitignore excludes .env files
- @fastify/rate-limit: 100 req/min global, 5/min login, 3/min signup
- CORS_ORIGIN default changed from '*' to 'http://localhost:5173'

Phase 3 (Product):
- Team invite flow: tenant_invites table, POST /invite, GET /invites, DELETE /invites/:id
- Signup accepts optional invite_token to join existing tenant
- Async webhook ingestion (P3): LPUSH to Redis, BRPOP worker, dead-letter queue

Console:
- All 5 product modules wired: drift, alert, portal, cost, run
- PageHeader accepts children prop
- 71 modules, 70KB gzipped production build

All 6 projects compile clean (tsc --noEmit).
2026-03-02 23:53:55 +00:00

213 lines
6.4 KiB
YAML

# dd0c Local Development Stack
#
# Usage: docker compose up -d
# All services share one Postgres and one Redis instance.
# Caddy handles TLS and routing for *.dd0c.localhost
services:
# --- Shared Infrastructure ---
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-dd0c}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dd0c-dev}
DB_DRIFT_PASSWORD: ${DB_DRIFT_PASSWORD:-dd0c-dev}
DB_ALERT_PASSWORD: ${DB_ALERT_PASSWORD:-dd0c-dev}
DB_PORTAL_PASSWORD: ${DB_PORTAL_PASSWORD:-dd0c-dev}
DB_COST_PASSWORD: ${DB_COST_PASSWORD:-dd0c-dev}
DB_RUN_PASSWORD: ${DB_RUN_PASSWORD:-dd0c-dev}
ports:
- "5433:5432"
volumes:
- pg_data:/var/lib/postgresql/data
- ./docker-init-db.sh:/docker-entrypoint-initdb.d/01-init-db.sh:ro
- ./01-llm-cost-router/migrations:/migrations/01-route:ro
- ./02-iac-drift-detection/saas/migrations:/migrations/02-drift:ro
- ./03-alert-intelligence/migrations:/migrations/03-alert:ro
- ./04-lightweight-idp/migrations:/migrations/04-portal:ro
- ./05-aws-cost-anomaly/migrations:/migrations/05-cost:ro
- ./06-runbook-automation/saas/migrations:/migrations/06-run:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dd0c"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
meilisearch:
image: getmeili/meilisearch:v1.8
environment:
MEILI_ENV: development
ports:
- "7700:7700"
volumes:
- meili_data:/meili_data
# --- dd0c Products ---
# P1: LLM Cost Router (Rust — API server)
# NOTE: Rust services are behind the "rust" profile because they take 10+ min to compile.
# Start without Rust: docker compose up -d
# Start with Rust: docker compose --profile rust up -d
route-api:
profiles: ["rust"]
build:
context: ./01-llm-cost-router
dockerfile: Dockerfile
command: ["dd0c-api"]
ports:
- "3001:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c:${POSTGRES_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_route
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P1: LLM Cost Router (Rust — proxy)
route-proxy:
profiles: ["rust"]
build:
context: ./01-llm-cost-router
dockerfile: Dockerfile
command: ["dd0c-proxy"]
ports:
- "8080:8080"
environment:
DATABASE_URL: postgresql://dd0c:${POSTGRES_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_route
REDIS_URL: redis://redis:6379
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P1: LLM Cost Router (Rust — worker)
route-worker:
profiles: ["rust"]
build:
context: ./01-llm-cost-router
dockerfile: Dockerfile
command: ["dd0c-worker"]
environment:
DATABASE_URL: postgresql://dd0c:${POSTGRES_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_route
REDIS_URL: redis://redis:6379
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P2: IaC Drift Detection (SaaS)
drift:
image: reg.dd0c.net/dd0c-drift:latest
build:
context: ./02-iac-drift-detection/saas
dockerfile: Dockerfile
ports:
- "3002:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c_drift:${DB_DRIFT_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_drift
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P3: Alert Intelligence
alert:
image: reg.dd0c.net/dd0c-alert:latest
build:
context: ./03-alert-intelligence
dockerfile: Dockerfile
ports:
- "3003:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c_alert:${DB_ALERT_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_alert
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P4: Lightweight IDP / Service Catalog
portal:
image: reg.dd0c.net/dd0c-portal:latest
build:
context: ./04-lightweight-idp
dockerfile: Dockerfile
ports:
- "3004:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c_portal:${DB_PORTAL_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_portal
REDIS_URL: redis://redis:6379
MEILI_URL: http://meilisearch:7700
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
meilisearch: { condition: service_started }
# P5: AWS Cost Anomaly Detection
cost:
image: reg.dd0c.net/dd0c-cost:latest
build:
context: ./05-aws-cost-anomaly
dockerfile: Dockerfile
ports:
- "3007:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c_cost:${DB_COST_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_cost
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
ANOMALY_THRESHOLD: "50"
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
# P6: Runbook Automation (SaaS)
run:
image: reg.dd0c.net/dd0c-run:latest
build:
context: ./06-runbook-automation/saas
dockerfile: Dockerfile
ports:
- "3006:3000"
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_URL: postgresql://dd0c_run:${DB_RUN_PASSWORD:-dd0c-dev}@postgres:5432/dd0c_run
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-in-production!!}
LOG_LEVEL: info
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
volumes:
pg_data:
meili_data: