{
    "meta": {
        "title": "D-Central — Local-AI Telemetry & Air-Gap Audit Registry",
        "description": "Audit of 14 self-hosted AI tools: what each contacts BY DEFAULT (update checks, analytics, registry pulls), exact domains, opt-in vs opt-out, the exact env var or setting that disables it, and a copy-paste air-gap recipe per tool.",
        "generated": "2026-07-18T05:42:06+00:00",
        "version": "1.0",
        "license": "https://creativecommons.org/licenses/by/4.0/",
        "license_name": "CC BY 4.0",
        "source": "https://d-central.tech/local-ai-telemetry-audit/",
        "record_count": 14,
        "method": "Evidence-graded: A = verified in the tool's source code or maintainer-confirmed, B = official docs only (e.g. closed-source LM Studio), C = community reports. User-initiated model downloads are expected and excluded from the default-outbound verdict; the audit concerns what fires on install/launch/idle without asking. Anything not verifiable exactly (a domain, an env var) says \"unverified\" — it is never guessed. Point-in-time: telemetry behavior changes between versions; version_scope states what each row covers.",
        "provenance": "Tool source repositories (updater/telemetry/analytics code paths), official privacy documentation, maintainer statements in issues/discussions. DO_NOT_TRACK convention: consoledonottrack.com.",
        "disclaimer": "Not packet-capture-verified end to end; a docs promise of no telemetry is not the same as an observed-silent socket. For true air-gap: pre-download models, set the listed env vars, and firewall the listed domains."
    },
    "rows": [
        {
            "tool": "llamafile",
            "version_scope": "~v0.10.x, July 2026",
            "license": "Apache-2.0",
            "default_outbound": "none",
            "endpoints": [],
            "telemetry": "no",
            "opt_model": "n/a",
            "airgap": "Run ./model.llamafile — weights are embedded in the executable, the HTTP server binds localhost only, zero outbound out of the box. Nothing to download, nothing to block.",
            "evidence_basis": "official docs + source (Mozilla llamafile repo)",
            "evidence_grade": "A",
            "notes": "The strictest air-gap answer on this list: a single Cosmopolitan-libc binary with the model inside."
        },
        {
            "tool": "Whisper.cpp",
            "version_scope": "master, July 2026",
            "license": "MIT",
            "default_outbound": "none",
            "endpoints": [
                {
                    "domain": "huggingface.co",
                    "purpose": "GGML model download via the OPTIONAL download-ggml-model.sh helper",
                    "when": "only when the user runs the script",
                    "optout": "supply a local model path"
                }
            ],
            "telemetry": "no",
            "opt_model": "n/a",
            "airgap": "whisper-cli -m /path/ggml-*.bin -f audio.wav makes zero network calls. The only outbound path is the user-run download helper script.",
            "evidence_basis": "source code (no HTTP client in core)",
            "evidence_grade": "A",
            "notes": "Plain C/C++ with no update check and no analytics."
        },
        {
            "tool": "KoboldCpp",
            "version_scope": "~v1.9x, July 2026",
            "license": "AGPL-3.0",
            "default_outbound": "none",
            "endpoints": [
                {
                    "domain": "*.trycloudflare.com",
                    "purpose": "optional public tunnel to your local server (also downloads the cloudflared binary on first use)",
                    "when": "ONLY with --remotetunnel",
                    "optout": "do not pass --remotetunnel (default off)"
                }
            ],
            "telemetry": "no",
            "opt_model": "opt-in (all remote features)",
            "airgap": "koboldcpp --model model.gguf binds localhost:5001 with zero outbound. Skip --remotetunnel, AI-Horde worker mode and URL model fetch and it never opens a socket outward.",
            "evidence_basis": "source code + maintainer README",
            "evidence_grade": "A",
            "notes": "Single-file binary; every outbound path is an explicit opt-in flag."
        },
        {
            "tool": "llama.cpp (llama-server)",
            "version_scope": "master, July 2026",
            "license": "MIT",
            "default_outbound": "none",
            "endpoints": [
                {
                    "domain": "huggingface.co",
                    "purpose": "model download/revalidation for -hf / --hf-repo models",
                    "when": "only when an -hf model is used (may revalidate cache at startup)",
                    "optout": "use a local -m path.gguf, or pass --offline"
                }
            ],
            "telemetry": "no",
            "opt_model": "n/a",
            "airgap": "llama-server -m /path/model.gguf is fully offline — no update check, no analytics code in the binary. If you used -hf, add --offline to pin the cache.",
            "evidence_basis": "source code (network only in the HF download path)",
            "evidence_grade": "A",
            "notes": "The one caveat: an -hf-sourced model may GET huggingface.co at startup unless --offline is set."
        },
        {
            "tool": "text-generation-webui (oobabooga)",
            "version_scope": "main, July 2026",
            "license": "AGPL-3.0",
            "default_outbound": "model-downloads-only",
            "endpoints": [
                {
                    "domain": "huggingface.co",
                    "purpose": "model/tokenizer downloads via HF Hub",
                    "when": "user-initiated or uncached HF repo id",
                    "optout": "HF_HUB_OFFLINE=1 + TRANSFORMERS_OFFLINE=1, load local folders"
                }
            ],
            "telemetry": "no",
            "opt_model": "opt-out (project already disabled it for you)",
            "airgap": "Gradio's api.gradio.app analytics is double-disabled in-tree (GRADIO_ANALYTICS_ENABLED=False + analytics_enabled=False). Export HF_HUB_OFFLINE=1 and TRANSFORMERS_OFFLINE=1 after caching models and it is fully offline.",
            "evidence_basis": "source code (server.py, issue #801)",
            "evidence_grade": "A",
            "notes": "The historically risky path — Gradio's default telemetry — is explicitly neutralized in source."
        },
        {
            "tool": "LocalAI",
            "version_scope": "v4.7.x, July 2026",
            "license": "MIT",
            "default_outbound": "model-downloads-only",
            "endpoints": [
                {
                    "domain": "raw.githubusercontent.com",
                    "purpose": "default model + backend gallery indexes",
                    "when": "when the gallery is used/auto-loaded",
                    "optout": "--galleries='[]' --autoload-galleries=false --disable-gallery-endpoint"
                },
                {
                    "domain": "huggingface.co",
                    "purpose": "model weight downloads",
                    "when": "user-initiated install",
                    "optout": "preload local models"
                },
                {
                    "domain": "quay.io",
                    "purpose": "OCI backend images (v3+ runtime backend pulls)",
                    "when": "backend install",
                    "optout": "pre-provision backend images"
                }
            ],
            "telemetry": "no",
            "opt_model": "n/a",
            "airgap": "Preload models ($LOCALAI_PRELOAD_MODELS) and backends, then --autoload-galleries=false --galleries='[]' --disable-gallery-endpoint. The local /metrics endpoint is Prometheus-style with no egress (disable: $LOCALAI_DISABLE_METRICS_ENDPOINT=true).",
            "evidence_basis": "maintainer statement (issue #3942) + source + CLI docs",
            "evidence_grade": "A",
            "notes": "The one telemetry scare was a misread of the bundled OpenTelemetry /metrics endpoint — local-only; the maintainer rebutted it and shipped a disable flag."
        },
        {
            "tool": "Ollama",
            "version_scope": "July 2026 (updater unchanged for years)",
            "license": "MIT",
            "default_outbound": "update-check",
            "endpoints": [
                {
                    "domain": "ollama.com",
                    "purpose": "desktop tray-app update check (GET /api/update)",
                    "when": "macOS/Windows GUI only, ~hourly",
                    "optout": "run headless ollama serve (Linux/CLI has no updater), or firewall the domain"
                },
                {
                    "domain": "registry.ollama.ai",
                    "purpose": "model layer/manifest pulls",
                    "when": "only on ollama pull/run of a non-local model",
                    "optout": "n/a — user-initiated; fully local once cached"
                }
            ],
            "telemetry": "no",
            "opt_model": "n/a",
            "airgap": "ollama serve has no updater and no analytics SDK — pre-pull your models and it runs fully offline. The only unsolicited ping is the GUI tray updater: use the server binary or block ollama.com.",
            "evidence_basis": "source code (app/lifecycle updater.go; no analytics SDK in repo)",
            "evidence_grade": "A",
            "notes": "\"Zero telemetry\" is true of analytics but imprecise for the desktop app — the tray updater phones ollama.com hourly. Server use: genuinely silent."
        },
        {
            "tool": "ComfyUI (core)",
            "version_scope": "master, July 2026; Manager + Desktop are separate products",
            "license": "GPL-3.0-only",
            "default_outbound": "none",
            "endpoints": [
                {
                    "domain": "api.comfy.org",
                    "purpose": "ComfyUI-MANAGER node-registry fetch (not core)",
                    "when": "Manager startup unless offline mode",
                    "optout": "network_mode = private or offline in Manager config.ini"
                },
                {
                    "domain": "raw.githubusercontent.com",
                    "purpose": "Manager node-DB channel lists",
                    "when": "Manager catalog refresh",
                    "optout": "Manager offline mode"
                },
                {
                    "domain": "github.com",
                    "purpose": "core frontend package resolution; Manager node clones",
                    "when": "frontend version resolution / user installs",
                    "optout": "pin --front-end-version; install offline"
                }
            ],
            "telemetry": "no (core even sets DO_NOT_TRACK=1 and HF_HUB_DISABLE_TELEMETRY=1 itself)",
            "opt_model": "n/a (Desktop app's analytics are opt-in — a separate product using PostHog/Datadog per its privacy policy)",
            "airgap": "Core Python install is fully offline once models and frontend are present. If Manager is installed set network_mode=offline (and verify — reports exist it is not always fully honored). Avoid the Desktop build for true air-gap. Audit custom nodes separately — they run arbitrary Python.",
            "evidence_basis": "source code (main.py) + Manager source + comfy.org privacy docs",
            "evidence_grade": "A",
            "notes": "Network activity blamed on ComfyUI is almost always the Manager extension or the Desktop app, not core."
        },
        {
            "tool": "Jan",
            "version_scope": "~v0.8.x (Tauri), July 2026",
            "license": "Apache-2.0",
            "default_outbound": "update-check",
            "endpoints": [
                {
                    "domain": "apps.jan.ai",
                    "purpose": "Tauri auto-updater",
                    "when": "on launch / update check",
                    "optout": "disable auto-update or firewall"
                },
                {
                    "domain": "github.com",
                    "purpose": "fallback updater manifest + update binaries",
                    "when": "update check",
                    "optout": "disable auto-update"
                },
                {
                    "domain": "eu-assets.i.posthog.com",
                    "purpose": "PostHog EU analytics (in app CSP)",
                    "when": "ONLY if analytics toggled on",
                    "optout": "leave Settings > Privacy > Analytics OFF (default off)"
                }
            ],
            "telemetry": "yes — PostHog EU, but OPT-IN and off by default",
            "opt_model": "opt-in",
            "airgap": "Analytics needs no action (default off). Disable the auto-updater or firewall apps.jan.ai + github.com; import local GGUF files and inference is fully offline.",
            "evidence_basis": "source code (tauri.conf.json updater + CSP hosts) + privacy docs",
            "evidence_grade": "A",
            "notes": "Default install phones home only for update checks; exact PostHog ingest hostname unverified (CSP lists the EU asset host)."
        },
        {
            "tool": "GPT4All",
            "version_scope": "v3.10.0 (Feb 2025 — app dormant since)",
            "license": "MIT",
            "default_outbound": "multiple",
            "endpoints": [
                {
                    "domain": "gpt4all.io",
                    "purpose": "model catalog, release check, news banner — plain HTTP with certificate verification DISABLED",
                    "when": "on launch, UNCONDITIONALLY (no toggle)",
                    "optout": "none in-app — firewall the domain"
                },
                {
                    "domain": "api.mixpanel.com",
                    "purpose": "Mixpanel analytics",
                    "when": "ONLY if opted in at first launch",
                    "optout": "decline the opt-in prompt (default off)"
                },
                {
                    "domain": "api.gpt4all.io",
                    "purpose": "opt-in Datalake chat contribution",
                    "when": "ONLY if Datalake enabled",
                    "optout": "leave Datalake off (default)"
                },
                {
                    "domain": "api.ipify.org",
                    "purpose": "public-IP lookup within the opted-in analytics flow",
                    "when": "only with analytics on",
                    "optout": "off with telemetry"
                }
            ],
            "telemetry": "yes — Mixpanel, but OPT-IN and off by default",
            "opt_model": "opt-in (analytics + Datalake); the catalog/update/news fetch has NO toggle",
            "airgap": "Decline both first-launch prompts, then firewall gpt4all.io, api.gpt4all.io, api.mixpanel.com, api.ipify.org. Inference is fully local after model download; the unconditional catalog/news fetches fail harmlessly offline.",
            "evidence_basis": "source code (network.cpp, mysettings.cpp, download.cpp)",
            "evidence_grade": "A",
            "notes": "Genuinely opt-in analytics, but NOT zero-outbound: the launch-time catalog/update/news fetch runs plain HTTP with cert verification off and cannot be disabled in-app. Desktop app appears dormant since Feb 2025."
        },
        {
            "tool": "LM Studio",
            "version_scope": "0.3.x line, July 2026 (closed source)",
            "license": "Proprietary",
            "default_outbound": "multiple",
            "endpoints": [
                {
                    "domain": "unverified (update/CDN host not published)",
                    "purpose": "update check — sends app/OS version + IP per privacy policy",
                    "when": "launch / periodic",
                    "optout": "no dedicated toggle; block at network level"
                },
                {
                    "domain": "unverified (model catalog/hub)",
                    "purpose": "model search/downloads + anonymized search/download stats",
                    "when": "when searching/downloading",
                    "optout": "Settings: disable anonymous usage reporting"
                }
            ],
            "telemetry": "yes — anonymized usage/search stats per privacy policy (vendor undisclosed; closed source)",
            "opt_model": "opt-out",
            "airgap": "Disable anonymous usage data in Settings, pre-download models, then network-block the app. Docs state chats/RAG never leave the device — but closed source means none of it is code-verifiable.",
            "evidence_basis": "official docs only (lmstudio.ai/app-privacy) — no source to audit",
            "evidence_grade": "B",
            "notes": "Never describe LM Studio as zero-telemetry: it phones home for updates and ships opt-out usage stats, and exact domains are unpublished."
        },
        {
            "tool": "Open WebUI",
            "version_scope": "v0.6.x, July 2026",
            "license": "BSD-3-Clause + branding clause (since v0.5.4 — not pure OSI)",
            "default_outbound": "telemetry",
            "endpoints": [
                {
                    "domain": "*.posthog.com (via bundled ChromaDB)",
                    "purpose": "ChromaDB's anonymized product telemetry — a dependency, not an Open WebUI pixel",
                    "when": "on vector-DB init/use",
                    "optout": "ANONYMIZED_TELEMETRY=false (official Docker image ships this) or DO_NOT_TRACK=true"
                },
                {
                    "domain": "unverified Scarf gateway",
                    "purpose": "install/pull download analytics",
                    "when": "image/package pull",
                    "optout": "SCARF_NO_ANALYTICS=true"
                },
                {
                    "domain": "api.github.com",
                    "purpose": "version/update check",
                    "when": "admin version check",
                    "optout": "unverified toggle; block the domain"
                }
            ],
            "telemetry": "yes — PostHog via the ChromaDB dependency + Scarf install analytics",
            "opt_model": "opt-out (official Docker image pre-disables the ChromaDB telemetry)",
            "airgap": "Set ANONYMIZED_TELEMETRY=false, DO_NOT_TRACK=true, SCARF_NO_ANALYTICS=true (the first is already false in the official image). Chat inference itself stays local once models are on your runtime.",
            "evidence_basis": "maintainer statement (discussion #15624) + .env.example + docs",
            "evidence_grade": "A",
            "notes": "The telemetry originates in the bundled ChromaDB vector store; a ChromaDB pin fix landed in v0.6.18. Scarf destination domain unverified."
        },
        {
            "tool": "AnythingLLM",
            "version_scope": "~v1.15, July 2026",
            "license": "MIT",
            "default_outbound": "telemetry",
            "endpoints": [
                {
                    "domain": "app.posthog.com",
                    "purpose": "PostHog Cloud anonymous product analytics (events like sent_chat, document_uploaded — metadata only, no content per docs)",
                    "when": "ON BY DEFAULT: app start + tracked events",
                    "optout": "DISABLE_TELEMETRY=\"true\" env var (exact string match in source) or the in-app Privacy toggle"
                },
                {
                    "domain": "huggingface.co",
                    "purpose": "one-time built-in model/embedder downloads",
                    "when": "user-initiated",
                    "optout": "skip once local"
                }
            ],
            "telemetry": "yes — PostHog Cloud, ON by default",
            "opt_model": "opt-out",
            "airgap": "Set DISABLE_TELEMETRY=\"true\" exactly (source checks === \"true\") or use the Privacy toggle; pick local providers only (Ollama/LocalAI + built-in embedder + LanceDB). Belt-and-suspenders: firewall app.posthog.com — issues #5496/#4534 suggest the flag alone may not kill every connection.",
            "evidence_basis": "source code (server/models/telemetry.js, docker/.env.example)",
            "evidence_grade": "A",
            "notes": "Docs say anonymous event metadata only, never chats/documents — but it ships enabled, which most self-hosters do not expect."
        },
        {
            "tool": "vLLM",
            "version_scope": "v0.7–v0.11 line, July 2026",
            "license": "Apache-2.0",
            "default_outbound": "telemetry",
            "endpoints": [
                {
                    "domain": "stats.vllm.ai",
                    "purpose": "first-party anonymous usage stats (GPU count/type/memory, cloud provider, OS/arch, vLLM version, model architecture)",
                    "when": "on first engine start, then every 10 minutes",
                    "optout": "VLLM_NO_USAGE_STATS=1, or DO_NOT_TRACK=1, or touch ~/.config/vllm/do_not_track"
                }
            ],
            "telemetry": "yes — first-party (no third-party SDK; collected data is publicly released)",
            "opt_model": "opt-out",
            "airgap": "Export VLLM_NO_USAGE_STATS=1 (or DO_NOT_TRACK=1) before launch; add HF_HUB_OFFLINE=1 with pre-downloaded weights for full air-gap. A local copy of what would be sent sits in ~/.config/vllm/usage_stats.json — read it yourself.",
            "evidence_basis": "source code (vllm/usage/usage_lib.py) + official usage-stats docs",
            "evidence_grade": "A",
            "notes": "The most-cited surprise in local-AI serving: production inference engine, telemetry ON by default — though transparently documented and honoring DO_NOT_TRACK."
        }
    ]
}