Skip to content

Implement configurable cache header policies#860

Draft
ChristianPavilonis wants to merge 156 commits into
mainfrom
refactor/cache-headers
Draft

Implement configurable cache header policies#860
ChristianPavilonis wants to merge 156 commits into
mainfrom
refactor/cache-headers

Conversation

@ChristianPavilonis

@ChristianPavilonis ChristianPavilonis commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Standardizes cache policy rendering across Fastly, Cloudflare, CDN, and s-maxage fallback headers.
  • Adds safe, configurable caching for hash-validated TSJS, publisher-origin static assets, and rehosted asset proxy responses.
  • Hardens privacy handling so private, no-store, and cookie-bearing responses strip shared edge-cache headers.

Changes

File Change
crates/trusted-server-core/src/cache_policy.rs Adds typed cache-policy rendering for browser and edge headers, including no-store/private cleanup.
crates/trusted-server-core/src/settings.rs Adds cache.asset_rules config, matchers/presets, validation, runtime prep, and path-to-policy resolution.
trusted-server.example.toml Documents disabled operator-controlled static/fingerprinted asset cache-rule examples.
crates/trusted-server-core/src/http_util.rs Routes static ETag responses through the cache-policy renderer.
crates/trusted-server-core/src/tsjs.rs Uses exact module-set hashes when available and avoids unverifiable fallback hashes.
crates/trusted-server-js/Cargo.toml Makes hashing dependencies available to the build script.
crates/trusted-server-js/build.rs Generates per-module SHA-256 metadata for bundled JS modules.
crates/trusted-server-js/src/bundle.rs Exposes per-module hashes and caches concatenated bundle hashes.
crates/trusted-server-core/src/publisher.rs Applies hash-validated immutable TSJS caching and configured publisher asset cache rules.
crates/trusted-server-core/src/proxy.rs Applies normalized cache policies to rehosted asset proxy responses and reapplies them after finalization.
crates/trusted-server-core/src/response_privacy.rs Removes shared-cache headers from private/no-store/cookie-bearing responses.
crates/trusted-server-core/src/integrations/prebid.rs Makes the neutralized Prebid shim no-store, private instead of long-lived public cache.
crates/trusted-server-core/src/integrations/testlight.rs Updates the default TSJS fallback comment/source behavior for registry-free configuration.
crates/trusted-server-core/src/lib.rs Exports the new cache_policy module.
crates/trusted-server-adapter-axum/src/app.rs Passes the portable s-maxage fallback edge header mode to TSJS and publisher handlers.
crates/trusted-server-adapter-cloudflare/src/app.rs Passes the Cloudflare-specific CDN cache header mode to TSJS and publisher handlers.
crates/trusted-server-adapter-fastly/src/app.rs Passes Fastly Surrogate-Control mode through EdgeZero fallback dispatch.
crates/trusted-server-adapter-fastly/src/main.rs Reapplies asset cache policies with Fastly Surrogate-Control in legacy and EdgeZero flows.
crates/trusted-server-adapter-fastly/src/route_tests.rs Updates route-test finalization for selected edge cache headers.
crates/trusted-server-adapter-spin/src/app.rs Passes the portable s-maxage fallback edge header mode to TSJS and publisher handlers.
docs/superpowers/specs/2026-07-06-cache-control-header-design.md Adds the cache-control design scope and deferred dynamic caching notes.
docs/superpowers/plans/2026-07-06-cache-control-header-implementation-plan.md Adds the implementation and verification plan for cache-header work.

Closes

Closes #293

Follow-up: #859 tracks deferred dynamic response/template caching.

Test plan

  • cargo test-fastly && cargo test-axum
  • cargo clippy-fastly && cargo clippy-axum
  • cargo fmt --all -- --check
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve
  • Other: cargo test-cloudflare && cargo test-spin
  • Other: cargo clippy-cloudflare && cargo clippy-spin-native && cargo clippy-spin-wasm
  • Other: cd crates/trusted-server-js/lib && node build-all.mjs
  • Other: git diff --check
  • Other: npx prettier --check docs/superpowers/plans/2026-07-06-cache-control-header-implementation-plan.md docs/superpowers/specs/2026-07-06-cache-control-header-design.md

Note: cd docs && npm run format failed because docs-local Prettier was not installed; touched docs were checked with npx prettier instead.

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses tracing macros (not println!)
  • New code has tests
  • No secrets or credentials committed

jevansnyc and others added 30 commits April 15, 2026 20:47
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Incorporate all review feedback (aram356 + jevansnyc): cache contract,
  consent/GDPR gating, async restructuring detail, CreativeOpportunityFormat
  schema, glob pattern fix, XSS escaping, win notifications, APS params,
  timeout config key, defineSlot fix, gpt.rs ownership, KV migration path,
  Phase 2 sketch
- Fix Prettier formatting (format-docs CI)
- Add implementation plan (12 tasks, TDD, ordered by dependency)
- Incorporate all review feedback (aram356 + jevansnyc): cache contract,
  consent/GDPR gating, async restructuring detail, CreativeOpportunityFormat
  schema, glob pattern fix, XSS escaping, win notifications, APS params,
  timeout config key, defineSlot fix, gpt.rs ownership, KV migration path,
  Phase 2 sketch
- Fix Prettier formatting (format-docs CI)
- Add implementation plan (12 tasks, TDD, ordered by dependency)
Replace the head-injected __ts_bids design with a server-cached bid
delivery model fetched by the client via a new /ts-bids endpoint. The
auction never blocks page rendering — </head> flushes immediately, body
parses without waiting for bids, and the client fetches bids in parallel
with content paint.

Key changes:
- §2 Goal: bid delivery decoupled from page rendering; FCP unchanged from
  no-TS baseline
- §4.3 Auction Trigger: drop buffered/streaming dichotomy; single mode
  forces chunked encoding on all origins (WordPress, NextJS, etc.)
- §4.4 Head Injection: only __ts_ad_slots and __ts_request_id injected at
  <head> open; bid results moved to /ts-bids endpoint
- §4.6 Client Residual: __tsAdInit defines slots immediately, fetches
  bids via /ts-bids, applies targeting and fires refresh() after resolve
- §4.7 (new) Caching Behavior: explicit cacheability table for HTML, JS,
  CSS, tsjs bundle, bid results; Fastly edge HTTP cache leveraged for
  origin HTML
- §5 Request-Time Sequence: full mermaid diagram covering content +
  creative + burl flow with cache-hit and cache-miss branches; separate
  text sequences for cache-hit (~80ms FCP, ~900ms ad-visible) and
  cache-miss (~250ms FCP, ~1,050ms ad-visible)
- §6 Performance Summary: cache-hit and cache-miss columns; FCP added
  as a tracked metric
- §7 Implementation Scope: add bid_cache.rs, /ts-bids endpoint, force
  chunked encoding step
- §8 Edge Cases: origin-agnostic entries; new entries for /ts-bids 404
  and client-never-fetches-/ts-bids

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pivot from the /ts-bids fetch endpoint + in-process bid_cache design to
inline __ts_bids injection before </body>. The earlier design relied on
shared state that doesn't reliably survive Fastly Compute's per-request Wasm
isolate model — body injection achieves the same FCP property in a single
response with no shared-state requirement.

Key changes:

- §4.3: replace /ts-bids long-poll with bounded </body> hold tied to
  A_deadline. Body content above </body> paints first; close-tag held
  until auction completes or A_deadline fires (graceful __ts_bids = {}
  fallback).
- §4.3: add auction-eligibility gating (consent, bot UA, prefetch hints,
  HEAD method, slot match) so auctions fire on real first-page-load
  impressions only.
- §4.4: replace __ts_request_id + /ts-bids machinery with two inline
  <script> blocks — __ts_ad_slots at <head> open, __ts_bids before
  </body> via lol_html el.on_end_tag().
- §4.5: move both nurl and burl to client-side firing from
  slotRenderEnded after hb_adid match. Server-side firing rejected to
  avoid billing inflation on bids that never render.
- §4.6: replace fetch+Promise pattern with synchronous __ts_bids read.
  Add lazy slim-Prebid loader (post-window.load) for scroll/refresh
  auctions and Phase B identity warm-up. Add ts_initial=1 slot-ownership
  sentinel.
- §4.7: switch Cache-Control from private, no-store to private,
  max-age=0 to preserve browser BFCache eligibility while still
  preventing intermediate-cache leaks.
- §4.8 (new): document the EC/KV identity model as load-bearing auction
  input — Phase A retrieval at request time, Phase B post-render
  enrichment via slim-Prebid userID modules. Add bare-EC first-impression
  caveat and auction_eid_count metric. Note federated-consortium
  passphrase property and clickstream-compounding speed win.
- §5: update mermaid + cache-hit/miss timelines for bounded body hold;
  ad-visible converges to ~870ms (hit) / ~1,020ms (miss).
- §6: drop /ts-bids RTT row; add DCL row; add clickstream-compounding,
  TS-overhead, identity-coverage, and confidence-interval framing.
- §7: drop bid_cache.rs and /ts-bids endpoint from scope; add
  auction-eligibility gating and slim-Prebid bundle build target. Add
  explicit "Deleted" subsection.
- §8: drop /ts-bids edge cases; add SPA/pushState, bare-EC, bot/prefetch,
  HEAD, BFCache restoration cases.
- §9.6: server-side GAM downgraded from "Phase 2 commitment" to
  aspirational and contingent on Google agreement. §9.8 (slim-Prebid
  bundle composition), §9.9 (Privacy Sandbox), §9.10 (per-bidder consent)
  added as follow-ups.

Implementation plan at docs/superpowers/plans/2026-04-30-server-side-ad-templates.md
is now stale relative to this spec; needs regenerating before code lands.
…ities.toml

Adds the creative_opportunities field to Settings struct to deserialize
configuration for the server-side ad auction feature. Includes build.rs
stubs for types required during build-time configuration validation.

Creates creative-opportunities.toml with example slot configuration and
updates trusted-server.toml with the [creative_opportunities] section
defining GAM network ID, auction timeout, and price granularity settings.

Tests pass with proper TOML parsing of the creative_opportunities section.
…ared auction state

- Add `ad_slots_script: Option<String>` and `ad_bids_state: Arc<RwLock<Option<String>>>` fields to `HtmlProcessorConfig`
- Update `from_settings` to initialize both new fields with safe defaults
- Prepend `ad_slots_script` inside the existing `<head>` handler before integration inserts
- Add `element!("body", ...)` handler that uses `end_tag_handlers()` to inject `__ts_bids` before `</body>`; falls back to empty `{}` when auction state is `None`
- Add `IntegrationRegistry::empty_for_tests()` test helper
- Add three new tests covering all injection paths
…gibility gates; max-age=0

- Make handle_publisher_request async; add orchestrator and slots_file params
- Dispatch origin request with send_async before running auction in parallel
- Gate auction on GET, no prefetch, no bot, matched slots, TCF purpose-1 consent
- Run server-side auction and write bucketed bids to ad_bids_state Arc<RwLock>
- Compute ad_slots_script after response headers; set Cache-Control: private, max-age=0
- Fix Stream arm to thread actual ad_slots_script and ad_bids_state through
- Add build_auction_request, build_bid_map, build_bids_script, build_ad_slots_script helpers
- Update route_tests.rs to pass empty slots_file to route_request
- build_bid_map now returns serde_json::Map with full bid objects (hb_pb,
  hb_bidder, hb_adid, nurl, burl) instead of a plain CPM string map
- build_bids_script / build_ad_slots_script now emit full <script> tags
  using JSON.parse("…") for safe inline embedding; add html_escape_for_script helper
- build_ad_slots_script uses correct property names (gam_unit_path, div_id,
  formats, targeting) matching the client-side TSJS bundle expectations
- Replace map_or(false, …) with is_some_and(…) on lines 546, 549, 567
- Add # Panics doc sections to handle_publisher_request and create_html_processor
… from slotRenderEnded; slim-Prebid lazy loader
- Enable APS and adserver_mock in auction config; set providers and mediator
- Increase auction_timeout_ms from 500ms to 3000ms — 500ms was too tight
  for HTTPS round-trips to mocktioneer, leaving the mediator zero budget
- Fix mediation request: send numeric price instead of opaque encoded_price;
  mocktioneer requires a decoded price field and does not support encoded_price
- Expand creative-opportunities slot page_patterns to include /news/**
Define SlotRenderEndedEvent, SlotRenderEvent, and TestWindow types to
eliminate all @typescript-eslint/no-explicit-any violations in
gpt/index.ts and gpt/index.test.ts. Extend GptWindow with
__tsjs_slim_prebid_url so installSlimPrebidLoader avoids the any cast.
Set gam_network_id to 88059007 (autoblog production network). Update
atf_sidebar_ad slot to /88059007/autoblog/news with div_id
ad-atf_sidebar-0-_r_2_ (desktop ATF sidebar, 300x250); restrict
page_patterns to article paths only (/20**, /news/**) since that div
does not exist on the homepage. Add homepage_header_ad slot targeting
/88059007/autoblog/homepage with ad-header-0-_R_jpalubtak5lb_ for
970x90/728x90/970x250 leaderboard formats. Reduce auction_timeout_ms
from 3000 to 500 to cap TTFB at the spec-recommended ceiling.
The bids script set window.__ts_bids but never invoked the
__tsAdInit function, leaving GPT slots undefined and server-side
targeting (hb_pb, hb_bidder) never applied. Both the winning-bid
path (build_bids_script) and the no-auction fallback (html_processor
None branch) now guard-call the function after the assignment.
prk-Jr and others added 25 commits June 22, 2026 23:38
Resolve conflicts from the EdgeZero PR #257 sync on main (#761):

- Cargo.toml: adopt main's crate renames, fastly/log-fastly 0.12, and
  edgezero tracking the upstream main branch; keep the branch's glob dep.
- publisher.rs: keep both sides' new tests; forward-port test body
  extraction to the Option-returning Body::into_bytes API and drop the
  now-unused response_body_string helper superseded by the branch tests.
- auction/endpoints.rs: unwrap_or_default the Option-returning into_bytes.
- Relocate the new GPT SPA tests into the renamed crates/trusted-server-js
  tree and refresh stale crates/js doc-comment paths.
- Take main's CI-validated integration-tests lockfile (deps unchanged on
  the branch).
The EdgeZero sync (#761) renamed crates/js and crates/integration-tests
to crates/trusted-server-*. The old directories still hold local-only
build artifacts (node_modules, target, dist) whose gitignore rules moved
with the rename, so git now sees them as untracked. Ignore the defunct
paths until the directories are removed from disk.
P1 — EdgeZero finalize cache/Set-Cookie privacy parity:
Share the protected finalizer between the legacy and EdgeZero paths.
apply_finalize_headers now strips surrogate cache headers and downgrades
cookie-bearing responses to private, and skips operator response_headers
that would re-enable shared caching on uncacheable responses;
finalize_response delegates to it. The EdgeZero entry point re-applies an
HttpResponse enforce_set_cookie_cache_privacy after ec_finalize_response
and request-filter effects so a late EC Set-Cookie cannot reach a shared
cache. Adds middleware tests for both cases.

P1 — empty page-bids must not enable GPT services:
adInit() only enables GPT services when it has a slot to display or
refresh, and the SPA hook skips adInit() for an empty page-bids response
unless prior TS state needs sweeping. Prevents a consent-denied or
kill-switched navigation from activating the publisher's GPT setup.

P2 — scope Prebid refresh targeting to the refreshed slots:
setTargetingForGPTAsync is called with the synthetic refresh ad-unit
codes so a one-slot refresh no longer mutates unrelated GPT slots.

P2 — validate nested slot value shapes at build time:
The creative-slot build check now validates media_type against the
runtime MediaType variants, targeting as a string map, page_patterns as
strings, providers.aps.slot_id as a string, providers.prebid.bidders as a
map, and floor_price as a number — closing build-green/runtime-broken
gaps. A drift-guard test ties media_type to the runtime enum.

CI — suppress CodeQL cleartext-logging false positives:
Annotate the provider/mediator "not registered" warnings; they log static
config identifiers, not secrets.
The merge took main's trusted-server-integration-tests Cargo.lock, but the
branch's trusted-server-core now pulls in glob (the creative-slot build
check uses glob::Pattern). The integration crate path-depends on core, so
its locked graph was missing glob and the --locked CI build refused to
update it. Add only glob v0.3.3; no other versions change, keeping the
shared direct-dependency parity check green.
…tes-impl

Reconcile the server-side ad-template/auction work with main's EdgeZero
canary rollout, the Axum/Cloudflare/Spin adapters, and the edgezero
into_bytes() repin.

- main(): route to EdgeZero only when the canary rollout selects it and
  the settings carry no creative_opportunity slots (combine both gates).
- Thread the new handle_publisher_request(kv, ec_context, auction)
  parameters through the Axum, Cloudflare, and Spin adapters with an
  empty AuctionDispatch — server-side auction stays deferred there, as on
  the Fastly EdgeZero path.
- Keep HEAD's apply_floor_prices None-price drop and the corrected
  publisher OwnedProcessResponseParams / single stream_publisher_body;
  union the diverged publisher, html_processor, and orchestrator tests.
- Drop the stale into_bytes().unwrap_or_default() test calls and add the
  new HtmlProcessorConfig / PlatformBackendSpec fields and the
  route_request slots argument to the bench and adapter tests.
Blocking:
- Move tokio to [dev-dependencies] in trusted-server-core; it was only used
  by #[tokio::test] and was linking the runtime into the wasm prod build.
  Confirmed the release wasm adapter build no longer pulls tokio.
- Roll back the SPA currentPath on a failed /__ts/page-bids fetch so a
  transient error no longer permanently strands that route (gpt/index.ts).

Build/runtime parity and diagnostics:
- Bound creative-opportunity format width/height to u32 range at build time
  so values the runtime u32 cannot hold are rejected early.
- Add #[serde(deny_unknown_fields)] to the build.rs config stub to match the
  runtime type and reject mistyped table keys at build time.
- Warn when the <body> end-tag handler is absent so a silently non-rendering
  server-side ad feature is diagnosable.
- Log dropped slot bidders that are neither configured nor the aps provider.
- Log build_bid_index collisions (multiple bids per seat/imp).

JS correctness:
- Narrow uid.atype to a number before the range check in sanitizeAuctionUid.
- Resolve findInjectedSlotForRefresh by exact/container match before the
  prefix fallback, with a regression test for prefix-overlapping div_ids.
- Guard the gpt_bootstrap prefix scan against an empty div_id.
- Route injectAdmIntoSlot through findSlotElementByDivId for consistency.

Cleanup and docs:
- Remove the dead has_post_processors routing dependency from
  classify_response_route and (now unused) handle_publisher_request.
- Extract the duplicated EID resolution/consent-gating/device tail shared by
  the initial-page and page-bids dispatch paths into one helper.
- Anchor the surrogate cache-header list in a shared const so the legacy and
  EdgeZero Set-Cookie privacy paths stay aligned.
- Refresh stale docs (PublisherResponse::Stream, the publisher module
  platform-coupling note, and UserInfo.eids consent-gate location).
Moving tokio to trusted-server-core dev-dependencies removed it from the
crate's normal dependency list, so the integration-tests lockfile (which
resolves core's non-dev deps) no longer pins tokio under core. Keeps
`cargo --locked` green for the integration job.
These EdgeZero-style adapters finalize buffered, and the sync
`buffer_publisher_response` drives `stream_publisher_body`, which ignores
`params.dispatched_auction` — so they injected an empty `tsjs.bids = {}`
while Fastly (legacy streaming finalize) served real bids.

- Add `buffer_publisher_response_async` in core: for the Stream variant it
  drives `stream_publisher_body_async`, which awaits
  `collect_dispatched_auction`, writes `ad_bids_state`, and injects the bids
  before `</body>`.
- Pass the configured `creative_opportunities.slot` (not empty) to
  `handle_publisher_request` on all three adapters; it matches them against
  the request path internally.
- Call the async finalize from each adapter (Cloudflare/Spin via their
  now-async `resolve_publisher_response`).

EID targeting stays off for now (these adapters pass `kv: None`).
…ters

The auction consent gate (`consent_allows_server_side_auction`) reads
jurisdiction and TCF consent from the EC context. The adapters passed
`EcContext::default()` to `handle_publisher_request`, leaving jurisdiction
Unknown with no consent — so the gate failed closed and no auction ran
(empty `tsjs.bids`), even though the slots matched.

Build the context via `read_from_request_with_geo` (consent from the
request, geo from the platform), mirroring the Fastly entry point, and fall
back to default on a parse error. Cloudflare resolves geo from the Workers
`cf` object when deployed; Axum and Spin have no-op geo providers, so on
those a known non-GDPR jurisdiction requires the request to carry geo or the
gate needs a TCF consent signal.
When the auction does not run, this pinpoints which gate suppressed it
(slots, bot, navigation, consent, or orchestrator kill switch) instead of
only seeing `dispatch_auction: None`. Pair with the EC-context jurisdiction
log when consent_allows_auction is false.
The EdgeZero buffered path passed empty slots and finalized via the sync
`buffer_publisher_response`, so configured creative-opportunity slots were
routed to the legacy path by `edgezero_can_handle_settings`. Now that
`buffer_publisher_response_async` collects the dispatched auction, EdgeZero
can run the full ad stack:

- Pass the configured `creative_opportunities.slot` and finalize via
  `buffer_publisher_response_async` (the path's `ec.ec_context` already
  carries consent + platform geo). EID targeting stays off (`registry: None`).
- Drop the `edgezero_can_handle_settings` gate, its routing branch, the three
  tests, and the now-unused test settings helpers — EdgeZero handles
  configured slots, so the legacy fallback for them is obsolete.
The EdgeZero publisher path dispatched the auction with registry: None, so
the bid request carried no KV identity-graph EIDs (only client cookie EIDs).
It already passes ec.kv_graph as the identity KV, so wire the matching
PartnerRegistry::from_config(settings.ec.partners) into the AuctionDispatch
to resolve server-side partner EIDs — matching the legacy auction path.

Fastly-only: the sync EC identity graph (KvIdentityGraph/EcKvStore) works on
Fastly's sync KV; the async-KV portability adapters are unaffected (they
still pass registry: None until the EC graph supports async stores).
Resolve the fifth code-review pass. The blocking findings were all
cross-adapter parity gaps in the server-side auction:

- Build the geo-aware EC context in the /auction handlers on Axum,
  Cloudflare, and Spin. They passed EcContext::default(), leaving
  jurisdiction Unknown and failing the consent gate closed even for
  consented users. A shared per-adapter build_ec_context helper now
  serves /auction, page-bids, and the publisher fallback, and logs
  (rather than swallows) a malformed-consent read error.
- Wire GET /__ts/page-bids and its OPTIONS->403 CSRF guard on the
  Fastly EdgeZero path and all three portability adapters, reusing core
  handle_page_bids and a shared page_bids_preflight_denied() helper.
  Previously it was Fastly-legacy-only, so SPA re-auction silently fell
  through to the origin on every other path.
- Add trusted_server_core::response_privacy with the Set-Cookie
  cache-privacy downgrade and the uncacheable-operator-header guard, and
  call it from every adapter's apply_finalize_headers so a shared cache
  (Cloudflare) can no longer serve an operator/origin public Cache-Control
  on a cookie-bearing response.

Also address the inline and non-blocking findings: warn on a dropped
dispatched auction for bodiless responses, extract build_slot_json shared
by the initial-page and page-bids paths, use creative_opportunity_slots()
everywhere, drop the PBS id->ad_id fallback, log APS slot-id collisions,
align the parallel provider parse with the collect path, remove the dead
sync buffer_publisher_response, factor the mediator placeholder request,
drop the unused toml dependency, guard MediaType against a future
serde(default), and document the Fastly-only KV EID enrichment.

JS: dedup win/billing beacons across concurrent renders, add the SSR
guard to installSlimPrebidLoader, and short-circuit waitForSlotElements
on an already-aborted signal. Add regression tests for the currentPath
rollback and the u32::MAX format-dimension rejection.
Adopt main's ts-CLI config model over the branch's build-time embedding:
drop the build.rs config generation and [build-dependencies], accept the
trusted-server.toml deletion, remove the now-orphaned creative_slot_build_check
module, and migrate the [creative_opportunities] example into
trusted-server.example.toml. Slot validation is preserved via
Settings::prepare_runtime, which the CLI runs at config push time.

Adopt main's refactored EdgeZero finalize path in the Fastly adapter and
centralize the per-user Set-Cookie cache-privacy guard in send_edgezero_response
so it covers every EdgeZero send path.

Fix three core test helpers for main's Body::into_bytes -> Option change.
- Stop forwarding client-supplied X-Forwarded-For to Prebid Server;
  synthesize it from the platform-attested client IP instead
- Make the APS slot ID remapping request-scoped by threading the
  AuctionRequest through parse_response_with_context, removing the
  shared provider-instance mutex that concurrent auctions could race
- Guard dispatch_auction against multi-provider fan-out on platforms
  whose HTTP client executes requests sequentially, mirroring
  run_providers_parallel
- Reject negative and non-finite creative-opportunity floor prices at
  config validation
- Re-run the Set-Cookie cache-privacy downgrade after operator response
  headers are applied so configured Set-Cookie plus public cache
  headers cannot produce a shared-cacheable response
- Install Prebid user ID modules immediately when the bundle loads
  after window.load (slim-Prebid lazy path), with a once-only listener
- Drop allow-same-origin from debug ADM fallback iframes and validate
  extracted iframe src schemes to http(s) only
@ChristianPavilonis ChristianPavilonis changed the base branch from main to server-side-ad-templates-impl July 7, 2026 17:59
Base automatically changed from server-side-ad-templates-impl to main July 7, 2026 20:08
ChristianPavilonis

This comment was marked as low quality.

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.

4 participants