Skip to content

Commit db79c86

Browse files
mikhael28claude
andauthored
fix: respect MASTRA_TELEMETRY_DISABLED across CLI, core, and Studio (#16990)
## Summary A user reported that setting `MASTRA_TELEMETRY_DISABLED=true` in their project's `.env` did not stop PostHog network requests from `localhost:4112` after running `mastra dev`. Investigation found bugs in **three** places, each with different (and sometimes wrong) handling of the env var. This PR integrates [PR #16921](#16921) (Devin session for @smthomas — partial overlap) on top of the missing deployer fix that #16921 didn't address. ### Bugs | Location | Behavior before | Fix | |---|---|---| | `packages/deployer/src/server/index.ts:490` | Hardcoded `telemetryDisabled: \`''\`` when injecting playground HTML, so `window.MASTRA_TELEMETRY_DISABLED` was always empty in the browser regardless of `.env` | Propagate `process.env.MASTRA_TELEMETRY_DISABLED` into the injected HTML | | `packages/core/src/telemetry/posthog.ts:13` | Strict `=== '1'` check — `true`, `yes`, etc. silently kept enterprise telemetry on | Accept `1` / `true` / `yes` (case-insensitive, trimmed) | | `packages/cli/src/analytics/index.ts` constructor | Ran disk I/O (write `mastra-cli.json` with tracking IDs) and generated a distinctId before checking the env var | Early-return at the top of the constructor when disabled | | `packages/playground/src/lib/analytics.tsx` | Plain truthy check — would have parsed `'false'` as truthy if the dev server had ever propagated it | Match the canonical `1` / `true` / `yes` parser used elsewhere | ### Why this is a superset of #16921 PR #16921 modified the playground's parser to use the canonical truthy values, but it **did not touch `packages/deployer/src/server/index.ts:490`**. Under #16921 alone, `window.MASTRA_TELEMETRY_DISABLED` would still be the hardcoded empty string injected by the dev server, the canonical parser would correctly return `false` for an empty string, and **the user-reported network requests would still fire**. The deployer fix in this PR is what actually stops the reported behavior. The other three files in this PR are taken verbatim from #16921. ## Test plan - [x] `vitest run src/telemetry/posthog.test.ts` in `@mastra/core` — 12/12 passing (parameterized matrix from #16921) - [x] `vitest run src/build/utils.test.ts` in `@mastra/deployer` — 85/85 passing - [ ] Manual: scaffold a fresh project with `npm create mastra`, set `MASTRA_TELEMETRY_DISABLED=true` in `.env`, run `mastra dev`, confirm no PostHog requests in the browser Network tab - [ ] Manual: same setup without the env var — confirm PostHog requests still fire (regression check) - [ ] Manual: confirm `mastra-cli.json` is **not** written when telemetry is disabled ## Coordination Should be merged in place of (or with closure of) #16921 since this is a strict superset. /cc the author of #16921. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## ELI5 There was a setting (MASTRA_TELEMETRY_DISABLED) meant to turn off analytics, but different parts of the app interpreted it differently or didn't pass it to the browser. This PR makes the setting consistently disable telemetry everywhere by (1) sending the env value to the Studio browser, and (2) treating any common "truthy" value (e.g., "1", "true", "yes", case-insensitive, trimmed) as "disable telemetry". ## Problem MASTRA_TELEMETRY_DISABLED was handled inconsistently: - CLI (packages/cli/src/analytics/index.ts): already treated truthy values as disabling telemetry (correct). - Core (packages/core/src/telemetry/posthog.ts): only `'1'` disabled telemetry, ignoring other truthy values. - Studio/browser (packages/deployer/src/server/index.ts → playground): the served HTML injected an empty string, so window.MASTRA_TELEMETRY_DISABLED was always falsy and browser PostHog always initialized. This meant setting MASTRA_TELEMETRY_DISABLED=true in .env didn't reliably stop PostHog requests. ## Changes - packages/deployer/src/server/index.ts - Propagate process.env.MASTRA_TELEMETRY_DISABLED into the injected Studio HTML template (was hardcoded to empty string), so the browser sees the actual env value. - packages/core/src/telemetry/posthog.ts - Introduced TRUTHY_DISABLED_VALUES = new Set(['1','true','yes']). - Updated isEETelemetryEnabled() to trim/lowercase the env value and disable telemetry for any of those truthy values (previously only '1'). - packages/core/src/telemetry/posthog.test.ts - Updated tests to parameterize telemetry-disabled and telemetry-enabled cases to cover '1', 'true', 'TRUE', 'yes', ' True ' as disabling, and values like '0', 'false', 'no', 'off', 'on' as not disabling. - packages/playground/src/lib/analytics.tsx - Added matching client-side isTelemetryDisabled() logic reading window.MASTRA_TELEMETRY_DISABLED and treating '1','true','yes' (trimmed, lowercased) as disabling telemetry. - packages/cli/src/analytics/index.ts - Telemetry enablement remains consistent; CLI already treats those truthy values as disabling telemetry. The constructor short-circuits early when telemetry is disabled (avoids disk I/O and PostHog client creation). - Changesets - Release notes updated for `@mastra/deployer` and `@mastra/core` documenting the fix and canonical truthy disable values. ## Result MASTRA_TELEMETRY_DISABLED set to any common truthy value (e.g., true, 1, yes — case-insensitive, trimmed) now consistently disables telemetry across CLI, core (server-side), and the browser-based Studio/playground. When disabled, PostHog clients are not constructed and no telemetry network requests are made. ## Tests - Unit: core telemetry tests updated and passing (5/5); deployer build utils (85/85) unchanged/passing per test plan. - Manual: documented manual steps to scaffold a project and verify PostHog requests stop when toggling MASTRA_TELEMETRY_DISABLED. ## Reviewer question Core previously only disabled telemetry for the literal string '1' and had a dedicated test that suggested intentional strictness. If strict behavior should be preserved, revert the core change and document that only MASTRA_TELEMETRY_DISABLED=1 is canonical. Otherwise, this PR's relaxed behavior (accepting '1', 'true', 'yes') ensures consistent and user-friendly opt-out handling across components. <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16990?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ab7e12c commit db79c86

7 files changed

Lines changed: 69 additions & 17 deletions

File tree

.changeset/public-steaks-kiss.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@mastra/deployer': patch
3+
---
4+
5+
Fixed Studio playground browser telemetry not respecting `MASTRA_TELEMETRY_DISABLED`. The dev server was hardcoding an empty value into the served `index.html`, so `window.MASTRA_TELEMETRY_DISABLED` was always falsy in the browser and the playground React app initialized PostHog regardless of the user's `.env`. The dev server now propagates `process.env.MASTRA_TELEMETRY_DISABLED` to the browser, where the playground applies the same canonical opt-out parsing as the rest of the framework.
6+
7+
**Before:** Setting `MASTRA_TELEMETRY_DISABLED=true` in `.env` had no effect on playground network requests to PostHog.
8+
9+
**After:**
10+
11+
```bash
12+
# .env
13+
MASTRA_TELEMETRY_DISABLED=true
14+
```
15+
16+
Playground analytics are now disabled.

.changeset/thick-numbers-dance.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@mastra/core': patch
3+
'mastra': patch
4+
---
5+
6+
Fixed `MASTRA_TELEMETRY_DISABLED` opt-out detection. The values `1`, `true`, and `yes` (case-insensitive, trimmed) now reliably disable telemetry in both `@mastra/core` enterprise events and the `mastra` CLI's PostHog analytics.
7+
8+
Previously, `@mastra/core` only treated the literal string `'1'` as disabled, so common opt-out values like `MASTRA_TELEMETRY_DISABLED=true` silently kept telemetry on.
9+
10+
The `mastra` CLI's `PosthogAnalytics` constructor now also short-circuits when telemetry is disabled — no disk I/O, no tracking ID generation, no PostHog client. Previously the config file (`mastra-cli.json`) was written even when telemetry was disabled.
11+
12+
**Example:**
13+
14+
```bash
15+
# .env — any of these now reliably disable telemetry
16+
MASTRA_TELEMETRY_DISABLED=true
17+
MASTRA_TELEMETRY_DISABLED=1
18+
MASTRA_TELEMETRY_DISABLED=yes
19+
```

packages/cli/src/analytics/index.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ export function setAnalytics(instance: PosthogAnalytics): void {
3030
}
3131

3232
export class PosthogAnalytics {
33-
private sessionId: string;
33+
private sessionId: string = '';
3434
private client?: PostHog;
35-
private distinctId: string;
35+
private distinctId: string = '';
3636
private version: string;
37-
private packageManager: string;
37+
private packageManager: string = '';
3838

3939
constructor({
4040
version,
@@ -46,6 +46,11 @@ export class PosthogAnalytics {
4646
host: string;
4747
}) {
4848
this.version = version;
49+
50+
if (!PosthogAnalytics.isTelemetryEnabled()) {
51+
return;
52+
}
53+
4954
this.packageManager = getPackageManager();
5055
const cliConfigPath = path.join(__dirname, 'mastra-cli.json');
5156
if (existsSync(cliConfigPath)) {
@@ -71,9 +76,7 @@ export class PosthogAnalytics {
7176
});
7277
}
7378

74-
if (this.isTelemetryEnabled()) {
75-
this.initializePostHog(apiKey, host);
76-
}
79+
this.initializePostHog(apiKey, host);
7780
}
7881

7982
private writeCliConfig({ distinctId, sessionId }: { distinctId: string; sessionId: string }): void {
@@ -99,12 +102,11 @@ export class PosthogAnalytics {
99102
});
100103
}
101104

102-
private isTelemetryEnabled(): boolean {
103-
// Check environment variable first
104-
if (process.env.MASTRA_TELEMETRY_DISABLED) {
105+
private static isTelemetryEnabled(): boolean {
106+
const value = process.env.MASTRA_TELEMETRY_DISABLED;
107+
if (value && ['1', 'true', 'yes'].includes(value.trim().toLowerCase())) {
105108
return false;
106109
}
107-
// Default to enabled
108110
return true;
109111
}
110112

packages/core/src/telemetry/posthog.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ describe('EE PostHog telemetry', () => {
3131
resetEETelemetryForTests();
3232
});
3333

34-
it('suppresses events when telemetry is disabled', () => {
35-
process.env['MASTRA_TELEMETRY_DISABLED'] = '1';
34+
it.each(['1', 'true', 'TRUE', 'yes', ' True '])('suppresses events when MASTRA_TELEMETRY_DISABLED=%s', value => {
35+
process.env['MASTRA_TELEMETRY_DISABLED'] = value;
3636

3737
captureEEEvent('ee_license_check', 'user-1', { license_hash: 'safe' });
3838

3939
expect(PostHog).not.toHaveBeenCalled();
4040
expect(capture).not.toHaveBeenCalled();
4141
});
4242

43-
it('only treats MASTRA_TELEMETRY_DISABLED=1 as disabled', () => {
44-
process.env['MASTRA_TELEMETRY_DISABLED'] = '0';
43+
it.each(['0', 'false', 'no', 'off', 'on'])('still sends events when MASTRA_TELEMETRY_DISABLED=%s', value => {
44+
process.env['MASTRA_TELEMETRY_DISABLED'] = value;
4545

4646
captureEEEvent('ee_license_check', 'user-1', { license_hash: 'safe' });
4747

packages/core/src/telemetry/posthog.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import { PostHog } from 'posthog-node';
44

55
const POSTHOG_API_KEY = 'phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT';
66
const POSTHOG_HOST = 'https://us.posthog.com';
7+
const TRUTHY_DISABLED_VALUES = new Set(['1', 'true', 'yes']);
78

89
let client: PostHog | null = null;
910

1011
export type EEEventName = 'ee_license_check' | 'ee_feature_used';
1112

1213
export function isEETelemetryEnabled(): boolean {
13-
return process.env['MASTRA_TELEMETRY_DISABLED'] !== '1';
14+
const value = process.env['MASTRA_TELEMETRY_DISABLED'];
15+
if (!value) {
16+
return true;
17+
}
18+
return !TRUTHY_DISABLED_VALUES.has(value.trim().toLowerCase());
1419
}
1520

1621
export function hashTelemetryValue(value: string): string {

packages/deployer/src/server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ export async function createHonoServer(
487487
cloudApiEndpoint: `'${cloudApiEndpoint}'`,
488488
experimentalFeatures: `'${experimentalFeatures}'`,
489489
templates: `'${templatesEnabled}'`,
490-
telemetryDisabled: `''`,
490+
telemetryDisabled: `'${process.env.MASTRA_TELEMETRY_DISABLED ?? ''}'`,
491491
requestContextPresets: `'${escapeForHtml(requestContextPresets)}'`,
492492
experimentalUI: `'${experimentalUI}'`,
493493
agentSignals: `'${agentSignals}'`,

packages/playground/src/lib/analytics.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,24 @@ import posthog from 'posthog-js';
33
import type { ReactNode } from 'react';
44
import { useEffect } from 'react';
55

6+
const TRUTHY_DISABLED_VALUES = ['1', 'true', 'yes'];
7+
8+
function isTelemetryDisabled(): boolean {
9+
const value = window.MASTRA_TELEMETRY_DISABLED;
10+
if (!value) {
11+
return false;
12+
}
13+
return TRUTHY_DISABLED_VALUES.includes(value.trim().toLowerCase());
14+
}
15+
616
export function PostHogProvider({ children }: { children: ReactNode }) {
717
useEffect(() => {
818
if ('brave' in navigator) {
919
console.info('[Analytics]: Telemetry is disabled for browser constraints.');
1020
return;
1121
}
1222

13-
if (window.MASTRA_TELEMETRY_DISABLED) {
23+
if (isTelemetryDisabled()) {
1424
console.info('[Analytics]: Telemetry is disabled.');
1525
return;
1626
}

0 commit comments

Comments
 (0)