services: mongo: image: mongo:7 restart: unless-stopped # modest cache so the whole stack fits comfortably on small hosts command: ["mongod", "--wiredTigerCacheSizeGB", "0.5"] # WiredTiger needs far more FDs than the 1024 some hosts default to; # running out crashes mongod with a WT_PANIC during file creation ulimits: nofile: soft: 64000 hard: 64000 # Mongo is reachable only on the compose network; never published to the # host (§12). volumes: - mongo-data:/data/db healthcheck: test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"] interval: 10s timeout: 5s retries: 5 start_period: 15s app: build: . restart: unless-stopped env_file: .env environment: MONGO_URI: mongodb://mongo:27017 # in-network address handed to the external services for callbacks # and signed attachment URLs (§5.2 example: http://app:8787/…) APP_INTERNAL_URL: http://app:8787 ports: - "127.0.0.1:${APP_PORT:-8787}:8787" depends_on: mongo: condition: service_healthy healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8787/healthz"] interval: 10s timeout: 5s retries: 3 start_period: 10s # --- placeholder external services (docker compose --profile mocks up) --- # Two fully independent services: separate builds, ports, tokens (§5). atomizer-mock: build: ./services/atomizer profiles: ["mocks"] restart: unless-stopped environment: PORT: "8090" ATOMIZER_TOKEN: ${ATOMIZER_TOKEN} ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} ANTHROPIC_OPENAI_BASE_URL: ${ANTHROPIC_OPENAI_BASE_URL:-https://api.anthropic.com/v1} ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-4-6} LLM_API_STYLE: ${LLM_API_STYLE:-openai} ports: - "127.0.0.1:8090:8090" healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8090/healthz"] interval: 10s timeout: 5s retries: 3 start_period: 5s work-performer: build: ./services/work-performer profiles: ["mocks"] restart: unless-stopped environment: PORT: "8091" WORK_PERFORMER_TOKEN: ${WORK_PERFORMER_TOKEN} PUBLIC_BASE_URL: http://work-performer:8091 # host Claude Code config/secrets (§9.2); mounted into the node user's # home because claude refuses --dangerously-skip-permissions as root volumes: - ${HOME}/.claude:/home/node/.claude - ${HOME}/.claude.json:/home/node/.claude.json - work-data:/work ports: - "127.0.0.1:8091:8091" extra_hosts: - "host.docker.internal:host-gateway" healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:8091/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""] interval: 10s timeout: 5s retries: 3 start_period: 5s volumes: mongo-data: work-data: