Skip to content

Migrate website analytics to self-hosted Umami - #22

Merged
Acksell merged 2 commits into
mainfrom
codex/migrate-analytics-to-umami
Aug 31, 2026
Merged

Acksell merged 2 commits into
mainfrom
codex/migrate-analytics-to-umami

Conversation

@Acksell

@Acksell Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary

  • replace Plausible with the self-hosted Umami tracker and first-party relay configuration
  • enable sampled replay and heatmaps only on clean marketing/signup URLs
  • prevent SPA navigation from carrying the recorder into account, OAuth, repository, or queried URLs
  • sanitize analytics URLs and mask/block sensitive form and identity content
  • update the privacy disclosure and remove the Plausible dependency

Verification

  • bun test (37 passing)
  • production Vite build with CI environment values
  • deployed recorder configuration verified to block inputs, textareas, selects, contenteditable fields, and analytics-sensitive elements

Summary by cubic

Replaces Plausible with a self-hosted Umami tracker and first-party relay, and limits sampled replay and heatmaps to clean marketing and signup URLs. The old tracker captured every page view; the new setup never records on authenticated, OAuth, repository, or queried pages, and forces a reload when SPA navigation crosses that boundary.

  • Updates the privacy policy to disclose recordings and removes Plausible as a subprocessor.
  • Adds sanitizeAnalyticsPayload to strip credentials and keep only marketing attribution parameters.
  • Adds analytics-sensitive class to mask form controls, email fields, and identity content from recordings, including the signup email/password inputs.
  • Retries loading the tracker if the script fails instead of getting stuck.
  • Requires two new env vars PUBLIC_UMAMI_TRACKED_DOMAIN and PUBLIC_UMAMI_WEBSITE_ID; removes PUBLIC_PLAUSIBLE_DOMAIN and the @plausible-analytics/tracker dependency.

Written for commit 940734f. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Migrated website analytics from Plausible to self-hosted Umami.
    • Added optional interaction recordings and click/scroll heatmaps on eligible marketing and sign-up pages.
  • Privacy

    • Updated the privacy policy with analytics exclusions, sensitive-data masking, attribution-only URL details, and 30-day retention.
    • Confirmed that mobile app and sandbox activity is not tracked.
  • Bug Fixes

    • Improved analytics behavior during navigation and handled unavailable or invalid configurations gracefully.

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 8abefa3a-06c1-46d7-b905-cd7d27dea49c

📥 Commits

Reviewing files that changed from the base of the PR and between a1d341e and 940734f.

📒 Files selected for processing (2)
  • src/lib/analytics.js
  • src/routes/signup/+page.svelte

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The application replaces Plausible with configurable Umami analytics. It sanitizes analytics URLs, limits recordings to public routes, reloads SPA navigation across recording boundaries, marks sensitive fields, and updates the privacy policy and environment configuration.

Changes

Umami analytics migration

Layer / File(s) Summary
Umami configuration and dependency replacement
.env.example, src/lib/config.js, vite.config.js, .github/workflows/ci.yml, package.json
Umami tracked-domain and website-ID settings replace the Plausible setting. The Plausible tracker dependency is removed.
Payload sanitization and recording configuration
src/lib/analytics-payload.js, src/lib/analytics-script.js, src/lib/analytics-payload.test.js
Analytics URLs retain attribution parameters only. Recording routes, URL eligibility, script configuration, navigation behavior, and related tests are added.
Umami initialization and navigation control
src/lib/analytics.js, src/routes/+layout.svelte, src/lib/analytics-payload.test.js
Analytics initialization loads Umami and optional recording scripts. Event tracking uses Umami. Navigation reloads when recording eligibility changes.
Privacy disclosures and sensitive-field markings
src/routes/(marketing)/privacy/+page.svelte, src/routes/delete-account/+page.svelte, src/routes/oauth/consent/+page.svelte, src/routes/signup/+page.svelte
The privacy policy documents Umami recordings and heatmaps. Sensitive account, OAuth, and signup fields receive the analytics-sensitive class.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 94073

The analytics migration and related privacy/configuration updates have no actionable merge-blocking risk identified at the current head; the PR is merge-ready after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant Layout
  participant initAnalytics
  participant Umami
  participant Recorder
  Browser->>Layout: initialize application
  Layout->>initAnalytics: initialize analytics
  initAnalytics->>Umami: load tracker with sanitized payload handler
  initAnalytics->>Recorder: load recorder for eligible public route
  Umami-->>initAnalytics: expose tracker
  Layout->>Layout: evaluate beforeNavigate
  Layout->>Browser: reload navigation across recording boundary
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 6 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: migrating website analytics from Plausible to self-hosted Umami.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 6 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/migrate-analytics-to-umami

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

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

🤖 review-bot (run started · HEAD a1d341e) — ⏳ waiting on bot reviews (up to 25m)…


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

⚠️ can't post commit statuses — merge-box signal disabled this run


Generated by Claude Code

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/lib/analytics-script.js">

<violation number="1" location="src/lib/analytics-script.js:11">
P2: The recorder is loaded on '/signup', which renders password and email inputs and handles OAuth code exchange. The only thing this repo does for those routes is load the recorder; sanitizeAnalyticsPayload rewrites url/referrer and passes Umami 'recorded' captures (the blob) through unchanged, so the password/email/PII protection depends entirely on external recorder masking config that lives outside this PR and can't be audited here. Move '/signup' (and other routes containing identity forms) out of ANALYTICS_RECORDING_PATHS, or add an in-repo defense-in-depth that suppresses 'recorded' payloads for these routes.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/lib/analytics.js
'/demo',
'/pricing',
'/privacy',
'/signup',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The recorder is loaded on '/signup', which renders password and email inputs and handles OAuth code exchange. The only thing this repo does for those routes is load the recorder; sanitizeAnalyticsPayload rewrites url/referrer and passes Umami 'recorded' captures (the blob) through unchanged, so the password/email/PII protection depends entirely on external recorder masking config that lives outside this PR and can't be audited here. Move '/signup' (and other routes containing identity forms) out of ANALYTICS_RECORDING_PATHS, or add an in-repo defense-in-depth that suppresses 'recorded' payloads for these routes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/analytics-script.js, line 11:

<comment>The recorder is loaded on '/signup', which renders password and email inputs and handles OAuth code exchange. The only thing this repo does for those routes is load the recorder; sanitizeAnalyticsPayload rewrites url/referrer and passes Umami 'recorded' captures (the blob) through unchanged, so the password/email/PII protection depends entirely on external recorder masking config that lives outside this PR and can't be audited here. Move '/signup' (and other routes containing identity forms) out of ANALYTICS_RECORDING_PATHS, or add an in-repo defense-in-depth that suppresses 'recorded' payloads for these routes.</comment>

<file context>
@@ -0,0 +1,32 @@
+  '/demo',
+  '/pricing',
+  '/privacy',
+  '/signup',
+  '/terms'
+]);
</file context>

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

🤖 review-bot (run started · HEAD a1d341e) — coderabbit done: no actionable comments (1 docstring-coverage nit, noise). Still waiting on cubic · ~3m/25m.


Generated by Claude Code

- Reset the umami promise on load/network failure so a later
  initAnalytics() call can retry instead of getting stuck forever.
- Mark the signup form's email/password inputs analytics-sensitive,
  matching the pattern already used for other credential fields, since
  /signup is a recorded route.

Addresses cubic review findings on PR #22.

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

Automated review-bot triage

Rank Source ID File:line Issue LoC Real-world impact Verdict
1 cubic 3898543241 src/lib/analytics.js:32 umami promise stays truthy after a failed/rejected script load, blocking all retries for the rest of the page session 4 Real — one transient network blip permanently disables analytics client-side (best-effort, but silent) Do
2 cubic 3898543253 src/lib/analytics-script.js:11 /signup is a recorded route with live password/email <input>s; those inputs don't carry the analytics-sensitive class used elsewhere in this PR for credential fields 2 Real — credential-adjacent fields on an auth page, cheap to close the gap outright rather than lean solely on the recorder's default input handling Do
3 coderabbit (PR-level pre-merge check) Docstring coverage 12.5% < 80% threshold Noise — repo-wide coverage gate, not a line-level finding; this codebase doesn't use JSDoc as a convention Skip

Trusted review (current HEAD before this push): cubic ✓ (2 issues, both actioned below); CodeRabbit ✓ (no actionable comments); Greptile absent (no check on this PR); Gemini/Copilot absent (no review). Mergeability is the auto-merge skill's call, not review-bot's.

Plan: 1 commit, both cubic findings fixed — reset the tracker on load/error so it retries, and mask the signup email/password inputs the same way the rest of this PR masks credential fields. Pushed as 940734f.


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

✅ done — pushed 1 fix commit (2 findings addressed) as 940734f; bots re-reviewing, next run triages.


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

🤖 review-bot (run 00:00 UTC · HEAD 940734f) — ⏳ waiting on bot reviews (up to 25m)…


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

⚠️ can't post commit statuses — no GitHub MCP tool exposes the Statuses API in this environment (gh api/direct API access is unavailable here). Merge-box signal disabled for this run; the activity-stream comments below are the sole liveness signal.


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

🤖 review-bot (run 00:00 UTC · HEAD 940734f) — coderabbit done: no actionable comments on this head (docstring-coverage noise only). Still waiting on cubic · ~2m/25m.


Generated by Claude Code

Acksell commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

✅ Review-bot converged

run 21:37 UTC · HEAD 940734f · status → success (merge-box signal unavailable this run — see note above)

No bot comment postdates the prior triage (table, 21:32 UTC) — this HEAD is that triage's own fix-push. CodeRabbit's fresh pass on 940734f found no actionable comments; cubic's check run completed success with no new findings, and auto-marked the analytics.js:32 thread resolved against 940734f. The remaining open cubic thread (analytics-script.js:11, discussion) is the same one already triaged and fixed last round — cubic just hasn't auto-resolved it against the new file location. Nothing left to triage.

Trusted review (current HEAD): CodeRabbit ✓ (no actionable comments); cubic ✓ (check-run success, no new findings). Greptile absent (no check on this PR); Gemini/Copilot absent (no review). Mergeability is the auto-merge skill's call, not review-bot's.

Loop totals: 1 round, 3 findings (2 do, 0 defer, 0 won't-do, 1 skip).


Generated by Claude Code

@Acksell
Acksell merged commit 4215429 into main Aug 31, 2026
3 checks passed
@Acksell
Acksell deleted the codex/migrate-analytics-to-umami branch August 31, 2026 21:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants