Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
ff782e0
Quantize auction transport timeouts to stabilize Fastly backend names
prk-Jr Jul 8, 2026
35e872b
Stream publisher origin bodies end-to-end on Fastly
prk-Jr Jul 8, 2026
affb46c
Harden the streaming publisher pipeline after review
prk-Jr Jul 8, 2026
2c64f3c
Emit processor_init_error telemetry on streaming finalize failure
prk-Jr Jul 8, 2026
b95813c
Fix Prebid User ID diagnostics
ChristianPavilonis Jul 10, 2026
e94430e
Order Prebid imports for lint
ChristianPavilonis Jul 10, 2026
b56adcb
Preserve vendor-specific OpenRTB atype values
ChristianPavilonis Jul 10, 2026
b52b415
Dump full SSAT prebid responses in ts-debug auction comment
prk-Jr Jul 10, 2026
48db8bf
Surface prebid HTTP error status and body in auction dump
prk-Jr Jul 10, 2026
08de9c4
Merge branch 'main' into feat/ssat-write-prebid-response-debug
prk-Jr Jul 10, 2026
d5871da
Stop leaking prebid error body into the public auction response
prk-Jr Jul 13, 2026
1cb8adf
Merge remote-tracking branch 'origin/feat/ssat-write-prebid-response-…
prk-Jr Jul 13, 2026
b577451
Merge branch 'main' into streaming-pipeline-async-core
aram356 Jul 13, 2026
4e8e3bf
Merge branch 'main' into quantize-auction-transport-timeouts
aram356 Jul 13, 2026
b56a0a1
Add design spec for SSAT inline creative rendering
prk-Jr Jul 13, 2026
6e8c48f
Add implementation plan for SSAT inline creative rendering
prk-Jr Jul 13, 2026
16e9d95
Correct plan call graph, add TsjsApi type + beacon steps, note precon…
prk-Jr Jul 13, 2026
34018e4
Revise SSAT inline-creative plan/spec per review
prk-Jr Jul 13, 2026
a1b844d
Refine plan test details per second review
prk-Jr Jul 13, 2026
6649875
Always include adm in bid map; gate only debug_bid blob
prk-Jr Jul 13, 2026
e2b13bb
Gate GAM-bypass adm injection on per-bid debug_bid
prk-Jr Jul 13, 2026
cb60fd2
Apply prettier formatting to spec/plan docs
prk-Jr Jul 13, 2026
c8ae53f
Harden Fastly publisher streaming against review findings
prk-Jr Jul 14, 2026
5512d85
Address auction transport-timeout review findings
prk-Jr Jul 14, 2026
62a2dd0
Add Prebid error diagnostics
ChristianPavilonis Jul 10, 2026
ef61953
Suppress fabricated empty Prebid bidder params
prk-Jr Jul 14, 2026
0acac4b
Preserve Prebid ad units across GPT refreshes
ChristianPavilonis Jul 14, 2026
c59a064
Handle deferred Prebid delivery refreshes
ChristianPavilonis Jul 14, 2026
310e62f
Use HTTP status error type for Prebid failures
ChristianPavilonis Jul 14, 2026
641e512
Merge remote-tracking branch 'origin/feat/ssat-write-prebid-response-…
ChristianPavilonis Jul 15, 2026
e93de21
Merge remote-tracking branch 'origin/add-prebid-error-diagnostics' in…
ChristianPavilonis Jul 15, 2026
af6367a
Merge remote-tracking branch 'origin/feat/ssat-render-inline-creative…
ChristianPavilonis Jul 15, 2026
e6d8d83
Merge remote-tracking branch 'origin/fix/prebid-suppress-empty-bidder…
ChristianPavilonis Jul 15, 2026
e80c164
Merge remote-tracking branch 'origin/fix/prebid-refresh-auctions' int…
ChristianPavilonis Jul 15, 2026
6b4e82e
Make auction creative rewriting optional
ChristianPavilonis Jul 15, 2026
1ccf3fa
Merge remote-tracking branch 'origin/feature/optional-creative-rewrit…
ChristianPavilonis Jul 15, 2026
2aa2a9e
Improve APS auction diagnostics
ChristianPavilonis Jul 15, 2026
ba3bad9
Log APS responses in test environment
ChristianPavilonis Jul 15, 2026
1610e9d
Add first-class APS OpenRTB integration
ChristianPavilonis Jul 16, 2026
26afb47
Merge APS OpenRTB integration for deployment testing
ChristianPavilonis Jul 16, 2026
eb8f37c
Resolve APS test scanning alerts
ChristianPavilonis Jul 16, 2026
43ac72a
Merge APS scan alert fixes
ChristianPavilonis Jul 16, 2026
e87b6e6
Merge branch 'main' into streaming-pipeline-async-core
prk-Jr Jul 16, 2026
e80cbb7
Flush streaming body encoder per chunk so output decodes before finish
prk-Jr Jul 16, 2026
5cdaf3d
Stream the ready prefix before collecting the held auction
prk-Jr Jul 16, 2026
43499f8
Correct Content-Length framing for bodiless 204/205 responses
prk-Jr Jul 16, 2026
0976cfb
Emit abandoned-auction telemetry for bodiless dispatched responses
prk-Jr Jul 16, 2026
5d6fc21
Apply rustfmt after the edition-2024 merge
prk-Jr Jul 16, 2026
4828512
Harden Fastly backend naming and transport-timeout quantization
prk-Jr Jul 16, 2026
baddbdc
Merge remote-tracking branch 'origin/main' into quantize-auction-tran…
prk-Jr Jul 16, 2026
1569a06
Log APS responses at info level
ChristianPavilonis Jul 16, 2026
77f3521
Update predict_name tests for digest-based backend names
prk-Jr Jul 16, 2026
1923031
logs and whatnot
ChristianPavilonis Jul 16, 2026
954b799
Merge pull request #865 from IABTechLab/quantize-auction-transport-ti…
ChristianPavilonis Jul 16, 2026
5bbb8f6
Merge pull request #867 from IABTechLab/streaming-pipeline-async-core
ChristianPavilonis Jul 16, 2026
a241166
Merge branch 'main' into fix-prebid-user-id-diagnostics
aram356 Jul 16, 2026
6381ac5
Merge pull request #887 from IABTechLab/fix-prebid-user-id-diagnostics
ChristianPavilonis Jul 16, 2026
b3f5b11
Add APS inventory identity overrides
ChristianPavilonis Jul 16, 2026
397a4d6
Trace winning auction bids to rendered creatives on the page
prk-Jr Jul 16, 2026
cdd4d07
Merge APS OpenRTB integration
ChristianPavilonis Jul 16, 2026
9f898e2
Render APS bids from the trustedServer Prebid adapter
ChristianPavilonis Jul 16, 2026
96fec88
Merge APS Prebid adapter rendering fix into July RC
ChristianPavilonis Jul 16, 2026
58706b4
Merge pull request #922 from IABTechLab/trace-auction-winners-to-crea…
ChristianPavilonis Jul 17, 2026
b96a7ec
Fix release candidate CI
ChristianPavilonis Jul 17, 2026
7f448bc
Add APS auction debug metadata
ChristianPavilonis Jul 17, 2026
5c40c6e
Merge branch 'issue-764-aps-openrtb' into rc/july
ChristianPavilonis Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,16 @@ jobs:
with:
node-version: ${{ steps.shared-setup.outputs.node-version }}
cache: npm
cache-dependency-path: crates/trusted-server-integration-tests/browser/package-lock.json
cache-dependency-path: |
crates/trusted-server-integration-tests/browser/package-lock.json
crates/trusted-server-js/lib/package-lock.json

- name: Build TSJS browser fixtures
working-directory: crates/trusted-server-js/lib
run: |
npm ci
npm run build
npm run build:prebid-external

- name: Install Playwright
working-directory: crates/trusted-server-integration-tests/browser
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Breaking** — Replaced the legacy APS contextual integration with APS OpenRTB at `/e/pb/bid`. APS configuration now uses canonical `account_id` (`pub_id` remains a compatibility alias), no longer requires APS-specific slot IDs, and defaults script creative eligibility off. Operators must update the endpoint, disable native APS demand for Trusted Server cohorts, and prepare GAM/Universal Creative targeting for `hb_bidder=aps` before rollout.
- **Breaking** — `bid_param_zone_overrides` inner values must now be JSON objects; previously non-object or empty values (`"header" = "x"`, `"header" = {}`) were accepted and silently produced a dead rule at runtime. They now fail at startup with a configuration error. Operators upgrading should audit their `bid_param_zone_overrides` config for non-object zone entries.
- **Breaking** — Sourcepoint browser module inclusion now requires explicit `[integrations.sourcepoint].enabled = true`; operators relying on the previous unconditional Sourcepoint module should enable the integration before upgrading.
- Added optional APS `inventory_domain` and `inventory_page_origin` overrides for deployments whose edge hostname differs from the APS-authorized inventory identity.
- Preserved APS renderer capabilities through the client-side `trustedServer` Prebid adapter, allowing its generated `hb_adid` to render through GAM and Prebid Universal Creative instead of producing an empty creative.

### Security

- Validate synthetic ID format on inbound values from the `x-synthetic-id` header and `synthetic_id` cookie; values that do not match the expected format (`64-hex-hmac.6-alphanumeric-suffix`) are discarded and a fresh ID is generated rather than forwarded to response headers, cookies, or third-party APIs

### Added

- Added opt-in APS HTTP debug metadata for controlled test sites, exposing the direct request and response under `/auction` provider metadata using the Prebid Server `debug.httpcalls` shape.
- Added the default-true `[auction].rewrite_creatives` option. Setting it to `false` preserves mandatory `/auction` creative sanitization while skipping first-party resource/click URL rewriting and creative TSJS injection.
- Added typed APS renderer transport for direct auctions and GAM/Prebid Universal Creative, using a minimized one-bid envelope, a fragment-bound nonce, and an opaque sandboxed renderer endpoint.
- Added Osano consent mirror integration docs and public enablement guidance.
- Implemented basic authentication for configurable endpoint paths (#73)
- Added integrations guide with example `testlight` integration
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ version = "0.1.0"

[workspace.dependencies]
anyhow = "1"
async-stream = "0.3"
async-trait = "0.1"
axum = "0.8"
base64 = "0.22"
Expand Down
5 changes: 3 additions & 2 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ INFO: Running 2 bidders in parallel
INFO: Requesting bids from: prebid
INFO: Prebid returned 2 bids (time: 120ms)
INFO: Requesting bids from: aps
INFO: APS (MOCK): returning 2 bids in 80ms
INFO: GAM mediation: slot 'header-banner' won by 'amazon-aps' at $2.50 CPM
INFO: APS requests bids for 2 impressions
INFO: APS returns 2 accepted bids in 80ms
INFO: GAM mediation: slot 'header-banner' won by 'aps' at $2.50 CPM
```

### Verify Provider Registration
Expand Down
12 changes: 11 additions & 1 deletion crates/trusted-server-adapter-axum/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,19 @@ impl PlatformBackend for AxumPlatformBackend {
let port = spec
.port
.unwrap_or(if spec.scheme == "https" { 443 } else { 80 });
// Keep two providers that share an origin on distinct names so auction
// response correlation cannot cross providers.
let discriminator = spec
.discriminator
.as_deref()
.map(|d| format!("_p_{}", normalize_env_segment(d)))
.unwrap_or_default();
Ok(format!(
"{}_{}_{}",
"{}_{}_{}{}",
normalize_env_segment(&spec.scheme),
normalize_env_segment(&spec.host),
port,
discriminator,
))
}

Expand Down Expand Up @@ -644,6 +652,7 @@ mod tests {
first_byte_timeout: Duration::from_secs(15),
between_bytes_timeout: Duration::from_secs(15),
host_header_override: None,
discriminator: None,
};
let name1 = backend.predict_name(&spec).expect("should return a name");
let name2 = backend
Expand All @@ -664,6 +673,7 @@ mod tests {
first_byte_timeout: Duration::from_secs(15),
between_bytes_timeout: Duration::from_secs(15),
host_header_override: None,
discriminator: None,
};
assert_eq!(
backend.predict_name(&spec).expect("should return name"),
Expand Down
9 changes: 8 additions & 1 deletion crates/trusted-server-adapter-cloudflare/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,15 @@ impl PlatformBackend for NoopBackend {
} else {
"_nocert"
};
// Keep two providers that share an origin on distinct names so auction
// response correlation cannot cross providers.
let discriminator = spec
.discriminator
.as_deref()
.map(|d| format!("_p_{d}"))
.unwrap_or_default();
Ok(format!(
"{}_{}_{}_{timeout_ms}ms{cert_suffix}",
"{}_{}_{}_{timeout_ms}ms{cert_suffix}{discriminator}",
spec.scheme, spec.host, port
))
}
Expand Down
1 change: 1 addition & 0 deletions crates/trusted-server-adapter-fastly/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ log = { workspace = true }
log-fastly = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha2 = { workspace = true }
trusted-server-core = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
Expand Down
68 changes: 50 additions & 18 deletions crates/trusted-server-adapter-fastly/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@
//! run on these responses. Legacy ran EC finalization on its own auth
//! challenges. Like the 401 geo-skip, this is privacy-conservative: no EC
//! cookies are issued to unauthenticated callers.
//! - **Publisher responses** are buffered (bounded by
//! `publisher.max_buffered_body_bytes`) instead of streamed to the client.
//! Asset responses are streamed straight to the client (see
//! [`dispatch_asset_fallback`]), matching legacy.
//! - **Publisher responses** keep Fastly origin bodies streaming through the
//! `EdgeZero` response body when the body is processable or pass-through.
//! Adapters without streaming-body support still use the bounded buffered
//! finalizer.
//! - **Router-level 405s** (unregistered verbs) skip EC finalization along
//! with the middleware chain; the entry point still adds TS headers.
//!
Expand Down Expand Up @@ -116,8 +116,8 @@ use trusted_server_core::proxy::{
handle_first_party_proxy, handle_first_party_proxy_rebuild, handle_first_party_proxy_sign,
};
use trusted_server_core::publisher::{
AuctionDispatch, buffer_publisher_response_async, handle_page_bids, handle_publisher_request,
handle_tsjs_dynamic, page_bids_preflight_denied,
AuctionDispatch, handle_page_bids, handle_publisher_request, handle_tsjs_dynamic,
page_bids_preflight_denied, publisher_response_into_streaming_response,
};
use trusted_server_core::request_signing::{
handle_deactivate_key, handle_rotate_key, handle_trusted_server_discovery,
Expand Down Expand Up @@ -721,10 +721,9 @@ async fn dispatch_fallback(
let result = if uses_dynamic_tsjs_fallback(&method, &path) {
handle_tsjs_dynamic(&req, &state.registry)
} else if state.registry.has_route(&method, &path) {
// Integration-proxy responses are not bounded by publisher.max_buffered_body_bytes.
// Only the handle_publisher_request branch below routes through
// buffer_publisher_response_async. Integration responses are small in practice
// and the EdgeZero flag is off by default; extend the cap here if that changes.
// Integration-proxy responses are not bounded by
// publisher.max_buffered_body_bytes. Publisher fallback below uses the
// publisher-specific streaming finalizer instead.
state
.registry
.handle_proxy(ProxyDispatchInput {
Expand Down Expand Up @@ -773,9 +772,8 @@ async fn dispatch_fallback(
match runtime_services_for_consent_route(&state.settings, services) {
Ok(publisher_services) => {
// Run the server-side auction with the configured creative-
// opportunity slots and collect the dispatched bids in the
// buffered finalize (`buffer_publisher_response_async`), matching
// the legacy streaming path. `handle_publisher_request` matches the
// opportunity slots and collect dispatched bids from the lazy
// publisher body stream. `handle_publisher_request` matches the
// slots against the request path. The partner registry plus the
// EC identity-graph KV (`ec.kv_graph`) enrich the bid request with
// server-side EIDs, same as the legacy auction.
Expand All @@ -798,13 +796,13 @@ async fn dispatch_fallback(
.await
{
Ok(pub_response) => {
buffer_publisher_response_async(
publisher_response_into_streaming_response(
pub_response,
&method,
&state.settings,
&state.registry,
&state.orchestrator,
&publisher_services,
Arc::clone(&state.settings),
state.registry.as_ref(),
Arc::clone(&state.orchestrator),
publisher_services.clone(),
)
.await
}
Expand Down Expand Up @@ -2158,6 +2156,10 @@ mod tests {

#[async_trait::async_trait(?Send)]
impl PlatformHttpClient for StreamingHttpClient {
fn supports_streaming_responses(&self) -> bool {
true
}

async fn send(
&self,
request: PlatformHttpRequest,
Expand Down Expand Up @@ -2268,6 +2270,36 @@ mod tests {
);
}

#[test]
fn dispatch_fallback_streams_publisher_body_without_buffering() {
// Regression guard for the publisher streaming cutover (#849): a
// successful publisher origin fetch must hand `edgezero_main` a lazy
// streaming body (`Body::Stream`) so headers commit at origin first
// byte, rather than draining the processed page into a buffered
// `Body::Once`. Core tests cover the rewrite pipeline itself; this
// guards the adapter wiring that could silently re-buffer.
let settings = test_settings();
let state = build_state_from_settings(settings).expect("should build state");
let services = streaming_runtime_services();
let req = empty_request(Method::GET, "/article");

let response = block_on(super::dispatch_fallback(&state, &services, req));

assert_eq!(
response.status(),
StatusCode::OK,
"publisher proxy should succeed against the streaming origin stub"
);
assert!(
matches!(response.body(), Body::Stream(_)),
"EdgeZero publisher dispatch must attach the lazy streaming body, not buffer it"
);
assert!(
!response.headers().contains_key(header::CONTENT_LENGTH),
"processed streaming publisher responses must not carry a stale Content-Length"
);
}

#[test]
fn dispatch_runs_request_filter_and_threads_response_effects() {
// Regression guard for the EdgeZero request-filter bypass: the publisher
Expand Down
Loading