diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ec85b96a..4973afe4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc49c80..fd9f0566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,11 @@ 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. APS renderer winners now preserve the upstream bid `id`, omit `crid` when APS omits it, and carry `ext.trusted_server.renderer` instead of `adm`; external `/auction` consumers must support this response shape. - **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 @@ -18,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 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 diff --git a/TESTING.md b/TESTING.md index e1ac1c3a..e5ccba4c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -46,8 +46,8 @@ curl -X POST http://localhost:7676/auction \ **With Orchestrator Enabled** (`auction.enabled = true`): - Logs showing: `"Using auction orchestrator"` -- Parallel execution of APS (mocked) and Prebid (real) -- GAM mediation (mocked) selecting winning bids +- Parallel execution of APS OpenRTB and Prebid Server +- Optional mock-adserver mediation selecting winning bids - Final response with winning creatives **With Orchestrator Disabled** (`auction.enabled = false`): @@ -66,11 +66,13 @@ providers = ["prebid", "aps"] mediator = "adserver_mock" # If set: mediation, if omitted: highest bid wins timeout_ms = 2000 -# Mock provider configs +# APS OpenRTB provider. The built-in production endpoint is used when +# endpoint is omitted; use only an account authorized for test traffic. [integrations.aps] enabled = true -mock = true -mock_price = 2.50 +account_id = "example-account" +timeout_ms = 800 +debug = false [integrations.adserver_mock] enabled = true @@ -90,10 +92,10 @@ mediator = "adserver_mock" # Mediator configured = parallel mediation strategy ``` **Expected Flow:** -1. Prebid queries real SSPs -2. APS returns mock bids ($2.50 CPM) -3. AdServer Mock mediates between all bids -4. Winning creative returned +1. Prebid queries its configured bidders through Prebid Server +2. APS sends an OpenRTB request for eligible banner impressions +3. AdServer Mock mediates the provider responses +4. The winning creative or typed APS renderer is returned ### Scenario 2: Parallel Only (No Mediation) **Config:** @@ -131,8 +133,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 @@ -156,18 +159,19 @@ INFO: Registering auction provider: adserver_mock ## Next Steps -1. **Test with real Prebid Server** - Verify Prebid bids work correctly -2. **Implement real APS** - Replace mock with actual Amazon TAM API calls -3. **Implement real GAM** - Add Google Ad Manager API integration -4. **Add metrics** - Track bid rates, win rates, latency per provider +1. **Verify Prebid Server demand** - Confirm configured bidders return expected test bids +2. **Verify APS eligibility** - Confirm the test account, inventory identity, and `/e/pb/bid` endpoint are authorized +3. **Exercise renderer security** - Run the APS browser integration suite for iframe and script creatives +4. **Add metrics** - Track bid rates, win rates, latency, and aggregate drop reasons per provider -## Mock Provider Behavior +## Provider Behavior ### APS (Amazon) -- Returns bids for all slots -- Default mock price: $2.50 CPM -- Always returns 2 bids -- Response time: ~80ms (simulated) +- Sends real OpenRTB requests for eligible banner slots +- Safely drops malformed, unsupported, or unrenderable bids and reports aggregate reasons +- Reduces multiple APS candidates to one winner per impression +- Returns typed renderer descriptors rather than exposing `adm` outside the sandbox +- Automated tests intercept upstream traffic and use fictional response fixtures ### AdServer Mock - Acts as mediator by calling mocktioneer's mediation endpoint diff --git a/crates/trusted-server-core/src/auction/README.md b/crates/trusted-server-core/src/auction/README.md index dcc9e150..44ba9ce1 100644 --- a/crates/trusted-server-core/src/auction/README.md +++ b/crates/trusted-server-core/src/auction/README.md @@ -117,7 +117,7 @@ When a request arrives at the `/auction` endpoint, it goes through the following ▼ ┌──────────────────────────────────────────────────────────────────────┐ │ 9. Each Provider Processes Request │ -│ - Transform AuctionRequest → Provider format (e.g., APS TAM) │ +│ - Transform AuctionRequest → Provider OpenRTB request │ │ - Send HTTP request to provider endpoint │ │ - Parse provider response │ │ - Transform → AuctionResponse with Bid[] │ @@ -188,30 +188,21 @@ AdSlot { #### 3. Provider Execution Each registered provider (APS, Prebid, etc.) receives the `AuctionRequest` and: -- Transforms it to their specific format (e.g., APS TAM, OpenRTB) +- Transforms it to the provider's OpenRTB request format - Makes HTTP request to their endpoint - Parses the response - Returns `AuctionResponse` with `Bid[]` For example, APS provider: ```rust -// Transform AuctionRequest → ApsBidRequest -let aps_request = ApsBidRequest { - pub_id: "5128", - slots: vec![ - ApsSlot { - slot_id: "header-banner", - sizes: vec![[728, 90], [970, 250]], - slot_name: Some("header-banner"), - } - ], - page_url: Some("https://example.com"), - ua: Some("Mozilla/5.0..."), - timeout: Some(800), -}; - -// HTTP POST to http://localhost:6767/e/dtb/bid -// Parse response → AuctionResponse +// Transform AuctionRequest → APS OpenRTB request +// - ext.account = configured account_id +// - ext.sdk = { source: "prebid", version: "2.2.0" } +// - banner slots become secure impressions with matching formats/floors +// - existing consent, identity, device, and geo privacy gates apply + +// HTTP POST to https://aps.example.com/e/pb/bid +// Parse decoded-price response → AuctionResponse with a typed renderer ``` #### 4. Response Assembly @@ -222,17 +213,29 @@ The orchestrator collects all bids and creates an OpenRTB response: "id": "auction-response", "seatbid": [ { - "seat": "amazon-aps", + "seat": "aps", "bid": [ { - "id": "amazon-aps-header-banner", + "id": "fictional-selected-bid-id", "impid": "header-banner", "price": 2.5, - "adm": "