Route browser analytics through first-party API

Closed
LA
2mo ago
+766-60

Summary

  • Move browser analytics capture to /api/analytics/browser-events, an allowlisted first-party route that forwards to PostHog server-side.
  • Disable PostHog browser SDK network capture surfaces while keeping local distinct-id persistence for attribution.
  • Send pageviews, clicks, and waitlist signups through the first-party helper instead of direct r.cmux.com or /e/ browser requests.

Testing

  • bun test tests/browser-analytics-route.test.ts
  • bun run typecheck
  • bunx eslint app/[locale]/posthog.tsx app/[locale]/(landing)/tracked-link.tsx app/[locale]/components/download-button.tsx app/[locale]/components/faq-platform-answer.tsx app/[locale]/components/github-button.tsx app/[locale]/components/github-stars.tsx app/[locale]/components/nav-links.tsx app/[locale]/components/pro-cta-link.tsx app/[locale]/components/waitlist-callout.tsx app/[locale]/components/waitlist-dialog.tsx app/lib/analytics.ts app/api/analytics/browser-events/route.ts services/analytics/browserEventPolicy.ts tests/browser-analytics-route.test.ts
  • bun run lint currently fails on unrelated pre-existing lint errors in billing/vault/test files.

Issues

  • Related: user-reported uBlock Origin block of https://r.cmux.com/i/v0/e/?...

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


[!NOTE]
Medium Risk
Touches all marketing analytics and waitlist person properties in PostHog; misconfiguration of the new rate limiter or forwarding could drop events or block signups in production until env is set.

Overview
Browser analytics no longer posts directly to r.cmux.com from the client. Clicks, pageviews, and waitlist signups go through new helpers in app/lib/analytics.ts that POST to /api/analytics/browser-events, which allowlists events, sanitizes payloads, rate-limits on Vercel via CMUX_BROWSER_ANALYTICS_RATE_LIMIT_ID, and forwards to PostHog /batch/ server-side.

posthog-js is reconfigured so the SDK does not drive ingest: api_host points at a disabled path, autocapture/pageleave/session recording/surveys/flags/decide are off, and $pageview is sent via the first-party helper. The SDK remains mainly for get_distinct_id, with a localStorage/in-memory fallback when that is unavailable.

Waitlist signup stops using a direct browser POST to PostHog’s /e/ endpoint; it uses captureAnalyticsEvent and the route re-validates email, platforms, and distinctId before applying server-controlled $set / Early Access enrollment.

Landing and marketing UI components swap posthog.capture for captureAnalyticsClick (download, GitHub, pricing, pro CTA, tracked links, waitlist opens).

Reviewed by Cursor Bugbot for commit be9c2bc786ce36fdde523c4ffc9e4c41e1091a6b. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Route all browser analytics through a first‑party API that forwards events to PostHog server‑side, with a dedicated Vercel rate limiter, a persistent fallback distinct id, anonymous‑profile behavior for non‑signup events, and strict server validation for waitlist signups. This avoids uBlock/extension blocks on r.cmux.com while keeping attribution via the SDK or a local fallback.

  • New Features

    • Added /api/analytics/browser-events to allowlist events, sanitize payloads, rate‑limit via Vercel Firewall using CMUX_BROWSER_ANALYTICS_RATE_LIMIT_ID, and forward to PostHog /batch/ (returns 429 when limited, 503 if misconfigured). Sets $process_person_profile: false for forwarded events.
    • Validates waitlist signups: requires distinctId === email, allowlists platforms, and normalizes $set/$set_once (adds $feature_enrollment/*); rejects invalid payloads before forwarding while preserving enrollment for valid signups.
    • Added app/lib/analytics.ts with captureAnalyticsEvent and captureAnalyticsClick, plus a stored fallback distinct id (localStorage with in‑memory backup). $pageview and clicks use this helper. Tests cover forwarding, event validation (incl. waitlist), rate limiting, distinct‑id fallback, and profile‑processing behavior.
  • Refactors

    • Replaced direct posthog-js captures with captureAnalyticsClick across links/buttons and routed waitlist signup through the first‑party API; UI waits for server acceptance.
    • Reconfigured posthog-js to not send network analytics (disabled autocapture, pageleave, session recording, surveys, flags/decide, and external dependency loading) and set api_host to a disabled path; SDK remains for get_distinct_id only.

Summary by CodeRabbit

  • New Features
    • Added a browser analytics API endpoint that validates allowlisted events, enforces rate limiting, and forwards approved events for tracking.
  • Bug Fixes / Improvements
    • Centralized click and pageview tracking through a shared client analytics helper, removing direct third-party tracking calls across key UI flows.
    • Improved waitlist tracking to use the shared helper, with safer delivery/acceptance handling.
    • Added stricter event allowlisting, payload sanitization, and clearer handling for unknown and rate-limited requests.
    • Added distinct-id fallback logic with local persistence when needed.
  • Tests
    • Added automated coverage for the analytics route and distinct-id behavior.