services: postgres: image: pgvector/pgvector:pg16 environment: POSTGRES_DB: "${POSTGRES_DB:-duriin}" POSTGRES_USER: "${POSTGRES_USER:-duriin}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env before starting PostgreSQL}" command: - postgres - -c - shared_buffers=128MB - -c - work_mem=4MB - -c - maintenance_work_mem=64MB - -c - effective_cache_size=256MB - -c - max_connections=40 volumes: - postgres_data:/var/lib/postgresql/data mem_limit: 512m cpus: "0.50" restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 10s timeout: 5s retries: 12 networks: - nginx_proxy_manager_default postgres-migrate: profiles: [migration] build: context: . provenance: false command: node scripts/migrate-sqlite-to-postgres.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data:ro environment: DATABASE_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" SQLITE_ARCHIVE_PATH: /data/archive.sqlite SQLITE_INTELLIGENCE_PATH: /data/intelligence.sqlite depends_on: postgres: condition: service_healthy networks: - nginx_proxy_manager_default # DB backend is one switch for the whole stack and it defaults to sqlite on purpose. # the postgres copy is a stale snapshot (predictions stop around 2026-08-17) and the # migrate script only appends, it never replays UPDATEs, so a `compose up` must not # quietly flip us over. set DURIIN_DB_BACKEND=postgres + DURIIN_USE_POSTGRES=true in # .env only after a fresh intelligence migration has been run and verifyed. api: build: context: . provenance: false env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" DURIIN_RUN_SCHEDULER: "false" AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" depends_on: postgres: condition: service_healthy restart: unless-stopped networks: - nginx_proxy_manager_default # same image as api, but this one actually runs the cron scheduler # (rss / gdelt / edgar / alphavantage / finnhub). it also boots fastify on # 3001 but nothing proxies to it, so its effectivly ingestion only. ingest: build: context: . provenance: false env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" DURIIN_RUN_SCHEDULER: "true" AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" depends_on: postgres: condition: service_healthy restart: unless-stopped networks: - nginx_proxy_manager_default # enrichment chain: queue feeder -> augor -> consolidation -> graph -> signal -> outcome. # this is what fills event_id / content / has_embedding, which the autonomy # reconcilers require before they enqueue anything. enrichment: build: context: . provenance: false command: node workers/index.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" depends_on: postgres: condition: service_healthy restart: unless-stopped networks: - nginx_proxy_manager_default intelligence: # superseded by the "enrichment" service above, kept behind a profile profiles: [legacy] build: context: . provenance: false command: node workers/index.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite restart: unless-stopped networks: - nginx_proxy_manager_default autonomy: build: context: . provenance: false command: node workers/autonomy-entrypoint.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_POLL_MS: "${AUTONOMY_POLL_MS:-5000}" depends_on: postgres: condition: service_healthy cpus: "0.50" mem_limit: 512m restart: unless-stopped networks: - nginx_proxy_manager_default coordinator: build: context: . provenance: false command: node workers/coordinator-entrypoint.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_POLL_MS: "${AUTONOMY_COORDINATOR_POLL_MS:-5000}" depends_on: postgres: condition: service_healthy cpus: "0.75" mem_limit: 768m restart: unless-stopped networks: - nginx_proxy_manager_default autonomy-outcomes: build: context: . provenance: false command: node workers/outcome-autonomy-entrypoint.js env_file: .env volumes: - ./data:/data environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_OUTCOME_POLL_MS: "${AUTONOMY_OUTCOME_POLL_MS:-60000}" depends_on: postgres: condition: service_healthy cpus: "0.25" mem_limit: 384m restart: unless-stopped networks: - nginx_proxy_manager_default replay: build: context: . provenance: false command: node workers/replay-entrypoint.js env_file: .env volumes: - ./config.json:/app/config.json:ro - ./data:/data environment: NODE_ENV: production DURIIN_DB: /data/archive.sqlite INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_REPLAY_POLL_MS: "${AUTONOMY_REPLAY_POLL_MS:-15000}" AUTONOMY_REPLAY_DAILY_BUDGET: "${AUTONOMY_REPLAY_DAILY_BUDGET:-100}" AUTONOMY_REPLAY_WATERMARK_DAYS: "${AUTONOMY_REPLAY_WATERMARK_DAYS:-7}" depends_on: postgres: condition: service_healthy cpus: "0.50" mem_limit: 768m restart: unless-stopped networks: - nginx_proxy_manager_default calibration: build: context: . provenance: false command: node workers/calibration-entrypoint.js env_file: .env volumes: - ./data:/data environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_CALIBRATION_POLL_MS: "${AUTONOMY_CALIBRATION_POLL_MS:-60000}" depends_on: postgres: condition: service_healthy cpus: "0.25" mem_limit: 384m restart: unless-stopped networks: - nginx_proxy_manager_default execution: build: context: . provenance: false command: node workers/execution-entrypoint.js env_file: .env volumes: - ./data:/data environment: NODE_ENV: production INTELLIGENCE_DB: /data/intelligence.sqlite DURIIN_DB_BACKEND: "${DURIIN_DB_BACKEND:-sqlite}" DURIIN_USE_POSTGRES: "${DURIIN_USE_POSTGRES:-false}" DURIIN_POSTGRES_URL: "postgresql://${POSTGRES_USER:-duriin}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-duriin}" AUTONOMY_EXECUTION_MODE: "${AUTONOMY_EXECUTION_MODE:-shadow}" AUTONOMY_DEFAULT_NOTIONAL: "${AUTONOMY_DEFAULT_NOTIONAL:-100}" AUTONOMY_EXECUTION_POLL_MS: "${AUTONOMY_EXECUTION_POLL_MS:-10000}" depends_on: postgres: condition: service_healthy cpus: "0.25" mem_limit: 384m restart: unless-stopped networks: - nginx_proxy_manager_default networks: nginx_proxy_manager_default: external: true volumes: postgres_data: