Step 6 of the on-by-default telemetry rollout — the server-side half. The API now actually emits the events documented at ferrlabs.com/telemetry instead of only persisting envelopes from external POSTs.
Wiring. api/src/telemetry_emit.rs owns the Emitter (BLAKE3-keyed Hasher loaded from FERRLABS_TELEMETRY_SALT at startup, ephemeral fallback in dev with a loud warn). Two helpers, spawn_emit_org and spawn_emit_user, fire-and-forget through tokio::spawn — they check the relevant opt-out (organizations.telemetry_opt_out for org events, user_prefs.telemetry_opt_out for user events) before writing into telemetry_events. Emit failures tracing::warn! and never propagate — telemetry is never the reason a request fails.
Wired call sites.
| Surface | Event | Properties |
|---|---|---|
POST /v1/orgs | org.created | (org_hash + actor user_hash) |
POST /v1/orgs/{slug}/members | org.member.added | role |
POST /v1/orgs/{slug}/subscriptions | product.activated | product, tier |
PATCH /v1/orgs/{slug}/subscriptions/{product} | product.tier_changed | product, tier_to (when tier changes) |
DELETE /v1/orgs/{slug}/subscriptions/{product} | product.canceled | product |
POST /auth/login | auth.login | method (currently always password) |
POST /auth/verify-email | auth.signup | method (account becomes active) |
OAuth login methods will populate method=oauth-google / oauth-github / sso once those flows ship.
Hash stability. FERRLABS_TELEMETRY_SALT is a 64-char hex (32 bytes) loaded from a sealed K8s secret in prod. Rotating it invalidates correlation across the rotation boundary by design — same intent as the 90-day raw retention on telemetry_events.
Wording flip is next. Step 7 (drop the draft banner from /telemetry, switch principle №04 + hero stat №03 to “Telemetry, but transparent” / “OPEN TELEMETRY”) will land as soon as the events are visibly arriving in DB on a preview cluster.