Telemetry
Motestack ships with a telemetry contract, not a telemetry pipeline. The goal is to have the call sites, types, and consent flow already wired so adopting a backend later (Plausible, PostHog, custom) is a single-file swap. No data is collected today.
Where it lives
src/features/telemetry/
types.ts — TelemetryEvent union, TelemetryTraits
provider.ts — TelemetryProvider interface + NullTelemetryProvider
index.ts — public API: initTelemetry, setOptIn, track, identify, flushDefault state
- The default provider is
NullTelemetryProvider. Itsinit / identify / track / flush / shutdownare all no-ops. - The opt-in toggle (
UserPreferences.telemetryOptIn) is off by default. - Even with opt-in on, the null provider does nothing, so the toggle has no observable effect in v0.15.x. It exists so consent is recorded ahead of any future backend integration.
What gets emitted
Already wired call sites (every one is gated by the opt-in):
| Event type | Trigger |
|---|---|
app.opened | Boot, after telemetry init |
app.error | Top-level ErrorBoundary catches an exception |
project.opened | Active project loads (storage or .mstack import) |
project.saved | .mstack written to disk |
project.exported | Any export completes (PNG / sheet / GIF / JSON / mstack / upscaled-png) |
tool.activated | User picks a tool |
export.completed | PNG / sheet / GIF / JSON / upscale finishes (with duration when measured) |
backup.created | Auto or manual backup snapshot |
backup.restored | A backup is restored |
autotile.generated | Auto-tile-47 dialog Generate button (multi-source flag included) |
upscale.run | xBR or Nearest upscale runs (algorithm + scale) |
plan.changed | Dev plan override switches |
Payloads are strictly aggregate / categorical. Project content (pixels, palette, names) is never transmitted by design.
Privacy posture (for the eventual implementation)
- Off by default. Users opt in via Preferences → Privacy.
- Identifiers must be anonymous (random uuid persisted in localStorage), never tied to email or hardware fingerprint.
- Session id is fresh per page-load — never persisted.
- Events should remain aggregate; never include free-form user input.
- Telemetry must never crash the app —
track()swallows exceptions and warns to the console.
Wiring a real backend (future)
ts
import { initTelemetry } from "@/features/telemetry";
class MyProvider implements TelemetryProvider {
// … see provider.ts contract
}
await initTelemetry(new MyProvider());setOptIn(false) immediately calls provider.shutdown(), so flipping consent off severs the data flow without a reload.
Related
- Preferences — the toggle lives here under "Privacy".
- Monetization foundations — companion contract: feature flags + quotas, also off-by-default in 0.9.x.