From f64d283740b3e26b704405d081dba6074e240547 Mon Sep 17 00:00:00 2001 From: irl-dan <97565471+irl-dan@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:01:50 -0700 Subject: [PATCH 1/3] docs(cli): document anonymous opt-out telemetry Add a Telemetry page to the CLI section covering what reactor-cli collects (anonymous, content-free, bucketed), every way to opt out (DO_NOT_TRACK, REACTOR_TELEMETRY=0, REACTOR_OFFLINE=1, CI/non-TTY, reactor.yml, and `reactor telemetry disable`), the dev/prod endpoints, and what is never collected. Links to the in-package TELEMETRY.md for the field-by-field schema. --- content/docs/cli/meta.json | 1 + content/docs/cli/telemetry.mdx | 107 +++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 content/docs/cli/telemetry.mdx diff --git a/content/docs/cli/meta.json b/content/docs/cli/meta.json index 9a4a032..65c6186 100644 --- a/content/docs/cli/meta.json +++ b/content/docs/cli/meta.json @@ -7,6 +7,7 @@ "compile-run-serve", "connectors-and-sandbox", "observability", + "telemetry", "command-reference" ] } diff --git a/content/docs/cli/telemetry.mdx b/content/docs/cli/telemetry.mdx new file mode 100644 index 0000000..77533af --- /dev/null +++ b/content/docs/cli/telemetry.mdx @@ -0,0 +1,107 @@ +--- +title: Telemetry +description: Anonymous, opt-out, content-free CLI telemetry — exactly what is collected, and every way to turn it off. +--- + +# Telemetry + +The Reactor CLI collects anonymous, content-free usage telemetry so we can see how many people use Reactor and for what. It is on by default for interactive runs, off in CI, and one line to turn off for good. This page is the plain-language version; the exact field-by-field schema lives in [`TELEMETRY.md`](https://github.com/openprose/prose/blob/main/packages/reactor-cli/TELEMETRY.md) in the package. + +The posture, in one breath: + +- **Anonymous.** The only identifier is a random per-machine UUID. No account, no user, no email, no IP-derived geo. +- **CLI-only.** Telemetry lives entirely in `@openprose/reactor-cli`. The SDK, `@openprose/reactor`, emits zero network traffic — a library that phones home from inside your stack is a trust violation, so it never does. +- **Content-free.** We collect the *shape* of usage, never the *content* — never your prose, file paths, names, prompts, keys, or model input/output. +- **Opt-out, honored permanently.** `DO_NOT_TRACK=1`, `REACTOR_TELEMETRY=0`, or `reactor telemetry disable` each turn it off for good. +- **Fire-and-forget.** A short, bounded request that never blocks, slows, or errors a command — even when the endpoint is down. + +## What is collected + +Each event carries a coarse, content-free shape of one command: + +- The CLI and SDK versions, Node version, OS family, and CPU arch. +- A `ci` boolean, the command name, and a coarse outcome (`success`, `failure`, or `cache_hit`). +- Bucketed durations and counts — never raw numbers. A compile or run also reports bucketed node/edge/cost counts, the disposition tally, and the provider **class** (`anthropic`, `openai`, `local`, ...), never a key. +- On a failure, a coarse error **category** (`provider`, `config`, `io`, `chain_verify`, `unknown`) — never the message or stack. + +Counts collapse to `0` / `1-5` / `6-20` / `21+`; durations to `<1s` / `1-5s` / `5-30s` / `30s+`. The bucketers exist so a raw value can never slip through. + +## What is never collected + +The trust invariant is that we send the shape of usage, never the content of it. Forbidden in every field, no exceptions: + +- World-model content, the markdown, prompt text, or any model input/output +- File paths, project or directory names, exact facet or node names +- API keys, tokens, base URLs, or model ids +- Error messages or stacks +- Raw counts or durations, and precise or IP-derived geo + +## Turning it off + +Telemetry is disabled if **any** one of these holds — each is permanent on its own: + +| Condition | How | +| --- | --- | +| `DO_NOT_TRACK` is truthy | `export DO_NOT_TRACK=1` (the [consoledonottrack.com](https://consoledonottrack.com) convention). | +| Reactor env opt-out | `export REACTOR_TELEMETRY=0`, or set `REACTOR_TELEMETRY_DISABLED`. | +| Offline | `REACTOR_OFFLINE=1`. | +| CI | `CI` is truthy, or a known CI marker is set. | +| Non-interactive | stdout is not a TTY (piped / redirected / automated runs are never tracked). | +| Project config | `reactor.yml` → `telemetry.enabled: false`. | +| Machine config | `reactor telemetry disable` (writes `~/.reactor/config.json`). | + +So a CI pipeline or a piped invocation is off without you doing anything. The simplest permanent opt-out on a workstation is: + +```sh +reactor telemetry disable +``` + +### First-run notice + +The first time you run `reactor doctor` on a machine, a short notice prints to stdout: what is collected, that it is anonymous, the one-liner to turn it off, and a pointer to the schema. It shows once per machine. There is no banner at CLI entry and nothing on stderr. + +## Inspecting and managing it + +```sh +reactor telemetry # status: enabled?, reason if off, endpoint, install id +reactor telemetry disable # permanent machine-level opt-out +reactor telemetry enable # clear the machine-level opt-out +reactor telemetry --dump # print the exact JSON that would be sent, then exit +``` + +`reactor telemetry --dump` is the transparency surface: it prints the precise endpoint and Segment batch a representative event would send, and it never opens a socket. All three of `status`, `enable`, and `disable` accept `--json`. + +```sh +$ reactor telemetry --dump +{ + "endpoint": "https://api.openprose.ai/analytics", + "batch": [ + { + "type": "track", + "anonymousId": "3f8c1e0a-...", + "event": "reactor.doctor", + "properties": { + "schemaVersion": 1, + "cliVersion": "0.2.0", + "command": "doctor", + "outcome": "success", + "durationBucket": "<1s" + }, + "context": { "library": "@openprose/reactor-cli" }, + "timestamp": "2026-06-02T18:00:00.000Z" + } + ] +} +``` + +## Endpoints + +The published CLI sends to `https://api.openprose.ai/analytics`. Local and dev builds send to `https://api.dev.openprose.ai/analytics`. Self-hosters can redirect telemetry with the `REACTOR_TELEMETRY_ENDPOINT` environment variable, or per project: + +```yaml +# reactor.yml +telemetry: + endpoint: https://analytics.example.com/analytics +``` + +The collection code is open source under `src/telemetry/` in `@openprose/reactor-cli`, and the full field-by-field schema is published in [`TELEMETRY.md`](https://github.com/openprose/prose/blob/main/packages/reactor-cli/TELEMETRY.md). From 811d29a7c39e05d551fa72defa9333f9c2e8f726 Mon Sep 17 00:00:00 2001 From: irl-dan <97565471+irl-dan@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:03:14 -0700 Subject: [PATCH 2/3] docs(cspell): add telemetry-page domain terms (bucketers, consoledonottrack, hosters) --- .cspell.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index ea75ff9..4165054 100644 --- a/.cspell.json +++ b/.cspell.json @@ -97,7 +97,10 @@ "headlessly", "screenshotting", "unparseable", - "bucketings" + "bucketings", + "bucketers", + "consoledonottrack", + "hosters" ], "ignoreRegExpList": ["https?://[^\\s)]+", "`[^`]*`"] } From 7f4c4cd6ae2c9fcb3a82af727d3538621e75a13f Mon Sep 17 00:00:00 2001 From: irl-dan <97565471+irl-dan@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:04:52 -0700 Subject: [PATCH 3/3] docs(cli): use house-style -- instead of em-dashes on telemetry page --- content/docs/cli/telemetry.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/docs/cli/telemetry.mdx b/content/docs/cli/telemetry.mdx index 77533af..69574ca 100644 --- a/content/docs/cli/telemetry.mdx +++ b/content/docs/cli/telemetry.mdx @@ -1,6 +1,6 @@ --- title: Telemetry -description: Anonymous, opt-out, content-free CLI telemetry — exactly what is collected, and every way to turn it off. +description: Anonymous, opt-out, content-free CLI telemetry -- exactly what is collected, and every way to turn it off. --- # Telemetry @@ -10,10 +10,10 @@ The Reactor CLI collects anonymous, content-free usage telemetry so we can see h The posture, in one breath: - **Anonymous.** The only identifier is a random per-machine UUID. No account, no user, no email, no IP-derived geo. -- **CLI-only.** Telemetry lives entirely in `@openprose/reactor-cli`. The SDK, `@openprose/reactor`, emits zero network traffic — a library that phones home from inside your stack is a trust violation, so it never does. -- **Content-free.** We collect the *shape* of usage, never the *content* — never your prose, file paths, names, prompts, keys, or model input/output. +- **CLI-only.** Telemetry lives entirely in `@openprose/reactor-cli`. The SDK, `@openprose/reactor`, emits zero network traffic -- a library that phones home from inside your stack is a trust violation, so it never does. +- **Content-free.** We collect the *shape* of usage, never the *content* -- never your prose, file paths, names, prompts, keys, or model input/output. - **Opt-out, honored permanently.** `DO_NOT_TRACK=1`, `REACTOR_TELEMETRY=0`, or `reactor telemetry disable` each turn it off for good. -- **Fire-and-forget.** A short, bounded request that never blocks, slows, or errors a command — even when the endpoint is down. +- **Fire-and-forget.** A short, bounded request that never blocks, slows, or errors a command -- even when the endpoint is down. ## What is collected @@ -21,8 +21,8 @@ Each event carries a coarse, content-free shape of one command: - The CLI and SDK versions, Node version, OS family, and CPU arch. - A `ci` boolean, the command name, and a coarse outcome (`success`, `failure`, or `cache_hit`). -- Bucketed durations and counts — never raw numbers. A compile or run also reports bucketed node/edge/cost counts, the disposition tally, and the provider **class** (`anthropic`, `openai`, `local`, ...), never a key. -- On a failure, a coarse error **category** (`provider`, `config`, `io`, `chain_verify`, `unknown`) — never the message or stack. +- Bucketed durations and counts -- never raw numbers. A compile or run also reports bucketed node/edge/cost counts, the disposition tally, and the provider **class** (`anthropic`, `openai`, `local`, ...), never a key. +- On a failure, a coarse error **category** (`provider`, `config`, `io`, `chain_verify`, `unknown`) -- never the message or stack. Counts collapse to `0` / `1-5` / `6-20` / `21+`; durations to `<1s` / `1-5s` / `5-30s` / `30s+`. The bucketers exist so a raw value can never slip through. @@ -38,7 +38,7 @@ The trust invariant is that we send the shape of usage, never the content of it. ## Turning it off -Telemetry is disabled if **any** one of these holds — each is permanent on its own: +Telemetry is disabled if **any** one of these holds -- each is permanent on its own: | Condition | How | | --- | --- |