diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 5113c12..eda7050 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -51,8 +51,13 @@ services: command: ["uv", "run", "--no-dev", "alembic", "upgrade", "head"] labels: com.centurylinklabs.watchtower.enable: "true" + # All app config comes from .env (twelve-factor) — no per-setting allow-list + # to maintain. The `environment:` block below only pins values that must NOT + # come from .env. See the backend service for the full rationale. + env_file: + - path: .env + required: false environment: - APP_ENV: ${APP_ENV:-development} DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://provenance:provenance@postgres:5432/provenance} depends_on: postgres: @@ -63,50 +68,24 @@ services: image: git.jpaul.io/justin/provenance-backend:${IMAGE_TAG:-test-main} labels: com.centurylinklabs.watchtower.enable: "true" + # Twelve-factor: ALL application settings come straight from .env — owner, + # AI providers, mailer/SMTP, S3, sessions, everything in app/core/config.py. + # No per-setting allow-list to maintain, so a new setting in .env (and + # .env.example) reaches the app with no compose edit. The `environment:` + # block below is only for values that must NOT come from .env: + # - RUN_MIGRATIONS: backend-only flag, not an app setting. + # - DATABASE_URL: pinned to the compose-internal host as a safety net — + # the code default points at localhost, which is wrong inside the + # network. (.env normally sets it; this guards against it being absent.) + # `environment:` wins over `env_file`, so these always take effect. + # Trade-off (accepted): env_file also exposes infra secrets (POSTGRES_*, + # MINIO_*, CLOUDFLARE_TUNNEL_TOKEN) to the app process; the app ignores them. + env_file: + - path: .env + required: false environment: - APP_ENV: ${APP_ENV:-development} - # Self-migrate on start so a Watchtower in-place image swap applies any new - # migrations (idempotent). The one-shot `migrate` service covers the same - # for `compose up`; the depends_on below serializes them so they never run - # alembic concurrently. RUN_MIGRATIONS: "1" DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://provenance:provenance@postgres:5432/provenance} - # Instance owner/operator — the account(s) with instance-admin rights. - OWNER_EMAIL: ${OWNER_EMAIL:-} - S3_ENDPOINT_URL: ${S3_ENDPOINT_URL:-http://minio:9000} - S3_BUCKET: ${S3_BUCKET:-provenance} - S3_ACCESS_KEY: ${S3_ACCESS_KEY:-provenance} - S3_SECRET_KEY: ${S3_SECRET_KEY:-change-me-too} - S3_REGION: ${S3_REGION:-us-east-1} - # Email / mailer — verification + password-reset links. APP_BASE_URL is the - # base for those links; MAILER=smtp activates the SMTP_* settings. - APP_BASE_URL: ${APP_BASE_URL:-http://localhost} - REQUIRE_EMAIL_VERIFICATION: ${REQUIRE_EMAIL_VERIFICATION:-false} - MAILER: ${MAILER:-console} - SMTP_HOST: ${SMTP_HOST:-} - SMTP_PORT: ${SMTP_PORT:-587} - SMTP_USERNAME: ${SMTP_USERNAME:-} - SMTP_PASSWORD: ${SMTP_PASSWORD:-} - SMTP_FROM: ${SMTP_FROM:-Provenance } - # Model providers (AI assistant + embeddings). Each activates when its key - # is set; DEFAULT_*_PROVIDER picks the default. 'null' keeps AI off. - DEFAULT_LLM_PROVIDER: ${DEFAULT_LLM_PROVIDER:-null} - DEFAULT_EMBEDDING_PROVIDER: ${DEFAULT_EMBEDDING_PROVIDER:-null} - LLM_MAX_TOKENS: ${LLM_MAX_TOKENS:-4096} - EMBEDDING_DIMENSIONS: ${EMBEDDING_DIMENSIONS:-1536} - ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} - ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-opus-4-8} - OPENAI_API_KEY: ${OPENAI_API_KEY:-} - OPENAI_BASE_URL: ${OPENAI_BASE_URL:-https://api.openai.com/v1} - OPENAI_MODEL: ${OPENAI_MODEL:-gpt-4o} - OPENAI_EMBEDDING_MODEL: ${OPENAI_EMBEDDING_MODEL:-text-embedding-3-small} - XAI_API_KEY: ${XAI_API_KEY:-} - XAI_BASE_URL: ${XAI_BASE_URL:-https://api.x.ai/v1} - XAI_MODEL: ${XAI_MODEL:-grok-2-latest} - OLLAMA_ENABLED: ${OLLAMA_ENABLED:-false} - OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://localhost:11434/v1} - OLLAMA_MODEL: ${OLLAMA_MODEL:-llama3.1} - OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-nomic-embed-text} depends_on: postgres: condition: service_healthy @@ -133,14 +112,14 @@ services: command: ["uv", "run", "--no-dev", "python", "-m", "app.worker"] labels: com.centurylinklabs.watchtower.enable: "true" + # Same .env-driven config as the backend (see its comment). The worker reads + # the model-provider settings too, so the upcoming embedding/matching jobs + # are configured the moment they land — no compose change needed. + env_file: + - path: .env + required: false environment: - APP_ENV: ${APP_ENV:-development} DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://provenance:provenance@postgres:5432/provenance} - S3_ENDPOINT_URL: ${S3_ENDPOINT_URL:-http://minio:9000} - S3_BUCKET: ${S3_BUCKET:-provenance} - S3_ACCESS_KEY: ${S3_ACCESS_KEY:-provenance} - S3_SECRET_KEY: ${S3_SECRET_KEY:-change-me-too} - S3_REGION: ${S3_REGION:-us-east-1} depends_on: postgres: condition: service_healthy