privacy

Your voice doesn't
leave your machine.

Dimmy runs locally by default. We collect a deliberately small amount of anonymous telemetry to understand how the app is used and to fix crashes. Never enough to identify you, never enough to recover what you said.

Disable it any time inSettings → Privacy(each toggle is independent).
Last updated: 2026-05-11

TL;DR

  • Local mode is the default. Audio and transcripts never leave your device.
  • Telemetry is anonymous, opt-out, two independent toggles (analytics, crash reports).
  • We send event names and numbers (counts, durations, error categories) to PostHog EU and Sentry EU. No audio, no text, no prompts, no IP, no API keys.
  • Your API keys live encrypted on your machine with AES-256-GCM and a machine-bound key.
  • No account, no signup, no email required.
#data

What we collect

Every section below corresponds to specific code paths in the Rust core. The full list of events and their payloads is below.

Per-install identifier

A random UUIDv4 generated on first launch, stored locally in ~/.config/dimmy/analytics_id (or %APPDATA%\dimmy\analytics_id on Windows). It only exists to de-duplicate "1 user did X 5 times" from "5 users each did X once". Reset it any time from Settings → Privacy → Reset anonymous ID. It is never linked to anything that could identify you.

Per-process session identifier

A fresh UUIDv4 generated on each app launch, not persisted. Lets us answer questions like "how many transcriptions per session?" without tracking open or close events.

Platform context

Attached to every event:

  • App version (e.g. 0.6.27).
  • Operating system family (windows / macos).
  • CPU architecture (x86_64 / aarch64).

Lifecycle events

  • app.startedWhen the app launches. Includes cold-start time in milliseconds.
  • app.session_endedWhen the app closes. Includes total session duration (seconds) and number of transcriptions in the session.

Transcription events

  • transcription.completedWhen a transcription succeeds. Path (local whisper.cpp vs cloud provider), provider category (groq / openai / anthropic / gemini / deepgram / openrouter / local: only the tag, never the URL or API key), audio duration in seconds, processing time in milliseconds, word count (the number only, never the words), language ISO code (e.g. en, it), whether filler removal ran (boolean), whether LLM post-processing ran (boolean).
  • transcription.failedProvider category and error category (e.g. 401, timeout).
  • transcription.cancelledAudio duration up to the cancel.

LLM post-processing events

  • llm.appliedProvider category, style name, tone name, processing time. Never the prompt. Never the output.
  • llm.failedProvider category and error category.

Configuration changes

When you change any of these in Settings, we log that the change happened. For free-text fields we never log the value, just that something changed.

  • STT mode toggle (local ↔ cloud)
  • Cloud provider switch (categorical: groq → openai, etc.)
  • LLM enabled toggle
  • LLM style dropdown
  • Preprocessing toggle
  • Input gain slider value (number)
Not tracked: prompt text, custom LLM prompt, microphone device name, shortcut combo string.

Feature usage

  • feature.hotkey_triggeredWhen the global hotkey starts a recording. Helps us understand whether people prefer hotkey or button.
  • feature.api_key_setWhen you save an API key, we log the scope (stt / llm) and the provider category. The key itself never leaves your machine.

Performance + stability

  • perf.startup_msCold-start duration in milliseconds.
  • perf.gpu_statusOn each launch: which GPU backend was compiled (vulkan / cuda / metal / cpu), whether the previous launch crashed during GPU init, whether a sticky known-bad marker is set.
  • error.gpu_crashOnly on the launch immediately after a GPU crash: which backend, which call site (e.g. whisper_load: <path>, with paths scrubbed).

Counters

Atomic increment operators on a small set of cumulative per-user counters. They let us segment active users from users who installed but never tried it, without scanning every event.

  • total_transcriptions
  • total_transcription_failures
  • total_transcriptions_cancelled
  • total_llm_uses
  • total_llm_failures
  • total_sessions

Person properties

Attached to your anonymous-ID record so dashboards can build cohorts and retention curves.

  • first_seen_at: timestamp of your first event (set once).
  • first_app_version, first_os, first_arch: your install context (set once).
  • latest_app_version, latest_seen_at, latest_os, latest_arch: refreshed on every event.
  • latest_stt_provider, latest_stt_mode: last STT provider used.
  • latest_llm_provider: last LLM provider used.

Crash reports (Sentry)

When the app crashes or hits an error path, we send to Sentry EU:

  • Error message, truncated to 4 KB. Secret-shaped substrings are replaced with <redacted>.
  • A Rust stack trace (function names, currently mangled in shipping builds, source-mapped in a future release).
  • Platform context (OS, arch, app version, environment = production).
  • The anonymous ID (so we can de-duplicate the same crash from the same user).
We do not send server name, hostname, username, environment variables (PATH, HOME, USERPROFILE and similar are stripped), IP addresses (Sentry EU drops them at ingest), file paths, microphone device names, transcripts, prompts.

Feedback form

The Settings → Send feedback form goes to Sentry as a tagged message. The text you type is included verbatim. The email field is optional and only included if you explicitly type it. We never auto-fill it from anywhere.

What we never collect

There is no telemetry path in the codebase that sends any of the following. Audit the source if you want to verify, every send site is logged with [telemetry] in the local log.

  • The audio you record.
  • The text of any transcription.
  • Any prompt text (system, user, custom).
  • Any LLM output.
  • Names of contacts, files, or applications you transcribe into.
  • Microphone device names or hardware fingerprints.
  • File paths beyond a categorical "where this kind of file lives" tag (and even those are scrubbed).
  • API keys. They live in ~/.config/dimmy/keys.enc, AES-256-GCM encrypted, and are sent only to the provider you configured.
  • IP addresses. Sentry EU drops them server-side; PostHog is forced to $ip: null in every event.
  • Username, hostname, email (the only exception is the explicit email you may type into the Feedback form).

Where the data goes

Two destinations, both hardcoded in the binary, both in EU data centres, both GDPR-aligned.

Analytics events
PostHog EU
https://eu.i.posthog.com

Anonymous usage events. The endpoint is hardcoded and cannot be overridden at runtime.

Crash reports + feedback
Sentry EU
https://o*.ingest.de.sentry.io

Stack traces, error messages (truncated and secret-redacted), and the optional text you submit through the Feedback form.

We control both projects. Only the Dimmy team can read the data.

Inspect what's being sent

Every telemetry call is logged locally. Open the log file and look for lines starting with [telemetry].

Windows%APPDATA%\dimmy\dimmy.log
macOS~/.config/dimmy/dimmy.log

Example of what you'll see in the log:

[2026-05-11 09:47:06] [telemetry] track event=transcription.completed
[2026-05-11 09:47:06] [telemetry] spawn send for event=transcription.completed (payload 275 bytes)
[2026-05-11 09:47:06] [telemetry] send: HTTP 200 OK (sent=4)

You can also confirm the PostHog and Sentry project keys embedded in the build: search for key-diag in the same log. The prefix is logged once per process, so you can verify we're sending to the project we claim and nowhere else.

How to disable

  1. 1Open the app, click the gear on the pill (or right-click the menu-bar / tray icon).
  2. 2Go to Privacy.
  3. 3Flip Send anonymous usage data off to stop PostHog analytics.
  4. 4Flip Send crash reports off to stop Sentry.
  5. 5Each toggle takes effect immediately for events emitted after the flip. Events already sent cannot be recalled.

Want to start fresh? Settings → Privacy → Reset anonymous ID rotates your per-install UUID.

Changes to this policy

Material changes are announced in the release notes. The ground truth lives in PRIVACY.md inside the app repository. Run git log PRIVACY.md to see the full history of changes.

Contact

Questions, deletion requests, or anything else: [email protected]. Security issues: [email protected].