# Cancer-center research-intelligence API, fronted by an existing Traefik. # # Assumes Traefik is already running and attached to an external Docker network # (named `traefik` here — change to match yours). The serving DuckDB (marts + FTS # index) is baked into the API image at build time (ADR-0023), so there is no data # mount and no runtime lake access. Rebuild it on the host before building: # uv run python -m cu_openalex.cancer_center.build # Refresh the data by rebuilding + redeploying (`docker compose up -d --build`). # No database server: the API reads the baked DuckDB in-process, read-only. # # docker compose up -d --build services: # FastAPI + DuckDB. Internal only — the web service proxies /api to it, so it # needs no public Traefik route (keeps one origin, simpler CORS). api: build: . image: cu-openalex-api:latest restart: unless-stopped environment: # Server-side Gemini key — enables /api/chat. End users never supply it. GEMINI_API_KEY: ${GEMINI_API_KEY:-} # Optional: override the chat model (default gemini-2.5-flash). CU_OPENALEX_CHAT_MODEL: ${CU_OPENALEX_CHAT_MODEL:-} # --- Application backend (ADR-0026): auth + editable profiles ------------ # The tier activates only when an overlay DB password is present. Secrets # are injected as env (resolved from GSM at deploy time — see DEPLOYMENT.md) # so the runtime container never contacts GSM. With these unset the API # serves the read-only analytics only. UCCC_APP_BASE_URL: ${UCCC_APP_BASE_URL:-https://insights.uccc.cancerdatasci.org} UCCC_APP_HOSTED_DOMAIN: ${UCCC_APP_HOSTED_DOMAIN:-cuanschutz.edu} UCCC_APP_ADMIN_EMAILS: ${UCCC_APP_ADMIN_EMAILS:-} # Overlay Postgres — the dedicated `uccc_app` database on the shared # `pg_main` server (reached over its docker network `pg`, below). Not the # shared `lake` catalog. UCCC_APP_DB_HOST: ${UCCC_APP_DB_HOST:-pg_main} UCCC_APP_DB_PORT: ${UCCC_APP_DB_PORT:-5432} UCCC_APP_DB_NAME: ${UCCC_APP_DB_NAME:-uccc_app} UCCC_APP_DB_USER: ${UCCC_APP_DB_USER:-uccc_app} UCCC_APP_DB_PASSWORD: ${UCCC_APP_DB_PASSWORD:-} UCCC_APP_OIDC_CLIENT_ID: ${UCCC_APP_OIDC_CLIENT_ID:-} UCCC_APP_OIDC_CLIENT_SECRET: ${UCCC_APP_OIDC_CLIENT_SECRET:-} UCCC_APP_SESSION_SECRET: ${UCCC_APP_SESSION_SECRET:-} # No volume mount: the serving DuckDB is baked into the image (ADR-0023). networks: - internal - pg # React SPA on nginx; serves the app and reverse-proxies /api → api:8000. # This is the only public-facing service. web: build: ./web image: cu-openalex-web:latest restart: unless-stopped depends_on: [api] networks: - proxy - internal labels: - "traefik.enable=true" - "traefik.docker.network=proxy" # One app under the uccc.cancerdatasci.org umbrella. Router/service names # are app-scoped (uccc-insights) so additional .uccc.cancerdatasci.org # services can coexist behind the same Traefik. TLS-ALPN issues a per-host # cert on demand once DNS for this name resolves to the host. - "traefik.http.routers.uccc-insights.rule=Host(`insights.uccc.cancerdatasci.org`)" - "traefik.http.routers.uccc-insights.entrypoints=websecure" - "traefik.http.routers.uccc-insights.tls=true" - "traefik.http.routers.uccc-insights.tls.certresolver=cloudflare" - "traefik.http.services.uccc-insights.loadbalancer.server.port=80" # Strongly recommended for an EAB/leadership tool — gate behind auth. # Reuse the existing dashboard-auth middleware, or add a dedicated one: # - "traefik.http.routers.uccc-insights.middlewares=dashboard-auth@file" networks: # Externally-managed network that the Traefik instance is attached to. proxy: external: true # The shared Postgres server's network — the api service joins it to reach the # `uccc_app` overlay (ADR-0026). Externally managed by the pg_main stack. pg: external: true name: pg_main_stack_default # Private network between web (nginx) and api. internal: driver: bridge