# DeerFlow Administrator & Developer Guide

## 1. Architecture Overview

DeerFlow is a full-stack AI agent platform built on LangGraph, FastAPI, and Next.js. It implements a **lead agent** orchestration pattern where a central agent delegates to sub-agents, tools, and skills to complete complex multi-step tasks. The platform supports pluggable LLM providers, sandboxed code execution, persistent memory, MCP server integration, a skills framework, and multi-channel IM integrations.

### 1.1 High-Level Architecture

```
┌──────────────────────────────────────────────────────────────────┐
│                         nginx (:2026)                            │
│   Reverse proxy — routes /api/* to gateway, LangGraph to :2024   │
├─────────────┬──────────────────┬─────────────────────────────────┤
│  Frontend   │  Gateway (:8001) │  LangGraph Server (:2024)       │
│  Next.js    │  FastAPI custom   │  LangGraph runtime (lead_agent) │
│  :3000      │  endpoints        │  checkpointer + store           │
├─────────────┴──────────────────┴─────────────────────────────────┤
│                  deerflow-harness (Python package)                │
│  Config │ Models │ Sandbox │ Skills │ Memory │ MCP │ Guardrails   │
├──────────────────────────────────────────────────────────────────┤
│              External Services & Infrastructure                   │
│  LLM APIs │ K8s Provisioner │ IM Channels │ Tracing (LangSmith)  │
└──────────────────────────────────────────────────────────────────┘
```

### 1.2 Component Inventory

| Component | Technology | Port | Purpose |
|-----------|-----------|------|---------|
| **Frontend** | Next.js 16, React 19, pnpm 10 | 3000 | Web UI with chat, artifacts, settings |
| **Gateway** | FastAPI (uvicorn) | 8001 | Custom API: models, MCP, memory, skills, uploads, agents, channels, runs |
| **LangGraph Server** | `langgraph dev` | 2024 | Agent graph execution, checkpointing, state persistence |
| **nginx** | nginx:alpine | 2026 | Reverse proxy, CORS, unified entry point |
| **Provisioner** | FastAPI | 8002 | Kubernetes sandbox Pod lifecycle (optional) |

### 1.3 Agent Architecture

```
lead_agent (LangGraph StateGraph)
├── Tool nodes (web_search, web_fetch, file ops, bash, image_search)
├── Sub-agents
│   ├── general-purpose — multi-step research and analysis
│   └── bash — shell command execution
├── Skills (15+ built-in, user-extensible)
├── MCP tool nodes (stdio/sse/streamable_http servers)
├── ACP agents (Claude Code, Codex CLI via subprocess)
└── Middleware
    ├── TitleMiddleware — auto-generate conversation titles
    ├── SummarizationMiddleware — compress long context
    └── MemoryMiddleware — persistent fact extraction
```

The lead agent is registered in `backend/langgraph.json` as `deerflow.agents:make_lead_agent`. All custom agents route through `make_lead_agent` with an `agent_name` injected into `configurable`, which loads a per-agent `SOUL.md` and `config.yaml` from the `agents/<name>/` directory.

---

## 2. System Requirements

### 2.1 Backend Requirements

| Requirement | Version |
|-------------|---------|
| Python | >= 3.12 |
| Package manager | uv (recommended) |
| OS | Linux, macOS, Windows (WSL recommended for Docker) |
| Node.js | >= 18 (for frontend) |
| pnpm | 10.26.2 |

### 2.2 Python Dependencies (Core)

From `pyproject.toml` (`deerflow-harness`):

| Package | Version Constraint | Purpose |
|---------|-------------------|---------|
| `langgraph` | >= 1.0.6, < 1.0.10 | Agent graph runtime |
| `langgraph-api` | >= 0.7.0, < 0.8.0 | LangGraph Platform compatibility |
| `langgraph-runtime-inmem` | >= 0.22.1 | In-memory runtime |
| `langchain` | >= 1.2.3 | LLM framework |
| `langchain-openai` | >= 1.1.7 | OpenAI provider |
| `langchain-anthropic` | >= 1.3.4 | Anthropic provider |
| `langchain-deepseek` | >= 1.0.1 | DeepSeek provider |
| `langchain-google-genai` | >= 4.2.1 | Google Gemini provider |
| `pydantic` | >= 2.12.5 | Config models |
| `kubernetes` | >= 30.0.0 | K8s provisioner client |
| `agent-client-protocol` | >= 0.4.0 | ACP agent integration |
| `agent-sandbox` | >= 0.0.19 | Sandbox abstraction |
| `langchain-mcp-adapters` | >= 0.1.0 | MCP server integration |
| `langgraph-checkpoint-sqlite` | >= 3.0.3 | SQLite checkpointer |

**Optional extras:**

| Extra | Package | Purpose |
|-------|---------|---------|
| `ollama` | `langchain-ollama >= 0.3.0` | Ollama native API support |
| `pymupdf` | `pymupdf4llm >= 0.0.17` | Better PDF-to-Markdown conversion |

### 2.3 Frontend Dependencies (Key)

| Package | Version | Purpose |
|---------|---------|---------|
| `next` | 16 | React framework with App Router |
| `react` | 19 | UI library |
| `@langchain/langgraph-sdk` | latest | LangGraph client SDK |
| `@xyflow/react` | latest | Flow diagram rendering |
| `better-auth` | latest | Authentication |
| `nextra` | latest | Documentation site |
| `streamdown` | latest | Streaming markdown renderer |
| `codemirror` | latest | Code editor |

---

## 3. Configuration Reference

DeerFlow uses two configuration files plus environment variables:

| File | Format | Purpose |
|------|--------|---------|
| `config.yaml` | YAML | Primary configuration: models, tools, sandbox, memory, channels |
| `extensions_config.json` | JSON | MCP server definitions, skill state overrides |
| `.env` | dotenv | API keys and secrets |

> [!IMPORTANT]
> Copy `config.example.yaml` to `config.yaml` before first run. The setup wizard (`make setup` or `python scripts/setup_wizard.py`) generates a minimal working config interactively.

### 3.1 Configuration Resolution

1. `DEER_FLOW_CONFIG_PATH` environment variable (highest priority)
2. `config.yaml` in the current working directory
3. `config.yaml` at the repository root

For extensions: `DEER_FLOW_EXTENSIONS_CONFIG_PATH` or `extensions_config.json` in the same search order.

### 3.2 Config Version

```yaml
config_version: 7   # Schema version — bump triggers make config-upgrade
```

Run `make config-upgrade` after upgrading DeerFlow to merge new fields into your local config.

### 3.3 Models Configuration

Each model entry defines an LLM provider:

```yaml
models:
  - name: gpt-4o                          # Internal identifier
    display_name: GPT-4o                   # Shown in UI
    use: langchain_openai:ChatOpenAI       # Python class path
    model: gpt-4o                          # Provider model name
    api_key: $OPENAI_API_KEY               # $VAR resolves from env
    base_url: https://api.openai.com/v1    # Optional override
    request_timeout: 600.0
    max_retries: 2
    max_tokens: 4096
    temperature: 0.7
    supports_vision: true
    supports_thinking: true
    supports_reasoning_effort: true
    when_thinking_enabled:                 # Applied when thinking toggle is on
      extra_body:
        thinking:
          type: enabled
    when_thinking_disabled:
      extra_body:
        thinking:
          type: disabled
```

**Supported providers and their `use` class paths:**

| Provider | `use` Path | Notes |
|----------|-----------|-------|
| OpenAI | `langchain_openai:ChatOpenAI` | Standard; also works for OpenRouter |
| Anthropic | `langchain_anthropic:ChatAnthropic` | Requires `max_tokens` |
| DeepSeek | `deerflow.models.patched_deepseek:PatchedChatDeepSeek` | Patched for thinking |
| Google Gemini (native) | `langchain_google_genai:ChatGoogleGenerativeAI` | Uses `gemini_api_key` |
| Gemini (OpenAI-compat) | `deerflow.models.patched_openai:PatchedChatOpenAI` | Preserves thought_signature |
| Ollama (native) | `langchain_ollama:ChatOllama` | Install extra: `uv pip install 'deerflow-harness[ollama]'` |
| vLLM | `deerflow.models.vllm_provider:VllmChatModel` | Self-hosted; Qwen-style reasoning toggle |
| MiniMax | `deerflow.models.patched_minimax:PatchedChatMiniMax` | Temperature must be in (0.0, 1.0] |
| Claude Code OAuth | `deerflow.models.claude_provider:ClaudeChatModel` | Uses local `~/.claude` credentials |
| Codex CLI | `deerflow.models.openai_codex_provider:CodexChatModel` | Uses `~/.codex/auth.json` |

> [!WARNING]
> For Ollama models, use `langchain_ollama:ChatOllama` (native API), not the OpenAI-compatible endpoint. The `/v1/chat/completions` endpoint drops or flattens reasoning content.

### 3.4 Tools Configuration

Tools are organized into groups for access control:

```yaml
tool_groups:
  - name: web
  - name: file:read
  - name: file:write
  - name: bash

tools:
  - name: web_search
    group: web
    use: deerflow.community.ddg_search.tools:web_search_tool  # No API key needed
    max_results: 5

  - name: web_fetch
    group: web
    use: deerflow.community.jina_ai.tools:web_fetch_tool
    timeout: 10

  - name: bash
    group: bash
    use: deerflow.sandbox.tools:bash_tool
```

**Search providers:** DuckDuckGo (free, default), Tavily, InfoQuest, Exa, Firecrawl.

**Web fetch providers:** Jina AI (free, default), Exa, InfoQuest, Firecrawl.

**File operation tools:** `ls`, `read_file`, `glob`, `grep`, `write_file`, `str_replace`.

> [!TIP]
> Only one `web_search` and one `web_fetch` provider can be active at a time. Comment out alternatives before enabling a new one.

### 3.5 Sandbox Configuration

Three sandbox modes are available:

| Mode | `use` Path | Isolation | Requirements |
|------|-----------|-----------|-------------|
| **Local** (default) | `deerflow.sandbox.local:LocalSandboxProvider` | None (host process) | None |
| **AIO Container** | `deerflow.community.aio_sandbox:AioSandboxProvider` | Docker / Apple Container | Docker or Apple Container runtime |
| **Provisioner (K8s)** | `deerflow.community.aio_sandbox:AioSandboxProvider` + `provisioner_url` | Kubernetes Pod per sandbox | K8s cluster + provisioner service |

```yaml
# Local sandbox (default)
sandbox:
  use: deerflow.sandbox.local:LocalSandboxProvider
  allow_host_bash: false          # Set true only for trusted single-user
  bash_output_max_chars: 20000
  read_file_output_max_chars: 50000
  ls_output_max_chars: 20000
  # mounts:
  #   - host_path: /home/user/project
  #     container_path: /mnt/project
  #     read_only: true
```

> [!CAUTION]
> `allow_host_bash: true` gives the agent unrestricted shell access on the host. Only enable for fully trusted, single-user local workflows. For multi-user or production use, deploy the AIO Container or Kubernetes sandbox.

### 3.6 Subagents Configuration

```yaml
subagents:
  timeout_seconds: 900           # Default 15 min
  # max_turns: 120               # Global max-turn override
  agents:
    general-purpose:
      timeout_seconds: 1800      # 30 min for complex tasks
      max_turns: 160
      # model: qwen3:32b         # Override model
    bash:
      timeout_seconds: 300       # 5 min
      max_turns: 80
```

Subagents inherit the lead agent's model by default. Set `model` to use a cheaper/faster model for specific agents.

### 3.7 Memory Configuration

```yaml
memory:
  enabled: true
  storage_path: memory.json      # Relative to backend/
  debounce_seconds: 30           # Queue delay before processing
  model_name: null               # null = default model
  max_facts: 100
  fact_confidence_threshold: 0.7
  injection_enabled: true        # Inject into system prompt
  max_injection_tokens: 2000
```

Memory uses a debounced queue: fact extraction requests are batched and processed after `debounce_seconds` of inactivity. Facts below `fact_confidence_threshold` are discarded.

### 3.8 Summarization Configuration

Automatic context compression when conversations approach token limits:

```yaml
summarization:
  enabled: true
  model_name: null               # Use a lightweight model for cost savings
  trigger:
    - type: tokens
      value: 15564               # Summarize when token count reached
    # - type: messages
    #   value: 50
    # - type: fraction
    #   value: 0.8               # 80% of model max input
  keep:
    type: messages
    value: 10                    # Retain 10 most recent messages
  trim_tokens_to_summarize: 15564
```

### 3.9 Checkpointer Configuration

State persistence for multi-turn conversations:

| Type | Persistence | Use Case |
|------|-------------|----------|
| `memory` | In-process only (default) | Development, ephemeral |
| `sqlite` | File-based | Single-process, survives restarts |
| `postgres` | PostgreSQL | Multi-process, production |

```yaml
checkpointer:
  type: sqlite
  connection_string: checkpoints.db

# PostgreSQL:
# checkpointer:
#   type: postgres
#   connection_string: postgresql://user:password@localhost:5432/deerflow
```

> [!NOTE]
> The checkpointer setting applies to the embedded `DeerFlowClient`. The LangGraph Server manages its own persistence separately via server infrastructure.

### 3.10 IM Channels Configuration

All channels use outbound connections (WebSocket or polling) and require no public IP:

```yaml
channels:
  langgraph_url: http://localhost:2024
  gateway_url: http://localhost:8001

  session:                       # Default session settings
    assistant_id: lead_agent
    context:
      thinking_enabled: true
      is_plan_mode: false

  feishu:
    enabled: false
    app_id: $FEISHU_APP_ID
    app_secret: $FEISHU_APP_SECRET

  slack:
    enabled: false
    bot_token: $SLACK_BOT_TOKEN
    app_token: $SLACK_APP_TOKEN  # Socket Mode
    allowed_users: []            # Empty = allow all

  telegram:
    enabled: false
    bot_token: $TELEGRAM_BOT_TOKEN

  wechat:
    enabled: false
    bot_token: $WECHAT_BOT_TOKEN
    ilink_bot_id: $WECHAT_ILINK_BOT_ID
    qrcode_login_enabled: true

  wecom:
    enabled: false
    bot_id: $WECOM_BOT_ID
    bot_secret: $WECOM_BOT_SECRET
```

Per-channel and per-user session overrides are supported. Set `session.assistant_id` to a custom agent name for IM-specific behavior.

### 3.11 MCP Server Configuration

Define MCP servers in `extensions_config.json`:

```json
{
  "mcpServers": {
    "filesystem": {
      "enabled": true,
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"],
      "env": {},
      "description": "Filesystem access"
    },
    "github": {
      "enabled": false,
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": { "GITHUB_TOKEN": "$GITHUB_TOKEN" }
    }
  }
}
```

Supported transport types: `stdio`, `sse`, `streamable_http`. Environment variables in `env` values prefixed with `$` resolve from the host environment.

**Tool search (deferred loading):** When many MCP servers expose large tool sets, enable deferred loading to reduce context:

```yaml
tool_search:
  enabled: true   # Tools listed by name only; loaded on demand via tool_search
```

### 3.12 Guardrails Configuration

Pre-execution authorization for tool calls:

```yaml
# Option 1: Built-in allowlist
guardrails:
  enabled: true
  provider:
    use: deerflow.guardrails.builtin:AllowlistProvider
    config:
      denied_tools: ["bash", "write_file"]

# Option 2: OAP (Open Agent Passport) standard
guardrails:
  enabled: true
  provider:
    use: aport_guardrails.providers.generic:OAPGuardrailProvider
```

### 3.13 Circuit Breaker

Prevents retry storms when an LLM provider is down:

```yaml
circuit_breaker:
  failure_threshold: 5      # Consecutive failures before opening
  recovery_timeout_sec: 60  # Wait before half-open probe
```

### 3.14 Additional Configuration

| Feature | Config Key | Default | Description |
|---------|-----------|---------|-------------|
| Title generation | `title.enabled` | `true` | Auto-generate conversation titles |
| Title max length | `title.max_words` / `title.max_chars` | 6 / 60 | Title constraints |
| Token usage tracking | `token_usage.enabled` | `false` | Record input/output tokens per call |
| Log level | `log_level` | `info` | Options: debug, info, warning, error |
| Agents API | `agents_api.enabled` | `false` | HTTP management of custom agent SOUL/config |
| Skill evolution | `skill_evolution.enabled` | `false` | Agent can create skills in `skills/custom/` |
| Skills path | `skills.path` | `../skills` | Host path to skills directory |
| Skills container path | `skills.container_path` | `/mnt/skills` | Sandbox-visible path |

---

## 4. Environment Variables Reference

### 4.1 Backend Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `DEER_FLOW_CONFIG_PATH` | (none) | Override config.yaml location |
| `DEER_FLOW_EXTENSIONS_CONFIG_PATH` | (none) | Override extensions_config.json location |
| `DEER_FLOW_HOME` | (none) | Base directory for runtime data (.deer-flow/) |
| `DEER_FLOW_HOST_BASE_DIR` | (none) | Host-side base dir for sandbox volume mounts |
| `DEER_FLOW_HOST_SKILLS_PATH` | (none) | Host-side skills dir for sandbox mounts |
| `DEER_FLOW_SANDBOX_HOST` | (none) | Hostname for sandbox access from containers |
| `GATEWAY_HOST` | `0.0.0.0` | Gateway bind address |
| `GATEWAY_PORT` | `8001` | Gateway bind port |
| `CORS_ORIGINS` | `http://localhost:3000` | Comma-separated allowed CORS origins |

### 4.2 Frontend Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `NEXT_PUBLIC_BACKEND_BASE_URL` | (none) | Gateway API URL for client-side requests |
| `NEXT_PUBLIC_LANGGRAPH_BASE_URL` | (none) | LangGraph server URL |
| `NEXT_PUBLIC_STATIC_WEBSITE_ONLY` | (none) | Run as static documentation site |
| `BETTER_AUTH_SECRET` | (none) | Auth signing secret |
| `BETTER_AUTH_GITHUB_CLIENT_ID` | (none) | GitHub OAuth client ID |
| `BETTER_AUTH_GITHUB_CLIENT_SECRET` | (none) | GitHub OAuth client secret |
| `SKIP_ENV_VALIDATION` | (none) | Bypass env validation at build time |

### 4.3 Provisioner Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `K8S_NAMESPACE` | `deer-flow` | Kubernetes namespace for sandbox Pods |
| `SANDBOX_IMAGE` | `enterprise-public-cn-beijing.cr.volces.com/...` | Container image for sandbox Pods |
| `SKILLS_HOST_PATH` | `/skills` | Host path for K8s HostPath volume (skills) |
| `THREADS_HOST_PATH` | `/.deer-flow/threads` | Host path for thread data |
| `SKILLS_PVC_NAME` | (none) | PVC name for skills (replaces hostPath) |
| `USERDATA_PVC_NAME` | (none) | PVC name for user data (replaces hostPath) |
| `KUBECONFIG_PATH` | `/root/.kube/config` | Path to kubeconfig inside container |
| `NODE_HOST` | `host.docker.internal` | Hostname to reach K8s NodePort services |
| `K8S_API_SERVER` | (none) | Override K8s API server URL |

### 4.4 LLM Provider API Keys

| Variable | Provider |
|----------|----------|
| `OPENAI_API_KEY` | OpenAI |
| `ANTHROPIC_API_KEY` | Anthropic |
| `DEEPSEEK_API_KEY` | DeepSeek |
| `GEMINI_API_KEY` | Google Gemini |
| `OPENROUTER_API_KEY` | OpenRouter |
| `VLLM_API_KEY` | vLLM (self-hosted) |
| `TAVILY_API_KEY` | Tavily search |
| `EXA_API_KEY` | Exa search |
| `FIRECRAWL_API_KEY` | Firecrawl search |
| `MINIMAX_API_KEY` | MiniMax |

### 4.5 IM Channel Secrets

| Variable | Channel |
|----------|---------|
| `FEISHU_APP_ID` / `FEISHU_APP_SECRET` | Feishu/Lark |
| `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` | Slack (Socket Mode) |
| `TELEGRAM_BOT_TOKEN` | Telegram |
| `WECHAT_BOT_TOKEN` / `WECHAT_ILINK_BOT_ID` | WeChat |
| `WECOM_BOT_ID` / `WECOM_BOT_SECRET` | WeCom |

### 4.6 Observability

| Variable | Provider |
|----------|----------|
| `LANGSMITH_API_KEY` (or `LANGCHAIN_API_KEY`) | LangSmith tracing |
| `LANGFUSE_HOST` / `LANGFUSE_PUBLIC_KEY` / `LANGFUSE_SECRET_KEY` | Langfuse tracing |

---

## 5. Deployment & Scaling

### 5.1 Local Development (make commands)

```bash
# First-time setup
make setup                # Interactive wizard: LLM provider, search, sandbox

# Start services
cd backend && uv sync && PYTHONPATH=. uv run uvicorn app.gateway.app:app \
    --host 0.0.0.0 --port 8001 --reload
cd backend && uv run langgraph dev --no-browser --host 0.0.0.0 --port 2024

# Frontend
cd frontend && pnpm install && pnpm run dev
```

### 5.2 Docker Compose Development

```bash
# Set project root for volume mounts
export DEER_FLOW_ROOT=$(pwd)

# Start all services
docker compose -f docker/docker-compose-dev.yaml up --build

# Access at http://localhost:2026
```

**Services started:**

| Service | Container Name | Exposed Port | Health Check |
|---------|---------------|-------------|-------------|
| nginx | deer-flow-nginx | 2026 | — |
| frontend | deer-flow-frontend | (internal 3000) | — |
| gateway | deer-flow-gateway | (internal 8001) | — |
| langgraph | deer-flow-langgraph | (internal 2024) | — |
| provisioner | deer-flow-provisioner | (internal 8002) | `curl http://localhost:8002/health` |

**Docker networking:** All services communicate on a bridge network (`192.168.200.0/24`). For inter-container references, use service names (`gateway:8001`, `langgraph:2024`).

> [!IMPORTANT]
> On Docker Compose, set `DEER_FLOW_ROOT` to the absolute path of the project root before starting. Volume mounts for skills, config, and thread data depend on this variable.

### 5.3 Docker Volume Strategy

Named volumes preserve Python virtual environments across restarts:

| Volume | Purpose |
|--------|---------|
| `gateway-venv` | Gateway `.venv` (prevents host mount shadowing) |
| `langgraph-venv` | LangGraph server `.venv` |
| `gateway-uv-cache` | uv cache (avoids symlink failures on macOS) |
| `langgraph-uv-cache` | uv cache for LangGraph |

### 5.4 Kubernetes Sandbox Provisioner

For production sandbox isolation, the provisioner creates per-sandbox Pods:

```
┌────────────┐  HTTP   ┌─────────────┐  K8s API  ┌──────────────┐
│  backend   │ ──────▸ │ provisioner │ ────────▸ │  K8s cluster │
│            │         │ :8002       │           │              │
└────────────┘         └─────────────┘           └──────┬───────┘
                                                        │ creates
                       ┌─────────────┐          ┌───────▼──────┐
                       │   backend   │ ───────▸ │ sandbox Pods │
                       │             │ NodePort  │              │
                       └─────────────┘          └──────────────┘
```

**Provisioner API:**

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/api/sandboxes` | Create sandbox Pod + Service |
| `DELETE` | `/api/sandboxes/{sandbox_id}` | Destroy sandbox |
| `GET` | `/api/sandboxes/{sandbox_id}` | Get sandbox status & URL |
| `GET` | `/api/sandboxes` | List all sandboxes |
| `GET` | `/health` | Health check |

For production, use PVC-backed storage instead of hostPath:

```yaml
environment:
  - SKILLS_PVC_NAME=deer-flow-skills-pvc
  - USERDATA_PVC_NAME=deer-flow-userdata-pvc
```

### 5.5 nginx Routing

nginx handles all external traffic on port 2026:

- `/api/*` requests → gateway (:8001)
- LangGraph requests → LangGraph server (:2024), controlled by `LANGGRAPH_UPSTREAM` and `LANGGRAPH_REWRITE` env vars
- All other requests → frontend (:3000)
- CORS is handled at the nginx layer (not FastAPI middleware)

---

## 6. Operations

### 6.1 Gateway API Endpoints

The Gateway exposes 13 router groups:

| Router | Mount Path | Tag | Purpose |
|--------|-----------|-----|---------|
| `models` | `/api/models` | models | Query available LLM models and configs |
| `mcp` | `/api/mcp` | mcp | MCP server configuration management |
| `memory` | `/api/memory` | memory | Global memory data access |
| `skills` | `/api/skills` | skills | Skills listing and enable/disable |
| `artifacts` | `/api/threads/{thread_id}/artifacts` | artifacts | Thread file artifacts |
| `uploads` | `/api/threads/{thread_id}/uploads` | uploads | File upload management |
| `threads` | `/api/threads/{thread_id}` | threads | Thread-local filesystem cleanup |
| `agents` | `/api/agents` | agents | Custom agent CRUD |
| `suggestions` | `/api/threads/{thread_id}/suggestions` | suggestions | Follow-up question generation |
| `channels` | `/api/channels` | channels | IM channel status and management |
| `assistants_compat` | (LangGraph stub) | assistants-compat | LangGraph Platform assistants compatibility |
| `thread_runs` | (LangGraph compat) | runs | Thread-scoped run lifecycle |
| `runs` | (LangGraph compat) | runs | Stateless runs (stream/wait without thread) |

**Health endpoint:** `GET /health` returns `{"status": "healthy", "service": "deer-flow-gateway"}`.

**API documentation:** Available at `/docs` (Swagger UI) and `/redoc` (ReDoc) when the gateway is running.

### 6.2 Runtime Singletons

The gateway initializes four singletons during startup via the `langgraph_runtime` context manager:

| Singleton | Class | Purpose |
|-----------|-------|---------|
| `stream_bridge` | `StreamBridge` | SSE event distribution to clients |
| `checkpointer` | (async provider) | State persistence |
| `store` | (async provider) | Thread metadata persistence |
| `run_manager` | `RunManager` | Run lifecycle, conflict detection |

If any singleton is missing at request time, the corresponding getter raises HTTP 503.

### 6.3 Run Lifecycle

```
Client POST /runs/stream (or /threads/{id}/runs/stream)
  → RunManager.create_or_reject()     # 409 on conflict
  → resolve_agent_factory()           # Always make_lead_agent
  → build_run_config()                # Thread ID, agent_name, context overrides
  → asyncio.create_task(run_agent())  # Background execution
  → SSE consumer yields frames       # event: data: id: format
  → On disconnect:
      cancel mode → abort background task
      continue mode → task runs, events discarded
```

**Context overrides in run requests:** The `context` field carries DeerFlow-specific keys:

| Key | Type | Purpose |
|-----|------|---------|
| `model_name` | string | Override LLM model for this run |
| `mode` | string | Agent mode |
| `thinking_enabled` | bool | Toggle thinking/reasoning |
| `reasoning_effort` | string | Reasoning effort level |
| `is_plan_mode` | bool | Enable plan mode with todo lists |
| `subagent_enabled` | bool | Allow sub-agent delegation |
| `max_concurrent_subagents` | int | Concurrency limit |
| `agent_name` | string | Custom agent selection |
| `is_bootstrap` | bool | Bootstrap mode flag |

### 6.4 Filesystem Layout

```
backend/.deer-flow/                   # DEER_FLOW_HOME
├── memory.json                       # Global memory store
├── USER.md                           # Global user profile
├── agents/                           # Custom agents
│   └── {agent-name}/
│       ├── config.yaml               # Agent config (name, model, skills)
│       ├── SOUL.md                    # Agent personality/values
│       └── memory.json               # Per-agent memory
├── threads/
│   └── {thread-id}/
│       └── user-data/
│           ├── work/                  # Sandbox working directory
│           ├── uploads/               # User-uploaded files
│           └── outputs/               # Agent-generated artifacts
└── checkpoints.db                    # SQLite checkpointer (if configured)
```

Thread IDs are validated against pattern `^[A-Za-z0-9_\-]+$`. Path traversal is explicitly blocked with a 403 response.

### 6.5 Observability & Tracing

DeerFlow supports two tracing providers that can run simultaneously:

| Provider | Required Variables | Auto-Detection |
|----------|--------------------|---------------|
| **LangSmith** | `LANGSMITH_API_KEY` (or `LANGCHAIN_API_KEY`) | Enabled when key is set |
| **Langfuse** | `LANGFUSE_HOST`, `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY` | Enabled when all three are set |

Configure via `config.yaml`:

```yaml
tracing:
  langsmith:
    enabled: true
  langfuse:
    enabled: true
```

Validation runs at startup — missing keys for enabled providers raise `ValueError` with specific guidance.

### 6.6 Logging

```yaml
log_level: info    # debug | info | warning | error
```

Logs use format: `%(asctime)s - %(name)s - %(levelname)s - %(message)s`. Gateway and provisioner each configure independent logging.

---

## 7. Security

### 7.1 Sandbox Isolation

| Level | Isolation | Risk |
|-------|-----------|------|
| Local | None — runs as the host process | Agent has full host access when `allow_host_bash: true` |
| AIO Container | Docker container per sandbox | Container escape = host compromise |
| K8s Provisioner | Dedicated Pod per sandbox | Best isolation; use PVCs and NetworkPolicy |

> [!CAUTION]
> `LocalSandboxProvider` with `allow_host_bash: true` is **not a security boundary**. The agent can execute arbitrary commands on the host. Use only in fully trusted, single-user environments.

### 7.2 Path Traversal Protection

The `Paths.resolve_virtual_path()` method validates all sandbox file access:

- Paths must start with a recognized prefix (`/mnt/user-data/`, `/mnt/skills/`)
- Path traversal (`../`) is detected and rejected with HTTP 403
- Thread IDs are validated against `^[A-Za-z0-9_\-]+$`

### 7.3 Authentication

- **Frontend:** `better-auth` with optional GitHub OAuth (`BETTER_AUTH_GITHUB_CLIENT_ID`)
- **Gateway API:** No built-in auth layer — CORS handled by nginx. The `agents_api` is disabled by default
- **IM Channels:** Each channel has its own auth (bot tokens, app secrets). `allowed_users` lists restrict access

### 7.4 Agent API Security

The `agents_api.enabled` flag (default: `false`) controls HTTP access to custom agent management. When enabled, it exposes read/write access to SOUL.md, config.yaml, and USER.md for all agents.

> [!WARNING]
> Only enable `agents_api` when the gateway is behind a trusted, authenticated admin boundary. The API has no built-in auth — anyone who can reach the gateway can modify agent configurations.

### 7.5 ACP Agent Permissions

ACP agents (Claude Code, Codex CLI) run as subprocesses. `auto_approve_permissions: false` (default) denies all permission requests. Set to `true` only for trusted agents.

### 7.6 Guardrails

When `guardrails.enabled: true`, every tool call passes through the configured provider before execution. The built-in `AllowlistProvider` supports `denied_tools` to block specific tools (e.g., `bash`, `write_file`). External OAP-compliant providers enable policy-based authorization.

---

## 8. Custom Agents

### 8.1 Creating a Custom Agent

Create a directory under `agents/` (inside `DEER_FLOW_HOME`):

```
agents/my-agent/
├── config.yaml    # Required
└── SOUL.md        # Optional personality/values prompt
```

**config.yaml:**

```yaml
name: my-agent
description: A specialized agent for data analysis
model: gpt-4o              # Override default model (optional)
tool_groups:                # Restrict available tool groups (optional)
  - web
  - file:read
skills:                     # Restrict loaded skills (optional)
  - crawl_and_report
  # null or omitted = all skills; [] = no skills
```

**SOUL.md:** Injected into the lead agent's system prompt when `agent_name` matches. Define personality, values, and behavioral guardrails.

### 8.2 Agent Name Rules

- Pattern: `^[A-Za-z0-9-]+$` (letters, digits, hyphens only)
- Normalized to lowercase with underscores replaced by hyphens
- Invalid names raise `ValueError`

### 8.3 Per-Channel Agent Routing

IM channels can specify a custom agent via `session.assistant_id`. Per-user overrides are supported:

```yaml
channels:
  wechat:
    session:
      assistant_id: mobile-agent
      users:
        "123456789":
          assistant_id: vip-agent
```

---

## 9. Skills System

### 9.1 Overview

Skills are structured markdown files in `skills/` that the lead agent can invoke. They define reusable workflows (e.g., "crawl and generate report", "create podcast script").

- **Built-in skills:** `skills/public/` (15+ included)
- **Custom skills:** `skills/custom/` (user-created)
- **Skill evolution:** When `skill_evolution.enabled: true`, the agent can autonomously create and improve skills in `skills/custom/`

### 9.2 Skill Access Control

Per-agent skill restrictions in `agents/<name>/config.yaml`:

| `skills` value | Behavior |
|----------------|----------|
| `null` / omitted | Load all globally enabled skills |
| `[]` (empty list) | Disable all skills |
| `["skill-name"]` | Load only specified skills |

Skill enable/disable state can also be managed via `extensions_config.json` under the `skills` key and through the `/api/skills` gateway endpoint.

---

## 10. Troubleshooting

### 10.1 Common Errors

| Error | Cause | Resolution |
|-------|-------|------------|
| `Failed to load configuration during gateway startup` | Missing or invalid `config.yaml` | Run `make setup` or copy `config.example.yaml` |
| `Config file specified by DEER_FLOW_CONFIG_PATH not found` | `DEER_FLOW_CONFIG_PATH` points to nonexistent file | Verify the path; unset to use default resolution |
| `Model {name} not found in config` | Requested model not in `models:` list | Add model entry to `config.yaml` |
| `Model {name} does not support thinking` | `supports_thinking` not set | Add `supports_thinking: true` to model config |
| `Environment variable {VAR} not found for config value` | `$VAR` in config but not in env | Set the variable in `.env` or shell |
| `Extensions config file is not valid JSON` | Malformed `extensions_config.json` | Validate JSON syntax |
| `LangSmith tracing enabled but API key not set` | Tracing enabled without credentials | Set `LANGSMITH_API_KEY` or disable tracing |
| `Stream bridge not available` (503) | Runtime not initialized | Check gateway startup logs for initialization errors |
| `Run manager not available` (503) | Runtime not initialized | Same as above |
| `Invalid assistant_id` (ValueError) | Special characters in agent name | Use only letters, digits, and hyphens |
| `Agent directory not found` | Custom agent path missing | Create `agents/<name>/` with `config.yaml` |
| `Access denied: path traversal detected` (403) | `../` in file path | Use absolute virtual paths without traversal |
| `Codex CLI credential not found` | Missing `~/.codex/auth.json` | Run `codex auth` to authenticate |

### 10.2 Docker Troubleshooting

| Issue | Resolution |
|-------|------------|
| `uv sync` fails on startup | Container recreates `.venv` and retries once automatically. If persistent, run `docker compose down -v` to clear named volumes |
| Frontend hot-reload not working | Ensure `WATCHPACK_POLLING=true` is set (default in compose file) |
| Gateway can't reach LangGraph | Verify both are on `deer-flow-dev` network; use service name `langgraph:2024` |
| Provisioner health check failing | Ensure kubeconfig is mounted and K8s cluster is reachable |
| macOS `uv` symlink failures | Named volumes (`gateway-uv-cache`) avoid this — do not bind-mount the uv cache |

### 10.3 Debugging

Interactive debug mode with breakpoints:

```bash
cd backend && PYTHONPATH=. uv run python debug.py
```

This starts a REPL-style session with the lead agent. Configure model and thread ID in `debug.py`.

---

## 11. Developer Internals

### 11.1 Module Reference

**Package structure:**

```
backend/
├── app/gateway/              # FastAPI gateway application
│   ├── app.py                # App factory, lifespan, router mounting
│   ├── config.py             # GatewayConfig (host, port, CORS)
│   ├── deps.py               # Runtime singletons (StreamBridge, RunManager, etc.)
│   ├── services.py           # Run lifecycle, SSE formatting, input normalization
│   ├── path_utils.py         # Virtual path resolution
│   └── routers/              # 13 router modules
├── packages/harness/deerflow/
│   ├── agents/               # Lead agent, subagents, checkpointer
│   ├── config/               # 22 config modules (Pydantic models)
│   ├── models/               # LLM provider wrappers
│   │   ├── factory.py        # create_chat_model() — model instantiation
│   │   ├── claude_provider.py    # ClaudeChatModel (OAuth)
│   │   ├── openai_codex_provider.py  # CodexChatModel
│   │   ├── patched_deepseek.py   # PatchedChatDeepSeek
│   │   ├── patched_minimax.py    # PatchedChatMiniMax
│   │   ├── patched_openai.py     # PatchedChatOpenAI (thought_signature)
│   │   ├── vllm_provider.py      # VllmChatModel
│   │   └── credential_loader.py  # Claude Code + Codex CLI auth loading
│   ├── community/            # Search/fetch provider implementations
│   ├── guardrails/           # AllowlistProvider, guardrail interface
│   ├── mcp/                  # MCP tool initialization
│   ├── runtime/              # StreamBridge, RunManager, RunRecord, RunStatus
│   ├── sandbox/              # LocalSandboxProvider, tool implementations
│   └── utils/                # file_conversion, network, readability
├── debug.py                  # Interactive agent debugger
├── langgraph.json            # LangGraph server configuration
└── ruff.toml                 # Linter config (line-length: 240, py312)
```

### 11.2 Key Classes

| Class | Module | Purpose |
|-------|--------|---------|
| `AppConfig` | `config/app_config.py` | Central config — models, tools, sandbox, all subsystems |
| `ExtensionsConfig` | `config/extensions_config.py` | MCP servers, skill state |
| `GatewayConfig` | `gateway/config.py` | Gateway host/port/CORS |
| `Paths` | `config/paths.py` | 20+ path resolution methods (thread dirs, virtual paths) |
| `SandboxConfig` | `config/sandbox_config.py` | Sandbox provider + mounts + truncation limits |
| `SubagentsAppConfig` | `config/subagents_config.py` | Per-agent timeout/model/max_turns overrides |
| `StreamBridge` | `runtime/` | Per-run SSE event distribution |
| `RunManager` | `runtime/` | Run lifecycle, conflict detection (409 on concurrent) |
| `RunRecord` | `runtime/` | Run state: pending → running → completed/cancelled |
| `AgentConfig` | `config/agents_config.py` | Custom agent definition (name, model, skills, tool_groups) |
| `ACPAgentConfig` | `config/acp_config.py` | External ACP agent subprocess config |
| `MemoryConfig` | `config/memory_config.py` | Memory system settings |
| `SummarizationConfig` | `config/summarization_config.py` | Context compression triggers and retention |
| `CircuitBreakerConfig` | `config/app_config.py` | LLM call failure circuit breaker |
| `TracingConfig` | `config/tracing_config.py` | LangSmith + Langfuse tracing |

### 11.3 Configuration Loading Chain

```
AppConfig.from_file(config_path)
  → resolve_config_path()           # DEER_FLOW_CONFIG_PATH → cwd → repo root
  → yaml.safe_load()
  → resolve_env_variables()         # Recursive $VAR substitution
  → Load subsystem configs:
      load_memory_config_from_dict()
      load_summarization_config_from_dict()
      load_title_config_from_dict()
      load_subagents_config_from_dict()
      load_checkpointer_config_from_dict()
      load_guardrails_config_from_dict()
      load_acp_config_from_dict()
      load_agents_api_config_from_dict()
      load_stream_bridge_config_from_dict()
      load_tool_search_config_from_dict()
```

Environment variable resolution is recursive: any string value starting with `$` is replaced with the corresponding environment variable. Missing variables raise `ValueError`.

### 11.4 Model Factory

`create_chat_model(name, thinking_enabled)` in `models/factory.py`:

1. Looks up model name in `AppConfig.models`
2. Resolves the `use` class path via `resolve_variable()`
3. Applies `when_thinking_enabled` or `when_thinking_disabled` overrides
4. Instantiates the LangChain `BaseChatModel` subclass

### 11.5 Credential Loading (Zero-Config Auth)

Two zero-config auth providers use local CLI credentials:

| Provider | Credential Source | Env Override |
|----------|------------------|-------------|
| Claude Code OAuth | `~/.claude/credentials.json` | `CLAUDE_CODE_CREDENTIALS_PATH`, `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_AUTH_TOKEN` |
| Codex CLI | `~/.codex/auth.json` | `CODEX_AUTH_PATH` |

`ClaudeCodeCredential` tracks token expiry via `is_expired()`. Docker containers mount `~/.claude` and `~/.codex` read-only for auto-auth.

### 11.6 Code Conventions

| Convention | Value |
|-----------|-------|
| Python version | 3.12+ |
| Line length | 240 (ruff) |
| Lint rules | E, F, I, UP (ruff) |
| Import sorting | isort, first-party: `deerflow`, `app` |
| Quote style | Double |
| Indent style | Spaces |
| Config models | Pydantic v2 `BaseModel` |
| Async | `asyncio` throughout (gateway, runtime, providers) |

### 11.7 Testing

- **Backend linting:** `ruff check` with `ruff.toml` configuration
- **Frontend:** pnpm-based test and lint commands
- **Unit tests:** Located alongside source in standard pytest layout
- **API tests:** Reference in `frontend/tests/unit/core/api/`
- **Debug mode:** `backend/debug.py` for interactive agent testing with breakpoints

### 11.8 Known Issues (from GitHub)

| Issue | Description | Status |
|-------|-------------|--------|
| #2293 | Error 400 during normal operation | Open |
| #2302 | Event loop closed unexpectedly | Open |
| #2255 | Duplicate tool calls generated | Open |
| #2203 | JSON parse errors in responses | Open |
| #2262 | File upload failures | Open |
| #2303 | Conversation switching interrupts running tasks | Open |
| #2304 | Loop detection triggering incorrectly | Open |
| #2297 | Docker container cannot access host ports | Open |

---

## 12. Quick Reference

### 12.1 Key Files

| File | Purpose |
|------|---------|
| `config.yaml` | Primary config (models, tools, sandbox, memory, channels) |
| `config.example.yaml` | Full config reference with all options documented |
| `extensions_config.json` | MCP server definitions |
| `.env` | API keys and secrets |
| `backend/langgraph.json` | LangGraph server config (graph entry point, checkpointer) |
| `backend/ruff.toml` | Python linter configuration |
| `docker/docker-compose-dev.yaml` | Docker Compose development environment |
| `docker/nginx/nginx.conf` | nginx reverse proxy configuration (template) |

### 12.2 Common Commands

| Task | Command |
|------|---------|
| First-time setup | `make setup` |
| Start gateway | `cd backend && PYTHONPATH=. uv run uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001 --reload` |
| Start LangGraph | `cd backend && uv run langgraph dev --no-browser --host 0.0.0.0 --port 2024` |
| Start frontend | `cd frontend && pnpm run dev` |
| Docker Compose up | `docker compose -f docker/docker-compose-dev.yaml up --build` |
| Config upgrade | `make config-upgrade` |
| Debug agent | `cd backend && PYTHONPATH=. uv run python debug.py` |
| Install Ollama extra | `cd backend && uv pip install 'deerflow-harness[ollama]'` |

### 12.3 Port Map

| Port | Service | Protocol |
|------|---------|----------|
| 2024 | LangGraph Server | HTTP/SSE |
| 2026 | nginx (unified entry) | HTTP |
| 3000 | Frontend (Next.js dev) | HTTP |
| 8001 | Gateway API | HTTP |
| 8002 | Sandbox Provisioner | HTTP |
| 11434 | Ollama (if local) | HTTP |

---

_Generated May 2026 from bytedance/deer-flow at 259a684. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
