Cloud Upload Architecture#
comfy-cli provides two distinct upload systems with different endpoints, purposes, and outputs. Confusing them is the most common source of file-staging bugs.
System 1 β comfy upload (transfer command)#
Entry point: comfy_cli/command/transfer.py β execute_upload
Endpoint: POST /upload/image on the ComfyUI server
Purpose: Puts files into the server's input/ directory so that ComfyUI nodes (e.g. LoadImage, LoadVideo) can reference them by filename. Works against both local and cloud targets.
Key behaviors:
- Sends a
multipart/form-databody with a field named"image"regardless of actual file type - Returns a
cloud_name(the server-assigned filename) indata.uploads[0].cloud_name - Safety cap: 2 GB per file
- Within a project, prefer
comfy assets pushβ it deduplicates by content hash, records server names in.comfy/assets.lock.json, and lets blueprints reference files as$asset.<path>without manual name tracking
Typical usage:
comfy --json upload photo.png --where cloud
# β data.uploads[0].cloud_name is the server-side filename to paste into workflows
System 2 β comfy generate upload (generate API)#
Entry point: comfy_cli/command/generate/upload.py β upload_target
Endpoint: POST /customers/storage β signed PUT URL
Purpose: Hosts a reference asset on Comfy's CDN and returns a signed download URL for use as an input to partner-API comfy generate calls (e.g., image-to-video). Not a ComfyUI server upload β the result is a CDN URL, not a server-side filename.
Key behaviors:
- Two-step: POST to
/customers/storagewith{file_name, content_type, file_hash}β get{upload_url, download_url, existing_file}, then PUT bytes to the signedupload_url - Content-hash deduplication: if
existing_fileistrue, the PUT is skipped anddownload_urlis returned immediately β no re-upload download_urlis a signed URL that expires after 24 hours- Accepts a local file path or a remote
http(s)://URL (downloads and re-hosts the remote) - Use
--jsonin scripts β without it the URL may soft-wrap in the terminal
Typical usage (I2V pattern):
comfy generate <image-model> --download still.png
comfy generate upload still.png --json # β {url, expires_at}
comfy generate <i2v-model> --image <url> --download clip.mp4
Comparison#
comfy upload | comfy generate upload | |
|---|---|---|
| Endpoint | POST /upload/image (ComfyUI server) | POST /customers/storage (Comfy API) |
| Result | Server-side filename (cloud_name) | Signed CDN URL (24h expiry) |
| Used for | Workflow node inputs (LoadImage, etc.) | Partner-API comfy generate calls |
| Dedup | None | SHA-256 content hash |
| Project integration | Yes ($asset / lock file) | No |
| Auth | CLI-managed target auth headers | API key via generate client |
Known Limitation: Videos Not Catalogued for Node Selection#
Uploading a video via comfy upload (System 1) does not make it selectable in LoadVideo's input choices on cloud β only pushed images appear in LoadImage's choices :
"pushed videos are not yet catalogued for LoadVideo on cloud, so the cross-job video handoff requires local assembly today."
Workaround: For a cross-job video handoff, assemble clips locally rather than relying on a second cloud graph picking up the video by name.
Note: the correct input key for LoadVideo is "file" (not "video") β the wrong key passes client-side validation but fails server-side at input staging .
Related Files#
comfy_cli/command/transfer.pyβexecute_upload,execute_downloadcomfy_cli/command/generate/upload.pyβ signed-URL upload forgeneratesubcommandscomfy_cli/fragments.pyβPATH_LOADERStable (authoritative input keys per modality)comfy_cli/skills/comfy/SKILL.mdβ operator guidance for file transfer patterns and project asset push