feat: pin the GA4 client_id to the device - #31
Merged
Conversation
GA4's own client_id lives in the _ga cookie and these players largely boot with fresh storage, so it churns and does not identify a screen. Where a Screenly player sends asset metadata there is now a stable device id, and signage-kit derives a client_id from the same hash that produces player_device, so the two always agree about which screen this is. The inline <head> tag becomes analyticsBootstrap() from the kit. It has to own the config call, because client_id is stamped onto each event as it is sent; it cannot move into main.js, because screens that never load the bundle would then report nothing at all; and it cannot be rendered into this HTML, because the page is edge-cached with no per-screen component and every screen would inherit whichever one warmed the cache. So it fetches the no-store /api/player route at runtime. Fails open: profile arrival, fetch rejection, a non-ok response and a timeout all end in exactly one config call, so an offline screen still reports with GA4's default behaviour. Also bumps signage-kit to 2026.8.8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pins the GA4
client_idto the Screenly device id, so one screen is one GA4 user.GA4's own
client_idlives in the_gacookie, and these players largely boot with fresh storage, so it churns and does not identify a screen. Where a player sends asset metadata there is now a stable device id, and the kit derives aclient_idfrom the same hash that producesplayer_device, so the two always agree about which screen this is.Why the tag moves into the kit bootstrap
The inline
<head>snippet is nowanalyticsBootstrap()from the kit, because three constraints rule out the easier options:confighas to wait for the device id.client_idis stamped onto each event as it is sent, so the bootstrap has to own theconfigcall rather than patch it afterwards.main.js. That is a separate request, and a share of screens fire apage_viewand never get further. Movingconfigthere would turn a counting fix into a data loss for exactly those screens.client_idwould be handed to every screen that hit the cache afterwards and the fleet would collapse onto one GA4 user. Hence fetching the no-store/api/playerroute at runtime.Fails open
The profile arriving, the fetch rejecting, a non-ok response, or a timeout all end in exactly one
configcall. An offline screen still reports, just with GA4's default behaviour. Telemetry is never the reason a screen goes quiet.Kit release: https://github.com/Screenly-Labs/signage-kit/releases/tag/2026.8.8
🤖 Generated with Claude Code