version: "3.9" # Development compose — hot reload, debug mode services: backend: build: context: . dockerfile: Dockerfile.backend ports: - "8000:8000" volumes: - ./backend:/app/backend - ./configs:/app/configs - ./scripts:/app/scripts - ./data:/app/data - ./.artifacts:/app/.artifacts - /var/run/docker.sock:/var/run/docker.sock environment: - OMNISIGHT_DEBUG=true - OMNISIGHT_FRONTEND_ORIGIN=http://localhost:3000 - OMNISIGHT_DATABASE_PATH=/app/data/omnisight.db - OMNISIGHT_COGNEE_NEO4J_URI=${OMNISIGHT_COGNEE_NEO4J_URI:-bolt://neo4j:7687} - OMNISIGHT_COGNEE_NEO4J_USER=${OMNISIGHT_COGNEE_NEO4J_USER:-neo4j} - OMNISIGHT_COGNEE_NEO4J_PASSWORD=${OMNISIGHT_COGNEE_NEO4J_PASSWORD:-${NEO4J_PASSWORD:-}} env_file: - .env restart: unless-stopped # FX.7.7 — cgroup memory caps. Dev image runs uvicorn --reload + a single # worker so steady-state RSS is ~300-500 MB; 4 GB ceiling tolerates a # debugger / large dataset load, mem_reservation=512m is the soft floor # under host pressure (matches typical dev workload baseline). mem_limit: 4g mem_reservation: 512m healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:8000/readyz"] interval: 30s timeout: 5s retries: 3 start_period: 20s frontend: build: context: . dockerfile: Dockerfile.frontend args: BACKEND_URL: http://backend:8000 ports: - "3000:3000" environment: - BACKEND_URL=http://backend:8000 depends_on: - backend restart: unless-stopped # FX.7.7 — cgroup memory caps. Next.js dev server (hot-reload + bundler) # peaks higher than prod SSR; 2g ceiling matches prod frontend cap so # local OOM behaviour matches what prod hits, mem_reservation=512m # mirrors the prod soft floor. mem_limit: 2g mem_reservation: 512m # ────────────────────────────────────────────────────────────── # O3 (#266): stateless agent worker pool. # Bring up with: docker compose --profile workers up -d # Scale with: docker compose --profile workers up -d --scale worker=3 # ────────────────────────────────────────────────────────────── worker: build: context: . dockerfile: Dockerfile.backend profiles: ["workers"] command: > python3 -m backend.worker run --capacity ${OMNISIGHT_WORKER_CAPACITY:-1} --tenant-filter ${OMNISIGHT_WORKER_TENANT_FILTER:-} --capability-filter ${OMNISIGHT_WORKER_CAPABILITY_FILTER:-} volumes: - ./backend:/app/backend - ./configs:/app/configs - ./scripts:/app/scripts - ./data:/app/data - ./.artifacts:/app/.artifacts - /var/run/docker.sock:/var/run/docker.sock environment: - OMNISIGHT_DATABASE_PATH=/app/data/omnisight.db - OMNISIGHT_COGNEE_NEO4J_URI=${OMNISIGHT_COGNEE_NEO4J_URI:-bolt://neo4j:7687} - OMNISIGHT_COGNEE_NEO4J_USER=${OMNISIGHT_COGNEE_NEO4J_USER:-neo4j} - OMNISIGHT_COGNEE_NEO4J_PASSWORD=${OMNISIGHT_COGNEE_NEO4J_PASSWORD:-${NEO4J_PASSWORD:-}} env_file: - .env depends_on: - backend stop_signal: SIGTERM stop_grace_period: 60s restart: unless-stopped # FX.7.7 — cgroup memory caps. Worker is stateless agent runtime; # baseline ~150 MB, occasional bursts during large doc/code execution. # 2g ceiling stops a runaway agent loop from starving backend, 256m # reservation is the host-pressure soft floor (workers scale ≥ 1, so # the floor is per-replica, not aggregate). mem_limit: 2g mem_reservation: 256m # ────────────────────────────────────────────────────────────── # OP-852 (Sprint C / C3): Neo4j graph store for Cognee KG. # Lives behind the "cognee" profile so default `docker compose up` # workflows are unaffected. Bring up with: # docker compose --profile cognee up -d neo4j # The runtime adapter (`backend.agents.cognee_integration`) and the # nightly rebuild script (`scripts/cognee_full_rebuild.py`) read # connection details from OMNISIGHT_COGNEE_NEO4J_* env vars. OP-899 # refuses the default "neo4j" password; set NEO4J_PASSWORD or # OMNISIGHT_COGNEE_NEO4J_PASSWORD before enabling this profile. # See docs/operations/cognee-runbook.md for bring-up + recovery. # ────────────────────────────────────────────────────────────── neo4j: image: neo4j:5.24-community profiles: ["cognee"] ports: - "7474:7474" # HTTP browser (optional — disable in prod) - "7687:7687" # Bolt protocol (driver clients connect here) environment: - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD:?set NEO4J_PASSWORD to a non-default strong value} - NEO4J_PLUGINS=["apoc"] - NEO4J_dbms_memory_heap_initial__size=512m - NEO4J_dbms_memory_heap_max__size=2G - NEO4J_dbms_memory_pagecache_size=512m volumes: - /var/lib/omnisight/neo4j/data:/data - /var/lib/omnisight/neo4j/logs:/logs - /var/lib/omnisight/neo4j/import:/var/lib/neo4j/import - /var/lib/omnisight/neo4j/plugins:/plugins healthcheck: test: ["CMD-SHELL", "cypher-shell -u neo4j -p ${NEO4J_PASSWORD:?set NEO4J_PASSWORD} 'RETURN 1' >/dev/null 2>&1 || exit 1"] interval: 30s timeout: 10s retries: 5 start_period: 30s restart: unless-stopped # OP-852 cgroup memory caps. Heap 2G + pagecache 512m + JVM overhead # → 3g ceiling tolerates startup + sustained ECL ingest, 1g floor # keeps the daily backup snapshot from getting evicted under host # pressure. Scale up the heap envs above if the KG grows past ~50k # nodes. mem_limit: 3g mem_reservation: 1g # ────────────────────────────────────────────────────────────── # OP-901 (F3): Graphiti temporal-memory MCP service. # Bring up with: docker compose --profile graphiti up -d graphiti # Caddy exposes it at mcp-graphiti.sora.services; ingestion bridges # authenticate to /ingest/* with OMNISIGHT_MCP_GRAPHITI_TOKEN. # ────────────────────────────────────────────────────────────── graphiti: image: zepai/graphiti:latest profiles: ["graphiti"] ports: - "${OMNISIGHT_GRAPHITI_PORT:-8003}:8000" environment: - PORT=8000 - GRAPHITI_TELEMETRY_ENABLED=${GRAPHITI_TELEMETRY_ENABLED:-false} - OMNISIGHT_MCP_GRAPHITI_TOKEN=${OMNISIGHT_MCP_GRAPHITI_TOKEN:?set OMNISIGHT_MCP_GRAPHITI_TOKEN} - OPENAI_API_KEY=${OPENAI_API_KEY:-} env_file: - .env healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/v1/health')"] interval: 30s timeout: 5s retries: 3 start_period: 30s restart: unless-stopped mem_limit: 2g mem_reservation: 512m