3.5 KiB
Analytics in TidGi Desktop
This document explains how analytics works in TidGi Desktop, what is intentionally tracked, what is intentionally blocked, and how plugin authors should integrate with the analytics service.
Goals
TidGi uses analytics to understand coarse product usage without collecting user content.
The current design goals are:
- Keep all network delivery in the Electron main process
- Never expose the analytics API key to renderer or plugin code
- Track only coarse, product-level behavior
- Reject free-form content, note text, file paths, URLs, tokens, and other sensitive payloads
- Give plugin authors a stable way to emit custom product events without bypassing privacy guardrails
Architecture
- Renderer code and TiddlyWiki plugins call the TidGi analytics service through the existing IPC proxy layer
- The analytics service runs in the main process
- The main process sends events to Rybbit over HTTP
Delivery model
- Analytics is enabled only when all of the following are true:
analyticsEnabledpreference istrueanalyticsHostis configuredanalyticsSiteIdis configured- a main-process-only analytics API key is configured
- Unsent events may be queued temporarily in memory
- The queue is dropped immediately when the user disables analytics
- The first-run disclosure is tracked separately from normal product events
Privacy constraints
TidGi analytics must never contain:
- note titles or note bodies
- workspace names
- filesystem paths
- raw URLs
- access tokens, OAuth codes, cookies, or API keys
- free-form user text copied from the UI
If a proposed event depends on any of the above, do not add it to analytics.
Built-in events
The application currently emits built-in events such as:
app.launchedanalytics.disclosure_dismissedworkspace.createdworkspace.activatedpreferences.analytics_updatedsettings.openedtheme.changeddeep_link.openedsync.triggeredsync.completedsync.failedupdater.check_startedupdater.update_availableupdater.update_not_availableupdater.check_failederror.report_requested
Built-in events use an allowlist. For each built-in event, only explicitly approved property keys are retained.
That allowlist lives in src/services/analytics/index.ts.
Plugin-defined custom events
Plugin authors must not emit arbitrary event names through the low-level built-in event contract.
Instead, plugins should call:
await window.service.analytics.trackPluginEvent(pluginId, eventName, properties);
or inside a TiddlyWiki plugin:
await $tw.tidgi.service.analytics.trackPluginEvent(pluginId, eventName, properties);
Final event name format
The service converts plugin calls into this final event name:
plugin.<pluginId>.<eventName>
Example:
await window.service.analytics.trackPluginEvent('kanban-board', 'card_created', {
source: 'toolbar',
has_due_date: true,
});
Emits:
plugin.kanban-board.card_created
Validation rules
Plugin event names are intentionally restricted.
pluginIdmust match:^[a-z0-9]+(?:[-_][a-z0-9]+)*$eventNamemust match:^[a-z0-9]+(?:[-_][a-z0-9]+)*$- Property keys must match:
^[a-z][a-z0-9_]{0,39}$ - Property values must be
string | number | boolean - String values are truncated to 120 characters
If pluginId or eventName is invalid, the event is rejected.
If all properties are invalid, the event is still allowed to be sent without properties.