mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-14 03:15:58 +00:00
474c89bac2
* fix(security): do not bind-mount host CLI auth dirs by default The Compose stack bind-mounted the entire ~/.claude and ~/.codex dirs (read-only) into the root gateway container in every configuration -- exposing not just credentials but full conversation history, per-project session data, and global CLI config. The default OpenAI-compatible model providers and the local sandbox never use them. Move the mounts to an opt-in docker/docker-compose.cli-auth.yaml overlay. Document env-token paths (CLAUDE_CODE_OAUTH_TOKEN, CODEX_AUTH_PATH) in .env.example -- the Gateway credential loader reads env first, so most setups need no mount at all. Document the exposure and per-mode options in SECURITY.md. Reported by @greatmengqi. * docs: clarify ACP adapter auth and add Claude single-file credential option - ACP adapters authenticate independently (many take an env API key like ANTHROPIC_API_KEY and need no mount); the cli-auth overlay is only for adapters that read the full CLI config dir. Avoids steering users toward mounting the whole dir for ACP when env auth usually suffices. - Add CLAUDE_CODE_CREDENTIALS_PATH (single .credentials.json) as a Claude one-file option, matching codex CODEX_AUTH_PATH and the README. * docs: cite claude-code-acp env auth and CLAUDE_CONFIG_DIR in ACP guidance Replace the generic 'some adapters' wording with the verified behavior of the common claude-code-acp adapter (env ANTHROPIC_API_KEY startup + CLAUDE_CONFIG_DIR), so the 'no ~/.claude mount needed for ACP' guidance is backed by a concrete adapter.
193 lines
8.0 KiB
YAML
193 lines
8.0 KiB
YAML
# DeerFlow Development Environment
|
|
# Usage: docker-compose -f docker-compose-dev.yaml up --build
|
|
#
|
|
# Services:
|
|
# - nginx: Reverse proxy (port 2026)
|
|
# - frontend: Frontend Next.js dev server (port 3000)
|
|
# - gateway: Backend Gateway API + agent runtime (port 8001)
|
|
# - provisioner (optional): Sandbox provisioner (creates Pods in host Kubernetes)
|
|
#
|
|
# Prerequisites:
|
|
# - Kubernetes cluster + kubeconfig are only required when using provisioner mode.
|
|
#
|
|
# Access: http://localhost:2026
|
|
|
|
services:
|
|
# ── Sandbox Provisioner ────────────────────────────────────────────────
|
|
# Manages per-sandbox Pod + Service lifecycle in the host Kubernetes
|
|
# cluster via the K8s API.
|
|
# Backend accesses sandboxes directly via host.docker.internal:{NodePort}.
|
|
provisioner:
|
|
build:
|
|
context: ./provisioner
|
|
dockerfile: Dockerfile
|
|
args:
|
|
APT_MIRROR: ${APT_MIRROR:-}
|
|
container_name: deer-flow-provisioner
|
|
volumes:
|
|
- ~/.kube/config:/root/.kube/config:ro
|
|
environment:
|
|
- K8S_NAMESPACE=deer-flow
|
|
- SANDBOX_IMAGE=enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
|
|
# Host paths for K8s HostPath volumes (must be absolute paths accessible by K8s node)
|
|
# On Docker Desktop/OrbStack, use your actual host paths like /Users/username/...
|
|
# Set these in your shell before running docker-compose:
|
|
# export DEER_FLOW_ROOT=/absolute/path/to/deer-flow
|
|
- SKILLS_HOST_PATH=${DEER_FLOW_ROOT}/skills
|
|
- THREADS_HOST_PATH=${DEER_FLOW_ROOT}/backend/.deer-flow/threads
|
|
# Production: use PVC instead of hostPath to avoid data loss on node failure.
|
|
# When set, hostPath vars above are ignored for the corresponding volume.
|
|
# USERDATA_PVC_NAME uses subPath (deer-flow/users/{user_id}/threads/{thread_id}/user-data) automatically.
|
|
# - SKILLS_PVC_NAME=deer-flow-skills-pvc
|
|
# - USERDATA_PVC_NAME=deer-flow-userdata-pvc
|
|
- KUBECONFIG_PATH=/root/.kube/config
|
|
- NODE_HOST=host.docker.internal
|
|
# Override K8S API server URL since kubeconfig uses 127.0.0.1
|
|
# which is unreachable from inside the container
|
|
- K8S_API_SERVER=https://host.docker.internal:26443
|
|
env_file:
|
|
- ../.env
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
networks:
|
|
- deer-flow-dev
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:8002/health"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 6
|
|
start_period: 15s
|
|
|
|
# ── Reverse Proxy ──────────────────────────────────────────────────────
|
|
# Routes API traffic to gateway and (optionally) provisioner.
|
|
nginx:
|
|
image: nginx:alpine
|
|
container_name: deer-flow-nginx
|
|
ports:
|
|
- "2026:2026"
|
|
volumes:
|
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf.template:ro
|
|
command:
|
|
- sh
|
|
- -c
|
|
- |
|
|
set -e
|
|
cp /etc/nginx/nginx.conf.template /etc/nginx/nginx.conf
|
|
test -e /proc/net/if_inet6 || sed -i '/^[[:space:]]*listen[[:space:]]\+\[::\]:2026;/d' /etc/nginx/nginx.conf
|
|
exec nginx -g 'daemon off;'
|
|
depends_on:
|
|
- frontend
|
|
- gateway
|
|
networks:
|
|
- deer-flow-dev
|
|
restart: unless-stopped
|
|
|
|
# Frontend - Next.js Development Server
|
|
frontend:
|
|
build:
|
|
context: ../
|
|
dockerfile: frontend/Dockerfile
|
|
target: dev
|
|
args:
|
|
PNPM_STORE_PATH: ${PNPM_STORE_PATH:-/root/.local/share/pnpm/store}
|
|
NPM_REGISTRY: ${NPM_REGISTRY:-}
|
|
container_name: deer-flow-frontend
|
|
command: sh -c "cd frontend && pnpm run dev > /app/logs/frontend.log 2>&1"
|
|
volumes:
|
|
- ../frontend/src:/app/frontend/src
|
|
- ../frontend/public:/app/frontend/public
|
|
- ../frontend/next.config.js:/app/frontend/next.config.js:ro
|
|
- ../logs:/app/logs
|
|
# Mount pnpm store for caching
|
|
- ${PNPM_STORE_PATH:-~/.local/share/pnpm/store}:/root/.local/share/pnpm/store
|
|
working_dir: /app
|
|
environment:
|
|
- NODE_ENV=development
|
|
- WATCHPACK_POLLING=true
|
|
- CI=true
|
|
- DEER_FLOW_INTERNAL_GATEWAY_BASE_URL=http://gateway:8001
|
|
env_file:
|
|
- ../frontend/.env
|
|
networks:
|
|
- deer-flow-dev
|
|
restart: unless-stopped
|
|
|
|
# Backend - Gateway API
|
|
gateway:
|
|
build:
|
|
context: ../
|
|
dockerfile: backend/Dockerfile
|
|
target: dev
|
|
# cache_from disabled - requires manual setup: mkdir -p /tmp/docker-cache-gateway
|
|
args:
|
|
APT_MIRROR: ${APT_MIRROR:-}
|
|
UV_IMAGE: ${UV_IMAGE:-ghcr.io/astral-sh/uv:0.7.20}
|
|
UV_INDEX_URL: ${UV_INDEX_URL:-https://pypi.org/simple}
|
|
container_name: deer-flow-gateway
|
|
# Startup logic lives in docker/dev-entrypoint.sh — UV_EXTRAS validation,
|
|
# `uv sync --all-packages`, .venv self-heal, and uvicorn handoff. Keeps
|
|
# this file readable and lets the script be linted (shellcheck-clean).
|
|
# See PR #2767 / Issue #2754.
|
|
command: ["sh", "/usr/local/bin/dev-entrypoint.sh"]
|
|
volumes:
|
|
# Mount the dev entrypoint as a read-only file so edits to the script
|
|
# take effect on `make docker-restart` without requiring an image rebuild.
|
|
- ./dev-entrypoint.sh:/usr/local/bin/dev-entrypoint.sh:ro
|
|
- ../backend/:/app/backend/
|
|
# Preserve the .venv built during Docker image build — mounting the full backend/
|
|
# directory above would otherwise shadow it with the (empty) host directory.
|
|
- gateway-venv:/app/backend/.venv
|
|
- ../config.yaml:/app/config.yaml
|
|
- ../extensions_config.json:/app/extensions_config.json
|
|
- ../skills:/app/skills
|
|
- ../logs:/app/logs
|
|
# Use a Docker-managed uv cache volume instead of a host bind mount.
|
|
# On macOS/Docker Desktop, uv may fail to create symlinks inside shared
|
|
# host directories, which causes startup-time `uv sync` to crash.
|
|
- gateway-uv-cache:/root/.cache/uv
|
|
# DooD: AioSandboxProvider runs inside the Gateway process.
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
# CLI auth dirs (Claude Code / Codex) are NOT mounted by default: they
|
|
# expose the entire ~/.claude and ~/.codex (history, projects, global
|
|
# config, credentials) into the container. Mount them only when you use
|
|
# the Claude/Codex CLI login as a model provider or ACP agent, via the
|
|
# opt-in docker-compose.cli-auth.yaml overlay. Prefer an env token
|
|
# (CLAUDE_CODE_OAUTH_TOKEN, see .env.example / SECURITY.md).
|
|
working_dir: /app
|
|
environment:
|
|
- CI=true
|
|
- DEER_FLOW_PROJECT_ROOT=/app
|
|
- DEER_FLOW_HOME=/app/backend/.deer-flow
|
|
- DEER_FLOW_CHANNELS_LANGGRAPH_URL=${DEER_FLOW_CHANNELS_LANGGRAPH_URL:-http://gateway:8001/api}
|
|
- DEER_FLOW_CHANNELS_GATEWAY_URL=${DEER_FLOW_CHANNELS_GATEWAY_URL:-http://gateway:8001}
|
|
- DEER_FLOW_INTERNAL_AUTH_TOKEN=${DEER_FLOW_INTERNAL_AUTH_TOKEN:-}
|
|
- DEER_FLOW_HOST_BASE_DIR=${DEER_FLOW_ROOT}/backend/.deer-flow
|
|
- DEER_FLOW_HOST_SKILLS_PATH=${DEER_FLOW_ROOT}/skills
|
|
- DEER_FLOW_SANDBOX_HOST=host.docker.internal
|
|
# Proxy values (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) are inherited from ../.env via env_file.
|
|
# Only NO_PROXY is declared here so internal service hostnames are always exempt from the proxy.
|
|
- NO_PROXY=${NO_PROXY:-}${NO_PROXY:+,}localhost,127.0.0.1,::1,gateway,frontend,nginx,provisioner,host.docker.internal
|
|
- no_proxy=${no_proxy:-}${no_proxy:+,}localhost,127.0.0.1,::1,gateway,frontend,nginx,provisioner,host.docker.internal
|
|
env_file:
|
|
- ../.env
|
|
extra_hosts:
|
|
# For Linux: map host.docker.internal to host gateway
|
|
- "host.docker.internal:host-gateway"
|
|
networks:
|
|
- deer-flow-dev
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
# Persist .venv across container restarts so dependencies installed during
|
|
# image build are not shadowed by the host backend/ directory mount.
|
|
gateway-venv:
|
|
gateway-uv-cache:
|
|
|
|
networks:
|
|
deer-flow-dev:
|
|
driver: bridge
|
|
ipam:
|
|
config:
|
|
- subnet: 192.168.200.0/24
|