Skip to content

Module inventory

Everything lives under src/comfy_env/. Line counts are wc -l at fe9ff74 (2026-09-12): 46 files, 19,264 lines. Layering, low to high: config / settings / debug / pixi and the five memory-floor leaves -> detection -> packages -> environment -> install + isolation -> cli / __init__ (the full edge list is on the import layering page).

Top level

File ~LoC Responsibility
__init__.py 101 Public facade. Exports the five-name public API (install, setup_env, register_nodes, copy_files, register_serializer); defines __version__ from installed metadata; imports settings for its tombstone side effect. Internals are deliberately NOT re-exported -- __getattr__ turns reaching for one into a signpost naming where it lives.
cli.py 578 The comfy-env console entrypoint: init, install, info, settings, gc (cleanup is a deprecated alias). Settings is a single-tab (Debug) curses TUI with a plain-text fallback, persisting to ~/.comfy-env/debug.env.
settings.py 79 Tombstones for settings removed in 0.4.25: a falsy COMFY_ENV_ISOLATE / COMFY_ENV_INSTALL_ISOLATED, or a truthy COMFY_ENV_AUTO_INSTALL, raises at import with a self-locating message. No settings file, no TOML key mapping: the live settings are plain COMFY_ENV_* env vars read at their point of use.
debug.py 65 Granular debug-category switches (SERIALIZE, IPC, WORKER, VRAM, ...), env var or ~/.comfy-env/debug.env (env var wins). Workers cannot import it (different venv) and parse env vars directly.
pixi.py 111 Provisions the pinned pixi binary (version + sha256 vendored in the file) into the comfy-env-owned ~/.comfy-env/pixi/<version>/ -- deliberately not ~/.pixi, which belongs to the user's own install. Checksum mismatch refuses to install.

config/

File ~LoC Responsibility
config/__init__.py 189 The entire config layer. Loads comfy-env.toml / comfy-env-root.toml via tomli into ComfyEnvConfig (dict subclass with dot access). In an env file, unknown tables are not errors -- they land in pixi_passthrough and the generator forwards them verbatim into the generated pixi.toml, refusing only the compiler-owned keys (ADR-0013). The root file is the opposite: a closed schema that rejects anything outside [node_packs]/[types] (see the config reference).

detection/ -- pure functions, no side effects

File ~LoC Responsibility
detection/__init__.py 84 Re-exports; platform helpers (is_windows() etc.) and the (os, machine) -> pixi platform table.
detection/backend.py 70 Which accelerator torch actually uses; ground truth is torch's local version label (2.5.0+cu128 -> cuda). Fixes a ROCm misdetection where torch.cuda.is_available() is True but torch.version.cuda is None.
detection/cuda.py 132 CUDA version probing (pixi info --json virtual packages, then torch metadata) and bootstrap-interpreter probing: host torch / torchvision / torchaudio versions, macOS min version.
detection/gpu.py 270 GPU enumeration with a 4-method fallback chain (NVML -> nvidia-smi -> PyTorch -> sysfs; on Windows nvidia-smi -> NVML -> PyTorch, because a pynvml or torch DLL failure can crash the host), 60s TTL cache; compute capability -> architecture name -> recommended CUDA version.

packages/ -- dependency sourcing and manifest generation

File ~LoC Responsibility
packages/cuda_wheels.py 433 Resolves prebuilt CUDA wheel URLs from the cuda-wheels GitHub Pages simple index; retries TCP resets with a real User-Agent; falls back to the GitHub Releases API. Derives torch family pins and platform tags.
packages/toml_generator.py 683 The manifest compiler: each comfy-env.toml -> a per-env pixi.toml. One self-contained feature per env, always named node (pixi reserves default), in a single environment default with no-default-feature = true; torch-family pins replicated into every feature; the host's comfy-aimdo / comfy-kitchen pins read from the running host (read_host_pin) and substituted for, or injected beside, the pack's own; CUDA wheels inlined as URL pypi-dependencies.
packages/node_packs.py 159 Installs other ComfyUI nodepacks declared in [node_packs]: git clone (tag / branch / commit), or a Comfy Registry zip (api.comfy.org), then runs the peer's install.py if it has one. No requirements.txt step. Recurses into the peer's own [node_packs].

environment/ -- paths and platform workarounds

File ~LoC Responsibility
environment/cache.py 794 Workspace layout authority (v0.4 per-env manifests; no v0.3 back-compat). Env naming and the ABI-tagged directory name (<name>_<abi-tag>), workspace root resolution (Windows LocalAppData vs ~/.ce, COMFY_ENV_ROOT override), env stamping and validation -- validate_env_stamp refuses on a mismatched abi_tag or source only; the comfy-env version, torch pin, ComfyUI version and host pins are recorded in the stamp for diagnostics and drift notes, never validated -- and ComfyUI dir discovery incl. the Desktop app.
environment/setup.py 73 The prestartup hook setup_env(): faulthandler, workspace banner, libomp dedupe. (The parent-side shareable-pool hook was removed in 0.4.22; the base_directory fill-in in 0.4.27.)
environment/runtime.py 84 The RuntimeEnv record behind comfy-env info --json.
environment/libomp.py 151 macOS-only: symlinks redundant bundled libomp.dylib copies to torch's canonical one (multiple loaded copies corrupt OMP state and SIGSEGV).

install/ -- build time

File ~LoC Responsibility
install/__init__.py 73 install() entrypoint; infers the caller's directory via inspect.stack(); runs the [node_packs] step, then install_workspace. No pip step. Importing it also sets PYTHONUNBUFFERED=1 and switches sys.stdout / sys.stderr to line buffering for the whole process.
install/plugin.py 26 Plugin half: one function, _install_node_packs, which lists what it would clone under --dry-run and otherwise calls install_node_packs. Nothing re-runs the plugin's requirements.txt.
install/workspace.py 1,053 Workspace half: discover configs, resolve bootstrap torch pin (CPU-only without GPU), pick wheel combo, hash configs for change detection, write per-env pixi.toml, run pixi install per env, stamp.
install/progress.py 244 Live pixi install progress from the filesystem: counts conda-meta/*.json and site-packages/*.dist-info against what pixi.lock declares, because pixi suppresses its own bar when stderr is piped.
install/helpers.py 126 Cross-platform utilities: _rmtree via robocopy-mirror-from-empty-dir (defeats Windows long-path/read-only deletes), uv discovery and platform patch, tee logging, streaming subprocess runner.

isolation/ -- runtime

File ~LoC Responsibility
isolation/wrap.py 576 Runtime entry point: register_nodes(), config discovery, per-pack env resolution, proxy synthesis, and the in-process fallback when an env is absent. The worker pool moved to pool.py.
isolation/pool.py 1,936 The worker pool and the whole host side of the memory floor: one worker per env with a generation counter, the VRAM budget callback workers call back into, the reserve publish, the stand-in registration into ComfyUI's ledger, the idle sweep and the pressure hook, atexit and signal cleanup, stale-worker reaping.
memory_manager.py 1,038 The worker side of the floor. Enables comfy-aimdo, the release ladders (full_release, partial_release, release_pins), the per-node and per-prompt cast boundaries, prompt marks, and the pin census the host reads. Staged into the worker, so it must parse on the oldest worker Python.
state_sync.py 892 Pure arithmetic and policy, imports neither torch nor comfy: the residency census and its sequence protocol, the admission ceiling, the idle and pressure release planners, the prompt-epoch marks, and the node self state wire in both directions.
contract.py 287 The upstream coupling contract as data. Twenty-four entries with per-entry severity and tier (floor / paged / shared) and the version each appeared in, checked against the real tree at startup; a fatal gap refuses to start. Imports nothing; staged into workers.
mirrored_args.py 197 The host-to-worker CLI flag mirror, as an allowlist. Decides a worker's dtype, attention backend and pinning behaviour, with a global kill switch and a per-flag escape hatch.
reserve.py 168 The reserve arithmetic, pure: what a worker is charged, what gets published, what is forwarded into the pager's headroom, and the discount that stops a requester being charged for its own load twice.
isolation/errors.py 88 The closed error vocabulary that crosses the wire, including translating a worker OOM back into the host's real exception class.
isolation/subenv.py 121 Per-platform isolation env construction.
isolation/procgroup.py 66 Runs a child in its own process group so a timeout kills the whole tree (killpg on POSIX, taskkill /T on Windows); the metadata scan under pixi run uses it. Stdlib only.
server_stub.py 110 Top-level, stdlib-only stand-in for ComfyUI's server module, installed into sys.modules in workers and in the scan. PromptServer.instance forwards send_sync, send_progress_text and carries the host's client_id; any other attribute raises an AttributeError naming what is and is not forwarded.
isolation/metadata.py 1,891 Spawns a short-lived subprocess in the isolation env to write out node metadata as JSON (INPUT_TYPES, ...), then synthesizes proxy classes in the parent. Handles ComfyUI v3 schema, dynamic combo providers (live model/input-dir dropdowns), synthesized validation, and a scan cache keyed on every .py file's st_mtime_ns. Both proxy shapes call the worker through one _call_in_worker.
isolation/model_patcher.py 401 SubprocessModelPatcher: bridges worker-resident GPU models into ComfyUI's VRAM manager; eviction IPCs the worker to move the model to CPU. Only module importing ComfyUI at module scope.
isolation/tensor_utils.py 64 IPC preparation (clone a tensor whose storage came from another process so it can be re-exported), release_tensor() via madvise(MADV_DONTNEED).

isolation/workers/

File ~LoC Responsibility
workers/base.py 73 Worker ABC (call(), shutdown()), WorkerError, and InterruptRequested (raised parent-side, in the progress-callback handler, when the user cancels the run, so the callback reply carries a typed error_kind the worker can act on).
workers/subprocess.py 1,371 Parent-side driver: spawns the isolated interpreter, materializes _persistent_worker.py into a temp dir with copies of _ipc_shared.py and the staged leaves (memory_manager.py, state_sync.py, mirrored_args.py, contract.py, server_stub.py), handshake, health checks, request/response with timeouts, bidirectional callbacks, exit diagnostics.
workers/_ipc_parent.py 526 Parent-side IPC internals: socket creation (AF_UNIX, TCP fallback), SocketTransport (thread-safe length-prefixed JSON), all tensor serialization strategies, shm helpers.
workers/_ipc_shared.py 943 Deliberately stdlib-only so it can be copied beside the worker script and imported in the isolated venv: CUDA mem-pool ctypes bindings, SCM_RIGHTS FD passing, memfd helpers, generic shm walker.
workers/_persistent_worker.py 2,762 The worker program. Never imported by the parent -- read as text (subprocess.py) and run by the isolated interpreter with _ipc_shared.py and the staged leaves copied alongside (the shared serialization core; the worker keeps only thin side-specific wrappers). Faulthandler + watchdog, the print/logger hijack, the worker half of the memory floor, and the main loop -- one 1,957-line main().