# GTI Fiscal Hub — production compose (RC1). Only two containers: PostgreSQL + Hub. No Kubernetes/Redis/RabbitMQ. # # cd deploy/hub && cp .env.example .env && (edit .env) && docker compose -f docker-compose.prod.yml --env-file .env up -d --build # # PostgreSQL is on an internal network only: it publishes NO port and is not reachable from the LAN/internet. # The Hub binds to HUB_BIND (default 127.0.0.1): put a TLS reverse proxy (HTTPS) in front of it for the Agents. # Migrations run automatically at Hub start (Dockerfile CMD: alembic upgrade head). name: gti-fiscal-hub x-logging: &logging driver: json-file options: max-size: "10m" max-file: "5" services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: gti_fiscal POSTGRES_USER: gti POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required (URL-safe, e.g. openssl rand -hex 24)} volumes: - postgres_data:/var/lib/postgresql/data - ${BACKUP_DIR:-./backups}:/backups networks: [internal] healthcheck: test: ["CMD-SHELL", "pg_isready -U gti -d gti_fiscal"] interval: 10s timeout: 5s retries: 12 logging: *logging fiscal-hub: build: context: ../.. dockerfile: central/Dockerfile image: gti-fiscal-hub:${GTI_FISCAL_VERSION:-1.0.0-rc1} restart: unless-stopped depends_on: postgres: condition: service_healthy environment: APP_ENV: production POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} LOG_LEVEL: ${LOG_LEVEL:-INFO} BASE_URL: ${BASE_URL:?BASE_URL is required (public HTTPS URL of the Hub)} AGENT_ONLINE_THRESHOLD_SECONDS: ${AGENT_ONLINE_THRESHOLD_SECONDS:-120} # Reverse proxy on another host: uvicorn only honours X-Forwarded-For/-Proto # from these addresses (real client IP in audit/session records, scheme=https). FORWARDED_ALLOW_IPS: ${FORWARDED_ALLOW_IPS:-127.0.0.1} # 1 = session/CSRF cookies are sent only over HTTPS. Set once TLS is in front. WEB_COOKIE_SECURE: ${WEB_COOKIE_SECURE:-0} # AGENT_DEV_TOKEN is intentionally NOT set: real Agents are registered with central/scripts/register_agent.py. ports: - "${HUB_BIND:-127.0.0.1}:${HUB_PORT:-8000}:8000" networks: [internal, edge] healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] interval: 15s timeout: 5s retries: 10 start_period: 30s logging: *logging networks: internal: internal: true # no route to the outside: PostgreSQL is never exposed edge: {} volumes: postgres_data: