# ComfyUI Administrator & Developer Guide

## 1. Architecture Overview

ComfyUI is a modular, node-based generative AI platform. The server process is a single Python `aiohttp` application. A browser frontend communicates with it over HTTP and WebSocket. A background thread runs the execution queue. The entire system is stateless between runs — all persistence is file system (models, outputs) or SQLite (assets, user data).

```
Browser (Frontend)
    │
    ├── HTTP/REST  ──────────────────────────────┐
    └── WebSocket (real-time events) ────────────┤
                                                 ▼
                                      PromptServer (aiohttp, :8188)
                                        │
                                        ├── /prompt, /queue, /history
                                        ├── /ws  (WebSocket hub)
                                        ├── /settings, /users, /userdata
                                        ├── /models, /embeddings, /view
                                        ├── /object_info
                                        ├── /internal/   (InternalRoutes)
                                        ├── /api/assets/ (AssetRoutes, opt-in)
                                        ├── /api/jobs/   (Jobs API)
                                        └── FrontendManager (static assets)
                                                 │
                                        PromptQueue (asyncio + thread)
                                                 │
                                        prompt_worker (background thread)
                                                 │
                                        PromptExecutor
                                           ├── comfy_execution/graph.py
                                           ├── comfy_execution/caching.py
                                           └── comfy/ (ML engine)
```

> [!NOTE]
> The queue worker runs in a **dedicated background thread** started at server boot. All ML compute happens in this thread; the aiohttp event loop remains free for HTTP/WebSocket traffic. This design allows real-time progress updates while inference runs without blocking the server.

### Component Map

| Component | Path | Files | Responsibility |
|-----------|------|------:|----------------|
| Entry points | `.` | 13 | `main.py`, `server.py`, `nodes.py`, `execution.py`, `folder_paths.py` |
| ML engine | `comfy/` | 48 | Model loading, sampling, memory management, CLIP, VAE, LoRA, hooks, ops |
| Built-in nodes | `comfy_extras/` | 99 | Samplers, image processing, video, audio, 3D, IPAdapter, ControlNet, etc. |
| Execution engine | `comfy_execution/` | 8 | Graph traversal, caching, validation, job status, progress tracking |
| Versioned node API | `comfy_api/` | 4+ | Stable API surface for node authors (latest, v0_0_2, v0_0_1) + feature flags |
| External API nodes | `comfy_api_nodes/` | 32+9 | BFL, Gemini, OpenAI, Kling, and other cloud integrations |
| Application layer | `app/` | 8 | Settings, frontend, user, model, custom node, subgraph managers |
| Asset management | `app/assets/` | ~20 | SQLite-backed asset tracking, scanning, tagging (requires `--enable-assets`) |
| Internal API | `api_server/` | 4 | `/internal/` routes, terminal service, file operations |
| Middleware | `middleware/` | 2 | Cache-control headers |
| Custom node config | `comfy_config/` | 2 | `pyproject.toml` parser for custom node packages |
| Utilities | `utils/` | 4 | Extra config loading, install helpers, JSON, MIME types |
| Database migrations | `alembic_db/` | 4 | SQLite schema (assets, asset references, metadata job ID) |
| Workflow templates | `blueprints/` | 38 | Packaged workflow JSON files |
| Tests | `tests/` | 7 | Integration tests (execution, inference, comparison) |
| Unit tests | `tests-unit/` | ~50 | Pytest unit test suite |

### Request Lifecycle

1. Browser POSTs a workflow graph to `/prompt`.
2. `PromptServer.post_prompt()` validates the origin header, parses the JSON body, and calls `queue.put()`.
3. `PromptQueue` assigns a `prompt_id` and enqueues.
4. The background `prompt_worker` thread dequeues via `get()` (blocking).
5. `PromptExecutor.execute()` topologically sorts the graph (`DynamicPrompt`), resolves cache hits, and runs each node in dependency order.
6. After each node, a WebSocket `executing` event is broadcast. On output image nodes, a `PREVIEW_IMAGE` binary event is sent.
7. On completion, the result is stored in history and an `executed` event is broadcast.

### WebSocket Event Protocol

Clients connect to `/ws?clientId=<uuid>`. Feature flag negotiation happens on the first server→client message.

| Event Type | Direction | Payload |
|------------|-----------|---------|
| `status` | Server→Client | Queue remaining count, exec info |
| `executing` | Server→Client | `{"node": "<id>", "prompt_id": "<id>"}` |
| `progress` | Server→Client | `{"value": N, "max": M, "prompt_id": ...}` |
| `executed` | Server→Client | `{"node": "<id>", "output": {...}, "prompt_id": ...}` |
| `PREVIEW_IMAGE` | Server→Client | Binary: 4-byte type prefix + JPEG/PNG bytes |
| `PREVIEW_IMAGE_WITH_METADATA` | Server→Client | Binary: preview + JSON metadata |
| `TEXT` | Server→Client | Binary text event |

---

## 2. System Requirements

### Minimum Hardware

| Component | Minimum | Recommended |
|-----------|---------|-------------|
| GPU | NVIDIA GTX 10xx (8 GB VRAM) | RTX 30xx / 40xx / 50xx (16+ GB VRAM) |
| CPU | 4-core x86-64 | 8+ core |
| RAM | 16 GB | 32+ GB |
| Storage | 20 GB free | 200+ GB (models are large) |

> [!NOTE]
> ComfyUI runs on CPU (`--cpu` flag) but is extremely slow. AMD (ROCm), Intel Arc (IPEX), Apple Silicon (MPS), and Huawei Ascend (CANN) are all supported with their respective driver stacks.

### GPU Platform Matrix

| Platform | Flag / Mechanism | Notes |
|----------|-----------------|-------|
| NVIDIA CUDA | Default (no flag needed) | Best support, all precision modes |
| NVIDIA CUDA (specific GPU) | `--cuda-device N` | Multi-GPU selection |
| AMD ROCm | `--default-device cuda` with ROCm PyTorch | Requires ROCm-enabled torch build |
| Intel Arc / Xe | `--oneapi-device-selector ...` | IPEX required; `--disable-ipex-optimize` to skip |
| DirectML (Windows) | `--directml` | Fallback for non-CUDA Windows GPUs |
| Apple MPS | Auto-detected | macOS with Apple Silicon |
| CPU fallback | `--cpu` | Slow; useful for debugging |
| Huawei Ascend NPU | `--cuda-device N` + Ascend SDK | `ASCEND_RT_VISIBLE_DEVICES` set automatically |

### Python & Package Requirements

| Package | Version | Purpose |
|---------|---------|---------|
| Python | ≥ 3.10 (3.12+ recommended) | Runtime |
| torch | latest stable | ML framework |
| torchvision | matching torch | Image ops |
| torchaudio | matching torch | Audio ops |
| torchsde | latest | SDE samplers |
| aiohttp | ≥ 3.11.8 | HTTP server + WebSocket |
| sqlalchemy | latest | ORM |
| alembic | latest | DB migrations |
| pillow | latest | Image I/O |
| transformers | ≥ 4.50.3 | HuggingFace model loading |
| safetensors | ≥ 0.4.2 | Safe model format |
| numpy | ≥ 1.25.0 | Numerical computing |
| einops | latest | Tensor manipulation |
| scipy | latest | Scientific computing |
| av | ≥ 14.2.0 | Video processing (FFmpeg wrapper) |
| blake3 | latest | Fast file hashing |
| spandrel | latest | Upscale/restoration model loader |
| kornia | ≥ 0.7.1 | Computer vision transforms |
| pydantic | ~2.0 | Data validation (API layer) |
| comfy-aimdo | ≥ 0.2.12 | Dynamic VRAM management |
| comfy-kitchen | ≥ 0.2.8 | Shared utility functions |
| simpleeval | ≥ 1.0.0 | Safe math expression evaluation |

> [!IMPORTANT]
> **PyTorch version must match your CUDA toolkit.** Installing mismatched builds silently falls back to CPU. Always install from the official PyTorch matrix at `pytorch.org/get-started/locally/`. Verify GPU detection with `python -c "import torch; print(torch.cuda.is_available())"` before running the server.

### Operating System Support

| OS | Status | Notes |
|----|--------|-------|
| Windows 10/11 | Fully supported | `--windows-standalone-build` for packaged installs |
| Ubuntu 20.04+ / Debian | Fully supported | Primary development target |
| macOS 12+ (Apple Silicon) | Supported | MPS backend; some nodes CPU-only |
| macOS (Intel) | Partial | No GPU acceleration |
| Other Linux | Best-effort | ROCm distros may need custom PyTorch builds |

---

## 3. Configuration Reference

### Command-Line Interface (All 94 Flags)

#### Network

| Flag | Default | Description |
|------|---------|-------------|
| `--listen [IP]` | `127.0.0.1` | Bind address. Use `0.0.0.0` to listen on all interfaces |
| `--port PORT` | `8188` | HTTP port |
| `--tls-keyfile PATH` | — | Path to TLS private key (enables HTTPS) |
| `--tls-certfile PATH` | — | Path to TLS certificate |
| `--enable-cors-header [ORIGIN]` | — | Enable CORS header. Omit value for `*` |
| `--max-upload-size MB` | `100` | Maximum upload file size in megabytes |
| `--enable-compress-response-body` | off | Enable gzip response compression |

> [!IMPORTANT]
> Setting `--listen 0.0.0.0` without authentication exposes the server to your entire network. ComfyUI has **no built-in authentication**. Use a reverse proxy with auth or strict firewall rules before opening to a LAN.

#### Directories

| Flag | Default | Description |
|------|---------|-------------|
| `--base-directory PATH` | CWD | Root directory for all relative paths |
| `--extra-model-paths-config PATH` | — | YAML file mapping extra model directories |
| `--output-directory PATH` | `./output` | Generated image/video output directory |
| `--temp-directory PATH` | `./temp` | Temporary files (intermediate renders) |
| `--input-directory PATH` | `./input` | Input file upload directory |
| `--user-directory PATH` | `./user` | Per-user settings and workflow storage |

#### Launch Behavior

| Flag | Default | Description |
|------|---------|-------------|
| `--auto-launch` | off | Open browser automatically on start |
| `--disable-auto-launch` | — | Suppress auto-launch (standalone builds) |
| `--windows-standalone-build` | off | Windows packaged build mode |

#### GPU / Device Selection

| Flag | Default | Description |
|------|---------|-------------|
| `--cuda-device N` | — | Select CUDA device by index |
| `--default-device DEVICE` | auto | Set default PyTorch device |
| `--cuda-malloc` | on | Use CUDA memory allocator |
| `--disable-cuda-malloc` | — | Disable CUDA memory allocator |
| `--directml [N]` | — | Use DirectML device (Windows fallback) |
| `--oneapi-device-selector FILTER` | — | Intel OneAPI device filter string |
| `--disable-ipex-optimize` | — | Skip `ipex.optimize()` call |
| `--supports-fp8-compute` | auto | Declare FP8 compute capability |

#### Precision Control

| Flag | Scope | Description |
|------|-------|-------------|
| `--force-fp32` | Global | Force FP32 everywhere |
| `--force-fp16` | Global | Force FP16 everywhere |
| `--fp32-unet` / `--fp64-unet` | UNet | UNet in FP32 / FP64 |
| `--bf16-unet` / `--fp16-unet` | UNet | UNet in BF16 / FP16 |
| `--fp8_e4m3fn-unet` / `--fp8_e5m2-unet` / `--fp8_e8m0fnu-unet` | UNet | UNet in FP8 variants |
| `--fp16-vae` / `--fp32-vae` / `--bf16-vae` | VAE | VAE precision |
| `--cpu-vae` | VAE | Run VAE decode on CPU |
| `--fp8_e4m3fn-text-enc` / `--fp8_e5m2-text-enc` | Text encoder | Text encoder FP8 variants |
| `--fp16-text-enc` / `--fp32-text-enc` / `--bf16-text-enc` | Text encoder | Text encoder precision |
| `--fp16-intermediates` | Intermediate tensors | Use FP16 for intermediate values |
| `--force-channels-last` | Memory layout | Force channels-last tensor layout |

#### VRAM Management

| Flag | Description |
|------|-------------|
| `--gpu-only` | Keep everything on GPU, never offload |
| `--highvram` | Aggressive VRAM use; slower model transitions |
| `--normalvram` | Default behavior |
| `--lowvram` | Offload UNet activations to CPU when needed |
| `--novram` | Offload everything; only run operations on GPU |
| `--cpu` | No GPU; all compute on CPU |
| `--reserve-vram GB` | Reserve this many GB for other applications |
| `--async-offload` | Enable async CPU offload streams |
| `--disable-async-offload` | Disable async offload |
| `--disable-dynamic-vram` | Disable `comfy-aimdo` dynamic management |
| `--enable-dynamic-vram` | Force enable dynamic VRAM |
| `--force-non-blocking` | Force non-blocking CUDA transfers |
| `--disable-smart-memory` | Disable intelligent model retention in VRAM |

#### Attention Mechanism

| Flag | Description |
|------|-------------|
| `--use-split-cross-attention` | Split cross-attention (memory savings) |
| `--use-quad-cross-attention` | Quad split cross-attention |
| `--use-pytorch-cross-attention` | Force PyTorch native attention |
| `--use-sage-attention` | Use Sage Attention (if installed) |
| `--use-flash-attention` | Use Flash Attention (if installed) |
| `--disable-xformers` | Do not use xformers even if available |
| `--force-upcast-attention` | Always upcast attention to FP32 |
| `--dont-upcast-attention` | Never upcast attention |

#### Caching Strategy

| Flag | Description |
|------|-------------|
| `--cache-classic` | Classic LRU-style cache (default) |
| `--cache-lru N` | LRU cache keeping N entries per cache key |
| `--cache-none` | Disable node output caching entirely |
| `--cache-ram` | RAM-pressure-aware cache (evicts under memory pressure) |

#### Preview Image

| Flag | Default | Description |
|------|---------|-------------|
| `--preview-method METHOD` | `auto` | Preview encoding: `none`, `auto`, `latent2rgb`, `taesd` |
| `--preview-size PX` | `512` | Maximum preview dimension in pixels |

#### Performance Tuning

| Flag | Description |
|------|-------------|
| `--fast` | Enable experimental speed optimizations |
| `--deterministic` | Reproducible outputs (disables non-deterministic ops; slower) |
| `--disable-pinned-memory` | Disable pinned (page-locked) CPU memory allocations |
| `--mmap-torch-files` | Memory-map model files instead of loading fully |
| `--disable-mmap` | Disable memory mapping |
| `--default-hashing-function ALGO` | Hash function for file change detection (`blake3`, `sha256`, `md5`) |

#### Custom Node Manager

| Flag | Description |
|------|-------------|
| `--enable-manager` | Enable the custom node manager UI |
| `--disable-manager-ui` | Disable manager UI but keep backend |
| `--enable-manager-legacy-ui` | Use legacy manager interface |

#### Custom Node Loading

| Flag | Description |
|------|-------------|
| `--disable-all-custom-nodes` | Skip loading any custom node packages |
| `--whitelist-custom-nodes NAME [NAME...]` | Load only listed custom node packages |
| `--disable-api-nodes` | Disable external API node integrations |

#### Miscellaneous

| Flag | Default | Description |
|------|---------|-------------|
| `--multi-user` | off | Enable per-user storage isolation |
| `--verbose [LEVEL]` | `INFO` | Log verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `--log-stdout` | off | Print logs to stdout in addition to the log buffer |
| `--disable-metadata` | off | Do not embed metadata in output PNG files |
| `--front-end-version VER` | bundled | Use a specific frontend version |
| `--front-end-root PATH` | bundled | Serve frontend from local path (dev override) |
| `--comfy-api-base URL` | — | Base URL for API node external calls |
| `--database-url URL` | SQLite at `user/comfy.db` | Database connection string |
| `--enable-assets` | off | Enable the asset management system |
| `--dont-print-server` | off | Suppress server start banner |
| `--quick-test-for-ci` | off | Start, validate nodes, and immediately exit (CI use) |

### extra_model_paths.yaml

Maps additional model directories from other UIs or shared network shares. Place at the project root or pass via `--extra-model-paths-config`.

```yaml
# Map models from another WebUI install
automatic1111:
    base_path: /path/to/stable-diffusion-webui
    checkpoints: models/Stable-diffusion
    vae: models/VAE
    loras: models/Lora
    embeddings: embeddings
    hypernetworks: models/hypernetworks
    controlnet: models/ControlNet

# Map from a shared NAS
nas_models:
    base_path: /mnt/nas/ai-models
    checkpoints: checkpoints
    upscale_models: upscalers
    clip: clip
```

Supported folder keys: `checkpoints`, `vae`, `loras`, `embeddings`, `hypernetworks`, `controlnet`, `clip`, `clip_vision`, `diffusers`, `gligen`, `upscale_models`, `style_models`, `photomaker`, and any custom folder registered by node packages.

### Model Directory Structure

```
models/
├── checkpoints/       Full model checkpoints (SD 1.x, SDXL, Flux, etc.)
├── clip/              CLIP text encoder weights
├── clip_vision/       CLIP vision encoder weights
├── configs/           Model architecture config YAMLs
├── controlnet/        ControlNet models
├── diffusers/         HuggingFace diffusers format models
├── embeddings/        Textual inversion embeddings
├── gligen/            GLIGEN spatial grounding models
├── hypernetworks/     Hypernetwork weights
├── loras/             LoRA and LyCORIS weights
├── photomaker/        PhotoMaker identity weights
├── style_models/      Style reference models
├── unet/              Standalone UNet weights (Flux, etc.)
├── upscale_models/    ESRGAN, RealESRGAN, SwinIR upscalers
├── vae/               VAE weights
├── vae_approx/        Approximate VAE for previews (TAESD)
└── ...                Additional categories registered by custom nodes
```

### Environment Variables

#### Auto-Set by main.py

| Variable | Value | Purpose |
|----------|-------|---------|
| `HF_HUB_DISABLE_TELEMETRY` | `1` | Disable HuggingFace Hub telemetry |
| `DO_NOT_TRACK` | `1` | Global analytics opt-out |
| `TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL` | `1` | ROCm AoTriton experimental features |
| `CUDA_VISIBLE_DEVICES` | From `--cuda-device` | NVIDIA GPU selection |
| `HIP_VISIBLE_DEVICES` | From `--cuda-device` | AMD GPU selection |
| `ASCEND_RT_VISIBLE_DEVICES` | From `--cuda-device` | Huawei Ascend NPU selection |
| `ONEAPI_DEVICE_SELECTOR` | From flag | Intel OneAPI device filter |
| `CUBLAS_WORKSPACE_CONFIG` | `:4096:8` | When `--deterministic` is set |
| `OCL_SET_SVM_SIZE` | `262144` | ROCm SVM heap size |
| `MIMALLOC_PURGE_DELAY` | `0` | Windows memory purge tuning |

#### User-Configurable

| Variable | Default | Purpose |
|----------|---------|---------|
| `PYTORCH_CUDA_ALLOC_CONF` | — | Fine-tune PyTorch CUDA allocator (e.g., `max_split_size_mb:512`) |
| `COMFYUI_SERVER` | `http://localhost:8988` | Server URL for integration tests |
| `DISPLAY` | system | X11 display for GLSL nodes |
| `WAYLAND_DISPLAY` | system | Wayland display for GLSL nodes |

### Database Configuration

By default, SQLite is created at `<user-directory>/comfy.db`. Override with `--database-url`:

```bash
# Default (SQLite, auto-created)
python main.py

# Custom SQLite path
python main.py --database-url sqlite:////data/comfyui.db

# In-memory (tests only — data lost on exit)
python main.py --database-url sqlite:///:memory:
```

The SQLite database operates in WAL (Write-Ahead Logging) mode. Foreign keys are enforced. Three Alembic migration versions are applied automatically on startup:

1. `assets` — initial asset table
2. `merge_to_asset_references` — separate asset references table
3. `add_metadata_job_id` — metadata and job tracking columns

---

## 4. Deployment & Scaling

### Bare-Metal Single-Node (Standard)

```bash
# Install dependencies (CUDA 12.x example)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install -r requirements.txt

# Start server (local access only)
python main.py

# Start server (LAN access, custom port)
python main.py --listen 0.0.0.0 --port 8188

# High-VRAM workstation
python main.py --highvram --listen 0.0.0.0

# Low-VRAM machine (8 GB)
python main.py --lowvram --preview-method latent2rgb
```

### Reverse Proxy (Nginx)

For production or shared access, front ComfyUI with nginx. WebSocket proxying is required.

```nginx
server {
    listen 443 ssl;
    server_name comfy.example.com;

    ssl_certificate     /etc/ssl/certs/comfy.crt;
    ssl_certificate_key /etc/ssl/private/comfy.key;

    # Increase for model uploads
    client_max_body_size 2G;

    location / {
        proxy_pass http://127.0.0.1:8188;
        proxy_http_version 1.1;

        # WebSocket upgrade
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}
```

> [!WARNING]
> Do **not** expose ComfyUI directly to the public internet without authentication middleware. The application has no built-in access control; any visitor can submit arbitrary generation jobs, read outputs, and access uploaded files.

### Docker

ComfyUI has no official Docker image, but the community pattern is straightforward:

```dockerfile
FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y python3.12 python3-pip git \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 \
 && pip install -r requirements.txt

EXPOSE 8188

CMD ["python", "main.py", "--listen", "0.0.0.0", \
     "--output-directory", "/data/output", \
     "--input-directory", "/data/input", \
     "--user-directory", "/data/user"]
```

```yaml
# docker-compose.yml
services:
  comfyui:
    build: .
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=0
    ports:
      - "8188:8188"
    volumes:
      - ./models:/app/models
      - comfy_data:/data
    restart: unless-stopped

volumes:
  comfy_data:
```

> [!TIP]
> Mount the `models/` directory as a bind volume so model weights persist across container rebuilds. Model files are typically tens of gigabytes and must never be baked into the image layer.

### Multi-GPU Setup

ComfyUI currently supports one GPU per server process. To utilize multiple GPUs:

- Run one server process per GPU with different ports and `--cuda-device N`.
- Load-balance with nginx `upstream` across the ports.
- Each server has an independent queue and VRAM pool.

```bash
# Process for GPU 0
python main.py --cuda-device 0 --port 8188

# Process for GPU 1
python main.py --cuda-device 1 --port 8189
```

### Windows Standalone Build

The official Windows release uses `--windows-standalone-build`. This flag adjusts path resolution and enables bundled Python. The `run_nvidia_gpu.bat` launcher sets this flag automatically.

### Systemd Service (Linux)

```ini
[Unit]
Description=ComfyUI Generative AI Server
After=network.target

[Service]
Type=simple
User=comfy
WorkingDirectory=/opt/comfyui
ExecStart=/opt/comfyui/venv/bin/python main.py \
    --listen 127.0.0.1 \
    --port 8188 \
    --highvram \
    --output-directory /data/comfyui/output \
    --user-directory /data/comfyui/user
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
```

---

## 5. Operations

### Starting and Stopping

```bash
# Standard start
python main.py [flags]

# Start and open browser
python main.py --auto-launch

# Quick CI validation (starts, validates nodes, exits)
python main.py --quick-test-for-ci

# Graceful stop: Ctrl+C
# The server finishes the current job before exiting.
```

### Queue Management

```bash
# Get current queue state
curl http://localhost:8188/queue

# Clear all pending jobs
curl -X POST http://localhost:8188/queue \
  -H "Content-Type: application/json" \
  -d '{"clear": true}'

# Delete specific job
curl -X POST http://localhost:8188/queue \
  -H "Content-Type: application/json" \
  -d '{"delete": ["<prompt_id>"]}'

# Interrupt current execution
curl -X POST http://localhost:8188/interrupt
```

### History Management

```bash
# Get full history
curl http://localhost:8188/history

# Get specific job
curl http://localhost:8188/history/<prompt_id>

# Clear history
curl -X POST http://localhost:8188/history \
  -H "Content-Type: application/json" \
  -d '{"clear": true}'
```

### Memory and Model Management

```bash
# Free GPU memory (unload all cached models)
curl -X POST http://localhost:8188/free \
  -H "Content-Type: application/json" \
  -d '{"unload_models": true, "free_memory": true}'

# System stats (RAM, VRAM, GPU info, Python version)
curl http://localhost:8188/system_stats
```

Sample `system_stats` response:

```json
{
  "system": {
    "os": "Linux-6.1.0-x86_64",
    "python_version": "3.12.3",
    "embedded_python": false
  },
  "devices": [
    {
      "name": "NVIDIA GeForce RTX 4090",
      "type": "cuda",
      "index": 0,
      "vram_total": 25769803776,
      "vram_free": 18274025472,
      "torch_vram_total": 25769803776,
      "torch_vram_free": 18274025472
    }
  ]
}
```

> [!NOTE]
> The `/free` endpoint is the primary operational lever for releasing VRAM between large jobs. Call it before loading a significantly different model configuration to avoid OOM errors.

### Log Access

```bash
# Get buffered logs (JSON array)
curl http://localhost:8188/internal/logs

# Get raw log text
curl http://localhost:8188/internal/logs/raw

# Subscribe to live log stream (SSE)
curl -X PATCH http://localhost:8188/internal/logs/subscribe
```

### Asset System Operations (requires `--enable-assets`)

```bash
# Trigger background asset scan
curl -X POST http://localhost:8188/api/assets/seed

# Check scan status
curl http://localhost:8188/api/assets/seed/status

# Cancel running scan
curl -X POST http://localhost:8188/api/assets/seed/cancel

# Mark files that no longer exist on disk
curl -X POST http://localhost:8188/api/assets/prune
```

> [!TIP]
> Run `POST /api/assets/seed` after adding new model files to the `models/` directories. The scan runs in the background; poll `/api/assets/seed/status` for completion. Large output directories (thousands of files) can take several minutes to scan.

### Folder Path Inspection

```bash
# See all configured folder paths (name → list of directories)
curl http://localhost:8188/internal/folder_paths

# List files in a directory type
curl "http://localhost:8188/internal/files/output"
curl "http://localhost:8188/internal/files/checkpoints"
```

### User Management (Multi-User Mode)

Enable with `--multi-user`. Each user gets isolated storage under `<user-directory>/<user-id>/`.

```bash
# List users
curl http://localhost:8188/users

# Create user
curl -X POST http://localhost:8188/users \
  -H "Content-Type: application/json" \
  -d '{"username": "alice"}'
```

### Monitoring Checklist

> [!NOTE]
> ComfyUI emits no structured metrics by default. Monitoring relies on polling the REST endpoints and tailing logs.

| Check | Endpoint / Method | Recommended Frequency |
|-------|-------------------|-----------------------|
| Queue depth | `GET /queue` → `queue_remaining` | Every 30s |
| VRAM headroom | `GET /system_stats` → `vram_free` | Every 60s |
| Error rate | `GET /internal/logs/raw` → grep `ERROR` | Continuous |
| Job history count | `GET /history` → count keys | Hourly |
| Disk usage (output/) | `du -sh output/` | Daily |
| Stuck jobs | `GET /queue` + compare timestamps | Every 5 min |

---

## 6. Performance

### VRAM Tiering Strategy

| Available VRAM | Recommended Flags |
|---------------|-------------------|
| 24+ GB | `--gpu-only` or default |
| 16–24 GB | default or `--highvram` |
| 8–16 GB | `--normalvram` (default) |
| 6–8 GB | `--lowvram` |
| < 6 GB | `--novram` |
| < 4 GB or no GPU | `--cpu` |

### Precision Tradeoffs

| Precision | VRAM Usage | Speed | Quality |
|-----------|-----------|-------|---------|
| FP32 | 4× baseline | Slowest | Reference |
| BF16 | 2× baseline | Fast | Near-identical to FP32 |
| FP16 | 2× baseline | Fast | Good; rare artifacts on some models |
| FP8 e4m3fn | 1× baseline | Fastest | Slight quality loss; requires hardware support |
| FP8 e5m2 | 1× baseline | Fastest | More dynamic range, less mantissa precision |

### Attention Implementation Performance

| Implementation | Speed | VRAM | Requirements |
|---------------|-------|------|--------------|
| xformers | Fast | Low | `pip install xformers` (auto-detected) |
| Flash Attention | Fastest | Lowest | `pip install flash-attn` + CUDA 11.6+ |
| Sage Attention | Fast | Low | `pip install sageattention` |
| Split cross-attention | Medium | Low | No extra install |
| PyTorch native | Baseline | Medium | Always available |

### Caching Strategy Selection

| Mode | Flag | Use Case |
|------|------|---------|
| Classic | `--cache-classic` | Default; good for iterative refinement |
| LRU N | `--cache-lru N` | Explicit capacity; `N=0` disables eviction |
| None | `--cache-none` | Maximum VRAM; no repeated-node benefit |
| RAM | `--cache-ram` | Evicts automatically under RAM pressure |

> [!TIP]
> For batch processing workflows where each job is unique (no repeated node inputs), use `--cache-none` to avoid wasting RAM on cache entries that will never hit again.

### Memory Mapping

`--mmap-torch-files` uses `mmap()` to load model weights. This reduces peak RAM during model load on Linux but may slow the first inference pass (cold read from disk). Particularly useful when running multiple ComfyUI instances that share the same physical model files.

### Preview Performance

| Method | CPU Cost | Latency | Visual Quality |
|--------|---------|---------|---------------|
| `none` | Zero | None | No preview |
| `latent2rgb` | Near-zero | Real-time | Color-approximate |
| `taesd` | Low | ~50ms | Good |
| `auto` | Adaptive | Adaptive | Picks best available |

### Async Offloading

`--async-offload` enables CUDA streams for CPU↔GPU transfers that overlap with compute. Recommended for VRAM-constrained configurations where offloading is frequent. Use `--disable-async-offload` if you observe ordering artifacts on specific hardware.

### Deterministic Mode

`--deterministic` sets `CUBLAS_WORKSPACE_CONFIG=:4096:8` and disables non-deterministic PyTorch ops. Expect 10–30% latency increase. Use only when reproducibility is required (A/B comparisons, regression testing).

---

## 7. Security

### Threat Model

ComfyUI's design assumes a **trusted local or private network environment**. There is no built-in authentication, no user authorization, and no sandboxing of workflow execution. Every client that can reach the HTTP port can:

- Submit arbitrary workflows and consume GPU resources
- Read all generated outputs
- Access all uploaded input files
- Execute system introspection via `/system_stats`
- List and download model file metadata

> [!CAUTION]
> **Never expose ComfyUI directly to the internet** without a hardened reverse proxy providing authentication (Basic Auth, OAuth, client certificates). The attack surface includes arbitrary file reads through path traversal vulnerabilities if not mitigated, and arbitrary code execution through custom node installation.

### Built-In Protections

#### Origin Checking

`PromptServer` enforces `Origin` header validation for non-GET requests. This prevents cross-site request forgery (CSRF) where a malicious webpage triggers generation jobs on a locally-running instance. The check compares the incoming `Origin` against the server's configured listen address and port.

#### Path Traversal Mitigation

File upload and download handlers sanitize requested paths. The `view_image` endpoint (`GET /view`) restricts access to the configured output, input, and temp directories. Attempts to traverse outside these directories are rejected.

#### Dangerous MIME Type Forcing

File downloads for HTML, JavaScript, and CSS content are forced to `application/octet-stream`. This prevents stored XSS if a user uploads a file with a dangerous extension. MIME type handling lives in `utils/mime_types.py`.

#### Content-Security-Policy

When API nodes are disabled (`--disable-api-nodes`), the server sets a `Content-Security-Policy` header restricting script sources. When API nodes are enabled, CSP is relaxed to permit the external connections those nodes require.

#### CORS Control

CORS is disabled by default. Enable with `--enable-cors-header [ORIGIN]`. Setting to `*` allows all origins — appropriate only for local development.

#### TLS

Pass `--tls-keyfile` and `--tls-certfile` to enable HTTPS at the aiohttp layer. For production deployments, TLS termination at the reverse proxy is generally preferred (simpler certificate rotation, HTTP/2 support).

### Custom Node Security Risk

> [!CAUTION]
> Custom nodes are **arbitrary Python code** executed in the server process with full system access. Installing a custom node from an untrusted source is equivalent to running untrusted code on your machine. Vet custom node packages before installation. Use `--whitelist-custom-nodes` to restrict which packages load.

Custom nodes are loaded from `custom_nodes/` at startup. Prestartup scripts (`prestartup_script.py` in a custom node package) run before the main server with no restrictions. `--disable-all-custom-nodes` prevents all custom node loading for hardened deployments.

### API Node Security

`comfy_api_nodes/` nodes make outbound HTTPS calls to external services (BFL, Gemini, OpenAI, Kling, etc.). API keys are stored in user settings JSON files on disk. Use `--disable-api-nodes` to block all outbound calls from the server process.

### File Upload Security

- Uploaded files are written to the configured `input/` or `temp/` directory only.
- Hash comparison (BLAKE3/SHA256/MD5 per `--default-hashing-function`) detects and deduplicates re-uploads.
- MIME type forcing protects against serving uploaded HTML/JS as browser-executable content.
- Maximum upload size enforced by `--max-upload-size` (default 100 MB).

### Authentication Guidance (Not Built-In)

For shared or internet-facing deployments, implement authentication at the reverse proxy layer:

```nginx
# Nginx Basic Auth
location / {
    auth_basic "ComfyUI";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:8188;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
```

For device-level access control, use Tailscale or WireGuard. For single-developer use, an SSH tunnel (`ssh -L 8188:localhost:8188 user@server`) avoids any server-side auth configuration.

> [!WARNING]
> GitHub issue #10653 tracks the absence of built-in authentication. Until native auth lands, the reverse proxy approach is the only supported mitigation for shared deployments.

---

## 8. Developer Internals

### Execution Engine (`comfy_execution/`)

#### `graph.py` — DynamicPrompt

`DynamicPrompt` is the runtime representation of a submitted workflow. It:
- Wraps the raw prompt JSON (node ID → node definition mapping)
- Resolves node output links to form a directed acyclic graph (DAG)
- Supports dynamic node count (nodes can be added mid-execution by other nodes)
- Provides `get_node(id)`, `get_nodes_by_type(class_type)`, `has_node(id)`

Graph traversal for execution ordering is a topological sort of the DAG. Cycles are rejected at validation time.

#### `caching.py` — Cache System

The cache stores node output tensors keyed by the node's effective inputs. On re-execution, if inputs haven't changed, the cached output is returned without re-running the node.

| Class | Strategy |
|-------|---------|
| `CacheKeySet` (Classic) | Full output set; evicts least-recently-used |
| `LRUCacheKeySet` | Bounded LRU with configurable capacity |
| `RAMCacheKeySet` | Evicts under `psutil` RAM pressure threshold |
| `NoCacheKeySet` | No caching; always recomputes |

`CacheType` enum drives selection based on CLI flags.

#### `cache_provider.py`

Allows external plugins to register custom cache backends. The external provider is consulted before the built-in cache lookup.

#### `validation.py`

Validates node inputs before execution:
- Type checking against node's `INPUT_TYPES` specification
- Required vs optional input presence
- Link target existence validation
- Circular dependency detection

Validation runs on every `POST /prompt` call. Invalid prompts return HTTP 400 with a structured error body containing per-node error details.

#### `jobs.py` — JobStatus

Tracks each prompt's lifecycle:

```python
class JobStatus(Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
```

Job records are stored in the in-memory history dict and queryable via the Jobs API.

#### `progress.py`

Per-node progress state. Each executing node reports `(value, max)` progress. Aggregated and broadcast via WebSocket `progress` events to all connected clients.

#### `utils.py` — ExecutingContext

```python
@dataclass
class ExecutingContext:
    prompt_id: str
    node_id: str
```

Thread-local context injected during node execution. Nodes can access the current `prompt_id` and `node_id` for logging or conditional behavior.

### ML Engine (`comfy/`)

#### Model Loading Pipeline

1. `folder_paths.get_filename_list("checkpoints")` enumerates available models.
2. The user selects a model in a loader node (e.g., `CheckpointLoaderSimple`).
3. `comfy/sd.py:load_checkpoint_guess_config()` calls `model_detection.py` to identify the architecture.
4. `supported_models.py` returns the matching model class.
5. `comfy/model_management.py` loads weights onto the appropriate device.
6. A `ModelPatcher` wraps the model, tracking patches (LoRA, IP-Adapter, hooks).

#### `model_detection.py`

Heuristic detection based on weight key names and shapes. Detects: SD 1.x, SD 2.x, SDXL, Flux, AuraFlow, HunyuanDiT, Mochi, CogVideoX, StableAudio, and dozens more. Returns a `ModelType` enum value that drives class instantiation.

#### `model_patcher.py` — ModelPatcher / ModelPatcherDynamic

`ModelPatcher` is the central object passed between nodes. It wraps the raw PyTorch model and tracks:
- Applied patches (LoRA, hooks, IP-Adapter weights)
- Memory allocation state (which device holds the weights)
- Inference device vs offload device assignment

`ModelPatcherDynamic` extends this for dynamic VRAM (comfy-aimdo): individual layers can be moved to GPU on demand and evicted back to CPU between operations.

#### `memory_management.py`

Low-level VRAM accounting. Tracks all loaded models, their VRAM footprint, and decides when to evict. Key functions:

| Function | Purpose |
|----------|---------|
| `get_free_memory(device)` | Query available VRAM on device |
| `load_models_gpu(models)` | Move models to GPU, evicting as needed |
| `unload_model_clones(model)` | Remove duplicate model references |
| `soft_empty_cache()` | Release unreferenced CUDA tensors |
| `minimum_inference_memory()` | Minimum VRAM needed for an inference step |

#### `samplers.py` / `sample.py`

`sample.py` orchestrates the full denoising loop:
1. Prepares the noise tensor according to the selected scheduler.
2. Calls the selected sampler's `sample()` method.
3. Injects callbacks for per-step progress reporting.
4. Handles conditioning signals (positive/negative prompts, ControlNet, etc.).

`samplers.py` maintains the sampler registry. The k-diffusion library (`comfy/k_diffusion/`) provides Euler, Euler Ancestral, Heun, DPM++ variants (2M, 2S, SDE, 3M), UniPC, SA-Solver, DEIS, LCM, and others.

#### `lora.py` / `lora_convert.py`

`lora.py` loads LoRA weights and applies them to the model via weight decomposition (A×B matrices added to base weights). Supports multiple simultaneous LoRAs with individual `model_strength` and `clip_strength` parameters.

`lora_convert.py` converts between LoRA format variants (Kohya SD, Diffusers, ComfyUI native) on load.

#### `hooks.py`

A hook system for intercepting and modifying model behavior during inference without subclassing. Used by ControlNet, IP-Adapter, and advanced conditioning nodes. Hooks attach to `ModelPatcher` and are invoked at specific points in the forward pass (pre-attention, post-attention, etc.).

#### `ops.py`

Custom PyTorch operation overrides for precision management. Key components: `cast_bias_weight()` for mixed-precision weight handling, quantized linear layer implementations, and manual cast modules. FP8 compute paths are implemented here.

### Server Layer (`server.py`)

`PromptServer` is the single aiohttp application instance. Key responsibilities:

```python
class PromptServer:
    def __init__(self, loop):
        self.app = web.Application()
        self.sockets = {}          # clientId → WebSocket
        self.prompt_queue = None   # set after queue initialization
        self.number = 0            # monotonic execution counter
        ...
```

Route registration happens in `__init__` by assembling an `aiohttp.web.Application`. Managers (UserManager, ModelFileManager, etc.) register their routes via a module-level `routes` decorator that is merged into the server application on init.

#### Feature Flag Negotiation

On WebSocket connect, the server sends a `features` message listing server capabilities. The frontend uses this to enable/disable UI panels (asset panel, jobs API, etc.). Feature flags are also accessible via `GET /features`.

#### Security Middleware

```python
async def origin_check_middleware(request, handler):
    if request.method != 'GET':
        origin = request.headers.get('Origin', '')
        if not is_safe_origin(origin, request.host):
            raise web.HTTPForbidden()
    return await handler(request)
```

### Node System (`nodes.py`)

#### Node Registration

Nodes self-register using class-level attributes collected at import time:

```python
class MyNode:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "image": ("IMAGE",),
                "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.01}),
            },
            "optional": {
                "mask": ("MASK",),
            }
        }

    RETURN_TYPES = ("IMAGE",)
    RETURN_NAMES = ("result",)
    FUNCTION = "process"
    CATEGORY = "image/processing"
    OUTPUT_NODE = True  # True if this node produces a final output (images, video, etc.)

    def process(self, image, strength, mask=None):
        # ... processing logic ...
        return (result_image,)

NODE_CLASS_MAPPINGS = {"MyNode": MyNode}
NODE_DISPLAY_NAME_MAPPINGS = {"MyNode": "My Processing Node"}
```

#### Custom Node Loading

At startup, `nodes.py` scans `custom_nodes/` for directories and `.py` files. Each is imported via `importlib`. If the module defines `NODE_CLASS_MAPPINGS`, those classes are merged into the global node registry. Name collisions are logged as warnings; the first registered class wins.

Prestartup scripts (`custom_nodes/<package>/prestartup_script.py`) execute before the main scan. Uses: dependency installation, compatibility patching, platform detection.

#### pyproject.toml Configuration (`comfy_config/`)

Custom node packages can include a `pyproject.toml` with a `[tool.comfy]` section:

```toml
[tool.comfy]
PublishedBy = "author_name"
Tags = ["image", "controlnet"]
Version = "1.2.3"
License = "MIT"
```

`comfy_config/config_parser.py` reads this and exposes metadata to the manager UI.

### Application Managers (`app/`)

#### `app/app_settings.py` — SettingsManager

Stores user settings as JSON in `<user-directory>/comfy.settings.json`. Settings have a server-defined schema with defaults. Changes via `POST /settings` are immediately persisted to disk.

#### `app/frontend_management.py` — FrontendManager

In production, serves the bundled frontend from the package. With `--front-end-root`, serves from a local directory for frontend development. With `--front-end-version`, downloads and caches a specific frontend build from the release feed.

#### `app/user_manager.py` — UserManager

In single-user mode: all data goes to `<user-directory>/`.
In multi-user mode (`--multi-user`): each user gets `<user-directory>/<user-id>/` isolation. `get_request_user_id(request)` extracts the current user from the session.

#### `app/model_manager.py` — ModelFileManager

Wraps `folder_paths` to provide REST-friendly model enumeration. Handles model preview image serving — looks for `<model_name>.preview.png` or `.preview.jpg` alongside the model file.

#### `app/custom_nodes/` — CustomNodeManager

Tracks installed custom node packages, their metadata (via `pyproject.toml`), and enabled/disabled state. Backs the `--enable-manager` UI. Provides install/uninstall operations.

#### `app/subgraph_manager.py` — SubgraphManager

Manages "global subgraphs" — reusable node groups saved server-side and available across all workflows. Backed by JSON files in the user directory.

### Internal Routes (`api_server/`)

`api_server/routes/internal/` provides the `/internal/` prefix routes:

| Route | Handler | Purpose |
|-------|---------|---------|
| `GET /internal/logs` | `get_logs` | JSON log buffer |
| `GET /internal/logs/raw` | `get_raw_logs` | Plain text logs |
| `PATCH /internal/logs/subscribe` | `subscribe_logs` | SSE live log stream |
| `GET /internal/folder_paths` | `get_folder_paths` | All registered folder paths |
| `GET /internal/files/{directory_type}` | `get_files` | File listing |

`api_server/services/terminal_service.py` captures stdout/stderr into a circular buffer and serves it to the log endpoints.

### Versioned Node API (`comfy_api/`)

The `comfy_api/` package provides a stable API surface for node authors, insulating them from internal changes:

| Module | Purpose |
|--------|---------|
| `comfy_api/latest/` | Current API version (mirrors internal classes) |
| `comfy_api/v0_0_2/` | Stable v0.0.2 surface |
| `comfy_api/v0_0_1/` | Legacy v0.0.1 surface |
| `comfy_api/feature_flags.py` | Feature flag definitions |

Node authors should import from `comfy_api.latest` for forward compatibility. Pinning to a versioned subpackage provides long-term stability guarantees.

### Built-In Node Library (`comfy_extras/`)

The 99 modules in `comfy_extras/` implement all built-in nodes:

| Category | Module Examples |
|----------|----------------|
| Sampling | `nodes_custom_sampler.py`, `nodes_differential_diffusion.py`, `nodes_sd3.py`, `nodes_flux.py` |
| Image processing | `nodes_images.py`, `nodes_compositing.py`, `nodes_upscale_model.py`, `nodes_rebatch.py` |
| Video | `nodes_video_model.py`, `nodes_hunyuan_video.py`, `nodes_mochi.py`, `nodes_ltxv.py` |
| Audio | `nodes_audio.py`, `nodes_stable_audio.py` |
| 3D | `nodes_3d.py`, `nodes_hunyuan3d.py` |
| Conditioning | `nodes_cond.py`, `nodes_gligen.py`, `nodes_ip_adapter.py`, `nodes_photomaker.py` |
| ControlNet | `nodes_controlnet.py` |
| Masking | `nodes_mask.py`, `nodes_mask_sequence.py` |
| Model loading | `nodes_model_advanced.py`, `nodes_hooks.py`, `nodes_loras.py` |
| Latents | `nodes_latent.py`, `nodes_latent_noise.py` |
| Primitives | `nodes_primitive.py`, `nodes_string.py` |
| Advanced techniques | `nodes_cfg_rescale.py`, `nodes_freelance.py`, `nodes_pag.py` |

### External API Nodes (`comfy_api_nodes/`)

32 node modules integrate cloud AI services:

| Service | Nodes Provided |
|---------|---------------|
| Black Forest Labs (BFL/Flux API) | Image generation, inpainting |
| Google Gemini | Image understanding, multimodal generation |
| OpenAI (DALL-E, GPT-4V) | Image generation, description |
| Kling (Kuaishou) | Video generation |
| Luma Dream Machine | Video generation |
| Runway | Video generation |
| Pika | Video generation |
| Stability AI | Image generation |
| Ideogram | Text-heavy image generation |
| Recraft | Vector and image generation |
| fal.ai | Hosted model inference |
| MiniMax | Multimodal generation |

The `comfy_api_nodes/apis/` subdirectory contains typed HTTP client wrappers for each service. All connections are outbound HTTPS. Blocked entirely by `--disable-api-nodes`.

### Asset Management System (`app/assets/`)

```
app/assets/
├── asset_service.py      Business logic layer
├── asset_api.py          Route handlers (/api/assets/*)
├── db_queries.py         SQLAlchemy query helpers
├── scan_service.py       Background directory scanner
├── metadata_service.py   Metadata extraction (EXIF, PNG chunks)
└── models.py             SQLAlchemy ORM models
```

**Data model:**
- `Asset`: file record (path, hash, size, metadata)
- `AssetReference`: link between asset hash and a specific file location
- `Tag`: free-form tag string
- `AssetTag`: many-to-many join table

**Deduplication:** BLAKE3 hash of file contents. If the same file exists in multiple locations, one `Asset` record links to multiple `AssetReference` rows. `HEAD /api/assets/hash/{hash}` checks existence without downloading.

**Background scanning:** `POST /api/assets/seed` starts `scan_service.py` in a background thread. Walks `output/` and `input/` directories, hashes new files, updates the database. Large output directories can take minutes.

### Database Layer (`app/database/`)

```python
# Connection setup (WAL mode, FK enforcement)
engine = create_engine(
    database_url,
    connect_args={"check_same_thread": False},
)

@event.listens_for(engine, "connect")
def set_sqlite_pragma(conn, record):
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
```

Alembic migration chain applied automatically on startup:

```
base → assets → merge_to_asset_references → add_metadata_job_id
```

### Code Patterns and Conventions

#### Async Node Execution

The execution engine supports both sync and async node `FUNCTION` methods. Async nodes are awaited via `asyncio.run_coroutine_threadsafe()` from the background worker thread.

#### Type System

Node I/O types are strings by convention. Standard types: `IMAGE`, `LATENT`, `MODEL`, `VAE`, `CLIP`, `CONDITIONING`, `MASK`, `STRING`, `INT`, `FLOAT`, `BOOLEAN`. Custom types are declared by returning any unique string not in the standard set.

#### Widget-to-Input Promotion

Any `INPUT_TYPES` entry can be forced to a connector (wire) by the user via right-click. The `"forceInput": True` hint makes connector mode the default for that input.

#### Hidden Inputs

Nodes may declare `"hidden"` inputs that receive server-injected values at runtime:

```python
"hidden": {
    "prompt": "PROMPT",          # full prompt dict for current run
    "extra_pnginfo": "EXTRA_PNGINFO",  # metadata to embed in PNG outputs
    "unique_id": "UNIQUE_ID",    # this node's ID in the graph
}
```

#### Error Handling Pattern

Node execution errors are caught by `PromptExecutor`. A node raising an exception causes the entire prompt to fail with that node's error message. The error is stored in history and broadcast via WebSocket `status` event. Nodes should raise plain `Exception` for user-visible errors.

---

## 9. Integration Points

### Complete API Endpoint Reference

#### Core Workflow

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/ws` | WebSocket connection (`?clientId=<uuid>`) |
| `GET` | `/` | Frontend index.html |
| `POST` | `/prompt` | Submit workflow graph for execution |
| `GET` | `/prompt` | Queue info (running + pending counts) |
| `GET` | `/queue` | Full queue state |
| `POST` | `/queue` | `{"clear": true}` or `{"delete": ["id"]}` |
| `POST` | `/interrupt` | Interrupt execution (`{"prompt_id": "..."}` optional) |
| `POST` | `/free` | Free GPU/memory (`{"unload_models": true, "free_memory": true}`) |
| `GET` | `/history` | All execution history |
| `GET` | `/history/{prompt_id}` | Single job history entry |
| `POST` | `/history` | `{"clear": true}` or `{"delete": ["id"]}` |

#### Resource Discovery

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/embeddings` | List available embedding files |
| `GET` | `/models` | List model type folder names |
| `GET` | `/models/{folder}` | List model files in folder |
| `GET` | `/extensions` | List loaded frontend extensions |
| `GET` | `/object_info` | All registered node class definitions |
| `GET` | `/object_info/{node_class}` | Single node definition (inputs, outputs, category) |
| `GET` | `/features` | Server feature flags |
| `GET` | `/system_stats` | System info (GPU, RAM, Python version) |
| `GET` | `/workflow_templates` | List blueprint workflow templates |
| `GET` | `/i18n` | Internationalization strings |
| `GET` | `/node_replacements` | Node class rename/alias mappings |
| `GET` | `/global_subgraphs` | List global subgraph definitions |
| `GET` | `/global_subgraphs/{id}` | Single subgraph definition |

#### File Operations

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/upload/image` | Upload image (`?type=input\|temp\|output&subfolder=...`) |
| `POST` | `/upload/mask` | Upload mask image with composite |
| `GET` | `/view` | Download file (`?filename=...&type=...&subfolder=...`) |
| `GET` | `/view_metadata/{folder_name}` | Safetensors metadata (`?filename=...`) |

#### Settings

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/settings` | All settings for current user |
| `GET` | `/settings/{id}` | Single setting by ID |
| `POST` | `/settings` | Update multiple settings (JSON body) |
| `POST` | `/settings/{id}` | Update single setting |

#### User Management

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/users` | List users |
| `POST` | `/users` | Create user (`{"username": "..."}`) |
| `GET` | `/userdata` | List user data files (`?path=...`) |
| `GET` | `/v2/userdata` | List user data (v2, with pagination) |
| `GET` | `/userdata/{file}` | Get user data file |
| `POST` | `/userdata/{file}` | Save user data file |
| `DELETE` | `/userdata/{file}` | Delete user data file |
| `POST` | `/userdata/{file}/move/{dest}` | Move/rename user data file |

#### Jobs API

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/jobs` | List jobs with filtering, sorting, pagination |
| `GET` | `/api/jobs/{job_id}` | Get single job by ID |

#### Experimental Model Management

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/experiment/models` | List all model folders |
| `GET` | `/experiment/models/{folder}` | List models in folder with metadata |
| `GET` | `/experiment/models/preview/...` | Get model preview image |

#### Internal Routes (`/internal/`)

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/internal/logs` | Buffered application logs (JSON) |
| `GET` | `/internal/logs/raw` | Raw log text |
| `PATCH` | `/internal/logs/subscribe` | Subscribe to log stream (SSE) |
| `GET` | `/internal/folder_paths` | All registered folder path mappings |
| `GET` | `/internal/files/{directory_type}` | File listing for directory type |

#### Asset Management (`/api/assets/`, requires `--enable-assets`)

| Method | Path | Description |
|--------|------|-------------|
| `HEAD` | `/api/assets/hash/{hash}` | Check if asset with hash exists |
| `GET` | `/api/assets` | List assets (filtering, pagination) |
| `GET` | `/api/assets/{id}` | Get asset details by ID |
| `GET` | `/api/assets/{id}/content` | Download asset file |
| `POST` | `/api/assets/from-hash` | Create asset reference from existing hash |
| `POST` | `/api/assets` | Upload new asset |
| `PUT` | `/api/assets/{id}` | Update asset metadata |
| `DELETE` | `/api/assets/{id}` | Delete asset record |
| `GET` | `/api/tags` | List all tags |
| `POST` | `/api/assets/{id}/tags` | Add tags to asset |
| `DELETE` | `/api/assets/{id}/tags` | Remove tags from asset |
| `GET` | `/api/assets/tags/refine` | Refined tag suggestions |
| `POST` | `/api/assets/seed` | Start background asset scan |
| `GET` | `/api/assets/seed/status` | Check scan progress |
| `POST` | `/api/assets/seed/cancel` | Cancel running scan |
| `POST` | `/api/assets/prune` | Mark missing files as absent |

### Programmatic API Client Pattern

```python
import asyncio
import json
import uuid
import websockets
import aiohttp

SERVER = "http://localhost:8188"
WS_SERVER = "ws://localhost:8188"

async def submit_and_wait(workflow: dict) -> dict:
    """Submit a workflow and wait for completion via WebSocket."""
    client_id = str(uuid.uuid4())

    async with aiohttp.ClientSession() as session:
        # Submit workflow
        async with session.post(f"{SERVER}/prompt", json={
            "prompt": workflow,
            "client_id": client_id
        }) as resp:
            result = await resp.json()
            prompt_id = result["prompt_id"]

    # Wait for completion via WebSocket
    async with websockets.connect(f"{WS_SERVER}/ws?clientId={client_id}") as ws:
        async for message in ws:
            if isinstance(message, bytes):
                continue  # skip binary preview frames
            data = json.loads(message)
            if data["type"] == "executed":
                if data["data"]["prompt_id"] == prompt_id:
                    return data["data"]
```

### Minimal Workflow API Payload

```json
{
  "client_id": "550e8400-e29b-41d4-a716-446655440000",
  "prompt": {
    "1": {
      "class_type": "CheckpointLoaderSimple",
      "inputs": {
        "ckpt_name": "v1-5-pruned-emaonly.safetensors"
      }
    },
    "2": {
      "class_type": "CLIPTextEncode",
      "inputs": {
        "text": "a photo of a cat",
        "clip": ["1", 1]
      }
    },
    "3": {
      "class_type": "CLIPTextEncode",
      "inputs": {
        "text": "ugly, blurry",
        "clip": ["1", 1]
      }
    },
    "4": {
      "class_type": "EmptyLatentImage",
      "inputs": {
        "width": 512,
        "height": 512,
        "batch_size": 1
      }
    },
    "5": {
      "class_type": "KSampler",
      "inputs": {
        "model": ["1", 0],
        "positive": ["2", 0],
        "negative": ["3", 0],
        "latent_image": ["4", 0],
        "seed": 42,
        "steps": 20,
        "cfg": 7.0,
        "sampler_name": "euler_ancestral",
        "scheduler": "karras",
        "denoise": 1.0
      }
    },
    "6": {
      "class_type": "VAEDecode",
      "inputs": {
        "samples": ["5", 0],
        "vae": ["1", 2]
      }
    },
    "7": {
      "class_type": "SaveImage",
      "inputs": {
        "images": ["6", 0],
        "filename_prefix": "api_output"
      }
    }
  }
}
```

Link format: `["<source_node_id>", <output_index>]`. CheckpointLoaderSimple outputs: index 0 = MODEL, index 1 = CLIP, index 2 = VAE.

### Custom Node Development Reference

#### Minimal Node Template

```python
# custom_nodes/my_package/my_node.py

class TextUpperNode:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "text": ("STRING", {"multiline": False}),
            },
            "optional": {
                "prefix": ("STRING", {"default": ""}),
            }
        }

    RETURN_TYPES = ("STRING",)
    RETURN_NAMES = ("upper_text",)
    FUNCTION = "execute"
    CATEGORY = "utils/text"

    def execute(self, text: str, prefix: str = ""):
        return (prefix + text.upper(),)


NODE_CLASS_MAPPINGS = {
    "TextUpperNode": TextUpperNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
    "TextUpperNode": "Text to Uppercase",
}
```

#### Web Extension Registration

Place a JS file in `<custom_node_package>/web/extensions/`. The server auto-discovers and serves it.

```javascript
// web/extensions/my_extension.js
import { app } from "../../scripts/app.js";

app.registerExtension({
    name: "my_package.my_extension",
    async setup() {
        // runs once on frontend load
    },
    async beforeRegisterNodeDef(nodeType, nodeData, appInstance) {
        // runs for each node type definition
    },
});
```

### Testing Guide

#### Test Infrastructure

```bash
# Install test dependencies
pip install pytest pytest-asyncio pytest-aiohttp websocket-client

# Unit tests (no running server required)
pytest tests-unit/

# Integration tests (requires running server)
export COMFYUI_SERVER=http://localhost:8188
pytest tests/execution/

# Inference tests (runs actual generation — slow, requires models)
pytest tests/inference/

# Quality comparison tests
pytest tests/compare/

# CI quick validation
python main.py --quick-test-for-ci
```

#### Test Category Reference

| Directory | Type | Requirements | Notes |
|-----------|------|-------------|-------|
| `tests-unit/` | Unit | Python, pytest | No server or models needed |
| `tests/execution/` | Integration | Running ComfyUI | Uses WebSocket + REST |
| `tests/inference/` | E2E generation | Running + models | Slow; validates actual outputs |
| `tests/compare/` | Quality diff | Two instances + reference | For regression detection |

> [!NOTE]
> The `tests/` integration suite uses `websocket-client` for synchronous WebSocket communication. The `tests-unit/` suite uses `pytest-asyncio` for async test support and `pytest-aiohttp` for aiohttp test client fixtures. Both must be installed for the full suite to run.

#### Unit Test Pattern

```python
# tests-unit/test_my_node.py
import pytest
from custom_nodes.my_package.my_node import TextUpperNode

def test_basic_upper():
    node = TextUpperNode()
    result = node.execute("hello world")
    assert result == ("HELLO WORLD",)

def test_with_prefix():
    node = TextUpperNode()
    result = node.execute("hello", prefix=">> ")
    assert result == (">> HELLO",)

def test_empty_string():
    node = TextUpperNode()
    result = node.execute("")
    assert result == ("",)
```

### Troubleshooting Reference

> [!NOTE]
> Most runtime errors appear simultaneously in the browser console (F12) and the server terminal. Start there before querying the `/internal/logs` endpoint. Node-level errors also appear on the node itself in the workflow editor.

| Symptom | Likely Cause | Resolution |
|---------|-------------|------------|
| `CUDA out of memory` | VRAM exhausted | Use `--lowvram`, reduce batch size, switch to FP8/BF16 |
| `RuntimeError: Expected all tensors on same device` | Mixed device tensors | Usually a LoRA/model mismatch; check node connections |
| Node turns red in UI | Node raised exception | Hover for error; check server logs |
| Custom node not appearing | Import error at load | Check terminal for `ImportError`; install missing packages |
| WebSocket not connecting | Port blocked or server crash | `curl http://localhost:8188/system_stats` to test; inspect terminal |
| `Prompt outputs failed validation` | Invalid node wiring | Check `GET /object_info/{class}` for correct input types |
| Models not listed | Path not configured | Verify `extra_model_paths.yaml`; check `GET /internal/folder_paths` |
| Slow preview generation | `taesd` decode overhead | Switch to `--preview-method latent2rgb` |
| Upload rejected with 413 | File exceeds size limit | Increase `--max-upload-size` |
| SQLite "database is locked" | Concurrent write access | Check no other process holds `comfy.db`; WAL mode normally prevents this |
| Black/grey output images | VAE precision mismatch | Try `--fp32-vae`; some models are VAE-sensitive |
| Sampler not found | Typo or custom sampler not loaded | Check `GET /object_info/KSampler` for available sampler names |

> [!TIP]
> For persistent "model not found" errors: `curl http://localhost:8188/models/<folder>` shows exactly what the server has indexed. The folder scan runs once at startup — add models, then restart the server.

> [!WARNING]
> Setting `--deterministic` while using xformers will error — xformers attention is inherently non-deterministic. Pair `--deterministic` with `--use-pytorch-cross-attention` or `--disable-xformers`.

> [!CAUTION]
> The `--fast` flag enables experimental optimizations that may produce incorrect outputs on some model+hardware combinations. Do not use in production or for reproducibility-sensitive workflows.

---

*Quick Reference: Common Startup Commands*

```bash
# Development (local only, verbose)
python main.py --verbose DEBUG

# LAN sharing, NVIDIA GPU, 16 GB VRAM
python main.py --listen 0.0.0.0 --normalvram --preview-method taesd

# Server with asset tracking and multi-user
python main.py --listen 127.0.0.1 --enable-assets --multi-user --log-stdout

# Headless batch processing (maximum throughput)
python main.py --dont-print-server --cache-classic \
    --disable-all-custom-nodes --disable-manager-ui

# Reproducible output (for A/B testing)
python main.py --deterministic --use-pytorch-cross-attention --disable-xformers

# Memory-constrained workstation (6 GB VRAM)
python main.py --lowvram --fp8_e4m3fn-unet --bf16-vae \
    --preview-method latent2rgb

# CI validation (start, check, exit)
python main.py --quick-test-for-ci
```

---

_Generated May 2026 from Comfy-Org/ComfyUI at c033bbf. 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._
