Skip to content

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, flush

Default state

  • The default provider is NullTelemetryProvider. Its init / identify / track / flush / shutdown are 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 typeTrigger
app.openedBoot, after telemetry init
app.errorTop-level ErrorBoundary catches an exception
project.openedActive project loads (storage or .mstack import)
project.saved.mstack written to disk
project.exportedAny export completes (PNG / sheet / GIF / JSON / mstack / upscaled-png)
tool.activatedUser picks a tool
export.completedPNG / sheet / GIF / JSON / upscale finishes (with duration when measured)
backup.createdAuto or manual backup snapshot
backup.restoredA backup is restored
autotile.generatedAuto-tile-47 dialog Generate button (multi-source flag included)
upscale.runxBR or Nearest upscale runs (algorithm + scale)
plan.changedDev 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.


Motestack is a personal hobby project. The editor and these docs ship under no warranty — back up your `.mstack` files.