> ## Documentation Index
> Fetch the complete documentation index at: https://extension.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Telemetry and privacy controls

> Review the Extension.js telemetry contract: what anonymous data is collected, what is never collected, and how to opt out of telemetry entirely.

Extension.js collects a tiny amount of anonymous telemetry to understand which commands you run and which fail. It never collects source code, file paths, URLs, or project content.

The privacy bar is strict by design:

* Two events total: `command_executed` and `command_failed`
* Three properties on every event: `command`, `success`, `version`
* Opt-out with an environment variable, a CLI flag, or a persistent consent command
* Extension.js samples and caps events to stay well inside the [PostHog](https://posthog.com/) (open-source analytics) free tier

## What Extension.js collects

Per CLI run, at most one of the following. The exception is a watch session (`dev`, `start`, `preview`), which reports `command_executed` with `session: started` when it comes up and a later `command_failed` if it breaks, so one run can produce two rows:

| Event | Sampled | Properties |
| - | - | - |
| `command_executed` | 20 % (configurable, see below) | `command`, `success: true`, `version` |
| `command_failed` | 100 % (Extension.js always sends failures) | `command`, `success: false`, `version`, `code`, `exit_code` |

A failure adds two more properties so a failure count reads as a cause rather than a number. `code` is one name from the CLI's fixed error catalog, for example `E_MANIFEST_NOT_FOUND`, and `exit_code` is the process exit code as an integer from 0 to 255. `code` is checked against the catalog before it is sent, so a Node errno, an error message, or any value that is not a catalog name is dropped and the event reports `E_INTERNAL` instead. No error text ever travels.

When a run offers to download a managed browser on a first run, the command's own event carries the outcome, so an offer someone accepted can be told apart from one that ended the session. `browser_install` is one of `offered`, `accepted`, `declined` or `failed`, `browser_install_browser` names the managed browser, for example `chrome`, and `browser_install_seconds` is the whole number of seconds a download took. There is no separate install event, the counts stay on the two events above.

The `create` command adds two more properties so a broken advertised starter shows up in its failure counts: `template` (the starter name as listed by `extension create --help`, or absent) and `source` (`cli`, or `templates` when the docs template gallery started the create). `template` is checked against the published starter list before it is sent. A GitHub URL, a local folder path, or any other value that is not an advertised name is dropped rather than trimmed, so a private repository or a directory name never leaves the machine. `source` is checked the same way against those two surfaces, and any other value is sent as `cli`. The `create` command is also exempt from sampling, so template usage numbers stay accurate.

Common context attached to every event: `os` (`darwin`/`linux`/`win32`), `arch`, `node_major`, `is_ci`, `is_source_build`, and `app`, which is always the word `extension`. Nothing else. `is_source_build` is a single boolean answering "was this a registry install or a repository checkout". It is computed from the shape of the install path (whether a `node_modules`-style segment sits above the CLI). The path itself is never read into the event, sent, or hashed.

## Volume controls

Three independent controls limit how much data leaves the machine:

* **Sampling:** Extension.js samples `command_executed` at 20 % by default. Override with `EXTENSION_TELEMETRY_SAMPLE_RATE` (0.0–1.0). Failures are never sampled.
* **Per-run cap:** at most **3 events** per CLI process. Override with `EXTENSION_TELEMETRY_MAX_EVENTS`.
* **Debounce (duplicate suppression):** Extension.js drops duplicate `(event, command, success)` tuples within 60 s. Override with `EXTENSION_TELEMETRY_DEBOUNCE_MS`.

## What Extension.js never collects

The Extension.js telemetry contract explicitly excludes:

* Source code, manifest contents, HTML output, or `package.json` contents
* Repo names, Git remotes, GitHub org/user names, branch names, commit SHAs, or preview URLs
* Dependency lists, permission lists, or freeform project identifiers
* Environment variable values, filesystem paths, or machine-local URLs
* Stack traces, error messages, or free-text error names
* IP addresses. The PostHog project discards the request address at ingestion (`anonymize_ips`) and every payload sends `$ip: null` with `$geoip_disable: true`, so no location is derived from it either. Events sent before 2026-10-06 carried the request address.

## Releases before 4.0

The 3.x line sent a different schema, published in the repository contract until 2026-04-17: lifecycle events (`cli_boot`, `cli_command_start`, `cli_command_finish`, `cli_vendor_start`, `cli_vendor_finish`, `cli_shutdown`, `cli_telemetry_consent`), `manifest_summary` (permission and content script counts), `project_profile` (framework family and package manager), `workflow_profile` (a usage cohort) and `cli_build_summary` (asset counts and bytes). Counts, booleans and names from fixed lists, never a path, a URL or a project name. Installs still on 3.x keep sending those events, and since 2026-10-07 the PostHog project drops every event that is not one of the two above at ingestion, so nothing from that schema is stored any more.

## Opt out

Three ways to disable telemetry, listed in precedence order:

```bash theme={null}
# 1. Environment variable (wins over everything else)
EXTENSION_TELEMETRY_DISABLED=1 extension dev   # preferred
EXTENSION_TELEMETRY=0 extension dev            # back-compat, also honored

# 2. Per-run flag
extension dev --no-telemetry

# 3. Persistent consent file via the telemetry command
extension telemetry disable
extension telemetry enable
extension telemetry status    # show current state (default when no argument)
```

The consent file lives at `$XDG_CONFIG_HOME/extensionjs/telemetry/consent` (or the platform equivalent). When that location is not writable, storage falls back in order: the platform cache directory, then a folder under the system temp directory, then `./.cache/extensionjs` in the working directory.

## Default behavior

In an interactive terminal, telemetry is **opt-out**. On the first run where none of the overrides above apply, Extension.js prints a one-line notice explaining how to disable it. It also records an `enabled` consent marker so the notice does not repeat.

### CI is off by default

In CI with nothing attached to stdout (a CI marker set and no TTY), telemetry is **off by default** and reports `source: ci`. A stored `enabled` consent does not carry into a pipeline: consent recorded by a person at a keyboard cannot cover a pipeline that inherited that home directory, so the stored opt-in is demoted below the CI gate. A stored `disabled` is a refusal, and a refusal always wins, in CI and everywhere else.

To send telemetry from a pipeline on purpose, set `EXTENSION_TELEMETRY=1`. That explicit machine-level opt-in sits above the CI gate.

## Local audit log

Extension.js appends every event it considers sending (whether or not it actually ships) to `events.jsonl` next to the consent file. Inspect it any time. Delete it freely.

The audit log stays bounded. At 1 MiB it rolls to a single `events.jsonl.1` backup, replacing any previous backup. Override the cap with `EXTENSION_TELEMETRY_AUDIT_MAX_BYTES`. A file grossly over the cap (10 times or more) is dropped instead of kept. If the audit write itself fails, Extension.js disables all further sends for that run: an event that cannot be audited locally is not sent.

## Best practices

* Use `EXTENSION_TELEMETRY_DISABLED=1` in CI when policy requires no telemetry.
* Treat privacy regressions as product regressions.
* Read the repository-level contract for the exact event list.

## Next steps

* Review [Global flags](/docs/workflows/global-flags) for `--no-telemetry` and environment variable overrides.
* Use [build](/docs/commands/build) and [dev](/docs/commands/dev) for release and automation workflows.
* Read the repository-level [`docs/TELEMETRY.md`](https://github.com/extension-js/extension.js/blob/main/docs/TELEMETRY.md) contract for the exact event list.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.