From 20923741dd358795773352649e263694bb55daf1 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:17:58 +0000 Subject: [PATCH 01/12] feat: add Docker Compose setup for local development --- DOCKER_DEV.md | 160 +++++++++++++++++++++++++++++++ Makefile | 17 ++++ dev/Dockerfile.dev | 16 ++++ dev/dev.sh | 49 ++++++++++ dev/docker-compose.dev.yml | 192 +++++++++++++++++++++++++++++++++++++ 5 files changed, 434 insertions(+) create mode 100644 DOCKER_DEV.md create mode 100644 Makefile create mode 100644 dev/Dockerfile.dev create mode 100755 dev/dev.sh create mode 100644 dev/docker-compose.dev.yml diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md new file mode 100644 index 000000000..648275261 --- /dev/null +++ b/DOCKER_DEV.md @@ -0,0 +1,160 @@ +# Docker-based Local Development + +Run the entire monorepo (Next.js backend + all Cloudflare Workers + PostgreSQL) with a single command instead of managing ~16 terminal windows. + +## Prerequisites + +| Tool | Version | Check | +|------|---------|-------| +| Docker | 20.10+ | `docker --version` | +| Docker Compose v2 | 2.20+ | `docker compose version` | +| pnpm | 10.27.0 | `pnpm --version` | +| Node.js | ^22 | `node --version` | + +> **Note:** `pnpm install` must be run at least once before starting — the Docker containers mount the repo and expect `node_modules` to exist. + +## Quick Start + +```bash +# Install dependencies (if not already done) +pnpm install + +# Start everything +./dev/dev.sh + +# Or directly with docker compose +docker compose -f dev/docker-compose.dev.yml --profile all up +``` + +## Profiles + +Services are grouped into profiles for selective startup. PostgreSQL always starts (no profile required). + +| Profile | Services | +|---------|----------| +| `core` | PostgreSQL + Next.js backend | +| `agents` | PostgreSQL + cloud-agent + cloud-agent-next | +| `workers` | PostgreSQL + all Cloudflare Workers | +| `all` | Everything | + +```bash +# Core only (postgres + nextjs) +./dev/dev.sh --profile core up + +# Core + agents +./dev/dev.sh --profile core --profile agents up + +# Everything +./dev/dev.sh # defaults to --profile all +./dev/dev.sh --profile all up + +# Specific services by name +docker compose -f dev/docker-compose.dev.yml up postgres nextjs cloud-agent +``` + +## Port Map + +| Service | Port | Directory | +|---------|------|-----------| +| PostgreSQL | 5432 | — | +| Next.js backend | 3000 | `.` (root) | +| cloud-agent | 8788 | `cloud-agent/` | +| cloudflare-ai-attribution | 8787 | `cloudflare-ai-attribution/` | +| cloudflare-code-review-infra | 8789 | `cloudflare-code-review-infra/` | +| cloudflare-app-builder | 8790 | `cloudflare-app-builder/` | +| cloudflare-auto-triage-infra | 8791 | `cloudflare-auto-triage-infra/` | +| cloudflare-webhook-agent-ingest | 8793 | `cloudflare-webhook-agent-ingest/` | +| cloud-agent-next | 8794 | `cloud-agent-next/` | +| kiloclaw | 8795 | `kiloclaw/` | +| cloudflare-auto-fix-infra | 8796 | `cloudflare-auto-fix-infra/` | +| cloudflare-db-proxy | 8797 | `cloudflare-db-proxy/` | +| cloudflare-deploy-builder | 8798 | `cloudflare-deploy-infra/builder/` | +| cloudflare-deploy-dispatcher | 8799 | `cloudflare-deploy-infra/dispatcher/` | +| cloudflare-session-ingest | 8800 | `cloudflare-session-ingest/` | +| cloudflare-o11y | 8801 | `cloudflare-o11y/` | +| cloudflare-git-token-service | 8802 | `cloudflare-git-token-service/` | + +> Ports are overridden via `--port` in the wrangler dev command to avoid conflicts between workers that share the same default port in their `wrangler.jsonc`. + +## Environment Variables + +### Next.js backend + +The Next.js service loads `env_file: .env` from the repo root. Make sure this file exists: + +```bash +# If you have an .env.example, copy it +cp .env.example .env +# Then fill in the required values +``` + +### Cloudflare Workers + +Workers that need secrets use `.dev.vars` files in their respective directories. Copy the examples: + +```bash +# Example for cloud-agent +cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars + +# Workers with .dev.vars.example files: +# cloud-agent, cloud-agent-next, cloudflare-app-builder, +# cloudflare-auto-fix-infra, cloudflare-auto-triage-infra, +# cloudflare-code-review-infra, cloudflare-db-proxy, +# cloudflare-deploy-infra/builder, cloudflare-deploy-infra/dispatcher, +# cloudflare-git-token-service, cloudflare-webhook-agent-ingest, +# kiloclaw +``` + +## Architecture + +- **Shared Docker image** (`dev/Dockerfile.dev`): `node:22-slim` with pnpm, wrangler, and bun pre-installed. +- **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). +- **`network_mode: host`**: All services bind directly to the host network, so they can reach each other on `localhost` just like bare-metal dev. +- **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. + +## Troubleshooting + +### Port already in use + +If a service fails with `EADDRINUSE`, another process is already using that port. Check with: + +```bash +lsof -i : +# or +ss -tlnp | grep +``` + +### Worker fails to start + +Some workers (cloud-agent, cloud-agent-next) have `predev` scripts that build a wrapper using `bun`. The Docker image includes bun, but if you see build errors, try: + +```bash +# Rebuild the Docker image +docker compose -f dev/docker-compose.dev.yml build --no-cache +``` + +### node_modules issues + +The containers mount the host's `node_modules`. If you see missing dependency errors: + +```bash +pnpm install +# Then restart the containers +docker compose -f dev/docker-compose.dev.yml --profile all restart +``` + +### Viewing logs for a single service + +```bash +docker compose -f dev/docker-compose.dev.yml logs -f nextjs +docker compose -f dev/docker-compose.dev.yml logs -f cloud-agent +``` + +### Stopping everything + +```bash +docker compose -f dev/docker-compose.dev.yml --profile all down + +# Also remove the postgres volume if you want a fresh database +docker compose -f dev/docker-compose.dev.yml --profile all down -v +``` diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..0815642e7 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +# Makefile — convenience targets for kilocode-backend + +COMPOSE_DEV := docker compose -f dev/docker-compose.dev.yml + +.PHONY: dev-docker dev-docker-core dev-docker-down + +## Start all services in Docker (postgres + nextjs + all workers) +dev-docker: + ./dev/dev.sh + +## Start only postgres + nextjs +dev-docker-core: + $(COMPOSE_DEV) --profile core up + +## Stop all Docker dev services +dev-docker-down: + $(COMPOSE_DEV) --profile all down diff --git a/dev/Dockerfile.dev b/dev/Dockerfile.dev new file mode 100644 index 000000000..42c120af6 --- /dev/null +++ b/dev/Dockerfile.dev @@ -0,0 +1,16 @@ +FROM node:22-slim + +# Install pnpm +RUN corepack enable && corepack prepare pnpm@10.27.0 --activate + +# Install wrangler globally for workers +RUN pnpm add -g wrangler + +# Install bun (needed by cloud-agent and cloud-agent-next predev scripts) +RUN apt-get update && apt-get install -y curl unzip && \ + curl -fsSL https://bun.sh/install | bash && \ + ln -s /root/.bun/bin/bun /usr/local/bin/bun && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app diff --git a/dev/dev.sh b/dev/dev.sh new file mode 100755 index 000000000..23f8eb187 --- /dev/null +++ b/dev/dev.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Start the Docker Compose dev environment for the monorepo. +# +# Usage: +# ./dev/dev.sh # start everything +# ./dev/dev.sh --profile core # postgres + nextjs +# ./dev/dev.sh --profile core --profile agents # core + cloud-agent services +# ./dev/dev.sh --profile workers # postgres + all CF workers +# ./dev/dev.sh up postgres nextjs cloud-agent # specific services only + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +COMPOSE_FILE="$REPO_ROOT/dev/docker-compose.dev.yml" + +# ── Preflight checks ───────────────────────────────────────────────── + +if ! command -v docker &>/dev/null; then + echo "❌ Docker is not installed. Please install Docker first." + echo " https://docs.docker.com/get-docker/" + exit 1 +fi + +if ! docker compose version &>/dev/null; then + echo "❌ Docker Compose v2 is required (docker compose, not docker-compose)." + echo " https://docs.docker.com/compose/install/" + exit 1 +fi + +if [ ! -d "$REPO_ROOT/node_modules" ]; then + echo "⚠️ node_modules not found. Running pnpm install first..." + (cd "$REPO_ROOT" && pnpm install --frozen-lockfile) +fi + +if [ ! -f "$REPO_ROOT/.env" ]; then + echo "⚠️ No .env file found at repo root." + echo " Copy .env.example or create one before the Next.js service can start." +fi + +# ── Launch ──────────────────────────────────────────────────────────── + +# If no arguments provided, start everything with the "all" profile +if [ $# -eq 0 ]; then + echo "🚀 Starting all services..." + exec docker compose -f "$COMPOSE_FILE" --profile all up +else + echo "🚀 Starting services..." + exec docker compose -f "$COMPOSE_FILE" "$@" +fi diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml new file mode 100644 index 000000000..1480c4a12 --- /dev/null +++ b/dev/docker-compose.dev.yml @@ -0,0 +1,192 @@ +# Docker Compose dev orchestration for the full monorepo. +# +# Usage: +# docker compose -f dev/docker-compose.dev.yml up # everything +# docker compose -f dev/docker-compose.dev.yml up postgres nextjs # core only +# docker compose -f dev/docker-compose.dev.yml up postgres cloud-agent # postgres + one worker +# docker compose -f dev/docker-compose.dev.yml up --profile core # postgres + nextjs +# docker compose -f dev/docker-compose.dev.yml up --profile core --profile agents # core + agents + +x-worker-base: &worker-base + build: + context: .. + dockerfile: dev/Dockerfile.dev + volumes: + - ..:/app + network_mode: host + restart: unless-stopped + +services: + # ── PostgreSQL (always starts — no profile) ───────────────────────── + postgres: + image: pgvector/pgvector:pg18 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + network_mode: host + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 3s + retries: 5 + + # ── 1. Next.js backend (port 3000) ────────────────────────────────── + nextjs: + <<: *worker-base + profiles: ["core", "all"] + env_file: + - ../.env + depends_on: + postgres: + condition: service_healthy + command: sh -c "pnpm install --frozen-lockfile && pnpm dev" + + # ── 2. cloud-agent (port 8788) ───────────────────────────────────── + cloud-agent: + <<: *worker-base + profiles: ["agents", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloud-agent && pnpm dev --port 8788" + + # ── 3. cloud-agent-next (port 8794) ──────────────────────────────── + cloud-agent-next: + <<: *worker-base + profiles: ["agents", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloud-agent-next && pnpm dev --port 8794" + + # ── 4. cloudflare-ai-attribution (port 8787) ─────────────────────── + cloudflare-ai-attribution: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-ai-attribution && pnpm dev --port 8787" + + # ── 5. cloudflare-app-builder (port 8790) ────────────────────────── + cloudflare-app-builder: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-app-builder && pnpm dev --port 8790" + + # ── 6. cloudflare-auto-fix-infra (port 8796) ────────────────────── + cloudflare-auto-fix-infra: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + cloud-agent: + condition: service_started + command: sh -c "cd /app/cloudflare-auto-fix-infra && pnpm dev --port 8796" + + # ── 7. cloudflare-auto-triage-infra (port 8791) ─────────────────── + cloudflare-auto-triage-infra: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + cloud-agent: + condition: service_started + command: sh -c "cd /app/cloudflare-auto-triage-infra && pnpm dev --port 8791" + + # ── 8. cloudflare-code-review-infra (port 8789) ─────────────────── + cloudflare-code-review-infra: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + cloud-agent: + condition: service_started + command: sh -c "cd /app/cloudflare-code-review-infra && pnpm dev --port 8789" + + # ── 9. cloudflare-db-proxy (port 8797) ───────────────────────────── + cloudflare-db-proxy: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-db-proxy && pnpm dev --port 8797" + + # ── 10. cloudflare-deploy-infra/builder (port 8798) ──────────────── + cloudflare-deploy-builder: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-deploy-infra/builder && pnpm dev --port 8798" + + # ── 11. cloudflare-deploy-infra/dispatcher (port 8799) ───────────── + cloudflare-deploy-dispatcher: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-deploy-infra/dispatcher && pnpm dev --port 8799" + + # ── 12. cloudflare-session-ingest (port 8800) ───────────────────── + cloudflare-session-ingest: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-session-ingest && pnpm dev --port 8800" + + # ── 13. cloudflare-o11y (port 8801) ──────────────────────────────── + cloudflare-o11y: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-o11y && pnpm dev --port 8801" + + # ── 14. cloudflare-webhook-agent-ingest (port 8793) ──────────────── + cloudflare-webhook-agent-ingest: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + cloud-agent: + condition: service_started + command: sh -c "cd /app/cloudflare-webhook-agent-ingest && pnpm dev --port 8793" + + # ── 15. cloudflare-git-token-service (port 8802) ────────────────── + cloudflare-git-token-service: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/cloudflare-git-token-service && pnpm dev --port 8802" + + # ── 16. kiloclaw (port 8795) ─────────────────────────────────────── + kiloclaw: + <<: *worker-base + profiles: ["workers", "all"] + depends_on: + postgres: + condition: service_healthy + command: sh -c "cd /app/kiloclaw && pnpm dev --port 8795" + +volumes: + postgres_data: From c6892067f2038e02362d136e4de83cdef095bd2b Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:41:02 +0000 Subject: [PATCH 02/12] fix: address PR review comments - DOCKER_DEV.md: document that network_mode: host is Linux-only - Dockerfile.dev: pin wrangler to 4.61.1 for reproducibility - Dockerfile.dev: pin bun to v1.2.5 for reproducibility - docker-compose.dev.yml: fix postgres volume path to /var/lib/postgresql/data - docker-compose.dev.yml: remove pnpm install from nextjs container command --- DOCKER_DEV.md | 2 +- dev/Dockerfile.dev | 8 ++++---- dev/docker-compose.dev.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 648275261..bb9eff93f 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -109,7 +109,7 @@ cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars - **Shared Docker image** (`dev/Dockerfile.dev`): `node:22-slim` with pnpm, wrangler, and bun pre-installed. - **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). -- **`network_mode: host`**: All services bind directly to the host network, so they can reach each other on `localhost` just like bare-metal dev. +- **`network_mode: host`**: All services bind directly to the host network, so they can reach each other on `localhost` just like bare-metal dev. **Note:** `network_mode: host` only works natively on Linux. Docker Desktop (macOS/Windows) does not support host networking the same way. If you're on macOS or Windows, you'll need to replace `network_mode: host` with explicit `ports:` mappings in `docker-compose.dev.yml`. - **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. ## Troubleshooting diff --git a/dev/Dockerfile.dev b/dev/Dockerfile.dev index 42c120af6..a1d52ef5e 100644 --- a/dev/Dockerfile.dev +++ b/dev/Dockerfile.dev @@ -3,13 +3,13 @@ FROM node:22-slim # Install pnpm RUN corepack enable && corepack prepare pnpm@10.27.0 --activate -# Install wrangler globally for workers -RUN pnpm add -g wrangler +# Install wrangler globally for workers (pinned to match repo devDependencies) +RUN pnpm add -g wrangler@4.61.1 # Install bun (needed by cloud-agent and cloud-agent-next predev scripts) +# Pinned to a specific version for reproducibility RUN apt-get update && apt-get install -y curl unzip && \ - curl -fsSL https://bun.sh/install | bash && \ - ln -s /root/.bun/bin/bun /usr/local/bin/bun && \ + curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash -s "bun-v1.2.5" && \ apt-get clean && rm -rf /var/lib/apt/lists/* # Set working directory diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index 1480c4a12..b70729cf6 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -27,7 +27,7 @@ services: network_mode: host restart: unless-stopped volumes: - - postgres_data:/var/lib/postgresql + - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s @@ -43,7 +43,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "pnpm install --frozen-lockfile && pnpm dev" + command: sh -c "pnpm dev" # ── 2. cloud-agent (port 8788) ───────────────────────────────────── cloud-agent: From 5996a6f55d971438cbd3b727da13397ee892014d Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:45:25 +0000 Subject: [PATCH 03/12] fix: replace network_mode: host with explicit ports for macOS compatibility - Replace network_mode: host with explicit ports: mappings (host networking is Linux-only and doesn't work on Docker Desktop for macOS) - Add --ip 0.0.0.0 to wrangler dev commands so services bind to all interfaces inside the container (required for port forwarding) - Add extra_hosts for host.docker.internal resolution - Update DOCKER_DEV.md: remove ss command (Linux-only), use lsof only, update architecture section to reflect ports-based approach - Change prerequisite from Docker 20.10+ to Docker Desktop 4.x+ --- DOCKER_DEV.md | 6 ++-- dev/docker-compose.dev.yml | 68 ++++++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index bb9eff93f..1e4cb7e32 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -6,7 +6,7 @@ Run the entire monorepo (Next.js backend + all Cloudflare Workers + PostgreSQL) | Tool | Version | Check | |------|---------|-------| -| Docker | 20.10+ | `docker --version` | +| Docker Desktop | 4.x+ | `docker --version` | | Docker Compose v2 | 2.20+ | `docker compose version` | | pnpm | 10.27.0 | `pnpm --version` | | Node.js | ^22 | `node --version` | @@ -109,7 +109,7 @@ cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars - **Shared Docker image** (`dev/Dockerfile.dev`): `node:22-slim` with pnpm, wrangler, and bun pre-installed. - **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). -- **`network_mode: host`**: All services bind directly to the host network, so they can reach each other on `localhost` just like bare-metal dev. **Note:** `network_mode: host` only works natively on Linux. Docker Desktop (macOS/Windows) does not support host networking the same way. If you're on macOS or Windows, you'll need to replace `network_mode: host` with explicit `ports:` mappings in `docker-compose.dev.yml`. +- **Port mappings**: Each service exposes its port via explicit `ports:` mappings, which works on both macOS (Docker Desktop) and Linux. - **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. ## Troubleshooting @@ -120,8 +120,6 @@ If a service fails with `EADDRINUSE`, another process is already using that port ```bash lsof -i : -# or -ss -tlnp | grep ``` ### Worker fails to start diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index b70729cf6..3ecbde20a 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -13,7 +13,8 @@ x-worker-base: &worker-base dockerfile: dev/Dockerfile.dev volumes: - ..:/app - network_mode: host + extra_hosts: + - "host.docker.internal:host-gateway" restart: unless-stopped services: @@ -24,7 +25,8 @@ services: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres - network_mode: host + ports: + - "5432:5432" restart: unless-stopped volumes: - postgres_data:/var/lib/postgresql/data @@ -38,6 +40,8 @@ services: nextjs: <<: *worker-base profiles: ["core", "all"] + ports: + - "3000:3000" env_file: - ../.env depends_on: @@ -49,144 +53,174 @@ services: cloud-agent: <<: *worker-base profiles: ["agents", "all"] + ports: + - "8788:8788" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloud-agent && pnpm dev --port 8788" + command: sh -c "cd /app/cloud-agent && pnpm dev --port 8788 --ip 0.0.0.0" # ── 3. cloud-agent-next (port 8794) ──────────────────────────────── cloud-agent-next: <<: *worker-base profiles: ["agents", "all"] + ports: + - "8794:8794" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloud-agent-next && pnpm dev --port 8794" + command: sh -c "cd /app/cloud-agent-next && pnpm dev --port 8794 --ip 0.0.0.0" # ── 4. cloudflare-ai-attribution (port 8787) ─────────────────────── cloudflare-ai-attribution: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8787:8787" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-ai-attribution && pnpm dev --port 8787" + command: sh -c "cd /app/cloudflare-ai-attribution && pnpm dev --port 8787 --ip 0.0.0.0" # ── 5. cloudflare-app-builder (port 8790) ────────────────────────── cloudflare-app-builder: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8790:8790" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-app-builder && pnpm dev --port 8790" + command: sh -c "cd /app/cloudflare-app-builder && pnpm dev --port 8790 --ip 0.0.0.0" # ── 6. cloudflare-auto-fix-infra (port 8796) ────────────────────── cloudflare-auto-fix-infra: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8796:8796" depends_on: postgres: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-auto-fix-infra && pnpm dev --port 8796" + command: sh -c "cd /app/cloudflare-auto-fix-infra && pnpm dev --port 8796 --ip 0.0.0.0" # ── 7. cloudflare-auto-triage-infra (port 8791) ─────────────────── cloudflare-auto-triage-infra: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8791:8791" depends_on: postgres: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-auto-triage-infra && pnpm dev --port 8791" + command: sh -c "cd /app/cloudflare-auto-triage-infra && pnpm dev --port 8791 --ip 0.0.0.0" # ── 8. cloudflare-code-review-infra (port 8789) ─────────────────── cloudflare-code-review-infra: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8789:8789" depends_on: postgres: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-code-review-infra && pnpm dev --port 8789" + command: sh -c "cd /app/cloudflare-code-review-infra && pnpm dev --port 8789 --ip 0.0.0.0" # ── 9. cloudflare-db-proxy (port 8797) ───────────────────────────── cloudflare-db-proxy: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8797:8797" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-db-proxy && pnpm dev --port 8797" + command: sh -c "cd /app/cloudflare-db-proxy && pnpm dev --port 8797 --ip 0.0.0.0" # ── 10. cloudflare-deploy-infra/builder (port 8798) ──────────────── cloudflare-deploy-builder: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8798:8798" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-deploy-infra/builder && pnpm dev --port 8798" + command: sh -c "cd /app/cloudflare-deploy-infra/builder && pnpm dev --port 8798 --ip 0.0.0.0" # ── 11. cloudflare-deploy-infra/dispatcher (port 8799) ───────────── cloudflare-deploy-dispatcher: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8799:8799" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-deploy-infra/dispatcher && pnpm dev --port 8799" + command: sh -c "cd /app/cloudflare-deploy-infra/dispatcher && pnpm dev --port 8799 --ip 0.0.0.0" # ── 12. cloudflare-session-ingest (port 8800) ───────────────────── cloudflare-session-ingest: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8800:8800" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-session-ingest && pnpm dev --port 8800" + command: sh -c "cd /app/cloudflare-session-ingest && pnpm dev --port 8800 --ip 0.0.0.0" # ── 13. cloudflare-o11y (port 8801) ──────────────────────────────── cloudflare-o11y: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8801:8801" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-o11y && pnpm dev --port 8801" + command: sh -c "cd /app/cloudflare-o11y && pnpm dev --port 8801 --ip 0.0.0.0" # ── 14. cloudflare-webhook-agent-ingest (port 8793) ──────────────── cloudflare-webhook-agent-ingest: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8793:8793" depends_on: postgres: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-webhook-agent-ingest && pnpm dev --port 8793" + command: sh -c "cd /app/cloudflare-webhook-agent-ingest && pnpm dev --port 8793 --ip 0.0.0.0" # ── 15. cloudflare-git-token-service (port 8802) ────────────────── cloudflare-git-token-service: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8802:8802" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-git-token-service && pnpm dev --port 8802" + command: sh -c "cd /app/cloudflare-git-token-service && pnpm dev --port 8802 --ip 0.0.0.0" # ── 16. kiloclaw (port 8795) ─────────────────────────────────────── kiloclaw: <<: *worker-base profiles: ["workers", "all"] + ports: + - "8795:8795" depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/kiloclaw && pnpm dev --port 8795" + command: sh -c "cd /app/kiloclaw && pnpm dev --port 8795 --ip 0.0.0.0" volumes: postgres_data: From 1aa81074d8a6b08b9c5090ddb77469ded349c262 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:11:59 +0000 Subject: [PATCH 04/12] fix: inter-service networking for Docker Compose bridge network MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add dev/docker-wrangler-entrypoint.sh that patches wrangler.jsonc at startup, replacing localhost references with Docker service names (e.g., localhost:3000 → nextjs:3000, localhost:5432 → postgres:5432) - Override Next.js env vars (POSTGRES_URL, CLOUD_AGENT_API_URL, WEBHOOK_AGENT_URL) in docker-compose to use Docker service names - Add networking documentation to DOCKER_DEV.md explaining how inter-service communication works on the bridge network - Workers now use the entrypoint script instead of raw pnpm dev, which also handles predev scripts (e.g., cloud-agent wrapper build) All services share the default Docker Compose bridge network and reach each other by service name via Docker DNS. No network_mode: host needed, so this works on both macOS (Docker Desktop) and Linux. --- DOCKER_DEV.md | 32 ++++++++++++++++ dev/docker-compose.dev.yml | 52 +++++++++++++++++-------- dev/docker-wrangler-entrypoint.sh | 63 +++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 15 deletions(-) create mode 100755 dev/docker-wrangler-entrypoint.sh diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 1e4cb7e32..91ba4a232 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -76,6 +76,23 @@ docker compose -f dev/docker-compose.dev.yml up postgres nextjs cloud-agent > Ports are overridden via `--port` in the wrangler dev command to avoid conflicts between workers that share the same default port in their `wrangler.jsonc`. +## Networking + +All services share the default Docker Compose bridge network. Services reach each other by their Compose service name (e.g., `postgres`, `nextjs`, `cloud-agent`) via Docker's built-in DNS — no `network_mode: host` needed, so this works on both **macOS** (Docker Desktop) and **Linux**. + +### How inter-service URLs are resolved + +The wrangler.jsonc files in each worker hardcode `localhost` for inter-service URLs (e.g., `KILOCODE_BACKEND_BASE_URL: "http://localhost:3000"`, Hyperdrive `localConnectionString: "postgres://...@localhost:5432/..."`). Inside Docker containers on a bridge network, `localhost` refers to the container itself, not other services. + +To fix this without modifying the shared wrangler.jsonc files: + +- **Wrangler workers** use [`dev/docker-wrangler-entrypoint.sh`](dev/docker-wrangler-entrypoint.sh) which creates a temporary patched copy of `wrangler.jsonc` at startup, replacing `localhost` references with Docker service names (e.g., `localhost:3000` → `nextjs:3000`, `localhost:5432` → `postgres:5432`). +- **Next.js** overrides env vars directly via the `environment:` key in docker-compose (e.g., `POSTGRES_URL`, `CLOUD_AGENT_API_URL`), which take precedence over values in `.env`. + +### Accessing services from the host + +From your host machine, services are still accessible at `localhost:` via Docker's port forwarding (the `ports:` mappings in docker-compose). + ## Environment Variables ### Next.js backend @@ -88,6 +105,8 @@ cp .env.example .env # Then fill in the required values ``` +The docker-compose file overrides `POSTGRES_URL`, `CLOUD_AGENT_API_URL`, and `WEBHOOK_AGENT_URL` to use Docker service names. You don't need to set these in `.env` for Docker dev. + ### Cloudflare Workers Workers that need secrets use `.dev.vars` files in their respective directories. Copy the examples: @@ -105,11 +124,14 @@ cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars # kiloclaw ``` +> **Note:** You do NOT need to change `localhost` references in `.dev.vars` files for Docker — the entrypoint script handles URL rewriting automatically via the wrangler.jsonc patching. + ## Architecture - **Shared Docker image** (`dev/Dockerfile.dev`): `node:22-slim` with pnpm, wrangler, and bun pre-installed. - **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). - **Port mappings**: Each service exposes its port via explicit `ports:` mappings, which works on both macOS (Docker Desktop) and Linux. +- **Inter-service networking**: Docker Compose bridge network with DNS-based service discovery. See [Networking](#networking) above. - **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. ## Troubleshooting @@ -141,6 +163,16 @@ pnpm install docker compose -f dev/docker-compose.dev.yml --profile all restart ``` +### Inter-service connection refused + +If a worker can't reach another service (e.g., `ECONNREFUSED` to `nextjs:3000`), make sure the target service is running. Check with: + +```bash +docker compose -f dev/docker-compose.dev.yml ps +``` + +Services only start if their profile is active. For example, `cloud-agent` requires the `agents` or `all` profile. + ### Viewing logs for a single service ```bash diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index 3ecbde20a..f5248f43a 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -1,5 +1,18 @@ # Docker Compose dev orchestration for the full monorepo. # +# Networking: All services share the default bridge network created by Docker +# Compose. Services can reach each other by their Compose service name (e.g., +# "postgres", "nextjs", "cloud-agent") via Docker's built-in DNS. This works +# on both macOS (Docker Desktop) and Linux — no network_mode: host needed. +# +# Because wrangler.jsonc files hardcode "localhost" for inter-service URLs +# (Hyperdrive connection strings, KILOCODE_BACKEND_BASE_URL, etc.), wrangler +# workers use dev/docker-wrangler-entrypoint.sh which creates a patched copy +# of wrangler.jsonc at startup, replacing "localhost" with Docker service names. +# +# The Next.js service overrides env vars directly via the `environment:` key +# so that POSTGRES_URL, CLOUD_AGENT_API_URL, etc. point to Docker service names. +# # Usage: # docker compose -f dev/docker-compose.dev.yml up # everything # docker compose -f dev/docker-compose.dev.yml up postgres nextjs # core only @@ -44,12 +57,21 @@ services: - "3000:3000" env_file: - ../.env + # Override localhost URLs so Next.js can reach other services on the + # Docker bridge network by service name. + environment: + - POSTGRES_URL=postgres://postgres:postgres@postgres:5432/postgres + - CLOUD_AGENT_API_URL=http://cloud-agent:8788 + - WEBHOOK_AGENT_URL=http://cloudflare-webhook-agent-ingest:8793 depends_on: postgres: condition: service_healthy command: sh -c "pnpm dev" # ── 2. cloud-agent (port 8788) ───────────────────────────────────── + # Uses docker-wrangler-entrypoint.sh to patch wrangler.jsonc localhost + # references (KILOCODE_BACKEND_BASE_URL, Hyperdrive, etc.) to Docker + # service names before starting wrangler dev. cloud-agent: <<: *worker-base profiles: ["agents", "all"] @@ -58,7 +80,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloud-agent && pnpm dev --port 8788 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloud-agent --env dev --port 8788 --ip 0.0.0.0 # ── 3. cloud-agent-next (port 8794) ──────────────────────────────── cloud-agent-next: @@ -69,7 +91,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloud-agent-next && pnpm dev --port 8794 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloud-agent-next --env dev --port 8794 --ip 0.0.0.0 # ── 4. cloudflare-ai-attribution (port 8787) ─────────────────────── cloudflare-ai-attribution: @@ -80,7 +102,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-ai-attribution && pnpm dev --port 8787 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-ai-attribution --env dev --port 8787 --ip 0.0.0.0 # ── 5. cloudflare-app-builder (port 8790) ────────────────────────── cloudflare-app-builder: @@ -91,7 +113,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-app-builder && pnpm dev --port 8790 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-app-builder --port 8790 --ip 0.0.0.0 # ── 6. cloudflare-auto-fix-infra (port 8796) ────────────────────── cloudflare-auto-fix-infra: @@ -104,7 +126,7 @@ services: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-auto-fix-infra && pnpm dev --port 8796 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-auto-fix-infra --port 8796 --ip 0.0.0.0 # ── 7. cloudflare-auto-triage-infra (port 8791) ─────────────────── cloudflare-auto-triage-infra: @@ -117,7 +139,7 @@ services: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-auto-triage-infra && pnpm dev --port 8791 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-auto-triage-infra --port 8791 --ip 0.0.0.0 # ── 8. cloudflare-code-review-infra (port 8789) ─────────────────── cloudflare-code-review-infra: @@ -130,7 +152,7 @@ services: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-code-review-infra && pnpm dev --port 8789 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-code-review-infra --port 8789 --ip 0.0.0.0 # ── 9. cloudflare-db-proxy (port 8797) ───────────────────────────── cloudflare-db-proxy: @@ -141,7 +163,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-db-proxy && pnpm dev --port 8797 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-db-proxy --port 8797 --ip 0.0.0.0 # ── 10. cloudflare-deploy-infra/builder (port 8798) ──────────────── cloudflare-deploy-builder: @@ -152,7 +174,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-deploy-infra/builder && pnpm dev --port 8798 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-deploy-infra/builder --port 8798 --ip 0.0.0.0 # ── 11. cloudflare-deploy-infra/dispatcher (port 8799) ───────────── cloudflare-deploy-dispatcher: @@ -163,7 +185,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-deploy-infra/dispatcher && pnpm dev --port 8799 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-deploy-infra/dispatcher --port 8799 --ip 0.0.0.0 # ── 12. cloudflare-session-ingest (port 8800) ───────────────────── cloudflare-session-ingest: @@ -174,7 +196,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-session-ingest && pnpm dev --port 8800 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-session-ingest --port 8800 --ip 0.0.0.0 # ── 13. cloudflare-o11y (port 8801) ──────────────────────────────── cloudflare-o11y: @@ -185,7 +207,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-o11y && pnpm dev --port 8801 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-o11y --port 8801 --ip 0.0.0.0 # ── 14. cloudflare-webhook-agent-ingest (port 8793) ──────────────── cloudflare-webhook-agent-ingest: @@ -198,7 +220,7 @@ services: condition: service_healthy cloud-agent: condition: service_started - command: sh -c "cd /app/cloudflare-webhook-agent-ingest && pnpm dev --port 8793 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-webhook-agent-ingest --env dev --port 8793 --ip 0.0.0.0 # ── 15. cloudflare-git-token-service (port 8802) ────────────────── cloudflare-git-token-service: @@ -209,7 +231,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/cloudflare-git-token-service && pnpm dev --port 8802 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh cloudflare-git-token-service --port 8802 --ip 0.0.0.0 # ── 16. kiloclaw (port 8795) ─────────────────────────────────────── kiloclaw: @@ -220,7 +242,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "cd /app/kiloclaw && pnpm dev --port 8795 --ip 0.0.0.0" + command: /app/dev/docker-wrangler-entrypoint.sh kiloclaw --port 8795 --ip 0.0.0.0 volumes: postgres_data: diff --git a/dev/docker-wrangler-entrypoint.sh b/dev/docker-wrangler-entrypoint.sh new file mode 100755 index 000000000..dcc3361c8 --- /dev/null +++ b/dev/docker-wrangler-entrypoint.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Entrypoint for wrangler-based services in Docker Compose. +# +# Problem: wrangler.jsonc files hardcode "localhost" for inter-service URLs +# (e.g., Hyperdrive localConnectionString, KILOCODE_BACKEND_BASE_URL). When +# running in Docker Compose with bridge networking, "localhost" inside a +# container refers to that container itself, not other services. +# +# Solution: This script creates a temporary copy of the worker's wrangler.jsonc, +# replaces "localhost" references with Docker Compose service names (which +# resolve via Docker's built-in DNS on the shared bridge network), then runs +# wrangler dev using the patched config. +# +# Usage (in docker-compose.dev.yml): +# command: /app/dev/docker-wrangler-entrypoint.sh [wrangler-dev-args...] +# +# Example: +# command: /app/dev/docker-wrangler-entrypoint.sh cloud-agent --env dev --port 8788 --ip 0.0.0.0 + +set -euo pipefail + +WORKER_DIR="$1" +shift # remaining args are passed to wrangler dev + +WORKER_PATH="/app/${WORKER_DIR}" +ORIGINAL_CONFIG="${WORKER_PATH}/wrangler.jsonc" +PATCHED_CONFIG="/tmp/wrangler-docker.jsonc" + +if [ ! -f "$ORIGINAL_CONFIG" ]; then + echo "❌ wrangler.jsonc not found at ${ORIGINAL_CONFIG}" + exit 1 +fi + +# Create a patched copy with Docker service names instead of localhost. +# These replacements map localhost ports to the corresponding Docker Compose +# service names (which resolve via DNS on the shared bridge network): +# +# localhost:3000 → nextjs:3000 (Next.js backend) +# localhost:5432 → postgres:5432 (PostgreSQL via Hyperdrive) +# localhost:8787 → cloudflare-ai-attribution:8787 +# localhost:8788 → cloud-agent:8788 +# localhost:8793 → cloudflare-webhook-agent-ingest:8793 +# localhost:8794 → cloud-agent-next:8794 +# localhost:8800 → cloudflare-session-ingest:8800 +sed \ + -e 's|localhost:3000|nextjs:3000|g' \ + -e 's|localhost:5432|postgres:5432|g' \ + -e 's|localhost:8787|cloudflare-ai-attribution:8787|g' \ + -e 's|localhost:8788|cloud-agent:8788|g' \ + -e 's|localhost:8793|cloudflare-webhook-agent-ingest:8793|g' \ + -e 's|localhost:8794|cloud-agent-next:8794|g' \ + -e 's|localhost:8800|cloudflare-session-ingest:8800|g' \ + "$ORIGINAL_CONFIG" > "$PATCHED_CONFIG" + +cd "$WORKER_PATH" + +# Run predev script if it exists (e.g., cloud-agent builds its wrapper via bun) +if node -e "const p=require('./package.json'); process.exit(p.scripts?.predev ? 0 : 1)" 2>/dev/null; then + echo "▶ Running predev script for ${WORKER_DIR}..." + pnpm run predev +fi + +exec wrangler dev --config "$PATCHED_CONFIG" "$@" From 171f20a8e4d9d68ba68614a3a1ea22ca5882c04b Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:34:12 +0000 Subject: [PATCH 05/12] =?UTF-8?q?fix:=20address=20PR=20review=20feedback?= =?UTF-8?q?=20=E2=80=94=20pin=20images,=20add=20validations,=20fix=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pin node base image to 22.14.0-slim for reproducibility - Pin pgvector/pgvector to 0.8.0-pg18 for consistent local-dev behavior - Add argument validation in docker-wrangler-entrypoint.sh - Fix repo name in Makefile header (kilocode-backend → Kilo-Org/cloud) - Update Docker prerequisite wording to cover Linux (Docker Engine) - Add pnpm preflight check in dev.sh, drop --frozen-lockfile for local dev - Use exec-form command for nextjs service (proper PID1 signal handling) --- DOCKER_DEV.md | 2 +- Makefile | 2 +- dev/Dockerfile.dev | 2 +- dev/dev.sh | 8 +++++++- dev/docker-compose.dev.yml | 4 ++-- dev/docker-wrangler-entrypoint.sh | 6 ++++++ 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 91ba4a232..33c094a02 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -6,7 +6,7 @@ Run the entire monorepo (Next.js backend + all Cloudflare Workers + PostgreSQL) | Tool | Version | Check | |------|---------|-------| -| Docker Desktop | 4.x+ | `docker --version` | +| Docker Desktop (macOS/Windows) / Docker Engine (Linux) | 4.x+ | `docker --version` | | Docker Compose v2 | 2.20+ | `docker compose version` | | pnpm | 10.27.0 | `pnpm --version` | | Node.js | ^22 | `node --version` | diff --git a/Makefile b/Makefile index 0815642e7..3e30d73ec 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Makefile — convenience targets for kilocode-backend +# Makefile — convenience targets for Kilo-Org/cloud COMPOSE_DEV := docker compose -f dev/docker-compose.dev.yml diff --git a/dev/Dockerfile.dev b/dev/Dockerfile.dev index a1d52ef5e..fb094c89e 100644 --- a/dev/Dockerfile.dev +++ b/dev/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM node:22-slim +FROM node:22.14.0-slim # Install pnpm RUN corepack enable && corepack prepare pnpm@10.27.0 --activate diff --git a/dev/dev.sh b/dev/dev.sh index 23f8eb187..d93b06529 100755 --- a/dev/dev.sh +++ b/dev/dev.sh @@ -28,8 +28,14 @@ if ! docker compose version &>/dev/null; then fi if [ ! -d "$REPO_ROOT/node_modules" ]; then + if ! command -v pnpm &>/dev/null; then + echo "❌ pnpm is not installed and node_modules is missing." + echo " Install pnpm first: corepack enable && corepack prepare pnpm@10.27.0 --activate" + echo " Then run: pnpm install" + exit 1 + fi echo "⚠️ node_modules not found. Running pnpm install first..." - (cd "$REPO_ROOT" && pnpm install --frozen-lockfile) + (cd "$REPO_ROOT" && pnpm install) fi if [ ! -f "$REPO_ROOT/.env" ]; then diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index f5248f43a..d3b0d849f 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -33,7 +33,7 @@ x-worker-base: &worker-base services: # ── PostgreSQL (always starts — no profile) ───────────────────────── postgres: - image: pgvector/pgvector:pg18 + image: pgvector/pgvector:0.8.0-pg18 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -66,7 +66,7 @@ services: depends_on: postgres: condition: service_healthy - command: sh -c "pnpm dev" + command: ["pnpm", "dev"] # ── 2. cloud-agent (port 8788) ───────────────────────────────────── # Uses docker-wrangler-entrypoint.sh to patch wrangler.jsonc localhost diff --git a/dev/docker-wrangler-entrypoint.sh b/dev/docker-wrangler-entrypoint.sh index dcc3361c8..395581be7 100755 --- a/dev/docker-wrangler-entrypoint.sh +++ b/dev/docker-wrangler-entrypoint.sh @@ -19,6 +19,12 @@ set -euo pipefail +if [ $# -lt 1 ]; then + echo "Usage: docker-wrangler-entrypoint.sh [wrangler-dev-args...]" + echo " e.g. docker-wrangler-entrypoint.sh cloud-agent --env dev --port 8788 --ip 0.0.0.0" + exit 1 +fi + WORKER_DIR="$1" shift # remaining args are passed to wrangler dev From ca6ef6278c23fc95206a62a00d276d1c6f34395c Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:35:40 +0000 Subject: [PATCH 06/12] docs: update DOCKER_DEV.md to reflect pinned node image version --- DOCKER_DEV.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 33c094a02..d7cda3977 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -128,7 +128,7 @@ cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars ## Architecture -- **Shared Docker image** (`dev/Dockerfile.dev`): `node:22-slim` with pnpm, wrangler, and bun pre-installed. +- **Shared Docker image** (`dev/Dockerfile.dev`): `node:22.14.0-slim` with pnpm, wrangler, and bun pre-installed. - **Volume mount**: The entire repo is mounted at `/app` — file changes are reflected immediately (hot reload works). - **Port mappings**: Each service exposes its port via explicit `ports:` mappings, which works on both macOS (Docker Desktop) and Linux. - **Inter-service networking**: Docker Compose bridge network with DNS-based service discovery. See [Networking](#networking) above. From be877c7b5e0b05ad52632bc7e28763ce67ee8a32 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:47:09 +0000 Subject: [PATCH 07/12] fix: address review feedback - align env_file docs and bind postgres to loopback - DOCKER_DEV.md: fix env_file reference to match actual ../.env path in compose - docker-compose.dev.yml: bind postgres port to 127.0.0.1 to prevent network exposure on Linux --- DOCKER_DEV.md | 2 +- dev/docker-compose.dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index d7cda3977..076cb2e44 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -97,7 +97,7 @@ From your host machine, services are still accessible at `localhost:` via ### Next.js backend -The Next.js service loads `env_file: .env` from the repo root. Make sure this file exists: +The Next.js service loads `env_file: ../.env` (repo root `.env`). Make sure this file exists: ```bash # If you have an .env.example, copy it diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index d3b0d849f..62ebd2d69 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -39,7 +39,7 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres ports: - - "5432:5432" + - "127.0.0.1:5432:5432" restart: unless-stopped volumes: - postgres_data:/var/lib/postgresql/data From 164dcc53cd2ebc6ba50396497741fdf0f2ad17fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Tue, 17 Feb 2026 16:50:57 +0100 Subject: [PATCH 08/12] Update dev/docker-compose.dev.yml Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- dev/docker-compose.dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index 62ebd2d69..c5c647fd9 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -54,7 +54,7 @@ services: <<: *worker-base profiles: ["core", "all"] ports: - - "3000:3000" + - "127.0.0.1:3000:3000" env_file: - ../.env # Override localhost URLs so Next.js can reach other services on the From 462bf6ee72f4fed050ce1de09d532963c7c86c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Tue, 17 Feb 2026 17:12:59 +0100 Subject: [PATCH 09/12] Update dev/docker-compose.dev.yml Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> --- dev/docker-compose.dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index c5c647fd9..f3b45ed02 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -76,7 +76,7 @@ services: <<: *worker-base profiles: ["agents", "all"] ports: - - "8788:8788" + - "127.0.0.1:8788:8788" depends_on: postgres: condition: service_healthy From 5ad98f40370265bb48e5acf3cafff3ba42be998e Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:20:11 +0000 Subject: [PATCH 10/12] fix: add cloud-agent to workers profile to fix cross-profile dependency --- DOCKER_DEV.md | 2 +- dev/docker-compose.dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 076cb2e44..206a2095b 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -34,7 +34,7 @@ Services are grouped into profiles for selective startup. PostgreSQL always star |---------|----------| | `core` | PostgreSQL + Next.js backend | | `agents` | PostgreSQL + cloud-agent + cloud-agent-next | -| `workers` | PostgreSQL + all Cloudflare Workers | +| `workers` | PostgreSQL + cloud-agent + all Cloudflare Workers | | `all` | Everything | ```bash diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index f3b45ed02..4ad27eedb 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -74,7 +74,7 @@ services: # service names before starting wrangler dev. cloud-agent: <<: *worker-base - profiles: ["agents", "all"] + profiles: ["agents", "workers", "all"] ports: - "127.0.0.1:8788:8788" depends_on: From 9173e7ba5cfed62284038facf2e6fd583c13fccd Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:25:57 +0000 Subject: [PATCH 11/12] fix: bind all worker ports to 127.0.0.1 for consistent localhost-only exposure All 14 remaining worker services now use 127.0.0.1:PORT:PORT port mappings, matching postgres, nextjs, and cloud-agent. This prevents accidental exposure on all network interfaces on Linux Docker Engine. --- dev/docker-compose.dev.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index 4ad27eedb..795dc593c 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -87,7 +87,7 @@ services: <<: *worker-base profiles: ["agents", "all"] ports: - - "8794:8794" + - "127.0.0.1:8794:8794" depends_on: postgres: condition: service_healthy @@ -98,7 +98,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8787:8787" + - "127.0.0.1:8787:8787" depends_on: postgres: condition: service_healthy @@ -109,7 +109,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8790:8790" + - "127.0.0.1:8790:8790" depends_on: postgres: condition: service_healthy @@ -120,7 +120,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8796:8796" + - "127.0.0.1:8796:8796" depends_on: postgres: condition: service_healthy @@ -133,7 +133,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8791:8791" + - "127.0.0.1:8791:8791" depends_on: postgres: condition: service_healthy @@ -146,7 +146,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8789:8789" + - "127.0.0.1:8789:8789" depends_on: postgres: condition: service_healthy @@ -159,7 +159,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8797:8797" + - "127.0.0.1:8797:8797" depends_on: postgres: condition: service_healthy @@ -170,7 +170,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8798:8798" + - "127.0.0.1:8798:8798" depends_on: postgres: condition: service_healthy @@ -181,7 +181,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8799:8799" + - "127.0.0.1:8799:8799" depends_on: postgres: condition: service_healthy @@ -192,7 +192,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8800:8800" + - "127.0.0.1:8800:8800" depends_on: postgres: condition: service_healthy @@ -203,7 +203,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8801:8801" + - "127.0.0.1:8801:8801" depends_on: postgres: condition: service_healthy @@ -214,7 +214,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8793:8793" + - "127.0.0.1:8793:8793" depends_on: postgres: condition: service_healthy @@ -227,7 +227,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8802:8802" + - "127.0.0.1:8802:8802" depends_on: postgres: condition: service_healthy @@ -238,7 +238,7 @@ services: <<: *worker-base profiles: ["workers", "all"] ports: - - "8795:8795" + - "127.0.0.1:8795:8795" depends_on: postgres: condition: service_healthy From a1f25a7edfd65eac9a6f8e9ac4dd7c22c70f2f96 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 09:35:23 +0000 Subject: [PATCH 12/12] Mount host Docker socket for workers with container-backed DOs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workers that use container-backed Durable Objects (cloud-agent, cloud-agent-next, cloudflare-app-builder, cloudflare-deploy-builder) need Docker to spawn sandbox containers via Wrangler. Since these workers already run inside Docker, they can't use Docker natively. Mount /var/run/docker.sock into these 4 services so Wrangler can talk to the host Docker daemon to create sibling containers. ⚠️ This grants the container full control over the host's Docker daemon — acceptable for local dev, never for production. Also documents the requirement and security trade-off in DOCKER_DEV.md. --- DOCKER_DEV.md | 22 ++++++++++++++++++++++ dev/docker-compose.dev.yml | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/DOCKER_DEV.md b/DOCKER_DEV.md index 206a2095b..175ddbf57 100644 --- a/DOCKER_DEV.md +++ b/DOCKER_DEV.md @@ -134,6 +134,28 @@ cp cloud-agent/.dev.vars.example cloud-agent/.dev.vars - **Inter-service networking**: Docker Compose bridge network with DNS-based service discovery. See [Networking](#networking) above. - **Existing `dev/docker-compose.yml`** is untouched — it continues to work standalone for PostgreSQL-only usage. +## Docker Socket Mount (Container-backed Durable Objects) + +Four workers use Cloudflare's [container-backed Durable Objects](https://developers.cloudflare.com/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#container-durable-objects), which require Docker to spawn sandbox containers at runtime via Wrangler: + +- `cloud-agent` +- `cloud-agent-next` +- `cloudflare-app-builder` +- `cloudflare-deploy-builder` + +Because these workers already run inside Docker containers, they can't use Docker natively (Docker-in-Docker). Instead, the host's Docker socket is mounted into these containers: + +```yaml +volumes: + - /var/run/docker.sock:/var/run/docker.sock +``` + +This lets Wrangler inside the container talk to the host's Docker daemon to create sibling containers. + +### Security implications + +Mounting the Docker socket gives the container **full, unrestricted access to the host's Docker daemon** — equivalent to root access on the host. This is acceptable for local development but must **never** be used in production or CI environments with untrusted code. The mount is only applied to the four workers listed above; all other services use the default volume configuration from the shared base. + ## Troubleshooting ### Port already in use diff --git a/dev/docker-compose.dev.yml b/dev/docker-compose.dev.yml index 795dc593c..2deb84604 100644 --- a/dev/docker-compose.dev.yml +++ b/dev/docker-compose.dev.yml @@ -77,6 +77,13 @@ services: profiles: ["agents", "workers", "all"] ports: - "127.0.0.1:8788:8788" + # Container-backed Durable Objects: Wrangler needs access to the host + # Docker daemon to spawn sandbox containers. This grants the container + # full control over the host's Docker — acceptable for local dev, but + # must never be used in production. + volumes: + - ..:/app + - /var/run/docker.sock:/var/run/docker.sock depends_on: postgres: condition: service_healthy @@ -88,6 +95,10 @@ services: profiles: ["agents", "all"] ports: - "127.0.0.1:8794:8794" + # Container-backed DOs — needs host Docker daemon (see cloud-agent comment). + volumes: + - ..:/app + - /var/run/docker.sock:/var/run/docker.sock depends_on: postgres: condition: service_healthy @@ -110,6 +121,10 @@ services: profiles: ["workers", "all"] ports: - "127.0.0.1:8790:8790" + # Container-backed DOs — needs host Docker daemon (see cloud-agent comment). + volumes: + - ..:/app + - /var/run/docker.sock:/var/run/docker.sock depends_on: postgres: condition: service_healthy @@ -171,6 +186,10 @@ services: profiles: ["workers", "all"] ports: - "127.0.0.1:8798:8798" + # Container-backed DOs — needs host Docker daemon (see cloud-agent comment). + volumes: + - ..:/app + - /var/run/docker.sock:/var/run/docker.sock depends_on: postgres: condition: service_healthy