name: ${FORGE_COMPOSE_PROJECT:-forge-platform} x-platform-environment: &platform-environment NODE_ENV: production DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL or use DATABASE_PROVIDER=compose} FORGE_DATA_ROOT: ${FORGE_DATA_ROOT:?Set FORGE_DATA_ROOT to an absolute host path} WORKSPACE_PROVIDER: ${WORKSPACE_PROVIDER:-local} WORKSPACE_BACKEND_ID: ${WORKSPACE_BACKEND_ID:-primary} WORKSPACE_ROOT: ${WORKSPACE_ROOT:?Set WORKSPACE_ROOT to an absolute host path} WORKSPACE_NAS_SENTINEL: ${WORKSPACE_NAS_SENTINEL:-.forge-nas-ready} WORKSPACE_BACKENDS_JSON: ${WORKSPACE_BACKENDS_JSON:-} MODEL_CONFIG_ROOT: ${FORGE_DATA_ROOT}/model-config MODEL_SECRET_ROOT: ${FORGE_DATA_ROOT}/model-secrets PUBLISH_ROOT: ${FORGE_DATA_ROOT}/published services: api: image: ${FORGE_PLATFORM_IMAGE:-forge-platform}:${FORGE_IMAGE_TAG:-local} build: context: . dockerfile: docker/platform/Dockerfile args: NPM_REGISTRY: ${BUILD_NPM_REGISTRY:-https://registry.npmjs.org} ALPINE_MIRROR: ${BUILD_ALPINE_MIRROR:-https://mirrors.aliyun.com/alpine} command: ["api"] user: "${FORGE_RUNTIME_UID:?Set FORGE_RUNTIME_UID}:${FORGE_RUNTIME_GID:?Set FORGE_RUNTIME_GID}" environment: <<: *platform-environment PORT: 3001 HOST: 0.0.0.0 WEB_ORIGIN: ${WEB_ORIGIN:-http://localhost:5173} PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost:5173} PUBLIC_SITE_BASE_URL: ${PUBLIC_SITE_BASE_URL:-http://localhost:5173} ENABLE_ACCEPTANCE_AGENT: ${ENABLE_ACCEPTANCE_AGENT:-false} ALLOW_LOCAL_MODEL_KEYS: ${ALLOW_LOCAL_MODEL_KEYS:-false} env_file: - path: ${PLATFORM_ENV_FILE:-.env.platform} required: false ports: - "${FORGE_BIND_ADDRESS:-127.0.0.1}:${FORGE_API_PORT:-3001}:3001" volumes: - type: bind source: ${FORGE_DATA_ROOT} target: ${FORGE_DATA_ROOT} - type: bind source: ${WORKSPACE_ROOT} target: ${WORKSPACE_ROOT} extra_hosts: - host.docker.internal:host-gateway healthcheck: test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:3001/health"] interval: 10s timeout: 3s retries: 12 restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp worker: image: ${FORGE_PLATFORM_IMAGE:-forge-platform}:${FORGE_IMAGE_TAG:-local} command: ["worker"] user: "${FORGE_RUNTIME_UID:?Set FORGE_RUNTIME_UID}:${FORGE_RUNTIME_GID:?Set FORGE_RUNTIME_GID}" group_add: - "${DOCKER_GID:?Set DOCKER_GID to the Docker socket group ID}" environment: <<: *platform-environment WORKER_HEALTH_PORT: 3002 WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-2} WORKER_HEARTBEAT_INTERVAL_MS: ${WORKER_HEARTBEAT_INTERVAL_MS:-5000} GLOBAL_TASK_CONCURRENCY: ${GLOBAL_TASK_CONCURRENCY:-2} PER_USER_TASK_CONCURRENCY: ${PER_USER_TASK_CONCURRENCY:-1} TASK_LEASE_SECONDS: ${TASK_LEASE_SECONDS:-30} TASK_RETRY_BASE_DELAY_SECONDS: ${TASK_RETRY_BASE_DELAY_SECONDS:-5} MAX_TASK_EVENT_BYTES: ${MAX_TASK_EVENT_BYTES:-1048576} MAX_TASK_EVENT_ROWS: ${MAX_TASK_EVENT_ROWS:-500} TASK_EVENT_FLUSH_MS: ${TASK_EVENT_FLUSH_MS:-300} TASK_EVENT_BATCH_BYTES: ${TASK_EVENT_BATCH_BYTES:-32768} TASK_EVENT_COMPACTION_AGE_SECONDS: ${TASK_EVENT_COMPACTION_AGE_SECONDS:-300} TASK_EVENT_COMPACTION_INTERVAL_MS: ${TASK_EVENT_COMPACTION_INTERVAL_MS:-60000} SANDBOX_RUNTIME: ${SANDBOX_RUNTIME:-docker} SANDBOX_IMAGE: ${SANDBOX_IMAGE:-coding-agent-atomcode:4.26} SANDBOX_TIMEOUT_MS: ${SANDBOX_TIMEOUT_MS:-900000} SANDBOX_MEMORY: ${SANDBOX_MEMORY:-2g} SANDBOX_MEMORY_SWAP: ${SANDBOX_MEMORY_SWAP:-2g} SANDBOX_CPUS: ${SANDBOX_CPUS:-1} SANDBOX_PIDS_LIMIT: ${SANDBOX_PIDS_LIMIT:-256} SANDBOX_NETWORK_ACCESS: ${SANDBOX_NETWORK_ACCESS:-egress} DOCKER_SANDBOX_NETWORK: ${DOCKER_SANDBOX_NETWORK:-bridge} SANDBOX_RECONCILE_INTERVAL_MS: ${SANDBOX_RECONCILE_INTERVAL_MS:-5000} ENABLE_ACCEPTANCE_AGENT: ${ENABLE_ACCEPTANCE_AGENT:-false} PLATFORM_SECRETS_MASTER_KEY: "" env_file: - path: ${PLATFORM_ENV_FILE:-.env.platform} required: false expose: - "3002" volumes: - type: bind source: ${FORGE_DATA_ROOT} target: ${FORGE_DATA_ROOT} - type: bind source: ${WORKSPACE_ROOT} target: ${WORKSPACE_ROOT} - /var/run/docker.sock:/var/run/docker.sock extra_hosts: - host.docker.internal:host-gateway depends_on: api: condition: service_healthy healthcheck: test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:3002/health"] interval: 10s timeout: 3s retries: 12 restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp web: image: ${FORGE_WEB_IMAGE:-forge-web}:${FORGE_IMAGE_TAG:-local} build: context: . dockerfile: docker/web/Dockerfile args: VITE_API_URL: "" NPM_REGISTRY: ${BUILD_NPM_REGISTRY:-https://registry.npmjs.org} ALPINE_MIRROR: ${BUILD_ALPINE_MIRROR:-https://mirrors.aliyun.com/alpine} ports: - "${FORGE_BIND_ADDRESS:-127.0.0.1}:${FORGE_WEB_PORT:-5173}:80" environment: FORGE_API_HOST: api FORGE_API_PORT: 3001 depends_on: api: condition: service_healthy restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE - NET_BIND_SERVICE - SETGID - SETUID read_only: true tmpfs: - /etc/nginx/conf.d - /var/cache/nginx - /var/run admin-web: image: ${FORGE_ADMIN_WEB_IMAGE:-forge-admin-web}:${FORGE_IMAGE_TAG:-local} build: context: . dockerfile: docker/admin-web/Dockerfile args: VITE_API_URL: ${ADMIN_API_URL:-} NPM_REGISTRY: ${BUILD_NPM_REGISTRY:-https://registry.npmjs.org} ALPINE_MIRROR: ${BUILD_ALPINE_MIRROR:-https://mirrors.aliyun.com/alpine} ports: - "${FORGE_BIND_ADDRESS:-127.0.0.1}:${FORGE_ADMIN_WEB_PORT:-5181}:80" environment: FORGE_API_HOST: api FORGE_API_PORT: 3001 depends_on: api: condition: service_healthy restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE - NET_BIND_SERVICE - SETGID - SETUID read_only: true tmpfs: - /etc/nginx/conf.d - /var/cache/nginx - /var/run