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
312 lines
9.9 KiB
YAML
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:
|