Privacy & Analytics
Kangentic collects anonymous usage analytics, and crash and error reports, to help improve the product. No code or task content is ever collected.
Two vendors do two jobs, and both answer to the same kill switch: Aptabase counts product events, and Sentry groups and symbolicates errors so a crash is diagnosable rather than merely counted.
What is Collected
Section titled “What is Collected”Usage analytics are powered by Aptabase, a privacy-first, open-source analytics platform designed for desktop apps.
| Event | When it fires | Properties |
|---|---|---|
app_launch | App starts | Platform, architecture |
app_heartbeat | Every 30 minutes while at least one agent session is active; skipped when idle. Also fires once right before system sleep | Active, suspended, queued, and total session counts |
app_close | Graceful quit, Ctrl+C, SIGTERM, or OS shutdown/reboot/log-off | Duration in seconds |
app_error | Uncaught exception, unhandled rejection, renderer crash, UI error boundary, updater failure, or a failed terminal spawn | Source, sanitized message, plus per-source extras (see below) |
project_create | New project created | (none) |
project_move | Project folder moved via Settings > General > Move | (none) |
project_relocate | Project re-pointed at a moved or renamed folder | (none) |
session_spawn | Agent session reaches running state (board or transient) | Agent, whether the session is transient, the resolved permission mode, whether it ran in a git worktree |
task_complete | Task moves to Done | Agent, model, permission mode, duration, cost (USD), input/output tokens, tool calls |
session_exit | Agent session finishes | Exit code, duration, agent, model, cost (USD), tool calls, and whether the exit was intentional (a deliberate kill or suspend) rather than the agent stopping on its own |
transient_session_spawn | Transient session launched from command terminal | Agent |
onboarding_milestone | Once per install, per funnel step | Which step: first project, first task, first spawn, first completed task |
feature_first_use | The first time you touch one of ten curated features, once per install | Which feature |
feature_used | Once per curated feature per UTC day | Which feature |
board_snapshot | Once per project per app run, when a project is opened cold | Column count, whether the columns differ from the seeded default, a bucketed task count, number of Board Profiles |
update_outcome | Next launch after the app version changed | Applied or rolled back, and the from/to versions |
spawn_failed | An agent session failed to start | Agent, and which path failed (create, auto-spawn, resume, unknown agent, CLI not found) |
The costUsd, token, and tool-call properties are cumulative session metrics, omitted when not yet available - they are counts and totals only, never any code or content.
The curated feature vocabulary is exactly ten entries: command terminal, worktree session, board profile, pop-out window, browser pane, MCP server, mobile bridge, usage dashboard, Quick Find, and settings. Each contributes at most one feature_used event per day, and the event says only that the feature was touched - never what you used it for. Renderer-reported features cross a single IPC channel and are re-validated against that list in the main process, so a drifted renderer cannot invent new event names.
board_snapshot sends counts only. Column names and task content never leave the machine, and the task count is bucketed (0, 1-9, 10-49, 50-199, 200+) rather than sent exactly.
An app_error’s source says which failure path it came from: uncaughtException, unhandledRejection, render-process-gone (adding the crash reason and exit code), error_boundary (adding which of the three reporters caught it, the failing panel’s label, and a trail of React component names), updater, and pty_spawn (adding the shell, whether the shell and working directory exist, the errno, and the platform).
A UI error’s component trail sent to Aptabase is names only. The raw React component stack does not go there, because a production stack frame embeds a file:// URL containing your home directory; Kangentic reduces it to component names in the main process, and a name cannot carry a path. Sentry receives the real stack instead, with paths normalized first - see Crash and Error Reports.
The Aptabase SDK also records: OS name, OS version, locale, app version, and country (derived from IP, then immediately discarded).
The app_launch event alone carries clientId, a non-reversible id Kangentic derives from your OS install (an HMAC-SHA256 digest of already-hashed machine and home-directory identifiers, keyed with a fixed salt). It is attached only to app_launch - the one authoritative per-launch install signal - so unique installs can be counted without inflating every other event with a high-cardinality string. It contains no raw machine identifiers, paths, or usernames.
It is, however, stable: the same value across app updates, and unchanged by an uninstall and reinstall. It is unique per OS user account. A stable identifier of that kind is ordinarily personal data under the GDPR, so it is described here rather than filed under “nothing personal”. It shares the same KANGENTIC_TELEMETRY control as every other event - there is no separate opt-out.
Crash and Error Reports
Section titled “Crash and Error Reports”Crash monitoring is separate from product analytics and goes to Sentry. Aptabase’s app_error stays a coarse error-rate pulse; Sentry is what turns a crash into something diagnosable, with grouping, deduplication, and a readable stack trace instead of a truncated string.
- Paths are normalized before anything leaves your machine. The Sentry SDK rewrites stack-frame paths and URLs relative to the app root, so your home directory does not travel with a stack trace. Personally identifiable data collection (
sendDefaultPii) stays off, and Sentry’s own server-side data scrubbing is on. - Errors only. Release-health session tracking is filtered out, and performance tracing and session replay are never enabled.
- The renderer has no network path of its own. Renderer errors travel to the main process over internal IPC, and the main process is the single point of egress - the same shape as the analytics funnel.
- Errors that used to be counted are now reported. Deliberate catch sites that previously emitted only a sanitized count (updater failures, terminal spawn failures, silent agent-spawn catches) send the real error, so a hidden problem is fixable rather than just visible as a number.
- Affected-install counts use the same
clientIddescribed above, attached as the Sentry user id so an issue’s user count means “installs affected”. It carries no personal data and shares the same opt-outs. - Source maps never ship. They are uploaded at release time and deleted from the build output, so stack traces resolve on Sentry’s side rather than from anything in the app you installed.
What is NOT Collected
Section titled “What is NOT Collected”- Task titles, descriptions, or any user-generated content
- Project names or code
- File paths: analytics events never carry one, and stack-frame paths in an error report are normalized to the app root before it is sent
- Usernames, emails, or personally identifiable information
- Mid-board task moves (only completion is tracked)
- What you did with a feature:
feature_usedrecords that a feature was touched on a given day, never the content of that use
How to Opt Out
Section titled “How to Opt Out”KANGENTIC_TELEMETRY is the superset switch - setting it to 0 disables usage analytics and error reporting. KANGENTIC_ERROR_REPORTING controls error reporting alone.
| Variable | Value | Behavior |
|---|---|---|
KANGENTIC_TELEMETRY | 0 or false | All telemetry disabled: analytics and error reporting |
KANGENTIC_TELEMETRY | 1 or true | Telemetry force-enabled, even in dev builds (useful for local testing) |
KANGENTIC_TELEMETRY | (unset) | Enabled in production builds, disabled in development |
KANGENTIC_ERROR_REPORTING | 0 or false | Error reporting disabled; usage analytics unaffected |
KANGENTIC_ERROR_REPORTING | 1 or true | Error reporting enabled even in dev builds, unless KANGENTIC_TELEMETRY=0 |
KANGENTIC_ERROR_REPORTING | (unset) | Follows whatever KANGENTIC_TELEMETRY resolves to |
Privacy Controls in Settings
Section titled “Privacy Controls in Settings”Open Settings > Privacy to see a summary of what is and isn’t collected. The Privacy tab is informational - opt-out is controlled exclusively by the environment variables above. It also points to the Memory tab for the (fully local) conversation-search controls described below.
Conversation Memory and On-Device AI
Section titled “Conversation Memory and On-Device AI”Two of Kangentic’s newest features process your data with local, on-device models and no API key:
- Conversation Memory indexes your agents’ past conversations so you can search and recall them. Indexing is fully local: only the structured conversation turns are indexed (never the raw terminal scrollback), the index lives inside each project, and searching never leaves your machine. Indexing is on by default and can be turned off; the optional semantic-search layer is opt-in. The only network access is a one-time download of a local embedding model (from Hugging Face) the first time you enable semantic search - after that it runs offline. Manage it under Settings > Memory, including a Rebuild index action.
- Dictation transcribes push-to-talk voice to text on-device by default (an optional cloud endpoint can be configured for the final refinement pass only; the live preview always stays local).
Neither feature sends your code, task content, or conversations to Kangentic or any analytics backend.
Technical Details
Section titled “Technical Details”- All telemetry leaves from the main process - the renderer never opens a network path of its own, for analytics or for error reports
- Error messages are sanitized to strip file paths before transmission, and error-report stack frames are rewritten relative to the app root
- Aptabase uses no cookies, and its own session IDs are random and rotate daily. Kangentic separately attaches a non-reversible
clientId(see above) so it can count unique installs; it is not an Aptabase feature - The analytics request reveals your IP address to Aptabase, which resolves it to a country and then discards the address
- All analytics calls are fire-and-forget and never block the app
- Analytics retention follows Aptabase’s privacy policy; error reports follow Sentry’s plan retention for the Kangentic organization
Privacy Questions and Data Requests
Section titled “Privacy Questions and Data Requests”Kangentic stores your task, session, and conversation data locally on your own machine, so most data requests are things you can act on yourself. Deleting a project’s .kangentic/ directory removes that project’s tasks, sessions, board history, and conversation index. Global settings and debug logs live outside it, in the platform config and log directories listed under Configuration and Troubleshooting. The only data that leaves your machine is the anonymous analytics and error reports described above, which carry no identifier that could be traced back to you.
If you have a question about this policy, or a request that needs a human, email hello@kangentic.com.
Next steps
Section titled “Next steps”See also:
- Privacy Policy - the formal policy covering desktop, mobile, and this website
- Settings - the in-app Privacy tab summarizing collection
- Auto-Updates - what’s checked when the app polls for new versions
Kangentic is free and open source. A star helps other people find it.
Star on GitHub