Skip to content

Commit af49522

Browse files
hthillmanclaude
andcommitted
feat: add opt-in analytics with Mixpanel/PostHog provider abstraction
- Swappable providers (Mixpanel default, PostHog via VITE_ANALYTICS_PROVIDER) - Opt-in model with first-run disclosure in onboarding flow - Settings toggle, env var support (DO_NOT_TRACK, SCOPE_TELEMETRY_DISABLED) - Event tracking across all UI surfaces - Identity resolution with daydream.live accounts - Pre-disclosure event queuing - Telemetry docs page Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> Signed-off-by: Hunter Hillman <[email protected]>
1 parent 8668d28 commit af49522

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1975
-43
lines changed

app/src/preload.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ contextBridge.exposeInMainWorld('scope', {
112112
};
113113
},
114114

115+
getEnvTelemetryDisabled: () => {
116+
return (
117+
process.env.SCOPE_TELEMETRY_DISABLED === "1" ||
118+
process.env.DO_NOT_TRACK === "1"
119+
);
120+
},
121+
115122
openExternal: (url: string) => ipcRenderer.invoke(IPC_CHANNELS.OPEN_EXTERNAL, url),
116123

117124
onAuthCallback: (callback: (data: { token: string; userId: string | null; state: string | null }) => void) => {

docs/telemetry.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Scope Telemetry
2+
3+
Scope can collect anonymous usage data to help us improve the app. Telemetry
4+
is **off by default** — you choose whether to enable it during first launch or
5+
in Settings. This page documents exactly what is collected, how to opt in, and
6+
how identity works.
7+
8+
**Last updated:** 2026-03-23
9+
10+
## What We Collect
11+
12+
We track UI interactions and feature usage patterns. Every event is an explicit
13+
`track()` call in the source code — no auto-capture, no session replay.
14+
15+
### What Is NOT Collected
16+
17+
- Prompt text or any user-generated creative content
18+
- Generated images or video frames
19+
- File paths (local or remote)
20+
- Model file names or specific LoRA names
21+
- IP addresses (disabled in Mixpanel SDK config)
22+
- Window position or screen arrangement
23+
- Clipboard content
24+
- Individual keystrokes or mouse movements
25+
- Specific error messages (only error categories)
26+
27+
## How to Enable / Disable
28+
29+
Telemetry is off by default. You can enable it during first launch when
30+
prompted, or at any time in Settings.
31+
32+
### 1. Settings Toggle (UI)
33+
34+
Open **Settings > General > Privacy** and toggle **"Send anonymous usage
35+
data"** on or off. Takes effect immediately — no restart required.
36+
37+
### 2. Environment Variable (Scope-specific) — Force Disable
38+
39+
```bash
40+
SCOPE_TELEMETRY_DISABLED=1 daydream-scope
41+
```
42+
43+
### 3. Environment Variable (Global Convention)
44+
45+
```bash
46+
DO_NOT_TRACK=1 daydream-scope
47+
```
48+
49+
This follows the [Console Do Not Track](https://consoledonottrack.com/)
50+
convention used by Next.js, Astro, Gatsby, and others.
51+
52+
### Precedence
53+
54+
`SCOPE_TELEMETRY_DISABLED` > `DO_NOT_TRACK` > UI setting > default (OFF).
55+
56+
If an environment variable disables telemetry, the Settings toggle shows as
57+
disabled with a note explaining why.
58+
59+
## Identity
60+
61+
- **Anonymous by default:** On first launch, Scope generates a random device ID
62+
(UUID v4) stored in localStorage. All events use this anonymous ID.
63+
- **Linked on login:** If you sign in to a Daydream account, events are linked
64+
to your Daydream user ID so we can see the journey from website to app. On
65+
logout, tracking reverts to the anonymous device ID.
66+
- **No cross-device tracking:** The device ID is local to your machine.
67+
68+
## Event Taxonomy
69+
70+
### Onboarding (`surface: "onboarding"`)
71+
72+
| Event | Fires When | Properties |
73+
|-------|------------|------------|
74+
| `onboarding_started` | Onboarding overlay renders | `is_first_launch` |
75+
| `onboarding_inference_selected` | User picks cloud or local | `mode` |
76+
| `onboarding_auth_completed` | OAuth succeeds ||
77+
| `onboarding_workflow_selected` | User picks a starter workflow | `workflowId` |
78+
| `onboarding_workflow_downloaded` | Download completes | `workflowId` |
79+
| `onboarding_started_from_scratch` | User picks "Start from scratch" ||
80+
| `onboarding_imported_workflow` | User imports a workflow file ||
81+
| `onboarding_completed` | Onboarding finishes ||
82+
| `telemetry_disclosure_shown` | Disclosure card renders | `path` |
83+
| `telemetry_disclosure_responded` | User clicks accept or disable | `action`, `path`, `time_to_respond_ms`, `auto_advanced` |
84+
85+
### Performance Mode (`surface: "performance_mode"`)
86+
87+
| Event | Fires When | Properties |
88+
|-------|------------|------------|
89+
| `generation_started` | User hits Play | `surface` |
90+
| `generation_stopped` | User hits Stop | `surface` |
91+
| `parameter_changed` | User adjusts a parameter (debounced 2s) | `parameter_type`, `surface` |
92+
| `prompt_edited` | User modifies prompt (debounced 2s) | `prompt_length` |
93+
| `lora_applied` | User selects a LoRA | `lora_count` |
94+
| `lora_removed` | User removes a LoRA | `lora_count` |
95+
| `input_source_changed` | User switches input source | `source_type` |
96+
| `output_configured` | User sets up an output | `output_type` |
97+
| `mapping_created` | User maps a control ||
98+
| `mapping_deleted` | User removes a mapping ||
99+
| `fullscreen_toggled` | User enters/exits fullscreen | `entered` |
100+
| `fps_reported` | Periodic (every 60s during generation) | `fps_avg`, `fps_min`, `fps_max` |
101+
| `resolution_changed` | User changes output resolution | `from_resolution`, `to_resolution` |
102+
103+
### Graph Mode (`surface: "graph_mode"`)
104+
105+
| Event | Fires When | Properties |
106+
|-------|------------|------------|
107+
| `graph_mode_entered` | User switches to graph mode | `node_count`, `connection_count` |
108+
| `graph_mode_exited` | User switches back | `duration_ms` |
109+
| `node_added` | User adds a node | `node_type` |
110+
| `node_removed` | User deletes a node | `node_type` |
111+
| `connection_created` | User connects two nodes ||
112+
| `connection_removed` | User disconnects nodes ||
113+
| `node_registry_opened` | User opens the node browser ||
114+
| `node_registry_searched` | User types in registry search (debounced 2s) | `query_length`, `results_count` |
115+
| `workflow_saved` | User saves the workflow | `node_count` |
116+
| `workflow_exported` | User exports workflow to file | `node_count` |
117+
| `workflow_imported` | User imports a workflow | `node_count`, `source` |
118+
119+
### Settings (`surface: "settings"`)
120+
121+
| Event | Fires When | Properties |
122+
|-------|------------|------------|
123+
| `settings_opened` | User opens Settings panel | `entry_point` |
124+
| `settings_section_viewed` | User navigates to a section | `section` |
125+
| `inference_mode_changed` | User switches cloud/local | `from_mode`, `to_mode` |
126+
| `telemetry_opt_out` | User disables telemetry | `source` |
127+
| `telemetry_opt_in` | User re-enables telemetry | `source` |
128+
129+
### Hub / Plugins (`surface: "hub_browser"`)
130+
131+
| Event | Fires When | Properties |
132+
|-------|------------|------------|
133+
| `hub_opened` | User opens the hub browser | `entry_point` |
134+
| `hub_searched` | User searches in hub (debounced 2s) | `query_length`, `results_count` |
135+
| `hub_item_viewed` | User clicks into an item detail | `item_type`, `item_id` |
136+
| `hub_item_installed` | User installs from hub | `item_type`, `item_id` |
137+
| `hub_item_install_failed` | Install fails | `item_type`, `error_type` |
138+
139+
### I/O Configuration (`surface: "io_config"`)
140+
141+
| Event | Fires When | Properties |
142+
|-------|------------|------------|
143+
| `io_panel_opened` | User opens an I/O config panel | `io_type` |
144+
| `io_config_changed` | User modifies I/O settings (debounced 2s) | `io_type` |
145+
146+
### App Chrome (`surface: "app_chrome"`)
147+
148+
| Event | Fires When | Properties |
149+
|-------|------------|------------|
150+
| `app_launched` | Scope main window renders | `load_time_ms` |
151+
| `app_closed` | User quits Scope | `session_duration_ms` |
152+
| `mode_switched` | User toggles performance/graph | `from_mode`, `to_mode` |
153+
| `error_dialog_shown` | An error dialog appears | `error_category` |
154+
| `user_logged_in` | Auth completes in Scope | `source` |
155+
| `user_logged_out` | User logs out | `source` |
156+
157+
### Super Properties (Attached to Every Event)
158+
159+
| Property | Description |
160+
|----------|-------------|
161+
| `app_version` | Scope version string |
162+
| `platform` | OS platform (darwin, win32, linux) |
163+
| `session_id` | Random UUID per session |
164+
| `timestamp` | Unix timestamp (ms) |
165+
166+
## Technical Details
167+
168+
- **SDK:** [Mixpanel Browser SDK](https://developer.mixpanel.com/docs/javascript-quickstart)
169+
- **Persistence:** localStorage
170+
- **IP collection:** Disabled (`ip: false`)
171+
- **Auto-capture:** Disabled — every event is an explicit `track()` call
172+
- **Debouncing:** Continuous inputs (sliders, search) are debounced at 2 seconds
173+
- **Pre-disclosure queue:** Events generated before the user sees the telemetry
174+
disclosure are queued in memory. If the user accepts, they're sent. If they
175+
decline, they're dropped.
176+
177+
## Source Code
178+
179+
All tracking calls are in the open-source codebase. Search for `trackEvent(`
180+
or `track(` in the `frontend/src/` directory to see every event.
181+
182+
The telemetry module lives at `frontend/src/lib/telemetry.ts`.

0 commit comments

Comments
 (0)