You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add Phase 5: telemetry opt-out via config and CI detection strategy
Adds plan for persistent telemetry disable in config.toml (top-level setting,
not per-profile) with precedence: env var > config > default enabled. Includes
CI/CD detection strategy that tags CI traffic as source="ci" rather than
auto-disabling, preserving analytics visibility while enabling server-side
filtering.
https://claude.ai/code/session_01Y2eJZgKG78nDyN6Uw2tWQx
Copy file name to clipboardExpand all lines: PLAN.md
+122Lines changed: 122 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -295,6 +295,9 @@ Not in scope for this CLI PR, but documents the expected server-side changes:
295
295
|`pkg/gateway/mcp/server.go`| Capture MCP client info, store on Server struct | Small |
296
296
|`pkg/gateway/mcp/tools.go`| Add telemetry wrapping in tool dispatch | Medium |
297
297
|`pkg/hookdeck/sdkclient.go`| No changes needed (already reads singleton) | None |
298
+
|`pkg/config/config.go`| Add `TelemetryDisabled` field, read from viper in `constructConfig()`| Small |
299
+
|`pkg/hookdeck/telemetry.go`| Update `telemetryOptedOut()` to accept config flag; add `detectSource()` for CI detection | Small |
300
+
|`pkg/hookdeck/client.go`| Add `TelemetryDisabled` field, thread through opt-out check | Small |
298
301
299
302
## Risks and Edge Cases
300
303
@@ -308,6 +311,125 @@ Not in scope for this CLI PR, but documents the expected server-side changes:
308
311
309
312
5.**Backward compatibility**: The server must handle both old (empty) and new telemetry payloads. Since it's JSON with new fields, old servers will simply ignore unknown keys. New servers should treat missing `source` as `"cli"` for backward compat.
310
313
314
+
## Phase 5: Telemetry Opt-Out via Config
315
+
316
+
### Problem
317
+
318
+
Telemetry opt-out is currently only possible via the `HOOKDECK_CLI_TELEMETRY_OPTOUT` environment variable. This requires users to set it in their shell profile or per-invocation, which is fragile and inconvenient. Users who want telemetry permanently disabled (corporate policy, personal preference) need a persistent config-based option.
319
+
320
+
### Design
321
+
322
+
Add a **top-level**`telemetry` setting to `config.toml`. No per-profile override initially — telemetry is a user-level concern, not a project-level one. We can always add profile granularity later if needed.
323
+
324
+
**Precedence (highest to lowest):**
325
+
1.`HOOKDECK_CLI_TELEMETRY_OPTOUT` env var (existing, unchanged)
**File: `pkg/hookdeck/client.go` and `pkg/hookdeck/sdkclient.go`**
371
+
372
+
Thread the config value through. The `Client` struct already receives config indirectly — the simplest approach is to add a `TelemetryDisabled bool` field to `Client` (set during construction in `Config.GetClient()`) and check it alongside the env var in `PerformRequest`.
373
+
374
+
**UX — setting the value:**
375
+
376
+
Users can edit `config.toml` directly, or we expose a command:
377
+
```bash
378
+
hookdeck config set telemetry_disabled true
379
+
hookdeck config set telemetry_disabled false
380
+
```
381
+
382
+
This depends on whether a generic `config set` command exists or is planned. If not, a simple `hookdeck telemetry off/on` subcommand is the alternative.
383
+
384
+
### CI/CD Environment Detection
385
+
386
+
**Context:** Some CLI tools (e.g., `npx @anthropic-ai/claude-code`) detect CI environments and disable telemetry by default. The question is whether hookdeck-cli should do the same.
387
+
388
+
**How CI detection works:** Check for well-known environment variables:
389
+
-`CI=true` (GitHub Actions, GitLab CI, CircleCI, Travis, most CI systems)
390
+
-`GITHUB_ACTIONS=true`
391
+
-`GITLAB_CI=true`
392
+
-`JENKINS_URL` is set
393
+
-`CODEBUILD_BUILD_ID` is set (AWS CodeBuild)
394
+
-`TF_BUILD=true` (Azure Pipelines)
395
+
-`BUILDKITE=true`
396
+
397
+
A simple `isCI()` function checking `os.Getenv("CI")` covers ~90% of cases.
398
+
399
+
**Arguments for disabling in CI:**
400
+
- CI runs can generate massive telemetry volume (every build, every PR, every retry)
401
+
- CI telemetry is "noisy" — it represents automation, not human decision-making
402
+
- CI environments often have strict data-exfiltration policies
403
+
- Users don't expect background analytics from build tools
404
+
405
+
**Arguments against disabling in CI:**
406
+
- Knowing that hookdeck-cli is used in CI/CD pipelines is genuinely useful product insight
407
+
- CI usage patterns differ from interactive use — that's *interesting*, not noise
408
+
- A GitHub Action (`hookdeck/hookdeck-cli-action`) is a deliberate integration — the user chose to put it in CI
409
+
- Silently disabling telemetry means you lose visibility into a growing use case
410
+
411
+
**Recommended approach — middle ground:**
412
+
413
+
1.**Detect CI but don't fully disable.** Instead, set `source: "ci"` (alongside `"cli"` and `"mcp"`) so CI traffic can be filtered server-side. This gives the analytics team the ability to include or exclude CI data as needed, without losing it entirely.
414
+
415
+
2.**Respect explicit opt-out.** If `HOOKDECK_CLI_TELEMETRY_OPTOUT=1` or `telemetry_disabled = true` is set, disable completely — same as interactive mode.
416
+
417
+
3.**Do NOT auto-disable.** The user made a deliberate choice to run hookdeck-cli in CI. Unlike, say, a package manager that runs implicitly, hookdeck-cli is an explicit integration. Disabling telemetry by default in CI would be overly conservative.
418
+
419
+
4.**Log/document it.** If `CI=true` is detected, the telemetry header includes `"source": "ci"` and nothing else changes. Document this behavior so CI-conscious users know what's sent.
420
+
421
+
**Implementation:**
422
+
```go
423
+
funcdetectSource() string {
424
+
if os.Getenv("CI") == "true" || os.Getenv("GITHUB_ACTIONS") == "true" {
425
+
return"ci"
426
+
}
427
+
return"cli"// default; overridden to "mcp" by MCP server
428
+
}
429
+
```
430
+
431
+
This integrates cleanly with Phase 1's `source` field — it's just another source value. Server-side, PostHog dashboards can filter by `source = "ci"` to see CI-specific usage or exclude it from interactive metrics.
432
+
311
433
## Testing Strategy
312
434
313
435
1.**Unit tests for telemetry struct**: Verify JSON serialization includes new fields
0 commit comments