> ## Documentation Index
> Fetch the complete documentation index at: https://songbird.studiocollective.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# songbird-analytics

# 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

```rust theme={null}
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

```bash theme={null}
cargo test -p songbird-analytics
```
