Repository navigation
Preserve Prebid EID fidelity from browser capture to bidder request - #1415
Merged
dhruv8sh merged 15 commits intoOct 19, 2026
Merged
Conversation
Eid gains inserter, matcher, mm and ext, each skipped when absent, so serialization without them is byte-identical. Eid::new replaces the struct literals that never carry these fields. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
ec::client_eids parses each array element on its own, so one malformed entry or field costs only itself. It validates sources, UIDs, atype, EID-level provenance fields and ext sizes, drops duplicates before the 64-entry cap, and logs field names and sources but never values. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
AdRequest captures eids as raw JSON so a lone surrogate in one entry no longer rejects the whole /auction body. The ts-eids structured branch uses the same parser, so a mistyped field or bad entry costs only itself and the body's caps apply to the cookie. Admin diagnostics still report raw sources and UIDs. The end-to-end fixture tests land with the merge rewrite, which they also depend on. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Browser entries reach providers unchanged, in wire order, with repeated sources kept apart. KV adds entries only for registered sources the browser did not send, so its stale UID and operator atype no longer override the browser's. Includes the end-to-end TSJS fixture tests deferred from the shared-parser change, since they depend on this merge keeping EID-level fields. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Finalization chooses its browser input by sync source. /auction reads the client set the auction used, so a cookie from an earlier page is not ingested next to a scoped body. Other routes keep reading ts-eids, and the sharedId cookie is still applied last. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
collectBidderEids copies ortb2.user.ext.eids and user.eids as plain data within a 64 KiB UTF-8 budget. fitEidCookie builds the ts-eids value without EID-level ext, trimmed to 3,072 bytes and encoded as UTF-8 base64. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
…buildRequests buildRequests reads bidderRequest.ortb2 instead of getUserIdsAsEids(), so IDs restricted to other bidders or redacted for trustedServer are no longer forwarded, and ortb2 EIDs such as Lockr's ID5 now arrive with their EID-level fields. The cookie copies each request's set before it is sent and is cleared when Prebid's TCF rules block trustedServer. bidsBackHandler no longer touches it. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
The served shim measured 42,144 characters after it gained the bidder-request EID collector, ts-eids cookie fitting and the tcf2Enforcement listener, so the bound moves from 41,000 to 42,500. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
installUserIdModules forced auctionDelay 0, syncDelay 3000, syncsPerBidder 5 and iframe syncs for every bidder on window.load. Prebid accumulates userSync settings, so every later auction ran without waiting for pending IDs. TS now writes only managed userIds and, on a late seed, autoRefresh. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
A JSDOM suite evaluates the generated bundle and the served shim and captures /auction bodies. It covers a bidder-restricted ID, same-source entries with their own fields, the first auction after window.load, and TCF with and without the full trustedServer exception. Every arm except the timing control fails against main. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
D1, D3 and both TCF arms now assert that document.readyState is still loading after install and after the auction, so each proves it ran before window.load as spec 7.5 requires. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Only tests read resolvePrebidUserIdModulesFromEids and the registry's eidSources. The data could not be made correct, since several modules share sources. The CLI and bundle builder read only module and config names. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Guides now describe forwarding the trustedServer bidder request's EIDs, the browser-first merge, per-entry validation, KV ingestion inputs by route, TS no longer setting userSync, and the full trustedServer TCF rule with its warning. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Guard the cookie clear so a blocked document.cookie costs only the cookie, and cover the nothing-fits clear path and both failure paths with adapter tests. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
Admin diagnostics lost the parse error for cookies that decode to a non-array JSON value. Routing is unchanged. Record the final review fixes in the red log. Signed-off-by: dhruv8sh <dhruv8sh@proton.me>
13 of 17 tasks
dhruv8sh
marked this pull request as draft
October 19, 2026 11:28
dhruv8sh
merged commit Oct 19, 2026
0ea795a
into
spec/1246-prebid-eid-fidelity
15 of 16 checks passed
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.
Summary
trustedServerPrebid adapter now forwards exactly the EIDs Prebid placed in its own bidder request (ortb2.user.ext.eidsanduser.eids), with their OpenRTB 2.6inserter,matcher,mmandext. It no longer readspbjs.getUserIdsAsEids(), which leaked IDs restricted to other bidders or redacted fortrustedServer(D1), and missed everyortb2EID such as Lockr's ID5 (D2, Write Lockr IDs to KV store #1246).ts-eidswriter moved intobuildRequests, so the cookie copies each request's set (without EID-levelext) before the request is sent, and atcf2Enforcementlistener clears it when Prebid's TCF rules blocktrustedServer. The server validates body and cookie with one parser that drops only the bad entry or field, forwards browser entries as sent with KV filling only absent registered sources, and ingests into KV on/auctiononly the client set the auction used.userSyncsettings (the forcedauctionDelay: 0that sent first auctions before async ID modules resolved, D4).ts-eidsandx-ts-eidsboth stay. Stacked on #1416, which carries the spec and plan; this PR stays in draft until #1416 merges.Changes
crates/trusted-server-core/src/openrtb.rsEidgainsinserter,matcher,mm,ext(skipped when absent, byte-identical otherwise) andEid::newcrates/trusted-server-core/src/ec/client_eids.rs(new)crates/trusted-server-core/src/auction/formats.rs,auction/endpoints.rsAdRequest.eidscaptured as raw JSON; body and cookie go through the shared parser;merge_auction_eidsforwards browser entries as sent and adds KV only for absent registered sourcescrates/trusted-server-core/src/ec/prebid_eids.rs,ec/admin.rsts-eidscookie decoded through the shared parser; admin diagnostics keep raw sources and UIDs; non-array cookie JSON reported as a parse errorcrates/trusted-server-core/src/ec/finalize.rs/auctionreads its own client set, other routes the cookiecrates/trusted-server-core/src/{publisher.rs, ec/eids.rs, ec/identify.rs, integrations/*, auction/*}Eid::newcall sites; outbound serialization, merge and ingestion testscrates/trusted-server-adapter-fastly/src/app.rsts-eidshanded to finalizationcrates/trusted-server-js/lib/src/integrations/prebid/eids.ts(new)ts-eidsvalue builder (no EID-levelext, 3,072 bytes, UTF-8 base64)crates/trusted-server-js/lib/src/integrations/prebid/index.ts,src/core/auction.tsbuildRequestsforwards the bidder request's EIDs and writes or clears the cookie; TCF listener; old collector, cookie sync andinstallUserIdModulesremoved;AuctionEidis opaquecrates/trusted-server-js/lib/src/integrations/prebid/user_id_modules.{ts,json}eidSourcesremovedcrates/trusted-server-js/lib/test/**eids.test.ts; adapter tests with agetUserIdsAsEidscanary; real-Prebid JSDOM suiteprebid-eid-fidelity.test.mjs; cross-language fixturesdocs/guide/**trustedServerTCF exception ruledocs/superpowers/implementation-notes/2026-10-18-prebid-eid-fidelity.mdCloses
Closes #1246
Spec
Spec:
docs/superpowers/specs/2026-10-18-prebid-eid-fidelity-design.md(in #1416; five agent review rounds in its review log)What breaks (spec 8.2)
atypeare no longer sent for a source the browser sends, and repeated sources are no longer folded.syncEnabled,filterSettings,syncsPerBidder,syncDelayandauctionDelay, and no longer force-enables iframe syncs. Auctions wait up toauctionDelay(500 ms by default) while IDs are pending. A late managed seed still leavesautoRefresh: true.trustedServer: IDs restricted to them are no longer forwarded.trustedServerexception: the cookie is cleared instead of written fromgetUserIdsAsEids(). Body-less routes and KV ingestion lose browser EIDs for that traffic, including the IdentityLink flow.ortb2EIDs and provenance fields, never EID-levelext, written at request time without a Purpose 1 check of its own./auctionno longer reads the cookie next to the body.user.ext.eidsandx-ts-eids./_ts/admin/eidsconsumers: new fields in the cookie entries; a mistyped field is dropped instead of failing the whole cookie.sourceanduidsfrom new bodies and cookies.Operator actions (spec 8.3)
inserter,matcher,mmand EID-levelext) to each configured provider in staging and confirm it is accepted.ts prebid client).userSyncchange: setauctionDelayandfilterSettingsin their own Prebid config if Prebid's defaults do not suit them.trustedServerthe full TCF exception rule (and the fullpersonalizedAdsrule wheneidsRequireP4Consentis set). A partial rule switches off TCF checks for every bidder.Follow-ups (spec 8.5)
Prebid.js identityLink fix for a malformed
_lr_env(D7);sharedIdcookie ingestion ignoring Prebid restrictions; TS's own TCF identity; per-bidder EID forwarding through Prebid Servereidpermissions; refresh rules for browser-sourced KV IDs; restoringautoRefreshafter a late managed seed; gating thets-eidswrite on Purpose 1; a server-written cookie; a lenient parse of the rest of the/auctionbody; no client-sidetrustedServerdemand under GDPR without a publisher exception (predates this work).Test plan
cargo test-fastly && cargo test-axum(core 2985 passed), pluscargo test-fastly-reuse,cargo test-cloudflare,cargo test-spincargo clippy-fastly && cargo clippy-axum, plusclippy-cloudflare,clippy-cloudflare-wasm,clippy-spin-native,clippy-spin-wasm,clippy-cli,clippy-codegencargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest run(1252 passed, 52 files, Node 24.12.0), including the real-Prebid suite whose arms fail againstmaincd crates/trusted-server-js/lib && npm run format && npm run lintcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute servetemplate_build_digesttest and clippy;scripts/lint-wasm-clock.sh;scripts/test-cli.sh; Markdown Prettier outsidedocs/ts dev proxyon a page with Lockr and the default User ID preset (spec 7.6)Checklist
unwrap()in production code — useexpect("should ...")logmacros per AGENTS.md (notprintln!)