songbird-analytics
Analytics sink abstraction. Wraps PostHog today; swappable to Amplitude / Mixpanel / a self-hosted endpoint by replacing one file.What it owns
- The
AnalyticsSinktrait — the boundary every analytics backend implements. - The global
init()/track()/identify()/set_consent()functions used bysongbird-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
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-syncdepends on this crate and callson_command(...)after every dispatch.songbird-appconstructs the sink at startup (real PostHog ifPOSTHOG_API_KEYis set, otherwise no-op) and callsinit().- Consent lives on the existing
settingschannel assettings.set_analytics_consent { granted: bool }— it’s a setting like any other, not a separate analytics channel. The handler persists viasave_preferenceand callsset_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:- Add a new file
src/<vendor>.rsthat implementsAnalyticsSink. - Change the
Box::new(posthog::PosthogSink::new(...))line insongbird-app’s startup to construct your new sink. - Delete
src/posthog.rs.