Files
TimeTracker/docs/telemetry-architecture.md
T
Dries Peeters b0dde80ba9 feat(web): high-visibility support modal, prompts, and supporter UX
Add a support modal with usage stats, tier and license links, share control, and offline-safe outbound CTAs. Surface support from the header, sidebar, user menu, dashboard card, and settings "Support & Community" section without hiding entry points when a supporter license is active.

Introduce UsageStatsService and a persisted users.support_stats_reports_generated counter incremented on key report exports and custom report views. Add SupportPromptService for session-scoped soft toasts (after export, dashboard milestones, long session via POST /donate/request-soft-prompt).

Wire consent-aware track_event names support.* and mirror funnel rows in DonationInteraction; fix has_recent_donation_click to treat link_clicked as a recent click. Document events and SUPPORT_* / migration notes in docs.

Tests: tests/test_support_services.py for prompt and usage stats behavior.
2026-04-15 10:55:37 +02:00

3.6 KiB

Telemetry Architecture

This document describes the privacy-aware, two-layer telemetry system: base telemetry (always-on, minimal) and detailed analytics (opt-in only).

Overview

Layer When Purpose Events / Data
Base telemetry Always (when OTLP sink is configured) Install footprint, version/platform distribution, active installs base_telemetry.first_seen, base_telemetry.heartbeat
Detailed analytics Only when user opts in Feature usage, funnels, errors, retention All product events (e.g. auth.login, timer.started)
  • Consent: Stored in installation.json (telemetry_enabled) and synced to settings.allow_analytics. Source of truth: installation_config.get_telemetry_preference() / is_telemetry_enabled().
  • Identifiers: One install_id (random UUID in installation config) used for base telemetry and, when opt-in, sent with product events. Product events use internal user_id identity.

Base Telemetry (Always-On)

  • Schema (no PII): install_id, app_version, platform, os_version, architecture, locale, timezone, first_seen_at, last_seen_at, heartbeat_at, release_channel, deployment_type.
  • Events: base_telemetry.first_seen (once per install), base_telemetry.heartbeat (e.g. daily via scheduler).
  • Sink: Grafana Cloud OTLP with identity = install_id. No user-level linkage.
  • Trigger: First-seen sent at app startup (idempotent). Heartbeat via scheduled task (e.g. 03:00 daily).
  • Retention: Configure in Grafana backend (e.g. 12 months for base). No raw IP storage.

Detailed Analytics (Opt-In Only)

  • Gated by: is_telemetry_enabled() / allow_analytics. No product events sent without opt-in.
  • Events: Existing names (e.g. auth.login, timer.started, project.created). Support funnel events use the support.* prefix (e.g. support.modal_opened); see all_tracked_events.md. Optional prefix analytics.* in future.
  • Properties: Include install_id, app_version, deployment, request context (path, browser, device) only when opted in.
  • Sink: Grafana Cloud OTLP (identity = user_id for events).
  • Retention: Per Grafana retention policy. Document in privacy policy.
  • Opt-in: Setup wizard or Admin → Settings (Privacy & Analytics) or Admin → Telemetry. Enabling triggers one opt-in install ping (check_and_send_telemetry()).
  • Opt-out: Same toggles. Detailed analytics stop immediately; base telemetry continues (minimal footprint).
  • Data minimization: Base layer is fixed schema. Detailed layer only when user agrees.

Event Naming

  • Reserved: base_telemetry.* for base layer. Do not use for product events.
  • Product events: Keep current names (e.g. timer.started) or use analytics.*; all gated by opt-in.

Implementation

  • Service: app/telemetry/service.pysend_base_first_seen(), send_base_heartbeat(), send_analytics_event(), is_detailed_analytics_enabled().
  • App entry points: app/__init__.pytrack_event, track_page_view, identify_user delegate to telemetry service (consent-aware).
  • Scheduler: app/utils/scheduled_tasks.py — job send_base_telemetry_heartbeat (daily).
  • Startup: In create_app, after scheduler start, call send_base_first_seen() once per install.

Sink Configuration

  • Base and detailed telemetry are emitted through the same OTLP sender in app/telemetry/service.py.
  • Required configuration:
    • GRAFANA_OTLP_ENDPOINT
    • GRAFANA_OTLP_TOKEN