Problem
Gameplay analytics shipped through consent-denied GA4 in #70, but the property has never surfaced those events. The intended product boundary is now explicit: stonesiegegame.com may use consented GA4 for website traffic, while StoneSiege gameplay statistics must be sent only to a project-operated first-party service.
Today the live game still loads Google Tag Manager and sends app_open, menu_screen, match_start, match_resume, match_end, and campaign completion events to GA4. The private Knorcedger/stonesiege-be repository contains an ingest service, but it is not deployed or connected. The public privacy policy consequently also names the wrong processor for gameplay data.
Scope
- Productionise and deploy the existing first-party ingest service before changing the client.
- Replace the web/mobile gameplay GA4 transport with a resilient first-party HTTP sink while preserving the platform-neutral
AnalyticsSink boundary and keeping packages/sim untouched.
- Keep the existing Settings → Share anonymous gameplay stats opt-out; when disabled, send no analytics requests.
- Retain session-scoped identifiers only, add deduplicating event IDs and match IDs, and send no account, advertising, or persistent device identifier.
- Add protected aggregate reporting sufficient to answer launches, menu funnel, matches started/finished, duration, outcome, civilisation, difficulty, map size, campaign/chapter, platform, and app version.
- Remove gameplay-specific GA4 configuration and disclosures while keeping website analytics a separate website concern.
- Update the website privacy policy, mobile/store documentation, and declarations in the coordinated release.
Acceptance criteria
- The first-party service is live with health monitoring, validated/bounded ingestion, retention expiry, production CORS, rate limiting, and no stored client IP.
app_open, menu_screen, match_start, match_resume, match_end, and campaign_chapter_complete arrive with populated parameters and can be read through protected aggregate reporting.
- The game makes no request to Google Analytics or Google Tag Manager and contains no gameplay GA measurement ID.
- Turning off Share anonymous gameplay stats prevents the first-party client from loading/sending; offline or failed analytics never blocks or errors gameplay.
- Website traffic remains separate and uses GA4 only after website consent.
- Public privacy/store documentation accurately identifies first-party gameplay collection, infrastructure processors, retention, opt-out, and the absence of advertising/cross-site tracking.
npm run check passes and live browser verification covers a practice-match start and resignation.
Delivery order
- Backend PR and deployment.
- Website policy/consent PR and deployment.
- Game client PR and deployment.
- Live ingestion, reporting, GA4, and cookie verification.
Tradeoffs
Operating the service adds hosting, database, abuse-prevention, retention, monitoring, and reporting responsibilities. A public open-source client cannot safely hold an ingest secret, so the endpoint must rely on strict schemas, small request limits, CORS, rate limits, idempotency, and monitoring rather than a bundled token. Session-scoped identifiers deliberately cannot provide cross-session retention.
Supersedes the GA4 transport decision in #62 while retaining its event design, opt-out, offline-safety, and disclosure principles.
Problem
Gameplay analytics shipped through consent-denied GA4 in #70, but the property has never surfaced those events. The intended product boundary is now explicit: stonesiegegame.com may use consented GA4 for website traffic, while StoneSiege gameplay statistics must be sent only to a project-operated first-party service.
Today the live game still loads Google Tag Manager and sends
app_open,menu_screen,match_start,match_resume,match_end, and campaign completion events to GA4. The privateKnorcedger/stonesiege-berepository contains an ingest service, but it is not deployed or connected. The public privacy policy consequently also names the wrong processor for gameplay data.Scope
AnalyticsSinkboundary and keepingpackages/simuntouched.Acceptance criteria
app_open,menu_screen,match_start,match_resume,match_end, andcampaign_chapter_completearrive with populated parameters and can be read through protected aggregate reporting.npm run checkpasses and live browser verification covers a practice-match start and resignation.Delivery order
Tradeoffs
Operating the service adds hosting, database, abuse-prevention, retention, monitoring, and reporting responsibilities. A public open-source client cannot safely hold an ingest secret, so the endpoint must rely on strict schemas, small request limits, CORS, rate limits, idempotency, and monitoring rather than a bundled token. Session-scoped identifiers deliberately cannot provide cross-session retention.
Supersedes the GA4 transport decision in #62 while retaining its event design, opt-out, offline-safety, and disclosure principles.