Files
Duriin-API/docker-compose.yml
T
ImBenjiandClaude Opus 5 6f1d1eee2d fix: restart the stalled autonomy pipeline and make calibration honest
Archive ingestion had been dead since 2026-08-02 because nothing in the
compose stack actually ran it. Everything downstream starved from there.

- add ingest + enrichment services. server.js only starts the scheduler when
  DURIIN_RUN_SCHEDULER is not "false", and workers/index.js was not running at
  all, so articles never got event_id/content/has_embedding and the coordinator
  had nothing to lease.
- pass an explicit origin from coordinatorWorker. it was never passed, so
  acceptProposal defaulted to 'live' and 464 historical backfill predictions
  were recorded as live. that also meant verifyEvidence got a null cutoff and
  skipped its date check entirely.
- coarsen cohortKey to event families + horizon buckets. 201 free text event
  types produced 221 cohorts averaging 2.76 samples, so the n>=30 gate could
  never be reached and everything abstained for the wrong reason.
- gate on cohort diversity, not just sample count. one ticker was roughly half
  of all resolved outcomes, so a pure count gate was measuring one company.
  unknown diversity abstains rather than passing.
- resolve the admin archive db explicitly and probe it. it relied on a
  Dockerfile symlink, and without it better-sqlite3 quietly creates an empty
  file and serves a phantom archive.
- clamp implausible future publication dates at ingest.
- pin the db backend to sqlite by default. compose hardcoded postgres "true",
  which would have overridden the operator's own .env on the next redeploy and
  pointed everything at a stale snapshot.

scripts/repair-autonomy-labels.js relabels the affected rows. it is dry run by
default and has not been applied.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnNxwxfXSbeNtjvtz5gayb
2026-08-29 21:43:24 +01:00

312 lines
9.9 KiB
YAML

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: