The home-side GPU worker for the MEFAI image-to-video pipeline: ComfyUI in a pinned Docker container, a Python daemon that polls an outbound-only queue, and zero inbound ports opened on the home network.
This repository is the companion to mefai-dev/mefaihomevideoai (the cloud side: React UI, FastAPI job API, Postgres queue, signed media URLs). Together they show how a creative AI product runs a consumer GPU at home without ever renting cloud compute and without ever exposing that GPU to the public internet.
flowchart LR
subgraph Cloud[Cloud API]
Q[(Postgres queue)]
API[/worker/claim<br/>/worker/heartbeat<br/>/worker/result/]
Q --> API
end
subgraph Home[Home PC]
W[mefai-worker<br/>Python daemon]
C[ComfyUI container<br/>loopback 127.0.0.1:8188]
G[NVIDIA GPU]
C --> G
W -->|HTTP| C
end
W -.->|outbound HTTPS<br/>bearer token| API
API -.->|job payload| W
W -.->|signed PNG/MP4| API
Every arrow is initiated from the home PC. The router opens no inbound port. The home firewall stays closed. If the home side is unreachable, the queue simply stays full until the worker comes back.
- Outbound-only NAT traversal - the worker polls the cloud; the cloud never dials home. No tunnels, no port forwarding, no reverse proxies, no STUN, no UPnP, no cloudflared, no ngrok.
- Pull-based queue - the cloud writes a
jobsrow and goes back to sleep. The worker claims with a transactionalSELECT ... FOR UPDATEthe next time it polls. No push, no webhook, no fragile TCP session. - Idempotent result upload - every job carries a server-side id; the
final
POST /worker/resultis safe to retry on transient network failure without duplicating artifacts. - Heartbeat-based liveness - a background task pings
/worker/heartbeatevery 30 s. If the cloud watchdog misses N beats, it reclaims the job and makes it eligible for a fresh claim. A worker that crashes mid-render never leaks arunningrow forever. - Token lives in
.envonly - bearer token is loaded via pydantic-settings from theMEFAI_WORKER_WORKER_TOKENenv, wrapped inSecretStr, and passed through a structlog processor that redacts it from every log record (including nested dicts, lists, URLs, tuples). The repo ships an.env.examplewithCHANGE_MEplaceholders; the real.envis gitignored. - ComfyUI is loopback-only - the container's host-side publish is
hard-pinned to
127.0.0.1ininfra/compose.yml. A forgotten firewall rule cannot expose ComfyUI to the LAN. - Blackwell-capable torch bump - the base image's torch 2.4 + cu121
cannot run on sm_120 GPUs (RTX 50-series).
infra/Dockerfilelayers a torch 2.11 + cu128 wheel on top so the stack runs on both Ampere and Blackwell. - Wan 2.2 TI2V-5B and Wan 2.2 I2V workflows ship in-tree - the 48-channel unified TI2V path and the image-to-video variant are both captured as ComfyUI JSON graphs and patched at job claim time with the per-job prompt, seed, and input image URL.
- Auto-restart as a first-class install path -
setup/carries a scripted NSSM installer for Windows (service auto-start, 10 MiB log rotation, restart-on-crash) and a systemd unit example for Linux. - Typed, tested, gated -
pydantic-settingsfor configuration,httpx.MockTransportfor the client test suite,mypy --strictclean,ruff checkclean,ruff formatclean. The unit suite hits roughly 58% line coverage end-to-end; the orchestration modules (main.py,video.py) are exercised by manual smoke tests rather than unit-mocked.pyproject.tomlenforces a floor so coverage cannot regress unnoticed.
| Path | What's in it |
|---|---|
src/mefai_worker/ |
Python package: polling loop, ComfyUI client, HTTPS client, heartbeat, prompts, video orchestrator, image/overlay helpers |
workflows/ |
ComfyUI workflow graphs the worker submits (SDXL Turbo + Lightning, Wan 2.2 TI2V-5B, Wan 2.2 I2V, CogVideoX reference) |
infra/ |
compose.yml and Dockerfile for the ComfyUI container (loopback-only, NVIDIA runtime, Blackwell-capable torch) |
setup/ |
SETUP.md step-by-step, bootstrap scripts, NSSM installer, smoke tests, GPU verifier |
tests/ |
pytest suite (settings, prompts, workflow patch, client retries, comfy client, image hashing, overlay) |
assets/logos/ |
per-symbol PNG overlays (DEFAULT.png is the fallback) |
See setup/SETUP.md for the full walkthrough. Short version:
# 1. Install Docker Engine + NVIDIA Container Toolkit on the host.
# 2. Bring up ComfyUI in its pinned container.
cp .env.example .env # then fill in CHANGE_ME values
cd infra && docker compose up -d
# 3. Install the Python worker in a venv.
cd ..
python -m venv .venv
# PowerShell: .venv\Scripts\Activate.ps1
# bash: source .venv/bin/activate
pip install -e '.[dev]'
# 4. Run it (foreground).
python -m mefai_worker
# 5. Promote to a service (Windows NSSM or Linux systemd).
# See setup/SETUP.md for both paths.ruff check src tests
ruff format --check src tests
mypy --config-file mypy.ini
pytest --covTargets: ruff clean, mypy strict clean, coverage >= 55% (the floor
enforced by pyproject.toml; current run sits around 58%).
- No inbound ports. All traffic is initiated by the worker process. Neither the router nor the host firewall needs a hole.
- Token never reaches a log.
logging.pywraps structlog with a processor that walks every record (dict / list / tuple / str / URL query) and redacts any substring matching the configured secret. Tested against adversarial nesting. - Retry discipline. The HTTPS client retries only transient classes (5xx, 429, network error) with exponential backoff; it does not retry other 4xx responses so a misconfigured token fails loudly instead of silently.
- Workflow JSON is treated as data. The worker patches known node ids (prompt, seed, resolution, input image URL) without constructing graphs dynamically; this keeps the graph shape auditable and reviewable in source control.
Apache-2.0 - see LICENSE. Model weights (Wan 2.2 TI2V-5B,
CogVideoX-2B, SDXL Turbo, SDXL Lightning LoRA) are not distributed
by this repo and each carries its own upstream license; the setup/
walkthrough points to the official downloads.