# Dify -- Admin Guide

This guide is for the people who deploy, operate, secure, extend, and contribute to Dify. It assumes you have read the [User Guide](langgenius__dify__UserGuide.md) and have a Dify instance running. If you are new to Dify, start there.

> [!NOTE]
> Dify is classified as a "massive" complexity project: 11,069 files, 31.7 million source characters, 258 significant modules. This guide covers the full operational surface.

---

## Table of Contents

- [Executive Summary](#executive-summary)
- [Architecture](#architecture)
- [System Requirements](#system-requirements)
- [Installation and Deployment](#installation-and-deployment)
- [Configuration Reference](#configuration-reference)
- [Security](#security)
- [Operations](#operations)
- [Workflow Engine Internals](#workflow-engine-internals)
- [RAG Pipeline Internals](#rag-pipeline-internals)
- [Plugin System Internals](#plugin-system-internals)
- [Vector Database Backend System](#vector-database-backend-system)
- [Frontend Architecture](#frontend-architecture)
- [Backend Architecture](#backend-architecture)
- [Database Schema and Migrations](#database-schema-and-migrations)
- [Celery Task System](#celery-task-system)
- [Observability and Telemetry](#observability-and-telemetry)
- [Code Patterns and Conventions](#code-patterns-and-conventions)
- [SDKs and Client Libraries](#sdks-and-client-libraries)
- [E2E Testing](#e2e-testing)
- [Contributing](#contributing)
- [Known Issues and Improvements](#known-issues-and-improvements)
- [CLI Reference](#cli-reference)
- [Quick Reference](#quick-reference)

---

## Executive Summary

Dify is an open-source LLM application development platform that provides a visual workflow builder, RAG pipeline, agent framework, model abstraction layer, and plugin ecosystem. It serves as the full-stack infrastructure for building and deploying AI applications.

### Technology Stack

| Layer | Technology |
|-------|-----------|
| **Backend API** | Python 3.12+, Flask, Gunicorn + gevent, SQLAlchemy ORM, Pydantic v2 |
| **Task Queue** | Celery with Redis broker |
| **Frontend** | Next.js 15 (App Router), React 19, Tailwind CSS 4, TypeScript |
| **State Management** | Zustand, TanStack Query, Jotai |
| **Database** | PostgreSQL (relational), Redis (cache/queue/pubsub/sessions) |
| **Vector Store** | 30+ pluggable backends via `dify.vector_backends` entry points |
| **Plugin Runtime** | Go-based `plugin_daemon`, JSON-RPC over stdio |
| **Code Sandbox** | Isolated execution environment for user code |
| **Reverse Proxy** | nginx (Docker) or any external proxy |
| **Package Management** | uv (Python), pnpm 10.33.0 (JavaScript) |

### Key Metrics (Source: commit `13a9359`)

| Metric | Value |
|--------|-------|
| Total files | 11,069 |
| Source characters | 31.7M |
| Extracted classes | 1,997 |
| Extracted functions | 7,321 |
| CLI commands | 129 |
| Config keys | 59 extracted (200+ total in `.env.example`) |
| Vector DB backends | 30+ |
| HIGH-priority modules | 258 |

---

## Architecture

### System Architecture

```
                    Internet
                       |
              +--------v--------+
              |     nginx       |  Port 80/443, SSL termination
              |  (reverse proxy)|  Static files, WebSocket upgrade
              +---+--------+---+
                  |        |
        +---------v--+  +--v---------+
        |   web      |  |   api      |  Flask + gevent + WebSocket
        | (Next.js)  |  | :5001      |  4 API surfaces
        | :3000      |  +--+-+--+----+
        +------------+     | |  |
                           | |  +----> plugin_daemon (Go) :5002
                           | |          |-> Plugin processes (stdio JSON-RPC)
                           | |
                    +------+ +------+
                    |               |
              +-----v----+  +------v------+
              |  worker   |  |   sandbox   |  Code execution :8194
              | (Celery)  |  +-------------+
              +--+--+-----+
                 |  |
          +------+  +------+
          |                |
    +-----v------+  +------v-------+
    | PostgreSQL |  |    Redis     |  Cache, broker, sessions,
    |  :5432     |  |   :6379      |  rate limiting, pubsub
    +------------+  +--------------+
          |
    +-----v---------+
    | Vector Store   |  Weaviate :8080 (default)
    | (pluggable)    |  or any of 30+ backends
    +----------------+
```

### Four API Surfaces

Dify exposes four distinct API surfaces, each serving a different audience:

| Surface | Base Path | Auth | Purpose |
|---------|-----------|------|---------|
| **Console API** | `/console/api/*` | JWT (session) | Web UI operations (app CRUD, settings, workspace management) |
| **Service API** | `/v1/*` | API Key (Bearer) | External integrations (chat, completion, workflow execution) |
| **Web API** | `/api/*` | App token | End-user web app interactions |
| **Inner API** | `/inner/api/*` | `INNER_API_KEY_FOR_PLUGIN` | Plugin daemon communication |

> [!IMPORTANT]
> The Inner API is exclusively for plugin daemon communication. It is not intended for external use and uses a shared secret key (`INNER_API_KEY_FOR_PLUGIN`) for authentication.

### Process Model

The API server runs Flask under Gunicorn with the `gevent` worker class, providing cooperative multitasking for handling concurrent requests. The entry point is `api/app.py`:

```python
# api/app.py
app = create_app()
celery = app.extensions["celery"]

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)
```

In production:
```bash
gunicorn --bind 0.0.0.0:5001 \
  --worker-class gevent \
  --workers 1 \
  --timeout 200 \
  app:app
```

> [!WARNING]
> The gevent worker requires monkey-patching at import time. The `app_factory.py` handles this. Do not import standard library threading or socket modules before gevent patches are applied.

---

## System Requirements

### Hardware Requirements

| Deployment Size | CPU | RAM | Disk | Notes |
|----------------|-----|-----|------|-------|
| Development | 2 cores | 4 GB | 20 GB | Minimum viable |
| Small (< 10 users) | 4 cores | 8 GB | 50 GB | Comfortable for teams |
| Medium (10-100 users) | 8 cores | 16 GB | 100 GB | Separate DB recommended |
| Large (100+ users) | 16+ cores | 32+ GB | 500+ GB | Dedicated DB, Redis, VDB |

### Software Dependencies

| Component | Required Version | Notes |
|-----------|-----------------|-------|
| Docker Engine | >= 19.03 | Docker Compose >= 2.x |
| Python | 3.12+ | Managed via `uv` |
| Node.js | 22.22.1 | Strict version (enforced by `engines` field) |
| pnpm | 10.33.0 | Enforced by `packageManager` field |
| PostgreSQL | 15+ | WAL mode, foreign keys enabled |
| Redis | 6+ | Used for cache, broker, sessions, pubsub |

---

## Installation and Deployment

### Docker Compose (Production)

The `docker/` directory contains the complete production deployment configuration.

#### Container Orchestration

| Container | Image | Role | Ports |
|-----------|-------|------|-------|
| `nginx` | nginx:latest | Reverse proxy, static files, SSL | 80, 443 |
| `web` | Custom (Next.js) | Frontend application | 3000 (internal) |
| `api` | Custom (Flask) | Backend API server | 5001 (internal) |
| `worker` | Custom (Celery) | Background task processor | -- |
| `db` | postgres:15 | Relational database | 5432 (internal) |
| `redis` | redis:7 | Cache, message broker | 6379 (internal) |
| `weaviate` | semitechnologies/weaviate | Default vector store | 8080, 50051 (internal) |
| `plugin_daemon` | Custom (Go) | Plugin runtime | 5002 (internal) |
| `sandbox` | Custom | Code execution sandbox | 8194 (internal) |
| `ssrf_proxy` | Custom (Squid) | Outbound request filtering | 3128 (internal) |
| `certbot` | certbot/certbot | SSL certificate automation | -- |

#### Deployment Steps

```bash
cd dify/docker
cp .env.example .env
# Edit .env with your production values
docker compose up -d

# Verify all containers are healthy
docker compose ps
docker compose logs --tail 50 api
```

#### nginx Configuration

The bundled nginx config (`docker/nginx/`) handles:
- SSL termination (with Certbot-managed Let's Encrypt certificates)
- WebSocket upgrade for real-time streaming
- Static file serving for the frontend
- Proxy pass to `api:5001` for API requests
- Proxy pass to `web:3000` for frontend requests

Key nginx directives for Dify:
```nginx
# Required for SSE streaming
proxy_buffering off;

# Required for WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```

### Source Deployment

#### Backend Setup

```bash
cd api
pip install uv
uv sync --all-extras

# Configure
cp .env.example .env
# Edit .env

# Database initialization
uv run flask upgrade-db

# Start API server
uv run gunicorn --bind 0.0.0.0:5001 \
  --worker-class gevent \
  --workers 1 \
  --timeout 200 \
  app:app

# Start Celery worker (separate terminal)
uv run celery -A app.celery worker \
  -Q dataset,generation,mail,ops_trace \
  --loglevel INFO
```

#### Frontend Setup

```bash
cd web
pnpm install
pnpm build
pnpm start
```

Development mode with hot reload:
```bash
pnpm dev
# With debugging proxy:
pnpm dev:proxy
```

#### Monorepo Structure

The repository is a pnpm monorepo:

```
dify/
  package.json          # Root workspace config (pnpm 10.33.0, Node 22.22.1)
  web/                  # Main frontend (Next.js 15, version 1.13.3)
  e2e/                  # End-to-end tests (Playwright + Cucumber)
  packages/
    dify-ui/            # Shared UI components (@langgenius/dify-ui)
    iconify-collections/ # Custom icon sets
    tsconfig/           # Shared TypeScript configs
    migrate-no-unchecked-indexed-access/  # TS migration tool
  sdks/
    nodejs-client/      # Node.js SDK (dify-client v3.1.0)
    php-client/         # PHP SDK (Guzzle-based)
```

### SSRF Proxy

The `ssrf_proxy` container runs Squid configured to block requests to internal/private network ranges. All outbound HTTP requests from workflow HTTP Request nodes and tool calls are routed through this proxy to prevent Server-Side Request Forgery attacks.

Configuration:
```
SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128
SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128
SSRF_DEFAULT_MAX_RETRIES=3
SSRF_DEFAULT_TIME_OUT=5
SSRF_DEFAULT_CONNECT_TIME_OUT=5
SSRF_DEFAULT_READ_TIME_OUT=5
SSRF_DEFAULT_WRITE_TIME_OUT=5
```

### Sandbox (Code Execution)

The sandbox container provides isolated execution for Code nodes in workflows. It runs user-submitted Python and JavaScript in a restricted environment with resource limits.

```
CODE_EXECUTION_ENDPOINT=http://127.0.0.1:8194
CODE_EXECUTION_API_KEY=dify-sandbox
CODE_MAX_NUMBER=9223372036854775807
CODE_MIN_NUMBER=-9223372036854775808
CODE_MAX_STRING_LENGTH=80000
CODE_MAX_STRING_ARRAY_LENGTH=30
CODE_MAX_OBJECT_ARRAY_LENGTH=30
CODE_MAX_NUMBER_ARRAY_LENGTH=1000
TEMPLATE_TRANSFORM_MAX_LENGTH=80000
```

The sandbox config lives at `docker/volumes/sandbox/conf/`.

---

## Configuration Reference

All configuration is via environment variables, defined in `.env`. The authoritative list is in `docker/.env.example`. Below is the complete reference organized by category.

### Core Application

| Variable | Default | Description |
|----------|---------|-------------|
| `SECRET_KEY` | -- | Flask secret key. Required. Generate with `openssl rand -base64 42` |
| `DEBUG` | `false` | Enable Flask debug mode |
| `SQLALCHEMY_ECHO` | `false` | Echo all SQL queries to log |
| `LOG_LEVEL` | `INFO` | Logging level (DEBUG, INFO, WARNING, ERROR) |
| `LOG_FILE` | -- | Log file path. Empty = stdout only |
| `LOG_FILE_MAX_SIZE` | `20` | Max log file size in MB |
| `LOG_FILE_BACKUP_COUNT` | `5` | Number of rotated log files to keep |
| `LOG_DATEFORMAT` | `%Y-%m-%d %H:%M:%S` | Log timestamp format |
| `LOG_TZ` | `UTC` | Log timezone |
| `LOG_FORMAT` | `%(asctime)s...` | Python logging format string |
| `INIT_PASSWORD` | -- | Required password for initial `/install` setup |

### URLs

| Variable | Default | Description |
|----------|---------|-------------|
| `CONSOLE_API_URL` | `http://127.0.0.1:5001` | Console API base URL |
| `CONSOLE_WEB_URL` | `http://127.0.0.1:3000` | Console web base URL |
| `SERVICE_API_URL` | `http://127.0.0.1:5001` | Service API base URL |
| `APP_WEB_URL` | `http://127.0.0.1:3000` | Web app base URL |
| `FILES_URL` | `http://127.0.0.1:5001` | File serving base URL |
| `FILES_ACCESS_TIMEOUT` | `300` | File URL signature validity (seconds) |

### Database (PostgreSQL)

| Variable | Default | Description |
|----------|---------|-------------|
| `DB_USERNAME` | `postgres` | Database user |
| `DB_PASSWORD` | `difyai123456` | Database password |
| `DB_HOST` | `localhost` | Database host |
| `DB_PORT` | `5432` | Database port |
| `DB_DATABASE` | `dify` | Database name |

> [!CAUTION]
> Change `DB_PASSWORD` and `REDIS_PASSWORD` from their defaults before exposing Dify to any network. The defaults (`difyai123456`) are for local development only.

### Redis

| Variable | Default | Description |
|----------|---------|-------------|
| `REDIS_HOST` | `localhost` | Redis host |
| `REDIS_PORT` | `6379` | Redis port |
| `REDIS_USERNAME` | -- | Redis username (ACL) |
| `REDIS_PASSWORD` | `difyai123456` | Redis password |
| `REDIS_USE_SSL` | `false` | Enable TLS for Redis |
| `REDIS_DB` | `0` | Redis database number |
| `REDIS_KEY_PREFIX` | -- | Prefix for all Redis keys |
| `CELERY_BROKER_URL` | `redis://:difyai123456@localhost:6379/1` | Celery broker URL |

### Storage

| Variable | Default | Description |
|----------|---------|-------------|
| `STORAGE_TYPE` | `opendal` | Storage backend: opendal, s3, aliyun-oss, azure-blob, baidu-obs, google-storage, huawei-obs, oci-storage, tencent-cos, volcengine-tos, supabase |
| `OPENDAL_SCHEME` | `fs` | OpenDAL scheme (fs, s3, gcs, etc.) |
| `OPENDAL_FS_ROOT` | `storage` | Local filesystem root for OpenDAL |

### Vector Store

| Variable | Default | Description |
|----------|---------|-------------|
| `VECTOR_STORE` | `weaviate` | Vector backend identifier |
| `WEAVIATE_ENDPOINT` | `http://localhost:8080` | Weaviate HTTP endpoint |
| `WEAVIATE_API_KEY` | -- | Weaviate API key |
| `WEAVIATE_BATCH_SIZE` | `100` | Weaviate batch operation size |
| `WEAVIATE_TOKENIZATION` | `word` | Weaviate text tokenization mode |
| `KEYWORD_DATA_SOURCE_TYPE` | `database` | Keyword search data source |

### Authentication and Sessions

| Variable | Default | Description |
|----------|---------|-------------|
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `60` | JWT access token lifetime |
| `REFRESH_TOKEN_EXPIRE_DAYS` | `30` | JWT refresh token lifetime |
| `LOGIN_LOCKOUT_DURATION` | `86400` | Account lockout after failed attempts (seconds) |
| `RESET_PASSWORD_TOKEN_EXPIRY_MINUTES` | `5` | Password reset link lifetime |
| `EMAIL_REGISTER_TOKEN_EXPIRY_MINUTES` | `5` | Email registration token lifetime |
| `CHANGE_EMAIL_TOKEN_EXPIRY_MINUTES` | `5` | Email change token lifetime |
| `OWNER_TRANSFER_TOKEN_EXPIRY_MINUTES` | `5` | Ownership transfer token lifetime |

### CORS

| Variable | Default | Description |
|----------|---------|-------------|
| `WEB_API_CORS_ALLOW_ORIGINS` | `http://127.0.0.1:3000,*` | Allowed origins for Web API |
| `CONSOLE_CORS_ALLOW_ORIGINS` | `http://127.0.0.1:3000,*` | Allowed origins for Console API |

### Mail

| Variable | Default | Description |
|----------|---------|-------------|
| `MAIL_TYPE` | -- | Mail provider: `resend` or `smtp` |
| `MAIL_DEFAULT_SEND_FROM` | `no-reply <[email removed]>` | Default sender address |
| `RESEND_API_KEY` | -- | Resend API key |
| `RESEND_API_URL` | `https://api.resend.com` | Resend API endpoint |
| `SMTP_SERVER` | `smtp.example.com` | SMTP server hostname |
| `SMTP_PORT` | `465` | SMTP port |
| `SMTP_USERNAME` | -- | SMTP username |
| `SMTP_PASSWORD` | -- | SMTP password |
| `SMTP_USE_TLS` | `true` | Enable TLS for SMTP |
| `SMTP_OPPORTUNISTIC_TLS` | `false` | Opportunistic TLS (STARTTLS) |
| `SMTP_LOCAL_HOSTNAME` | -- | Override local hostname for HELO/EHLO |

### Notion Integration

| Variable | Default | Description |
|----------|---------|-------------|
| `NOTION_INTEGRATION_TYPE` | `public` | `public` (OAuth) or `internal` (direct token) |
| `NOTION_CLIENT_SECRET` | -- | OAuth client secret |
| `NOTION_CLIENT_ID` | -- | OAuth client ID |
| `NOTION_INTERNAL_SECRET` | -- | Internal integration token |

### Upload Limits

| Variable | Default | Description |
|----------|---------|-------------|
| `UPLOAD_FILE_SIZE_LIMIT` | `15` | Max file upload size (MB) |
| `UPLOAD_FILE_BATCH_LIMIT` | `5` | Max simultaneous file uploads |
| `UPLOAD_IMAGE_FILE_SIZE_LIMIT` | `10` | Max image upload size (MB) |
| `UPLOAD_VIDEO_FILE_SIZE_LIMIT` | `100` | Max video upload size (MB) |
| `UPLOAD_AUDIO_FILE_SIZE_LIMIT` | `50` | Max audio upload size (MB) |
| `BATCH_UPLOAD_LIMIT` | `10` | Batch upload limit |

### Workflow Execution

| Variable | Default | Description |
|----------|---------|-------------|
| `WORKFLOW_MAX_EXECUTION_STEPS` | `500` | Max nodes per workflow run |
| `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | Max execution time (seconds) |
| `WORKFLOW_CALL_MAX_DEPTH` | `5` | Max sub-workflow nesting depth |
| `MAX_VARIABLE_SIZE` | `204800` | Max variable size (bytes, 200 KB) |
| `MAX_SUBMIT_COUNT` | `100` | Max parallel thread pool submissions |
| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | Max file uploads per workflow run |

### Application Limits

| Variable | Default | Description |
|----------|---------|-------------|
| `APP_MAX_EXECUTION_TIME` | `1200` | Max app execution time (seconds) |
| `APP_DEFAULT_ACTIVE_REQUESTS` | `0` | Default concurrent request limit (0 = unlimited) |
| `APP_MAX_ACTIVE_REQUESTS` | `0` | Max concurrent request limit (0 = unlimited) |
| `CELERY_BEAT_SCHEDULER_TIME` | `1` | Celery beat polling interval (seconds) |

### Code Execution (Sandbox)

| Variable | Default | Description |
|----------|---------|-------------|
| `CODE_EXECUTION_ENDPOINT` | `http://127.0.0.1:8194` | Sandbox endpoint |
| `CODE_EXECUTION_API_KEY` | `dify-sandbox` | Sandbox API key |
| `CODE_MAX_NUMBER` | `9223372036854775807` | Max numeric value |
| `CODE_MIN_NUMBER` | `-9223372036854775808` | Min numeric value |
| `CODE_MAX_STRING_LENGTH` | `80000` | Max string length |
| `CODE_MAX_STRING_ARRAY_LENGTH` | `30` | Max string array length |
| `CODE_MAX_OBJECT_ARRAY_LENGTH` | `30` | Max object array length |
| `CODE_MAX_NUMBER_ARRAY_LENGTH` | `1000` | Max number array length |
| `TEMPLATE_TRANSFORM_MAX_LENGTH` | `80000` | Max Jinja2 template length |

### HTTP Request Node

| Variable | Default | Description |
|----------|---------|-------------|
| `HTTP_REQUEST_MAX_CONNECT_TIMEOUT` | `300` | Max connect timeout (seconds) |
| `HTTP_REQUEST_MAX_READ_TIMEOUT` | `600` | Max read timeout (seconds) |
| `HTTP_REQUEST_MAX_WRITE_TIMEOUT` | `600` | Max write timeout (seconds) |
| `HTTP_REQUEST_NODE_MAX_BINARY_SIZE` | `10485760` | Max binary response (10 MB) |
| `HTTP_REQUEST_NODE_MAX_TEXT_SIZE` | `1048576` | Max text response (1 MB) |

### API Tool

| Variable | Default | Description |
|----------|---------|-------------|
| `API_TOOL_DEFAULT_CONNECT_TIMEOUT` | `10` | Default connect timeout (seconds) |
| `API_TOOL_DEFAULT_READ_TIMEOUT` | `60` | Default read timeout (seconds) |

### Webhook

| Variable | Default | Description |
|----------|---------|-------------|
| `WEBHOOK_REQUEST_BODY_MAX_SIZE` | `10485760` | Max webhook body size (10 MB) |

### Plugin System

| Variable | Default | Description |
|----------|---------|-------------|
| `PLUGIN_DAEMON_KEY` | -- | Plugin daemon authentication key |
| `PLUGIN_DAEMON_URL` | `http://127.0.0.1:5002` | Plugin daemon endpoint |
| `PLUGIN_REMOTE_INSTALL_PORT` | `5003` | Remote plugin install port |
| `PLUGIN_REMOTE_INSTALL_HOST` | `localhost` | Remote plugin install host |
| `PLUGIN_MAX_PACKAGE_SIZE` | `15728640` | Max plugin package size (15 MB) |
| `INNER_API_KEY_FOR_PLUGIN` | -- | Shared secret for Inner API auth |

### Marketplace

| Variable | Default | Description |
|----------|---------|-------------|
| `MARKETPLACE_ENABLED` | `true` | Enable plugin marketplace |
| `MARKETPLACE_API_URL` | `https://marketplace.dify.ai` | Marketplace API endpoint |

### Endpoint Extensions

| Variable | Default | Description |
|----------|---------|-------------|
| `ENDPOINT_URL_TEMPLATE` | `http://localhost:5002/e/{hook_id}` | Plugin endpoint URL template |

### SSRF Protection

| Variable | Default | Description |
|----------|---------|-------------|
| `SSRF_PROXY_HTTP_URL` | -- | HTTP proxy for outbound requests |
| `SSRF_PROXY_HTTPS_URL` | -- | HTTPS proxy for outbound requests |
| `SSRF_DEFAULT_MAX_RETRIES` | `3` | Max retries for proxied requests |
| `SSRF_DEFAULT_TIME_OUT` | `5` | Total timeout (seconds) |
| `SSRF_DEFAULT_CONNECT_TIME_OUT` | `5` | Connect timeout (seconds) |
| `SSRF_DEFAULT_READ_TIME_OUT` | `5` | Read timeout (seconds) |
| `SSRF_DEFAULT_WRITE_TIME_OUT` | `5` | Write timeout (seconds) |

### Model Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `MULTIMODAL_SEND_FORMAT` | `base64` | Image format for multimodal models (`base64` or `url`) |
| `PROMPT_GENERATION_MAX_TOKENS` | `4096` | Max tokens for prompt generation |
| `CODE_GENERATION_MAX_TOKENS` | `1024` | Max tokens for code generation |

### Indexing

| Variable | Default | Description |
|----------|---------|-------------|
| `INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH` | `4000` | Max tokens per chunk |
| `ETL_TYPE` | `dify` | Document parser: `dify` or `Unstructured` |
| `UNSTRUCTURED_API_URL` | -- | Unstructured.io API URL |
| `UNSTRUCTURED_API_KEY` | -- | Unstructured.io API key |

### Proxy

| Variable | Default | Description |
|----------|---------|-------------|
| `HTTP_PROXY` | -- | HTTP proxy for outbound connections |
| `HTTPS_PROXY` | -- | HTTPS proxy for outbound connections |
| `NO_PROXY` | `localhost,127.0.0.1` | Proxy bypass list |

### Position Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `POSITION_TOOL_PINS` | -- | Pin tools to top of list |
| `POSITION_TOOL_INCLUDES` | -- | Tool whitelist |
| `POSITION_TOOL_EXCLUDES` | -- | Tool blacklist |
| `POSITION_PROVIDER_PINS` | -- | Pin providers to top |
| `POSITION_PROVIDER_INCLUDES` | -- | Provider whitelist |
| `POSITION_PROVIDER_EXCLUDES` | -- | Provider blacklist |

### Billing (Enterprise)

| Variable | Default | Description |
|----------|---------|-------------|
| `BILLING_API_URL` | -- | Billing service URL |
| `BILLING_API_SECRET_KEY` | -- | Billing service secret |

### Sentry

| Variable | Default | Description |
|----------|---------|-------------|
| `SENTRY_DSN` | -- | Sentry DSN for error tracking |

### Telemetry

| Variable | Default | Description |
|----------|---------|-------------|
| `SCARF_NO_ANALYTICS` | `false` | Disable anonymous install telemetry |

### Misc

| Variable | Default | Description |
|----------|---------|-------------|
| `RESPECT_XFORWARD_HEADERS_ENABLED` | `false` | Trust X-Forwarded-* headers |
| `CREATE_TIDB_SERVICE_JOB_ENABLED` | `false` | Enable TiDB service job creation |

### Frontend Environment Variables

All `NEXT_PUBLIC_*` variables are embedded at build time and available to the client:

| Variable | Description |
|----------|-------------|
| `NEXT_PUBLIC_API_PREFIX` | API URL prefix |
| `NEXT_PUBLIC_PUBLIC_API_PREFIX` | Public API URL prefix |
| `NEXT_PUBLIC_WEB_PREFIX` | Web app URL prefix |
| `NEXT_PUBLIC_DEPLOY_ENV` | Deployment environment |
| `NEXT_PUBLIC_EDITION` | Edition identifier |
| `NEXT_PUBLIC_SOCKET_URL` | WebSocket URL |
| `NEXT_PUBLIC_SENTRY_DSN` | Frontend Sentry DSN |
| `NEXT_PUBLIC_ALLOW_EMBED` | Allow iframe embedding |
| `NEXT_PUBLIC_ALLOW_INLINE_STYLES` | Allow inline styles in embed |
| `NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME` | Allow data: URI scheme |
| `NEXT_PUBLIC_BATCH_CONCURRENCY` | Parallel request limit |
| `NEXT_PUBLIC_COOKIE_DOMAIN` | Cookie domain scope |
| `NEXT_PUBLIC_CSP_WHITELIST` | Content Security Policy whitelist |
| `NEXT_PUBLIC_TOP_K_MAX_VALUE` | Max Top-K retrieval value |
| `NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS` | Generation timeout (ms) |
| `NEXT_PUBLIC_LOOP_NODE_MAX_COUNT` | Max loop iterations |
| `NEXT_PUBLIC_MAX_ITERATIONS_NUM` | Max agent iterations |
| `NEXT_PUBLIC_MAX_TOOLS_NUM` | Max tools per agent turn |
| `NEXT_PUBLIC_MAX_PARALLEL_LIMIT` | Max parallel branches |
| `NEXT_PUBLIC_MAX_TREE_DEPTH` | Max workflow tree depth |
| `NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH` | Max chunk tokens (UI) |
| `NEXT_PUBLIC_MAINTENANCE_NOTICE` | Maintenance banner text |
| `NEXT_PUBLIC_IS_MARKETPLACE` | Marketplace mode flag |
| `NEXT_PUBLIC_MARKETPLACE_API_PREFIX` | Marketplace API URL |
| `NEXT_PUBLIC_MARKETPLACE_URL_PREFIX` | Marketplace URL |
| `NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL` | Enable Firecrawl integration |
| `NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER` | Enable Jina Reader |
| `NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL` | Enable WaterCrawl |
| `NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX` | Enable single-dollar LaTeX |
| `NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON` | Disable image-as-icon upload |
| `NEXT_PUBLIC_AMPLITUDE_API_KEY` | Amplitude analytics key |
| `NEXT_PUBLIC_ZENDESK_WIDGET_KEY` | Zendesk chat widget key |

---

## Security

### Authentication Architecture

Dify uses JWT-based authentication with access/refresh token pairs:

1. **Login** -- User authenticates with email/password or OAuth (Google)
2. **Access Token** -- Short-lived JWT (`ACCESS_TOKEN_EXPIRE_MINUTES`, default 60 min)
3. **Refresh Token** -- Long-lived token (`REFRESH_TOKEN_EXPIRE_DAYS`, default 30 days)
4. **API Keys** -- Per-app keys for Service API access (Bearer token)

#### Brute-Force Protection

Failed login attempts trigger account lockout after repeated failures. The lockout duration is controlled by `LOGIN_LOCKOUT_DURATION` (default: 86400 seconds / 24 hours).

#### OAuth Integration

Google OAuth is supported natively. Configure via `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in `.env`.

### Encryption

#### LLM Credential Storage

LLM API keys and model credentials are encrypted at rest using workspace-specific asymmetric key pairs. Each workspace generates a unique key pair on creation.

> [!CAUTION]
> Running `flask reset-encrypt-key-pair` regenerates the workspace encryption keys. **All stored LLM credentials become invalid** and must be re-entered. Only use this command when the key pair is compromised.

#### Secret Key

The `SECRET_KEY` is used for:
- Flask session signing
- JWT token signing
- CSRF protection

Generate a strong key: `openssl rand -base64 42`

### API Key Security

- Service API keys are scoped to individual apps
- Keys are hashed before storage; the plaintext is shown only once on creation
- Rotate keys regularly; old keys can be revoked from the app settings

### SSRF Protection

The `ssrf_proxy` container blocks outbound requests to private network ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, etc.) to prevent Server-Side Request Forgery attacks from user-controlled workflow nodes.

### Content Security

#### Code Sandbox Isolation

User-submitted code in workflow Code nodes runs in the `sandbox` container with:
- Process isolation from the main application
- Resource limits (CPU, memory, execution time)
- No network access (unless explicitly configured)
- No filesystem access outside the sandbox

#### Content Moderation

Three layers of content moderation are available:
1. **OpenAI Moderation API** -- Automated content classification
2. **Custom Keywords** -- Regex/exact-match blocklist
3. **External API** -- Custom moderation endpoint

### File Access Control

Files are served with signed URLs that expire after `FILES_ACCESS_TIMEOUT` seconds (default: 300). The signature prevents unauthorized access to uploaded files.

### CORS Configuration

Restrict allowed origins in production:
```
WEB_API_CORS_ALLOW_ORIGINS=https://your-domain.com
CONSOLE_CORS_ALLOW_ORIGINS=https://your-domain.com
```

> [!WARNING]
> Never use `*` for CORS origins in production. This allows any website to make API requests on behalf of authenticated users.

### X-Forwarded Headers

Set `RESPECT_XFORWARD_HEADERS_ENABLED=true` only when Dify is behind a trusted reverse proxy. This tells Flask to trust `X-Forwarded-For`, `X-Forwarded-Proto`, and `X-Forwarded-Host` headers for correct URL generation and client IP detection.

---

## Operations

### Health Checks

| Endpoint | Purpose |
|----------|---------|
| `GET /health` | Full health check (DB, Redis connectivity) |
| `GET /ping` | Simple liveness probe |
| `GET /db-pool-stat` | SQLAlchemy connection pool statistics |
| `GET /threads` | Active thread count |

### Backup and Restore

#### PostgreSQL

```bash
# Backup
docker compose exec db pg_dump -U postgres dify > dify_backup_$(date +%Y%m%d).sql

# Restore
docker compose exec -T db psql -U postgres dify < dify_backup_20260513.sql
```

#### Redis

Redis data is ephemeral (cache, sessions) and does not require routine backup. For persistence, configure Redis RDB/AOF snapshots in `redis.conf`.

#### File Storage

Back up the storage directory based on your `STORAGE_TYPE`:
- `opendal` (fs): Back up the directory at `OPENDAL_FS_ROOT`
- S3/cloud: Rely on provider's backup mechanisms

#### Complete Backup Checklist

1. PostgreSQL dump
2. `.env` file
3. Storage directory (uploads, keys)
4. nginx configuration (if customized)
5. Plugin data (if using custom plugins)

### Scaling

#### Horizontal Scaling

- **API servers**: Run multiple `api` containers behind a load balancer. Ensure all share the same `SECRET_KEY`, database, and Redis.
- **Celery workers**: Add worker containers with `--concurrency` flag. All workers share the same Redis broker.
- **Frontend**: The `web` container is stateless; scale freely.

#### Vertical Scaling

- **Database**: Increase PostgreSQL `shared_buffers`, `work_mem`, `max_connections`
- **Redis**: Increase `maxmemory`; configure eviction policy (`allkeys-lru` recommended)
- **Vector store**: Scale according to your backend's documentation

#### Queue Scaling

Celery queues and their purposes:
- `dataset` -- Document processing, embedding, indexing
- `generation` -- LLM generation tasks
- `mail` -- Email delivery
- `ops_trace` -- Operational tracing/logging

Scale selectively:
```bash
# Dedicated worker for dataset processing
celery -A app.celery worker -Q dataset --concurrency 4

# Dedicated worker for generation
celery -A app.celery worker -Q generation --concurrency 2
```

### Log Management

#### API Logs

```bash
# View recent API logs
docker compose logs --tail 200 api

# Follow logs in real time
docker compose logs -f api

# Filter by level
docker compose logs api 2>&1 | grep ERROR
```

#### Log Configuration

```
LOG_LEVEL=INFO
LOG_FILE=/var/log/dify/api.log
LOG_FILE_MAX_SIZE=20
LOG_FILE_BACKUP_COUNT=5
LOG_TZ=UTC
```

#### Aliyun SLS Integration

For cloud-based log management:
```
ALIYUN_SLS_ENDPOINT=<endpoint>
ALIYUN_SLS_REGION=<region>
ALIYUN_SLS_PROJECT_NAME=<project>
ALIYUN_SLS_CHECK_CONNECTIVITY_TIMEOUT=30
```

### Database Migrations

Migrations use Flask-Migrate (Alembic). They run automatically on API container startup.

```bash
# Run migrations manually
docker compose exec api flask upgrade-db

# Check current migration version
docker compose exec api flask db current
```

### Storage Migration

Migrate files between storage backends:

```bash
docker compose exec api flask migrate-oss \
  --source opendal \
  --path upload_files \
  --path image_files \
  --dry-run

# Execute the migration
docker compose exec api flask migrate-oss \
  --source opendal \
  --update-db
```

Supported paths: `privkeys`, `upload_files`, `image_files`, `tools`, `website_files`, `keyword_files`, `ops_trace`.

### Data Retention

#### Workflow Runs

```bash
# Preview cleanup (dry run)
docker compose exec api flask clean-workflow-runs \
  --before-days 30 --dry-run

# Execute cleanup
docker compose exec api flask clean-workflow-runs \
  --before-days 30 --batch-size 200

# Archive to S3 before deleting
docker compose exec api flask archive-workflow-runs \
  --before-days 90 --delete-after-archive --workers 4

# Restore from archive
docker compose exec api flask restore-workflow-runs \
  --tenant-ids "tenant-uuid" --run-id "run-uuid"
```

#### Messages

```bash
docker compose exec api flask clean-expired-messages \
  --before-days 90 --graceful-period 21 --dry-run
```

The `--graceful-period` adds extra days after subscription expiration before cleanup.

#### Export Data

```bash
docker compose exec api flask export-app-messages \
  --app-id "app-uuid" \
  --end-before "2026-01-01" \
  --filename "export/messages" \
  --batch-size 1000

# Upload directly to cloud storage
docker compose exec api flask export-app-messages \
  --app-id "app-uuid" \
  --end-before "2026-01-01" \
  --use-cloud-storage
```

#### Orphaned Resources

```bash
# Find orphaned file records (DB records without storage files)
docker compose exec api flask clear-orphaned-file-records --force

# Find orphaned storage files (storage files without DB records)
docker compose exec api flask remove-orphaned-files-on-storage --force

# Query file usage
docker compose exec api flask file-usage --file-id "uuid" --json
```

#### Draft Variables

```bash
docker compose exec api flask cleanup_orphaned_draft_variables \
  --dry-run --batch-size 1000 --max-apps 100
```

---

## Workflow Engine Internals

### Graph Execution Model

The workflow engine uses a DAG-based graph executor built on the `graphon` package. Key concepts:

1. **Graph Definition** -- Nodes and edges defined in the workflow DSL (JSON/YAML)
2. **Topological Execution** -- Nodes execute in dependency order
3. **Parallel Branches** -- Independent branches run concurrently via a `ThreadPoolExecutor` (max `MAX_SUBMIT_COUNT` = 100 threads)
4. **Variable Aggregation** -- `Variable Aggregator` nodes act as join points, waiting for all upstream branches

### Execution Lifecycle

```
Start Node
  -> Resolve dependencies
  -> Execute node
    -> If parallel paths: submit to thread pool
    -> If conditional: evaluate IF/ELSE
    -> If iteration: loop over items
    -> If sub-workflow: recursive execution (depth <= WORKFLOW_CALL_MAX_DEPTH)
  -> Collect outputs
  -> Route to next node(s)
  -> ... repeat ...
End Node
  -> Assemble final outputs
  -> Persist workflow_run record
```

### Node Execution Details

Each node type has a specific executor:

- **LLM Node** -- Assembles prompt from template + variables, calls model, streams response
- **Knowledge Retrieval Node** -- Queries vector store, optionally re-ranks, returns chunks
- **Code Node** -- Sends code to sandbox via HTTP, receives output
- **HTTP Request Node** -- Makes HTTP call through SSRF proxy, parses response
- **Sub-Workflow Node** -- Creates a child workflow run, shares `trace_id` for observability

### Human-in-the-Loop

Workflows support human input nodes that pause execution and wait for user approval/input. The resume mechanism:
1. Workflow reaches human input node
2. Execution state is persisted to PostgreSQL
3. API returns a `task_id` for the paused run
4. User submits input via API
5. Workflow resumes from the paused node

### Draft/Preview Mode

Single-node execution for debugging:
- Creates its own `trace_id` derived from `node_execution_id`
- Does not create a workflow run record
- Useful for testing LLM prompts, code, and HTTP requests in isolation

---

## RAG Pipeline Internals

### Document Processing Pipeline

```
Upload -> Extractor -> Cleaner -> Chunker -> Embedder -> Vector Store
```

#### Extractors

| Format | Extractor | Notes |
|--------|-----------|-------|
| PDF | Built-in or Unstructured.io | `ETL_TYPE` controls which |
| DOCX | python-docx | |
| TXT/MD | Direct read | |
| HTML | BeautifulSoup | Strips tags, preserves structure |
| CSV/XLSX | pandas | Row-per-chunk option |
| EPUB | epub parser | |

#### Chunking Strategies

1. **Automatic** -- Heuristic-based splitting by paragraphs and sections
2. **Custom** -- User-defined separator (e.g., `\n\n`) + max chunk size + overlap
3. **Parent-Child** -- Hierarchical chunking for context preservation

Key parameter: `INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH` (default: 4000) sets the maximum tokens per chunk.

#### Embedding Pipeline

1. Text chunks are batched and sent to the configured embedding model
2. Vectors are stored in the configured vector backend
3. Full-text indexes are maintained in parallel (for keyword/hybrid search)
4. The `dataset` Celery queue handles all processing asynchronously

### Retrieval Pipeline

```
Query -> Query Embedding -> Vector Search (Top-K)
              |
              +-> Full-Text Search (BM25)
              |
              +-> Hybrid (merge + deduplicate)
                    |
                    +-> Reranker (optional)
                          |
                          +-> Score Threshold Filter
                                |
                                +-> Context Assembly -> LLM
```

### Vector Store Registration System

Vector backends are registered via Python entry points under the `dify.vector_backends` group. The discovery mechanism:

1. Backend packages live in `api/providers/vdb/<backend>/`
2. Each has a `pyproject.toml` with entry point registration
3. `vector_backend_registry.py` lazily loads and caches factory classes
4. At runtime, `get_vector_factory_class(vector_type)` resolves the backend

See [Vector Database Backend System](#vector-database-backend-system) for details.

---

## Plugin System Internals

### Architecture

```
Flask API
  |
  v
Inner API (/inner/api/*)
  |  (INNER_API_KEY_FOR_PLUGIN auth)
  v
plugin_daemon (Go binary, :5002)
  |
  +-> Plugin Process 1 (stdio JSON-RPC)
  +-> Plugin Process 2 (stdio JSON-RPC)
  +-> ...
```

### Plugin Types

| Type | Purpose | Registration |
|------|---------|-------------|
| **Model Provider** | Add LLM/embedding/reranker backends | Appears in Model Providers settings |
| **Tool** | Callable actions for agents/workflows | Appears in Tools panel |
| **Extension** | Custom endpoints, triggers | Endpoint-based |

### Plugin Package Format

Plugins are distributed as `.difypkg` files containing:
- Manifest (plugin metadata, version, dependencies)
- Implementation code
- Configuration schema

### Plugin Communication

The `plugin_daemon` manages plugin lifecycle:
1. Plugin installation (download, extract, validate)
2. Plugin startup (spawn process, establish stdio JSON-RPC channel)
3. Request routing (API -> Inner API -> daemon -> plugin process)
4. Plugin shutdown and cleanup

### Plugin CLI Commands

```bash
# Migrate existing data for plugin system
flask migrate-data-for-plugin

# Extract plugin definitions from existing configuration
flask extract-plugins --output_file plugins.jsonl --workers 10

# Bulk install plugins
flask install-plugins --input_file plugins.jsonl --output_file installed_plugins.jsonl --workers 100

# Install RAG pipeline plugins
flask install-rag-pipeline-plugins --workers 100

# Configure OAuth for tool plugins
flask setup-system-tool-oauth-client --provider github --client-params '{"client_id":"...","client_secret":"..."}'

# Configure OAuth for trigger plugins
flask setup-system-trigger-oauth-client --provider github --client-params '...'

# Configure OAuth for datasource plugins
flask setup-datasource-oauth-client --provider notion --client-params '...'

# Transform datasource credentials
flask transform-datasource-credentials --environment online
```

---

## Vector Database Backend System

### Architecture

The vector store system uses a plugin architecture based on Python entry points:

```
api/core/rag/datasource/vdb/
  vector_factory.py         # Main entry: Vector class
  vector_backend_registry.py # Entry point discovery + caching
  abstract_vector_factory.py # AbstractVectorFactory interface
  base_vector.py            # BaseVector interface

api/providers/vdb/
  <backend>/
    pyproject.toml          # Package + entry point registration
    src/dify_vdb_<backend>/
      <backend>_vector.py   # Implementation
```

### Interfaces

| Interface | Methods | Purpose |
|-----------|---------|---------|
| `AbstractVectorFactory` | `init_vector(dataset, attributes, embeddings)` | Create a `BaseVector` instance |
| `BaseVector` | `create`, `add_texts`, `search_by_vector`, `delete`, `delete_by_ids` | Vector operations |

### Complete Backend List

| Backend | Entry Point | Package | Key Dependency |
|---------|------------|---------|----------------|
| Weaviate | `weaviate` | `dify-vdb-weaviate` | `weaviate-client==4.20.5` |
| Qdrant | `qdrant` | `dify-vdb-qdrant` | `qdrant-client==1.9.0` |
| pgvector | `pgvector` | `dify-vdb-pgvector` | `pgvector==0.4.2` |
| pgvecto-rs | `pgvecto-rs` | `dify-vdb-pgvecto-rs` | `pgvecto-rs[sqlalchemy]~=0.2.2` |
| Milvus | `milvus` | `dify-vdb-milvus` | `pymilvus~=2.6.12` |
| Chroma | `chroma` | `dify-vdb-chroma` | `chromadb==0.5.20` |
| Elasticsearch | `elasticsearch` | `dify-vdb-elasticsearch` | `elasticsearch==8.14.0` |
| Elasticsearch (JP) | `elasticsearch-ja` | `dify-vdb-elasticsearch` | `elasticsearch==8.14.0` |
| OpenSearch | `opensearch` | `dify-vdb-opensearch` | `opensearch-py==3.1.0` |
| TiDB Vector | `tidb_vector` | `dify-vdb-tidb-vector` | `tidb-vector==0.0.15` |
| TiDB on Qdrant | `tidb_on_qdrant` | `dify-vdb-tidb-on-qdrant` | `qdrant-client==1.9.0` |
| Oracle | `oracle` | `dify-vdb-oracle` | `oracledb==3.4.2` |
| OceanBase | `oceanbase` | `dify-vdb-oceanbase` | `pyobvector~=0.2.17` |
| SeekDB | `seekdb` | `dify-vdb-oceanbase` | `pyobvector~=0.2.17` |
| Couchbase | `couchbase` | `dify-vdb-couchbase` | `couchbase~=4.6.0` |
| Tencent | `tencent` | `dify-vdb-tencent` | `tcvectordb~=2.1.0` |
| Baidu | `baidu` | `dify-vdb-baidu` | `pymochow==2.4.0` |
| Hologres | `hologres` | `dify-vdb-hologres` | `holo-search-sdk>=0.4.2` |
| AnalyticDB | `analyticdb` | `dify-vdb-analyticdb` | `alibabacloud_gpdb20160503~=5.2.0` |
| Alibaba Cloud MySQL | `alibabacloud_mysql` | `dify-vdb-alibabacloud-mysql` | `mysql-connector-python>=9.3.0` |
| Lindorm | `lindorm` | `dify-vdb-lindorm` | `opensearch-py==3.1.0` |
| Upstash | `upstash` | `dify-vdb-upstash` | `upstash-vector==0.8.0` |
| VikingDB | `vikingdb` | `dify-vdb-vikingdb` | `volcengine-compat~=1.0.0` |
| MyScale | `myscale` | `dify-vdb-myscale` | `clickhouse-connect~=0.15.0` |
| Tablestore | `tablestore` | `dify-vdb-tablestore` | `tablestore==6.4.4` |
| Huawei Cloud | `huawei_cloud` | `dify-vdb-huawei-cloud` | `elasticsearch==8.14.0` |
| IRIS | `iris` | `dify-vdb-iris` | `intersystems-irispython>=5.1.0` |
| MatrixOne | `matrixone` | `dify-vdb-matrixone` | `mo-vector~=0.1.13` |
| Clickzetta | `clickzetta` | `dify-vdb-clickzetta` | `clickzetta-connector-python>=0.8.102` |
| Relyt | `relyt` | `dify-vdb-relyt` | -- |
| OpenGauss | `opengauss` | `dify-vdb-opengauss` | -- |
| Vastbase | `vastbase` | `dify-vdb-vastbase` | `pyobvector~=0.2.17` |

### Adding a New Backend

1. Create `api/providers/vdb/<your_backend>/`
2. Add `pyproject.toml`:
   ```toml
   [project]
   name = "dify-vdb-your-backend"
   version = "0.0.1"
   dependencies = ["your-client-lib>=1.0"]
   
   [project.entry-points."dify.vector_backends"]
   your_backend = "dify_vdb_your_backend.your_backend_vector:YourBackendFactory"
   
   [tool.setuptools.packages.find]
   where = ["src"]
   ```
3. Implement `AbstractVectorFactory` and `BaseVector`
4. Add to `api/pyproject.toml`:
   - `[tool.uv.sources]`: `dify-vdb-your-backend = { workspace = true }`
   - `[project.optional-dependencies]`: add to `vdb-all` group
5. Run `uv sync` in `api/`

### Vector Migration

```bash
flask vdb-migrate --scope all
```

Migrates all datasets from the current vector backend to the one configured in `VECTOR_STORE`.

---

## Frontend Architecture

### Technology Stack

| Technology | Version | Purpose |
|-----------|---------|---------|
| Next.js | 15 | App Router framework |
| React | 19 | UI library |
| TypeScript | Latest | Type safety |
| Tailwind CSS | 4 | Utility-first styling |
| Zustand | Latest | Client state management |
| TanStack Query | Latest | Server state management |
| Jotai | Latest | Atomic state management |
| ReactFlow | Latest | Workflow canvas |
| socket.io-client | Latest | Real-time WebSocket communication |
| i18next | Latest | Internationalization (20+ languages) |

### Project Structure

```
web/
  app/                    # Next.js App Router pages
    (commonLayout)/       # Authenticated layout group
      datasets/           # Knowledge base UI
      apps/               # Application management
    signin/               # Authentication pages
    components/           # Shared components
      workflow/           # Workflow editor (ReactFlow)
      datasets/           # Dataset management
      plugins/            # Plugin management
      billing/            # Billing (enterprise)
  config/                 # App configuration
  hooks/                  # Custom React hooks (26 files)
  service/                # API service layer (55 files)
  models/                 # TypeScript type definitions (10 files)
  utils/                  # Utility functions (52 files)
  i18n/                   # Translations (20+ locales)
  i18n-config/            # i18n configuration
  contract/               # API contracts / types
  constants/              # App constants
  public/                 # Static assets
    vs/                   # Monaco editor (bundled)
  docker/                 # Frontend Dockerfile
```

### Build and Development

```bash
# Development
pnpm dev                  # Next.js dev server
pnpm dev:proxy            # Dev with Hono proxy
pnpm dev:vinext           # Vite-based dev (experimental)

# Build
pnpm build                # Next.js production build
pnpm build:vinext         # Vite-based build (experimental)

# Quality
pnpm type-check           # TypeScript checking
pnpm lint                 # ESLint
pnpm test                 # Vitest tests
pnpm test:coverage        # Test coverage
pnpm storybook            # Component development

# Utilities
pnpm gen-icons            # Regenerate icon components
pnpm i18n:check           # Validate translations
pnpm knip                 # Find unused code
pnpm analyze              # Bundle analysis
```

### Internationalization

Translations live in `web/i18n/<locale>/` (30 files per locale). Supported locales:
en-US, zh-Hans, zh-Hant, ja-JP, ko-KR, de-DE, fr-FR, it-IT, es-ES, pt-BR, ru-RU, tr-TR, vi-VN, th-TH, ar-TN, hi-IN, pl-PL, ro-RO, sl-SI, uk-UA, fa-IR.

---

## Backend Architecture

### Module Organization

```
api/
  app.py                  # Entry point (Flask + gevent WebSocket)
  app_factory.py          # Flask app factory (gevent monkey-patch)
  commands/               # Flask CLI commands (7 files, 129 commands)
    account.py            # reset-password, reset-email, create-tenant
    plugin.py             # Plugin migration and OAuth setup
    retention.py          # Data cleanup and archival
    storage.py            # File management and migration
    system.py             # DB upgrades, encryption, app conversion
    vector.py             # Vector DB migration
  configs/                # Pydantic v2 configuration
    middleware/            # Redis, PostgreSQL, storage, etc.
  constants/              # Application constants (6 files, 350KB)
  context/                # Request context management (4 files)
  controllers/            # API route handlers
    console/              # Console API (16 files + app/ 25 files + auth/ 9 files)
    service_api/          # Service API (3 files + app/ 11 files)
    web/                  # Web API (19 files)
    inner_api/            # Inner API (app/ 2 files + plugin/ 2 files)
    common/               # Shared controller utilities (6 files)
  core/                   # Core business logic
    app/apps/             # Application runtime (pipeline, common)
    moderation/           # Content moderation
    external_data_tool/   # External data integration
    tools/                # Tool system (12 util files)
    trigger/              # Trigger system
    plugin/               # Plugin integration (3 util files)
    rag/datasource/vdb/   # Vector store interfaces
    prompt/               # Prompt utilities (4 files)
    schemas/              # Core schemas (4 files)
    errors/               # Error definitions
    datasource/           # Data source utilities
  enums/                  # Enum definitions (3 files)
  extensions/             # Flask extensions
    otel/                 # OpenTelemetry integration
    ext_logstore.py       # Aliyun SLS log integration
  fields/                 # API response field definitions (26 files)
  libs/                   # Utility libraries (35 files)
  migrations/             # Alembic database migrations
  models/                 # SQLAlchemy ORM models (22 files, 395KB)
  repositories/           # Data access layer (11 files)
  schedule/               # Celery beat scheduled tasks (13 files)
  services/               # Business logic services (57 files, 1MB)
    auth/                 # Authentication services (5 files)
    plugin/               # Plugin services (9 files)
  providers/vdb/          # Vector database backends (30+ packages)
```

### Key Modules by Size

| Module | Files | Size | Purpose |
|--------|-------|------|---------|
| `api/services` | 57 | 1 MB | Core business logic services |
| `api/models` | 22 | 395 KB | SQLAlchemy ORM models |
| `api/constants` | 6 | 350 KB | Application constants |
| `api/controllers/console/app` | 25 | 329 KB | Console API handlers |
| `api/libs` | 35 | 156 KB | Utility libraries |
| `api/commands` | 7 | 123 KB | CLI commands |
| `api/repositories` | 11 | 124 KB | Data access layer |
| `api/controllers/web` | 19 | 110 KB | Web API handlers |
| `api/controllers/console` | 16 | 92 KB | Console API (non-app) |
| `api/controllers/service_api/app` | 11 | 81 KB | Service API handlers |
| `api/fields` | 26 | 80 KB | Response field definitions |

### Configuration System

Dify uses Pydantic v2 settings classes for configuration (`api/configs/`). The middleware config (`api/configs/middleware/`) defines typed settings for:
- PostgreSQL connection
- Redis connection
- Storage backends
- Vector store selection
- Celery configuration

All settings are loaded from environment variables at startup.

### Error Handling

The codebase defines specific error classes for different domains:

- **Auth errors**: `ApiKeyAuthFailedError`, `InvalidEmailError`, `PasswordMismatchError`, `AccountLoginError`, `UnauthorizedError`
- **Dataset errors**: `NoFileUploadedError`, `UnsupportedFileTypeError`, `FileTooLargeError`, `TooManyFilesError`
- **Workspace errors**: `WorkspaceMemberNotFoundError`, `WorkspaceQuotaExhaustedError`

Errors are caught by Flask error handlers and returned as structured JSON responses.

---

## Database Schema and Migrations

### ORM Layer

Dify uses SQLAlchemy ORM with 22 model files defining the complete schema. Key models include:

- **Account** -- User accounts, credentials, OAuth tokens
- **Tenant** -- Workspaces, member relationships, roles
- **App** -- Application definitions, configurations, DSL
- **Conversation** -- Chat conversations, message history
- **Message** -- Individual messages with metadata
- **Dataset** -- Knowledge base definitions
- **Document** -- Documents within datasets
- **DocumentSegment** -- Chunks/segments with embeddings
- **WorkflowRun** -- Workflow execution records
- **WorkflowNodeExecution** -- Per-node execution records
- **InstalledPlugin** -- Plugin installation records
- **ApiToken** -- API key records (hashed)
- **UploadFile** -- File upload metadata

### Migration Management

Migrations use Flask-Migrate (Alembic wrapper):

```bash
# Run pending migrations
flask upgrade-db

# Check current version
flask db current

# Generate new migration (development)
flask db migrate -m "description"
```

Migrations run automatically on API container startup. The database uses WAL mode with foreign keys enabled.

---

## Celery Task System

### Queue Architecture

| Queue | Tasks | Purpose |
|-------|-------|---------|
| `dataset` | Document processing, embedding, indexing | Knowledge base operations |
| `generation` | LLM inference, streaming responses | Model interactions |
| `mail` | Email delivery (password reset, invitations) | Notifications |
| `ops_trace` | Operational tracing, analytics | Observability |

### Scheduled Tasks

Celery Beat runs scheduled tasks defined in `api/schedule/` (13 files). The beat polling interval is `CELERY_BEAT_SCHEDULER_TIME` (default: 1 second).

### Worker Configuration

```bash
# Single worker, all queues
celery -A app.celery worker -Q dataset,generation,mail,ops_trace --loglevel INFO

# Dedicated workers per queue
celery -A app.celery worker -Q dataset --concurrency 4 --loglevel INFO
celery -A app.celery worker -Q generation --concurrency 2 --loglevel INFO
```

### Broker Configuration

Redis is the default and only supported broker:
```
CELERY_BROKER_URL=redis://:password@redis:6379/1
```

The broker uses Redis DB 1 (separate from the main cache on DB 0).

---

## Observability and Telemetry

### Enterprise Telemetry (OTEL)

Dify Enterprise supports OpenTelemetry integration with a "slim span + rich companion log" architecture:

| Signal | Purpose | Completeness |
|--------|---------|-------------|
| **Traces (Spans)** | Structure, identity, timing of operations | Per-workflow, per-node |
| **Structured Logs** | Deep context (inputs, outputs, metadata) | Correlated via `trace_id` + `span_id` |
| **Metrics** | Counters and histograms | 100% accurate (no sampling) |

#### Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `ENTERPRISE_ENABLED` | `false` | Master switch for enterprise features |
| `ENTERPRISE_TELEMETRY_ENABLED` | `false` | Master switch for telemetry |
| `ENTERPRISE_OTLP_ENDPOINT` | -- | OTLP collector endpoint |
| `ENTERPRISE_OTLP_HEADERS` | -- | Custom OTLP headers |
| `ENTERPRISE_OTLP_PROTOCOL` | `http` | Transport protocol (`http` or `grpc`) |
| `ENTERPRISE_OTLP_API_KEY` | -- | Bearer token for auth |
| `ENTERPRISE_INCLUDE_CONTENT` | `false` | Include sensitive I/O in logs |
| `ENTERPRISE_SERVICE_NAME` | `dify` | Service name in OTEL |
| `ENTERPRISE_OTEL_SAMPLING_RATE` | `1.0` | Trace sampling rate (0.0-1.0) |

#### Trace ID Correlation

- **Workflow runs**: `trace_id = UUID(workflow_run_id)`
- **Node executions**: `span_id = hash(node_execution_id)`, child of workflow span
- **Sub-workflows**: Shares parent's `trace_id`, linked via `parent_span_id`
- **Draft executions**: Own `trace_id = UUID(node_execution_id)` (not part of any workflow trace)

#### Content Gating

When `ENTERPRISE_INCLUDE_CONTENT=false`, sensitive content is replaced with reference strings:
```
ref:workflow_run_id=550e8400-e29b-41d4-a716-446655440000
ref:node_execution_id=660e8400-e29b-41d4-a716-446655440001
```

### Sentry Integration

Set `SENTRY_DSN` for backend error tracking and `NEXT_PUBLIC_SENTRY_DSN` for frontend.

### Amplitude Analytics

Set `NEXT_PUBLIC_AMPLITUDE_API_KEY` for frontend user analytics.

---

## Code Patterns and Conventions

### Backend Patterns

| Pattern | Usage |
|---------|-------|
| **Pydantic v2 validation** | Request/response models use Pydantic v2 for validation and serialization |
| **SQLAlchemy ORM** | All database access through ORM models, never raw SQL |
| **Flask-RESTful** | API routes use Flask-RESTful resource classes |
| **Celery tasks** | Long-running operations dispatched to background queues |
| **Entry points** | Plugin discovery via `importlib.metadata.entry_points` |
| **gevent** | Cooperative multitasking for concurrent request handling |
| **Click** | All CLI commands use the Click framework |
| **Config-as-code** | Pydantic settings classes with environment variable loading |

### Frontend Patterns

| Pattern | Usage |
|---------|-------|
| **App Router** | Next.js 15 App Router with route groups `(commonLayout)` |
| **Zustand** | Client-side state management (UI state) |
| **TanStack Query** | Server state management (API data) |
| **Jotai** | Atomic state for fine-grained reactivity |
| **ReactFlow** | Workflow canvas with custom node components |
| **i18next** | Internationalization with server/client split |
| **Tailwind CSS 4** | Utility-first styling with `@langgenius/dify-ui` preset |
| **Zod** | Runtime schema validation |

### API Response Format

API responses follow a consistent structure:
```json
{
  "result": "success",
  "data": { ... }
}
```

Error responses:
```json
{
  "code": "error_code",
  "message": "Human-readable description",
  "status": 400
}
```

### Testing

```bash
# Backend unit tests
cd api
uv run pytest tests/unit_tests/

# Backend integration tests
uv run pytest tests/integration_tests/

# Specific test
uv run pytest tests/unit_tests/core/workflow/graph_engine/layers/test_llm_quota.py

# Frontend tests
cd web
pnpm test
pnpm test:coverage
```

---

## SDKs and Client Libraries

### Node.js SDK (`dify-client` v3.1.0)

| Class | Purpose |
|-------|---------|
| `DifyClient` | Base client with HTTP methods |
| `ChatClient` | Chat/Agent-Chat interactions |
| `CompletionClient` | Completion requests |
| `WorkflowClient` | Workflow execution |
| `KnowledgeBaseClient` | Dataset management |

```bash
npm install dify-client
```

Requires Node.js >= 18.0.0. ESM-only (`"type": "module"`).

### PHP SDK

Based on Guzzle HTTP client v7.9. Requires PHP >= 7.2.

```bash
composer require langgenius/dify-client
```

### Python SDK

Maintained in a separate repository: `github.com/langgenius/dify-sdk-python`.

---

## E2E Testing

### Framework

End-to-end tests use Playwright + Cucumber (BDD):

```bash
cd e2e
pnpm e2e:install        # Install Playwright + Chromium
pnpm e2e:middleware:up   # Start test infrastructure
pnpm e2e                 # Run tests
pnpm e2e:full            # Full test suite
pnpm e2e:full:headed     # Full suite with browser UI
pnpm e2e:reset           # Reset test state
pnpm e2e:middleware:down # Stop test infrastructure
```

Tests are in `e2e/features/` with step definitions in `e2e/features/step-definitions/`.

---

## Contributing

### Development Setup

1. Fork and clone the repository
2. Set up the backend and frontend as described in [Source Deployment](#source-deployment)
3. Create a feature branch

### Code Quality Tools

```bash
# Linting (root)
pnpm lint
pnpm lint:fix

# Type checking (frontend)
cd web && pnpm type-check

# Backend tests
cd api && uv run pytest

# Frontend tests
cd web && pnpm test
```

### Commit and PR Guidelines

- PRs should target the `main` branch
- Include tests for new features
- Follow existing code patterns and naming conventions
- Update translations for UI-facing changes

---

## Known Issues and Improvements

### Open Issues (from GitHub)

Key bugs and feature requests from the issue tracker:

- **Performance**: Workflow execution with many parallel branches can hit thread pool limits (`MAX_SUBMIT_COUNT`)
- **Vector Migration**: `vdb-migrate` requires downtime; no online migration path
- **Plugin Isolation**: Plugin crashes can affect the daemon's ability to route to other plugins
- **Large File Processing**: Documents near the `UPLOAD_FILE_SIZE_LIMIT` can cause worker memory spikes during extraction

### Architecture Improvement Opportunities

1. **API server decomposition** -- `api/controllers/console/app/` (25 files, 329 KB) handles too many concerns; could benefit from further route grouping
2. **Service layer complexity** -- `api/services/` (57 files, 1 MB) is the largest module; some services could be broken into domain-specific sub-modules
3. **Workflow engine hardening** -- The graph engine delegates to `graphon` but the Dify layer adds complexity with human-input resume and sub-workflow recursion
4. **Frontend bundle size** -- The bundled Monaco editor (`web/public/vs/`, 377 KB compressed) loads on all pages; could be lazy-loaded
5. **Test coverage** -- Integration tests exist but are concentrated in `api/tests/test_containers_integration_tests/services` (65 files, 1.7 MB); unit test coverage for controllers is lighter

---

## CLI Reference

All CLI commands are invoked via Flask. In Docker: `docker compose exec api flask <command>`.

### Account Commands (`api/commands/account.py`)

#### `reset-password`

Reset an account's password.

```bash
flask reset-password \
  --email [email removed] \
  --new-password "SecurePassword123" \
  --password-confirm "SecurePassword123"
```

#### `reset-email`

Change an account's email address.

```bash
flask reset-email \
  --email [email removed] \
  --new-email [email removed] \
  --email-confirm [email removed]
```

#### `create-tenant`

Create a new workspace and account.

```bash
flask create-tenant \
  --email [email removed] \
  --name "My Workspace" \
  --language en-US
```

### Plugin Commands (`api/commands/plugin.py`)

#### `setup-system-tool-oauth-client`

Configure OAuth for a tool plugin provider.

```bash
flask setup-system-tool-oauth-client \
  --provider github \
  --client-params '{"client_id":"...","client_secret":"..."}'
```

#### `setup-system-trigger-oauth-client`

Configure OAuth for a trigger plugin provider.

```bash
flask setup-system-trigger-oauth-client \
  --provider github \
  --client-params '{"client_id":"...","client_secret":"..."}'
```

#### `setup-datasource-oauth-client`

Configure OAuth for a datasource plugin provider.

```bash
flask setup-datasource-oauth-client \
  --provider notion \
  --client-params '{"client_id":"...","client_secret":"..."}'
```

#### `transform-datasource-credentials`

Transform datasource credentials for a specific environment.

```bash
flask transform-datasource-credentials --environment online
```

#### `migrate-data-for-plugin`

Migrate existing data for the plugin system.

```bash
flask migrate-data-for-plugin
```

#### `extract-plugins`

Extract plugin definitions from existing configuration.

```bash
flask extract-plugins --output_file plugins.jsonl --workers 10
```

#### `extract-unique-identifiers`

Extract unique plugin identifiers.

```bash
flask extract-unique-identifiers \
  --input_file plugins.jsonl \
  --output_file unique_identifiers.json
```

#### `install-plugins`

Batch install plugins from extracted definitions.

```bash
flask install-plugins \
  --input_file plugins.jsonl \
  --output_file installed_plugins.jsonl \
  --workers 100
```

#### `install-rag-pipeline-plugins`

Install RAG pipeline plugins.

```bash
flask install-rag-pipeline-plugins \
  --input_file plugins.jsonl \
  --output_file installed_plugins.jsonl \
  --workers 100
```

### Retention Commands (`api/commands/retention.py`)

#### `clear-free-plan-tenant-expired-logs`

Clear expired logs for free-plan tenants.

```bash
flask clear-free-plan-tenant-expired-logs \
  --days 30 \
  --batch 100 \
  --tenant_ids "uuid1,uuid2"
```

#### `clean-workflow-runs`

Delete expired workflow runs and related data.

```bash
flask clean-workflow-runs \
  --before-days 30 \
  --batch-size 200 \
  --dry-run

# Date range mode
flask clean-workflow-runs \
  --start-from "2026-01-01" \
  --end-before "2026-04-01"

# Relative range mode
flask clean-workflow-runs \
  --from-days-ago 90 \
  --to-days-ago 30
```

#### `archive-workflow-runs`

Archive workflow runs to S3-compatible storage.

```bash
flask archive-workflow-runs \
  --before-days 90 \
  --workers 4 \
  --limit 1000 \
  --delete-after-archive \
  --dry-run

# Grayscale rollout
flask archive-workflow-runs \
  --tenant-ids "uuid1,uuid2" \
  --before-days 90
```

#### `restore-workflow-runs`

Restore archived workflow runs.

```bash
flask restore-workflow-runs \
  --tenant-ids "uuid1" \
  --run-id "run-uuid" \
  --workers 4 \
  --limit 100
```

#### `delete-archived-workflow-runs`

Delete archived workflow runs from the database.

```bash
flask delete-archived-workflow-runs \
  --tenant-ids "uuid1" \
  --run-id "run-uuid" \
  --dry-run
```

#### `cleanup_orphaned_draft_variables`

Remove orphaned draft variables.

```bash
flask cleanup_orphaned_draft_variables \
  --dry-run \
  --batch-size 1000 \
  --max-apps 100 \
  --force
```

#### `clean-expired-messages`

Clean expired messages.

```bash
flask clean-expired-messages \
  --before-days 90 \
  --from-days-ago 180 \
  --batch-size 1000 \
  --graceful-period 21 \
  --dry-run
```

#### `export-app-messages`

Export messages for an app to JSONL.GZ.

```bash
flask export-app-messages \
  --app-id "app-uuid" \
  --end-before "2026-06-01" \
  --filename "export/messages" \
  --batch-size 1000 \
  --use-cloud-storage \
  --dry-run
```

### Storage Commands (`api/commands/storage.py`)

#### `clear-orphaned-file-records`

Remove database records for files that no longer exist in storage.

```bash
flask clear-orphaned-file-records --force
```

#### `remove-orphaned-files-on-storage`

Delete storage files that have no corresponding database records.

```bash
flask remove-orphaned-files-on-storage --force
```

#### `file-usage`

Query where files are referenced in the system.

```bash
flask file-usage --file-id "uuid"
flask file-usage --key "upload_files/path"
flask file-usage --src "documents.%" --limit 50 --json
```

#### `migrate-oss`

Migrate files from local/OpenDAL to cloud storage.

```bash
flask migrate-oss \
  --source opendal \
  --path upload_files \
  --path image_files \
  --overwrite \
  --update-db \
  --dry-run
```

Default paths: `privkeys`, `upload_files`, `image_files`, `tools`, `website_files`, `keyword_files`, `ops_trace`.

### System Commands (`api/commands/system.py`)

#### `reset-encrypt-key-pair`

Reset workspace asymmetric key pair for credential encryption.

```bash
flask reset-encrypt-key-pair
```

> [!CAUTION]
> This invalidates ALL stored LLM credentials across ALL workspaces. Every model provider must be re-configured after running this command. Only use when the key pair is compromised. Only works in `SELF_HOSTED` mode.

#### `convert-to-agent-apps`

Convert legacy Agent Assistant apps to the new Agent App type.

```bash
flask convert-to-agent-apps
```

#### `upgrade-db`

Run pending database migrations.

```bash
flask upgrade-db
```

#### `fix-app-site-missing`

Repair apps that are missing their associated site records.

```bash
flask fix-app-site-missing
```

### Vector Commands (`api/commands/vector.py`)

#### `vdb-migrate`

Migrate vectors between backends.

```bash
flask vdb-migrate --scope all
```

The `--scope` option accepts `all` (default) to migrate all datasets.

#### `add-qdrant-index`

Add an index to Qdrant collections.

```bash
flask add-qdrant-index --field metadata.doc_id
```

#### `old-metadata-migration`

Migrate legacy vector metadata format.

```bash
flask old-metadata-migration
```

---

## Quick Reference

### Docker Commands

```bash
# Start all services
docker compose up -d

# Stop all services
docker compose down

# View logs
docker compose logs -f api
docker compose logs --tail 100 worker

# Restart a service
docker compose restart api

# Shell into container
docker compose exec api bash
docker compose exec db psql -U postgres dify

# Check health
curl http://localhost/health
curl http://localhost/ping
```

### Common Operations Cheat Sheet

| Task | Command |
|------|---------|
| Run DB migrations | `flask upgrade-db` |
| Reset user password | `flask reset-password --email <e> --new-password <pw> --password-confirm <pw>` |
| Create workspace | `flask create-tenant --email <e> --name <n>` |
| Migrate vector DB | `flask vdb-migrate --scope all` |
| Clean old workflow data | `flask clean-workflow-runs --before-days 30` |
| Export messages | `flask export-app-messages --app-id <id> --end-before <date>` |
| Migrate storage | `flask migrate-oss --source opendal --update-db` |
| Reset encryption keys | `flask reset-encrypt-key-pair` |
| Clean orphaned files | `flask clear-orphaned-file-records -f` |
| Env sync after upgrade | `python docker/dify-env-sync.py` |

### API Surface Summary

| Surface | Base Path | Auth | Audience |
|---------|-----------|------|----------|
| Console API | `/console/api/*` | JWT | Web UI |
| Service API | `/v1/*` | API Key | Integrations |
| Web API | `/api/*` | App token | End users |
| Inner API | `/inner/api/*` | Shared secret | Plugin daemon |

### System Endpoints

| Endpoint | Purpose |
|----------|---------|
| `GET /health` | Full health check |
| `GET /ping` | Liveness probe |
| `GET /version` | API version |
| `GET /init` | Setup status |
| `GET /db-pool-stat` | DB pool stats |
| `GET /threads` | Thread count |

### Port Reference

| Port | Service | Protocol |
|------|---------|----------|
| 80 | nginx | HTTP |
| 443 | nginx | HTTPS |
| 3000 | web (Next.js) | HTTP |
| 5001 | api (Flask) | HTTP + WebSocket |
| 5002 | plugin_daemon | HTTP |
| 5003 | plugin remote install | HTTP |
| 5432 | PostgreSQL | TCP |
| 6379 | Redis | TCP |
| 8080 | Weaviate HTTP | HTTP |
| 50051 | Weaviate gRPC | gRPC |
| 8194 | sandbox | HTTP |
| 3128 | ssrf_proxy | HTTP |

---

_Generated May 2026 from langgenius/dify at 13a9359. 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._
