Skip to main content

songbird-analytics

Analytics sink abstraction. Wraps PostHog today; swappable to Amplitude / Mixpanel / a self-hosted endpoint by replacing one file.

What it owns

  • The AnalyticsSink trait — the boundary every analytics backend implements.
  • The global init() / track() / identify() / set_consent() functions used by songbird-sync’s dispatch middleware.
  • The default PostHog HTTP backend (posthog::PosthogSink) and a no-op fallback (noop::NoopSink).
  • The command-name blocklist (blocklist.rs) — which sync commands DON’T fire analytics events. Default is to track everything; only noisy channels (meters, visual) and gestural drags (mixer.volume, clip.move, automation drawing) are excluded.
  • The PII scrubber (scrub.rs) — denylist of param keys that never leave the device.

Public API

songbird_analytics::init(sink);             // call once at app startup
songbird_analytics::set_consent(true);      // gate everything on opt-in
songbird_analytics::on_command(name, params, ok);  // dispatch middleware
songbird_analytics::track(event, props);    // manual event from anywhere
songbird_analytics::identify(distinct_id, traits);
The single entry point used by the sync engine is on_command(name, params, ok). The dispatch middleware in songbird-sync calls it after every command; this crate decides whether to emit anything.

How it fits

  • songbird-sync depends on this crate and calls on_command(...) after every dispatch.
  • songbird-app constructs the sink at startup (real PostHog if POSTHOG_API_KEY is set, otherwise no-op) and calls init().
  • Consent lives on the existing settings channel as settings.set_analytics_consent { granted: bool } — it’s a setting like any other, not a separate analytics channel. The handler persists via save_preference and calls set_consent() here.
  • Frontend doesn’t fire analytics events directly in v1. Every event either flows from a real sync command (observed by on_command) or is added later as a new sync command — keeping a single taxonomy.

Swap procedure

Replace PostHog with another vendor in three steps:
  1. Add a new file src/<vendor>.rs that implements AnalyticsSink.
  2. Change the Box::new(posthog::PosthogSink::new(...)) line in songbird-app’s startup to construct your new sink.
  3. Delete src/posthog.rs.
No call sites change. The dispatch middleware, the sync channel, and the allowlist all stay put.

Tests

cargo test -p songbird-analytics