# ANT — full local stack with only Docker installed. # # cp .env.example .env # put your provider key in it (or export it) # docker compose up -d # open http://localhost:4200 # # Notes: # - Redis is NOT published to the host on purpose: `pnpm dev:infra:redis` # already claims 16379 and the `ant-redis` container name. Debug with # `docker compose exec redis redis-cli`. # - The three ant-cli services share one image build (same tag); preview uses # the Dockerfile's `preview` stage (adds language toolchains for dev servers). # - Workspaces mount at /cross/workspaces (chowned to the non-root `ant` user # in the image). The image-default /data/workspaces is NOT chowned — a named # volume there would be root-owned and Ant would silently fall back to an # ephemeral in-container path. # - This compose publishes 4100/4101/4102/4103/4200/4201 — stop `pnpm dev:all` first. # - :4200 is the app (SPA + control planes); :4201 is the preview/deploy CONTENT # origin. They are deliberately different origins: content is user-authored, and # sharing an origin with the control-plane APIs let a deployed document drive # them with the viewer's session (report H-NEW-001). name: ant # Only ant-api carries the `build:` for this image — three services exporting # the same tag concurrently races under BuildKit ("image already exists"). # ant-realtime / ant-job reference the tag ant-api's build produces. x-ant-cli-common: &ant-cli-common image: ant-cli:local volumes: - ant-workspaces:/cross/workspaces depends_on: redis: condition: service_healthy x-ant-env-common: &ant-env-common ANT_SERVER_MODE: local ANT_REDIS_URL: redis://redis:6379 ANT_WORKSPACE_BASE_PATH: /cross/workspaces x-ant-env-llm: &ant-env-llm # Interpolated from your shell or a root .env file (gitignored). ANT_ENCRYPTION_KEY: ${ANT_ENCRYPTION_KEY:-} # empty = auto-generated onto the workspace volume ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} GEMINI_API_KEY: ${GEMINI_API_KEY:-} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-} GLM_API_KEY: ${GLM_API_KEY:-} KIMI_API_KEY: ${KIMI_API_KEY:-} services: redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redis-data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 12 ant-api: <<: *ant-cli-common build: context: . dockerfile: packages/ant-cli/Dockerfile command: ["tsx", "src/composition/server.ts"] environment: <<: [*ant-env-common, *ant-env-llm] PORT: "4100" ports: - "4100:4100" # direct access for debugging; UI traffic goes through ant-ui ant-realtime: <<: *ant-cli-common command: ["tsx", "src/infrastructure/realtime/start-realtime-server.ts"] environment: <<: *ant-env-common PORT: "4101" healthcheck: # Override the image HEALTHCHECK (it probes :4100, the api port). test: ["CMD", "node", "-e", "require('http').get('http://localhost:4101/health',(r)=>process.exit(r.statusCode===200?0:1))"] interval: 30s timeout: 10s start_period: 10s retries: 3 ports: - "4101:4101" ant-job: <<: *ant-cli-common command: ["tsx", "src/infrastructure/worker/start-job-worker.ts"] environment: <<: [*ant-env-common, *ant-env-llm] ANT_API_URL: http://ant-api:4100 # worker→api HTTP; the localhost default is wrong across containers healthcheck: disable: true # no HTTP surface; the inherited :4100 probe would false-FAIL ant-preview: <<: *ant-cli-common image: ant-cli:local-preview build: context: . dockerfile: packages/ant-cli/Dockerfile target: preview command: ["tsx", "src/infrastructure/preview/start-preview-server.ts"] environment: <<: *ant-env-common PORT: "4102" # control plane: /projects/* management API ANT_PREVIEW_CONTENT_PORT: "4103" # user content: preview + deploy proxies healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:4102/health',(r)=>process.exit(r.statusCode===200?0:1))"] interval: 30s timeout: 10s start_period: 10s retries: 3 ports: - "4102:4102" - "4103:4103" ant-ui: build: context: . dockerfile: packages/ant-ui/Dockerfile args: BUILD_ENV: dev # uses the committed .env.development: empty VITE_* = same-origin routing # Content is served from the :8081 block (published :4201) — a different # origin from the app and its control planes, on purpose (H-NEW-001). VITE_PREVIEW_CONTENT_HOST: http://localhost:4201 volumes: # The image ships no nginx.conf (cloud mounts one from a K8s ConfigMap). - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro ports: - "4200:8080" # app + control planes - "4201:8081" # preview/deploy CONTENT — separate origin, see docker/nginx.conf depends_on: - ant-api - ant-realtime - ant-preview volumes: redis-data: ant-workspaces: