diff --git a/FLEDGE.md b/FLEDGE.md index b7136f1fd..320362838 100644 --- a/FLEDGE.md +++ b/FLEDGE.md @@ -28,10 +28,11 @@ See [the Protected Audience API specification](https://wicg.github.io/turtledove - [2.5.2 Using Response Headers](#252-using-response-headers) - [3. Buyers Provide Ads and Bidding Functions (BYOS for now)](#3-buyers-provide-ads-and-bidding-functions-byos-for-now) - [3.1 Fetching Real-Time Data from a Trusted Server](#31-fetching-real-time-data-from-a-trusted-server) + - [3.1.1 Cross-Origin Trusted Server Signals](#311-cross-origin-trusted-server-signals) - [3.2 On-Device Bidding](#32-on-device-bidding) - [3.3 Metadata with the Ad Bid](#33-metadata-with-the-ad-bid) - [3.4 Ads Composed of Multiple Pieces](#34-ads-composed-of-multiple-pieces) - - [3.4.1 Flexible Component Ad Selection Considering k-Anonymity](#341-target-num-component-ads) + - [3.4.1 Flexible Component Ad Selection Considering k-Anonymity](#341-flexible-component-ad-selection-considering-k-anonymity) - [3.5 Filtering and Prioritizing Interest Groups](#35-filtering-and-prioritizing-interest-groups) - [3.6 Currency Checking](#36-currency-checking) - [4. Browsers Render the Winning Ad](#4-browsers-render-the-winning-ad) @@ -134,14 +135,11 @@ const myGroup = { 'trustedBiddingSignalsSlotSizeMode' : 'slot-size', 'maxTrustedBiddingSignalsURLLength' : 10000, 'userBiddingSignals': {...}, - 'ads': [{renderURL: shoesAd1, sizeGroup: 'group1', ...}, - {renderURL: shoesAd2, sizeGroup: 'group2', ...}, - {renderURL: shoesAd3, sizeGroup: 'size3', ...}], - 'adComponents': [{renderURL: runningShoes1, sizeGroup: 'group2', ...}, - {renderURL: runningShoes2, sizeGroup: 'group2', ...}, - {renderURL: gymShoes, sizeGroup; 'group2', ...}, - {renderURL: gymTrainers1, sizeGroup: 'size4', ...}, - {renderURL: gymTrainers2, sizeGroup: 'size4', ...}], + 'ads': [{renderUrl: shoesAd1, sizeGroup: 'group1', ...}, + {renderUrl: shoesAd2, sizeGroup: 'group2', ...}], + 'adComponents': [{renderUrl: runningShoes1, sizeGroup: 'group2', ...}, + {renderUrl: runningShoes2, sizeGroup: 'group2', ...}, + {renderUrl: gymShoes, sizeGroup; 'group2', ...}], 'adSizes': {'size1': {width: '100', height: '100'}, 'size2': {width: '100', height: '200'}, 'size3': {width: '75', height: '25'}, @@ -251,9 +249,9 @@ The `adComponents` field contains the various ad components (or "products") that The `adSizes` field (optionally) contains a dictionary of named ad sizes. Each size has the format `{width: widthVal, height: heightVal}`, where the values can have either pixel units (e.g. `100` or `'100px'`) or screen dimension coordinates (e.g. `100sw` or `100sh`). For example, the size `{width: '100sw', height: 50}` describes an ad that is the width of the screen and 50 pixels tall. The size `{width: '100sw', height: '200sw'}` describes an ad that is the width of the screen and has a 1:2 aspect ratio. Sizes with screen dimension coordinates are primarily intended for screen-width ads on mobile devices, and may be restricted in certain contexts (to be determined) for privacy reasons. -The `sizeGroups` field (optionally) contains a dictionary of named lists of ad sizes. Each ad declared above must specify a size group, saying which sizes it might be loaded at. Each named ad size is also considered a size group, so you don't need to manually define singleton size groups; for example see the `sizeGroup: 'size3'` code above. +The `sizeGroups` field (optionally) contains a dictionary of named lists of ad sizes. Each ad declared above must specify a size group, saying which sizes it might be loaded at. Each named ad size could in the future also be considered a size group, so you don't need to manually define singleton size groups. -At some point in the future - no earlier than Q1 2025 - when the sizes are declared, the URL-size pairings will be used to prefetch k-anonymity checks to limit the configurations that can win an auction, please see [this doc](https://developer.chrome.com/docs/privacy-sandbox/protected-audience-api/feature-status/#k-anonymity). In the present implementation, only the URL is used for k-anonymity checks, not the size. When an ad with a particular size wins the auction (including in the current implementation), the size will be substituted into any macros in the URL (through `{%AD_WIDTH%}` and `{%AD_HEIGHT%}`, or `${AD_WIDTH}` and `${AD_HEIGHT}`), and once loaded into a fenced frame, the size will be used by the browser to freeze the fenced frame's inner dimensions. We therefore recommend using ad size declarations, but they are not required at this time. +At some point in the future - no earlier than Q1 2025 - when the sizes are declared, the URL-size pairings will be used to prefetch k-anonymity checks to limit the configurations that can win an auction, please see [this doc](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity). In the present implementation, only the URL is used for k-anonymity checks, not the size. When an ad with a particular size wins the auction (including in the current implementation), the size will be substituted into any macros in the URL (through `{%AD_WIDTH%}` and `{%AD_HEIGHT%}`, or `${AD_WIDTH}` and `${AD_HEIGHT}`), and once loaded into a fenced frame, the size will be used by the browser to freeze the fenced frame's inner dimensions. We therefore recommend using ad size declarations, but they are not required at this time. The `auctionServerRequestFlags` field is optional and is only used for auctions [run on an auction server](https://github.com/WICG/turtledove/blob/main/FLEDGE_browser_bidding_and_auction_API.md). This field contains a list of enumerated values that change what data is sent in the auction blob: @@ -277,7 +275,7 @@ have the same origin, response header, fragment, or query requirements. (You can find detailed error conditions for all fields in step 6 of [the `joinAdInterestGroup()` section of the spec](https://wicg.github.io/turtledove/#dom-navigator-joinadinterestgroup)). -The browser will only render an ad if the same rendering URL is being shown to a sufficiently large number of people (e.g. at least 50 people would have seen the ad, if it were allowed to show). While in the [Outcome-Based TURTLEDOVE](https://github.com/WICG/turtledove/blob/master/OUTCOME_BASED.md) proposal this threshold applied only to the rendered creative, Protected Audience has the additional requirement that the tuple of the interest group owner, bidding script URL, and rendered creative (URL, and [no earlier than Q1 2025](https://developer.chrome.com/docs/privacy-sandbox/protected-audience-api/feature-status/#k-anonymity) the size if specified by `generateBid`) must be k-anonymous for an ad to be shown (this is necessary to ensure the current event-level reporting for interest group win reporting is sufficiently private). For interest groups that have component ads, all of the component ads must also separately meet this threshold for the ad to be shown. Since a single interest group can carry multiple possible ads that it might show, the group will have an opportunity to re-bid another one of its ads to act as a "fallback ad" any time its most-preferred choice is below threshold. This means that a small, specialized ad that is still below the k-anonymity threshold could still choose to participate in auctions, and its interest group has a way to fall back to a more generic ad until the more specialized one has a large enough audience. +The browser will only render an ad if the same rendering URL is being shown to a sufficiently large number of people (e.g. at least 50 people would have seen the ad, if it were allowed to show). While in the [Outcome-Based TURTLEDOVE](https://github.com/WICG/turtledove/blob/master/OUTCOME_BASED.md) proposal this threshold applied only to the rendered creative, Protected Audience has the additional requirement that the tuple of the interest group owner, bidding script URL, and rendered creative (URL, and [no earlier than Q1 2025](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity) the size if specified by `generateBid`) must be k-anonymous for an ad to be shown (this is necessary to ensure the current event-level reporting for interest group win reporting is sufficiently private). For interest groups that have component ads, all of the component ads must also separately meet this threshold for the ad to be shown. Since a single interest group can carry multiple possible ads that it might show, the group will have an opportunity to re-bid another one of its ads to act as a "fallback ad" any time its most-preferred choice is below threshold. This means that a small, specialized ad that is still below the k-anonymity threshold could still choose to participate in auctions, and its interest group has a way to fall back to a more generic ad until the more specialized one has a large enough audience. Similar to [the key-value server](#31-fetching-real-time-data-from-a-trusted-server), the server keeping track of which ad URLs are k-anonymous is publicly queryable. The ad URLs are not supposed to target small groups of users (less than k users). For these reasons, and also in the interest of passing the k-anonymity check, the ad URLs should not contain PII, or sensitive information. @@ -292,7 +290,7 @@ If any of these per-owner limits are exceeded, the interest group(s) that would #### 1.3 Permission Delegation -When a frame navigated to one domain calls joinAdInterestGroup(), leaveAdInterestGroup(), or clearOriginJoinedAdInterestGroups() for an interest group with a different owner, the browser will fetch the URL https://owner.domain/.well-known/interest-group/permissions/?origin=frame.origin, where `owner.domain` is domain that owns the interest group and `frame.origin` is the origin of the frame. The fetch uses the `omit` [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode), using the [Network Partition Key](https://fetch.spec.whatwg.org/#network-partition-keys) of the frame that invoked the method. To avoid leaking cross-origin data through the returned Promise unexpectedly, the fetch uses the `cors` [mode](https://fetch.spec.whatwg.org/#concept-request-mode). The fetched response should have a JSON MIME type and be of the format: +When a frame navigated to one domain calls joinAdInterestGroup(), leaveAdInterestGroup(), or clearOriginJoinedAdInterestGroups() for an interest group with a different owner, the browser will fetch the URL https://owner.domain/.well-known/interest-group/permissions/?origin=frame.origin, where `owner.domain` is domain that owns the interest group and `frame.origin` is the origin of the frame. The fetch uses the `omit` [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode), using the [Network Partition Key](https://fetch.spec.whatwg.org/#network-partition-keys) of the frame that invoked the method. To avoid leaking cross-origin data through the returned Promise unexpectedly, the fetch uses the `cors` [mode](https://fetch.spec.whatwg.org/#concept-request-mode). The fetched response should have a JSON MIME type, have a `Access-Control-Allow-Origin` that allows it to load from the calling origin, and be of the format: ``` { "joinAdInterestGroup": true/false, @@ -346,9 +344,9 @@ const myAuctionConfig = { 'maxTrustedScoringSignalsURLLength': 10000, 'interestGroupBuyers': ['https://www.example-dsp.com', 'https://buyer2.com', ...], 'auctionSignals': {...}, - 'requestedSize': {width: '100', height: '200'}, - 'allSlotsRequestedSizes': [{width: '100', height: '200'}, {width: '200', height: '300'}, ...], - 'directFromSellerSignals': 'https://www.example-ssp.com/...', + 'requestedSize': {'width': '100sw', 'height': '200px'}, + 'allSlotsRequestedSizes': [{'width': '100sw', 'height': '200px'}, {'width': '200px', 'height': '300px'}, ...], + 'directFromSellerSignalsHeaderAdSlot': 'adSlot/1', 'sellerSignals': {...}, 'sellerTimeout': 100, 'sellerExperimentGroupId': 12345, @@ -383,8 +381,8 @@ const myAuctionConfig = { 'perBuyerMultiBidLimits': {'https://example.com': 10, '*': 5}, 'sellerCurrency:' : 'CAD', 'reportingTimeout' : 200, - 'deprecatedRenderURLReplacements':{{'${SELLER}':'exampleSSP'}, - {'%%SELLER_ALT%%':'exampleSSP'}}, + 'deprecatedRenderURLReplacements':{'${SELLER}':'exampleSSP', + '%%SELLER_ALT%%':'exampleSSP'}, 'componentAuctions': [ {'seller': 'https://www.some-other-ssp.com', 'decisionLogicURL': ..., @@ -411,7 +409,7 @@ The optional `requestedSize` field recommends a frame size for the auction, whic `allSlotsRequestedSizes` may optionally be used to specify the size of all ad slots on the page, to be passed to each interest group's `trustedBuyerSignalsURL`, for interest groups that request it. All sizes in the list must be distinct. -The optional `directFromSellerSignals` field can also be used to pass signals to the auction, similar to `sellerSignals`, `perBuyerSignals`, and `auctionSignals`. The difference is that `directFromSellerSignals` are trusted to come from the seller because the content loads from a [subresource bundle](https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md) loaded from a seller's origin, ensuring the authenticity and integrity of the signals. For more details, see [2.5 directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals). +The optional `directFromSellerSignalsHeaderAdSlot` field can also be used to pass signals to the auction, similar to `sellerSignals`, `perBuyerSignals`, and `auctionSignals`. The difference is that signals from `directFromSellerSignalsHeaderAdSlot` are trusted to come from the seller because the content loads from response headers from an https fetch request made to the seller's origin, ensuring the authenticity and integrity of the signals. For more details, see [2.5 directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals) and [2.5.2 directFromSellerSignalsHeaderAdSlot](#252-using-response-headers). In some cases, multiple SSPs may want to participate in an auction, with the winners of separate auctions being passed up to another auction, run by another SSP. To facilitate these "component auctions", `componentAuctions` can optionally contain additional auction configurations for each seller's "component auction". The winning bid of each of these "component auctions" will be passed to the "top-level" auction. How bids are scored in this case is further described in [2.4 Scoring Bids in Component Auctions](#24-scoring-bids-in-component-auctions). The `AuctionConfig` of component auctions may not have their own `componentAuctions`. When `componentAuctions` is non-empty, `interestGroupBuyers` must be empty. That is, for any particular Protected Audience auction, either there is a single seller and no component auctions, or else all bids come from component auctions and the top-level auction can only choose among the component auctions' winners. @@ -495,7 +493,13 @@ bid a positive score. #### 2.2 Auction Participants -Each interest group the browser has joined and whose owner is in the list of `interestGroupBuyers` will have an opportunity to bid in the auction. See the "Buyers Provide Ads and Bidding Functions" section, below, for how interest groups bid. +Each interest group the browser has joined with: +* `owner` in the list of `interestGroupBuyers`. +* Non null `biddingLogicURL`. The fetch of `biddingLogicURL` and `biddingWasmHelperURL` (if specified) must succeed to bid. +* At least one registered creative in the `ads` element. This implies that [negative interest groups](#621-negative-interest-groups) cannot bid as they cannot contain `ads`. +* Priority, as stated by `priority` or calculated via `priorityVector`, is greater than or equal to 0. + +will have an opportunity to bid in the auction. See the "Buyers Provide Ads and Bidding Functions" section, below, for how interest groups bid. #### 2.3 Scoring Bids @@ -505,7 +509,7 @@ Once the bids are known, the seller runs code inside an _auction worklet_. With ``` scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals, - directFromSellerSignals) { + directFromSellerSignals, crossOriginTrustedSignals) { ... return {desirability: desirabilityScoreForThisAd, incomingBidInSellerCurrency: @@ -520,13 +524,13 @@ The function gets called once for each candidate ad in the auction. The argumen * adMetadata: Arbitrary metadata provided by the buyer. * bid: A numerical bid value. * auctionConfig: The auction configuration object passed to `navigator.runAdAuction()`. -* trustedScoringSignals: A value retrieved from a real-time trusted server chosen by the seller and reflecting the seller's opinion of this particular creative, as further described in [3.1 Fetching Real-Time Data from a Trusted Server](#31-fetching-real-time-data-from-a-trusted-server) below. (In the case of [ads composed of multiple pieces](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#34-ads-composed-of-multiple-pieces) this should instead be some collection of values, structure TBD.) +* trustedScoringSignals: A value retrieved from a real-time trusted server chosen by the seller and reflecting the seller's opinion of this particular creative, as further described in [3.1 Fetching Real-Time Data from a Trusted Server](#31-fetching-real-time-data-from-a-trusted-server) below. This is used when the server is same-origin to the seller; crossOriginTrustedSignals is used otherwise. * browserSignals: An object constructed by the browser, containing information that the browser knows and which the seller's auction script might want to verify: ``` { 'topWindowHostname': 'www.example-publisher.com', 'interestGroupOwner': 'https://www.example-dsp.com', 'renderURL': 'https://cdn.com/render_url_of_bid', - 'renderSize': {width: 100, height: 200}, /* if specified in the bid */ + 'renderSize': {'width': '100sw', 'height': '200px'}, /* if specified in the bid */ 'adComponents': ['https://cdn.com/ad_component_of_bid', 'https://cdn.com/next_ad_component_of_bid', ...], @@ -538,6 +542,11 @@ The function gets called once for each candidate ad in the auction. The argumen * directFromSellerSignals is an object that may contain the following fields: * sellerSignals: Like auctionConfig.sellerSignals, but passed via the [directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals) mechanism. These are the signals whose subresource URL ends in `?sellerSignals`. * auctionSignals: Like auctionConfig.auctionSignals, but passed via the [directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals) mechanism. These are the signals whose subresource URL ends in `?auctionSignals`. +* crossOriginTrustedSignals: like `trustedScoringSignals`, but used when the server is cross-origin + to the seller script. The value is an object that has as a key the trusted server's origin, e.g. + `"https://example.org"`, and as value an object in format `trustedScoringSignals` uses. See + [3.1.1 Cross-Origin Trusted Server Signals](#311-cross-origin-trusted-server-signals) for more + details. The output of `scoreAd()` is an object with the following fields: * desirability: Number indicating how desirable this ad is. Any value that is zero or negative indicates that the ad cannot win the auction. (This could be used, for example, to eliminate any interest-group-targeted ad that would not beat a contextually-targeted candidate.) The winner of the auction is the ad object which was given the highest score. @@ -646,7 +655,17 @@ Ad-Auction-Signals=[{ ] ``` -When invoking `navigator.runAdAuction()`, `directFromSellerSignalsHeaderAdSlot` is used to lookup the signals intended for that auction. `directFromSellerSignalsHeaderAdSlot` is a string that should match the `adSlot` value contained in some `Ad-Auction-Signals` response served from the origin of that auction's seller. Note that for multi-seller or component auctions, each component auction / top-level can specify its own `directFromSellerSignalsHeaderAdSlot`, and the response should be served from that component / top-level auction's seller's origin. Different sellers may safely use the same `adSlot` names without conflict. If `directFromSellerSignalsHeaderAdSlot` matches multiple `adSlot`s from header responses, signals from the most recently-received response will be sent to worklet functions. Furthermore, if a response is received for an adSlot whose name matches that for existing captured signals, memory from the old signals will be released and the new signals will be stored. A response that specifices the same adSlot name in multiple dictionaries is invalid. +When invoking `navigator.runAdAuction()`, `directFromSellerSignalsHeaderAdSlot` is used to lookup the signals intended for that auction: + +``` +navigator.runAdAuction({ + ... + directFromSellerSignalsHeaderAdSlot: "adSlot/1", + ... +}); +``` + +`directFromSellerSignalsHeaderAdSlot` is a string that should match the `adSlot` value contained in some `Ad-Auction-Signals` response served from the origin of that auction's seller. Note that for multi-seller or component auctions, each component auction / top-level can specify its own `directFromSellerSignalsHeaderAdSlot`, and the response should be served from that component / top-level auction's seller's origin. Different sellers may safely use the same `adSlot` names without conflict. If `directFromSellerSignalsHeaderAdSlot` matches multiple `adSlot`s from header responses, signals from the most recently-received response will be sent to worklet functions. Furthermore, if a response is received for an adSlot whose name matches that for existing captured signals, memory from the old signals will be released and the new signals will be stored. A response that specifices the same adSlot name in multiple dictionaries is invalid. The JSON will be parsed by the browser, and passed via the same `directFromSellerSignals` worklet functions parameter as in [the subresource bundle](#251-using-subresource-bundles) version of DirectFromSellerSignals, with `sellerSignals` only being delivered to the seller, `perBuyerSignals` only being delivered to the buyer for each buyer origin key, and `auctionSignals` being delivered to all parties. Since the top-level JSON value is an array, multiple `adSlot` responses may be set for a given `Ad-Auction-Signals` header. In the dictionary with the `adSlot`, the `sellerSignals`, `auctionSignals`, and `perBuyerSignals` fields are optional -- they will be passed as null if not specified. @@ -755,6 +774,75 @@ For detailed specification and explainers of the trusted key-value server, see a - [FLEDGE Key/Value Server APIs Explainer](https://github.com/WICG/turtledove/blob/master/FLEDGE_Key_Value_Server_API.md) - [FLEDGE Key/Value Server Trust Model Explainer](https://github.com/privacysandbox/fledge-docs/blob/main/key_value_service_trust_model.md) +##### 3.1.1 Cross-Origin Trusted Server Signals + +If the key-value server is on a different origin than the corresponding script, additional steps are +needed to ensure that sensitive information is not inappropriately leaked to, or is manipulated by, +a third party. + +In the case of bidder signals, the following changes occur: +1. The fetch is a cross-origin [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) fetch + with `Origin:` set to the buyer script's origin. +2. The value is passed to `crossOriginTrustedSignals` parameter, not the `trustedBiddingSignals` + parameter, and there is one more level of nesting denoting the server's origin, e.g: + +``` +{ + 'https://www.kv-server.example': { + 'keys': { + 'key1': arbitrary_json, + 'key2': arbitrary_json, + ...}, + 'perInterestGroupData': { + 'name1': { + 'priorityVector': { + 'signal1': number, + 'signal2': number, + ...} + }, + ... + } + } +} +``` +3. The data version is passed in `browserSignals.crossOriginDataVersion`, not + `browserSignals.dataVersion`. + +Seller signals have additional requirements, as the `trustedScoringSignalsURL` is provided by a +context that is not required to be same-origin with the seller: +1. The seller script must provide an `Ad-Auction-Allow-Trusted-Scoring-Signals-From` response header, + a [structured headers list of strings](https://www.rfc-editor.org/rfc/rfc8941) describing origins + from which fetching trusted signals is permitted. The trusted scoring signals fetch may not begin + until this header is received, in order to avoid leaking bid information to a third party. + This means using a cross-origin trusted server for seller information may carry a peformance + penalty. +2. The fetch is a cross-origin [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) fetch + with `Origin:` set to the seller script's origin. +3. The value is passed to `crossOriginTrustedSignals` parameter, not the `trustedScoringSignals` + parameter, and there is one more level of nesting denoting the server's origin, e.g: + +``` +{ + 'https://www.kv-server.example': { + 'renderURL': {'https://cdn.com/render_url_of_bidder': arbitrary_value_from_signals}, + 'adComponentRenderURLs': { + 'https://cdn.com/ad_component_of_a_bid': arbitrary_value_from_signals, + 'https://cdn.com/another_ad_component_of_a_bid': arbitrary_value_from_signals, + ...} + } +} +``` +4. The data version is passed in `browserSignals.crossOriginDataVersion`, not + `browserSignals.dataVersion`. + +Note that older versions of Chrome did not support cross-origin trusted signals. You can query +whether support is available as: + +``` +navigator.protectedAudience && navigator.protectedAudience.queryFeatureSupport( + "permitCrossOriginTrustedSignals") +``` + #### 3.2 On-Device Bidding Once the trusted bidding signals are fetched, each interest group's bidding function will run, inside a bidding worklet associated with the interest group owner's domain. The buyer's JavaScript is loaded from the interest group's `biddingLogicURL`, which must expose a `generateBid()` function: @@ -762,7 +850,8 @@ Once the trusted bidding signals are fetched, each interest group's bidding func ``` generateBid(interestGroup, auctionSignals, perBuyerSignals, - trustedBiddingSignals, browserSignals, directFromSellerSignals) { + trustedBiddingSignals, browserSignals, directFromSellerSignals, + crossOriginTrustedSignals) { ... return {'ad': adObject, 'adCost': optionalAdCost, @@ -785,15 +874,19 @@ The arguments to `generateBid()` are: * interestGroup: The interest group object, as saved during `joinAdInterestGroup()` and perhaps updated via the `updateURL`. * `priority` and `prioritySignalsOverrides` are not included. They can be modified by `generatedBid()` calls, so could theoretically be used to create a cross-site profile of a user accessible to `generateBid()` methods, otherwise. + * `lifetimeMs` is not included. It's ambiguous what should be passed: the lifetime when the group was joined, + or the remaining lifetime. Providing the remaining lifetime would also potentially give access to more + granular timing information than the API would otherwise allow, when state is shared across interest + groups. * auctionSignals: As provided by the seller in the call to `runAdAuction()`. This is the opportunity for the seller to provide information about the page context (ad size, publisher ID, etc), the type of auction (first-price vs second-price), and so on. * perBuyerSignals: The value for _this specific buyer_ as taken from the auction config passed to `runAdAuction()`. This can include contextual signals about the page that come from the buyer's server, if the seller is an SSP which performs a real-time bidding call to buyer servers and pipes the response back, or if the publisher page contacts the buyer's server directly. If so, the buyer may wish to check a cryptographic signature of those signals inside `generateBid()` as protection against tampering. -* trustedBiddingSignals: An object whose keys are the `trustedBiddingSignalsKeys` for the interest group, and whose values are those returned in the `trustedBiddingSignals` request. +* trustedBiddingSignals: An object whose keys are the `trustedBiddingSignalsKeys` for the interest group, and whose values are those returned in the `trustedBiddingSignals` request. This used when the trusted server is same-origin with the buyer's script. * browserSignals: An object constructed by the browser, containing information that the browser knows, and which the buyer's auction script might want to use or verify. The `dataVersion` field will only be present if the `Data-Version` header was provided and had a consistent value for all of the trusted bidding signals server responses used to construct the trustedBiddingSignals. `topLevelSeller` is only present if `generateBid()` is running as part of a component auction. Additional fields can include information about both the context (e.g. the true hostname of the current page, which the seller could otherwise lie about) and about the interest group itself (e.g. times when it previously won the auction, to allow on-device frequency capping). Note that unlike for `reportWin()` the `joinCount` and `recency` in `generateBid()`'s browser signals *isn't* subject to the [noising and bucketing scheme](#521-noised-and-bucketed-signals). Furthermore, `recency` in `generateBid()`'s browser signals is specified in milliseconds, rounded to the nearest 100 milliseconds. ``` { 'topWindowHostname': 'www.example-publisher.com', 'seller': 'https://www.example-ssp.com', 'topLevelSeller': 'https://www.another-ssp.com', - 'requestedSize': {width: 100, height: 200}, /* if specified in auction config */ + 'requestedSize': {'width': '100sw', 'height': '200px'}, /* if specified in auction config */ 'joinCount': 3, 'recency': 3600000, 'bidCount': 17, @@ -810,6 +903,12 @@ The arguments to `generateBid()` are: * directFromSellerSignals is an object that may contain the following fields: * perBuyerSignals: Like auctionConfig.perBuyerSignals, but passed via the [directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals) mechanism. These are the signals whose subresource URL ends in `?perBuyerSignals=[origin]`. * auctionSignals: Like auctionConfig.auctionSignals, but passed via the [directFromSellerSignals](#25-additional-trusted-signals-directfromsellersignals) mechanism. These are the signals whose subresource URL ends in `?auctionSignals`. +* crossOriginTrustedSignals: Like `trustedBiddingSignals`, but used when the trusted-server is + cross-origin to the buyer's script. The value is an object that has as a key the trusted + server's origin, e.g. `"https://www.kv-server.example"`, and as value an object in format + `trustedBiddingSignals` uses. See + [3.1.1 Cross-Origin Trusted Server Signals](#311-cross-origin-trusted-server-signals) for more + details. In the case of component auctions, an interest group's `generateBid()` function will be invoked in all component auctions for which it qualifies, though the `bidCount` value passed to future auctions will only be incremented by one for participation in that auction as a whole. @@ -830,7 +929,7 @@ The output of `generateBid()` contains the following fields: * modelingSignals (optional): A 0-4095 integer (12-bits) passed to `reportWin()`, with noising, as described in the [noising and bucketing scheme](#521-noised-and-bucketed-signals). Invalid values, such as negative, infinite, and NaN values, will be ignored and not passed. Only the lowest 12 bits will be passed. * targetNumAdComponents and numMandatoryAdComponents (both optional): Permits the browser to select only some of the returned adComponents in order to help - make the ad k-anonymous. See [Flexible Component Ad Selection Considering k-Anonymity](#341-target-num-component-ads) + make the ad k-anonymous. See [Flexible Component Ad Selection Considering k-Anonymity](#341-flexible-component-ad-selection-considering-k-anonymity) for more details. In case returning multiple bids is supported by the implementation in use, @@ -880,7 +979,7 @@ const maxAdComponents = navigator.protectedAudience ? navigator.protectedAudience.queryFeatureSupport("adComponentsLimit") : 20; ``` -The output of `generateBid()` can use the on-device ad composition flow through an optional adComponents field, listing additional URLs made available to the fenced frame the container URL is loaded in. The component URLs may be retrieved by calling `navigator.adAuctionComponents(numComponents)`, where numComponents will be capped to the maximum permitted value. To prevent bidder worklets from using this as a side channel to leak additional data to the fenced frame, exactly numComponents obfuscated URLs will be returned by this method, regardless of how many adComponent URLs were actually in the bid, even if the bid contained no adComponents, and the Interest Group itself had no adComponents either. +The output of `generateBid()` can use the on-device ad composition flow through an optional adComponents field, listing additional URLs made available to the fenced frame the container URL is loaded in. The fenced frame configs for the winning ads may be retrieved be calling `window.fence.getNestedConfigs()`, which will always return an Array of 40 fenced frame configs. Alternatively, URNs for the component ad URLs may be retrieved by calling `navigator.adAuctionComponents(numComponents)`, where numComponents will be capped to the maximum permitted value. To prevent bidder worklets from using this as a side channel to leak additional data to the fenced frame, both APIs will pad their result with fenced frame configs or URNs that map to about:blank, so the requested number of values will be returned, regardless of how many adComponent URLs were actually provided by the bid. ##### 3.4.1 Flexible Component Ad Selection Considering k-anonymity @@ -1002,7 +1101,7 @@ Reports are only sent and most interest group state changes (e.g. updating `prev ### 5. Event-Level Reporting (for now) -Once the winning ad has rendered in its Fenced Frame, the seller and the winning buyer each have an opportunity to perform logging and reporting on the auction outcome. The browser will call one reporting function in the seller's auction worklet and one in the winning buyer's bidding worklet. +Once the winning ad has rendered in its Fenced Frame, the seller(s) and the winning buyer each have an opportunity to perform logging and reporting on the auction outcome. The browser will call one reporting function in the seller's auction worklet and one in the winning buyer's bidding worklet; in the case of a multi-seller auction both the top-level and winning component seller's reporting function will be invoked. _As a temporary mechanism,_ these reporting functions will be able to send event-level reports to their servers. These reports can include contextual information, and can include information about the winning interest group if it is over an anonymity threshold. This reporting will happen synchronously, while the page with the ad is still open in the browser. @@ -1011,7 +1110,7 @@ In the long term, we need a mechanism to ensure that the after-the-fact reportin #### 5.1 Seller Reporting on Render -A seller's JavaScript (i.e. the same script, loaded from `decisionLogicURL`, that provided the `scoreAd()` function) can also expose a `reportResult()` function. This is called with the bid that won the auction, if applicable. For component auction seller scripts, `reportResult()` is only invoked if the bid that won the component auction also went on to win the top-level auction. +A seller's JavaScript (i.e. the same script, loaded from `decisionLogicURL`, that provided the `scoreAd()` function) can also expose a `reportResult()` function. This is called with the bid that won the auction, if applicable. ``` @@ -1021,6 +1120,8 @@ reportResult(auctionConfig, browserSignals, directFromSellerSignals) { } ``` +In a multi-seller auction `reportResult` will be called for both the top-level-seller and winning component seller. For the component auction sellers, `reportResult()` is only invoked if the bid that won their component auction also went on to win the top-level auction. The `signalsForWinner` passed to `reportWin` will come from the output of the winning component seller's `reportResult`. + The arguments to this function are: @@ -1030,7 +1131,7 @@ The arguments to this function are: * `topLevelSeller`, `topLevelSellerSignals`, and `modifiedBid` are only present for component auctions, while `componentSeller` is only present for top-level auctions when the winner came from a component auction. * `modifiedBid` is the bid value a component auction's `scoreAd()` script passed to the top-level auction. * `topLevelSellerSignals` is the output of the top-level seller's `reportResult()` method. - * `highestScoringOtherBid` is the value of a bid with the second highest score in the auction. It may be greater than `bid` since it's a bid instead of a score, and a higher bid value may get a lower score. Rejected bids are excluded when calculating this signal. If there was only one bid, it will be 0. In the case of a tie, it will be randomly chosen from all bids with the second highest score, excluding the winning bid if the winning bid had the same score. A component seller's `reportWin()` function will be passed a bid with the second highest score in the component auction, not the top-level auction. It is not reported to top-level sellers in a multi-SSP case because we expect a top-level auction in this case to be first-price auction only: + * `highestScoringOtherBid` is the value of a bid with the second highest score in the auction. It may be greater than `bid` since it's a bid instead of a score, and a higher bid value may get a lower score. Rejected bids are excluded when calculating this signal. If there was only one bid, it will be 0. In the case of a tie, it will be randomly chosen from all bids with the second highest score, excluding the winning bid if the winning bid had the same score. A component seller's `reportResult()` function will be passed a bid with the second highest score in the component auction, not the top-level auction. It is not reported to top-level sellers in a multi-SSP case because we expect a top-level auction in this case to be first-price auction only: ``` { 'topWindowHostname': 'www.example-publisher.com', @@ -1057,7 +1158,7 @@ The `browserSignals` argument must be handled carefully to avoid tracking. It c In the short-term, the `reportResult()` function's reporting happens by calling a `sendReportTo()` API which takes a single string argument representing a URL. The `sendReportTo()` function can be called at most once during a worklet function's execution. The URL is fetched when the frame displaying the ad begins navigating to the ad. Callers of `sendReportTo()` should avoid assembling URLs longer than browser's URL length limits (e.g. [2MB for Chrome](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/url_display_guidelines/url_display_guidelines.md#url-length)) as these may not be reported. The URL is required to have its [site](https://html.spec.whatwg.org/multipage/browsers.html#obtain-a-site) (scheme, eTLD+1) attested for Protected Audience API. Please see [the Privacy Sandbox enrollment attestation model](https://github.com/privacysandbox/attestation#the-privacy-sandbox-enrollment-attestation-model). Eventually reporting will go through the Private Aggregation API once it has been developed. -The output of `reportResult()` is not used for reporting, but rather as an input to the buyer's reporting function. +The output of `reportResult()` is not used for reporting, but rather as an input to the buyer's reporting function. If there is no output or the output is not JSON-serializable (i.e. supported by JSON.stringify()), it will be `null` in `reportWin()`'s `sellerSignals`. #### 5.2 Buyer Reporting on Render and Ad Events @@ -1078,7 +1179,7 @@ The arguments to this function are: * sellerSignals: The output of `reportResult()` above, giving the seller an opportunity to pass information to the buyer. In the case where the winning buyer won a component auction and then went on to win the top-level auction, this is the output of component auction's seller's `reportResult()` method. * browserSignals: Similar to the argument to `reportResult()` above, though without the seller's desirability score, but with additional `adCost`, `seller`, `madeHighestScoringOtherBid` and potentially `interestGroupName` fields: * The `adCost` field contains the value that was returned by `generateBid()`, stochastically rounded to fit into a floating point number with an 8 bit mantissa and 8 bit exponent. This field is only present if `adCost` was returned by `generateBid()`. - * The `interestGroupName` may be included if the tuple of interest group owner, name, bidding script URL, ad creative URL, and ad creative size (if specified by `generateBid`) were jointly k-anonymous. (Note: until [Q1 2025](https://developer.chrome.com/docs/privacy-sandbox/protected-audience-api/feature-status/#k-anonymity), in the implementation, the ad creative size is excluded from this check.) + * The `interestGroupName` may be included if the tuple of interest group owner, name, bidding script URL, ad creative URL, and ad creative size (if specified by `generateBid`) were jointly k-anonymous. (Note: until at least [Q1 2025](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity), in the implementation, the ad creative size is excluded from this check.) * The `madeHighestScoringOtherBid` field is true if the interest group owner was the only bidder that made bids with the second highest score. * The `highestScoringOtherBid` and `madeHighestScoringOtherBid` fields are based on the auction the interest group was directly part of. If that was a component auction, they're from the component auction. If that was the top-level auction, then they're from the top-level auction. Component bidders do not get these signals from top-level auctions since it is the auction seller joining the top-level auction, instead of winning component bidders joining the top-level auction directly. * The `dataVersion` field will contain the `Data-Version` from the trusted bidding signals response headers if they were provided by the trusted bidding signals server response and the version was consistent for all keys requested by this interest group, otherwise the field will be absent. diff --git a/FLEDGE_Key_Value_Server_API.md b/FLEDGE_Key_Value_Server_API.md index d1b61aaa4..a4567ba13 100644 --- a/FLEDGE_Key_Value_Server_API.md +++ b/FLEDGE_Key_Value_Server_API.md @@ -1,6 +1,4 @@ -> FLEDGE has been renamed to Protected Audience API. To learn more about the name change, see the [blog post](https://privacysandbox.com/intl/en_us/news/protected-audience-api-our-new-name-for-fledge) - -# FLEDGE Key/Value Server APIs Explainer +# Protected Audience Key/Value Server APIs Explainer Authors: @@ -9,9 +7,9 @@ Authors: ## Summary -[FLEDGE](https://github.com/WICG/turtledove/blob/main/FLEDGE.md) is a privacy-preserving API that facilitates interest group based advertising. Trusted -servers in FLEDGE are used to add real-time signals into ad selection for both -buyers and sellers. The FLEDGE proposal specifies that these trusted servers +[Protected Audience](https://github.com/WICG/turtledove/blob/main/FLEDGE.md) is a privacy-preserving API that facilitates interest group based advertising. Trusted +servers in Protected Audience are used to add real-time signals into ad selection for both +buyers and sellers. The Protected Audience proposal specifies that these trusted servers should provide basic key-value lookups to facilitate fetching these signals but do no event-level logging or have other side effects. @@ -39,7 +37,7 @@ though the keys may be unique across namespaces in today’s use cases. * For an SSP, there are `renderUrls` and `adComponentRenderUrls`. The -[FLEDGE explainer](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#31-fetching-real-time-data-from-a-trusted-server) +[Protected Audience explainer](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#31-fetching-real-time-data-from-a-trusted-server) provides more context about these namespaces. ## Query API version 2 @@ -48,25 +46,58 @@ __Query versions 2 and beyond are specifically designed for the trusted TEE key/ ### Background -In this version we present a protocol that enables trusted communication between Chrome and the trusted key/value service. The protocol assumes a functioning trust model as described in [the key/value service explainer](https://github.com/privacysandbox/fledge-docs/blob/main/key_value_service_trust_model.md) is in place, primarily that only service implementations recognized by Privacy Sandbox can obtain private decryption keys, and the mechanism for the client and service to obtain cryptographic keys is available but outside the scope of this document. +In this version we present a protocol that enables trusted communication between Chrome and the trusted key/value service. The protocol assumes a functioning trust model as described in [the key/value service explainer](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/key_value_service_trust_model.md) is in place, primarily that only service implementations recognized by Privacy Sandbox can obtain private decryption keys, and the mechanism for the client and service to obtain cryptographic keys is available but outside the scope of this document. -On a high level, the protocol is based on HTTPS + [Oblivious HTTP](https://datatracker.ietf.org/doc/draft-ietf-ohai-ohttp/)(OHTTP). +On a high level, the protocol is similar to the [Bidding & Auction services protocol](https://github.com/WICG/turtledove/blob/main/FLEDGE_browser_bidding_and_auction_API.md), with (bidirectional) [HPKE encryption](https://datatracker.ietf.org/doc/rfc9180/). * TLS is used to ensure that the client is talking to the real service operator (identified by the domain). FLEDGE enforces that the origin of the trusted server matches the config owner ([interest group owner](https://wicg.github.io/turtledove/#joining-interest-groups) for the trusted bidding signal server or [auction config’s seller](https://wicg.github.io/turtledove/#running-ad-auctions) for the trusted scoring signal server). -* OHTTP is used to ensure that the message is only visible to the approved versions of services inside the trusted execution environment (TEE). - * The reason to use OHTTP is that the request must be encrypted and can only be decrypted by the trusted service itself. A notable alternative protocol is TLS which in addition to the domain validation, validates the service identity attestation. However, attestation verification as part of TLS can present performance challenges and is still being evaluated. +* HPKE is used to ensure that the message is only visible to the approved versions of services inside the trusted execution environment (TEE). + * The reason to use HPKE is that the request must be encrypted and can only be decrypted by the trusted service itself. A notable alternative protocol is TLS which in addition to the domain validation, validates the service identity attestation. However, attestation verification as part of TLS can present performance challenges and is still being evaluated. + * The protocol to configure the HPKE is roughly based on the [Oblivious HTTP proposal](https://datatracker.ietf.org/doc/rfc9458/). -For more information on the design, please refer to [the trust model explainer](https://github.com/privacysandbox/fledge-docs/blob/main/key_value_service_trust_model.md). +For more information on the design, please refer to [the trust model explainer](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/key_value_service_trust_model.md). ### Overview ![V2 API diagram](assets/fledge_kv_server_v2_api.png) -HTTP(s) is used to transport data. The message body is an encrypted binary HTTP message as specified by the Oblivious HTTP standard. The binary HTTP message, as it is encrypted, can contain sensitive information. The headers can be used to specify metadata such as compression algorithms. The body is an optionally compressed data structure of the actual request/response message. Padding is applied to the serialized binary HTTP message. +HTTPS is used to transport data. The method is `POST`. + +The HTTP POST body is encrypted. + +#### Encryption + +We will use [Oblivious HTTP](https://datatracker.ietf.org/doc/draft-ietf-ohai-ohttp/) with the following configuration for encryption: + +* 0x0020 DHKEM(X25519, HKDF-SHA256) for KEM (Key encapsulation mechanisms) +* 0x0001 HKDF-SHA256 for KDF (key derivation functions) +* AES256GCM for AEAD scheme. + +Since we are [repurposing the OHTTP encapsulation mechanism, we are required to define new media types](https://www.rfc-editor.org/rfc/rfc9458.html#name-repurposing-the-encapsulati): +* The OHTTP request media type is “message/ad-auction-trusted-signals-request” +* The OHTTP response media type is “message/ad-auction-trusted-signals-response” + +Note that these media types are [concatenated with other fields when creating the HPKE encryption context](https://www.rfc-editor.org/rfc/rfc9458.html#name-encapsulation-of-requests), and are not HTTP content or media types. + +Inside the ciphertext, the request/response is framed with a 5 byte header, where the first byte is the format+compression byte, and the following 4 bytes are the length of the request message in network byte order. Then the request is zero padded to a set of pre-configured lengths. + +The lower 2 bits are used for compression specification. The higher 6 bits are currently unused. + +#### Format+compression byte + +- `0x00` - [CBOR](https://www.rfc-editor.org/rfc/rfc8949.html) no compression +- `0x01` - CBOR compressed in brotli +- `0x02` - CBOR compressed in gzip + +For request, the byte value is 0x00. For response, the byte value depends on the “acceptCompression” field in the request and the server behavior. + +#### Padding + +Padding is applied with sizes as multiples of 2^n KBs ranging from 0 to 2MB. So the valid response sizes will be [0, 128B, 256B, 512B, 1KB, 2KB, 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB, 512KB, 1MB, 2MB]. ### Core data -Core request and response data structures are all in JSON. +Core request and response data structures are all in [CBOR](https://www.rfc-editor.org/rfc/rfc8949.html). The schema below is defined following the spec by https://json-schema.org/. @@ -76,127 +107,78 @@ The API is generic, agnostic to DSP or SSP use cases. ##### Request version 2.0 -Requests are not compressed. Compression could save size but may add latency. Request size is presumed to be small, so compression may not improve overall performance. Version 2 will not use compression for the request but more experimentation is necessary to determine if compression is an overall win and should be included in future protocol versions. +Requests are not compressed. Compression could save size but may add latency. Request size is presumed to be small, so compression may not improve overall performance. Version 2 will initially not implement compression for the request but more experimentation is necessary to determine if compression is an overall win and should be implemented in the future. -In the request, one major difference from [V1](#query-api-version-1) is that the keys are now grouped. There is a tree-like hierarchy: +In the request, one major difference from V1/BYOS is that the keys are now grouped. There is a tree-like hierarchy: -* Each request contains one or more partitions. Each partition is a collection of keys that can be processed together by the service without any potential privacy leakage (For example, if the server uses User Defined Functions to process, one UDF call can only process one partition). This is controlled by the client. For example, Chrome may put all keys from the same interest group into one partition. With certain optimizations allowed, such as with [“executionMode: group-by-origin”](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#12-interest-group-attributes), keys from all interest groups with the same joining site may be in one partition. +* Each request contains one or more partitions. Each partition is a collection of keys that can be processed together by the service without any potential privacy leakage (For example, if the server uses [User Defined Functions](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/key_value_service_user_defined_functions.md) to process, one UDF call can only process one partition). Keys from one interest group must be in the same partition. Keys from different interest groups with the same joining site may or may not be in the same partition, so the server User Defined Functions should not make any assumptions based on that. * Each partition contains one or more key groups. Each key group has its unique attributes among all key groups in the partition. The attributes are represented by a list of “Tags”. Besides tags, the key group contains a list of keys to look up. * Each partition has a unique id. * Each partition has a compression group field. Results of partitions belonging to the same compression group can be compressed together in the response. Different compression groups must be compressed separately. See more details below. The expected use case by the client is that interest groups from the same joining origin and owner can be in the same compression group. ![request structure](assets/fledge_kv_server_v2_req_structure.jpeg) -##### Available Tags - -###### Version 2023.01 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Tag category - Category description - Tag - Description - Restrictions -
Client awareness - Each key group has exactly one tag from this category. - structured - Browser defines the format of this key/value pair and will use the results for internal purposes. An example use case is the PerInterestGroupData in the FLEDGE explainer. - -
custom - Browser is oblivious to the keys and directly passes the results to DSP/SSP javascript functions - -
Namespace - Each key group has exactly one tag from this category. - interestGroupNames - Names of interest groups in the encompassing partition. - The service expects the client to only pair this with ‘structured’ tag -
keys - “keys” is a list of trustedBiddingSignalsKeys strings. - The service expects the client to only pair this with ‘custom’ tag -
renderUrls - Similarly, sellers may want to fetch information about a specific creative, e.g. the results of some out-of-band ad scanning system. This works in much the same way, with the base URL coming from the trustedScoringSignalsUrl property of the seller's auction configuration object. -
adComponentRenderUrls -
- -If the restrictions are not followed by the client, for example due to misconfiguration, the service will still try to process the request as much as possible, rather than returning an error. The user defined function on the service would still receive the input and can decide what to return. A basic solution could be to ignore the invalid piece of input. - #### Schema of the request ```json { - "title": "Key Value Service GetValues request", + "title": "tkv.request.v2.Request", + "description": "Key Value Service GetValues request", "type": "object", "additionalProperties": false, "properties": { - "metadata": { - "description": "global metadata shared by all partitions", - "type": "object", - "additionalProperties": false, - "properties": { - "hostname": { - "description": "The hostname of the top-level frame calling runAdAuction().", - "type": "string" - } - } + "acceptCompression": { + "type": "array", + "items": { + "type": "string", + "description": "must contain at least one of none, gzip, brotli" + }, + "description": "Algorithm accepted by the browser for the response." }, "partitions": { - "description": "A list of partitions. Each must be processed independently", + "description": "A list of partitions. Each must be processed independently. Accessible by UDF.", "type": "array", "items": { - "title": "Single partition object", - "description": "A collection of keys that can be processed together", + "title": "tkv.request.v2.Partition", + "description": "Single partition object. A collection of keys that can be processed together", "type": "object", "additionalProperties": false, "properties": { "id": { "description": "Unique id of the partition in this request", - "type": "number" + "type": "unsigned integer" }, "compressionGroupId": { "description": "Unique id of a compression group in this request. Only partitions belonging to the same compression group will be compressed together in the response", - "type": "number" + "type": "unsigned integer" + }, + "metadata": { + "title": "tkv.request.v2.PartitionMetadata", + "description": "metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "hostname": { + "description": "The hostname of the top-level frame calling runAdAuction().", + "type": "string" + }, + "experimentGroupId": { + "type": "string" + }, + "slotSize": { + "description": "Available if trustedBiddingSignalsSlotSizeMode=slot-size. In the form of ,", + "type": "string" + }, + "allSlotsRequestedSizes": { + "description": "Available if trustedBiddingSignalsSlotSizeMode=all-slots-requested-sizes. In the form of ,,,,...", + "type": "string" + } + } }, "arguments": { "type": "array", "items": { + "title": "tkv.request.v2.Argument", "description": "One group of keys and common attributes about them", "type": "object", "additionalProperties": false, @@ -228,27 +210,75 @@ If the restrictions are not followed by the client, for example due to misconfig } }, "required": [ - "metadata", "partitions" ] } + ``` +##### Available Tags + +###### Version 2024.04 + + + + + + + + + + + + + + + + + + + + + + + + + +
Tag category + Category description + Tag + Description +
Namespace + Each key group has exactly one tag from this category. + interestGroupNames + Names of interest groups in the encompassing partition. +
keys + “keys” is a list of trustedBiddingSignalsKeys strings. +
renderUrls + Similarly, sellers may want to fetch information about a specific creative, e.g. the results of some out-of-band ad scanning system. This works in much the same way, with the base URL coming from the trustedScoringSignalsUrl property of the seller's auction configuration object. +
adComponentRenderUrls +
+ Example trusted bidding signals request from Chrome: ```json { - "metadata": { - "hostname": "example.com" - }, + "acceptCompression": [ + "none", + "gzip" + ], "partitions": [ { "id": 0, "compressionGroupId": 0, + "metadata": { + "hostname": "example.com", + "experimentGroupId": "12345", + "slotSize": "100,200", + }, "arguments": [ { "tags": [ - "structured", "interestGroupNames" ], "data": [ @@ -257,7 +287,6 @@ Example trusted bidding signals request from Chrome: }, { "tags": [ - "custom", "keys" ], "data": [ @@ -273,7 +302,6 @@ Example trusted bidding signals request from Chrome: "arguments": [ { "tags": [ - "structured", "interestGroupNames" ], "data": [ @@ -283,7 +311,6 @@ Example trusted bidding signals request from Chrome: }, { "tags": [ - "custom", "keys" ], "data": [ @@ -300,81 +327,34 @@ Example trusted bidding signals request from Chrome: ##### Response version 2.0 -The response is compressed. Due to security and privacy reasons the compression is applied independently to each compression group. That means, the response body will be a concatenation of compressed blobs in network byte order. Each blob is for outputs of one or more partitions, sharing the same compressionGroup value as specified in the request. - -Each compressed blob has a prefix of a 32-bit unsigned integer also in network byte order to indicate the length of the compressed blob. +The response is compressed. Due to security and privacy reasons the compression is applied independently to each compression group. That means, The response object mainly contains a list of compressed blobs, each for one compression group. Each blob is for outputs of one or more partitions, sharing the same compressionGroup value as specified in the request. -![compression](assets/fledge_kv_server_v2_compression.png) - -The schema of the JSON in one compression group: +###### tkv.response.v2.Response ```json { - "title": "Response object for a compression group", + "title": "tkv.response.v2.Response", "type": "object", "additionalProperties": false, "properties": { - "partitions": { + "compressionGroups": { "type": "array", "items": { - "title": "Output for one partition", + "title": "tkv.response.v2.CompressedCompressionGroup", "type": "object", + "description": "Object for a compression group, compressed using the algorithm specified in the request", + "additionalProperties": false, "properties": { - "id": { - "description": "Unique id of the partition from the request", - "type": "number" + "compressionGroupId": { + "type": "unsigned integer" }, - "keyGroupOutputs": { - "type": "array", - "items": { - "title": "Output for one key group", - "type": "object", - "additionalProperties": false, - "properties": { - "tags": { - "description": "Attributes of this key group.", - "type": "array", - "items": { - "description": "List of tags describing this key group's attributes", - "type": "string" - } - }, - "keyValues": { - "description": "If a keyValues object exists, it must at least contain one key-value pair. If no key-value pair can be returned, the key group should not be in the response.", - "type": "object", - "patternProperties": { - ".*": { - "description": "One value to be returned in response for one key", - "type": "object", - "additionalProperties": false, - "properties": { - "value": { - "type": [ - "string", - "number", - "integer", - "object", - "array", - "boolean" - ] - }, - "global_ttl_sec": { - "description": "Adtech-specified TTL for client-side caching, not dedicated to a specific subkey. In seconds. Unset means no caching.", - "type": "integer" - }, - "dedicated_ttl_sec": { - "description": "Adtech-specified TTL for client-side caching, specific to the subkey in the request. In seconds. Unset means no caching.", - "type": "integer" - } - }, - "required": [ - "value" - ] - } - } - } - } - } + "ttl_ms": { + "description": "Adtech-specified TTL for client-side caching. In milliseconds. Unset means no caching.", + "type": "unsigned integer" + }, + "content": { + "description": "compressed CBOR binary string. For details see compressed response content schema: tkv.response.v2.CompressionGroup", + "type": "byte string" } } } @@ -383,113 +363,122 @@ The schema of the JSON in one compression group: } ``` -Example of one (trusted bidding signals server) response: +###### tkv.response.v2.CompressionGroup + +The content of each compressed blob is a CBOR list of partition outputs. This object contains actual key value results for partitions in the corresponding compression group. ```json { - "partitions": [ - { - "id": 0, - "keyGroupOutputs": [ - { - "tags": [ - "structured", - "interestGroupNames" - ], - "keyValues": { - "InterestGroup1": { - "value": { - "priorityVector": { - "signal1": 1 - } - }, - "dedicated_ttl_sec": 1 - } - } - }, - { - "tags": [ - "custom", - "keys" - ], - "keyValues": { - "keyAfromInterestGroup1": { - "value": "valueForA", - "dedicated_ttl_sec": 120 + "type": "array", + "items": { + "title": "tkv.response.v2.PartitionOutput", + "description": "Output for one partition", + "type": "object", + "properties": { + "id": { + "description": "Unique id of the partition from the request", + "type": "unsigned integer" + }, + "dataVersion" { + "description": "An optional field to indicate the state of the data that generated this response, which will then be available in bid generation/scoring and reporting.", + "type": "unsigned integer" + }, + "keyGroupOutputs": { + "type": "array", + "items": { + "title": "tkv.response.v2.KeyGroupOutput", + "title": "Output for one key group", + "type": "object", + "additionalProperties": false, + "properties": { + "tags": { + "description": "Attributes of this key group.", + "type": "array", + "items": { + "description": "List of tags describing this key group's attributes", + "type": "string" + } }, - "keyBfromInterestGroup1": { - "value": [ - "value1ForB", - "value2ForB" - ], - "dedicated_ttl_sec": 60 + "keyValues": { + "description": "If a keyValues object exists, it must at least contain one key-value pair. If no key-value pair can be returned, the key group should not be in the response.", + "type": "object", + "patternProperties": { + ".*": { + "description": "One value to be returned in response for one key", + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": [ + "text string" + ] + } + }, + "required": [ + "value" + ] + } + } } } } - ] + } } - ] + } } + ``` -Example of one trusted scoring signals server response: +Example: ```json -{ - "partitions": [ - { - "id": 1, - "keyGroupOutputs": [ - { - "tags": [ - "custom", - "renderUrls" - ], - "keyValues": { - "renderurls.com/1": { - "value": "value3", - "dedicated_ttl_sec": 120 - }, - "renderurls.com/2": { - "value": "value4", - "dedicated_ttl_sec": 120 - } +[ + { + "id": 0, + "dataVersion": 102, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "InterestGroup1": { + "value": "{\"priorityVector\":{\"signal1\":1},\"updateIfOlderThanMs\": 10000}" } - }, - { - "tags": [ - "custom", - "adComponentRenderUrls" - ], - "keyValues": { - "adcomponents.com/1": { - "value": "value1", - "dedicated_ttl_sec": 120 - }, - "adcomponents.com/2": { - "value": [ - "value2A", - "value2B" - ], - "dedicated_ttl_sec": 60 - } + } + }, + { + "tags": [ + "keys" + ], + "keyValues": { + "keyAfromInterestGroup1": { + "value": "valueForA" + }, + "keyBfromInterestGroup1": { + "value":"[\"value1ForB\",\"value2ForB\"]" } } - ] - } - ] -} + } + ] + } +] ``` #### Structured keys response specification Structured keys are keys that the browser is aware of and the browser can use the response to do additional processing. The value of these keys must abide by the following schema for the browser to successfully parse them. -##### Response schema for tag interestGroupNames +Note that they must be serialized to string when stored as the value. + +##### tkv.response.v2.InterestGroupResponse + +For values for keys from the `interestGroupNames` namespace, they must conform to the following schema, prior to being serialized to string: ```json { - "title": "Format for value of keys in groups tagged 'structured' and 'interestGroupNames'", + "title": "tkv.response.v2.InterestGroupResponse", + "description": "Format for value of keys in groups tagged 'interestGroupNames'", "type": "object", "additionalProperties": false, "properties": { @@ -501,6 +490,10 @@ Structured keys are keys that the browser is aware of and the browser can use th "type": "number" } } + }, + "updateIfOlderThanMs": { + "description": "This optional field specifies that the interest group should be updated if the interest group hasn't been joined or updated in a duration of time exceeding `updateIfOlderThanMs` milliseconds. Updates that ended in failure, either parse or network failure, are not considered to increment the last update or join time. An `updateIfOlderThanMs` that's less than 10 minutes will be clamped to 10 minutes.", + "type": "unsigned integer" } } } @@ -513,109 +506,25 @@ Example: "priorityVector": { "signal1": 1, "signal2": 2 - } + }, + "updateIfOlderThanMs": 10000 } ``` -#### Client-side caching TTL - -Version 1’s API simply returns a key value pair for each key. In this version, each key maps to a JSON object which has 3 fields: value, global\_ttl\_sec and dedicated\_ttl\_sec: - -* The global\_ttl\_sec tells the client to cache the entry for no longer than this TTL under all request contexts. -* The dedicated\_ttl\_sec indicates the same TTL behavior except it should only be applied to requests that contain the same subkey as this response is for. - #### Size limitation -For the K/V service, plaintext response payload before compression must not be more than 2MB. 2MB is a preliminary target and can be changed in the future if there is a need and no significant negative impact to the browser. - -The service would only keep key-value pairs with a total size less than that. The rest of the key-value pairs will be discarded. The selection of discarded pairs will be arbitrary. - -If a single value is larger than the limit, the service should always discard it. +For the K/V service, plaintext response payload before compression must not be more than 8MB. 8MB is a preliminary target and can be changed in the future if there is a need and no significant negative impact to the browser. #### Error handling -If the server fails partially, such as failures to process one partition, the response will not contain the corresponding part and will not contain any error report. This could be revisited in the future versions as the privacy implications become clearer. - -### [BinaryHTTP](https://datatracker.ietf.org/doc/rfc9292/): The packaging layer for HTTP k/v service requests - -#### Request - -The core request data is packaged by a binary HTTP request message. The method is PUT. The JSON is stored as the body of the message. - -The service respects the following headers in the message: - - - - Header - - Description - - - - - - - - - - -
Accept-Encoding - What compression algorithm the client can accept -

-Optional. -

X-kv-query-request-version - Request version used as specified in this document. -
- -#### Response - -The core response data is packaged by a binary HTTP response message. The response is compressed and stored as the body of the message. - -The service can set the following headers in the message: - - - - Header - - Description - - - - - - - - - - -
Content-Encoding - What compression algorithm the service used. -

-Set if the client specifies “Accept-Encoding” header. -

x-kv-query-response-version - Response version used as specified in this document. -
- -### Padding - -Padding is applied according to the Binary HTTP [padding specification](https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-padding-and-truncation). - -Padding is applied with sizes as multiples of 2^n KBs ranging from 0 to 2MB. So the valid response sizes will be [0, 128B, 256B, 512B, 1KB, 2KB, 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB, 512KB, 1MB, 2MB]. - -### Encryption - -We will use [Oblivious HTTP](https://datatracker.ietf.org/doc/draft-ietf-ohai-ohttp/) with the following configuration for encryption: - -* 0x0020 DHKEM(X25519, HKDF-SHA256) for KEM (Key encapsulation mechanisms) -* 0x0001 HKDF-SHA256 for KDF (key derivation functions) -* AES256GCM for AEAD scheme. +To be supported in the future. ## Server Internal APIs and procedures -The system will provide multiple private APIs and procedures, for loading data and user-defined functions whose model is described in detail in the [Key/Value server design explainer](https://github.com/privacysandbox/fledge-docs/blob/main/key_value_service_trust_model.md#support-for-user-defined-functions-udfs). +The system will provide multiple private APIs and procedures, for loading data and user-defined functions whose model is described in detail in the [Key/Value server design explainer](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/key_value_service_trust_model.md#support-for-user-defined-functions-udfs). These APIs and procedures are ACLs controlled by the ad tech operator of the system, for only the operator (and their designated parties) to use. -The key/value server code is available on its [Privacy Sandbox github repo](https://github.com/privacysandbox/fledge-key-value-service) which reflects the most recent API and procedures. As an example, the [data loading guide](https://github.com/privacysandbox/fledge-key-value-service/blob/main/docs/loading_data.md) has specific instructions on integrating with the data ingestion procedure. The procedure may be based on the actual storage medium of the dataset, e.g., the server can read data from data files from a prespecified location. +The key/value server code is available on its [Privacy Sandbox github repo](https://github.com/privacysandbox/protected-auction-key-value-service) which reflects the most recent API and procedures. As an example, the [data loading guide](https://github.com/privacysandbox/protected-auction-key-value-service/blob/main/docs/data_loading/loading_data.md) has specific instructions on integrating with the data ingestion procedure. The procedure may be based on the actual storage medium of the dataset, e.g., the server can read data from data files from a prespecified location. -As the [FLEDGE API](https://github.com/WICG/turtledove/blob/main/FLEDGE.md) describes the client side flow, the APIs and procedures related to the server system design and implementation will have the specification posted and updated in the key/value server’s [github repo](https://github.com/privacysandbox/fledge-key-value-service). +As the [FLEDGE API](https://github.com/WICG/turtledove/blob/main/FLEDGE.md) describes the client side flow, the APIs and procedures related to the server system design and implementation will have the specification posted and updated in the key/value server’s [github repo](https://github.com/privacysandbox/protected-auction-key-value-service). diff --git a/FLEDGE_browser_bidding_and_auction_API.md b/FLEDGE_browser_bidding_and_auction_API.md index 641cc93d9..4b6c7b014 100644 --- a/FLEDGE_browser_bidding_and_auction_API.md +++ b/FLEDGE_browser_bidding_and_auction_API.md @@ -1,16 +1,16 @@ > FLEDGE has been renamed to Protected Audience API. To learn more about the name change, see the [blog post](https://privacysandbox.com/intl/en_us/news/protected-audience-api-our-new-name-for-fledge) -# FLEDGE Browser Bidding & Auction API +# Protected Audience Browser Bidding & Auction API ## Background -This document seeks to propose an API for web pages to perform FLEDGE auctions using Bidding and Auction (B&A) servers running in Trusted Execution Environments (TEE), as was announced [here](https://developer.chrome.com/blog/bidding-and-auction-services-availability/). This document seeks to document the web-exposed JavaScript API. The browser is responsible for formatting the data sent to the B&A servers using the B&A server API documented in [this explainer](https://github.com/privacysandbox/fledge-docs/blob/main/bidding_auction_services_api.md). +This document seeks to propose an API for web pages to perform Protected Audience auctions using Bidding and Auction (B&A) servers running in Trusted Execution Environments (TEE), as was announced [here](https://developer.chrome.com/blog/bidding-and-auction-services-availability/). This document seeks to document the web-exposed JavaScript API. The browser is responsible for formatting the data sent to the B&A servers using the B&A server API documented in [this explainer](https://github.com/privacysandbox/fledge-docs/blob/main/bidding_auction_services_api.md). -## Steps to perform a FLEDGE auction using B&A +## Steps to perform a Protected Audience auction using B&A ### Step 1: Get auction blob from browser -To execute an on-server FLEDGE auction, sellers begin by calling `navigator.getInterestGroupAdAuctionData()` with returns a `Promise`: +To execute an on-server Protected Audience auction, sellers begin by calling `navigator.getInterestGroupAdAuctionData()` with returns a `Promise`: ```javascript const auctionBlob = navigator.getInterestGroupAdAuctionData({ @@ -145,7 +145,7 @@ Another way to prevent the encrypted blob’s size from being a leak is to have 1. This would hugely complicate the B&A server’s interactions and API, making adoption likely infeasible. The B&A API would no longer be a RESTful API as it would have to coordinate communication from both the browser and other servers (e.g. contextual auction server). -1. This would also require the on-device JavaScript to determine whether to send the FLEDGE request to the B&A server, perhaps at a time before it has the results of the contextual auction which might influence the decision. Without this information the device would have to send the encrypted blob for every ad request, even in cases where the contextual call indicated it was wasteful to do so. +1. This would also require the on-device JavaScript to determine whether to send the Protected Audience request to the B&A server, perhaps at a time before it has the results of the contextual auction which might influence the decision. Without this information the device would have to send the encrypted blob for every ad request, even in cases where the contextual call indicated it was wasteful to do so. Exposing size of the blob is a temporary leak that we hope to mitigate in the future: diff --git a/FLEDGE_extended_PA_reporting.md b/FLEDGE_extended_PA_reporting.md index 52de9ac73..9b4b90d6c 100644 --- a/FLEDGE_extended_PA_reporting.md +++ b/FLEDGE_extended_PA_reporting.md @@ -181,8 +181,11 @@ The parameters consist of: * an `eventType` which is a string identifying the event type that triggers this report to be sent (see [Triggering reports](#triggering-reports) below), and * a `contribution` object which contains: - * a `bucket` which is a 128bit ID or a `signalBucket` which tells the browser how to calculate the bucket (represented as BigInt) and - * a `value` which is a non-negative integer or a `signalValue` which tells the browser how to calculate the value. + * a `bucket` which is a 128bit ID or a `signalBucket` which tells the browser how to calculate the bucket (represented as BigInt), + * a `value` which is a non-negative integer or a `signalValue` which tells the browser how to calculate the value, and + * a `filteringId` which is an optional integer in the range [0, 255] used to allow for separating aggregation service queries. For + more detail, please see the [flexible filtering + explainer](https://github.com/patcg-individual-drafts/private-aggregation-api/blob/main/flexible_filtering.md#proposal-filtering-id-in-the-encrypted-payload). Where `signalBucket` and `signalValue` is a dictionary which consists of: diff --git a/Fenced_Frames_Ads_Reporting.md b/Fenced_Frames_Ads_Reporting.md index 0668d80e2..740382052 100644 --- a/Fenced_Frames_Ads_Reporting.md +++ b/Fenced_Frames_Ads_Reporting.md @@ -52,7 +52,7 @@ There are two variants of the `reportEvent` API for event-level reporting that a This API is available from all documents in a fenced frame tree (i.e., the ad creative URL that won the Protected Audience auction). Child iframe or redirected documents that are same-origin to the mapped URL of the fenced frame config can call this API without any restrictions. Cross-origin child iframe documents can only call this API if there is opt-in from both the fenced frame root and the cross-origin document. The fenced frame root opts in by being served with a new `Allow-Cross-Origin-Event-Reporting` response header set to `true`. The cross-origin document opts in by calling `reportEvent` with `crossOriginExposed=true`. [See TURTLEDOVE issue #1077](https://github.com/WICG/turtledove/issues/1077) for the motivation behind cross-origin support. -The browser processes the beacon by sending an HTTP POST request, like the existing [navigator.sendBeacon](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon). +The browser processes the beacon by sending an HTTP POST request, like the existing [navigator.sendBeacon](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon). The POST request is sent immediately or as soon as the corresponding registerAdBeacon is invoked, and is done asynchronously to avoid blocking other actions. Note `window.fence` here is a new namespace for APIs that are only available from within a fenced frame. In the interim period when FLEDGE supports rendering the winning ad in an iframe, `window.fence` will also be available in such an iframe. @@ -330,7 +330,7 @@ For fenced frames rendering the ad components under the top-level ad fenced fram ``` window.fence.setReportEventDataForAutomaticBeacons({ - 'eventType': 'reserved.top_navigation', + 'eventType': 'reserved.top_navigation_commit', 'destination':['seller', 'buyer'] }); ``` diff --git a/PA_Feature_Detecting.md b/PA_Feature_Detecting.md index d4bf0874f..81f3a0e09 100644 --- a/PA_Feature_Detecting.md +++ b/PA_Feature_Detecting.md @@ -70,7 +70,7 @@ beacon, and then the ad frame can set the same automatic beacon data for both ev `top_navigation_commit` and `top_navigation` beacons, and filter out duplicate beacons that have the same exact data. ## Increase in limit to number of component ads -[Intent to Ship](TBD) +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/3RUQk0GCC9Q/m/wmbXOOB8AAAJ) Inside `generateBid` one can determine the currently active limit on number of components ads as follows: ``` @@ -85,7 +85,7 @@ const maxAdComponents = navigator.protectedAudience ? ``` ## Reporting timeout -[Intent to Ship](TBD) +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/ZdZXN1D-MtI/) Inside `reportWin` one can determine its reporting timeout as follows: ``` @@ -101,11 +101,12 @@ const reportingTimeout = auctionConfig.reportingTimeout ? From the context of a web page, whether custom reporting timeout is enabled can be queried as follows: ``` -const reportingTimeoutEnabled = navigator.protectedAudience ? - navigator.protectedAudience.queryFeatureSupport("reportingTimeout") : false; +const reportingTimeoutEnabled = navigator.protectedAudience && + navigator.protectedAudience.queryFeatureSupport("reportingTimeout"); ``` ## Returning multiple bids from generateBid() +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/ZdZXN1D-MtI/) Inside `generateBid()`, if `browserSignals.multiBidLimit` exists then returning an array of bids is supported. The value of `browserSignals.multiBidLimit` @@ -113,7 +114,56 @@ returns the maximum numbers of bids that can be returned, which may be as low as 1. ## Component ad subsetting with targetNumAdComponents +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/ZdZXN1D-MtI/) Inside `generateBid()`, if `browserSignals.multiBidLimit` exist then the `targetNumAdComponents` and `numMandatoryAdComponents` bid fields will be considered. + +## Cross-origin trusted signals +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/5nvBAjmoO2g) + +From context of a web page: +``` +navigator.protectedAudience && navigator.protectedAudience.queryFeatureSupport( + "permitCrossOriginTrustedSignals") +``` + +## Real time reporting +[Intent to Ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/9_dR-BdyeWE) + +From context of a web page: +``` +navigator.protectedAudience && navigator.protectedAudience.queryFeatureSupport( + "realTimeReporting") +``` + +## Getting browser-side detectable features as an object +Sometimes it's desirable to get status of all features detectable via `queryFeatureSupport` in a +forward-compatible way. Sufficiently recent versions provide this functionality via +`queryFeatureSupport('*')`, which returns a dictionary describing state of various features. Since +that functionality isn't available in older versions, backwards-compatibility polyfilling is +suggested: + +``` +let qfs = navigator.protectedAudience ? + navigator.protectedAudience.queryFeatureSupport.bind(navigator.protectedAudience) : null; + +let allFeatureStatus = qfs ? + (qfs("*") || { + adComponentsLimit: qfs("adComponentsLimit"), + deprecatedRenderURLReplacements: qfs("deprecatedRenderURLReplacements"), + reportingTimeout: qfs("reportingTimeout") + }) : {} +``` + +An example return value would be: +``` +{ + "adComponentsLimit":40, + "deprecatedRenderURLReplacements":false, + "permitCrossOriginTrustedSignals":true, + "realTimeReporting":true, + "reportingTimeout":true +} +``` diff --git a/PA_real_time_monitoring.md b/PA_real_time_monitoring.md index 3f2b3c902..04eb032e5 100644 --- a/PA_real_time_monitoring.md +++ b/PA_real_time_monitoring.md @@ -31,9 +31,9 @@ The goal of real-time reporting is to get auction monitoring data to the buyer a The high level Real Time Reporting API here is similar to the [Private Aggregation API](https://developers.google.com/privacy-sandbox/relevance/private-aggregation) in that it allows you to contribute to a histogram, but the contribution is subject to substantial local noise and is sent soon after the auction is completed to allow for a fast detection SLA. Once an error has been detected, [downsampled](https://github.com/WICG/turtledove/blob/main/FLEDGE.md#712-downsampling) `forDebuggingOnly` can be utilized for root cause analysis. -This API will provide histogram contribution values of only 0s and 1s, which can be used to correspond to whether or not the event being monitored behaved expectedly or aberrantly. There are a number of events that adtechs may wish to monitor in real time - adtechs assign meanings to buckets for events in `generateBid()` and `scoreAd()` (e.g. an adtech might define bucket #44 as signal parsing error in generateBid). For certain event-types that cannot be reported from the aforementioned worklets, we propose the browser defines a set of platform-defined buckets (e.g. the browser might define bucket #12 as failure to fetch bidding or scoring script). Read more about [Platform Contributions here](#platform-contributions-reporting-errors-not-detectable-in-worklet-JS). +This API will provide histogram contribution values of only 0s and 1s, which can be used to correspond to whether or not the event being monitored behaved expectedly or aberrantly. There are a number of events that ad techs may wish to monitor in real time - ad techs assign meanings to buckets for events in `generateBid()` and `scoreAd()` (e.g. an ad tech might define bucket #44 as signal parsing error in generateBid). For certain event-types that cannot be reported from the aforementioned worklets, the browser defines a set of platform-defined buckets (e.g. the browser might define the first platform bucket as failure to fetch bidding or scoring script). Read more about [Platform Contributions here](#platform-contributions-reporting-errors-not-detectable-in-worklet-JS). -Note that the type of buckets that adtechs can define in `generateBid()` and `scoreAd()` can already be created in the Private Aggregation API. Where latency is acceptable, Private Aggregation API (with Aggregation Service) will provide more accuracy and should be the preferred solution for reporting beyond event-level `reportWin()` and `reportResult()`. +Note that the type of buckets that ad techs can define in `generateBid()` and `scoreAd()` can already be created in the Private Aggregation API. Where latency is acceptable, Private Aggregation API (with Aggregation Service) will provide more accuracy and should be the preferred solution for reporting beyond event-level `reportWin()` and `reportResult()`. ## Opting into reporting @@ -77,53 +77,112 @@ const BIDDING_SLOW_WEIGHT = 0.1; function generateBid(...) { // or scoreAd // Contribute to the histogram if the worklet execution takes longer than a // specified # of ms. - realTimeReporting.contributeOnWorkletLatency( - BIDDING_SLOW_BUCKET, - { priorityWeight: BIDDING_SLOW_BUCKET, + realTimeReporting.contributeToHistogram( + { bucket: BIDDING_SLOW_BUCKET, + priorityWeight: BIDDING_SLOW_WEIGHT, latencyThreshold: 500}); // In milliseconds - var bid; + let bidOut; try { // Bid generation logic... - bid = ... + bidOut = ... ... } catch (error) { - realTimeReporting.contributeToRealTimeHistogram( - BIDDING_THREW_BUCKET, - { priorityWeight: BIDDING_THREW_WEIGHT }); + realTimeReporting.contributeToHistogram( + { bucket: BIDDING_THREW_BUCKET, + priorityWeight: BIDDING_THREW_WEIGHT }); return; } - if (bid.bid > MAX_REASONABLE_BID) { - realTimeReporting.contributeToRealTimeHistogram( + if (bidOut.bid > MAX_REASONABLE_BID) { + realTimeReporting.contributeToHistogram( BID_TOO_HIGH_BUCKET, - { priorityWeight: BID_TOO_HIGH_WEIGHT }); + { bucket: BID_TOO_HIGH_BUCKET, + priorityWeight: BID_TOO_HIGH_WEIGHT }); return; } - return bid; + return bidOut; } ``` ## Platform contributions: reporting errors not detectable in worklet JS -There may be some errors that occur that are not visible in either `scoreAd()` or `generateBid()`. These include things like failures to fetch the bidding script, trusted real-time signals, or creative URL. In these cases, we propose to allocate a certain portion of the resulting histogram for platform errors. While this can be done as a completely separate set of buckets that are immutable from JavaScript, we would still require the number of total contributions across all buckets to be capped at 1, so these platform-assisted contributions will come with a default weighting and participate in the prioritization algorithm just like regular contributions. +There may be some errors that occur that are not visible in either `scoreAd()` or `generateBid()`. These include things like failures to fetch the bidding script, trusted real-time signals, or creative URL. In these cases, we allocate a separate set of buckets for platform errors. While this is done as a completely separate set of buckets that are immutable from JavaScript, we would still require the number of total contributions across all buckets to be capped at 1, so these platform-assisted contributions come with a default weighting of 1 and participate in the prioritization algorithm just like regular contributions. -The priority weight of platform contributions will be hardcoded and specified, so you can reason about the behavior across platform and developer contributions. +The priority weight of platform contributions is hardcoded as 1, so you can reason about the behavior across platform and developer contributions. +Platform contributions are reported in the report’s `platformHistogram` field, separately from regular contributions. See [below](#sending-reports-after-an-auction) for more details. + +| Bucket | Error | +| --- | --- | +| 0 | Bidding script fetch error | +| 1 | Scoring script fetch error | +| 2 | Trusted bidding signals fetch error | +| 3 | Trusted scoring signals fetch error | + +Fetch errors include non-2xx response code, response header does not have “Ad-Auction-Allowed: true”, response body cannot be parsed as valid json for trusted signals, etc,. ## Sending reports after an auction -After the auction completes, all opted-in participants (sellers, and buyers with interest groups present on device) will always emit one report per participant per auction, even if no ad wins or no `contributeToRealTimeHistogram()` calls are made. These reports will be sent to `domain.example/.well-known/interest-group/real-time-report` where `domain.example` would be replaced with the seller’s origin or the buyer’s interest group owner origin. These reports are not tied to specific interest groups, they are aggregated over all interest groups.The serialization format of the report is TBD, but see [below](#histogram-contributions-and-the-rappor-noise-algorithm) for the high level description of the encoding. Reporting domains may be configurable in the future. +After the auction completes, all opted-in participants (sellers, and buyers with interest groups present on device) will always emit one report per participant per auction, even if no ad wins or no `contributeToRealTimeHistogram()` calls are made. These reports will be sent to `domain.example/.well-known/interest-group/real-time-report` where `domain.example` would be replaced with the seller’s origin or the buyer’s interest group owner origin. These reports are not tied to specific interest groups, they are aggregated over all interest groups. See [below](#histogram-contributions-and-the-rappor-noise-algorithm) for the high level description of the encoding. Reporting domains may be configurable in the future. Participants who did not call `contributeToRealTimeHistogram()` will contribute an array of zeros by default, which will still require the input going through the noising mechanism to satisfy the privacy requirements. After the noise mechanism, it is very unlikely that output will be all zeros. Even still, if we encounter a contribution of all zeros post-noising, we will still report on it. This is important for debiasing the noisy results, as explained below. +Real time reports are sent as the body of an HTTP POST request to ad techs and are encoded as [CBOR](https://www.rfc-editor.org/rfc/rfc8949.html) with the following schema (specified using [JSON Schema](https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01)): + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "version": { + "type": "number", + "description": "Data version." }, + "histogram": { + "type": "object", + "properties": { + "buckets": { + "type": "byte string", + "description": "a byte string bit packed from regular contribution buckets, padded with zeros to the nearest byte." + }, + "length": "integer", + "description": "the number of regular contribution buckets before bit packing." + }, + }, + "platformHistogram": { + "type": "object", + "properties": { + "buckets": { + "type": "byte string", + "description": "a byte string bit packed from platform contribution buckets, padded with zeros to the nearest byte." + }, + "length": "integer", + "description": "the number of platform contribution buckets before bit packing." + }, + }, + "required": ["version", "histogram", "platformHistogram"] +} +``` + +Each histogram bucket value is encoded as a bit in a CBOR byte string ("buckets" byte strings in histogram and platformHistogram objects). Histogram bucket 0's value will be indicated by the most-significant bit in the first byte of the CBOR byte array. For example, [1,0,0,0,0,0,1,1, 1] will be packed to [0x83, 0x80] (or [131, 128]). See the "Data Notations" section of https://www.rfc-editor.org/rfc/rfc1700 for more information. + +Example: +```json +{ + "version": 1, + "histogram": {"buckets": [0x08, 0x09, …, 0x00, 0x84], "length": 1024}, + "platformHistogram": {"buckets": [0x90], "length": 4}, +} +``` + +In this example above, `histogram` field's buckets vector length is 128, and the number of buckets before bit packing is 1024. `platformHistogram`'s buckets vector is [1,0,0,1] (0x90 = 144 = 0b10010000) before bit packing. ## Histogram contributions and the RAPPOR noise algorithm -Each participant in the auction will maintain a histogram with a fixed number of buckets. Initially, we are proposing to fix the number of buckets to 1024, which was chosen both for system health reasons (bandwidth, etc), but also to discourage the measurement of fine-grained events, which are not easily compatible with strong local privacy protections. Callers that need fewer than 1024 buckets should just ignore the unused fraction of the histogram. +Each participant in the auction will maintain a histogram with a fixed number of buckets. Initially, the API fixes the number of buckets to 1028 (1024 regular contribution buckets and 4 platform contribution buckets), which was chosen both for system health reasons (bandwidth, etc), but also to discourage the measurement of fine-grained events, which are not easily compatible with strong local privacy protections. Callers that need fewer than 1028 buckets should just ignore the unused fraction of the histogram. Each auction participant can contribute a single time to this histogram from an auction. If an auction participant calls `contributeToRealTimeHistogram()` multiple times, one of those contributions is selected based on the `priorityWeight` param in the above API, and at the end of the auction a contribution is selected via weighted sampling. The `priorityWeight` param, required to be a [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) greater than zero and less than infinity, dictates the relative likelihood of which bucket will get the contribution, so the priority for critical error types should be set significantly higher than other error types. For example, if an auction participant calls `contributeToRealTimeHistogram()` twice, once contributing to bucket 123 with a `priorityWeight` of 0.1 and once contributing to bucket 456 with a `priorityWeight` of 0.2, then their actual contribution from the auction has a $^1/_3$ chance of being in bucket 123 and a $^2/_3$ chance of being in bucket 456. @@ -135,10 +194,9 @@ buyer2.example: [0, 0, 0, 0, 0, ..., 0, 1, 0] buyer3.example: [0, 0, 0, 0, 0, ..., 0, 0, 0] buyer4.example: [1, 0, 0, 0, 0, ..., 0, 0, 0] seller.example: [0, 0, 0, 0, 0, ..., 0, 0, 1] - ``` -i.e. each auction participant holds a bit vector of length 1024, with at most one “1”. This encoding is important to describe the noise mechanism: RAPPOR ([Erlingsson et al 2014](https://research.google/pubs/rappor-randomized-aggregatable-privacy-preserving-ordinal-response/)). Basic RAPPOR noises each coordinate of the bit vector independently, and it is parameterized by `epsilon`, a measure of privacy loss. Here is an reference python implementation of the algorithm: +i.e. each auction participant holds a bit vector of length 1028, with at most one "1". This encoding is important to describe the noise mechanism: RAPPOR ([Erlingsson et al 2014](https://research.google/pubs/rappor-randomized-aggregatable-privacy-preserving-ordinal-response/)). Basic RAPPOR noises each coordinate of the bit vector independently, and it is parameterized by `epsilon`, a measure of privacy loss. Here is an reference python implementation of the algorithm: ```python import math @@ -160,9 +218,9 @@ def rappor(epsilon: float, value: int, histogram_length: int): return out ``` -In this proposal, we propose an epsilon value of 1 yielding a flipping probability of ~0.378 (value of f of ~.755). +For this API, we set an epsilon value of 1 yielding a flipping probability of ~0.378 (value of f of ~.755). -The very large flipping probability is the essential privacy protection of this proposal. In the report sent to the auction participant, probably around 370 to 400 of the 1024 buckets will be 1s. Moreover the actual bucket to which the auction contributed a 1 also has a 37.8% chance of its value being flipped, to a 0. +The very large flipping probability is the essential privacy protection of this proposal. In the report sent to the auction participant, probably around 370 to 400 of the 1028 buckets will be 1s. Moreover the actual bucket to which the auction contributed a 1 also has a 37.8% chance of its value being flipped, to a 0. This means that data returned from this mechanism should not be interpreted as-is, because it will be heavily biased from all the random flipping. Rather than interpreting directly, individual reports should be aggregated and then subsequently debiased with the following reference code: @@ -187,9 +245,9 @@ def debias_rappor(epsilon: float, N: int, histogram: Sequence[int]): The standard deviation ($\sigma$) from true count can be calculated using the formula, $\sigma \approx 2\sqrt{N}$, where N is the number of contributions. Measuring deviation at $2\sigma$ from true count will give the 95% confidence interval for errors. -This API’s primary goal is to help adtechs detect that errors are occurring and may need debugging. Due to the noisy contributions and probabilistic nature of the `priorityWeight` param, the count and frequency of these errors are not exact. For this reason, we recommend interpreting the output as a means to identify deviation from trends rather than representative counts for the frequency of contributions. Let’s consider an example: +This API’s primary goal is to help ad techs detect that errors are occurring and may need debugging. Due to the noisy contributions and probabilistic nature of the `priorityWeight` param, the count and frequency of these errors are not exact. For this reason, we recommend interpreting the output as a means to identify deviation from trends rather than representative counts for the frequency of contributions. Let’s consider an example: -Suppose an adtech receives 1M reports in 5 minutes, and Bucket 4’s count is 390,000. We can estimate the number of times an auction actually contributed to Bucket 4 using the formula (390000 - 1000000 * .755/2) / (1-.755), which gives us an estimate of approximately 51,000 contributions to Bucket 4 in this example. The standard deviation is approximately $2\sqrt{1M} = 2000$, so the 95% confidence interval is around 4000 on either side of the estimate. The adtech can be highly confident that of the 1M auctions that sent a report, the number that contributed their 1 to Bucket 4 is somewhere between 47,000 and 55,000, i.e. between 4.7% and 5.5% of the auctions. +Suppose an ad tech receives 1M reports in 5 minutes, and Bucket 4’s count is 390,000. We can estimate the number of times an auction actually contributed to Bucket 4 using the formula (390000 - 1000000 * .755/2) / (1-.755), which gives us an estimate of approximately 51,000 contributions to Bucket 4 in this example. The standard deviation is approximately $2\sqrt{1M} = 2000$, so the 95% confidence interval is around 4000 on either side of the estimate. The ad tech can be highly confident that of the 1M auctions that sent a report, the number that contributed their 1 to Bucket 4 is somewhere between 47,000 and 55,000, i.e. between 4.7% and 5.5% of the auctions. As the API will send reports soon after the auction is completed, the only delay in measuring aggregates comes from aggregating reports after the fact. The cadence and grouping of aggregation is entirely up to the API user, and due to the local noise any report can be queried or analyzed an unlimited number of times. For example, running the de-noising calculation on 1 minute's worth of reports, rather than waiting to collect for 5 minutes, will give estimates 4 minutes sooner, but at the cost of relatively more noise: the size of the 95% confidence interval will be approximately $\sqrt{5} \approx 2.2$ times as wide. @@ -225,11 +283,11 @@ Given that sellers have significant existing control over the auction, we deemed ## Privacy considerations -At the `epsilon` we are proposing ($\epsilon$ = 1), the entropy leaked is limited to approximately 0.18 bits per auction. This makes it very difficult for a bad actor to gain any meaningful user identifying information from an auction using this API. +At the `epsilon` we are proposing ($\epsilon$ = 1), the information leaked is limited to approximately 0.18 bits per auction[^1]. This makes it very difficult for a bad actor to gain any meaningful user identifying information from an auction using this API. While the tight privacy parameters provide strong protections, there are two privacy considerations of note: -* It reveals a small amount of information from scoreAd and generateBid to sellers and bidders, respectively. These contents are protected by the locally differentially private RAPPOR algorithm. The scope of this risk can be measured with the privacy loss epsilon parameter. This risk could be magnified by a bad actor running many auctions solely for the purpose of collecting more information from a publisher page. At launch, we plan to mitigate this risk by bounding the number of contributions that this API will send to an adtech from a page in a given period of time for each browser. +* It reveals a small amount of information from scoreAd and generateBid to sellers and bidders, respectively. These contents are protected by the locally differentially private RAPPOR algorithm. The scope of this risk can be measured with the privacy loss epsilon parameter. This risk could be magnified by a bad actor running many auctions solely for the purpose of collecting more information from a publisher page. We mitigate this risk by bounding the number of contributions that this API will send to an ad tech from a page in a given period of time for each browser. It’s set to 10 reports per reporting origin per page per 20 seconds (per browser). * It reveals to the ad tech the fact that it had an interest group present on the device. This is mitigated by the fact that the reports do not contain any information about which auction triggered them, and the report is heavily noised. We also considered sending reports to all *eligible* auction participants for a given auction (i.e. all those present in `interestGroupBuyers`, even if they do not have interest groups), but this will result in an overwhelming number of reports sent. We plan to address both these considerations in [future work](#limitations-and-future-work). @@ -253,14 +311,14 @@ The downsampled reporting is better suited for root-cause analysis. We could consider extending the [Private Aggregation API](https://developers.google.com/privacy-sandbox/relevance/private-aggregation) rather than introducing a new API surface for real-time bid reporting. There were a few reasons we chose a new API: -1. Due to the random client-side delay, adtech time to accumulate reports and batch processing time, PAA reporting SLA may not be brought down to the real time nature of reporting for monitoring required by some adtechs. Reducing the delay is possible but would likely require sending more null reports to satisfy the privacy requirements. +1. Due to the random client-side delay, ad tech time to accumulate reports and batch processing time, PAA reporting SLA may not be brought down to the real time nature of reporting for monitoring required by some ad techs. Reducing the delay is possible but would likely require sending more null reports to satisfy the privacy requirements. 1. PAA is designed for a very large domain of keys in its underlying histogram, whereas the RAPPOR mechanism cannot readily support such large domains due to bandwidth / computation constraints. 1. Utilizing PAA could interfere with existing PAA privacy budgets, and it isn’t clear exactly how to share budgets across central and local mechanisms. ## Limitations and future work -The proposal suggested in this document is limited in scope due to the strong privacy guarantee and use of local noise. In the future, we plan on exploring alternative architectures such as shufflers or trusted servers to improve utility without compromising on privacy or the real-time requirement. In particular, we are exploring two solutions with the following goals: +The API is limited in scope due to the strong privacy guarantee and use of local noise. In the future, we plan on exploring alternative architectures such as shufflers or trusted servers to improve utility without compromising on privacy or the real-time requirement. In particular, we are exploring two solutions with the following goals: * Decrease the amount of total noise * Allow for measuring larger and more fine-grained histograms @@ -268,3 +326,5 @@ The proposal suggested in this document is limited in scope due to the strong pr * Address the privacy concern that this proposal leaks the auction participation The two solutions we are considering are 1) add a local shuffle and IP address proxy mechanism to the Real Time Monitoring API, or 2) replace the Real Time Monitoring API with a low-latency Private Aggregation solution. + +[^1]: Wang, Shaowei et al. “Mutual Information Optimally Local Private Discrete Distribution Estimation.” https://arxiv.org/abs/1607.08025 (2016): Theorem 2.1 diff --git a/assets/fledge_kv_server_v2_api.png b/assets/fledge_kv_server_v2_api.png index 88c1d60aa..12acd33c6 100644 Binary files a/assets/fledge_kv_server_v2_api.png and b/assets/fledge_kv_server_v2_api.png differ diff --git a/assets/fledge_kv_server_v2_req_structure.jpeg b/assets/fledge_kv_server_v2_req_structure.jpeg index 1b9181ad9..edd87a640 100644 Binary files a/assets/fledge_kv_server_v2_req_structure.jpeg and b/assets/fledge_kv_server_v2_req_structure.jpeg differ diff --git a/fledge-tester-list.md b/fledge-tester-list.md index 19c7634ce..8ffad7853 100644 --- a/fledge-tester-list.md +++ b/fledge-tester-list.md @@ -86,6 +86,8 @@ The usefulness of this page depends on testers sharing information and updates. | Media.net | SSP & DSP | Testing in progress | | privacysandbox@media.net | | Kargo | SSP | H2 2024 | | privacysandbox@kargo.com | | Lucead | DSP & SSP | Testing in progress | | privacysandbox@lucead.com | +| Optable | DSP & SSP | Testing in progress | | privacysandbox@optable.co | +| Taboola | DSP & SSP | Testing in progress | | privacy-sandbox@taboola.com | ## Table - Publishers and Advertisers Interested in Testing or Early Adoption Companies who may be interested in participating in tests and early adoption opportunities provided by ad tech companies. @@ -103,4 +105,4 @@ Companies who may be interested in participating in tests and early adoption opp | TNL Mediagene | Publisher | | privacysandbox@tnlmediagene.com | | Ringier Axel Springer Poland | Publisher | | privacysandbox@ringieraxelspringer.pl | | Bullwhip Technologies | Analytics SaaS | | privacysandbox@bullwhip.io | - +| Mintegral (Mobvista) | SSP & DSP | Test Privacy Sandbox | partnerships@mobvista.com | diff --git a/meetings/2024-05-15-FLEDGE-call-minutes.md b/meetings/2024-05-15-FLEDGE-call-minutes.md new file mode 100644 index 000000000..a0ee004ad --- /dev/null +++ b/meetings/2024-05-15-FLEDGE-call-minutes.md @@ -0,0 +1,249 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday May 15, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + + + +1. Paul Jensen (Google Privacy Sandbox) +2. Brian May (Dstillery) +3. Sven May (Google Privacy Sandbox) +4. Orr Bernstein (Google Privacy Sandbox) +5. Wojciech Biały (Wirtualna Polska Media) +6. David Eilertsen (Remerge) +7. Andrew Pascoe (NextRoll) +8. Harshad Mane (PubMatic +9. Shafir Uddin (Raptive) +10. Patrick McCann (Raptive) +11. Alex Cone (Google Privacy Sandbox) +12. Aymeric Le Corre (Lucead) +13. Wendell Baker (Yahoo) +14. Antoine Niek (Optable) +15. Garrett McGrath (Magnite) +16. Jacob Goldman (Google Ad Manager) +17. Yanay Zimran (Start.io) +18. Russ Hamilton (Google Privacy Sandbox) +19. Abishai Gray (Google Privacy Sandbox) +20. David Tam (Relay42) +21. Laura Morinigo (Samsung) +22. Brian Schmidt (OpenX) +23. Warren Fernandes (Media.net) +24. Matt Davies (Criteo | Bidswitch) +25. Sathish Manickam (Google Privacy Sandbox) +26. Alexandre Nderagakura\* +27. Elmostapha BEL JEBBAR (Lucead) +28. Michael Kleber (Google Privacy Sandbox) (second half only) +29. Alonso Velasquez(Google Privacy Sandbox) + + +## Note taker: Orr Bernstein + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Patch’y Updates When on Joining Origin: https://github.com/WICG/turtledove/issues/1162 + * Attestation Process https://github.com/privacysandbox/attestation/issues/53 + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 +* Warren Fernandes + * Tweak to the signalValue returned by aggregate reporting functions to support cross-component auction loss reporting: https://github.com/WICG/turtledove/issues/1161 + * Scope reductions to the analytics entity proposal discussed last week: https://github.com/WICG/turtledove/issues/1115 \ + +* Patrick McCann + * InterestGroupBuyers as promise: https://github.com/WICG/turtledove/issues/1093 + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + + +# Notes + + +## Warren Fernandes: Scope reductions to the analytics entity proposal discussed last week: https://github.com/WICG/turtledove/issues/1115 + + + +* Warren Fernandes + * Linked a version of the doc that’s scoped down, has just the aggregate-only version of the prior proposal. https://docs.google.com/document/d/1j1OZlrU1OTMmUaqt8OzidZzNOHsJezAfTy2athIij0I/edit#heading=h.xhlrw7f4erxq + * The prior proposal - two new functions + * Event level reporting for auctions + * Captures other data at an aggregate level + * The new proposal has just the second function + * First table suggests comparison to prebid analytics +* Paul Jensen + * List of events, aggregated + * What’s are the metrics you’re interested in learning about at that time? +* Warren + * Function receives these events, can do analytics reporting using the standard aggregated reporting mechanism +* Paul + * So, there’s a piece of JavaScript that receives one of these for each event type. +* Warren + * Suggested implementation section in the document. + * New analytics.js file + * /.well-known/pa-api-analytics/permissions would include the list of analytics to be reported by the auction + * The entity provides its own JS. It has one function in it: reportOutcome + * A couple of cases + * Could have a winner, in which the structure referenced in the doc is provided. + * Uses the standard histogram function to send these over to private aggregated service + * Each component seller’s winning bid is provided to the reportOutcome function, both the winning component seller and all other component sellers +* Paul + * The analytics script would only be able to exfiltrate using the aggregation API? +* Warren + * Correct. +* Paul + * Seems very seller-specific +* Patrick McCann + * This would be helpful for us, but seems like it maybe even has too much detail. +* Paul + * Some of this data is very internal too; for example, desirability may only be meaningful to the seller. Some of this is already available through aggregation - the browser already exports it to private aggregation. +* Patrick + * But the publisher can’t report it. +* Paul + * Say someone can already run JavaScript; a lot of this is information they can already report back. +* Warren + * Also reports information on bids that didn’t happen, for example, “noBidReason: timeout”. When an entity doesn’t provide permission to provide these analytics, would be conveyed in the reportOutcome call. +* Brian May + * Seems worthwhile to prioritize aggregates by what’s most valuable. + * What keys do you propose? +* Warren + * Not proposing a specific set of keys, just a mechanism to get that data to new entities, who can then report it however they want. +* Patrick + * MVP would be event counting. Just knowing how often each entity was activated. +* Paul + * By activated, do you mean bidding/scoring, or winning? +* Patrick + * Both. An analytics provider could run an aggregation API in a TEE and also count how often various partners activated and won. +* Wojciech Bialy + * Looking at those events from publisher POV, what’s important to us is that this provides some mechanism for cost reporting. Winning ask, and the ones that started to deliver. “noBidReason: timeout” is possible to discern for top-level seller. If possible, would like to see the difference between bid won and bid delivered. Cannot discern what cases the auction won but the ad was not attached to the page. + * Do you see this being called once with all of the events from the auction, or once per event? +* Warren + * Proposing that it’s called once, so that it can be aggregated. Also, if it’s firing in a worklet, avoid having to fire up a new process. +* Matt Davies + * Excellent proposal. Would really help us. Slightly different use case. Other side of the equation from the publisher - entity in the middle - but also need this same information. +* Paul + * How would you envision connecting to this flow? Would you be one of the analytics providers that publishers would list? +* Matt + * Don’t know; separate URL. Maybe Bidswitch could indicate that we were involved in this auction on the RTB side. Figuring out at the moment how to do this with network calls and that sort of stuff, but those loopholes will be closed very quickly. Would also be useful to others who are not integral to the component auction. At this stage we seem to be locked out because we can’t run a component auction, and we’re not the top-level seller, so there’s nowhere else to go. +* Warren + * One of the issues as an SSP with implementing Bidswitch’s proposal is that we can’t make multiple calls to the reporting. With this, would be far more straightforward as an SSP to support them directly. +* Paul + * There’s potentially often a lot more losing bids than winning bids - orders of magnitude more. Might get expensive to run analytics scripts for each person who participated. +* Warren + * reportResult might help. +* Paul + * Only for winning bids. +* Matt + * Would be willing to start from winning bids and impressions alone, can revisit lost bids later on. +* Paul + * When you asked about multiple calls to sendReport, do you mean more data to the same origin, or calls for different origins? +* Warren + * Neither would work. +* Brian May + * If you send analytics for every bid +* Paul + * Sounds a lot more expensive to make a lot of calls. +* Brian May + * Didn’t mean to invoke this function for every bid, but rather aggregate that information for the one call. +* Michael Kleber + * Is there some version of this in which information is written into SharedStorage, and then some separate process runs aggregation and sends an aggregate report? On-device coalescing of lots of different losing bids. Avoids the problem of flooding with lots of information from the same browser. And allows for some opt-in for those who want to be notified. +* Warren + * I’ve thought of this, don’t know if from a functional standpoint, if this is very different. This would work, though. +* Patrick + * Agree with Warren that this would work for us too. +* Paul + * The difference seems to be the .well-known file that provides permissions, could be the SharedStorage option. +* Michael + * Publisher .well-known file could tell the browser which entities’ information we should record, and which JavaScript we should run for reporting. +* Paul + * A .well-known file would mean making two fetches for every document, may be a performance issue. But something in the HTML or the auction config that says, go fetch from the .well-known instead of issuing it on lots of sites that have no analytics may be more performant. +* Patrick + * Publisher won’t necessarily know that Bidswitch should be included in their auction config. Maybe it’s a different proposal, but the shared storage write proposal maybe solves the Bidswitch use case and publisher use case, while calling something with a reportResult use case may not solve the Bidswitch use case. +* Matt + * Yes, would need some way of declaring that Bidswitch is involved. +* Paul + * Often more performant to the browser if we can use a declarative form. For example, if we could say, report to aggregation if this event happens. +* Matt + * Something like that would be very useful. Do need to track the impression events. We should contribute to this histogram if something like this happens. +* Paul + * Tricky thing is to know how some entity X wants that, and not somebody else asserts that entity X wants that. +* Warren + * Wondering if you had thoughts of a mechanism for how publishers can indicate which entities should have access to this information in shared storage? Unlikely that a publisher has relationship with Bidswitch. +* Michael + * People who come to the data gathering role not because the publisher brings them along but because they’re involved with some other entity in the auction would have some more limited visibility to the auction. Would presume would have visibility scoped to the entity they have the relationship with. For example, someone who comes in via association into a buyer in the auction would have visibility to that buyer’s bids but not other buyers’. +* Brian May + * Are you suggesting that a subset of data is available to downstream partners? Limited to visibility of partner who brought them to the table, and some general signals available to everyone in the auction. +* Patrick + * Proposed future state, right? +* Michael + * Yes, we don’t have information available to publisher today, that goes through top-level seller. +* Paul + * Makes me think about iframes, how they prevent leaking information out of that frame. Aggregation service to be the medium there. +* Patrick + * Michael’s proposal doesn’t call to aggregation; instead, writes to Shared Storage, and then an entity can call to aggregation. +* Michael + * Two-sided opt-in kind of situation to ensure that nobody’s information is given away. +* Warren + * Any reason we currently can’t do multiple calls to some kind of sendReport option, as a seller? +* Paul + * Not sure we’ve heard a lot of ask for that before. Is it about sending more data, or sending data to more people? +* Warren + * About sending to more people in your supply chain, if you want. +* Paul + * May use more device upload bandwidth, versus using more server-to-server bandwidth, which is cheaper. +* Warren + * That works also. +* Michael + * This sounds like a different conversation; we were talking about aggregated reporting, and now we’re talking about event-level reporting. For buyers, we already support what you’re asking for buyers, who can indicate other domains who can get win type and events-in-the-creative type reports. Don’t have something like that on the sell-side; the solution we have right now is focused on the buy-side. +* Patrick + * So, if buyer is connected to A directly and B indirectly, how would they convey to send to B? +* Paul + * Could include that in their bid directly. + * It sounds like the next steps are, think about what an efficient registration mechanism would be for this. .well-known file, HTTP response header, HTML/JavaScript. What has good ergonomics for publishers? We’ve heard HTTP headers can be hard. + * And thinking about how to connect this to Shared Storage, so we don’t have to create another new reporting mechanism. + * Also, how are we getting permissions right, which would be a security vulnerability. + * And we talked about, boiling down the set of desired things into a prioritized list. Pat mentioned that just learning about which sellers were activated was a top thing on his side. +* Patrick + * Could component sellers write to any entity? Does the publisher indicates an analytics party in the well-known? +* Paul + * That’s the question framed above; .well-known file, HTTP response header, HTML/JavaScript. If it’s a runAdAuction argument, don’t know how we would pipe that into an iframe. How does the publisher dictate the analytics providers, and how do auction participants indicate that they’re OK with sharing that information. Might have to talk to security folks to see whether wildcard could be useful. +* Brian May + * Sounds like we have a general purpose mechanism we want to enable that can give service providers access to data written to shared storage when there is an agreement between the provider and the entity they’re providing services and the entity can indicate what data can be written to storage accessible by their provider. Would need to be a handshake between the principal and the service provider and that could be an opportunity to load the service provider scripts. +* Paul + * Also, want to make sure that the person who’s writing into shared storage has permission to do so. +* Michael + * Yes, need the two-sided opt-in +* Warren + * Would like to push back on having this in runAdAuction. Something the top-level seller would have to add, instead of the publisher. +* Patrick + * No technical requirement that person who calls runAdAuction is the top-level seller. But I agree with the sense of the idea. +* Brian May + * To Paul’s earlier suggestion of capabilities being communicated between participants, it would be helpful in making partnership decisions if there was a standard means by which participants could identify the capabilities they were willing to share with partners. diff --git a/meetings/2024-05-22-FLEDGE-call-minutes.md b/meetings/2024-05-22-FLEDGE-call-minutes.md new file mode 100644 index 000000000..5cf79b3e6 --- /dev/null +++ b/meetings/2024-05-22-FLEDGE-call-minutes.md @@ -0,0 +1,130 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday May 22, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + + + +1. Paul Jensen (Google Privacy Sandbox) +2. Michael Kleber (Google Privacy Sandbox) +3. Brian May (Dstillery) +4. Wojciech Biały (Wirtualna Polska Media) +5. Harshad Mane (PubMatic) +6. Garrett McGrath (Magnite) +7. Miguel Morales (TechLab) +8. Sven May (Google Privacy Sandbox) +9. Matt Davies (Bidswitch | Criteo) +10. Matt Menke (Google Chrome) +11. Laura Morinigo (Samsung) +12. Matthew Atkinson (Samsung) +13. Alexandre Nderagakura (Not affiliated / consulting) +14. Laurentiu Badea (OpenX) +15. Brian Schmidt (OpenX) +16. Isaac Foster (Msft ads) +17. Konstantin Stepanov (Microsoft Ads) +18. Pat McCann (Raptive) +19. Sarah Harris (Flashtalking) +20. Jacob Goldman (Google Ad Manager) +21. David Dabbs (Epsilon) +22. Fabian Höring (Criteo) +23. Arthur Coleman (IDPrivacy/ThinkMedium) +24. Paul Spadaccini (Flashtalking) +25. Yanay Zimran(Start.io) +26. Pawel Ruchaj (Audigent) +27. Aymeric Le Corre (Lucead) +28. Victor Pena (Google Privacy Sandbox) +29. Courtney Johnson (Privacy Sandbox) +30. Shafir Uddin (Raptive) +31. Elmostapha BEL JEBBAR (Lucead) +32. Alonso Velasquez (Google Privacy Sandbox) +33. Rickey Davis (Flashtalking) +34. Abishai Gray (Google Privacy Sandbox) +35. Alex Peckham (Flashtalking) +36. Andrew Pascoe (NExtRoll) +37. Stan Belov (Google Ad Manager) +38. Taranjit Singh (Jivox) +39. Sid Sahoo (Google Chrome) + + +## Note taker: Victor Pena + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Patrick McCann + * InterestGroupBuyers as promise: https://github.com/WICG/turtledove/issues/1093 \ + +* Isaac Foster: + * Patch’y Updates When on Joining Origin: https://github.com/WICG/turtledove/issues/1162 + * Attestation Process https://github.com/privacysandbox/attestation/issues/53 + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + + +# Notes + + +## Patrick McCann, InterestGroupBuyers as promise: https://github.com/WICG/turtledove/issues/1093 + + + +* Patrick McCann raises issue 1093. Prebid SSPs want to have access to contextual signals. + * Additionally, asking for a requirement to list all entities in auction for component sellers to block a buyer prior to auction. As a solution to hardcode logic as an SSP. + * Wojciech B. asks if this is a single-level auction, Patrick confirms his view is only for component auctions. + * Patrick says Component auctions promises or auction requirements are unchanged. + * Wojciech says can a solution stagger the component auctions to different points of time to parallelize the auction. + * Patrick says entities have hard coded interest groups (IGs) then you have to figure out who are the IG buyers which would be challenging + * Patrick says his view is that component auctions would run and then another top level auction would run. + * M Kleber says Privacy Sandbox reviewed a similar solution previously as an auction blob that could be fed in as a component of another auction. It is not the architecture PS uses. + * Michael recaps issue 1093 and provides a potential solution design as a response to 1093’s problem statement + * kleber: I think to achieve this goal you could: + * start the auction, with each seller listing a maximal set of all the buyers it works with. This is compatible with how prebid works. + * each component seller could at some later time decide which buyers it wants to accept bids from, and pass that information into the auction via a Promise in its own sellerSignals + * Then in that buyer's scoreAd(), they could quickly reject any bids from buyers that they decided to exclude in this particular auction + * This would give the right _auction winner_ and is something that could be done with the API as-is. However, Pat is quite right that this would be somewhat inefficient because it might result in buyers running bidding logic to produce doomed bids. So from the latency point of view, there is room for improvement here, even if the outcome is possible to achieve today. + * Isaac Foster says they want to improve efficiency during auction like loading buyer IGs earlier. + * Michael says maybe this is not parallelism, instead it is about starting to load earlier. + * Isaac adds detail to how SSPs view the auction for example want to increase buyer counts and invite them but can skip buyers lacking proper signals. + * Patrick says maybe don't need a change in the auction config…change the decision logic like avoid downstream bidding functions, like kleber's suggestion + * Michael says component seller kicks off auction with maximum set of buyers. Component seller identifies or restricts to a smaller list of buyers. If the browser understands that restriction, then we have the opportunity to optimize the later set of buyers: might be able to skip the bidding step for bids that won't ever have a chance to win, and might be able to skip the scoring step if the seller has already told us they plan to reject the bid based on the buyer, so end up scoring fewer bids. + * The seller can pass signals to its own scoring logic and also there is an opportunity to share this signal in a way the browser understands which makes those additional latency optimizations possible. + * Paul says maybe it is a solution to set buyer timeouts to zero? This avoids wasting time for a buyer who the seller decided they don’t want to be part of the auction. This might do a good job of conserving those resources already today, even without us shipping any new feature in the browser. +* Michael says we have a limited 30 minute session. We are going to other hands up. +* Yanush Piskevich - Another architecture would be to run whole auctions in parallel, and have the output of one auction be able to feed into another later auction. Would that do a better job with parallelization? + * Michael says we don't plan to stitch together multiple on browser auctions, we spent time talking about this design decision a couple of years ago. If that turns into a desirable feature then we might consider +* Brian May - There is a concern with leakage if buyer timeouts are set to zero and if a better solution would be a single standard that all entities can rely on. Question whether an exclusion or inclusion list would be wanted, assume the latter, but maybe we allow it to be defined with the list. As with any feature that allows modification of the auction dynamics, we’d want to have reporting for this feature so we could assess the impact it is having in cases where the bidding performance isn’t what we expect. +* Michael says we could do it with either an allow or deny list in syntax, and we don’t need a full solution design today +* David Dabbs - The mechanism should not interfere with a seller’s ability to randomize buyer order for fairness, as recommended by the [Implementation Overview](https://github.com/WICG/turtledove/blob/main/PA_implementation_overview.md). The buyer set changing mid-auction has interplay with buyer eligibility and updateURL fetching. Buyer(s) ‘speculatively’ added at the start then omitted would never have been eligible. Michael says this is a good point that we will need to consider + +Meeting ended promptly after 30 minutes diff --git a/meetings/2024-05-29-FLEDGE-call-minutes.md b/meetings/2024-05-29-FLEDGE-call-minutes.md new file mode 100644 index 000000000..b7a863b72 --- /dev/null +++ b/meetings/2024-05-29-FLEDGE-call-minutes.md @@ -0,0 +1,162 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday May 29, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + +1. Michael Kleber (Google Privacy Sandbox) +1. Aymeric Le Corre (Lucead) +1. Brian May (Dstillery) +1. Harshad Mane (PubMatic) +1. David Eilertsen (Remerge) +1. Matt Kendall (Index Exchange) +1. Kevin Lee (Google Privacy Sandbox) +1. Laurentiu Badea (OpenX) +1. Jacob Goldman (Google Ad Manager) +1. Anthony Yam (Flashtalking) +1. Leeron Israel (Google Privacy Sandbox) +1. Sven May (Google Privacy Sandbox) +1. David Dabbs (Epsilon) +1. Alexandre Nderagakura (Independent / consulting) +1. Rickey Davis (Flashtalking) +1. Alex Cone (Google Privacy Sandbox) +1. Yanay Zimran (start.io) +1. Garrett McGrath (Magnite) +1. Omri Ariav (Taboola) +1. Abishai Gray (Google Privacy Sandbox) +1. Scott Myers (Google Privacy Sandbox) +1. Paul Jensen (Google Privacy Sandbox) +1. JASON LYDON (MO) +1. Becky Hatley (Flashtalking) +1. Manny Isu (Google Privacy Sandbox) +1. Tim Taylor (Flashtalking) +1. Luckey Harpley (Remerge) +1. Courtney Johnson (Privacy Sandbox) +1. Orr Bernstein (Google Privacy Sandbox) +1. Paul Spadaccini (Flashtalking) +1. Sarah Harris (Flashtalking) +1. Isaac Foster (MSFT Ads) +1. Fabian Höring (Criteo) +1. Matt Davies (Criteo | Bidswitch) +1. Caleb Raitto (Google Chrome) +1. Felipe Gutierrez (Microsoft) +1. Tamara Yaeger (Criteo | BidSwitch) +1. Shivani Sharma (Privacy Sandbox) +1. Matthew Atkinson (Samsung) +1. Brian Schneider (Privacy Sandbox) +1. Alonso Velasquez (Privacy Sandbox) +1. Elmostapha Beljebbar (Lucead) +1. Sathish Manickam (Google Privacy Sandbox) +1. Shafir Uddin (Raptive) +1. Warren Fernandes (Media.net) +1. Tal Bar Zvi (Taboola) +1. Kevin Nolan (NextRoll) +1. Andrew Pascoe (NextRoll) +1. Koji Ota(CyberAgent) + + +## Note taker: Manny Isu + +# Agenda +## Process reminder: Join WICG +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + +## Suggest agenda items here: + +* Leeron Israel: + * Deals support in Protected Audience: https://github.com/WICG/turtledove/issues/873#issuecomment-1994888034 + + +* Isaac Foster: + * Patch’y Updates When on Joining Origin: https://github.com/WICG/turtledove/issues/1162 + * Attestation Process https://github.com/privacysandbox/attestation/issues/53 + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + +* Omri Ariav: + * Follow up on main blockers for native advertising ([one](https://github.com/WICG/turtledove/issues/1074), [two](https://github.com/WICG/turtledove/issues/741), [three](https://github.com/WICG/turtledove/issues/1096)) + * Follow up on easing domain restrictions (https://github.com/WICG/turtledove/issues/956) + + * Warren Fernandes + * Follow up on the proposal to support an analytics entity (https://github.com/WICG/turtledove/issues/1115) + + +# Announcements +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +# Notes +## (Leeron Israel) Deals support in Protected Audience: https://github.com/WICG/turtledove/issues/873#issuecomment-1994888034 +* This is a proposal to support deals in PA API understanding that it is an important use case for all of you. We propose to use an existing field - buyer and seller reporting ID to store deal IDs to allow the buyer in generatebid to report in score ad and reportwin. This will satisfy the vast majority of use cases. Is anyone familiar with it or have any thoughts to share with the group? +* [Isaac] Is there a change to the API? +* [Leeron] Yes, there are a few changes we plan to make, 1) Modify buyer and seller reporting ID to accept an array 2.) Enable generatebid to select specific value from that array 3.) Buyer and seller reporting id will be made available to scoread 4.) K anon - If ad does not pass the k anon threshold, the bid will be withdrawn. +* [Isaac] So this will make it easier to bid on - but this will not allow a situation where deal eligibility can be determined based on private signals, correct? +* [Leeron] My understanding is that when it comes to deals, there are 2 categories 1. Targeting 2. Commercial. Commercial can be pre agreed upon… but there might be cross site seller provided information. We are not making any changes to Targeting to accommodate in this design. The reason why it is not included is to keep the scope tight. +* [Isaac] Would like to think through this, but I see some of it to be valuable. I think it’s a positive +* [Kleber] It is worth pointing out that the attribute that says this is an essential piece of information is already available today by putting it in the URL; subject to K anon… +* [Isaac] Understood. To the extent that we can reduce the number of places we have to persist things… +* [David] So the source of the list of deal structure has to come from the attributes of the ads, correct? +* [Leeron] Yes +* [David] So my sense of deals today is that I am going to get a bag of deal IDs specific to me that will change from… not grocking how to embed all the various deals. Maybe I am missing something +* [Leeron] Today, deals are created on the SSP side… someone on the buy side needs to setup targeting on a campaign for a specific deal. The change here is that the association needs to be stored in the IG (on device) +* [Isaac] Agree with David but guess you may be calling out a challenge with the proposal or a challenge with predeclaration of your targeting in IG +* [David] Yes, precisely. +* [Leeron] One thing to call out - Deals in contextual flow will continue to work as they do today. But of the buyer wants to bring some IG information with deals to target an ad, it may reduce the subset of deals perhaps. But what is the specific technical issue? +* [David] The deals I have to put on my creatives may be unwieldy… need to read this some more +* [Kleber] Would you feel differently if the list of possible deal ids were an attribute of the entire IG? +* [David] Might be but need to get over the conceptual mismatch from associating deals from a seller +* [Isaac] You choose to target a deal using your campaign and it gives you access to private marketing or pricing. The effect of the deal id on your evaluation will be in scoread and generatebid. The targeting will be accomplished by grabbing your object graph and dumping it into the IG +* [Brian] So will you put your entire catalog of deals into the IG? +* [David] Not sure. Today we receive the deals on a bid request from the seller. Need to look at this and check in with my folks on it. Maybe the sue case I am thinking of does not apply here +* [Warren] Can a seller inject deals on a per buyer basis via the auction config perhaps? +* [Leeron] Technically that is possible today. The list of deals will be available in scoread but the key piece of information missing is the association between the deal and the bid. Our solution addresses that because we can do the k anon check in advance to make sure it can egress from the auction. +* [Warren] What we put into the per buyer signals tend to be what we… +* [David] We have an industry group working to fill in… one of the things is an opt in mechanism for the buyer to address exactly what you are describing. +* [Kleber] On the subject of unlocking inventory, it is worth pointing out that if the fundamental thing you are trying to do is for the seller to annotate the auction; if you use Deal ID 1 2 3 then you can unlock the inventory. If this is the problem you are trying to solve then a way of looking at the solution is… From the k anon pov, a single response for taking the seller up on their offer could be used on different pages. The notion that IG carries the deal id around with it might be useful in some situations. From the browser pov, we the browser can check the k anon properties before the auction starts. We cannot allow bids on arbitrary deal id is that there will be some privacy leakage. +* [Miguel Morales] We looked at Deal ID and did not realize that it is subject to k anon? +* [Leeron] Do not believe that any value passed into per buyer signals is subject to k anon check +* [Miguel] But the ad metadata is not subject to k anon +* [Leeron] Yes but it is also does not egress from the auction +* [Kleber] If you are willing to use PAgg to find out how often a deal got used, then that is also good. This is specifically about getting deal id into event level reports +* Note: Please review GH post https://github.com/WICG/turtledove/issues/873 and if there is anything else, please post on that GH + + + +## Omri Ariav: Follow up on main blockers for native advertising ([one](https://github.com/WICG/turtledove/issues/1074), [two](https://github.com/WICG/turtledove/issues/741), [three](https://github.com/WICG/turtledove/issues/1096)) +* [Omri] Taboola is a leading native advertising vendor with code on page on many publishers websites. We show sponsored and organic recommendations, usually in the form of an endless feed below the article. . We have some challenges with implementation. Is there any plan for a dedicated loop with Native? +* [Kleber] The focus to integrate information from publisher pages has been focused on the video use case. A lot of the answers being developed for video may be useful for native rendering. No primary focus recently, so you are right in that regard +* [Omri] Can we expect a bolder discussion of the native use cases in the foreseeable future? +* +* [Kleber] The look and feel is best suited for reusing some of the video discussions happening. +* [Alonso] Do you really mean targeting in bgeneral or do you mean that there is a specific targeting capability for the native use case? +* [Omri] It is the first one… having it working in the context of native (O.A: in native the amount of ad slots can be endless comparing to display) +* [Kleber] The negative targeting GH is Issue 1096. It sounds like the core thing you are looking for is… suppose that same buyer is going to show a bunch of different ads, you don’t run a single auction, you run a series of auction, one for each ad slot, and when you return a result, then you are dealing with having the second ad slot on the page to be aware of the first ad slot so you can skip that one and take the next one based on priority. Is that correct? +* [Omri] That is correct. +* [Kleber] It seems like this is something that we should be able to help with. Do you feel like frequency capping can help solve your issue? +* [Omri] Yes, we can look into it. Let’s work together to solve those use cases +* [Fabian] It is an interesting proposal. So the idea is to take out stuff that has already been displayed? +* [Kleber] Yes, that is correct. +* [Fabian] But then what about atomic …? +* [Kleber] Need to spend some time talking to the Chrome engineers working on the auctions to figure out how to add appropriate atomicity to make this work. We will need some time to think about this one +* [Paul] Which one of the issues is the highest priority? +* [Omri] Issue 1096 (Negative Targeting) and then Issue 1074 (Return Multiple Results) +* [Paul] Are those ads all from the same IG? +* [Omri] Not necessarily. As long as we keep it siloed per auction, if we will keep it one auction per slot it will result performance issues, many worklets running, and high infra costs to native advertising vendors +* [Omri] Ads may come from multiple advertisers as well as multiple IGs.. +* [Kleber] I think this is an area where we can design something better to support your use cases +* [Omri] we are willing to collaborate and help you in the design +* Follow up on easing domain restrictions (https://github.com/WICG/turtledove/issues/956) in the next meeting diff --git a/meetings/2024-06-05-FLEDGE-call-minutes.md b/meetings/2024-06-05-FLEDGE-call-minutes.md new file mode 100644 index 000000000..0e9d99543 --- /dev/null +++ b/meetings/2024-06-05-FLEDGE-call-minutes.md @@ -0,0 +1,253 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday June 5, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + + + +1. Jason Lydon (FT) +2. Paul Jensen (Google Privacy Sandbox) +3. Don Marti (Raptive) +4. Brian May (Dstillery) +5. Patrick Mccann (Raptive) +6. David Dabbs (Epsilon) +7. Garrett McGrath (Magnite) +8. Matt Menke (Google Chrome) +9. Sven May (Google Privacy Sandbox) +10. Matt Kendall (Index Exchange) +11. Roni Gordon (Index Exchange) +12. Isaac Foster (MSFT Ads) +13. Laurentiu Badea (OpenX) +14. Kenneth Kharma (OpenX) +15. Jacob Goldman (Google Ad Manager) +16. Aymeric Le Corre (Lucead) +17. Alexandre Nderagakura (Independent / Consulting) +18. Harshad Mane (PubMatic) +19. Jeroune Rhodes- Google Privacy Sandbox +20. Sid Sahoo (Google Chrome) +21. Alonso Velasquez (Google Privacy Sandbox) +22. Orr Bernstein (Google Privacy Sandbox) +23. Fabian Höring (Criteo) +24. Sathish Manickam (Google Privacy Sandbox) +25. Wendell Baker (Yahoo) +26. Alex Peckham (Flashtalking) +27. Abishai (Google Privacy Sandbox) +28. Victor Pena (Chrome) +29. Russ Hamilton (Google Privacy Sandbox) +30. Laura Morinigo (Samsung) +31. Tal Bar Zvi (Taboola) +32. Guillaume Polaert (Pubstack) +33. Arthur Coleman (IDPrivacy/ThinkMedium) +34. Pawel Ruchaj (Audigent) +35. David Tam (Relay42) +36. Yanay Zimran (Start.IO) +37. Koji Ota(CyberAgent) +38. Maybelline Boon (Google Privacy Sandbox) + + +## Note taker: Orr Bernstein + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 +* Tal Bar Zvi (instead of Omri Ariav for this meeting) + * Follow up on easing domain restrictions (https://github.com/WICG/turtledove/issues/956) + * (Roni) https://github.com/WICG/turtledove/pull/1156 resolves https://github.com/WICG/turtledove/issues/813 +* Warren Fernandes + * Follow up on the proposal to support an analytics entity (https://github.com/WICG/turtledove/issues/1115) +* Patrick McCann (_from Google Chat_) + * https://github.com/WICG/turtledove/pull/1156 + * Can we expect a reasonable cap on the \* at:[ ](https://github.com/WICG/turtledove/pull/1156/file)for number of features returned in `queryFeatureSupport('\*')`? + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +**Join Privacy Sandbox Developer Webinar: Protected Audience API Reporting** + +The Google Privacy Sandbox team will be hosting our next series of webinars on the Protected Audience API. This is the fourth series of webinars covering the Protected Audience API and in this session, we will continue from the previous session and learn about reporting. The first **Americas friendly session** is happening on** June 25th 3-4 pm ET**. A second **EMEA friendly session** is happening **June 26th 12-1 pm GMT**. + +To join, please register below: + + +* AMER-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-amer) + + +* EMEA-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-emea) + + +# Notes + + +## Patch’y Updates When on Joining Origin: https://github.com/WICG/turtledove/issues/1162 + + + +* Isaac Foster + * Today, if you join an IG (ignoring cross-site) on example.com, you put in - among other things - an updateUrl and possibly ads and other things. Later, the updateUrl is called. UpdateUrl semantics are patchy - only updates keys you specify. Later on, the user comes back to the page - the code on page, because it doesn’t know what IGs have been registered, what their state is, rejoins the IG. Could make calls to a service for coordination, but would be nice if you could do patchy update instead of overwriting everything at IG join. + * Has come up in two contexts - internally, and talking with some folks about delegation scenarios. Broadly relevant. + * Proposal: leverage the joining origin. Allow patchy semantics joinAdInterestGroup if it’s joined from the same joining origin. Could rejoin an IG and then build from there. + * Intention is that this would only be applicable under the same joining origin, not suggesting we allow this across sites. +* Brian May + * You create an IG. Whatever state it’s in, you call an update server, which provides some updates to it. If you then create the IG and it calls the update server again, won’t it have everything it had? + * If all IGs could have unique state, very difficult to understand how to interact with an interest group - if IGs are not uniform across all expressions of them. +* Issac + * Are you referring to a distributed systems case where the IGs are kind of meant to be semantically the same across browsers, but they will definitely not be updated at the same point? +* Brian + * An IG is an entity shared across all browser instances in which it appears, hopefully has a common definition across all browsers. +* Paul Jensen + * Possible that an IG is different across different devices depending on when they were updated. +* Brian + * But if two IGs hit the update server at the same time, they should have the same understanding of what the IG is. +* Paul + * Could make your update server give back different results at different times. We’re not enforcing that it’s giving the same response every time. Could be adding ads to your IG as you go along. If an IG is updated at this time, and another at a later time, they could have different sets of ads. +* Brian + * Difficult to reason about what it is you’re interacting with if two IGs have the same name but very different ads. +* Isaac + * Problem of - can we effectively touch an IG on a page without changing features that require a call to the server - you prefer not to do that in some cases. In a multi-billion node distributed system, add a timestamp to make sure things are synced, but if you’re putting in something that’s meant to be synced in a more interesting way, that would be challenging. +* Paul + * If you break the original problem into two pieces, interacts with what Brian mentioned. a) extend the lifetime, and b) not lose all the ads that may have been downloaded during an update. + * If you look at (b), if you rejoin the IG, which deletes the ads, but then immediately call update, could get ads back, so they’re not lost. May run into rate limiting if we updated the IG recently. But the expiration date is a different problem, and the patchiness/partial updating is kind of a different problem. + * The thing we’re trying to preserve - one IG contains one site worth of data - and this can be broken when we don’t do these complete overwrites. I linked to a couple of GitHub comments where this is discussed. Could have one IG that’s joined from lots of different sites, and then it becomes a product of multiple sites. + * We’ve encountered this multiple times, and the solution we’ve come up with is - when IGs are joined from a different site, it’s often an error, and so it’s joined anew when joined from the second site. +* Isaac + * Without making a different call - different companies are going to setup different update strategies - modify the bidding signals or even the trusted signal URL - if you happened to join from different sites, then yes, it would go back to overwrite semantics. +* David Dabbs + * The use case you described is an IG that’s only going to be joined on an advertiser site. But can’t you set a CHIPS cookie, and so you don’t drop the IG with a second join? + * Lightweight, scaffolding at join time - mechanism to ensure that the updateURL gets called - already called the update URL and is ready to go. That proposal might provide some means to get what you want. +* Isaac + * Like the idea of not having to make the call. If it’s a matter of asking, please run the update if it hasn’t been run. Certainly could set something in cookie or local storage. +* Brian + * It occurred to me that the person creating the IG could call the update server and create the join based on what they got back from the update server; don’t need the browser to do it on their behalf. Once an IG is updated, we probably want to have a “don’t update this IG for a specific period of time”, so it’s not constantly being refreshed, either on purpose or by accident. If the semantics of an IG creation was to call the update server immediately. + * I would want to try to get the IGs to be as consistent as possible across these billion nodes; can’t treat the IG updated today the same as I would the IG updated yesterday. +* Isaac + * Setting local storage is fine. Having developers not need to do that - if you’re on a site, you’re navigating around - but having to make those calls on every page load instead of just being able to say, call this update unless you did so in the last two hours or whatever. +* Paul + * David - you’re right to point out that what Issac is asking for is something you could do today using first-party storage. Good that we’re asking for things that are asking for convenience. +* Roni Gordon + * In the interest of not trying to design it in real time, since I’m not a buyer, we have updateIfOlderThanTime, Brian has don’tUpdateIfOlderThanTime. What can be patched, what can’t be patched, and how do we do it we can leave to GitHub. +* Paul + * What we’re trying to prevent is that an IG contains data from multiple sites. Any update is OK because the updateURL comes from the original site. Priority vector is a little different because it’s not exposed; but in terms of joining and updating, what we’re trying to maintain is that it’s from one site. +* Brian + * Am interested in the concept of TTL. You don’t want the browser getting crowded. But do want IGs that are renewed as long as there’s a certain amount of activity related to them. Audience extension and other sorts of cross-site advertising without the cross-site data. +* Paul + * We have a 30 day TTL, and it can be extended by rejoining it, but not update. Need some kind of limit on the TTL and we need to prevent mechanisms that allow it to be updated. +* Isaac + * Have to drop for a different meeting. Thanks for the conversation. +* Paul + * Something like this could be possible as long as we have a mechanism for preventing it from accumulating state from multiple sites. Could have something like “updateOrDelete” - would delete the IG if it had been created from a different site. +* David + * Brian - to the points you’ve been making - the focus should be achieving a patchy update whether they have a conception of an IG that you do that has the IG being consistent - or whether the contents of the IG may be different based on what was on that first party site at join time. About a year ago, updateURL was removed from the k-anonymity calculus - which allows people to apply it in different ways. +* Paul + * To Brian’s comment - have to assume that if ads for campaigns are changing, the potential for different versions of the same IG on different devices is possible. Can’t have a perfectly distributed coherent DB across all devices. Signals like when it was updated or what version of it has been kept on that particular device could help. + + +## Follow up on easing domain restrictions (https://github.com/WICG/turtledove/issues/956) + + + +* Tal Bar Zvi (instead of Omri Ariav for this meeting) + * With Taboola, following up on question on a GitHub issue + * Owner domain - used both to download most static files - that contain GenerateBids, ScoreAd, reportWin, reportResult, and also downloading web assemblies. + * And then there’s trusted server - now BYOS - that has to use the same exact DNS name as the prefix, and this kind of server usually costs more because of the dynamic call. + * The first is static resource, the second is dynamic resource, and they have to have the same domain name. + * I know you addressed this subject recently in this issue that Roni created. Is this the same issue? +* Paul + * Yes, Roni filed #813. We talked about it on previous calls. It did boil down to that differentiation of resources in the interest group. Some static (CDN), some some more dynamic as you called them. Isaac mentioned a few calls ago that this was their motivation for asking on #813. + * We’ve been working on the solution for this. Pushed out the solution to Canary Chrome at 50% earlier this week. Should be on Chrome Dev Channel soonish maybe next week or something. + * Detectable with some feature detection mechanisms + * Should address the issue that #956 was asking about, where you might want to serve some things from static origin and other things from dynamic ones. + + +## https://github.com/WICG/turtledove/pull/1156 + + +## Can we expect a reasonable cap on the \* at:[ ](https://github.com/WICG/turtledove/pull/1156/file)for number of features returned in `queryFeatureSupport('\*')`? + + + +* Patrick McCann + * Speaking of feature detection - you guys plan to cap the number of features it returns, we’re just kind of worried about sending the whole thing over the wire. +* Paul + * We don’t plan to cap things. +* Patrick + * In a year, is it going to be 200-300 features? +* Paul + * Multiple ways we can address things. For example, could ignore things you already know about. In terms of keeping the list smaller and simpler, when we go about shipping these different features, we roll them out for testing on the early channels - canary, dev, beta, then stable some percentage, and then we turn them on by default. After that, all versions of Chrome will have them on supposedly forever. If you’re already getting the Chrome version, could omit those features that are enabled by default. Could only find out about experimental things that are enabled only for a fraction of traffic. +* Roni + * As of this version, these things are available. That’s not formally available either. If I can’t infer which versions are available - what can I figure out. +* Paul + * We do have the feature detection page in our repository. Put in links for the intent to ships (I2Ss), but could also put in links to the version of the CL that added it on by default. +* David + * Back in the dawn of time, the release notes were useful, but you guys don’t do that anymore. + + +## (Back to) Follow up on easing domain restrictions + + + +* David + * Separating the reporting from the bidding logic. Now that we can separate host, can we take that out of the calculation? +* Paul + * One of Isaac’s topics +* David + * Can address another time. +* Paul + * Isaac was looking at something like - many versions of a bidding script with fewer versions of reporting script - might be easier to reach k-anon with fewer versions of a reporting script. +* David + * If separating out different scripts for bidding and reporting, could support this. + + +## Follow up on the proposal to support an analytics entity (https://github.com/WICG/turtledove/issues/1115) + + + +* Warren Fernandes + * Was hoping to get some feedback on the analytics entity proposal. + * Got some feedback last time. +* Paul + * Some of the rows in the doc were aggregate statistics + * Events - when we wanted to report them. +* Warren + * If we write to shared storage instead of another reporting output gate, could take advantage of a lot of existing tooling, just reading out of shared storage. + * Followed up on proposal - effectively on the GitHub page +* Paul + * Could take a look at it tomorrow. + * We’ve been thinking more about integrations between Protected Audience and Shared Storage + * Josh filed https://github.com/WICG/turtledove/issues/1190 (Consider adding ability to read Interest Groups in Shared Storage worklets) - more related to cases of shared storage pulling directly from Protected Audience + * Warren - did you want to push stuff from +* Warren + * Want Chrome browser to automatically push a structured JSON object that could be picked up by some entity that could - on behalf of all sellers. +* Paul + * So a push instead of a pull. + * Haven’t had a chance to look further, will very soon. diff --git a/meetings/2024-06-12-FLEDGE-call-minutes.md b/meetings/2024-06-12-FLEDGE-call-minutes.md new file mode 100644 index 000000000..9462960ce --- /dev/null +++ b/meetings/2024-06-12-FLEDGE-call-minutes.md @@ -0,0 +1,315 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday June 12, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Luckey Haprley (Remerge) +3. Roni Gordon (Index Exchange) +4. David Dabbs (Epsilon) +5. Matt Menke (Google Chrome) +6. Orr Bernstein (Google Privacy Sandbox) +7. Paul Jensen (Google Privacy Sandbox) +8. Jacob Goldman (Google Ad Manager) +9. Andrew Pascoe (NextRoll) +10. Matt Kendall (Index Exchange) +11. Brian Schmidt (OpenX) +12. Fabian Höring (Criteo) +13. Isaac Foster (MSFT Ads) +14. Konstantin Stepanov (Microsoft Ads) +15. Victor Pena (Google Chrome) +16. Jeroune Rhodes (Google Privacy Sandbox) +17. Russ Hamilton (Google Privacy Sandbox) +18. Harshad Mane (PubMatic) +19. Rickey Davis (Flashtalking) +20. Paul Spadaccini (Flashtalking) +21. Becky Hatley (Flashtalking) +22. Xavier Capaldi (Optable) +23. Jason Lydon (FT) +24. Tamara Yaeger (BidSwitch) +25. Warren Fernandes (Media.net) +26. Eubert Go (Microsoft Ads) +27. Maybelline Boon (Google Chrome) +28. Laura Morinigo (Samsung) +29. Koji Ota (CyberAgent) +30. Alexandre Nderagakura (Independant / Consulting) +31. Matt WIlson (NextRoll) +32. Jeremy Bao (Google) +33. Abishai Gray (Google Privacy Sandbox) +34. Matthew Atkinson (Samsung) +35. Kenneth Kharma (OpenX) +36. Arthur Coleman (IDPrivacy) +37. Kevin Lee (Google) +38. Denvinn Magsino (Magnite) + + +## Note taker: Tamara Yaeger + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 +* Warren Fernandes: + * Shared storage-based analytics entity proposal: https://github.com/WICG/turtledove/issues/1115 [Shared Storage - PAAPI Analytics Extension Proposal](https://docs.google.com/document/d/1IbHq_XMpK4Q8YqbjSsve1E_Ux_fek_TlpP8vkXMyL38/edit?usp=sharing)https://docs.google.com/document/d/1IbHq_XMpK4Q8YqbjSsve1E_Ux_fek_TlpP8vkXMyL38/edit?usp=sharing + +* David Dabbs + * API change to trigger a daily update right after a JoinAdInterestGroup event (#1191) +https://github.com/WICG/turtledove/issues/1191 + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +**Join Privacy Sandbox Developer Webinar: Protected Audience API Reporting** + +The Google Privacy Sandbox team will be hosting our next series of webinars on the Protected Audience API. This is the fourth series of webinars covering the Protected Audience API and in this session, we will continue from the previous session and learn more about reporting. The first **Americas friendly session** is happening on** June 25th 3-4 pm ET**. A second **EMEA friendly session** is happening **June 26th 12-1 pm GMT**. + +To join, please register below: + + + +* AMER-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-amer) +* EMEA-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-emea) + + +# Notes + +Michael (Chrome) + +Check the doc for announcements; (1) MSFT Edge folks reg calls about ads select API (like PAAPI); (2) Upcoming webinars on PAAPI reporting - how PAAPI interacts w various reporting mechanisms that exist for learning what happened in auctions, check registration links. + +We did PATCHY updates last week. Warren has shared storage analytics proposal, also discussed last week (?) , David has API change to trigger updates, which we haven’t talked about before, let’s start there. + + +## David Dabbs: API change to trigger a daily update right after a JoinAdInterestGroup event (#1191), https://github.com/WICG/turtledove/issues/1191 + +David (Epsilon) + +Google ads is the original poster, Gianni said we should bring it to Wed discussion so I tried to put something on paper. We can have some interesting convos about it. We can open the last comment; basically I tried to put more descript to one of the scenarios that Gianni described, the basic goal is to take expensive IG ‘rendering’ off the main page by joining/planting a minimal set of attributes. Owner update URL would be required. Means would be a new attribute since Gianni’s proposal to use 2nd parameter doesn’t work because legacy 2nd calling parameter is still supported. The approach would have to be some attribute, regardless of how it’s done. The ask would be similar to the fetchLater API, which directs event to happen after timeout OR the page is unloaded, whichever happens first, to guarantee that update will be called. + +Michael + +Looking at the mechanism, it all seems like a good idea. HTTP header-based ways instead of code-on-page or JS API is in line with other parts of PSB. Triggering a daily update earlier than it would otherwise happen as mech for doing quick refreshes plays nicely the way you described it, being able to have minimal amount in HTTP response and defer other work to update that happens shortly thereafter, seems to work well together. + +Obviously fundamentally we can’t add API to say whether user is already in IG or new IG. So using HTTP header to update shortly thereafter has to behave same way as server sending join request, whether user is in the IG already or has just been added. We have to assure desired behavior to avoid unintentionally overwriting stuff. I don’t think it’s a bigger problem in this flow than how it exists today, but people shouldn’t take separation of concerns as if joint action is free; the joint action will have to do over-writing of group that’s already there if it’s in the browser up to this PATCHY thing from last week. + +David + +To be clear the initial baseline ask to leave the header activation off the table because base ask is what everybody is already established doing today; access to JS and providing minimal attributes that allows you to use this pattern, and off the main page have the full attribute updates. Once that’s in place the header activation it’s a nice add. To the comment about overwriting, if someone is coming to the advertiser for the first time or returning, you don’t know whether one is overwriting - that’s how PA is. + +Michael +You said something about guarantee to get updated even if user navigates away, we should be able to do something where the user navigating away does not disrupt the queue; but there is no way if user closes browser that we’d still do that update, or do it when they re-open. + +David + +If a user goes to a pub site and there are some tabs of auction where you’re eligible, then you get the update, as long as we are guaranteed that the IG was joined back on the advertiser site, we’re good. + +Isaac (MSFT Ads) + +Minor question, if an update were to be queued and browser closed, Why would the browser not hold onto that for later? + +Michael + +If you quit the browser it stops doing work. If it re-opens tabs it’s not going to send out network requests that it was thinking about before. + +David + +And Fetch Later doesn’t have that? + +Isaac + +It sounds like there isn’t a mechanism currently to say that the queue comes back after a crash. + +Michael + +Right, I don’t think it would be desirable for a bunch of reasons. For one example, if a network request causes a crash and then is called again it could be a cycle, makes user unhappy loop. + +David + +There is an earlier [issue](https://github.com/WICG/turtledove/issues/82) asking for a header-based API for other IG methods. #1191 only addressed joinAdInterestGroup omitting the others (leaveAdIntertestGroup, clearOriginJoined) because those seem straightforward; they’re basically passing in name + owner. + +Michael + +Haven’t thought about if it’s important to distinguish those APIs from others. + +Warren has the shared storage based analytics entity proposal. + + +## Warren Fernandes: Shared storage-based analytics entity proposal, https://github.com/WICG/turtledove/issues/1115 + +Warren (Media.net) + +Two important points, buyers don’t get auction ____ so they wouldn’t know, (2) there is not explicit handshake / recognition from the seller that they are acknowledging it exists. Any suggestions on browser signals where this can be indicated or permissioned better? + +Paul (Chrome) + +If you have an old website, take info, give it to someone else you have to get permission to share that info. Both sides can get the request and approve in response to share information. Don’t have great iea how to do it w analytics, perhaps auction participants can denote who they’re ok sharing their data with (which analytics writers). It seems even harder w buyers than w sellers. We added sellers capability thing where buyers say it’s ok for certain sellers to get info like # of interest groups, etc. In this proposal, what is the idea for getting auction participants to opt into sharing info? + +Michael + +Is all of the info that we’re talking about all stuff that sellers already get access to? Buyers deliberately when they make bids and pass info, is the info a subset of what they’re already passing to sellers? + +Warren + +Modeled on that process, info that is already available to sellers. If there is something in there that is not available sellers that would be an oversight. + +Michael + +Buyers clearly know they’re passing info to the sellers. If sellers are the only parties we need appropriately empowered to say they are w the analytics entity to pass info that addresses a lot of the concern. + +Warren + +Like prebid analytics it’s the pub’s choice and if they enable they screen pub pages to some frequency, there is no other direct mechanism for them to be informed. + +Michael + +From the precedent POV, I don't think it’s a compelling argument from the browser dev POV. The precedent is that everything that happens in prebid context in shared JS environ in which anybody w code on page can do anything, it’s a cesspool of lack of any control over info sharing that gives browser devs the heebie jeebies. The share of info should be much more explicit than info how it moves around in 1st party webpage that we have today. + +Paul + +Thinking about trusted bidding and scoring signals to go to diff origins, we’re talking about opt-in from seller, my idea would be coming from their scoring signals fetch, a header that says analytics provider can get their info. + +Warren + +In the case that isn’t permission granted from seller, would you propose that it should be rejected or should the analytics engineer not see them? + +Michael + +(1) How does seller make clear they’re ok w analytics entity seeing what happened, (2) if pub page says that an analytics entity should see what the auction and seller doesn’t agree, who wins? Pub as owner of page is the one who wins? They can say that component seller can be on their page but disclose the analytics entity. On the other hand it’s plausible that some SSPs don’t want to work w some analytics entities. Whether pubs config of analytics entity overrides and you’re kicked out of the auction seems like a delicate issue where we might need to make it optional or support both ways and let pubs decide whether it’s a precondition, or whether they’re flexible w sellers who don’t report. We’ll have to figure out something that leaves control in hands of owner but that allows for diff possibilities in relation between page owner and various sellers. + +Paul + +iFrames have security principle that they can’t see what’s happening an in iFrame, so we probably can’t let someone outside an iFrame what’s happening inside. May not be able to differentiate whether seller participated in auction vs participated but didn’t want to share info. + +Michael + +Permissions policy model says that iFrame is allowed to load but only if it agrees to certain criteria. + +Paul + +We could add very specific permission for certain sellers + +Michael + +Seems like right semantics; seems like pub should have an option of having that as a way to specify. + +Warren + +Same thing, possibly adding indication that pub can send that indicates behavior ; require analytics entity to see bids and any seller who is not ok w that has bids rejected. + +Paul + +What do we do when there’s no bids in a particular auction and we don’t ever look at seller’s scoring script. Are we considering always fetching it? + + +Michael + +But if it comes from response header, the usual would be request header from script; this would be set of entities that pub is asking about. + +Paul + +Tricky is if we wanted add anything we’d all have to update the version + +Michael + +We’ll think about details on how to enable it, but we’re all aligned. I think it’s good we don’t need to figure out a separate mechanism for buyer opt-in as well. We’ll continue in github issue. + +Ok we’ve done the Warren issue and the David issue and Isaac had to drop, which means we have no more agenda items? + + +## Blink Launch Process + +David + +Question – I noticed over past week a number of intent to ships that got their LGTM x3, the render size, reporting timeouts and multiple bids, curious if those are things that are already there and will be ramped up? Is there was to find out if it’s only on the feature card? Which milestone will render them fully available? + +Paul + +Our general way of launching has been to start w Chrome Canary channel, we enable 50% testing on Canary. Within week dev follows that. After we’ve done that a couple weeks and make sure we don’t cause crashes, we move onto Beta. 50% same thing, check metrics for a few weeks, then move to stable 1%. After 2 weeks on stable 1%, we get more data on each stage of rollout, if it looks good at 1% stable we go to 100% stable. We do that by landing a CO that turns it on by default, tip of tree, whatever branch that is. beyond that point it will have specific point of data how it lands, and it will be on forever unless we emergency turn it off. + +Through all these diff channels we try to have feat detect mechanisms available so you can tell what’s on and available. There’s also the Chrome status entries, we should probably update those more. The feature card - chromestatus.com (?) We should update that to say when something shipped but not sure we do. + +David + +It sounded like one of them was being ramped to some % stable, but it may not be the case. It already has to go then you ramp it 100%. + +Paul + +If we do 50% canary, 50% dev, 1% stable, 100% stable, we have to have it approved by 3 of the Blink API owners. We can never get to 100% stable until we have the intent to ship approved by 3 people. For more details see https://www.chromium.org/blink/launching-features/ + +David + +Also the trusted bidding signals, the cross origin had updates to fix the spec. It won’t affect it’s progress, just tweaks? + +Paul + +We ship change, sometimes we’ll forget something, the explainer….. (?) + +Roni (Index) + +How do we know it’s hit 100% stable? + +Paul + +Before we ramp it up to 100% stable, we’ll turn it on tip of tree, if you look at that CO you can copy paste the commit hash to the Chrome dashboard and it will tell you what release that particular CO went out to, we release a canary every day. Generally we will enable it by default on – imagine we put something today; the code would be there but it wouldn’t be on. Then let’s say intent to ship approved, + 6-8 weeks from now we would turn it on at tip of tree; 8 weeks is 2 milestones later, so that milestone would be on by default for everyone. Shortly after we enable by default by tip of tree, we’ll enable using controls for all branches. It’s on by default but we can really turn it on for all stable users when code is written. + +On the github issue, we try and say when it’s going to canary and then beta and then stable, that’s a specific place for each specific feature. + +Roni + +I keep track of every github issue, but for other that don’t it would be very helpful to see it somewhere together. + +Michael + +The webby answer here is that feature detection is better than on-by-default starting at milestone-whatever. Even if we say we’re turning something on by default in M-whatever, it’s not that you can just check something is sufficiently high milestone for it to be on because there are not the browsers that make separate decisions about thinks. If Edge turns out API selection and has some feature diffs, and you think it’s a high enough milestone, you might get it wrong. Feature detection is preferable because it’ll get the answer right every time as opposed to having edge cases. + +Roni + +Last week we were trying to figure out where the main plus delta is; I would love to know everything about everything, but if every auction includes 200 items out of which 197 have been stable we’re losing our signal to ratio. + +Paul + +Last week decided to prevent this sort of accumulation of long-term features, don’t know if we’ve done that yet. Thinking about how to ensure that list is not huge also, I think Pat’s concern was about sending it to servers. You could send a hash but you need a decoder ring. Otherwise the not-on-default version is what we’re talking about. + +Michael + +Sensible things that go well together + +Paul + +May be hard to implement, but… we can try to think about that. + +Michael + +**NOTE: No call next week, Wed June 19 = Juneteenth US Federal Holiday. See you June 26.** diff --git a/meetings/2024-06-26-FLEDGE-call-minutes.md b/meetings/2024-06-26-FLEDGE-call-minutes.md new file mode 100644 index 000000000..f06582857 --- /dev/null +++ b/meetings/2024-06-26-FLEDGE-call-minutes.md @@ -0,0 +1,260 @@ + +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday June 26, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + + +## Attendees: please sign yourself in! + + + +1. Paul Jensen (Google Privacy Sandbox) +2. Sven May (Google Privacy Sandbox) +3. Brian May (Dstillery) +4. David Dabbs (Epsilon) +5. Ricardo Bentin (Media.net) +6. Matt Davies (Bidswitch | Criteo) +7. Roni Gordon (Index Exchange) +8. Youssef Bourouphael (Google Privacy Sandbox) +9. Garrett McGrath (Magnite) +10. Jacob Goldman (Google Ad Manager) +11. Matt Kendall (Index Exchange) +12. Aymeric Le Corre (Lucead) +13. Matt Menke (Google Chrome) +14. Alonso Velasquez (Google Privacy Sandbox) +15. Sathish Manickam (Google Privacy Sandbox) +16. Orr Bernstein (Google Privacy Sandbox) +17. David Tam (Relay42) +18. Yanay Zimran (Start.io) +19. Denvinn Magsino (Magnite) +20. Gianni Campion (Google ads) +21. Arthur Coleman (IDPrivacy) +22. Laurentiu Badea (OpenX) +23. Abishai Gray (Google Privacy Sandbox) +24. Lydon, Jason (FT) +25. Becky Hatley (Flashtalking) +26. Anthony Yam (Flashtalking) +27. Owen Ridolfi (Flashtalking) +28. Rickey Davis (Flashtalking) +29. Itay Sharfi (Google) +30. Manny Isu (Google Privacy Sandbox) +31. Tamara Yaeger (BidSwitch) +32. Warren Fernandes (Media.net) +33. Koji Ota(CyberAgent) +34. Maybelline Boon (Google Privacy Sandbox) +35. Matthew Atkinson (Samsung) +36. Andrew Pascoe (NextRoll) +37. Alex Peckham (Flashtalking) +38. Tal Bar Zvi (Taboola) +39. Luckey Harpley (Remerge) + + +## Note taker: Orr Bernstein + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Trusted Scoring Signals for Financials: Via [Comment](https://github.com/WICG/turtledove/issues/824#issuecomment-2198376904) on [Roni Original](https://github.com/WICG/turtledove/issues/824) + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 \ + +* Alexander Tretyakov (Google Privacy Sandbox) + * Hybrid protocol – modify v2 protocol so that a single request can go to both BYOS and TKV servers. This is meant to be a temporary measure, and when the enforcement starts only TKV mode will be supported. The benefits of the hybrid protocol are: + * enables piecemeal migration. AdTechs can migrate pieces of their logic and asses the cost/latency and other parameters before fully committing + * allows keeping parts of the logic that are not currently supported by TKV in BYOS, while moving the rest to TKV. +* Roni Gordon + * We’re observing **biddingDurationMsec** values in production well above the default 50ms timeout (i.e. with the default auctionConfig values) – some as high as 322ms – can you think of any explanation for this behaviour? + * Haven’t yet logged an GH issue since we haven’t been able to reproduce in controlled tests, where the time spent in generateBid() seems to match the fDO QSP + * Can we discuss the implications of the tweaks to the deals proposal? + * https://github.com/WICG/turtledove/issues/873 +* Warren Fernandes + * Continued discussion on the addition of analytics entities to PA auctions: https://github.com/WICG/turtledove/issues/1115 + * New mechanism for seller opt-in proposed. +* David Tam + * There are 3 parties that can create IGs, the buyer (advertiser), the buyer’s delegated tech partner and the publisher. I would like to understand how a buyer would be able to bid for publisher curated IGs - https://github.com/WICG/turtledove/issues/1196 +* Gianni Campion (briefly discuss 0 ads API for joinIG + * https://github.com/WICG/turtledove/issues/1191) +* Yao Xiao + * discuss header api for joinIG https://github.com/WICG/turtledove/issues/82 + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +**Join Privacy Sandbox Developer Webinar: Protected Audience API Reporting** + +The Google Privacy Sandbox team will be hosting our next series of webinars on the Protected Audience API. This is the fourth series of webinars covering the Protected Audience API and in this session, we will continue from the previous session and learn more about reporting. The first **Americas friendly session** is happening on** June 25th 3-4 pm ET**. A second **EMEA friendly session** is happening **June 26th 12-1 pm GMT**. + +To join, please register below: + + + +* AMER-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-amer) +* EMEA-friendly: [Register Here](https://rsvp.withgoogle.com/events/protected-audience-webinar-4-reporting2-emea) + + +# Notes + + +## Roni Gordon: We’re observing biddingDurationMsec values in production well above the default 50ms timeout (i.e. with the default auctionConfig values) – some as high as 322ms – can you think of any explanation for this behaviour? + + + +* Roni Gordon + * Have observed. Bidding_duration_msec is the time that generateBid spends? Having trouble reconciling how this can be significantly higher than what we would see based on the timeout. +* Paul Jensen + * Asked the PA team how the bidding_duration_msec could be higher than the timeout. Largely historical reasons. The way it works is the perBuyerTimeout - which defaults to 50 ms - added way back in the beginning. In the implementation/spec, you can see that that one is tied to how much time it takes that one to execute the generateBid JavaScript, does not include the time for setup - create a JS execution context, initialize 7000 built-ins in JavaScript, then move those into the JS context. Can add a lot more time; these are all part of bidding_duration_msec because it’s all “the cost of generating the bid”, but for the timeout, we only enforce that on the generateBid call in JS. All measured using different timers, browser is running in a very multi-threaded environment, some of these threads are getting preempted at random points. This is the reason why there’s a bid difference between bidding_duration_msec, which includes the setup, and the timeout, which doesn’t. +* Brian May + * Timeout is the max amount of time that the code can run for, but the bidding duration is how long it took including a bunch of setup. Not based on the same thing. +* Paul + * If you use a debugger - attach via Chrome Developer Tools - the timeout would pause, because otherwise the timeout would always be exceeded because of a breakpoint; but the bidding duration msec is just a timer, so it would report the time even when it was paused at that breakpoint. + * While on the subject of per-buyer-timeouts, it was one of the first things we added to PA. We’ve sort of moved away from that to some degree. It made the browser opinionated on how many IGs you should have and how fast each of them should be. Wanted to make the browser less opinionated - maybe you have one IG that’s particularly important and you want to spend more time calculating the bid there because maybe it’s a bigger bid. So moving away from per-buyer timeout, and towards cumulative IG - controls the whole of all the interest groups, how long they can spend in aggregate. There’s a devrel website that describes the differences between these, and how the cumulative timeout gives people a bit more freedom. +* Brian (by chat) + * https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/latency +* Roni + * I thought this was a measure of the time spent on device in the generate bid function alone – seems like it also includes all the initialization pre-work required to get to execution of generateBid. It would be great to elaborate on this in the spec on what’s it’s measuring. +* David Dabbs + * In the spec, it says the reporting timeout is clamped to 5 seconds. Is there something similar for the per-buyer cumulative timeout? Seems like it should be mentioned in the explainer. +* Matt Menke (by chat) + * There's no max for cumulative timeout + * By default there is no timeout +* Paul + * The difference in incentivization is perhaps the reason why one is capped and one is not capped. +* Matt + * For reporting, it’s a background thing. You will typically navigate to a different page, maybe by clicking on an ad. Don’t want the reporting to keep on going for an excessively long time. + + +## https://github.com/WICG/turtledove/issues/873 + + + +* Roni Gordon + * Trying to ascertain whether or not its possible to have a dealID visible in the reporting. In other words, can you have a ‘split-brain’ situation where scoreAd seems dealID bids but reportResult doesn’t? +* Paul Jensen + * To refresh folks, the proposal that Roni put up on March 13 - issue 873 - taking an existing field that’s part of the ad part of the interest group - existing field is called buyerAndSellerReportingID - a string - and making it into an array of strings, and allowing the buyer to pick one of them and to have that chosen deal passed along to scoreAd and reportResult. So, as Roni noted, the same thing is available at scoreAd time - to assess the score if there’s some discount - and at reportingTime - so that we can account for how the desirability score was arrived at, and to keep track of who’s buying with a particular deal, keep track of that. Like buyerAndSellerReportID, it will have a k-anonymity reqiuirement on it. Could appear in scoreAd, but would only be visible for reporting functions if it’s k-anonymous with the URL and interest group owner. Part of Leeron’s proposal was to give buyers a way to say that the buyerAndSellerReportingID being passed through to reporting is required. You an imagine, if it’s not k-anonymous, without the deal ID, the bid might not make sense at a later time when the reports are benign processed, so we expose this boolean to the buyer to only report to scoring if it’s k-anonymous. +* Roni Gordon + * The way k-anon works for everything else - you have to end up with the seller choosing to select the ad in order for the k-anon to be ticked; otherwise, it never starts. What I wanted to confirm is that the same thing is happening here; you can score a deal bid with all the required parameters. +* Paul + * We give the buyer the ability to throw away a bid if the deal ID is not k-anon, you want to give the seller the same. We could expose a boolean, and seller can require that boolean to always to be true, that the deal id has to be k-anon to be used. +* Roni + * Yes, but also, if a seller rejects deal bids that are k-anon, it will never be k-anon – how so would that dealID ever reach the threshold? +* Brian + * Can we do this retroactively, at the point at which k-anon is reached, that we can report a deal ID that was not previously reported because it wasn’t yet k-anon. +* Matt + * Can’t convey whether it’s k-anon, or you could game k-anon. Need some way to say, if it’s not k-anon, don’t let it win. Specifically if it’s not k-anon because of the deal ID, but would be k-anon. +* Paul + * What if we allow everything to be scored, even if it’s not k-anonymous. Two winners - the k-anonymous winner and the non-k-anonymous winner. Sometimes you only have one or the other. For the non-k-anonymous winner, we could increment the k-anonymity count, and we’ll do it for the k-anonymous winner as well. This is what Leeron had planned for deal support, was at the bottom of his proposal. What I had suggested earlier of exposing that boolean to scoreAd, could still increment their k-count. +* Roni + * If it doesn’t meet the threshold, it’s discarded from the auction. Optional is whether or not it gets to the seller or not, if it gets to the seller, score it as normal. Just wanted to make sure that this is the behavior we’re getting, and not that reportResult is getting the winner without deal ID. +* Paul + * Yes, we could always expose the boolean. We don’t want to expose whether it’s k-anon early to avoid gaming. Want to know the highest non-k-anonymous winner so that we can increment its k-anonymity count. So I think we can expose that boolean to sellers to ensure that they can choose to only allow winners that would be reported correctly. +* Roni + * Seems like something we’d always want, but I’ll take the boolean in scoreAd, because for forDebugOnly, it’ll help make it obvious what’s happening. Absent k-anon enforcement, as many scoring bids I score is how many bids I submit to the upstream seller. As soon as we include k-anon, that number could be anything. The scoring counts are way off. It would be nice to see that if I submit a non-desirability score of non-zero. +* Paul + * I think we have a private aggregation on whether a bid is not used because of k-anon. +* Roni + * The current proposal with tweaks? +* Paul + * We already have the boolean for buyers, so what we’d change is to expose that boolean to the seller so that the seller could reject any bid from an IG that doesn’t have that boolean set, since it can’t guarantee that this bid would be correctly reported. +* Brian May + * What does the boolean say? +* Paul + * The boolean is provided by buyer and says that they want their bid to be thrown away if their bid - with the buyerAndSellerReportingID - is not k-anonymous. If it’s false, it can continue onwards. +* Brian + * Can we simplify that it’s reportable to all parties? Boolean that says that this thing is going to be reportable to everybody. +* Paul + * I think this is for everybody. +* Brian + * Language there that this is about deals. +* Paul + * Not sure I totally understand, but I think we’re aligned. Maybe if you could put it on the issue, I could better understand. And I’ll try to add more detail on the issue. +* Giani Campion + * Where is the boolean available? Does it cover buyerReportingID as well? +* Paul + * Indicates that the bid should be thrown away if the reporting ID is not k-anonymous. It covers both buyerReportingID and buyerAndSellerReportingID. + + +## Continued discussion on the addition of analytics entities to PA auctions: https://github.com/WICG/turtledove/issues/1115 + +New mechanism for seller opt-in proposed + + + +* Warren Fernandes + * Following up on the conversation from last time; request for some sort of mechanism for both the seller to be aware that they had analytics available. Proposed header that could be set on the call to the seller decision logic file that included a list of analytics entities that were present and another head they could respond with. Additional flag that the sellers could host on the well-known URL to indicate whether they had opted out of being observed by the analytics entity. +* Paul + * Sounds like you made the updates we’d talked about before. Did you want to discuss a part of that, or is this a request to take another look? +* Warren + * The latter. Is this good to go, want some feedback. Not really sure how to proceed at this point, so any feedback on that would be great. +* Paul + * I can take another look at that. May be less available in the next couple of weeks, but will try to take a look at it in the coming month, or to find someone else who can take a look sooner. + + +## There are 3 parties that can create IGs, the buyer (advertiser), the buyer’s delegated tech partner and the publisher. I would like to understand how a buyer would be able to bid for publisher curated IGs - https://github.com/WICG/turtledove/issues/1196 + + + +* David Tam + * As I understand it, there are three parties that can create an IG - buyer, delegated buyer, or publisher. For publisher, I assume that the publisher wants to monetize an IG and sell it to interested buyers. How would a buyer actually buy an IG? +* Alonso + * We don’t have my product peers on the call who have been thinking about this from the publisher side of things, but it’s a known request. I do see issue 1196, we’ll tag it for follow-up here. We’ll follow up. +* David + * Is it right that a publisher might want to sell an IG to a DSP? +* Alonso + * You’re showing a new use case. If you have any hypotheses, please feel free to post them to 1196. + * In this use case, an audience, by the publisher - what’s the scope of that audience? A little more clarify on the use case helps us. A lot of what we do is to ensure that we’re not creating cross-site profiles. If you have those ideas, please add them to 1196. +* Matt Davies (by chat) + * what about other 3rd party entities? + * Data providers, Pubs, other entities +* Brian May + * Original FLEDGE proposal was that someone else could create an IG and cede ownership. This sounds like a different use case. +* Alonso + * Would like to better understand the delta in these use cases. +* Paul + * The other thing that would help us understand the use case better is understanding how the seller or delegation of the IG would work. Today, it’s possible for whoever creates the IG to maintain control, still do things on another party’s behalf, could still grow/shrink/control the bidding directly. Sounds like something less tightly coupled; many of the behaviors are controlled by the new owner. +* David + * Yes, if you’ve got multiple publishers, want to basically create a single IG and monetize that. Another use case is to funnel activity through a PA audience. +* Paul + * PA doesn’t limit the sites that you’re joining from. Could have a third party iframe that joins from all different sites. The publishers could kind of pick that, join IGs from different sites. +* Brian + * Is this about giving publishers a mean of getting credit when an iG they created is monetized by somebody? +* David + * Essentially, yes. It’s an interesting use case where we can buy publisher-created interest groups. Some way of informing the buyer that this is the IG, would you like to buy it? +* Brian + * Maybe a different direction - could inform the creator of the IG that somebody has bought against it. +* David + * Because the seller initiated the auction, from my view, it just needs to be added into the auction confirm. +* Matt Davies + * It could be useful for publishers, but what about other types of ad techs? The second you start adding in third parties to the mix, e.g. BidSwitch, they’re an interested party in the mix. +* Brian + * It’s not about the creation; it’s about ownership once created. +* Brian + * Bidding logic, reporting API callouts - all of that, who’s getting those calls. +* Alonso + * Issue [1196](https://github.com/WICG/turtledove/issues/1196) is timely to galvanize the sell-side, looking into buy-side as well which is[ issue 1028](https://github.com/WICG/turtledove/issues/1028) If you have thoughts about that, I’d love to hear your thoughts in a future WICG. diff --git a/meetings/2024-07-03-FLEDGE-call-minutes.md b/meetings/2024-07-03-FLEDGE-call-minutes.md new file mode 100644 index 000000000..5e2a44c91 --- /dev/null +++ b/meetings/2024-07-03-FLEDGE-call-minutes.md @@ -0,0 +1,295 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday July 3, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Brian May (Dstillery) +3. Arthur Coleman (IDPrivacy) +4. Patrick McCann (Raptive) +5. Don Marti (Raptive) +6. Aymeric Le Corre (Lucead) +7. Jacob Goldman (Google Ad Manager) +8. Sven May (Google Privacy Sandbox) +9. Orr Bernstein (Google Privacy Sandbox) +10. Gianni Campion (GoogleAds) +11. Sarah Harris (Flashtalking) +12. Owen Ridolfi (Flashtalking) +13. Jeremy Bao (Google Privacy Sandbox) +14. Isaac Foster (MSFT Ads) +15. Tim Taylor (Flashtalking) +16. Luckey Harpley (Remerge) +17. Laura Morinigo (Samsung) +18. David Tam (Relay42) +19. Abishai Gray (Google Privacy Sandbox) +20. Alex Peckham (Flashtalking) +21. Stan Belov (Google Ad Manager) +22. Shafir Uddin (Raptive) +23. Matthew Atkinson (Samsung) +24. Ken Gordon (Azure) +25. David Dabbs (Epsilon) +26. Koji Ota(CyberAgent) +27. Matt Davies (Bidswitch | Criteo) +28. Kenneth Kharma (OpenX) +29. Paul Spadaccini (Flashtalking) +30. Rahul Shelat (Google Privacy Sandbox) +31. Andrew Pascoe (NextRoll) +32. Maybelline Boon (Google Privacy Sandbox) + + +## Note taker: Arthur Coleman + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + +* Isaac Foster: + + * Trusted Scoring Signals for Financials: Via [Comment](https://github.com/WICG/turtledove/issues/824#issuecomment-2198376904) on [Roni Original](https://github.com/WICG/turtledove/issues/824) + + + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + + + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + + + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 \ + + +* Gianni Campion (briefly discuss 0 ads API for joinIG https://github.com/WICG/turtledove/issues/1191) + + +* Yao Xiao + + * discuss header api for joinIG https://github.com/WICG/turtledove/issues/82 + +* Jeremy Bao (Google Privacy Sandbox) + * Feedback on negative interest group targeting proposal and usage budget https://github.com/WICG/turtledove/issues/896#issuecomment-2174598427 + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for their own comments - so go back later and make sure the notes are accurate + +You can even fix them on Github later!! :) + + +# Notes + + +## Isaac Foster: Trusted Scoring Signals for Financials, via [Comment](https://github.com/WICG/turtledove/issues/824#issuecomment-2198376904) on [Roni's issue 824](https://github.com/WICG/turtledove/issues/824) + +[Isaac] - Thinks we talked about this before around Roni’s issue - there is a challenge that we have right now with pricing trends - more than one, actually. + +Ideally, folks would rather not put sensitive pricing terms into the web page. Also a bit of a challenge with linking up with final pricing via your scoring. What we at MSFT do today - when we receive a bid from a DSP, while we are scoring we are also applying various financial terms to the bid to determine what the final price would be. All of which is not just a straightforward function of the bid. All of that magic happens, scoring happens, and then - you want to report out the figures you scored on. And those figures need to be consistent - bid $1 and you pay 90 cents - preferably there is a consistency between scoring and reporting time. Right now there is some challenge around that. Also, the object graph of what goes into pricing can be pretty complicated. A lot to jam into the auction config. + +So those are the problems. My idea was two fold. + + + +1. Chrome piece of support +2. Something the industry would have to do to enable this + +Simple version: + +The framework that Protected Audience Framework uses would inject into the scoring function of the seller the signals that had been sent back by the trusted scoring signals - the seller’s KV call - and those could be used along with things like creative audit to settle pricing. + +Then you know you are getting back the same signals back from the scoring function and reporting function. Then you wouldn’t need to put your entire graph into the scoring config. Then the industry would have to say there is enough information in the config of the renderURL to do the scoring/reporting. + +[Michael Kleber] - Let me repeat back what you are saying. There are two different times in the course of the Protected Audience auction where somebody gets to look up information from a trusted key value server - today your own, later the real, trusted one. One of those times is during bidding, some data made available to the bidder in generateBid(). In that case the keys are very unrestricted. You can put whatever you want in the keys of the interest group, then look up information from that from the trusted bidding signals server. And that information would not be safe to make available at event level logging time because keys have no good privacy properties and so values returned from server won’t have good privacy properties either. + +But the other time is when a party doing the sell side and scoring - when they get to look up signals from their trusted scoring signals server- the lookup key is the render URL of any ad that is of a bid being considered or scored in the near future. And the winning ad has this k-anonymity property associated with it. The thrust of the ask here - since we are already letting other reporting contain the winning ad renderURL itself, then can’t we let the winning event-level report contain other information - some values looked up from the sellers from the KV server. The contention is that there is no new privacy risk here because it is a key and if you know the key you should be able to know the value associated with that key. More convenient to make the value used in scoring available at reporting time because it prevents items from unexpectedly changing in the meantime, which could happen if you update the server at the wrong moment. + +Do I have that correct? + +[Isaac] Yes. + +[Stan Belov]. I understand the proposal from the API standpoint. I need to understand better how we would use it. In this case, are you thinking about some sort of pricing decisions that are specific to individual ad creatives? There’s another question, but explain this first. + +[Isaac] It would require both a feature on the Chrome side and then the buyer/seller have to agree on the protocol. Let’s stick to the idea it is fully in the renderURL for now. Today, in our exchange, we may apply different contract terms, fees, bid ups vs. bid downs - things like that - depending on advertiser and publisher and deal specifics. If that information is encoded in the renderURL - the IDs that the buyer and seller have agreed upon- then when that went to get looked up in the trusted scoring signals call. The seller could have indexed whatever from the combination of parameters - it is bid up, rev share, private marketplace etc. That then goes back into the scoreAd and the report itself since those two things can actually share code. You can have a common function and send common parameters into that common function. That should be roughly guaranteed to be consistent data between the two. You don’t have to put your pricing terms through the private auction config which is a little bit less secure, It seems a little better to me + +[Brian May] When you say data and consistent logic, you mean between the buyer and seller? + +[Isaac] I meant between score time vs. report result time. Score time vs recording of financials from the transaction time. So like today when we do our stuff around the auction, we log all that immediately. That process that is scored says ‘here are the financials’. Hypothetically, if we put that in a report result, we don’t have a way to put all that complex information in. Or the data may have changed. We scored on V1.01 and we reported on V1.02 - or contracts changed + +[Stan] Sounds like you are proposing to encode financial information that may affect pricing in the renderURL. And one of the major use cases here could be to deal with some sort of agreement between a buyer and a publisher - correct? I was wondering if you have looked at some other threads on the same topic of deal support. I just want to make sure by looking at this use case to make sure you know pricing terms that need to be accessed in the reporting and need to be accessed in scoreAd. But they themselves are not reflecting the actual creative. Can these concepts be split apart in some way? From an exchange perspective, there is a need to duplicate a renderURL, for each combination of potential deals, attributes related to classification,etc. + +[Isaac] Have read #873 before, so I do think we should maintain the decoupling of renderURL from important reporting information. So I did put something in the comment like the initial proposal - the renderURL - would we factor in some of the buyer and seller reporting ID things that still have the k-anonymity properties that will help us maintain the healthy decoupling. There is a second cut of this we need to specify better. + +The additional piece for Chrome would be in addition to the renderURL going through on the trusted scoring signals call, it would also be the buyer and seller reporting ID, which should be ok if we can agree on the k-anonymity restriction. Which we have already agreed in other contexts is needed. So I think we can maintain that decoupling and still get something useful. + +[Stan] I think the use of the buyer and seller reporting IDs and trusted scoring signals needs to be clear. + +I think the idea of propagating scoring signals to the reporting function is also quite valuable, especially if you want to ensure that the promises made to the publishers are fulfilled. Let’s say the creatives that don’t satisfy the requirements were indeed filtered. Maybe a publisher excludes certain categories, for example. That way synchronization between score time and reporting time are the same is very valuable. + +[Michael] Not surprised that this also turns into a request for the buyer and seller reporting (deal) ID to become a part of what is sent to the the seller’s trusted server - I think that is already there in your comment Isaac. I anticipated that would be part of the request. + +Again, I agree that this does not seem like a dramatic change to the privacy stance, because all this information you want included in the event level report that you can look up after receiving the event-level report - except for the synchronization issue. That seems ok even if a design change that addresses an edge case. Not too burdensome. + +From a privacy point of view, the seller's trusted scoring signals server is receiving multiple bids at the same time. As long as we are in BYOS, then that is obviously a new privacy leakage because it is possible the values returned by the TSS server could be based not just on the renderURL but all the other renderURLS as well which might include a unique user ID. If you are running your own server, you could use this to include a user ID in the event logs. + +If you are using BYOS and you do decide to cheat, then you can just log the user ID in your log server side. It is not like putting them in the event level report is a dramatic change in terms of level of harm, but let’s be clear we are giving away a mechanic to put user ID in the event level report. That’s not something we want to do. Requires migrating to a secure server running inside a TEE that have limits on how computation works. + +[Isaac] Want to make sure I am clear. It's not that there is a new privacy leak, it's that it exposes the leak in an additional place. + +[Michael] I mostly agree. There is already a privacy leak if your server wants to cheat in logging stuff you could find a way to log user ID in some way based on all bids sent to your server. + +[Isaac] Then you would have something you produced from all renderURLs in the same place as your auction config. + +[Michael] It is definitely an opportunity for the event level report to include a new privacy risk that it does not include today + +[Isaac] Yes, I agree, there is marginally more information. + +[Michael] I will have to talk to some other folks on the team to see how this marginal privacy risk stacks up with other things. Only makes me slightly uncomfortable - only because it is related to BYOS for now. If your goal were to cheat by violating what a trusted server is supposed to do, then doing it by returning data back through the browser and into the win report is a much more visible and discoverable way of cheating. Versus doing it server side which is an invisible and non-discoverable way of cheating. I think this is a tolerable privacy risk as a result. + +[Stan] From the privacy perspective, in today's environment the trusted scoring signals server can see a combination of information about all the ads and bids plus the top-level domain. Where here the potential delta would be the combination of all contextual information that is available in the report. This could include some proxy for a user ID, correct? + +[Isaac] What I was thinking, I think you could do it such that you would do it in such a way that you get an ID in a renderURL that may not pass k-anonymity, but that doesn’t matter, and put this in your process on your BYOS key value server. Does make it a bit easier, but not fundamentally different + +[Stan] I would argue something different. I think that the argument is that the incremental risk is the combination of the contextual information that is available in the report + +[Isaac] Yes you can do that in the BYOS scenario + +[Stan] A good question if the BYOS trusted scoring signals are required to be k-anonymous or something else. + +[Isaac] I’d have to check. The renderURL is only judged after the key value call? Michael? + +[Michael] - I think that is right. We have to talk about it with some folks not on the call. But it's not so big that I think it should not keep us from doing this. Both Isaac and Stan - it sounds like you think there is value. Any other points of view? Good discussion and we understand the goals. Tied to request to send buyer and seller reporting ID to Key Value Server and the whole deal support work going on. + + +## Gianni Campion (briefly discuss 0 ads API for joinIG), https://github.com/WICG/turtledove/issues/1191 + +[Gianni] - I think there was a Github discussion about what we would like to do regarding having a lightweight joinAdInterestGroup(). The idea is that the first daily update is going to update most of the stuff anyway. Our plan is to join the lightweight joinAdInterestGroup() across the response so you have a skeleton of information to send the device for how you set up an ID. Then we can rely on the first daily update to fill in the blanks needed for the interest group to perform its duties. Proposal is to have a field that is triggered on the first update with a certain delay. Most of the discussion seems to revolve around Brian May’s comment about the failure of the first update. So I didn’t look too much in detail, but if you think about it, let’s say you have an API that says _trigger this daily update in 10 seconds_ and the network fails, we would still have what exists now - an update that fixes it after the first auction. It becomes a quality issue related to how much do you lose between the first joinAdInterestGroup() and the first update. + +[Michael] I think this is reasonable. We talked about once before in the context of supporting the lightweight interest group joining from the HTTP response header. So not have to have a Javascript element on the page to do this but instead use a JavaScript response header. This seems like a good idea to me - lightweight join and call soon afterwards with JavaScript response header. Seems like we should do this - no promises about priorities - a fine future element to do. + +[Gianni] So what are the next steps? + +[Michael] Us to get around to doing it. + +[David Dabbs] Question for Gianni. Is your pattern of use - Isaac brought this up - he wanted to reduce the number of updates in a series of page-to-page-to page - this doesn’t get rid of that - which is fine. It is up to the caller to do that. + +[Gianni] What is the pattern you are mentioning? + +[David] Where you are going to drop and join interest groups. And instead of rendering the full interest group, in your approach you render a minimum and then 10 seconds later you still have to go back to an updateURL() to fill out the interest group later. + +If you want to reduce the number of those update calls, that’s up to the caller to do that. This is just getting it off the main line of rendering on the page - make it lighter. + +[Gianni] Yes. And you probably delete that after the update. If the same interest group gets called multiple times - maybe that interest group should be updated only once. So for this group, fire only one update for them. + +[David] Netting it out: _We do it after the user has left the site over time_ - might be more than we can expect from Chrome. It can probably guarantee doing something when the page/frame ceases to exist (kind of like _fetchLater_) but probably not beyond that. + +[Michael] A good thing to be concerned about here. Gianni - I would like the interest group to update itself soon, not once a day, for example. There has to be a minimal amount of time between updates - now it is 10 minutes - so we don’t overrun the browser. If you try to do this join and get an update immediately, and you try to do it for each page, then it seems like it is good to include in this request what behavior you hope for. I assume you are asking for an update when the user is on the same page where they were adding to the interest group. + +[Gianni] Not necessarily while on the same page, would be ok to happen a little later + +[Michael] But you are not asking for Chrome to deliberately wait until after the user has left this page, right? I don’t want you to be disappointed when a user goes to six different pages in a row, you don’t get an update on every page. + +[Brian] I suggest we set a flag that when someone calls joinAdInterestGroup(). Let’s say I want to add this interest group, the call is made but if the flag is set a certain way, then don't have me add the same information over and over. + +[David] In other words, it should silently fail. + +[Brian] Yes. If I do the same things 5 in a row, only do it once and ignore the other times. + +[Michael] If you do a minimal join for an interest group already there on the browser, then you probably don’t want to overwrite all the information on every page. So there is some complexity. + +[Gianni] My desire, let’s take the example of example.com - we set my IG join to delay one minute - in that one minute I visit 6 pages that have an interest group, call it Interest Group 10. I queue up six daily updates as a result. Not a good idea to do all six - so only do the last one? We need to agree on a mechanism here. + +So I keep adding this lightweight thing, and the heavy lifting occurs with the one daily update. So the last update should count. I think that my preferred behavior is to use the last one, since if we were doing all the updates the last one would be the final state. + +[Brian] We should be very careful about how we are thinking about the application of this. If we produce this capability, then what people will do is they will create 100s of lightweight interest groups, wait for them to update, and then try to monetize them in the future. + +[Gianni] How is that different? It is already doable. The API only allows you to not miss the first daily update + +[Michael] I don’t think there is any new marginal interest group creation capability here that people can’t do today. That risk exists today. This new capability would make it just as easy to create a bunch of interest groups except you would double the load on your server because you’d have to add a second call a few minutes later. + +[Brian] Let me push back on that. If I can create an interest group by popping minimal information into a header and then blasting a bunch of those headers down into the browser, then I will do a lot more interest group creation and then later update - so more likely to have it happen. It takes a lot more effort to create a number of interest groups today. + +[David] No it doesn't take much more effort. You just have to wait and lose a couple of auctions on the page. By the time the user goes and leaves the page, you have a couple of double or triple taps on auctions where Chrome hasn’t loaded you up. Now the update occurs and you have a fully operational interest group. And then you have a similar problem. + +[Brian] If the pattern is I create shell interest groups vs. fully-functional interest groups that can be immediately used, I will create many more shells because they are easy to create. + +[Michael] The point is that to create a shell and turn into a real interest group - the space between those two operations is a few seconds, so you are not reducing the work really because in a few seconds you have to do the full update anyway. The difference in workload in the two scenarios does not seem enough to me to change people’s likely behavior. + +[Brian] If I can create light groups and coordinate quickly only with myself now, I’ll do that more likely. + +[Gianni] You wouldn’t be able to do it now, for example by returning an interest group that only has a daily update - no keys and no ads. WIthin that automatic, it is literally something you can do now if you have implemented the update function. If your update function updates the keys… (interrupted) \ + + +[Brian] Not suggesting we shouldn’t do this, but we need to be aware of how people might use it. + +[Michael] The only marginal change Gianni is asking for is to update this newly created interest group in a few seconds rather than waiting for the 10 minutes that is the current minimum update functionality. It would get updated in the first auction. So I don’t bother waiting for this interest group to be invited to be in an auction. + +Yao - this seems related to what you asked about in your agenda item (shown below) + + + **Yao Xiao** + + + ** discuss header api for joinIG https://github.com/WICG/turtledove/issues/82** + +[Yao] Thank you. Basically what happens today - the way tagging works - on the advertiser side we inject the iFrame and the server side returns a second response. Inside this second response we have the joinAdInterestGroup API getting invoked. In this way we can join the interest group and store it in the browser. But there are performance issues around iFrames and, equally, we have to make sure the tag supports the joinAdInterestGroup() API, which is a JavaScript API. But there are companies/users that don’t want to support a JavaScript API, they want a header-based solution instead. We have already done something like this for attribution reporting API and shared storage API. If we are going to move to the header-based approach above, we want to provide header-based support for all three endpoints - joinAdInterestGroup, leaveAdInterestGroup(), and ClearOriginJoinedAdInterestGroups(). + +[Michael] This is exactly the kind of support that would make this Gianni ask into a useful thing. Aligned with prior discussion. + +[David] So to the original request, you (Michael/Chrome) agree this ask is reasonable and useful, but can’t provide commitment/timing. Revising my earlier request for Chrome to provide a recognised means to ‘crowdsource’ your stakeholders’ interest to guide your dev priorities. + +[Michael] Github is the way to do that right now. Our PMs try to take in info from the various sources and trying to prioritize based on what they are hearing from the market. + +[Arthur Coleman] David, you asked this a while ago. My company has built a community site for Protected Audience, I just did the roadmap for it. Let's talk offline. Happy to pull something together, with Alex and other Google folks. I agree that it's needed, we should have a way for the community to thumbs up/down and help prioritization. + +[David] WICG repos now have the _Discussions_ feature available. That's a possible option here, where we already review issues. + +[Brian] Is there value in people placing a “thumbs-up” on issues? + +[Michael] People writing something in a github issue is better than thumbs up. Much more informative + +[David] Yao, are you looking for a way to express an _entire_ interest group in a structured header or just a portion? + +[Michael] Let me point out there are limits to the lengths of HTTP headers. + +[Notetakers note for reader’s reference: Each individual header (name and value combined) has a maximum length of approximately 4096 bytes. This limit ensures compatibility with various network protocols and prevents excessively large headers from causing issues. The total size of all request and response headers combined has a limit of approximately 250 KB.] + +[Michael] The only way we could do a good job of supporting that without putting a limit on interest groups is to do the lightweight approach. A flag that says I would like to update this interest group as soon as possible and some limit in the HTTP header for what parameters you could set. + +[David] Yes, in the issue we outlined a specific attribute set for this “shell” pattern. + +[Brian] You may want to create multiple interest groups for one owner at one time. + +[Isaac] Ability to create interest groups via header - doing the light shell with refresh would be highly valued. Publishers are always hesitant to add JS to their page. + +[Michael] 100% our motivation for doing the header approach. + + +## [Jeremy Bao] - Solution for negative interest groups + +We didn’t get to cover that today. Several requests around that and we are creating a proposal around that. I see several folks on the call related to that proposal - please take a look at it. Github Issue [#896](https://github.com/WICG/turtledove/issues/896). diff --git a/meetings/2024-07-10-FLEDGE-call-minutes.md b/meetings/2024-07-10-FLEDGE-call-minutes.md new file mode 100644 index 000000000..50c27abc5 --- /dev/null +++ b/meetings/2024-07-10-FLEDGE-call-minutes.md @@ -0,0 +1,235 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday July 10, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Roni Gordon (Index Exchange) +3. Brian May (Dstillery) +4. Matt Menke (Google Chrome) +5. David Dabbs (Epsilon) +6. Matt Kendall (Index Exchange) +7. Sven May (Google Privacy Sandbox) +8. Orr Bernstein (Google Privacy Sandbox) +9. Brian Schmidt (OpenX) +10. Harshad Mane (PubMatic) +11. Ricardo Bentin (Media.net) +12. Fabian Höring (Criteo) +13. Arthur Coleman (IDPrivacy) +14. Laurentiu Badea (OpenX) +15. Kevin Lee (Google Privacy Sandbox) +16. Matt Davies (Bidswitch | Criteo) +17. Victor Pena (Google) +18. David Tam (Relay42) +19. Owen Ridolfi (Flashtalking) +20. Laura Morinigo (Samsung) +21. Jeremy Bao (Google Privacy Sandbox) +22. Paul Spadaccini (Flashtalking) +23. Omri Ariav (Taboola) +24. Alonso Velasquez (Google Privacy Sandbox) +25. Neha Gautam (Carnegie Mellon) +26. Becky Hatley (Flashtalking) +27. Kenneth Kharma (OpenX) +28. Aymeric Le Corre (Lucead) +29. Andrew Pascoe (NextRoll) +30. Abishai Gray (Google Privacy Sandbox) +31. Maybelline Boon (Google Privacy Sandbox) +32. Alex Peckham (Flashtalking) +33. Sid Sahoo (Google Chrome) + + +## Note taker: David Tam (or when he's talking: Arthur Coleman) + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 +* Jeremy: + * Seek feedback on Negative IG solution and usage budget +https://github.com/WICG/turtledove/issues/896 + +* David Tam: + * Seek feedback on proposal for how seller can set interest groups for buyers to bid on - https://github.com/WICG/turtledove/issues/1196 +* Matt Davies: + * Seek feedback on third party reporting + + https://github.com/WICG/turtledove/issues/1220 + + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for checking the accuracy of the notes doc, especially the part with their own comments - so go back later and make sure the notes are accurate. You can even fix them on Github later!! :) + + +# Notes + + +## JeremyBao: Seek feedback on Negative IG solution and usage budget, https://github.com/WICG/turtledove/issues/896 + +Jeremy Bao - product manager working on PAAPI with Privacy Sandbox. Seeking feedback on negative IG proposal. Supports IG targeting on contextual bids, can support negative targeting in PA bid. Add a field in IG. Must have the same owner. When the auction is initiated then the negative IG is filtered out. For negative IG set maximum 3 per IG, for contextual the maximum is 10. + +David Dabbs - Great. I wanted to bring your attention to Fabrice's question about needing additional bid key in PA bids + +Orr Bernstein: Don’t need additional bid key in negative IG. + +Additional bid keys are needed for additional bids. + +David Dabbs - Potential expansion of this feature. Is it possible to have negative targeting for a single positive IG. Don’t participate if the user is in a positive IG. + +Jeremey Bao - I assume the solution will solve this. + +David Dabbs - Is it beyond the pale of privacy protection. + +Jeremey Bao - Negative IG are tiny. Is there any reason why you just do not use negative IG. + +Michael - Positive and negative roles are separate from each other. It seems wise to keep them that way. I don't want to deal with questions of what happens when positive IG A is filtered out because of a negative IG B but is also being used as a negative IG by positive IG C. + +Brian May - What is the implication for k-anon. + +Michael - None at all. K-anon is not based on who is eligible to participate in an auction, only on winners of an auction. If an IG does not bid in an auction then it has nothing to do with k-anon- If you are not a potential winner of an auction then there is no k-anon. Only candidates that won an auction. + +Omri Ariav - adding negative targeting to the paapi flow can help in the negative IG native advertising use case. How to follow these announcements. + +David Dabbs - Watching the repo and the issues. + +Omri Ariav - Suppression or negative IG in PA flow. In terms of timeline what is the expectation of providing feedback. + +Jeremey Bao - we are still working on that piece. Timeline is not finalised. + +Michael - There are many github issues around the same set of questions. #1072, #1092 are similar issues. The negative IG work is also relevant. #1199 is more about interactions of ads on the same page. + +David Dabbs - Negative IG has to have the additional bid key. + +Michael - To remove from the auction contextually targeted ads. Extending the negative targeting capability it can also apply to bids from other IGs. + +David Dabbs - There was a negative IG could not have an updateURL. Requesting that the scope of the lightweight creation of IGs via HTTP headers encompass this new negative targeting use case. A neg IG is a lightweight IG use case. + +Jeremy Bao - Don’t know much about header joining. Need to explore more. Negative IG still additional bid keys. IG may still need to make additional bids. Additional bid key is part of a negative IG, though not needed for this new PA bids usage. + +Orr Beinstein - You should set an additional bid key in your negative IG, that way you can use it in both ways, contextual and other-IG. + +Michael - Even if you intend to use negative IG exclusively for negative targeting from other IGs, and you're sure that additional bids are irrelevant, the additionalBidKey is still needed to make the negative IG work. That's how we recognize a negative IG in the first place. + +Orr - It still needs to be there. + +David Dabbs - Not proposing that neg IGs be updatable. As I recall you have a validity check on addlKey (so passing empty string or even some non-key token value) probably not work. + +Orr - Join negative IG instead of join IG. + +Michael - Let negative IG filter out positive IG. Additional bid is a required field. + +David Dabbs - Over time the special-case single value `negativeInterestGroup` attribute would be unneeded and be deprecated (since you are eliminating the same-origin restriction). + + +Orr - Yes, we can simplify things. + + +Jeremy Bao - Agree that you do not need that special thing. An attribute maybe. No need to read origin domain. + + +## David Tam: Seek feedback on proposal for how seller can set interest groups for buyers to bid on - https://github.com/WICG/turtledove/issues/1196 + +[David Tam] Came up with a proposal on how publishers could create interest groups. Idea is that we add additional attributes to the IG to signify that it is a seller defined interest group. Based on that we can populate the dynamic attributes. Want to know if that is a possibility. + +[Michael] Looking at your proposal and who is here. No one from RTB House seems to be here. Their prime audience product is a seller-based audience launched on top of Protected Audiences - so it sounds like they are doing what you are proposing. This is a use case we have discussed in the past. + +[David] RIght now only the buyer can create an IG. But according to the specification there are three parties involved. The advertiser, the tech vendor that an advertiser can delegate to, and the publisher. So when a publisher creates an interest group, how does a buyer buy that interest group? The only way that is monetized today is that the advertiser or the delegated tech vendor of the advertiser creates it. It implies that all the attributes of that audience are known at creation time of the renderURL which, if this were a publisher-created interest group, you would not know in advance if you don’t know who the buyer is. + +[Michael] An interest group inherently has to contain some bidding logic since the interest group is the element that produces bids. If you want to use a model in which a publisher wants to participate in the creation of an audience, people who visit their site, then the model we have discussed before is one in which the publisher is going to need to work with some ad tech or multiple adtechs involved in the audience use business. So you are quite right that at some point there needs to be a delegation from the publisher that is involved in audience creation to the person who is actually doing the bidding. In the Protected Audience model we have today, that happens at the time of interest group creation. So if a publisher wants to create an interest group and make it available to different buyers, we recommend they have a set of different interest groups, one for each buyer they are offering these interest groups to. We are not set up to have anybody build one interest group that holds onto different pieces of bidding logic that come from multiple different ad techs. We put everything together by having a bunch of different interest groups - one each for each of the potential buyers. + +Second, for what you asked about the ad creatives not known in the browser ahead of time but only something you learn as a response in the middle of the auction as a response from the key value server: It is a problem because it does not play well with the k-anonymity constraints. The way that a browser knows that an ad is k-anonymous and therefore knows that it has appropriate privacy properties to show to a user, involves looking information up on the k-anon server which is an operation that generally happens earlier in the process, not in the critical path. So it is not viable from the latency point of view for the browser to only learn what the candidate ads are after all the bidding, key value return and and then Javascript execution of the bidding has taken place. That is late in the process. A lot of other issues - poor performance properties and poor privacy properties, for example. There is a issue in Github repository by Martin Thompson that points out the kind of privacy leakage that you could exploit if we made the change you're asking for. + +[David] So in the context, how would a publisher create an interest group to sell to different buyers that want to bid for it? + +[Michael] Exactly the reason I am talking about this. I think it is extremely reasonable for anyone to create and monetize an audience. But the transaction in which there is some kind of deal between the audience creator and buyer of that audience is not something that happens in the moment of the auction. Like many parts of PAAPI, things should happen ahead-of-time even though in ORTB we think about them happening at auction time. With PA we think of them happening earlier at interest group creation time or update time, for example. This is a good example of that. The audience creators' choice of what buyers they want to work with given an opportunity to buy this audience in exchange for dollars is something we feel should take place ahead of time and if that requires some new sort of reporting for this audience at bidding time, then we are interested in providing additional reporting functions. A lot of discussion on this topic has happened in this group. We certainly can talk about shortcomings. But handing an audience from an audience creator to a buyer in the Protected Audiences world has to happen ahead of time, not in the moment of the auction. + +[Daivd] Does that mean you are effectively agreeing that in some kind of offline world, that you want to buy this audience. So the publisher or whoever they delegate to create the audience will sell it while the creative… that all of that is basically agreed to in advance before that interest group is created? + +[Michael] I think that the sort of thing you are talking about could happen in an automated real-time way at the time of audience creation. Most things that involve financial transactions between two people tend to need some kind of setup to happen ahead of time outside the browser, of course, because it involves money changing hands. So I don’t think that is something dramatically new and different. Aside from the issue of how the payment channel gets established, the idea of creating a new audience and making it available to multiple parties to bid on is absolutely something that can happen in real time between two parties that have agreed on audience creation and then audience delegation based on whatever protocol and financial consideration they want. Again, I'm sorry the RTB House folks aren't here because they have worked on this and I expect they would have interesting opinions to contribute. Maybe we can revisit this on a future call when folks with more experience are here. + +[David] Last question, in the case of the publisher creating the interest group, who initiates the auction? + +[Michael] Exactly as they are today. Initiated on a publisher page by a publisher or ad tech that publisher works with. + +[David] The interest group will basically have the owner initiate some buyer. And then the bid requests will go to that buyer. Correct? + +[Michael] Yes that is the way it works today in Protected Audiences. + +[David] Do you have a reference to the RTB product? + +[Alonso] Yes, Prime Audience. [www.primeaudience.com](www.rimeaudience.com) + +[Brian] Wavering back and forth because I’m not sure how far into this we want to get. I assume the concern for publishers is that I want to maintain control over the value that I provide to my buyers via my audiences. We may want some way of metering the value that I am providing a buyer so that I know what my interest groups are. Are some interest groups more valuable on one browser versus another? And if someone is doing something I am uncomfortable with, can I block them from using those audiences? Publishers want to abide by a set of rules, and those are maintained by the goodwill of those who are using them versus any control you have directly over things. + +[Michael] That makes a lot of sense. Offhand I can think of three plausible approaches to deal with that set of questions: + + + +1. Some contractual arrangement between audience creator and audience buyer on what those rules are and they enforce the contract however they want. +2. There is some mechanism in the browser that allows for _reporting,_ so that the party that was involved in audience creation can get a stream of information about how the audience later gets used. That lets them monitor proper usage vs.the contract. That is a monitoring approach wiIth the possibility for after-the-fact repercussions if someone is acting in the wrong way - after the fact reporting. +3. There is some mechanism in the browser that allows for _control,_ so that the use of interest groups is not solely in the hands of the buyer. Instead there are other controls - multiple parties involved in real-time that ensure that the interest group is used in the way it was supposed to - buyer and seller have independent control over what the interest group does at the moment at the auction. + +Seems to me like 3 is rather difficult - not sure if it's impossible, but at least relatively difficult and different than how Protected Audience mechanics work today. On the other hand 2 - seems very compatible with the way Protected Audiences works today. This may be fully supportable today with our additional reporting endpoints, or we may need to add some additional reporting so that parties involved in audience creation can be sure they have an ongoing ability to monitor how the audiences are used. If there are holes in reporting that make it not suitable for this use case, then we can talk about adding additional reporting features. + +[Brian] I think for the second option I would want to be able to kill my interest groups as well. If someone doesn’t abide by contract, shut them down and make them non-functional + +[Michael] I understand the desire for a kill switch, but I’d have to think about how to implement that. Doesn’t seem like something that naturally exists today. Less demanding than full control, kind of an option 2½ . So I would need to think about it. + +[Brian] Maybe KV server could allow the owner to do that + +[Michael] The KV server touching the browser at the time of the auction is in control of the buyer, so the wrong party in the trust model you're worrying about. I will keep thinking about this. It is an entirely reasonable use case and one we are happy to talk about supporting. If features are missing, we can certainly find them. + +[Roni] Brian captured a lot of it. A difference between “this is my audience and you can use it” and “I am creating an interest group on your behalf” - which is what the browser lets you use today. Sure it can be done post- auction - but that is a bit problematic. Sure reporting should tell you what happened but there should be better responsiveness. There are then scalability challenges. Also the issue of making 100s of interest groups for every buyer they have seems “heavy” and problematic. In the interest of time, I will leave it there but want to noodle more. + +[Michael] Yes, interesting things for us to think about. + +[Matt Davies] In generic terms, what about third parties that want to create an interest group? Maybe we give it to an agency, who then might want to have multiple options for DSPs to do that. Does it just have to be at time of creation or could there be other use cases? Where a data provider can create the interest group and then have it duplicated into multiple SSPs/DSP? + +[Michael] Right now it has to be done at creation time. Willing to think about your use case, but am concerned about cloning an interest group. That sounds like a way to get runaway interest group creation. Something we need to pay attention to. Figuring out how to defer…how to allow a third party to take the interest groups or to allow them to default to them. You can’t have a set of different bidders each running their own code. Not interested to have every interest group having an auction that feeds into itself. Not interested in creating taller and deeper trees of IGs at auction time. + +[David] Agree with Roni. Does not make sense for publishers to create hundreds of thousands of interest groups. And if only one buyer can actually bid on an interest group then why is there a need for PA? The aim is for the seller (publisher) to create an interest group and auction this interest group to a number of buyers. + +[Michael] Are we talking about a buyer bidding on an interest group created by a publisher when visiting that same publisher's site, or when they leave the publisher site and see an ad elsewhere? If directly on their site, you don’t need Protected Audiences at all! That can just be done with information passed between the publisher and the buyer, and it doesn’t touch any of these APIs at all. Protected Audiences only applies if you want to create an audience on site 1 and use it on site 2. If you're using it on site 1 as well, for example a "Seller Defined Audiences" use case, then this whole discussion is not relevant. + +[Brian] So agree with Roni about the explosion of interest groups to gain a foothold on browsers. We need to find a way to prioritize interest groups. + +[Michael] It is what we already do. + +[Brian] What I’m concerned about is you hit a publisher page, it creates a bunch of interest groups, it washes out a number of interest groups - and we end up constantly churning through interest groups. + +[Michael] Excellent reason that interest group creation should be in the hands of buyers who are actually using the interest group. diff --git a/meetings/2024-07-17-FLEDGE-call-minutes.md b/meetings/2024-07-17-FLEDGE-call-minutes.md new file mode 100644 index 000000000..29e616d9c --- /dev/null +++ b/meetings/2024-07-17-FLEDGE-call-minutes.md @@ -0,0 +1,224 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday July 17, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Brian May (Dstillery) +3. Tomer Ben David (Taboola) +4. Sven May (Google Privacy Sandbox) +5. Roni Gordon (Index Exchange) +6. Andrew Pascoe (NextRoll) +7. Fabian Höring (Criteo) +8. Russ Hamilton (Google Privacy Sandbox) +9. Paul Jensen (Google Privacy Sandbox) +10. Sathish Manickam (Google Privacy Sandbox) +11. Laura Morinigo (Samsung) +12. Orr Bernstein (Google Privacy Sandbox) +13. Matt Menke (Google Chrome) +14. Harshad Mane (PubMatic) +15. Laurentiu Badea (OpenX) +16. Ricardo Bentin (Media.net) +17. Alex Peckham (Flashtalking) +18. Owen Ridolfi (Flashtalking) +19. Rickey Davis (Flashtalking) +20. Becky Hatley (Flashtalking) +21. Tim Taylor (Flashtalking) +22. Arthur Coleman (IDPrivacy) +23. Matt Kendall (Index Exchange) +24. Matt Davies (Bidswitch | Criteo) +25. Tamara Yaeger (BidSwitch) +26. Isaac Foster (MSFT Ads) +27. Warren Fernandes(Media.net) +28. Anthony Yam (Flashtalking) +29. Kevin Lee (Google Privacy Sandbox) +30. Jeremy Bao (Google Privacy Sandbox) +31. Achim Schlosser (Bertelsmann) +32. Luckey Harpley (Remerge) +33. Koji Ota(CyberAgent) +34. Alonso Velasquez (Google Privacy Sandbox) +35. Antoine Niek (Optable) +36. Denvinn Magsino (Magnite) +37. Abishai Gray (Google Privacy Sandbox) +38. David Dabbs (Epsilon) +39. Felipe Gutierrez (MSFT Ads) +40. Ken Gordon (Azure) +41. Jacob Goldman (Google Ad Manager) +42. Kenneth Kharma (OpenX) +43. Shafir Uddin (Raptive) +44. Maybelline Boon (Google Privacy Sandbox) +45. Laszlo Szoboszlai (Audigent) +46. Hari Krishna Bikmal (Google) + + +## Note taker: Tamara Yaeger + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + +* Matt Davies: + * Seek feedback on third party reporting + + https://github.com/WICG/turtledove/issues/1220 + + +* Warren: + * [Addition of an analytics/reporting entity to enable centralised reporting · Issue #1115 · WICG/turtledove · GitHub](https://github.com/WICG/turtledove/issues/1115) + +* David Dabbs + * Request for `updateURL` processing to support the leaving/joining of negative interest groups via the [nascent header feature](https://github.com/WICG/turtledove/issues/896) +(entered as issue [#1228](https://github.com/WICG/turtledove/issues/1228)) + + In the “shell IG” pattern a buyer joins an IG with a minimal, updateable attribute set. This centralizes and defers page latency inducing processing (computing and rendering IG content) to updateURL time. (Proposed) nNegative targeting adds a wrinkle. Scenario: a buyer intends to negatively target a positive interest group. The Chrome-suggested approach has the buyer pair the excluded positive IG with a negatively-targetable ‘negative shadow.’ At site visit time, the buyer joins the browser to “shells” of “EXCLUDED_POSITIVE,” the excluded IG, “EXCLUDED_NEGATIVE,” its negative ‘shadow,’ and “EXCLUDER,” an IG that seeks to negatively target the other group. EXCLUDER’s update will configure EXCLUDED_NEGATIVE in `negativeInterestGroups[]`. If EXCLUDED_POSITIVE’s update determines that IG membership condition doesn’t hold (or the update doesn’t run &c.), the buyer has a partial, incorrect IG set. + + The buyer’s ability to join or leave EXCLUDED_NEGATIVE during EXCLUDED_POSITIVE’s update would smooth this wrinkle. The primary can ensure its ‘shadow’ is always appropriately joined when it is processed. + + * Request for nascent header join/leave capability to support [fetchLater API](https://developer.chrome.com/blog/fetch-later-api-origin-trial) + (entered as [comment](https://github.com/WICG/turtledove/issues/896#issuecomment-2233667864) on #896) + + Chrome is [migrating](https://issues.chromium.org/issues/40236167) keep-alive request handling from the “renderer” (front-end) to the “browser” process (back-end). [Explainer](https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#). Attribution Reporting API (ARA) supports event and trigger header registrations on background requests, and this will move to the browser. ARA team has [extended that hook](https://issues.chromium.org/issues/40242339#comment48) so that fetchLater API requests will also be able to set ARA headers. Requesting that Protected Audience header processing also support fetchLater. + + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for checking the accuracy of the notes doc, especially the part with their own comments - so go back later and make sure the notes are accurate. You can even fix them on Github later!! :) + + +# Notes + +[Github: Third Party Reporting #1220](https://github.com/WICG/turtledove/issues/1220) + +Matt Davies (BidSwitch): + +Applies strongly to BidSwitch but sure many others in the supply chain would require access to this. Idea is to additional reporting party URL w/in PA response that comes back from oRTB where credentials are provided to the SSP / seller, to then be able to create their own PA auction. Idea would be to add a few extensions for 3P IG signals (HTP, URL, DATA URL) which would allow 3Ps to have access to auction results. You may have a direct participant or non-direct chain, it might be that the IG allows DPS A to trade with SSP B directly. The same IG may also be used on another transaction that goes through BidSwitch or 3P operator where they need to register that they are in the chain. It would be useful to understand if it’s a worthwhile use case. + +Michael Kleber (Chrome): Let me ask some questions to understand the relationship of parties. Trying to scope the story; in the PA world, we tend to think about there being 2 parties, buyer and seller, and we have possibility of component vs top-level seller, but don’t need to worry about that now. The way that we think about things like how do ppl get onto the pub page? The pub page is owned by the pub (1st party) and then the fact that anyone else is on the pub page is the result of some delegation / invitation, so we think about it as the pub invites some list of sellers to sell inventory - SSPs - who then invite buyers to buy into their auctions. If pub delegates to seller, and seller to buyer… where does BidSwitch use case fit in chain of delegation? Who is it that invited BidSwitch to be a participant in this auction? + +Matt: Depends how much you want to separate oRTB from PA. Basis is that oRTB side of equation starts that process; oRTB request goes to DSP, they send a response with PA credentials, from there seller configs; after that it’s sent to the seller and the auction is between just the buyer and seller. Part of where BSW comes in is in oRTB; many DSPs will have 15 top supply partners w direct integration, but work with 40-50 smaller entities like BidSwitch or others e.g. Magnite. When you see application traffic you see SSP partners inviting other SSPs. There are chains of oRTB transactions where it goes through, but impressions and spend need to be recorded; agreement w DSP / SSP. Required to have certain live accurate data around giga transactions. For sake of argument, imagine SSP ABC, and XYZ DSP. They run through BidSwitch; so the SSP sends oRTB request, transmitted to dSP, the DSP would respond via BidSwitch about auction participation. We will then pass to SSP, who will create component auction, which will then invited DSP directly in component in auction. Once impression created, noone in the chain would be able to record and have access to transactions. + +Alonso Velasquez (Google PSB): Ultimately we need to understand whether this reporting is in the service of the buy or sell side, or representing both in some cases as it may be with BidSwitch. I gather that the way to scope this problem is, that there are adtech parties that are not the seller or buyer and are still in the service of the Advertiser or Publisher, that certain reporting capabilities are missing from the PA auction in order for these parties to get the reporting needed to render their services to the Publisher or Advertiser. + +Matt: While BSW can’t run component auction…. + +Michael: It seems one natural answer is, why doesn’t BidSwitch run a component auction? If BidSwitch is a player that gets some amt of awareness of demand, and propagates demand to some collection of buyers that buy proxied through BidSwitch, then why isn’t it that BidSwitch is a component buyer in PA auction directly? What would prevent BidSwitch from being component seller? + +Matt: But the SSP we’re repping would also want to work their own component auction. There’s only top-line level seller (GAM / Prebid), then pub , seller, maybe exchange, buyer, etc. Only 2 components can run a component auction and no one else (?) + +Michael: Agreed, but why can’t we flatten that out and have BidSwitch be a component buyer directly in top level? Ah, but you’re saying BSW is only here because SSP invite, so SSP would get cut out of reporting chain? + +Matt: Yes, that would be the problem. There might be data providers that may need to also get access to this data. IGs belong to the DSPs not us, and we’re not privy what DSP wants to run in campaign at any given time. + +Michael: It seems like everything you are trying to do involves some kind of winning bid that went through BidSwitch during OpenRTB phase; that annotation is something winning component SSP is in good position to add, if they choose to. They know everything about their supply lines, when winning SSPs evaluates each bid they know they got it because of BidSwitch’s involvement in oRTB side. It’s possible for the reporting to happen, but only if the component SSP does the right thing. What if SSP doesn’t annotate the bid correctly? + +Matt: We wouldn’t want to rely on 3P for access… + +Michael: But isn’t it the SSP that invited BidSwitch? + +Matt: Yes but the tech or them to be able to feed the info back to us, would they add us to reportWin? It would be very useful, but it would also be useful to be able to state that we were def part of the auction instead of relying on the original SSP to confirm… what if it doesn’t happen 100% of the time? + +Harshad Mane (PubMatic): Another use case. Currently pubs rely on component sellers; let’s say pub is working w 5 SSPs, pub needs to combine all these reports offline. Pub doesn’t have way to get real time reports from auction. + +Michael: I agree we have been talking about getting direct reporting to the pub. Seems different from Matt’s use case; we all know why pub is entitled to reporting. They’re top of the chain of delegation of right to be on page, but now we’re talking about more steps removed from that. How do we even know who the parties are who are supposed to get reporting seems like the difficult part. Difficulty is – if BidSwitch is only in the auction because an SSP invited BidSwitch, then that SSP is the channel that sits between you and the browser. If SSP is not trustworthy to include BidSwitch, then anything I can do as a browser would only be in the reporting configuration proxied by the SSP. + +Brian May (Distillery): There is a question of trust, but also of people making mistakes and not recognizing it. If the SSP is missing something that needs to be visible. + +Alonso: Might be worthwhile to say we have infrastructure for both sellers and buyers to declare 3P destinations and which data from the auction they can receive (fenced frames, ad reporting infrastructure). Config needs to be done by seller / buyer still, but it’s feasible. [Additional edit from Alonso: we know that the ability to declare additional destinations is present for the buyer and not the seller, so this is perhaps an area to get feedback on below]. For more than a year we have known of 3Ps that need to get reporting; about a year ago enhanced flexibility for different kinds of reporting. Will share the explainer here: https://github.com/WICG/turtledove/blob/main/Fenced_Frames_Ads_Reporting.md . I encourage everyone to consider how far the reporting capabilities listed here from what you’re envisioning as the need and give us feedback on that. + +Warren (Media.net): Makes sense on buy side, but gap is on sell side; we don’t have a similar / easy way to have list of 3P behind use to get similar reporting. There’s no easy way built into the system for sellers to do what buyers can do. + +Michael: So the problem is that sellers would want an endpoint for reports to get sent to, but not be sent there all the time. There should be a step which makes it different from pubs getting duplicate reports, which could plausibly happen all the time. Somehow someone needs to trigger a win to have an additional party to get dupe copy of reporting. + +Warren: How do we trigger it, even when we figure out whose impression it is? + +Michael: As a browser person, I have to ask: why doesn’t the SSP server that receives the win report serve a response that is an HTTP redirect to deliver that report to another host in addition? Why can’t the SSP doing a browser redirect solve the problem? + +Matt: In our experience HTTP redirect leads to big discrepancies, 2-3%, which disappears when you have server to server APIs, then it’s 0.1%. It also leads to extra processes. We trust our SSPs implicitly, but as with anything, it is easier to do inhouse. Imagine chain, DSP send bid response to us, imagine there is something that shows which request it should be applied to. + +Roni (Index): Problem is if BSW represents buyer origin, there is nothing in reporting results to show it came from BSW. There is no notion of a “representative”. reportResult() only has IG owner and renderURL – it would be indistinguishable from a bid from the ultimate buyer that BSW is representing. So it’s not just a matter of calling sendReport() multiple times – there’s no way to know at reportResult() time that BSW was involved. + +Michael: If I understand correctly, the contextual response that was put there by BSW as part of OpenRTB contextual response, that should be enough info so that the SSP partner can look at contextual response as part of seller signal, and it should know all the bids coming from buyer, are bids that are in auction because of BidSwitch as an intermediary. Is that right? That the seller and buyer and seller signals are enough for SSPs to figure out whether to trigger BidSwitch reporting. + +Matt: Theoretically, unless the SSP knows which IG group won. They won’t know which path it went to. + +Michael: But they know who the buyer is, sounds like there is enough info. + +Matt: We don’t add anything to to PA response, we just send it back to them. There is nothing in that response that we would add to; if we were able to give a field to fill in in advance, they can put into that auction config to get automatically called when impression occurs. + +Michael: Part of your GitHub proposal is about wanting some new JS worklet coming from a new party that gets to execute on device. For that part of your request, as browser we’d prefer not to have additional worklets to contribute additional JS; possible but worst case scenario, in making the API more heavy. Having additional destinations is more appealing if the report can be built by the browser based on some static declaration, from an API design POV. Sending a browser-crafted report is a lot nicer than downloading someone’s JS to run a worklet. It would be great to know if there is a version of this proposal that could be implemented as a static config. I’m unclear on how triggering works, how the SSP can know which bids should get BSW reporting and which do not. Let me re-read notes and responses; if it’s really just a function of buyer / seller component auction and something that can be written in OpenRTB response. + +Roni: This is not merely about notifying BidSwitch, the seller needs to know that BidSwitch is involved at all stages. SSPs need to collect money from BSW in this scenario, so everything in the APIs – KV, scoreAd, reportResult, etc. – would need this awareness. + +Matt: At end of day we will still need to pay the SSP + +Michael: No one mentioned money changing hands goes through BidSwitch, not only an info flow. + +Matt: We are the proxy, we would invoice and pay SSP out is how we operate. We would be in billing chain as well. The only other option would be for SSP to be calling with new URL… + +Michael: The fact that a particular bid / buyer was proxied through BidSwitch, that info is knowable inside the auction, ScoreAd function, report win, report results, hopefully that’s still the case. Roni said that info needs to be known in the seller's key value server, which right now learns just the render URL, that adds further complication. Also Roni’s question is another complication – what if the same buyer has a direct relationship with the SSP AND a connection through BidSwitch? + +Roni: Nothing prevents me from onboarding a direct relationship, that can happen any day. I can map Brian, tomorrow it may be indistinguishable if Brian goes through Matt. The same IG fires, the same IG bid gets submitted, so I would continue to charge Brian. By design once that bid wins, that relationship is lost. + +Michael: It sounds like you’re increasingly making the case that the right answer is for BSW to be a component SSP. + +Matt: If we could do that, that would be amazing and we would build it out; we’ve been looking at it from the beginning, but it wasn’t in the spec. If we could run a component auction, passing details back and forth, that would create a third level auction. + +Michael: Point is that maybe we can flatten the tree and BidSwitch can be a component auction. Anyone bringing demand is a component auction. + +Warren: Comes back to previous issue about SSP then not having reporting… one or the other + +Michael: BidSwitch could send it reporting to the SSP that invited it to the auction. + +Brian: I think BidSwitch is more of an alias than a replacement. + +Paul (Chrome): What if 2 component auctions + +MIchael: I was thinking more linked component SSP that runs component auction could list each buy as a buyer or buyer-becaeuse-of-BidSwitch, making that level of aliasing more obvious. Then winning bid would come from buyer X or buyer X-because-of-BidSwitch. IG would belong to buyer X. If same buyer has both relationships, does that buyer bid twice in same component auction? + +Roni: Nothing prevents that from happening, multiple bids into the same auction. The KV doesn’t get the IG owner, I have to guess who owns the render URL. Now it would be another level of indirection. It’s not just who owns the IG, it’s who’s representing the buyer origin commercially? + +Brain: Suggest for Matt to mull over and come back + +Michael: If we propagate the buyer as part of reporting, and maybe each alias /annotation of buyer gets automatic reporting, maybe it’s enough to address this. + +Paul: What reporting are you looking for? + +Matt: Impression, spend, and click – did a billable event occur? From there we can run at least a record of the transaction occurring. + +Brian: If we’re reporting 3P data there needs to be consideration that the data wasn’t tampered with. diff --git a/meetings/2024-07-24-FLEDGE-call-minutes.md b/meetings/2024-07-24-FLEDGE-call-minutes.md new file mode 100644 index 000000000..67110afa4 --- /dev/null +++ b/meetings/2024-07-24-FLEDGE-call-minutes.md @@ -0,0 +1,237 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday July 24, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Brian May (Dstillery) +3. Eyal Segal (Taboola) +4. Roni Gordon (Index Exchange) +5. David Dabbs (Epsilon) +6. Isaac Foster (MSFT Ads | The Avengers) +7. Konstantin Stepanov (MSFT Ads) +8. Felipe Gutierrez (MSFT Ads) +9. Patrick McCann (Raptive) +10. Sven May (Google Privacy Sandbox) +11. Harshad Mane (PubMatic) +12. Marco Lugo (NextRoll) +13. Matt Kendall (Index Exchange) +14. Elmostapha BEL JEBBAR (Lucead) +15. Matt Davies (Bidswitch | Criteo) +16. Tamara Yaeger (BidSwitch) +17. Ivan Staritskii (Bidswitch | Criteo) +18. B. McLeod Sims (Media.net) +19. Owen Ridolfi (Flashtalking) +20. Anthony Yam (Flashtalking) +21. Harry Stevens (Bidswitch | Criteo) +22. Becky Hatley (Flashtalking) +23. Jason Lydon (Flashtalking …) +24. Arthur Coleman (IDPrivacy) +25. Alex Cone (Google Privacy Sandbox) +26. Jeroune Rhodes (Privacy Sandbox) +27. Alonso Velasquez (Google Privacy Sandbox) +28. Kevin Lee (Google Privacy Sandbox) +29. Ashley Irving (Google Privacy Sandbox) +30. Sid Sahoo (Google Chrome) +31. Orr Bernstein (Google Privacy Sandbox) +32. Sathish Manickam (Google Privacy Sandbox) +33. Fabian Höring (Criteo) +34. Laura Morinigo (Samsung) +35. Paul Jensen (Google Privacy Sandbox) +36. Matt Menke (Google Chrome) +37. Andrew Pascoe (NextRoll) +38. Antoine Niek (Optable) +39. Alex Peckham (Flashtalking) +40. Guillaume Polaert (Pubstack) +41. Sarah Harris (Flashtalking) +42. Taranjit Singh (Jivox) +43. Tim Taylor (Flashtalking) +44. Viacheslav Levshukov (Microsoft) +45. Josh Singh (Microsoft) +46. Miguel Morales (IAB TechLab) +47. Achim Schlosser (Bertelsmann) +48. Paul Spadaccini (Flashtalking) +49. Brian Schneider (Google Privacy Sandbox) +50. Matthew Atkinson (Samsung) +51. Jeremy Bao (Google Privacy Sandbox) +52. Aymeric Le Corre (Lucead) +53. Pierre Perez (Azerion) +54. Shafir Uddin (Raptive) +55. Premkumar Srinivasan (Microsoft Ads) +56. Daniel Rojas (Google Privacy Sandbox) +57. Courtney Johnson (Privacy Sandbox) +58. Manny Isu (Google Privacy Sandbox) +59. Amitava Ray Chaudhuri (Adobe Adcloud) +60. Koji Ota(CyberAgent) +61. Laurentiu Badea (OpenX) +62. Suresh Chahal (Microsoft) +63. Nick Colletti (Raptive) +64. Abishai Gray (Google Privacy Sandbox) +65. Veronica Kim (Raptive) +66. Jinhwa Rustand (Nativo) +67. Siddharth VP (Jivox) +68. Warren Fernandes(Media.net) +69. David Tam (Relay42) +70. Rickey Davis (Flashtalking) +71. Hillary Slattery (IAB Tech Lab) SORRY KLEBER! +72. Arian Senior (IAB France) +73. Maybelline Boon (Google Privacy Sandbox) +74. Caleb Raitto (Google Chrome) +75. Hari Krishna Bikmal (Google Ads) + + +## Note taker: Manny Isu, Alonso Velasquez + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Discussion of Monday's "A new path for Privacy Sandbox on the web" news https://privacysandbox.com/news/privacy-sandbox-update/ + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + +* Warren: + * [Addition of an analytics/reporting entity to enable centralised reporting · Issue #1115 · WICG/turtledove · GitHub](https://github.com/WICG/turtledove/issues/1115) + +* David Dabbs + * Low entropy client hints on TBS requests ([#1031](https://github.com/WICG/turtledove/issues/1031)) + * Interest also in updateURL and real-time reporting postbacks. + + * Request for `updateURL` processing to support the leaving/joining of negative interest groups via the [nascent header feature](https://github.com/WICG/turtledove/issues/896) +(entered as issue [#1228](https://github.com/WICG/turtledove/issues/1228)) + + In the “shell IG” pattern a buyer joins an IG with a minimal, updateable attribute set. This centralizes and defers page latency inducing processing (computing and rendering IG content) to updateURL time. (Proposed) negative targeting adds a wrinkle. Scenario: a buyer intends to negatively target a positive interest group. The Chrome-suggested approach has the buyer pair the excluded positive IG with a targetable ‘negative shadow.’ At site visit time, the buyer joins the browser to “shells” of “EXCLUDED_POSITIVE,” the excluded IG, “EXCLUDED_NEGATIVE,” its negative ‘shadow,’ and “EXCLUDER,” an IG that seeks to negatively target the other group. EXCLUDER’s update will configure EXCLUDED_NEGATIVE in `negativeInterestGroups[]`. If EXCLUDED_POSITIVE’s update determines that IG membership condition doesn’t hold (or the update doesn’t run &c.), the buyer has a partial, incorrect IG set. + + The buyer’s ability to join or leave EXCLUDED_NEGATIVE during EXCLUDED_POSITIVE’s update would smooth this wrinkle. The primary can ensure its ‘shadow’ is always appropriately joined when it is processed. + + * Request for nascent header join/leave capability to support [fetchLater API](https://developer.chrome.com/blog/fetch-later-api-origin-trial) +(entered as [comment](https://github.com/WICG/turtledove/issues/896#issuecomment-2233667864) on #896) + + Chrome is [migrating](https://issues.chromium.org/issues/40236167) keep-alive request handling from the “renderer” (front-end) to the “browser” process (back-end). [Explainer](https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#). Attribution Reporting API (ARA) supports event and trigger header registrations on background requests, and this will move to the browser. ARA team has [extended that hook](https://issues.chromium.org/issues/40242339#comment48) so that fetchLater API requests will also be able to set ARA headers. Requesting that Protected Audience header processing also support fetchLater. + + * K-Anonymity: According to the k-anon [doc](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity): + * _In Q1 2024, for up to 20% of Chrome Stable traffic, excluding Mode A and Mode B experimental traffic, we will begin to check k-anonymity with the same parameters._ + + _In Q3 2024, when the third-party cookie deprecation (3PCD) ramp-up process is planned to begin, k-anonymity will be checked for 100% of Chrome Stable traffic with the same parameters?_ + + * Will the experimental groups continue to be excluded? + + (You are discussing the path forward with CMA, but any clarity on k-anon, which has been and I assume still is the next major PA privacy enforcement change to drop, will be welcome when you can provide it.) + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for checking the accuracy of the notes doc, especially the part with their own comments - so go back later and make sure the notes are accurate. You can even fix them on Github later!! :) + + +# Notes + + +## Discussion of Monday's "A new path for Privacy Sandbox on the web" news https://privacysandbox.com/news/privacy-sandbox-update/ + + + +* [Kleber] Alex Cone will be discussing this blog post +* [Alex Cone] This is a new experience for users over 3PC - giving the users a choice. Acknowledge there are a ton of questions. But for now, we do not have all the answers to the questions but we are taking this very seriously, and we are working through them internally to make sure we provide answers to things like timeline, UX, etc… and will share with you all when we have them +* [Alex Cone] This team stays here, the APIs don’t go anywhere +* [Brian May] Will the community invite to participate in the interaction around user choice and will there be a WICG for it? + * [Alex Cone] We do not have plans for that yet, and we are still in discussion around it with regulators. We do not have designs yet, and generally you can expect we continue seeking feedback + * [Kleber] The kind of things discussed in WICG calls or other W3C groups are cross browser standards. That does not include the UI for a browser and has never included the UI of a browser - user settings are not part of a WICG call. These are things that any browser reasonably calls the shots on their own and there is no reason for any interoperability. I do not expect anything about settings UI to show up here or any W3C forum +* [Isaac] Curious to any extent you can comment - do you anticipate continuing to do some type of ramp to get stronger evidence of its performance (where it needs to get better)? I’ll decouple from 3PCD. + * [Alex Cone] Do you mean a ramp of how choice would work or…? + * I could see Chrome making a statement - the current 1% stays the way it is but rather than ramping to say 5% with no option to go back, mode B will become 5% with default 3PC off? + * [Alex Cone] We do not have a plan to announce that, but acknowledge that having more traffic to test with will be helpful +* [From in-call chat: Anthony Yam]: is the 1% rolling back? + * [Alex Cone] You can expect to hear from us later. We do not have an answer right now +* [Brian May] Can you give us any rough idea on how things will progress from here? Timeline? + * [Alex Cone] We are moving with haste on that, but a piece is the discussion with regulators. But I cannot give you a timeline for the timeline just yet. +* [Pat MacCann] Will the gnatcatcher IP privatization work now be tied to this new commitment date? Should I read everything that was tied to deprecation tied to UX release + * [Alex Cone] Still working through those details right now +* [B. McLeod Sims] My understanding is that work will continue to move forward on these APIs, is that correct? + * [Alex Cone] We are continuing to invest in the APIs both on the privacy and utility side + * [Kleber] These APIs are being added to the web platform. They are available today for folks who have 3PC turned on or have 3PC turned off. If we decide to make any change as to the availability of the APIs, you will know about it long before it happens. But for now, there is no reason to expect any change with respect to availability +* [Luckey Harpley] One goal of PS was to unify Chrome and Android. Any comment to this? + * [Kleber] No announcement as to changes for Android +* [Brian] Is the PS timeline going to be kept updated and used to communicate to the ecosystem? + * [Alex] In terms of how we continue to use ecosystem update, I cannot speak to that right now +* [Isaac] Curious on Chrome’s commitment to making additional web standard changes to ARA? + * [Kleber] Every change we make to the web platform in Chrome is designed to be a change to the Web in general. Our goal is for everything we are working on to become part of web standards across browsers. Those who can remember the days of different browsers behaving differently can understand we don’t want to go back to a similar state. +* [Harshad Mane] When will UX be presented to user and what will be the default position? + * [Alex Cone] This is something we are working through on what the experience will be + * [Harshad Mane] Will it be global or in stages during release? + * [Alex Cone] No plans to share at this point +* [From in-call chat: Warren Fernandes]: is IP obfuscation still on the cards? + * [Kleber] The linked news release does mention something about IP… we are going to be introducing IP protection in Chrome incognito mode. No statements to make as to what will happen to IP protection in the future. There is an ongoing discussion about IP addresses and privacy in every browser globally. This W3C meeting, and W3C in general, is not the forum for it because IP address stuff operates at a different protocol layer: W3C is about stuff happening at the HTTP layer, while IP address discussions are happening at the IETF, where network protocols live. Not a whole lot to say about it here. +* [David Dabbs]: Suggest that folks who have further questions on the announcement add their questions to this doc to be further addressed + * [Kleber]: given the nature of this doc, intended to be a log of the live discussion vs a discoverable document for people outside the call, may not be the most appropriate place for that. +* [David Tam]: What can you say about the announcement and the Shared Storage API? + * [Kleber]: Recall the Shared Storage API is an API that allows cross-site writing of data, with specific output gates for the secure and private consumption of it like the Private Aggregation API. There is no effect on that API based on this announcement +* [David Tam]: Currently, when a cookie sync call is made it is a redirect that calls a third party domain. If the cookie sits in shared storage then how do you read the cookie. Correction, I should have referred to CHIPS. + * [Kleber]: the behavior of cookies has been the same for the last 25 years. If the browser has 3rd-party cookies on, then they will continue to work the same way they have before. If the browser has 3p cookies off, that's also a setting that browsers have had for a long time. +* [From in-call chat: Kevin Lee]: Hello folks, please post your questions about User Choice in the PS Dev Support repo:[ https://github.com/privacysandbox/privacy-sandbox-dev-support/issues](https://github.com/privacysandbox/privacy-sandbox-dev-support/issues). A new issue label "user-choice" has been created. + * [Kleber] If you have questions about the whole new path user choice cookie thing, there is a right place for them in the Privacy Sandbox developer support GitHub repository that Kevin linked to. + + +## Discussion of: [Addition of an analytics/reporting entity to enable centralised reporting · Issue #1115 · WICG/turtledove · GitHub](https://github.com/WICG/turtledove/issues/1115) + + + +* [Warren F] Have you had a chance to go through the issue? + * [Paul] We discussed a few weeks ago and I had a chance to speak to the Shared Storage folks. Previously, we talked about who will be in charge of giving the information - we decided it will be the seller. Also, for header mechanism to facilitate that, it looks like you added that to the document. I wanted to talk to Josh (Shared Storage), and wondering the timing of it, how will it get permission? We envision it will get written to SS and how do we envision the SS worklet, say how will the publisher know when to publish and what timeframe? + * [Warren F] I think it is something the browser will do periodically. It is in section C of the document + * [Paul] Not an expert in SS. The browser does not do a whole lot of things periodically - it is hard for it to do so + * [Warren F] But it could be a similar mechanism as aggregate with some arbitrary delay. I think it will work fine no matter what the cadence is, but as long as it is not super infrequent + * [Patrick McCann] Publishers can call this, and on page, they can do a clean up and see if there are any events that they haven't called yet… the requirement is that some other entity will be able to generate this report. + * [Paul] I think SS is still the right proposal; the issue is the when - that is, when to run it + * [Kleber] I suspect the answer might run into a coordination problem with the publisher. But I think we should be able to find time to run it. +* [Brian] It sounds like they may be a parallel with the trigger attribution call in ARA + * [Kleber] Not sure about that call + * [Paul] I think at the time Patrick was describing, not sure there is a network fetch for a header response. We generally do not make a request at page unload time +* [Patrick McCann] There is [Event level reporting](https://github.com/WICG/shared-storage?tab=readme-ov-file#event-level-reporting) out of SS currently documented for 2026 - will that be moved forward? Should we still plan to build tooling on it? + * [Kleber] The flow we should build: Write to Shared Storage - Use existing output gate to do reporting. I do not think event-level reporting coming out of SS is a good fit for what you are trying to do - that's part of the selectURL output gate, and the event-level reporting is about which of the 8 possible URLs was selected, not about the contents of Shared Storage. The private aggregation is much more appropriate + * [Patrick] Makes sense. That answers it. +* [Warren F] How do we move forward on this one since everyone is broadly okay with it? + * [Paul] We should formalize the proposal a bit more; explicitly stating names of the header and names of the APIs and make sure we are all onboard. If everyone is interested, we can then consider moving forward + * [Warren F] You can pick whatever you want as it’s just a name + * [Patrick M] I agree with Warren. Just make the decisions and preserve the momentum of this effort. + * [Paul] I think we may be at the point where we can propose something more formal given my preliminary review. The next step is to convert this into an Explainer and then we can make it a PR to the GH issue. I will either do this or find somebody on the Chrome side to do this one. Note that this is pretty complicated, so it might take me a while. + +[Kevin Lee] + + + +* Hello folks, please post your questions about User Choice in the PS Dev Support repo: https://github.com/privacysandbox/privacy-sandbox-dev-support/issues +* A new issue label "user-choice" has been created. diff --git a/meetings/2024-07-31-FLEDGE-call-minutes.md b/meetings/2024-07-31-FLEDGE-call-minutes.md new file mode 100644 index 000000000..f1f618838 --- /dev/null +++ b/meetings/2024-07-31-FLEDGE-call-minutes.md @@ -0,0 +1,280 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday July 31, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Brian May (Dstillery) +3. Roni Gordon (Index Exchange) +4. Paul Jensen (Google Privacy Sandbox) +5. Omri Ariav (Taboola) +6. Fabian Höring (Criteo) +7. Harshad Mane (PubMatic) +8. Tim Taylor (Flashtalking) +9. David Dabbs (Epsilon) +10. Anthony Yam (Flashtalking) +11. Alex Cone (Google Privacy Sandbox) +12. Denvinn Magsino (Magnite) +13. Paul Spadaccini (Flashtalking) +14. Becky Hatley (Flashtalking) +15. Kevin Lee (Google Privacy Sandbox) +16. Yanay Zimran (Start.io) +17. Matt Kendall (Index Exchange) +18. Isaac Foster (MSFT Ads) +19. Felipe Gutierrez (MSFT Ads) +20. Josh Singh (MSFT Ads) +21. Laurentiu Badea (OpenX) +22. Laura Morinigo (Samsung) +23. Matt Davies (Bidswitch | Criteo) +24. Tamara Yaeger (BidSwitch) +25. Orr Bernstein (Google Privacy Sandbox) +26. Brian Schneider (Google Privacy Sandbox) +27. Herschel Wiseman(Google Privacy Sandbox) +28. Arthur Coleman (IDPrivacy) +29. Jeremy Bao (Google Privacy Sandbox) +30. Pooja Muhuri (Google Privacy Sandbox) +31. Alex Peckham (Flashtalking) +32. Stan Belov (Google Ad Manager) +33. Lydon, Jason (Flashtalking) +34. Matthew Atkinson (Samsung) +35. Bharat Rathi (Google Privacy sandbox) +36. Andrey Prokofyev (Google Display & Video 360) +37. Maybelline Boon (Google Privacy Sandbox) +38. Sarah Harris (Flashtalking) +39. Andrew Pascoe (NextRoll) +40. Sathish Manickam (Google Privacy Sandbox) +41. Abishai Gray (Google Privacy Sandbox) +42. Ricardo Bentin (Media.net) +43. Garrett McGrath (Magnite) +44. Victor Pena (Google Privacy Sandbox) +45. Shafir Uddin (Raptive) +46. Alonso Velasquez (Google Privacy Sandbox) + + +## Note taker: Orr Bernstein + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + +* David Dabbs + * Request for nascent header join/leave capability to support [fetchLater API](https://developer.chrome.com/blog/fetch-later-api-origin-trial) +(entered as [comment](https://github.com/WICG/turtledove/issues/896#issuecomment-2233667864) on #896) + + Chrome is [migrating](https://issues.chromium.org/issues/40236167) keep-alive request handling from the “renderer” (front-end) to the “browser” process (back-end). [Explainer](https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#). Attribution Reporting API (ARA) supports event and trigger header registrations on background requests, and this will move to the browser. ARA team has [extended that hook](https://issues.chromium.org/issues/40242339#comment48) so that fetchLater API requests will also be able to set ARA headers. Requesting that Protected Audience header join/leave capability also supports fetchLater. + + * K-Anonymity + According to the k-anon [doc](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity): + * _In Q1 2024, for up to 20% of Chrome Stable traffic, excluding Mode A and Mode B experimental traffic, we will begin to check k-anonymity with the same parameters._ + + _In Q3 2024, when the third-party cookie deprecation (3PCD) ramp-up process is planned to begin, k-anonymity will be checked for 100% of Chrome Stable traffic with the same parameters._ + * Will the experimental groups continue to be excluded? + + (You are no doubt discussing the path forward with CMA, but any clarity on k-anon, which has been and I assume still is the next major PA privacy enforcement change to drop, will be welcome when you can provide it.) + +* Roni Gordon: Update on the deals proposal + * https://github.com/WICG/turtledove/pull/1237/files, related to https://github.com/WICG/turtledove/issues/873#issuecomment-2196882982 + * Timeline for implementation? + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for checking the accuracy of the notes doc, especially the part with their own comments - so go back later and make sure the notes are accurate. You can even fix them on Github later!! :) + + +# Notes + + +## Low entropy client hints on Trusted Bidding Signals requests ([#1031](https://github.com/WICG/turtledove/issues/1031)) + + + +* David Dabbs + * Client hints on the Trusted Bidding Signals request + * Requesting an update on client hints on real-time reporting +* Paul Jensen + * Didn’t see a lot of motivation as to why folks wanted it + * Read over the Trusted Key Value Server trust model + * [FLEDGE Key/Value service trust model](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/key_value_service_trust_model.md) + * The basis of it is that there are three design principles + * Overall flow: “The browser needs to trust that the return value for each key will be based only on that key and the hostname, and that the server does no event-level logging and has no other side effects based on these requests.” + * Why would we want to send more data to it? +* David + * Omri Ariav may be able to answer this +* Omri Ariav + * Common use case for advertisers who have an operating system they want to target. +* Michael Kleber + * The bid is calculated at a particular time, a bunch of different opportunities for information to flow into the environment where you calculate your bid. At IG join time, you get unrestricted access to all of the information about this particular user and site. Another time is the contextual flow of information into the bid at the auction that the auction is taking place. You already have control, know everything you want to know, at these two times. You can use this information to decide what ads to put into the IG, and again, if you want to do some sort of cross-checking between the IG time and auction join time, the contextual information from that side of the auction is an opportunity to do so. Maybe the Trusted Bidding Server is not the best place to talk about that becoming available. On the other hand, what I heard as other people were discussing this, is that this is a question not just about the TBS, but also about the periodic IG refresh, and there it’s a very different question. +* David + * Suggestion you just made makes a great segue to that. Recent discussions of people pursuing the shell IG joining pattern, if you’re not making those decisions at join time and deferring that to the update and centralizing. +* Michael + * You could, at the time you’re making an IG, you can annotate in that IG, e.g. in the IG name, you can make available to you for updates in the future, e.g. by including user agent. + * However, not much reason not to make those low-entropy signals available at update time. +* Omri + * Wondering if it’s possible to have a formal explanation on the ticket. +* Michael + * Yes, I can do that. +* Omri + * Where would we put these signals in the IG? In the name of the IG? Or is there a field or object? +* Paul + * Yes, I think that’s the place to put it. +* Michael + * For the TBS, you can always include it in a key. Including it in the name of the IG is relevant as a workaround for what David mentioned about wanting to know that information at IG update time. +* David Dabbs + * Or you could put it in the update URL. You have to build the update URL. +* Michael + * Oops, yes, that’s absolutely right, update URL, not IG name. +* Paul + * David, you were talking about update to the user agent client hints. What’s the user case for that one? +* David + * Language header, the client hints on the version. Yes, we could bake that into the update URL. If we’re not targeting a certain version, we could have not joined the IG then. If it’s not just Chrome and the version - if it’s mobile or not - if you have a line item and you want to do the bid, or more in the IG about whether it’s a mobile device. You might be producing information like keys and other things that will impact other decisions later. Mobile device or other device - similar user case for real-time reporting, have opportunity to aggregate everything, can you see if the behavior is aberrant on a particular device. +* Brian May + * Important to have some information about device capabilities that naturally comes through this channel. +* David + * Want to emphasize that the ask here is solely low-entropy UACH. Not looking for the granular stuff. + * Last thing to follow up - not the original request in 1031, which we can’t abide - but should I open a separate request for the update URL? I think I already did comment on the real-time postbacks and you have said there’s already a request for that. +* Brian + * I’d like to vote for having a separate issue so that people who are interested can find it. +* David + * A separate one for each? A request to provide on updateURL and [real-time reporting] postbacks? +* Paul + * Different ones. I haven’t looked at how easy to implement. +* Michael + * We’ll have to do our due diligence. +* Paul + * As Michael said, you can do it through the keys for trusted bidding signals or through the URL for updates. +* David + * It does, but it adds to IG overhead, have to jam all that stuff into the update URL. +* Michael + * Possible to work around it today, more convenient if it had direct native support directly from the browser. We generally put higher priority on feature requests that are not otherwise possible over things that are possible but may be awkward to do it today, so may not be the first thing on the queue. +* David + * So, priority on real-time reporting postbacks because Chrome is in complete control there so API users have no work-around. +* Brian + * Suggestion to capture these exceptions on how the browser normally communicates be called out. + + +## Update on the Deals proposal: https://github.com/WICG/turtledove/pull/1237/files, related to https://github.com/WICG/turtledove/issues/873#issuecomment-2196882982 + + + +* Roni Gordon + * Following up on gaps to the original proposal. I saw an explainer out there. Just wanted to make sure I’m connecting the dots. End of the changes, curious about when we can try it out. +* Paul + * Have started work on implementation. In the coming weeks, it should be testable. I don’t think the first CL has landed yet, but we’re working on that right now. + * I did put up a PR to solidify that. I know, Roni, you’d asked for that, because we had Leeron’s original proposal and made some changes to that. + * If you’re looking closely, there were a couple of ergonomic renames of things, we’ll post on GitHub just to explain it a bit more, may not be worth getting into now. If you asked for a timeline for implementation, first change should be landing very soon, and I suspect all of it will be landing soon after that. We’ve been working on it for a couple of weeks. We’ll post back on the deals thing with the couple of ergonomic renames, and then when it’s available for testing in canary. +* Michael + * As a reminder, things we land to Chrome make it very rapidly to Chrome Dev and Chrome Canary, and then once a month gets promoted to Chrome Beta channel. Chrome 129 is going to move to Beta on or around August 21; that's the earliest that something can land in Beta, but if you’re super eager to try things out before they get to Beta/Stable, try them out in Dev/Canary channels. +* David + * You’d said the work is underway. Is there a buganizer item for that? Re the ergonomics, one thing that came up is, it looks like we’re putting “seat” in a particular field; is that the name of the field name? +* Paul + * I don’t know if there’s a buganizer if you’re interested in following along on the CLs landing. In terms of naming things “deal” or “sealID”, we tend not to be proscriptive. We tend to build infrastructure, and people can use it however they want, can innovate from there. + + +## Request for `updateURL` processing to support the leaving/joining of negative interest groups via the [nascent header feature](https://github.com/WICG/turtledove/issues/896) +(entered as issue [#1228](https://github.com/WICG/turtledove/issues/1228)) + + + +* David Dabbs + * Can we negatively target a regular interest group. Orr had said, why don’t we just create a shadow negative interest group? But it seems to be more ergonomic that, at update time, we could either join or leave an negative IG. The wrinkle I found is, I’m going to be joining some groups, put next to nothing in them, going to do all of the heavy lifting at update time. Want to be able to either add the negative shadow at update time, or leave it, so it’s effectively not impacting the IG that wants to anti-target it. +* Michael + * The existing work that we’ve talked about in this group is equivalent header. Very much in line of Privacy Sandbox using HTTPheaders instead of needing to run JS. The “interest group header joining” may already be in progress? +* David + * Met with Jeremy, seems there’s still some discussion about it. +* Jeremy Bao + * Still making progress. +* Michael + * You’re saying one particular request/response on which it’s helpful to have the header thing is the request/response on which one would join a negative interest group. One important property of an IG is that a page that a person is visiting when they join an IG - it’s an essential element of the nature of an IG, the time is also very important to an IG, related to the potential longevity of an IG. So the idea of joining of an IG when a person is not actually visiting a page - because it’s an HTTP response happening in the background - presents some problems. +* David + * What about leaving an IG? That’s what I’m really interest in. +* Michael + * If we’re just talking about leaving an IG, then it’s much easier to say yes to. +* David + * Extra-territoriality of the IG update call. Deleting or leaving would smooth out the wrinkle. If I’ve deferred the hard decisions to later, but then I call the update for the one that I join on the shadow. +* Michael + * Just to make sure I understand the flow you’re imagining. The shell IG flow you’re imagining. While the user is visiting the site, you make two different shell IG joins - one for regular IG, one for negative IG. +* David + * Actually three. We’re closing on the opportunity to join lazily. The IG - excluded positive - that’s the one you really want (if you could anti-target it, you would); then the IG that wants to negatively target that. And then, you need that actual negative IG, that is the shadow of the one that also may exist. Turns out the second positive IG - you can’t join it - you need to delete its negative shadow, that the other groups may be negatively targeting. That’s why three or more. +* Michael + * Almost makes sense. Let me repeat it back. + * What you’re envisioning. Instead of a single IG, you’re envisioning a flock of three IGs. + * IG X - functions like a regular old IG, that has a bunch of ads and potential bidding on those ads. + * Negative IG - which you add somebody to anytime you add them to IG X, which is an IG that serves that the user is in IG X, so that some other IG may choose to negatively target. + * IG that actually takes up that negative IG on its offer. Exactly the complement of the first positive IG. Going to show to people who are not in X. + * Person just joined X, so now you can target people in X and people not in X. +* David + * And their status may change - may be updated. +* Michael + * Not completely sold that it makes sense to join IGs in sets of three in the way you’re describing. You might often want to have negative IGs that are at a different granularity than just people not in a single IG. Not sold on the creation of a positive IG that negatively targets the negative IG, but nothing inherently wrong with how you’re describing using the flow. +* David + * But that’s why Jeremy is expanding the positive IGs to be able to negatively target a negative interest group. +* Jeremy Bao + * Proposal already out to allow negative targeting a PA bid. In the positive IG, you can specify the negative IG you want to anti-target. I’m trying to understand with your three group proposal, what’s the additional thing the current proposal cannot solve? +* David + * At the join site, if you’re deferring the full evaluation of information until update time - what the shell approach entails - because of that, you need the three IGs. +* Michael + * I’ll try again to repeat this back. + * If you don’t want to do any decision making about how to use IGs; you merely want to record events and use those events later, then, anytime you observe an event X, you propose creating two IGs + * One for ads you want to target that event X has happened + * And One for ads that want to target that event X has not happened + * And now you need to create three IGs - the negative IG, the regular IG that doesn’t negatively target the negative IG, and a regular IG that does negatively target the negative IG. + * Everything you’re talking about happens on this same site where user did event X. If you’re talking about a bunch of stuff that all happens on a single site, you only need one IG for all of that. This person is Nike customer 123456789, then you can send the fact that event X happened to that user, and then when you get your periodic daily update, you can make all the decisions you want at that point, and it does not require a large flock of interacting IGs. +* David + * This is true, but the differentiator is that the positive IG that, in the ideal world, would be negatively targeted, is something that could be joined on many sites, but the one that’s actually negatively targeted, could only ever be joined on a single site. +* Brian + * Jeremy - could you add a link to the proposal you mentioned? + * If we provide a capability for someone to do something, we need to provide a capability for them to undo that something if they did it by mistake. So, if we allow them to delete a negative IG, we should provide a capability to restore an accidentally deleted negative IG. +* Jeremy (in chat) + * https://github.com/WICG/turtledove/issues/896 +* Paul + * Another way to phrase that - maybe we have a capability to disable that temporarily. Maybe remove the key. +* David + * No way for the IG that’s negatively targeting the other one can know about its state or whether its active. It’s only the update to the positive IG that might be joined in various places that needs the negatively targeted shadow. +* Michael + * What Paul is suggesting is that instead of deleting the negative IG, delete the key that makes that negative IG work, and then you’ve disabled the negative targeting capability. +* David + * But they can’t have updateURLs. +* Jeremy + * But if we allow negative IGs to be updated, that may make your use cases simpler? +* Michael + * Will make the delete/undelete operation simpler. +* Orr + * Indeed, negative IGs can’t be updated; was considered but decided against allowing update because it’s a lot of network traffic for a lightweight object that has no fields that typically need to be updated. +* Brian + * If it can be turned off instead of destroyed, that would solve things for me. The use case I’m thinking about is, somebody doesn’t know what they’re doing, wipes out a subset of negative IG population, and we’d like to recover from this mistake. diff --git a/meetings/2024-08-07-FLEDGE-call-minutes.md b/meetings/2024-08-07-FLEDGE-call-minutes.md new file mode 100644 index 000000000..cf1ee99b2 --- /dev/null +++ b/meetings/2024-08-07-FLEDGE-call-minutes.md @@ -0,0 +1,246 @@ +# Protected Audience WICG Calls: Agenda & Notes + +Calls take place on most Wednesdays, at 11am US Eastern time; check [#88](https://github.com/WICG/turtledove/issues/88) for exceptions. + +That's 8am California = 5pm Paris time = 3pm UTC (during summer) + +This notes doc will be editable during the meeting — if you can only comment, hit reload + +Notes from past calls are all on GitHub [in this directory](https://github.com/WICG/turtledove/tree/main/meetings). + + +# Next video-call meeting: Wednesday Aug 7, 2024 + +To be added to a Google Calendar invitation for this meeting, join the Google Group https://groups.google.com/a/chromium.org/g/protected-audience-api-meetings/ + +If the meeting disappears from your calendar: try leaving and re-joining that group + + +## Attendees: please sign yourself in! + + + +1. Michael Kleber (Google Privacy Sandbox) +2. Roni Gordon (Index Exchange) +3. Pooja Muhuri (Google Privacy Sandbox) +4. Paul Jensen (Google Privacy Sandbox) +5. Orr Bernstein (Google Privacy Sandbox) +6. David Dabbs (Epsilon) +7. Matt Kendall (Index Exchange) +8. Harshad Mane (PubMatic) +9. Laura Morinigo (Samsung) +10. Isaac Foster (MSFT Ads) +11. Anthony Yam (Flashtalking) +12. David Tam (paapi.ai formerly Relay42) +13. Fabian Höring (Criteo) +14. Kevin Lee (Google Privacy Sandbox) +15. Alex Peckham (Flashtalking) +16. Rickey Davis (Flashtalking) +17. Sid Sahoo (Google Chrome) +18. Lydon, Jason (fine… Flashtalking) +19. Tamara Yaeger (BidSwitch) +20. Sarah Harris (Flashtalking) +21. Becky Hatley (Flashtalking) +22. Owen Ridolfi (Flashtalking) +23. Bharat Rathi [Google Privacy sandbox] +24. Laurentiu Badea (OpenX) +25. Jeremy Bao (Google Privacy Sandbox) +26. Sathish Manickam (Google Privacy Sandbox) +27. Laszlo Szoboszlai (Audigent) +28. Koji Ota(CyberAgent) +29. Paul Spadaccini (Flashtalking) +30. Josh Singh (MSFT) +31. Matthew Atkinson (Samsung) +32. Shafir Uddin (Raptive) +33. McLeod Sims (Media.net) + + +## Note taker: Orr Bernstein + + +# Agenda + + +## Process reminder: Join WICG + +If you want to participate in the call, please make sure you join the WICG: https://www.w3.org/community/wicg/ + +Contributions to this work are subject to W3C Community Contributor License Agreement (CLA) which can be found here: https://www.w3.org/community/about/process/cla/ + + +## Suggest agenda items here: + + + +* Isaac Foster: + * Brief revisit the “coarse information sharing” thing, we had talked about setting up time but never did, all got too busy…can even answer here + * Multi Tag Support via “Mixed Ranking”: (really, this + multi tag + bit leak discussion and how we can be creative) https://github.com/WICG/turtledove/issues/846 + * Optional decouple bidding/reporting function urls to allow smaller k tuple: https://github.com/WICG/turtledove/issues/679#issuecomment-1703973736 + +* David Dabbs + * Clarification on reporting values in Deals explainer: +https://github.com/WICG/turtledove/pull/1237#discussion_r1700378453 + + * Request for nascent header join/leave capability to support [fetchLater API](https://developer.chrome.com/blog/fetch-later-api-origin-trial) (entered as [comment](https://github.com/WICG/turtledove/issues/896#issuecomment-2233667864) on #896) + + Chrome is [migrating](https://issues.chromium.org/issues/40236167) keep-alive request handling from the “renderer” (front-end) to the “browser” process (back-end). [Explainer](https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#). Attribution Reporting API (ARA) supports event and trigger header registrations on background requests, and this will move to the browser. ARA team has [extended that hook](https://issues.chromium.org/issues/40242339#comment48) so that fetchLater API requests will also be able to set ARA headers. Requesting that Protected Audience header join/leave capability also supports fetchLater. + + * K-Anonymity + According to the k-anon [doc](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity): + * In Q1 2024, for up to 20% of Chrome Stable traffic, excluding Mode A and Mode B experimental traffic, we will begin to check k-anonymity with the same parameters. + + In Q3 2024, when the third-party cookie deprecation (3PCD) ramp-up process is planned to begin, k-anonymity will be checked for 100% of Chrome Stable traffic with the same parameters. + * Will the experimental groups continue to be excluded? + (You are no doubt discussing the path forward with CMA, but any clarity on k-anon, which has been and I assume still is the next major PA privacy enforcement change to drop, will be welcome when you can provide it.) + + + +# Announcements + +The Microsoft Edge folks are holding every-second-Thursday meetings at this same hour to discuss their very similar "Ad Selection API" work. See https://github.com/WICG/privacy-preserving-ads/issues/50 for logistics. + +Everyone is responsible for checking the accuracy of the notes doc, especially the part with their own comments - so go back later and make sure the notes are accurate. You can even fix them on Github later!! :) + + +# Notes + + +## Clarification on reporting values in Deals explainer: +https://github.com/WICG/turtledove/pull/1237#discussion_r1700378453 + + + +* David Dabbs + * The question in that series of comments - the read on section 1.2 is that those reporting values are mutually exclusive - but Paul said that one would get all of them. What’s the availability? +* Paul + * (Presents “Deals explainer” #1237) + * A bit trickier. Orr had a proposal for making it simpler, but can’t without changing existing behavior. There’s a complex English description of the behavior. + * Today, without deal support, in today’s behavior, there are three reporting IDs - IG name, buyerReportingId, buyerAndSellerReportingId. If you put buyerAndSellerReportingId, you get buyerAndSellerReportingId. If instead you put buyerReportingId, you get buyerReportingId. If you put neither, you get IG name. Whichever one you want to get through to reporting is passed through k-anon. + * Originally, we thought about using buyerAndSellerReportingId and making that an array. There are two problems with reusing an old field. Not a great idea when designing an API - confuses people who used it before. Also, an exciting JS behavior, when you pass an array to a string field in WebIDL, it stringifies the array and passes it through as a string. + * So, we decided to leave buyerAndSellerReportingId alone and create a new field, selectableBuyerAndSellerReportingIds. buyerAndSellerReportingId and buyerReportingId are still optional string fields. + * New selectableBuyerAndSellerReportingIds. + * Before, you picked one. New behavior is a little different. We want to pick the selected_buyerAndSellerReportingId - the one you pick in generateBid - but also to be able to allow ad techs to specify buyerAndSellerReportingId and buyerReportingId. buyerAndSellerReportingId is not selectable, but still gets passed to both buyer and seller. For deals use case, this could be the seat ID. You could take the seat ID and append it to every selectableBuyerAndSellerReportingIds element, but that would bloat your IG. buyerReportingId could be private information that you don’t want to report to the seller. + * So, when you’re using selectableBuyerAndSellerReportingIds, you can pass these in as well. This is different from the behavior when you don’t specify selectableBuyerAndSellerReportingIds - there, you still get only one of the fields. When you do pass selectableBuyerAndSellerReportingIds, you get all of the reporting fields you provided. + * I’m going to add a chart - it is complicated. We have a few charts internally to make sure we’re all on the same page. Going to add a chart to show which ones you get and which ones you don’t get. +* David + * To your point of private information, if I had access to the k-anonymous - when those win, all of the ads that get served are going to be k-anonymous - but all of the information I need will be in the URL. I’ll need to understand how I’m going to get them out. + * The actual renderUrl is not available to reportWin because the expectation is - you’ve got a buyerReportingId - you can channel anything you want through that. +* Michael + * No, the renderUrl is always available. + + +## Request for nascent header join/leave capability to support [fetchLater API](https://developer.chrome.com/blog/fetch-later-api-origin-trial) (entered as [comment](https://github.com/WICG/turtledove/issues/896#issuecomment-2233667864) on #896) + + + +* David Dabbs + * Forward looking. Attribution reporting API group has hooked to be able to trigger on background fetches. Putting in a request - when Jeremy and all puts in the feature for PA to process IG events or IG API activations via headers - to be able to have fetchLater fetches process PA header in the background. That’s another way to take PA related processing off the critical path on an advertise or other site. + * I requested this feature as a comment on #896 - negative targeting. This is where there was a lot of discussion about processing IG joins and leaves and whatever via headers. When we have that header processing, we want it on fetchLater. +* Michael + * Can you clarify why you think that fetchLater is a good use case? As I understand it, the use case for fetchLater is - I want to know how long a user spends on my webpage - and I want to use a beacon to send it, but because I waited until the last second to send it, it’s possible that I don’t get my beacon. So, fetchLater is useful when I want to send a request just as a person is leaving the webpage. + * There’s nothing fundamentally I don’t like about this request, but I don’t understand why it makes sense. +* David + * It’s retargeting. By virtue of visiting the site, you want to create an IG. I want to do this in the lightest possible way. Once there’s lightweight IG creation via headers, that’s how I prefer to do it. If it’s something in the background, then that’s how I’d want to do it. +* Michael + * I don’t think fetchLater would be useful, since its goal is - don’t do this at all until the person is leaving the webpage. You could just issue a regular fetch. + + +## K-Anonymity +According to the k-anon [doc](https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity): + + + +* _In Q1 2024, for up to 20% of Chrome Stable traffic, excluding Mode A and Mode B experimental traffic, we will begin to check k-anonymity with the same parameters. +In Q3 2024, when the third-party cookie deprecation (3PCD) ramp-up process is planned to begin, k-anonymity will be checked for 100% of Chrome Stable traffic with the same parameters._ +* Will the experimental groups continue to be excluded? +(You are no doubt discussing the path forward with CMA, but any clarity on k-anon, which has been and I assume still is the next major PA privacy enforcement change to drop, will be welcome when you can provide it.) +* David Dabbs + * Still on this timeline? + * Experimental groups - will they still be sent in headers? Or will the test cohorts still be excluded from k-anonymity? +* Michael Kleber + * To the second question: + * Can we do something so that k-anonymity no longer applies to actual bidding functions but rather to reporting functions, which is what we need for actual privacy properties. + * That’s a whole infrastructure change that we haven’t put effort into implementing. You’re quite right that we could do this without damaging the privacy properties of the PA flows. + * If it turns out that this is really important to somebody, please comment on the GitHub issue. Maybe because k-anonymity thresholds are low right now, it’s not clear how important or urgent this is to anybody. + * To to first question: + * The answer right now is that we expect things to progress exactly as we talked about. We expect to increase the amount of traffic subject to the k-anonymity constraint later this year, sometime in Q3 is what that document says, and we expect that to still be in the group that doesn’t have group A/B testing labels to it. We’ll be doing that ramp up in the traffic that isn’t in those groups. No changes; all status quo. + + +## David Tam: Mode A/B traffic. How long will that stick around? + + + +* Michael Kleber + * We’d intended for this labeled traffic being available only during the first half of the year, during CMA testing, but there’s been some churn about how useful this labeling is. + * We know that we said so far that we’re going to end them in the end of July. We’ve extended them - with permission from Blink owners. They’re still a temporary thing. We’ll have to be patient a little longer to get resolution. +* David Tam + * Limited amount of inventory that actually gets looked at by top-level seller. +* Michael + * Question for GAM. The point of the labels and one of the reasons people have found them still useful is exactly so that there’s some traffic that everybody agrees that everybody expects a PA auction. My understanding is that GAM runs PA auctions outside of the labeled traffic. There’s two questions - one is how much traffic they’re running on, and how much traffic you can count their running on. The labeled traffic is the traffic you can count their running on, because they said they would do so. I don’t know what their future plans are. +* David Tam + * How do I get an answer to this? +* David Dabbs + * There’s a Google Ads experiments GitHub repository where they engage: try asking a question on https://github.com/google/ads-privacy +* Michael + * Not sure if there are GAM folks on the calls. I don’t see any of their hands. GitHub or other forums - I don’t know if there are other forums, where people who are all experimenting with PA API are having conversations that I’m not a part of. +* Fabian + * Speaking from the buyer perspective - two additional reasons why the labels are useful. Useful for A/B testing. And the second reason is to save infra cost - you can use PA on those populations. +* Michael + * These are both issues we’re aware of, and understand it’s useful for the ad tech community to decide something based on whether people do or don't have 3P cookies in the future, and looking into how to provide that capability. Probably won’t be these labels in the future. + + +## Negative targeting / headers + + + +* David Dabbs + * Jeremy - what’s your next steps on these? Explainer? +* Jeremy Bao + * On the negative targeting for PA bids. + * Aren’t at the phase where we’re posting an explainer. Priority I’m currently working on. + * On the header bidding + * Not my priority yet +* David Dabbs + * Doesn’t sound like you need more interest expressed for these, something the community wants. +* Jeremy + * To make sure I get what you’re asking for - it’s allowing joinAdInterestGroup in HTTP response headers. +* David Dabbs + * Yes, but with limits on IG creation. To paraphrase Michael - we (Chrome) could do that, but you can’t create a _whole_ IG. You could create a negative IG, which is lightweight, or a “shell IG”, which just has name, owner, updateURL, and minimal attributes - because it’s otherwise too long for a header. Is that right? +* Michael + * Yes, there are technical limits on the size of an HTTP response header. Could use a header to create a small IG, and then use the update mechanism to fill it with ads and other attributes. +* David Dabbs + * If it’s supported in the JS API, and it makes sense and it’s lightweight enough. + * The other thing I’m asking for re. Header API processing is for Chrome to support deleting IGs via a header on the updateURL response. +* Michael + * If I recall the previous conversation we had about this, there’s some observation that - if your goal is to remove the IG, you could instead remove the cryptographic key, which is a field in the IG. +* David Dabbs + * We covered that, but Orr pointed out that negative IGs are not updatable. +* Michael + * Oh, that’s right, my mistake! So, what you’re saying, with the HTTP response header thing, in the updateUrl request, you want to be able to leave the IG that’s in the process of being updated, or being able to remove some other IG on the device, including possibly a negative IG. +* David Dabbs + * That’s correct. Just remove an IG. + * Part of the scope of a header-based activation of those APIs. + * Jeremy - if you need a feature request, I could sketch that out. +* Jeremy + * A feature request is still helpful. Different people asked for this feature for different reasons. Could you help write a feature? +* David Dabbs + * Sure. To reiterate the conversation from a couple of weeks ago, I just want the expanded negative targeting to say, “I just want to negatively target this positive IG”. + * But Michael said that there are privacy reasons why this was not up for consideration. +* Michael + * Not privacy - complexity. We already have positive IGs referencing negative IGs. + * Could reopen and change that, but as things work right now, you can only negatively target a negative IG. + * Do you have an answer for - what is the intended behavior if IG A negatively targets IG B which is on the browser, and IG negatively targets IG C, which is on the browser? Should IG A participate in the auction or not? The thing it’s looking for suppression is itself suppressed. +* David Dabbs + * Didn’t give thoughts to transitivity. +* Michael + * Having them be different things avoids the question. If you gave me an answer, I would then ask about what happens if there’s a cyclic chain. If we are going to rethink that restriction, we’ll need to get answers to all of these. +* David Tam + * I was under the impression that there could only be one negative IG per positive IG. +* David Dabbs + * Yes, In the current state, negative targeting with additional bids, you’re right. This convo has been talking to the expansion that Jeremy is proposing. +* Jeremy + * Wrt issue #896 https://github.com/WICG/turtledove/issues/896, we’ll support negative targeting of PA bids - with a limit of 3. With negative targeting of additional bids, it’s also 3, though there’s a current discussion. There’s also a limit of 10 additional bids with negative targeting. +* Michael + * If you have thoughts on how this feature ought to be used and how to appropriately give people enough flexibility to use them, please chime in on the issue. +* David Dabbs + * I will take thoughts on these things and how they interact with the header mechanism and draft it into a document. diff --git a/spec.bs b/spec.bs index 6467757b1..81d016fdc 100644 --- a/spec.bs +++ b/spec.bs @@ -58,7 +58,8 @@ spec: RFC8941; urlPrefix: https://httpwg.org/specs/rfc8941.html for: structured header text: boolean; url: boolean text: integer; url: integer - text: boolean; url: boolean + text: list; url: list + text: string; url: string spec: WebAssembly; urlPrefix: https://webassembly.github.io/spec/core/ type: dfn urlPrefix: appendix/embedding.html @@ -66,6 +67,9 @@ spec: WebAssembly; urlPrefix: https://webassembly.github.io/spec/core/ spec: WebAssembly-js-api; urlPrefix: https://webassembly.github.io/spec/js-api/ type: dfn text: compiling a WebAssembly module; url: #compile-a-webassembly-module +spec: WebAssembly-web-api; urlPrefix: https://webassembly.github.io/spec/web-api/ + type: dfn + text: compiling a potential WebAssembly response; url: #compile-a-potential-webassembly-response spec: WebIDL; urlPrefix: https://webidl.spec.whatwg.org/ type: dfn text: convert a Web IDL arguments list to an ECMAScript arguments list; url: #web-idl-arguments-list-converting @@ -80,6 +84,13 @@ spec: Shared Storage API; urlPrefix: https://wicg.github.io/shared-storage type: dfn text: shared-storage; url: #permissionspolicy-shared-storage text: shared-storage-select-url; url: #permissionspolicy-shared-storage-select-url +spec: CSP; urlPrefix: https://w3c.github.io/webappsec-csp/ + type: dfn + text: directive name; url: directive-name + text: directive value; url: directive-value +spec: CSPEE; urlPrefix: https://w3c.github.io/webappsec-cspee/ + type: dfn + text: required csp; url: browsing-context-required-csp -Each {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope}} has an associated -forDebuggingOnly, which is an -{{ForDebuggingOnly}} instance created alongside the -{{InterestGroupBiddingAndScoringScriptRunnerGlobalScope}}. +Each {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope}} has an associated {{ForDebuggingOnly}} +instance forDebuggingOnly, and +an associated {{RealTimeReporting}} instance +realTimeReporting, which are +created alongside the {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope}}.
@@ -3758,6 +4164,45 @@ The reportAdAuctionLoss(|url|) method s [=InterestGroupBiddingAndScoringScriptRunnerGlobalScope/debug loss report url=] to |parsedUrl|.
+ +
+The realTimeReporting +getter steps are: + + 1. Return [=this=]'s [=relevant global object=]'s + [=InterestGroupBiddingAndScoringScriptRunnerGlobalScope/realTimeReporting=]. +
+ +Each {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope}} has a +
+ : real time reporting contributions + :: A [=list=] of [=real time reporting contributions=]. +
+ +
+The contributeToHistogram({{RealTimeContribution}} |contribution|) +method steps are: + + 1. Let |global| be [=this=]'s [=relevant global object=]. + 1. If |contribution|["{{RealTimeContribution/bucket}}"] ≥ [=number of user buckets=] or is + negative, then return; + + Note: For forward compatibility with new values, don't [=exception/throw=]. + + 1. If |contribution|["{{RealTimeContribution/priorityWeight}}"] ≤ 0, then [=exception/throw=] a + {{TypeError}}. + 1. Let |contributionEntry| be a new [=real time reporting contribution=] with the following + [=struct/items=]: + : [=real time reporting contribution/bucket=] + :: |contribution|["{{RealTimeContribution/bucket}}"] + : [=real time reporting contribution/priority weight=] + :: |contribution|["{{RealTimeContribution/priorityWeight}}"] + : [=real time reporting contribution/latency threshold=] + :: |contribution|["{{RealTimeContribution/latencyThreshold}}"] if it [=map/exists=], null otherwise + 1. [=list/Append=] |contributionEntry| to |global|'s + [=InterestGroupBiddingAndScoringScriptRunnerGlobalScope/real time reporting contributions=]. +
+ ### InterestGroupBiddingScriptRunnerGlobalScope ### {#bidding-global-scope}
@@ -4158,6 +4603,8 @@ navigating to another page. Some implementations, such as Chromium, have chosen
       ::  «`Accept`: `application/json`»
       :   [=request/client=]
       ::  `null`
+      :   [=request/origin=]
+      ::  |owner|
       :   [=request/mode=]
       ::  "`no-cors`"
       :   [=request/referrer=]
@@ -4254,9 +4701,8 @@ navigating to another page. Some implementations, such as Chromium, have chosen
         
"`biddingLogicURL`"
"`biddingWasmHelperURL`"
"`updateURL`" -
"`trustedBiddingSignalsURL`"
- 1. For each |groupMember| and |interestGroupField| in the following table + 1. For each |groupMember| and |interestGroupField|, in the following table @@ -4271,21 +4717,22 @@ navigating to another page. Some implementations, such as Chromium, have chosen - - - -
Group memberInterest group field
"`updateURL`" [=interest group/update url=]
"`trustedBiddingSignalsURL`"[=interest group/trusted bidding signals url=]
- 1. Let |parsedURL| be the result of running the [=URL parser=] on |value|. 1. If |key| is not |groupMember|, [=iteration/continue=]. - 1. Jump to the step labeled Abort update - if any of the following conditions hold: - * |parsedURL| is failure; - * |parsedURL|'s [=url/origin=] is not [=same origin=] with |ig|'s - [=interest group/owner=]; - * |parsedURL| [=includes credentials=]; - * |parsedURL| [=url/fragment=] is not null. - 1. Set |ig|'s |interestGroupField| to |parsedURL|. + 1. If |value| is a [=string=]: + 1. Let |parsedURL| be the result of [=parse and verify a bidding code or update URL=] + on |value| and |ig|'s [=interest group/owner=]. + 1. If |parsedURL| is failure, jump to the step labeled + Abort update + 1. Set |ig|'s |interestGroupField| to |parsedURL|. + +
"`trustedBiddingSignalsURL`"
+
+ 1. If |value| is a [=string=]: + 1. Let |parsedURL| be the result of [=parse and verify a trusted signals URL=] on |value|. + 1. If |parsedURL| is failure, jump to the step labeled + Abort update + 1. Set |ig|'s [=interest group/trusted bidding signals url=] to |parsedURL|.
"`trustedBiddingSignalsKeys`"
@@ -4374,7 +4821,7 @@ navigating to another page. Some implementations, such as Chromium, have chosen following conditions hold: * |ig|'s [=interest group/ads=] is not null, and |ig|'s [=interest group/additional bid key=] is not null; - * |ig|'s [=interest group/estimated size=] is greater than 50 KB. + * |ig|'s [=interest group/estimated size=] is greater than 1048576 bytes. 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. Set |ig|'s [=interest group/last updated=] to the [=current wall time=]. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and @@ -4408,7 +4855,9 @@ To process updateIfOlderThanMs given an [=origin=] |buyer|, and an [= # Feature Detection # {#feature-detection} -The {{ProtectedAudience/queryFeatureSupport()}} method permits checking what functionality is available in the current implementation, in order to help deploy new features. The return values specified in this specification are for an implementation that fully implements it. +The {{ProtectedAudience/queryFeatureSupport()}} method permits checking what functionality is +available in the current implementation, in order to help deploy new features. The return values +specified in this specification are for an implementation that fully implements it. [SecureContext] @@ -4425,8 +4874,21 @@ interface ProtectedAudience { <div algorithm> The <dfn for=ProtectedAudience method>queryFeatureSupport(feature)</dfn> method steps are: -1. If feature is "adComponentsLimit", return 40. -2. Return `undefined`. +1. Let |featuresTable| be an [=ordered map=] whose keys are {{DOMString}}s and whose values are + {{boolean}}s or {{long}}s, with the following entries: + : "adComponentsLimit" + :: 40 + : "deprecatedRenderURLReplacements" + :: true + : "permitCrossOriginTrustedSignals" + :: true + : "realTimeReporting" + :: true + : "reportingTimeout" + :: true +1. If |feature| is "*", then return |featuresTable|. +1. If |featuresTable|[|feature|] [=map/exists=], then return |featuresTable|[|feature|]. +1. Return `undefined`. </div> @@ -4469,7 +4931,8 @@ The <dfn for="interest group">estimated size</dfn> of an [=interest group=] |ig| Note: Each of [=interest group/trusted bidding signals slot size mode=]'s value represents a {{long}} integer, so 4 bytes. -1. [=list/For each=] |key| of |ig|'s [=interest group/trusted bidding signals keys=]: +1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, + [=list/for each=] |key| of it: 1. The [=string/length=] of |key|. 1. If |ig|'s [=interest group/max trusted bidding signals url length=] is not null: 1. 4, which is the size of |ig|'s [=interest group/max trusted bidding signals url length=]. @@ -4508,6 +4971,33 @@ The <dfn for="interest group">estimated size</dfn> of an [=interest group=] |ig| 1. Return the [=URL serializer|serialization=] of |url|. </div> +<div algorithm> + To <dfn>parse and verify a bidding code or update URL</dfn> given a [=string=] |input| and an + [=origin=] |igOwner|: + + 1. Let |parsedUrl| be the result of running the [=URL parser=] on |input|. + 1. Return failure if any of the following conditions hold: + * |parsedUrl| is failure; + * |parsedUrl|'s [=url/origin=] is not [=same origin=] with |igOwner|; + * |parsedUrl| [=includes credentials=]; + * |parsedUrl|'s [=url/fragment=] is not null. + 1. [=Assert=]: |parsedUrl|'s [=url/scheme=] is "`https`". + 1. Return |parsedUrl|. +</div> + +<div algorithm> + To <dfn>parse and verify a trusted signals URL</dfn> given a [=string=] |input|: + + 1. Let |parsedUrl| be the result of running the [=URL parser=] on |input|. + 1. Return failure if any of the following conditions hold: + * |parsedUrl| is failure; + * |parsedUrl|'s [=url/scheme=] is not "`https`"; + * |parsedUrl| [=includes credentials=]; + * |parsedUrl|'s [=url/fragment=] is not null; + * |parsedUrl|'s [=url/query=] is not null. + 1. Return |parsedUrl|. +</div> + <div algorithm> To <dfn>round a value</dfn> given a {{double}} |value|: @@ -4551,6 +5041,14 @@ Issue: This would ideally be replaced by a more descriptive algorithm in Infra. 1. Return [=ASCII encoded=] |uuidStr|. </div> +<div algorithm> + To <dfn>insert entries to map</dfn> given an [=ordered map=] |map|, a |key| which is in same type + as |map|'s [=map/key=], and a [=list=] |entries| which is in same type as |map|'s [=map/value=]: + + 1. If |map|[|key|] [=map/exists=], then [=list/extend=] |map|[|key|] with |entries|. + 1. Otherwise, [=map/set=] |map|[|key|] to |entries|. +</div> + # Permissions Policy Integration # {#permissions-policy-integration} @@ -4893,10 +5391,12 @@ dictionary BiddingBrowserSignals { required long adComponentsLimit; required unsigned short multiBidLimit; + record<DOMString, DOMString> requestedSize; USVString topLevelSeller; sequence<PreviousWin> prevWinsMs; object wasmHelper; unsigned long dataVersion; + unsigned long crossOriginDataVersion; boolean forDebuggingOnlyInCooldownOrLockout = false; }; @@ -4907,12 +5407,28 @@ dictionary ScoringBrowserSignals { required unsigned long biddingDurationMsec; required DOMString bidCurrency; + record<DOMString, DOMString> renderSize; unsigned long dataVersion; + unsigned long crossOriginDataVersion; sequence<USVString> adComponents; boolean forDebuggingOnlyInCooldownOrLockout = false; }; +
+ To convert an ad size to a map given an [=ad size=] |adSize|: + + 1. Let |dict| be a new empty [=map=]. + 1. Let |jsWidth| be |adSize|'s [=ad size/width=], [=converted to an ECMAScript value=]. + 1. [=map/Set=] |dict|["width"] to the result of [=string/concatenating=] « [$ToString$](|jsWidth|), + |adSize|'s [=ad size/width units=] ». + 1. Let |jsHeight| be |adSize|'s [=ad size/height=], [=converted to an ECMAScript value=]. + 1. [=map/Set=] |dict|["height"] to the result of [=string/concatenating=] « [$ToString$](|jsHeight|), + |adSize|'s [=ad size/height units=] ». + 1. Return |dict|. + +
+ Note: {{ScoringBrowserSignals}}'s {{ScoringBrowserSignals/adComponents}} is {{undefined}} when [=generated bid/ad component descriptors=] is null or [=list/is empty|an empty list=]. It cannot be an [=list/is empty|empty list=]. @@ -5120,12 +5636,8 @@ An interest group is a [=struct=] with the following [=struct/items=] : trusted bidding signals url :: Null or a [=URL=]. Provide a mechanism for making real-time data available for use at bidding time. See [=building trusted bidding signals url=]. -

- When non-null, the [=interest group/trusted bidding signals url=]'s [=origin=] will always be - [=same origin=] with [=interest group/owner=]. -

: trusted bidding signals keys - :: Null or a [=list=] of [=string=]. See [=building trusted bidding signals url=]. + :: Null or a [=list=] of [=strings=]. See [=building trusted bidding signals url=]. : trusted bidding signals slot size mode :: "`none`", "`slot-size`" or "`all-slots-requested-sizes`". Initially "`none`". Each value reprensents a {{long}} integer. See [=calculate the ad slot size query param=]. @@ -5266,10 +5778,6 @@ An auction config is a [=struct=] with the following [=struct/items=] Provide a mechanism for making real-time data (information about a specific [=ad creative=]) available for use at [=evaluate a scoring script|scoring=] time, e.g. the results of some ad scanning system. -

- When non-null, the [=auction config/trusted scoring signals url=]'s [=origin=] will always be - [=same origin=] with [=auction config/seller=]. -

: max trusted scoring signals url length :: A {{long}} integer, initially 0. Indicates the maximum trusted scoring signals fetch url length for the auction config. 0 means no limit. @@ -5422,6 +5930,16 @@ An auction config is a [=struct=] with the following [=struct/items=] :: A [=boolean=] or failure, initially false. Specifies whether some bids will be provided as signed exchanges. Sets to failure if the {{AuctionAdConfig/additionalBids}} {{Promise}} is [=rejected=]. + : seller real time reporting config + :: Null, or a [=string=], initially null. Seller's real time reporting type. Currently the only + supported type is "default-local-reporting" indicating local differential privacy. If not null, + the seller opted in to receive real time reports. + : per buyer real time reporting config + :: An [=ordered map=], whose [=map/keys=] are [=origins=], and whose [=map/values=] are [=strings=]. + Each buyer's real time reporting type. Currently the only supported type is + "default-local-reporting" indicating local differential privacy. All buyers in the map opted in + to receive real time reports. +
@@ -5437,7 +5955,8 @@ To wait until configuration input promises resolve given an [=auction [=auction config/expects additional bids=] is false. 1. If |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], - [=auction config/per buyer timeouts=], [=auction config/per buyer cumulative timeouts=], [=auction config/deprecated render url replacements=], or + [=auction config/per buyer timeouts=], [=auction config/per buyer cumulative timeouts=], + [=auction config/deprecated render url replacements=], or [=auction config/direct from seller signals header ad slot=] is failure, return failure. 1. Return.
@@ -5493,13 +6012,278 @@ To look up per-buyer multi-bid limit given an [=auction config=] |auc

Bid generator

A per buyer bid generator is an [=ordered map=] whose [=map/keys=] are [=URLs=] -representing [=interest group/trusted bidding signals urls=], and whose [=map/values=] are -[=per signals url bid generators=]. +or null representing [=interest group/trusted bidding signals urls=], and whose [=map/values=] are +[=per signals url bid generators=]. The key of null is used for [=interest groups=] that do not +specify a [=interest group/trusted bidding signals url=]. A per signals url bid generator is an [=ordered map=] whose [=map/keys=] are [=origins=] representing [=interest group/joining origins=], and whose [=map/values=] are [=lists=] of [=interest groups=]. +

Script fetcher

+ +A script fetcher helps manage asynchronous fetching of scripts and handling of their +headers. It's a [=struct=] with the following [=struct/items=]: + +
+ : script body + :: A [=byte sequence=], null, or failure. Initially null. The body of the script. + : origins authorized for cross origin trusted signals + :: A [=list=] of [=origins=] or null. Initially null. Parsed value of + [:Ad-Auction-Allow-Trusted-Scoring-Signals-From:]. +
+ +
+To create a new script fetcher given a [=URL=] |url| and an [=origin=] |frameOrigin|: + + 1. Let |fetcher| be a new [=script fetcher=]. + 1. Let |queue| be the result of [=starting a new parallel queue=]. + 1. [=parallel queue/enqueue steps|Enqueue the following steps=] to |queue|: + 1. [=Fetch script=] given |url|, |frameOrigin| and |fetcher|. + 1. Return |fetcher|. +
+ +
+To wait for script body from a fetcher given a [=script fetcher=] |fetcher|: + + 1. Wait until |fetcher|'s [=script fetcher/script body=] is not null. + 1. Return |fetcher|'s [=script fetcher/script body=]. +
+ +
+To wait for cross origin trusted scoring signals authorization from a fetcher given a +[=script fetcher=] |fetcher|: + + 1. Wait until |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=]. + is not null. + 1. Return |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=]. +
+ +The Ad-Auction-Allow-Trusted-Scoring-Signals-From HTTP response +header is a [=structured header=] whose value must be a [=structured header/list=] of [=structured +header/strings=]. + +
+To parse allowed trusted scoring signals origins given a [=header list=] |headerList|: + + 1. Let |parsedHeader| be the result of [=header list/getting a structured field value=] + given [:Ad-Auction-Allow-Trusted-Scoring-Signals-From:] and "`list`" from |headerList|. + 1. If |parsedHeader| is null, return an empty [=list=]. + 1. Let |result| be a new [=list=] of [=origins=]. + 1. [=list/For each=] |entry| in |parsedHeader|: + 1. If |entry| is not a [=string=], return an empty [=list=]. + 1. Let |parsedEntry| be the result of [=parsing an https origin=] on |entry|. + 1. If |parsedEntry| is failure, return an empty [=list=]. + 1. [=list/Append=] |parsedEntry| to |result|. + 1. Return |result|. +
+ +
+To fetch script given a [=URL=] |url|, an [=origin=] |frameOrigin|, and a +[=script fetcher=] |fetcher|: + 1. Let |request| be a new [=request=] with the following properties: + : [=request/URL=] + :: |url| + : [=request/header list=] + :: «`Accept`: `text/javascript`» + : [=request/client=] + :: `null` + : [=request/origin=] + :: |frameOrigin| + : [=request/mode=] + :: "`no-cors`" + : [=request/referrer=] + :: "`no-referrer`" + : [=request/credentials mode=] + :: "`omit`" + : [=request/redirect mode=] + :: "`error`" + + Issue: One of the side-effects of a `null` client for this subresource request is it neuters all + service worker interceptions, despite not having to set the service workers mode. + + Issue: Stop using "`no-cors`" mode where possible + (WICG/turtledove#667). + 1. Let |fetchController| be the result of [=fetching=] |request| with [=fetch/useParallelQueue=] + set to true, and [=fetch/processResponse=] set to the following steps given + a [=response=] |response|: + 1. If the result of [=validating fetching response headers=] given |response| is false: + 1. [=fetch controller/Abort=] |fetchController|. + 1. Set |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=] to + an empty [=list=] of [=origins=]. + 1. Set |fetcher|'s [=script fetcher/script body=] to failure. + 1. Set |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=] to the + result of [=parsing allowed trusted scoring signals origins=] given |response|'s [=response/ + header list=]. + 1. Let |bodyStream| be |response|’s [=response/body=]’s [=body/stream=]. + 1. Let |bodyReader| be result of [=ReadableStream/getting a reader=] from |bodyStream|. + 1. Let |successSteps| be a set of steps that take a [=byte sequence=] |responseBody|, and + perform the following: + 1. If [=validate fetching response mime and body=] with |response|, |responseBody| and + "`text/javascript`" returns false, set |fetcher|'s [=script fetcher/script body=] to + failure. + 1. Otherwise, set set |fetcher|'s [=script fetcher/script body=] to |responseBody|. + 1. Let |failureSteps| be a set of steps that take an [=exception=] e, and + perform the following: + 1. Set set |fetcher|'s [=script fetcher/script body=] to failure. + 1. [=ReadableStreamDefaultReader/Read all bytes=] from |bodyReader|, given |successSteps| + and |failureSteps|. +
+ +

Trusted bidding signals batcher

+ +A trusted bidding signals batcher helps manage merging multiple trusted bidding signals +into smaller number of fetches. It's a [=struct=] with the following [=struct/items=]: + +
+ : all trusted bidding signals + :: An [=ordered map=] whose [=map/keys=] are [=strings=], and [=map/values=] are [=strings=]. + Contains all trusted bidding signals collected thus far. + : all per interest group data + :: An [=ordered map=] whose [=map/keys=] are [=interest group/name=] [=strings=] and whose + [=map/values=] are [=bidding signals per interest group data=]. + : no signals flags + :: An [=ordered map=] whose [=map/keys=] are [=interest group/name=] [=strings=] and whose + [=map/values=] are enums "no-fetch" or "fetch-failed". This is set to "not-fetched" if given + interest group did not request any trusted bidding signals keys, or "fetch-failed" if its + trusted signals fetch failed. + : data versions + :: An [=ordered map=] who [=map/keys=] are [=interest group/name=] [=strings=] and [=map/values=] + are {{unsigned long}} or null. This contains data version returned by a fetch that provided the + values for a given interest group name. + : keys + :: An [=ordered set=] of [=strings=]. Describes the keys collected to be fetched in the current + batch thus far. + : ig names + :: An [=ordered set=] of [=strings=]. Describes the interest group names to be fetched in the + current batch thus far. + : length limit + :: A {{long}}, initially 2147483647 (the maximum value that it can hold). Describes the URL length + limit the current batch is limited to, the smallest of the limits of the interest groups included. +
+ +A bidding signals per interest group data is a [=struct=] with the following +[=struct/items=]: + +
+ : updateIfOlderThanMs + :: Null or a {{double}}. If non-null, it is the [=duration=] in milliseconds after which the + [=interest group=] is eligible for an [update](#interest-group-update), if it hasn't been joined + or updated within that duration. When eligible, the update is performed either following a call + to {{Navigator/runAdAuction()}}, for each of the passed {{AuctionAdConfig/interestGroupBuyers}}, + or explicitly by the {{Navigator/updateAdInterestGroups()}} method. +
+ +
+ +To append to a bidding signals per-interest group data map given an [=ordered map=] +|sourceMap|, an [=ordered set=] |igNames|, and an [=ordered map=] |destinationMap|: +1. [=map/For each=] |sourceKey| → |sourceValue| of |sourceMap|: + 1. If |igNames| does not [=set/contain=] |sourceKey|, [=iteration/continue=]. + 1. Let |perGroupData| be a new [=bidding signals per interest group data=]. + 1. Issue: handle priority vector + (WICG/turtledove#1144). + 1. If |sourceValue| is not an [=ordered map=], [=iteration/continue=]. + 1. If |sourceValue|["`updateIfOlderThanMs`"] is a {{double}}: + 1. Let |updateIfOlderThanMs| be |sourceValue|["`updateIfOlderThanMs`"]. + 1. If |updateIfOlderThanMs| < 600,000, set |updateIfOlderThanMs| to 600,000. + + Note: 600,000 milliseconds is 10 minutes. + 1. Set |perGroupData|'s [=bidding signals per interest group data/updateIfOlderThanMs=] to + |updateIfOlderThanMs|. + 1. If |perGroupData|'s [=bidding signals per interest group data/updateIfOlderThanMs=] is not + null, then [=map/set=] |destinationMap|[|sourceKey|] to |perGroupData|. + +
+ + +
+To fetch the current outstanding trusted signals batch given a +[=trusted bidding signals batcher=] |trustedBiddingSignalsBatcher|, a [=URL=] |signalsUrl|, +an [=origin=] |scriptOrigin|, an {{unsigned short}}-or-null |experimentGroupId|, +an [=origin=] |topLevelOrigin|, and a [=string=] |slotSizeQueryParam|: + + 1. If |signalsUrl| is null, return. + 1. Let |biddingSignalsUrl| be the result of [=building trusted bidding signals url=] with + |signalsUrl|, |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=], + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=], + |experimentGroupId|, |topLevelOrigin|, and |slotSizeQueryParam|. + 1. Let « |partialTrustedBiddingSignals|, |partialPerInterestGroupData|, |dataVersion| » be the + result of [=fetching trusted signals=] with |biddingSignalsUrl|, |scriptOrigin|, and true. + 1. If |partialTrustedBiddingSignals| is not null: + 1. [=map/For each=] |key| → |value| in |partialTrustedBiddingSignals|, [=map/set=] + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/all trusted bidding + signals=][|key|] to |value|. + 1. [=set/For each=] |igName| of |trustedBiddingSignalsBatcher|'s [=trusted bidding signals + batcher/ig names=], [=map/set=] |trustedBiddingSignalsBatcher|'s [=trusted bidding signals + batcher/data versions=][|igName|] to |dataVersion|. + 1. [=Append to a bidding signals per-interest group data map=] with + |partialPerInterestGroupData|, |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/ig names=], and |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/all per interest group data=]. + 1. Otherwise, [=set/for each=] |igName| of |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/ig names=]: + 1. [=map/Set=] |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/no signals flags=][|igName|] to "fetch-failed". + +
+ +
+To batch or fetch trusted bidding signals given a [=trusted bidding signals batcher=] +|trustedBiddingSignalsBatcher|, [=interest group=] |ig|, a [=URL=] |signalsUrl|, +an [=origin=] |scriptOrigin| an {{unsigned short}}-or-null |experimentGroupId|, +an [=origin=] |topLevelOrigin|, and a [=string=] |slotSizeQueryParam|: + + 1. Let |igName| be |ig|'s [=interest group/name=]. + 1. If |signalsUrl| is null: + 1. [=map/Set=] |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/no signals flags=] + [|igName|] to "no-fetch". + 1. Return. + 1. [=map/Set=] |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/no signals flags=][|igName|] to "no-fetch" + if |ig|'s [=interest group/trusted bidding signals keys=] is null or [=map/is empty=]. + + Note: An interest group with no trusted signals keys requests would still fetch and process + per-interest group data like priorityVector and + [=bidding signals per interest group data/updateIfOlderThanMs=], but it will get null passed in + to its bidding function. + + 1. Let |putativeKeys| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/keys=]. + 1. Let |putativeIgNames| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/ig names=]. + 1. Let |putativeLengthLimit| be |ig|'s [=interest group/max trusted bidding signals url length=]. + 1. If |ig|'s [=interest group/max trusted bidding signals url length=] is 0 or > + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=], + then set |putativeLengthLimit| to |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/length limit=]. + 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, [=list/extend=] + |putativeKeys| with |ig|'s [=interest group/trusted bidding signals keys=]. + 1. [=list/Append=] |igName| to |putativeIgNames|. + 1. Let |biddingSignalsUrl| be the result of [=building trusted bidding signals url=] with + |signalsUrl|, |putativeKeys|, |putativeIgNames|, |experimentGroupId|, |topLevelOrigin|, and + |slotSizeQueryParam|. + 1. If [=URL serializer|serialized=] |biddingSignalsUrl|'s [=string/length=] ≤ |putativeLengthLimit|: + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to |putativeKeys|. + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=] to + |putativeIgNames|. + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=] to + |putativeLengthLimit|. + 1. Otherwise: + 1. [=Fetch the current outstanding trusted signals batch=] given |trustedBiddingSignalsBatcher|, + |signalsUrl|, |scriptOrigin|, |experimentGroupId|, |topLevelOrigin|, |slotSizeQueryParam|. + 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, set + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a + [=list/clone=] of |ig|'s [=interest group/trusted bidding signals keys=]. + 1. Otherwise, set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a + new [=list=]. + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=] to + « |igName| ». + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=] to + |ig|'s [=interest group/max trusted bidding signals url length=]. + +
+

Generated bid

A generated bid is a bid that needs to be scored by the seller. The bid is either the @@ -5633,6 +6417,7 @@ A bid debug reporting info is a [=struct=] with the following [=struc : interest group owner :: An [=origin=]. Matches [=interest group/owner=] of the interest group that was used to call `generateBid()` to produce this reporting information. + : bidder debug win report url :: Null or a [=URL=], initially null. Set by `generateBid()`'s {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope/forDebuggingOnly}}'s @@ -5904,6 +6689,25 @@ An auction report info is a [=struct=] with the following [=struct/it experimenting with third party cookie deprecation (before they have been fully removed), the `forDebuggingOnly` reporting APIs are not downsampled, so they are still lists. + : real time reporting contributions map + :: A [=real time reporting contributions map=] + + +A real time reporting contributions map is an [=ordered map=] whose [=map/keys=] are +[=origins=] and whose [=map/values=] are [=lists=] of [=real time reporting contributions=]. + +A real time reporting contribution is a [=struct=] with the following [=struct/items=]: + +
+ : bucket + :: A {{long}}. Must be greater than 0 and less than the sum of [=number of user buckets=] and + [=number of platform buckets=]. + : priority weight + :: A {{double}}. Must be greater than 0 and less than infinity, dictates the relative likelihood + of which bucket will get the contribution. + : latency threshold + :: Null or a [=duration=] in milliseconds. Initially null. + Reports when a latency (e.g., `generateBid()` execution latency) is greater than this threshold.

K-Anonymity Records