Skip to content

Support Sourcepoint GPP consent for EC generation#642

Open
ChristianPavilonis wants to merge 27 commits intofeature/edge-cookies-finalfrom
edge-cookie-sourcepoint-consent
Open

Support Sourcepoint GPP consent for EC generation#642
ChristianPavilonis wants to merge 27 commits intofeature/edge-cookies-finalfrom
edge-cookie-sourcepoint-consent

Conversation

@ChristianPavilonis
Copy link
Copy Markdown
Collaborator

Summary

  • Add client-side Sourcepoint JS integration that auto-discovers _sp_user_consent_* from localStorage and mirrors GPP consent into __gpp / __gpp_sid cookies
  • Extend server-side GPP decoding to extract sale_opt_out from US GPP sections (IDs 7–23)
  • Add GPP US consent branch to allows_ec_creation() between existing TCF and us_privacy checks

Closes #640

Test plan

  • Rust tests pass (cargo test --workspace — 992 tests including 8 new)
  • JS tests pass (npx vitest run — 288 tests including 6 new)
  • Clippy clean
  • Verify EC generation succeeds for a Sourcepoint-only site in a regulated US state with GPP consent present
  • Verify GPC still blocks EC when GPP US section is permissive
  • Verify existing TCF and us_privacy EC gating behavior unchanged

🤖 Generated with Claude Code

@ChristianPavilonis ChristianPavilonis marked this pull request as draft April 16, 2026 16:42
@ChristianPavilonis ChristianPavilonis marked this pull request as ready for review April 16, 2026 21:49
@ChristianPavilonis ChristianPavilonis force-pushed the feature/edge-cookies-final branch from 7009838 to 43df212 Compare April 16, 2026 22:14
@ChristianPavilonis ChristianPavilonis force-pushed the edge-cookie-sourcepoint-consent branch from 0e5c07c to a8b5b1c Compare April 16, 2026 22:22
Copy link
Copy Markdown
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Sourcepoint GPP consent is a small, well-motivated slice — the GPP-US decoder, the allows_ec_creation gating branch, and their tests are solid. But as shipped, the feature is non-functional (the JS module is never served to browsers), the PR fails CI, and it bundles several large unrelated changes that should be separate PRs.

Blocking

🔧 wrench

  • Sourcepoint JS module is built but never served. crates/trusted-server-core/src/integrations/registry.rs:790-792 hardcodes JS_ALWAYS = &["creative"]. The design spec (docs/superpowers/specs/2026-04-15-sourcepoint-gpp-consent-design.md:38) says the integration follows the same "JS-only, no Rust registration" pattern as creative, but that pattern requires JS_ALWAYS to list the module. integrations/mod.rs has no sourcepoint::register and JS_ALWAYS has no "sourcepoint" entry, so IntegrationRegistry::js_module_ids() will never include it — /static/tsjs=… will never concatenate tsjs-sourcepoint.js into a served bundle. End-to-end, no browser will ever execute mirrorSourcepointConsent(). Fix: add "sourcepoint" to JS_ALWAYS and a test asserting it ships in js_module_ids_immediate().

  • Orphaned dead-code file crates/trusted-server-core/src/ec/admin.rs. 380 lines implementing POST /_ts/admin/partners/register, but ec/mod.rs contains no pub mod admin; declaration (grep -rn "mod admin\|ec::admin::" crates/ returns zero hits). The base branch (feature/edge-cookies-final) replaced the KV-backed partner registry with config-based partners in commit 049a501e; that change dropped admin.rs but the rebase on this branch reinstated it unreferenced. Fix: delete crates/trusted-server-core/src/ec/admin.rs.

  • Clippy -D dead_code failures block CI. Three dead symbols introduced by the same rebase and carrying no callers:

    • crates/trusted-server-core/src/platform/test_support.rs:174recorded_request_headers method
    • crates/trusted-server-core/src/platform/test_support.rs:336build_services_with_http_client function
    • crates/integration-tests/tests/common/runtime.rs:56PartnerRegistrationFailed variant (this is the actual failing CI log message)

    Confirmed locally: cargo clippy --workspace --all-targets --all-features -- -D warnings fails in the PR's worktree. Fix: delete them all — nothing references any of the three.

  • Undisclosed scope: creative iframe sandbox lockdown. crates/js/lib/src/core/render.ts:7-18 removes allow-scripts, allow-same-origin, and allow-forms from the creative iframe sandbox= attribute, and crates/trusted-server-core/src/creative.rs:361-367 strips <iframe> elements entirely during HTML sanitization. Together, any creative relying on JavaScript (viewability pixels, click tracking, rich media, VPAID) or on third-party ad-tag iframes will stop rendering. This is a major ad-tech behavioral change wholly unrelated to Sourcepoint GPP consent, and it is not mentioned in the PR body. It needs its own PR with a threat model, product sign-off, and a rollout plan. Fix: extract these two changes to a dedicated PR.

❓ question

  • Why is Prebid User ID Module support shipping inside this PR? The title and body are "Support Sourcepoint GPP consent for EC generation," but the diff also contains ~132 lines of build-all.mjs User-ID-submodule generation, a new _user_ids.generated.ts, ~290 lines of new Prebid test coverage, a 212-line design spec (specs/2026-04-16-prebid-user-id-module-design.md), and a 599-line implementation plan. Two independent features in one review is hard to evaluate. Can Prebid User ID Module move to its own PR?

Non-blocking

📌 out of scope

  • No re-mirror after mid-session CMP interaction. mirrorSourcepointConsent() runs once when the module is parsed. If a user opens the Sourcepoint CMP and updates consent mid-session, __gpp / __gpp_sid won't reflect the new state until the next page load — meaning the same session can continue to block EC creation even after consent is granted. Follow-up: subscribe to __gppapi events (or a storage listener) and re-run on change.

Comment thread crates/trusted-server-core/src/cookies.rs Outdated
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts Outdated
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/trusted-server-core/src/consent/gpp.rs
Comment thread crates/trusted-server-core/src/consent/gpp.rs
Comment thread crates/js/lib/test/integrations/sourcepoint/index.test.ts Outdated
@ChristianPavilonis
Copy link
Copy Markdown
Collaborator Author

Addressed the requested review feedback in 3bbdb1d2.

Summary:

  • added sourcepoint to the JS-only bundle list and covered it with a registry test
  • removed the orphaned ec/admin.rs file
  • removed the dead-code clippy offenders in test support / integration tests
  • scoped this PR back down by reverting the unrelated creative-lockdown and Prebid user ID changes
  • aligned Sourcepoint cookie mirroring with the approved session-cookie spec
  • fixed the cookie test-name typo and the Sourcepoint test helper nit

Validation run locally:

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace
  • cd crates/js/lib && npx vitest run

I also replied to and resolved the inline threads above.

@aram356 aram356 requested a review from prk-Jr April 22, 2026 03:08
@ChristianPavilonis ChristianPavilonis force-pushed the edge-cookie-sourcepoint-consent branch from 3bbdb1d to 8a6df3a Compare April 22, 2026 20:33
@ChristianPavilonis ChristianPavilonis force-pushed the feature/edge-cookies-final branch from 9261993 to d8c943d Compare April 27, 2026 16:26
Copy link
Copy Markdown
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Follow-up review focused on areas not covered in the prior pass. Sourcepoint flow (mirror + GPP US decoding + EC gating) is functional and well-tested, but two doc/file-stale issues from the scoping commit (8a6df3af) need correction before merge, and the always-shipped Sourcepoint module has cross-CMP failure modes worth designing around.

PR effective scope note: vs main the diff is 80 commits / 111 files, because the intended base (feature/edge-cookies-final) has merged. The Sourcepoint-specific changes are a small subset (~14 files); reviewers landing on this PR via GitHub should read the description with that in mind.

Blocking

🔧 wrench

  • Stale Prebid User ID docs reference removed TSJS_PREBID_USER_IDS env var (docs/guide/integrations/prebid.md:226-261)
  • _user_ids.generated.ts claims to be auto-generated but is now hand-edited (crates/js/lib/src/integrations/prebid/_user_ids.generated.ts:1)

Non-blocking

🤔 thinking

  • Sourcepoint hardcoded as always-shipped will clobber __gpp set by other CMPs (registry.rs:792 + sourcepoint/index.ts:60-72)
  • First page load race: mirror runs before Sourcepoint CMP populates localStorage (sourcepoint/index.ts:91-93)
  • TCF presence silently overrides explicit GPP US sale_opt_out=Yes in US states (consent/mod.rs:506-515)
  • Session-scoped cookie + run-once mirror leaves stale __gpp after mid-session retraction (sourcepoint/index.ts:39)

♻️ refactor

  • Stale comment displaced by us_sale_opt_out insertion (consent/gpp.rs:74-75)
  • Make clearing logic CMP-safe by tracking write source (sourcepoint/index.ts:42-46)

⛏ nitpick

  • Unused/inconsistent default export (sourcepoint/index.ts:95)

CI Status

  • fmt: PASS
  • clippy: PASS
  • rust tests: PASS (1001 lib + 21 misc)
  • js tests: PASS (294)

Comment thread docs/guide/integrations/prebid.md Outdated
Comment thread crates/js/lib/src/integrations/prebid/_user_ids.generated.ts Outdated
Comment thread crates/trusted-server-core/src/integrations/registry.rs
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts Outdated
Comment thread crates/trusted-server-core/src/consent/mod.rs
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/trusted-server-core/src/consent/gpp.rs
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts Outdated
@ChristianPavilonis ChristianPavilonis force-pushed the edge-cookie-sourcepoint-consent branch from 8643550 to 24d4515 Compare May 5, 2026 12:30
@ChristianPavilonis ChristianPavilonis requested a review from aram356 May 5, 2026 12:50
Copy link
Copy Markdown
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Follow-up review focused on what's new since the last pass and on items I think were missed. The marker-cookie response in a2c6d831 substantively addresses most prior feedback (clearing is now CMP-safe, retries cover the first-load race, the stale comment is fixed, the default export is gone). The remaining issues are: the symmetric write-path clobber that the marker doesn't cover, a real CI failure inherited from the merge-base, and continued scope creep beyond the Sourcepoint feature.

Local verification on the PR HEAD passes: cargo check, cargo clippy --workspace --all-targets --all-features -- -D warnings, cargo test --workspace, and npx vitest run (301 tests). CI fails because the prepare integration artifacts job hits a build-time validation panic.

Blocking

🔧 wrench

Non-blocking

🤔 thinking

  • Scope: PR still bundles unrelated commits after the partial scope-down. 8e3ff806 removed the Prebid User ID feature, but the diff vs feature/edge-cookies-final still contains fedcc347 ("Wire request signing… and Move logging initialization") which re-applies already-merged PRs #609/#610 — once the base branch is rebased onto current main, this commit is duplicate work and a guaranteed merge-conflict source. 24d45156 ("Remove generated Prebid user ID shim") and the clearPrebidEidsCookie change at prebid/index.ts:454 are also outside the Sourcepoint feature. Suggestion: rebase feature/edge-cookies-final onto current main first to drop fedcc347, and split the Prebid User ID shim removal into its own PR.

  • __gpp_sid marker-check on the write path is dead code — see inline.

  • scheduleInitialRetry leaks a real setTimeout across Vitest tests — see inline.

  • DOMContentLoaded + setTimeout(500) can double-fire mirrorSourcepointConsent — see inline.

  • Prebid clearPrebidEidsCookie on missing getUserIdsAsEids is a behavior change — see inline.

♻️ refactor

  • decode_us_sale_opt_out accumulator can drop saw_not_opted_out — see inline.

🌱 seedling

  • Cookie size limits not enforced client-side. Server caps at MAX_GPP_STRING_LEN: 8192 in consent/gpp.rs:33, but browsers cap individual cookies near ~4096 bytes. A long Sourcepoint GPP payload (compound multi-section) can be silently truncated by the browser, leaving the server unable to decode. Worth a follow-up: cap and log client-side instead of writing a doomed cookie.

📌 out of scope

  • TCF-vs-GPP-US precedence is documented but not configurable. The spec section "TCF-before-GPP precedence is intentional" makes a permanent policy choice some publishers may want to override. Tracking issue would prevent this from getting buried.
  • Sourcepoint integration runs on every Trusted Server page (registry.rs:803, sourcepoint/index.ts:151). With the marker cookie this is correctness-safe, but every visitor on every site pays ~3 KB of payload + a localStorage scan + cookie writes for a feature that only applies to Sourcepoint customers. Worth a follow-up: opt-in via publisher config, or only register listeners after a first valid mirror.

⛏ nitpick

  • Spec lists test file at the wrong path — see inline.

CI Status

  • fmt: PASS (local)
  • clippy: PASS (local, --all-targets --all-features -- -D warnings)
  • rust tests: PASS (local, 1073 + 21 + 19)
  • js tests: PASS (local, 301 tests / 24 files)
  • integration tests: FAIL (prepare integration artifacts panics; see Blocking above)

Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts Outdated
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts Outdated
Comment thread crates/js/lib/src/integrations/sourcepoint/index.ts
Comment thread crates/js/lib/src/integrations/prebid/index.ts
Comment thread crates/trusted-server-core/src/consent/gpp.rs Outdated
Comment thread docs/superpowers/specs/2026-04-15-sourcepoint-gpp-consent-design.md Outdated
@ChristianPavilonis ChristianPavilonis requested a review from aram356 May 6, 2026 21:05
@ChristianPavilonis ChristianPavilonis force-pushed the feature/edge-cookies-final branch from f9d95fc to 0c542e3 Compare May 7, 2026 18:44
Introduces TSJS_PREBID_USER_IDS env var (mirroring TSJS_PREBID_ADAPTERS)
to control which Prebid User ID submodules are bundled. The hardcoded
imports in index.ts are replaced with a generated file written by
build-all.mjs at build time, defaulting to the same 13-submodule set.

- build-all.mjs: generatePrebidUserIds() validates names, denylists
  liveIntentIdSystem, and writes _user_ids.generated.ts. Existence check
  also probes dist/src/public/ to handle modules shipped as .ts in sources
  (sharedIdSystem).
- index.ts: replaces 13 hardcoded submodule imports with
  import './_user_ids.generated'
- _user_ids.generated.ts: committed default with all 13 submodules
- Tests: updated mocks and regression guard; added 9 syncPrebidEidsCookie
  behavior tests
- Docs: new "User ID Modules" section in prebid.md with TSJS_PREBID_USER_IDS
  usage; spec follow-up #1 marked complete
__gpp and __gpp_sid are read by the Rust server over HTTPS; they must be
Secure. Also sets Max-Age=86400 (matching ts-eids) so stale consent state
doesn't outlast the session, and replaces the legacy expires= deletion
pattern with Max-Age=0.
@ChristianPavilonis ChristianPavilonis force-pushed the edge-cookie-sourcepoint-consent branch from e7b561a to d0dd47b Compare May 7, 2026 18:55
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