Telemetry Initialization and Opt-Out#
comfy-cli collects usage analytics via two providers β Mixpanel and PostHog β configured in comfy_cli/tracking.py. Provider objects are instantiated at module import time (lines 189β192), meaning they are created on every comfy invocation, including contexts like shell completion that never execute a real command.
Providers#
Both providers are constructed unconditionally when tracking.py is imported :
| Provider | Token source | Host |
|---|---|---|
MixpanelProvider | Hard-coded public token 93aeab8962b622d431ac19800ccc9f67 | Mixpanel default |
PostHogProvider | Env POSTHOG_API_KEY, fallback to hard-coded key phc_β¦ | https://t.comfy.org |
The PostHog token can be overridden by setting the POSTHOG_API_KEY environment variable . Events sent to PostHog are prefixed with cli: (e.g., cli:install) to match a shared surface-prefix convention; Mixpanel keeps bare legacy names for historical continuity .
On exit, atexit.register(_flush_all_providers) is called so in-flight PostHog events (which ship asynchronously) are drained before the process terminates .
Consent Gate: Three Layers#
All passive telemetry is subject to a three-layer check implemented in _consent_enabled():
-
Environment variables β
_telemetry_disabled_by_env()checksDO_NOT_TRACKandCOMFY_NO_TELEMETRY. Either variable set to any non-empty, non-"0"value suppresses all telemetry. This follows the Console Do Not Track convention. -
Persisted config flag β
CONFIG_KEY_ENABLE_TRACKINGin the user's config file (viaConfigManager) . Written byinit_tracking(True/False)and inspected byconfig_manager.get_bool(). -
Session-only tracking β the module-level boolean
_session_only_tracking. This process-scoped flag captures agentic (non-interactive) usage without persisting consent to disk, so a later interactive human run can still prompt for consent.
The central gate in track_event() enforces all three: env opt-out short-circuits first, then persisted flag or session flag must be truthy.
Consent Prompt Flow#
prompt_tracking_consent() is called from the global CLI callback in cmdline.py on every invocation except comfy setup , which owns its own consent wizard:
- Env opt-out present β returns immediately; no prompt, no persistence.
- Consent already recorded (config flag exists) β returns immediately.
- Non-TTY (CI, piped input, agents) β generates and persists a stable anonymous
user_idwithout enabling telemetry. Avoids violating DO_NOT_TRACK spirit for OSS tooling . - Interactive TTY, first run β prompts: "Do you agree to enable tracking?" and calls
init_tracking().
init_tracking(True) persists the consent flag, generates a user_id (UUID, stored in config), and fires a one-time install event .
To enable or disable tracking manually:
comfy tracking enable
comfy tracking disable
These commands are registered at and exposed via app.add_typer(tracking.app, name="tracking", ...) in cmdline.py .
Data Sent and Sanitization#
Every event is enriched with cli_version and tracing_id (a per-invocation UUID) by _dispatch(). Command kwargs are scrubbed before sending via filter_command_kwargs():
- Sensitive names (
_token,_api_key,_secret,_password, exact matches likekey,prompt,changelog) β value replaced with"<redacted>". - URL values β query strings and fragments stripped to remove embedded tokens (e.g., CivitAI download links) .
- Non-JSON-serializable values and Click
ctx/contextkwargs β dropped .
The @tracking.track_command() decorator applies this automatically to decorated commands.
Special Cases#
User feedback (submit_feedback) is not gated on the consent flag β it is an explicit user-initiated action, so only the env opt-out applies . If opted out, it returns False and the caller notifies the user rather than silently dropping the message.
Agent reviews (submit_agent_review) are treated like passive telemetry and are fully consent-gated .
Known Gap: No Lightweight Context Optimization#
PostHog and Mixpanel client objects are instantiated at module import time regardless of the command being run. Shell completion, --version, and other trivial invocations all trigger the full provider initialization . There is currently no optimization to skip this in lightweight contexts, which means any startup overhead from the telemetry library imports is always paid.
Key Files#
| File | Purpose |
|---|---|
comfy_cli/tracking.py | All telemetry logic: providers, consent gate, event dispatch, sanitization |
comfy_cli/cmdline.py | Calls prompt_tracking_consent() in global callback; exposes comfy tracking subcommand |
comfy_cli/constants.py | Config key names (CONFIG_KEY_ENABLE_TRACKING, CONFIG_KEY_USER_ID, etc.) |
comfy_cli/config_manager.py | get_bool(), get_cli_version(), and config persistence |