Skip to content

Add privacy-first GA4 to badseo.dev#79

Closed
bensenescu wants to merge 1 commit into
mainfrom
codex/add-badseo-ga4-consent
Closed

Add privacy-first GA4 to badseo.dev#79
bensenescu wants to merge 1 commit into
mainfrom
codex/add-badseo-ga4-consent

Conversation

@bensenescu

@bensenescu bensenescu commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add GA4 measurement ID G-7MXV9FH7SS behind a fail-closed admin-verification gate
  • add opt-in Basic Consent Mode with no Google tag or ping before consent, accessible accept/reject/settings controls, cross-tab synchronization, cookie cleanup, and retryable withdrawals
  • add a same-origin, rate-limited append-only consent ledger in Workers KV
  • add and sitemap badseo.dev/privacy; the existing web privacy policy is unchanged
  • canonicalize production traffic to the HTTPS apex, remove remote Google Fonts, and preserve all crawler fixtures

Rollout safety

GA4_ADMIN_VERIFIED intentionally remains "false", so merging or deploying cannot load GA4 or show unnecessary controls. Before flipping it to "true", complete the checklist in badseo/README.md: verify Account Data Sharing is off, 14-month retention, no Ads/User-ID/Signals or ad personalization, intended Enhanced Measurement only, and validate with Tag Assistant plus GA Realtime.

Three independent final reviews—adversarial consent/security, CMP/privacy, and route/UI—found no high-confidence merge blockers.

Verification

  • full repository CI command passes locally: Prettier, Knip, TypeScript, and type-aware Oxlint
  • production Wrangler dry-run passed with KV, 5/60 limiter, real ID, and gate false
  • Playwright consent suite: 7/7 passed
  • real OpenSEO crawl audit: 42/42 checks and 27/27 issue types
  • generated enabled/disabled analytics scripts parse

Greptile Summary

This PR adds privacy-first GA4 support for badseo.dev. The main changes are:

  • Adds a fail-closed analytics script gated by GA4_ADMIN_VERIFIED, the GA4 measurement ID, KV, and rate-limit bindings.
  • Adds an opt-in consent UI with local receipt storage, cross-tab synchronization, withdrawal retry markers, and GA cookie cleanup.
  • Adds a same-origin /analytics-consent endpoint that rate-limits requests and appends consent events to Workers KV.
  • Adds a local /privacy page and includes it in the sitemap and audit clean-page checks.
  • Adds Playwright consent lifecycle coverage and Wrangler production safety configuration.

Confidence Score: 5/5

Safe to merge with low risk.

The analytics path is gated and fails closed when verification or required bindings are absent. Consent grants require a successful same-origin KV ledger write before GA loads. Focused Playwright coverage exercises consent grants, withdrawals, retries, cross-tab state, and privacy routing.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The Playwright consent suite was run against Wrangler and all 7 tests passed at the redacted URL with GA4 test env vars.
  • The UI capture validated the analytics consent flow by observing a POST to /analytics-consent returning 201 and the Google request count rising from 0 to 2 after acceptance.
  • The real-browser consent flow was inspected via a video recording and screenshots to confirm the consent banner, Accept/Reject controls, and the sequence of user interactions.
  • Post-acceptance UI state was verified to show the banner dismissed and the cookie settings panel available from the page.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
badseo/src/analytics.ts Adds fail-closed browser analytics bootstrap, consent UI, receipt persistence, cross-tab handling, cookie cleanup, and disabled cleanup script.
badseo/src/consent-log.ts Adds same-origin, rate-limited consent ledger endpoint with bounded JSON parsing and append-only KV writes.
badseo/src/index.ts Wires privacy, sitemap, analytics script, consent endpoint, and production canonical redirects into the Worker.
badseo/wrangler.jsonc Adds preview and production KV namespaces, rate limits, GA4 variables, and production domain safety settings.
badseo/e2e/consent.spec.ts Adds Playwright coverage for privacy routing, consent lifecycle, cross-tab synchronization, failed withdrawals, and stale grants.
badseo/src/privacy.ts Adds the badseo.dev privacy policy page describing optional GA4, consent storage, ledger retention, and withdrawal.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Browser
participant Worker
participant RateLimiter
participant KV
participant Google

Browser->>Worker: Request analytics script
alt Disabled gate or missing bindings
    Worker-->>Browser: Return cleanup script
    Browser->>Browser: Remove consent state and GA cookies
else Enabled gate and bindings
    Worker-->>Browser: Return consent bootstrap script
    Browser->>Browser: Queue denied defaults and show UI
    Browser->>Worker: Post grant consent event
    Worker->>RateLimiter: Check rate limit
    RateLimiter-->>Worker: Allowed
    Worker->>KV: Append grant event with TTL
    KV-->>Worker: Saved
    Worker-->>Browser: Return recorded receipt
    Browser->>Browser: Persist receipt and grant analytics storage
    Browser->>Google: Load gtag script after consent
    Browser->>Worker: Post withdrawal event
    Worker->>KV: Append withdrawal event with TTL
    Worker-->>Browser: Return withdrawal receipt
    Browser->>Browser: Deny consent, clear cookies, reload if needed
end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Browser
participant Worker
participant RateLimiter
participant KV
participant Google

Browser->>Worker: Request analytics script
alt Disabled gate or missing bindings
    Worker-->>Browser: Return cleanup script
    Browser->>Browser: Remove consent state and GA cookies
else Enabled gate and bindings
    Worker-->>Browser: Return consent bootstrap script
    Browser->>Browser: Queue denied defaults and show UI
    Browser->>Worker: Post grant consent event
    Worker->>RateLimiter: Check rate limit
    RateLimiter-->>Worker: Allowed
    Worker->>KV: Append grant event with TTL
    KV-->>Worker: Saved
    Worker-->>Browser: Return recorded receipt
    Browser->>Browser: Persist receipt and grant analytics storage
    Browser->>Google: Load gtag script after consent
    Browser->>Worker: Post withdrawal event
    Worker->>KV: Append withdrawal event with TTL
    Worker-->>Browser: Return withdrawal receipt
    Browser->>Browser: Deny consent, clear cookies, reload if needed
end
Loading

Reviews (1): Last reviewed commit: "Add privacy-first GA4 to badseo.dev" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor Author

Closing this copy because the requested review target is the origin repository. The replacement is https://github.com/bensenescu/open-seo/pull/380.

@bensenescu bensenescu closed this Jul 10, 2026
@bensenescu bensenescu deleted the codex/add-badseo-ga4-consent branch July 10, 2026 20:51
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.

1 participant