Skip to content

Add deploy environment header (X-Comfy-Env) to partner node API calls#13425

Open
Kosinkadink wants to merge 2 commits intomasterfrom
feature/deploy-environment-header
Open

Add deploy environment header (X-Comfy-Env) to partner node API calls#13425
Kosinkadink wants to merge 2 commits intomasterfrom
feature/deploy-environment-header

Conversation

@Kosinkadink
Copy link
Copy Markdown
Member

@Kosinkadink Kosinkadink commented Apr 16, 2026

Summary

Adds an X-Comfy-Env header to all API requests sent to api.comfy.org, allowing the API to differentiate between environment types (e.g. local_git, standalone, portable, desktop).

Relates to Comfy-Org/ComfyUI-Desktop-2.0-Beta#416

How it works

  1. A new module comfy/deploy_environment.py reads a .comfy_environment file from the ComfyUI base directory.
  2. If the file exists, its trimmed content is used as the environment value (e.g. standalone, portable, desktop).
  3. If the file is absent (vanilla git clone), the value defaults to local_git.
  4. The value is sent as an X-Comfy-Env header on all requests to api.comfy.org.
  5. The .comfy_environment file is gitignored so launchers/installers can write it without affecting the repository.

Usage

Launchers or installers simply write a .comfy_environment file in the ComfyUI root directory with the desired value:

standalone

No CLI args or environment variables needed. Git clone users get local_git automatically.

API Node PR Checklist

Scope

  • Is API Node Change

Pricing & Billing

  • Need pricing update
  • No pricing update

If Need pricing update:

  • Metronome rate cards updated
  • Auto‑billing tests updated and passing

QA

  • QA done
  • QA not required

Comms

  • Informed Kosinkadink

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds deployment-environment support: new module comfy/deploy_environment.py defines _DEFAULT_DEPLOY_ENV = "local_git", _ENV_FILENAME = ".comfy_environment", a cached module value, and get_deploy_environment() which reads the first line of that file (UTF-8), filters to printable ASCII (32–126), caches and returns it, falling back to "local_git" and logging a warning on unexpected errors. .gitignore now ignores .comfy_environment. The HTTP client (comfy_api_nodes/util/client.py) injects an X-Comfy-Env header with this value for relative URL requests.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding an X-Comfy-Env header to API calls sent to api.comfy.org.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the implementation, usage, and purpose of the X-Comfy-Env header functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@comfy_api_nodes/util/client.py`:
- Around line 620-623: The X-Comfy-Env header is only being set inside the
"relative URL" branch, so absolute requests to the Comfy API host miss it;
update the logic so payload_headers["X-Comfy-Env"] is added whenever the request
targets the Comfy API host (either parsed_url has no scheme/netloc or
parsed_url.netloc matches cfg.endpoint.host or cfg.endpoint.netloc), not only
for relative URLs. Keep the existing get_auth_header(cfg.node_cls) behavior for
relative URLs but move or add the X-Comfy-Env assignment to run for both the
relative-URL branch and for absolute URLs whose netloc equals the configured
Comfy API endpoint (use parsed_url, payload_headers, cfg.endpoint to
locate/compare).

In `@comfy/deploy_environment.py`:
- Around line 21-25: The code reads env_file and caches its raw contents into
_cached_value which can contain newlines or control characters that will break
the X-Comfy-Env HTTP header; inside the with open(...) block (where env_file,
value and _cached_value are used) sanitize the value before caching by taking
only the first line (value.splitlines()[0] if any) and stripping it, then remove
any control/non-ASCII or non-printable characters (e.g. filter out chars with
ord() < 32 or ==127) so the resulting string is header-safe, and assign that
sanitized string to _cached_value before returning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f031b8d-8871-468e-a44f-985ac78a0163

📥 Commits

Reviewing files that changed from the base of the PR and between 1de83f9 and 4de0e4e.

📒 Files selected for processing (3)
  • .gitignore
  • comfy/deploy_environment.py
  • comfy_api_nodes/util/client.py

Comment on lines 620 to 623
if not parsed_url.scheme and not parsed_url.netloc: # is URL relative?
payload_headers.update(get_auth_header(cfg.node_cls))
payload_headers["X-Comfy-Env"] = get_deploy_environment()
if cfg.endpoint.headers:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

X-Comfy-Env is applied only for relative URLs, not all calls to the Comfy API host.

At Line 620, the new header is tied to the relative-URL branch. Absolute URLs to the same API host won’t carry X-Comfy-Env, so the environment signal is incomplete.

Suggested fix
-        if not parsed_url.scheme and not parsed_url.netloc:  # is URL relative?
-            payload_headers.update(get_auth_header(cfg.node_cls))
-            payload_headers["X-Comfy-Env"] = get_deploy_environment()
+        if not parsed_url.scheme and not parsed_url.netloc:  # is URL relative?
+            payload_headers.update(get_auth_header(cfg.node_cls))
+
+        target_netloc = urlparse(url).netloc.lower()
+        base_netloc = urlparse(default_base_url()).netloc.lower()
+        if target_netloc == base_netloc:
+            payload_headers["X-Comfy-Env"] = get_deploy_environment()

As per coding guidelines: comfy_api_nodes/**: “Third-party API integration nodes. Focus on … Proper error handling for API failures … [and] Security of user data passed to external APIs.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comfy_api_nodes/util/client.py` around lines 620 - 623, The X-Comfy-Env
header is only being set inside the "relative URL" branch, so absolute requests
to the Comfy API host miss it; update the logic so
payload_headers["X-Comfy-Env"] is added whenever the request targets the Comfy
API host (either parsed_url has no scheme/netloc or parsed_url.netloc matches
cfg.endpoint.host or cfg.endpoint.netloc), not only for relative URLs. Keep the
existing get_auth_header(cfg.node_cls) behavior for relative URLs but move or
add the X-Comfy-Env assignment to run for both the relative-URL branch and for
absolute URLs whose netloc equals the configured Comfy API endpoint (use
parsed_url, payload_headers, cfg.endpoint to locate/compare).

Comment thread comfy/deploy_environment.py
@Kosinkadink Kosinkadink force-pushed the feature/deploy-environment-header branch from 4de0e4e to d443728 Compare April 16, 2026 03:05
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
comfy_api_nodes/util/client.py (1)

620-623: ⚠️ Potential issue | 🟠 Major

X-Comfy-Env is still not applied to absolute Comfy API URLs.

At Line 620, the header is gated by the relative-URL check, so absolute URLs targeting the same API host won’t include X-Comfy-Env. That leaves environment signaling incomplete versus the PR goal.

Suggested fix
-        if not parsed_url.scheme and not parsed_url.netloc:  # is URL relative?
-            payload_headers.update(get_auth_header(cfg.node_cls))
-            payload_headers["X-Comfy-Env"] = get_deploy_environment()
+        is_relative = not parsed_url.scheme and not parsed_url.netloc
+        if is_relative:  # relative URL uses default API host and auth
+            payload_headers.update(get_auth_header(cfg.node_cls))
+
+        base_netloc = urlparse(default_base_url()).netloc.lower()
+        target_netloc = parsed_url.netloc.lower() if parsed_url.netloc else base_netloc
+        if target_netloc == base_netloc:
+            payload_headers["X-Comfy-Env"] = get_deploy_environment()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comfy_api_nodes/util/client.py` around lines 620 - 623, The X-Comfy-Env
header is only added for relative URLs (parsed_url.scheme/netloc empty); change
the condition so X-Comfy-Env is also applied when the request targets the Comfy
API host even if an absolute URL is used: update the branch that sets
payload_headers (where parsed_url, payload_headers,
get_auth_header(cfg.node_cls) and get_deploy_environment() are used) to add
X-Comfy-Env when parsed_url.netloc is empty OR parsed_url.netloc equals the
Comfy API host (compare parsed_url.netloc against the configured base/API host
from cfg, e.g. urlparse(cfg.base_url or cfg.api_url).netloc), leaving other
header logic (cfg.endpoint.headers) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@comfy_api_nodes/util/client.py`:
- Around line 620-623: The X-Comfy-Env header is only added for relative URLs
(parsed_url.scheme/netloc empty); change the condition so X-Comfy-Env is also
applied when the request targets the Comfy API host even if an absolute URL is
used: update the branch that sets payload_headers (where parsed_url,
payload_headers, get_auth_header(cfg.node_cls) and get_deploy_environment() are
used) to add X-Comfy-Env when parsed_url.netloc is empty OR parsed_url.netloc
equals the Comfy API host (compare parsed_url.netloc against the configured
base/API host from cfg, e.g. urlparse(cfg.base_url or cfg.api_url).netloc),
leaving other header logic (cfg.endpoint.headers) intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20aaffab-13c7-4d73-99e7-67dbe3edda7b

📥 Commits

Reviewing files that changed from the base of the PR and between 4de0e4e and d443728.

📒 Files selected for processing (3)
  • .gitignore
  • comfy/deploy_environment.py
  • comfy_api_nodes/util/client.py
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • comfy/deploy_environment.py

Read a .comfy_environment file from the ComfyUI base directory to
determine the deployment environment (e.g. standalone, portable, desktop).
Defaults to 'local_git' when the file is absent.

The value is sent as an X-Comfy-Deploy-Env header on all requests to
api.comfy.org, allowing the API to differentiate between environment types.

The .comfy_environment file is gitignored so launchers/installers can
write it without affecting the repository.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d939e-6b4d-738b-8d1a-ac7cbf6736a4
@Kosinkadink Kosinkadink force-pushed the feature/deploy-environment-header branch from d443728 to e7fbb3c Compare April 16, 2026 03:15
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@comfy/deploy_environment.py`:
- Around line 14-33: get_deploy_environment currently lazily sets the
module-level _cached_value without any synchronization, so concurrent callers
can race and a temporary read failure can permanently pin _DEFAULT_DEPLOY_ENV;
fix by introducing a module-level lock (e.g., threading.Lock) and surround the
check/read/set of _cached_value with the lock (use double-checked locking: check
_cached_value before acquiring and re-check after acquiring) so only one thread
performs the file open/read logic for _ENV_FILENAME and sets _cached_value
(falling back to _DEFAULT_DEPLOY_ENV on error) while keeping logger warning
behavior intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e3340ae7-563e-4db0-a340-61b02aa19c8d

📥 Commits

Reviewing files that changed from the base of the PR and between d443728 and e7fbb3c.

📒 Files selected for processing (3)
  • .gitignore
  • comfy/deploy_environment.py
  • comfy_api_nodes/util/client.py
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • comfy_api_nodes/util/client.py

Comment on lines +14 to +33
def get_deploy_environment() -> str:
global _cached_value
if _cached_value is not None:
return _cached_value

env_file = os.path.join(folder_paths.base_path, _ENV_FILENAME)
try:
with open(env_file, encoding="utf-8") as f:
first_line = f.readline().strip()
value = "".join(c for c in first_line if 32 <= ord(c) < 127)
if value:
_cached_value = value
return _cached_value
except FileNotFoundError:
pass
except Exception as e:
logger.warning("Failed to read %s: %s", env_file, e)

_cached_value = _DEFAULT_DEPLOY_ENV
return _cached_value
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Synchronize cache initialization to avoid sticky transient fallback.

_cached_value is lazily initialized without a lock. Concurrent first calls can race, and a transient read failure in one caller can pin "local_git" for the rest of the process.

🔧 Proposed fix
 import logging
 import os
+import threading
 
 import folder_paths
@@
 _cached_value: str | None = None
+_cache_lock = threading.Lock()
@@
 def get_deploy_environment() -> str:
     global _cached_value
     if _cached_value is not None:
         return _cached_value
+    with _cache_lock:
+        if _cached_value is not None:
+            return _cached_value
 
-    env_file = os.path.join(folder_paths.base_path, _ENV_FILENAME)
-    try:
-        with open(env_file, encoding="utf-8") as f:
-            first_line = f.readline().strip()
-            value = "".join(c for c in first_line if 32 <= ord(c) < 127)
-            if value:
-                _cached_value = value
-                return _cached_value
-    except FileNotFoundError:
-        pass
-    except Exception as e:
-        logger.warning("Failed to read %s: %s", env_file, e)
+        env_file = os.path.join(folder_paths.base_path, _ENV_FILENAME)
+        try:
+            with open(env_file, encoding="utf-8") as f:
+                first_line = f.readline().strip()
+                value = "".join(c for c in first_line if 32 <= ord(c) < 127)
+                if value:
+                    _cached_value = value
+                    return _cached_value
+        except FileNotFoundError:
+            pass
+        except Exception as e:
+            logger.warning("Failed to read %s: %s", env_file, e)
+            return _DEFAULT_DEPLOY_ENV
 
-    _cached_value = _DEFAULT_DEPLOY_ENV
-    return _cached_value
+        _cached_value = _DEFAULT_DEPLOY_ENV
+        return _cached_value

As per coding guidelines: comfy/** review should focus on “Thread safety for concurrent execution.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@comfy/deploy_environment.py` around lines 14 - 33, get_deploy_environment
currently lazily sets the module-level _cached_value without any
synchronization, so concurrent callers can race and a temporary read failure can
permanently pin _DEFAULT_DEPLOY_ENV; fix by introducing a module-level lock
(e.g., threading.Lock) and surround the check/read/set of _cached_value with the
lock (use double-checked locking: check _cached_value before acquiring and
re-check after acquiring) so only one thread performs the file open/read logic
for _ENV_FILENAME and sets _cached_value (falling back to _DEFAULT_DEPLOY_ENV on
error) while keeping logger warning behavior intact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant