Skip to content

Conversation

@willyogo
Copy link
Member

@willyogo willyogo commented Jan 1, 2026

Summary

  • replace the Segment analytics client with a direct Mixpanel loader plus queued identify, page, and track calls
  • update security policies, dependencies, and privacy copy to align with Mixpanel-only analytics routing
  • drop the Segment package since event routing no longer relies on the Segment SDK

Testing

  • npm run lint (passes with existing unused variable warnings)

Codex Task

Summary by CodeRabbit

  • Documentation

    • Updated privacy notice to detail current analytics practices, data usage, and user privacy controls.
  • Chores

    • Streamlined analytics infrastructure by consolidating to Mixpanel.
    • Updated security policies for analytics services.
    • Removed unused analytics dependencies.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
space-system Ready Ready Preview, Comment Jan 6, 2026 5:30pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 1, 2026

📝 Walkthrough

Walkthrough

The pull request migrates analytics from Segment and FullStory to Mixpanel. Changes include removing the Segment dependency, updating Content-Security-Policy directives to allow Mixpanel domains, refactoring the analytics provider to integrate Mixpanel instead of Segment, and updating the privacy policy to reference Mixpanel exclusively.

Changes

Cohort / File(s) Summary
Analytics Provider Refactor
src/common/providers/AnalyticsProvider.tsx
Replaced Segment Analytics integration with Mixpanel. Introduces MIXPANEL_TOKEN, injects Mixpanel snippet via next/script, adds analyticsReady() guard, and updates track(), identify(), and page() methods to use window.mixpanel with ready-state checks. Removes Segment write-key initialization.
Security Configuration
next.config.mjs
Updated Content-Security-Policy script-src and connect-src directives. Removed cdn.segment.com, edge.fullstory.com, and rs.fullstory.com; added cdn.mxpnl.com, api-js.mixpanel.com, api.mixpanel.com, decide.mixpanel.com, and replay.mixpanel.com.
Dependency Removal
package.json
Removed @segment/analytics-next (^1.81.0) from dependencies section.
Privacy Documentation
src/app/privacy/page.tsx
Updated privacy notice to reference Mixpanel as the sole analytics provider. Removed mentions of Segment and FullStory across multiple sections (Usage analytics, Device & browser info, data sharing, user choices, and cookies). Simplified Do-Not-Track behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 From Segment's farewell to FullStory's goodbye,
Mixpanel takes flight through the analytics sky,
Guards keep it steady, the script does inject,
Tracking runs smooth with a readiness check!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Replace Segment with direct Mixpanel integration' directly and accurately describes the main change: removing Segment analytics and replacing it with Mixpanel throughout the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@willyogo willyogo added the LGFTP Looks Good From Testing Perspective label Jan 6, 2026
@willyogo willyogo marked this pull request as ready for review January 6, 2026 17:25
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/privacy/page.tsx (1)

8-85: Duplicate privacy notice content.

The PRIVACY_MD string contains the entire privacy notice twice. Lines 8-85 contain the full notice, and lines 87-208 repeat nearly identical content. This will render the privacy page with duplicate sections.

Remove one of the duplicated blocks to display the notice only once.

Also applies to: 87-208

🤖 Fix all issues with AI Agents
In @next.config.mjs:
- Line 16: The Content Security Policy in next.config.mjs still includes the
FullStory domain in the img-src directive; remove "https://rs.fullstory.com"
from the img-src entry in the contentSecurityPolicy (the img-src directive
string) so the CSP no longer references FullStory when that integration is
removed.

In @src/common/providers/AnalyticsProvider.tsx:
- Around line 89-91: The initial analytics.page() call can be dropped or guarded
because the Mixpanel script loads afterInteractive and analyticsReady() may
return false; either remove the useEffect (if you keep automatic page view
tracking via track_pageview:true) or replace the immediate call with a readiness
check/poll/callback that waits for analyticsReady() (or the mixpanel client to
be ready) before invoking analytics.page(); locate the useEffect with
analytics.page() in AnalyticsProvider and implement the chosen fix so the
initial page view is never silently dropped.
- Around line 24-26: The Mixpanel snippet constant MIXPANEL_SNIPPET currently
sets track_pageview:true which duplicates manual analytics.page() calls (see
analytics.page() usages). Fix by either changing track_pageview to false inside
MIXPANEL_SNIPPET so only manual analytics.page() calls fire, or remove/disable
the manual analytics.page() calls (the analytics.page() invocations) and keep
track_pageview:true; update the MIXPANEL_SNIPPET string accordingly if you
choose the first option.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b96e66a and 9a8ea80.

📒 Files selected for processing (4)
  • next.config.mjs
  • package.json
  • src/app/privacy/page.tsx
  • src/common/providers/AnalyticsProvider.tsx
💤 Files with no reviewable changes (1)
  • package.json
🔇 Additional comments (7)
next.config.mjs (2)

13-13: LGTM on Mixpanel script-src addition.

The CSP script-src directive correctly allows the Mixpanel CDN domain.


42-46: LGTM on Mixpanel connect-src additions.

The required Mixpanel endpoints are correctly added for analytics, session replay, and decision APIs.

src/common/providers/AnalyticsProvider.tsx (4)

9-20: LGTM on the Mixpanel type declaration.

The global type augmentation correctly defines the Mixpanel interface methods used in this file.


28-29: LGTM on the analytics ready guard.

The guard correctly checks for both window existence and that the Mixpanel track method is a function.


31-63: LGTM on the analytics methods.

The track, identify, and page methods are properly guarded against Mixpanel not being ready and include try-catch error handling.


99-101: Navigation page views depend on Mixpanel being loaded.

After the initial load, subsequent navigation page views should work correctly since Mixpanel will likely be loaded by then. This is fine if the duplicate tracking issue is resolved.

src/app/privacy/page.tsx (1)

26-27: LGTM on Mixpanel analytics references.

The usage analytics and device/browser info sections correctly reference Mixpanel as the sole analytics provider.

Also applies to: 106-107

script-src 'self' 'unsafe-eval' 'unsafe-inline' https://challenges.cloudflare.com https://www.youtube.com https://www.youtube.com/iframe_api https://auth.privy.nounspace.com https://cdn.mxpnl.com;
style-src 'self' 'unsafe-inline' https://i.ytimg.com https://mint.highlight.xyz;
media-src 'self' blob: data: https://stream.warpcast.com https://stream.farcaster.xyz https://res.cloudinary.com/ https://*.cloudflarestream.com https://*.b-cdn.net;
img-src 'self' blob: data: https: https://ipfs.io https://rs.fullstory.com;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Leftover FullStory domain in img-src.

https://rs.fullstory.com remains in the img-src directive but FullStory is being removed in this PR. Consider removing it for consistency.

Proposed fix
-    img-src 'self' blob: data: https: https://ipfs.io https://rs.fullstory.com;
+    img-src 'self' blob: data: https: https://ipfs.io;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
img-src 'self' blob: data: https: https://ipfs.io https://rs.fullstory.com;
img-src 'self' blob: data: https: https://ipfs.io;
🤖 Prompt for AI Agents
In @next.config.mjs at line 16, The Content Security Policy in next.config.mjs
still includes the FullStory domain in the img-src directive; remove
"https://rs.fullstory.com" from the img-src entry in the contentSecurityPolicy
(the img-src directive string) so the CSP no longer references FullStory when
that integration is removed.

* **Delete dashboard or Space:** Use in-app controls; dashboard data is wiped server-side, Space config removed from our DB (posts on Farcaster remain public).
* **California & GDPR rights:** You can request deletion, correction, or a copy of your personal data. We honour valid requests within 30 days.
* **Do-Not-Track signals:** We respect DNT headers by disabling Segment, Mixpanel, and Fullstory when DNT = 1.
* **Do-Not-Track signals:** We respect DNT headers by disabling Mixpanel when DNT = 1.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for DNT (Do Not Track) handling in the codebase
rg -n -i "do.?not.?track|navigator\.doNotTrack|DNT" --type=ts --type=tsx

Repository: blankdotspace/space-system

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Find and examine AnalyticsProvider.tsx
fd -n "AnalyticsProvider.tsx" -x cat {}

Repository: blankdotspace/space-system

Length of output: 300


🏁 Script executed:

#!/bin/bash
# Check the privacy page to see the exact statements at lines 61 and 165
fd -n "page.tsx" src/app/privacy -x cat -n {}

Repository: blankdotspace/space-system

Length of output: 300


🏁 Script executed:

#!/bin/bash
# Search for DNT handling - use broader approach without specific type filters
rg -i "do.?not.?track|navigator\.doNotTrack|DNT" -A 3 -B 1

Repository: blankdotspace/space-system

Length of output: 2108


🏁 Script executed:

#!/bin/bash
# Find AnalyticsProvider.tsx file
fd "AnalyticsProvider"

Repository: blankdotspace/space-system

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Find and read the privacy page
fd "page.tsx" src/app/privacy

Repository: blankdotspace/space-system

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Read the AnalyticsProvider.tsx file
cat -n src/common/providers/AnalyticsProvider.tsx

Repository: blankdotspace/space-system

Length of output: 5956


🏁 Script executed:

#!/bin/bash
# Read the privacy page to confirm exact lines
cat -n src/app/privacy/page.tsx | head -100

Repository: blankdotspace/space-system

Length of output: 6842


🏁 Script executed:

#!/bin/bash
# Get more lines from privacy page to find line 165
cat -n src/app/privacy/page.tsx | sed -n '150,180p'

Repository: blankdotspace/space-system

Length of output: 1514


🏁 Script executed:

#!/bin/bash
# Search the entire codebase for navigator.doNotTrack or similar DNT checks
rg "navigator\.doNotTrack|doNotTrack" -i

Repository: blankdotspace/space-system

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Search for any conditional Mixpanel initialization based on user preference
rg "mixpanel" -i -B 2 -A 2 | head -60

Repository: blankdotspace/space-system

Length of output: 6574


Remove false DNT claim from privacy notice or implement DNT handling.

The privacy notice (lines 61 and 165) claims "We respect DNT headers by disabling Mixpanel when DNT = 1", but AnalyticsProvider.tsx contains no DNT handling whatsoever. Mixpanel is initialized unconditionally without checking navigator.doNotTrack. Either implement DNT support by detecting the Do-Not-Track signal and conditionally disabling Mixpanel initialization, or remove this claim from the privacy notice.

Comment on lines +24 to +26
const MIXPANEL_SNIPPET = MIXPANEL_TOKEN
? `(function (f, b) { if (!b.__SV) { var e, g, i, h; window.mixpanel = b; b._i = []; b.init = function (e, f, c) { function g(a, d) { var b = d.split("."); 2 == b.length && ((a = a[b[0]]), (d = b[1])); a[d] = function () { a.push([d].concat(Array.prototype.slice.call(arguments, 0))); }; } var a = b; "undefined" !== typeof c ? (a = b[c] = []) : (c = "mixpanel"); a.people = a.people || []; a.toString = function (a) { var d = "mixpanel"; "mixpanel" !== c && (d += "." + c); a || (d += " (stub)"); return d; }; a.people.toString = function () { return a.toString(1) + ".people (stub)"; }; i = "disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove start_session_recording stop_session_recording".split(" "); for (h = 0; h < i.length; h++) g(a, i[h]); var j = "set set_once union unset remove delete".split(" "); a.get_group = function () { function b(c) { d[c] = function () { call2_args = arguments; call2 = [c].concat(Array.prototype.slice.call(call2_args, 0)); a.push([e, call2]); }; } for ( var d = {}, e = ["get_group"].concat( Array.prototype.slice.call(arguments, 0)), c = 0; c < j.length; c++) b(j[c]); return d; }; b._i.push([e, f, c]); }; b.__SV = 1.2; e = f.createElement("script"); e.type = "text/javascript"; e.async = !0; e.src = "undefined" !== typeof MIXPANEL_CUSTOM_LIB_URL ? MIXPANEL_CUSTOM_LIB_URL : "file:" === f.location.protocol && "//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\\/\\//) ? "https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js" : "//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js"; g = f.getElementsByTagName("script")[0]; g.parentNode.insertBefore(e, g); } })(document, window.mixpanel || []); mixpanel.init("${MIXPANEL_TOKEN}", {debug:${process.env.NODE_ENV !== "production"} ,track_pageview:true,persistence:"localStorage",autocapture:true,record_replay:true,record_replay_sample_rate:1,api_host:"https://api-js.mixpanel.com"}); if(mixpanel.start_session_recording){mixpanel.start_session_recording();}`
: "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Duplicate page view tracking.

The Mixpanel config sets track_pageview:true, which enables automatic page view tracking. However, manual analytics.page() calls on lines 90 and 100 will result in duplicate "Page View" events.

Consider either:

  1. Setting track_pageview:false and relying on manual tracking, or
  2. Removing the manual analytics.page() calls and relying on Mixpanel's automatic tracking.
🤖 Prompt for AI Agents
In @src/common/providers/AnalyticsProvider.tsx around lines 24 - 26, The
Mixpanel snippet constant MIXPANEL_SNIPPET currently sets track_pageview:true
which duplicates manual analytics.page() calls (see analytics.page() usages).
Fix by either changing track_pageview to false inside MIXPANEL_SNIPPET so only
manual analytics.page() calls fire, or remove/disable the manual
analytics.page() calls (the analytics.page() invocations) and keep
track_pageview:true; update the MIXPANEL_SNIPPET string accordingly if you
choose the first option.

Comment on lines 89 to +91
useEffect(() => {
if (writeKey) {
segment.load({ writeKey }).catch((e) => {
console.error(e);
});
}
}, [writeKey]);
analytics.page();
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Initial page view may be silently dropped.

This effect runs immediately on mount, but the Mixpanel script uses strategy="afterInteractive" and may not be loaded yet. The analyticsReady() guard will silently return, dropping the initial page view.

If automatic page view tracking via track_pageview:true is kept, this can be removed entirely. Otherwise, consider polling or using a callback to ensure Mixpanel is ready before tracking.

🤖 Prompt for AI Agents
In @src/common/providers/AnalyticsProvider.tsx around lines 89 - 91, The initial
analytics.page() call can be dropped or guarded because the Mixpanel script
loads afterInteractive and analyticsReady() may return false; either remove the
useEffect (if you keep automatic page view tracking via track_pageview:true) or
replace the immediate call with a readiness check/poll/callback that waits for
analyticsReady() (or the mixpanel client to be ready) before invoking
analytics.page(); locate the useEffect with analytics.page() in
AnalyticsProvider and implement the chosen fix so the initial page view is never
silently dropped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex LGFTP Looks Good From Testing Perspective

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants