services: postgres: # pgvector-enabled Postgres 16 (semantic search). Drop-in for postgres:16 data; # the 'vector' extension is created by the AddEmbeddingColumn migration. image: pgvector/pgvector:pg16 environment: POSTGRES_DB: inboxintel POSTGRES_USER: inboxintel # V-03: require an explicit strong password (fail fast if POSTGRES_PASSWORD is unset) # rather than silently defaulting to a guessable one. POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in deploy/.env} volumes: - pgdata:/var/lib/postgresql/data # V-03: bind to loopback only so the database is reachable from the host for local # tooling but NOT from other machines on the network. The api container reaches it # over the internal compose network regardless of this published port. ports: - "127.0.0.1:5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U inboxintel"] interval: 5s timeout: 5s retries: 10 api: build: context: . dockerfile: src/InboxIntel.Api/Dockerfile environment: ASPNETCORE_ENVIRONMENT: Production ASPNETCORE_URLS: http://+:8080 ConnectionStrings__Postgres: "Host=postgres;Port=5432;Database=inboxintel;Username=inboxintel;Password=${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in deploy/.env}" DataProtection__KeyPath: /keys GoogleOAuth__ClientId: ${GOOGLE_CLIENT_ID:-} GoogleOAuth__ClientSecret: ${GOOGLE_CLIENT_SECRET:-} Ai__Mode: ${AI_MODE:-Disabled} # Dev mode shows the dev banner and caps the initial sync. Set DEV_MODE=true # and MAX_MESSAGES=1000 in deploy/.env to exercise it in this Docker setup. App__DevMode: ${DEV_MODE:-false} GmailSync__MaxMessages: ${MAX_MESSAGES:-0} Cors__Origins__0: ${FRONTEND_ORIGIN:-http://localhost:8081} volumes: - keys:/keys depends_on: postgres: condition: service_healthy # V-08: bind to loopback so the API is not directly reachable from the network # (only via the frontend/nginx proxy over the internal compose network). This # prevents external clients from bypassing the proxy to spoof X-Forwarded-* headers. ports: - "127.0.0.1:8080:8080" frontend: build: context: ./frontend dockerfile: Dockerfile depends_on: - api ports: - "8081:80" # Optional reverse proxy. Enable with: docker compose --profile proxy up nginx: image: nginx:alpine profiles: ["proxy"] volumes: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - api - frontend ports: - "80:80" volumes: pgdata: keys: