Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing each interestGroup owner to be associated with multiple keys in perBuyerSignals #1211

Open
ningwangpanda opened this issue Jun 21, 2024 · 7 comments

Comments

@ningwangpanda
Copy link

In the Protected Audience API, each interestGroup owner is only associated with one key in perBuyerSignals, which defaults to the interestGroup owner, i.e., the buyer_origin. Currently, two DSPs from the same company (under the same domain) have to wire separate copies of perBuyerSignals to SSPs when their interestGroup owners are two different buyer_origins under the same domain, for example, dsp1.company.com and dsp2.company.com under company.com, even though their perBuyerSignals have common data. DSPs are optimizing the size of perBuyerSignals being sent to SSPs. Wiring perBuyerSignals twice is not an optimized solution due to extra bandwidth and process load. We propose that Chrome implement a mechanism to allow each interestGroup owner to be associated with perBuyerSignals shared across multiple interestGroup owners, so that DSPs can optimize wiring of perBuyersignals to SSPs across different DSPs from the same company without increasing load on wiring and impacting performance.

Here is our detailed proposal. Our proposal is only for the same contextual request that is eligible for multiple DSPs, which lands at the same contextual ad server. Serving and merging common perBuyerSignals from different DSP servers or across different contextual responses for the same ad slot are non-goals of this proposal.

We use DSP1 (dsp1.company.com) and DSP2 (dsp2.company.com) from company.com as an example. We also assume all interest groups of DSP1 have dsp1.company.com as the owner; all interest groups of DSP2 have dsp2.company.com as the owner.

DSP1 and DSP2 share the same contextual ad server from company.com. When both DSPs owned by the company are eligible to participate in auction, the contextual ad server wires common perBuyerSignals and perBuyerSignals specific to DSP1 and DSP2 according to the eligibility of DSP1 and DSP2 in the contextual response. Chrome will use all wired data to build the final perBuyerSignals (common perBuyerSignals and DSP-specific perBuyerSignals) for DSP1 and DSP2. The server wires DSP1-specific perBuyerSignals using dsp1.company.com; wires DSP2-specific perBuyerSignals using dsp2.company.com; wires common perBuyerSignals using company.com. The company.com domain does not own any interest groups; it is only being used to wire common perBuyerSignals.

per_buyer_signals = { 
         "https://www.company.com" : [common_per_buyer_signals] 
         "https://www.dsp1.company.com": [dsp1_specific_per_buyer_signals]
         "https://www.dsp2.company.com": [dsp2_specific_per_buyer_signals] 
 }

We propose that Chrome allows each interestGroup owner to be associated with multiple keys in perBuyerSignals for DSPs in the attestation files. For example, besides the default perBuyerSignals owned by interestGroup owners (i.e., buyer_origins), Chrome can allow DSPs to specify extra perBuyerSignals via a new key-value pair: <interestGroup owner, keys of extra perBuyerSignals>. The key-value pair will be static for DSPs. In the rare case that a company launches new DSPs, DSPs will update our attestation files.

{
     "enrollment_site": "https://www.company.com",
     "platform_attestations": [
       {
         "platform": "chrome",
         "attestations": {
           "protected_audience_api": {
             "perBuyerSignalsSharing": [
               {"https://www.dsp1.company.com": "https://www.company.com"},
               {"https://www.dsp2.company.com": "https://www.company.com"},
            ]
           }
         }
       }
     ]
}

In auction configs, interest groups owned by dsp1.company.com will be associated with company.com and dsp1.company.com in perBuyerSignals; interest groups owned by dsp2.company.com will be associated with company.com and dsp2.company.com in perBuyerSignals.

const myAuctionConfig = {
  'seller': 'https://www.example-ssp.com',

  // This should be a list of all DSPs that you wish to participate in this auction
  'interestGroupBuyers': ['https://www.dsp1.company.com', 'https://www.dsp2.company.com', ...],

  'perBuyerSignals': {'https://www.company.com': ...,
                      'https://www.dsp1.company.com': ...,
                      'https://www.dsp2.company.com': ...,
                      ...},
};
const result = await navigator.runAdAuction(myAuctionConfig);

In generateBid(), Chrome provides (common_per_buyer_signals and dsp1_specific_per_buyer_signals) for DSP1’s interest groups and (common_per_buyer_signals and dsp2_specific_per_buyer_signals) for DSP2’s interest groups.

generateBid(interestGroup, auctionSignals, perBuyerSignals,
    trustedBiddingSignals, browserSignals, directFromSellerSignals) {
  ...
  return {'ad': adObject,
          'bid': bidValue};
}
@michaelkleber
Copy link
Collaborator

It seems to me that you can accomplish this goal today, when the auction config is constructed:

const sharedCompanyDotComSignals = {'foo':123, 'bar':456};
const myAuctionConfig = {
  'seller': 'https://www.example-ssp.com',

  // This should be a list of all DSPs that you wish to participate in this auction
  'interestGroupBuyers': ['https://www.dsp1.company.com', 'https://www.dsp2.company.com'],

  'perBuyerSignals': {
                      'https://www.dsp1.company.com': {...sharedCompanyDotComSignals, 'dsp1signal':789},
                      'https://www.dsp2.company.com': {...sharedCompanyDotComSignals, 'dsp2signal':987}
                      },
};
const result = await navigator.runAdAuction(myAuctionConfig);

@dmdabbs
Copy link
Contributor

dmdabbs commented Jun 21, 2024

It seems to me that you can accomplish this goal today, when the auction config is constructed:

It is possible that the evolving OpenRTB IG buyer intent signaling specification could be amended to provide carriage for "shared pbs" so that the seller/SSP can assemble the config.

Wiring perBuyerSignals twice is not an optimized solution due to extra bandwidth and process load.

If your sibling buyers share perBuyerSignals, might they also share prioritySignals? Seems unlikely, but thought I'd ask.

NB: Link above is to a proposed revision. The PR is here: InteractiveAdvertisingBureau/openrtb#175.

@ningwangpanda
Copy link
Author

We have explored the option of constructing perBuyerSignals with help from SSPs. We believe sharing perBuyerSignals in Chrome API is a superior solution due to the following considerations.

  1. SSPs need to make bid protocol changes coordinating with the ecosystem to merge perBuyerSignals for ALL DSPs that they work with.
  2. If an SSP works with some DSPs that would like to share perBuyerSignals, that SSP needs to equally support all DSPs that it works with. This implies that most, if not all, SSPs have to support perBuyerSignals sharing.
  3. OpenRTB is one of the exchange protocols. We will need most, if not all, exchange protocols to make corresponding changes.
  4. Some DSPs do not need to share perBuyerSignals. However, for DSPs that need to share perBuyerSignals, changes need to happen in all buying paths, including all exchange bids, direct buys and reservations.

Overall, Chrome API support is much cleaner because it automatically covers all DSPs, SSPs and exchanges.

Note that DSPs need to be able to regularly update common perBuyerSignals, thus using a variable and JS to send them once is not an option for DSPs.

const sharedCompanyDotComSignals = {'foo':123, 'bar':456};

Lastly, we do not plan to share prioritySignals at this moment.

@dmdabbs
Copy link
Contributor

dmdabbs commented Jun 29, 2024

In generateBid(), Chrome provides (common_per_buyer_signals and dsp1_specific_per_buyer_signals) for DSP1’s interest groups and (common_per_buyer_signals and dsp2_specific_per_buyer_signals) for DSP2’s interest groups.

Today, Chrome is not opinionated about buyer signals - they're opaque "any JSON-serializable values." Since Chrome will be merging them you must be requiring them to be dictionaries for that to make sense. Your common signals illustration used arrays, so I assume this was simply a placeholder.

In any event, if our DSP decided we needed a second IG buyer domain with common signals as you describe, we would need the protocols to sellers/exchanges, such as OpenRTB, to accommodate "common pbs" as suggested earlier, else I do not see how we would provision the common signals to the auction.

@ningwangpanda
Copy link
Author

Chrome’s perBuySignals (i.e., buyer data) in their current forms are JS Objects, which are dictionary-like or hash-map-like. The keys are buyer_origins, the values are opaque "any JSON-serializable values." We are not proposing any changes here.

  'perBuyerSignals': {'https://www.company.com': <JSON-shared>,
                      'https://www.dsp1.company.com': <JSON1>,
                      'https://www.dsp2.company.com': <JSON2>,
                      ...},

Chrome defaults to the interestGroup owner, i.e., the buyer_origin when looking for perBuyerSignals in runAdAuction(). We are proposing that Chrome adds extra perBuyerSignals specified by extra keys (company.com) in our attestation files for each interestGroup owner (dsp1.company.com and dsp2.company.com), namely, we are proposing that Chrome looks for perBuyerSignals using both keys in the implementation of runAdAuction(). In our example, we are proposing that Chrome uses dsp1.company.com and company.com as keys for DSP1, and dsp2.company.com and company.com as keys for DSP2. Note that we are NOT proposing any interface changes on auctionConfig; nor are we asking Chrome to concatenate opaque JSON-serializable values here. DSPs provide perBuyerSignalsSharing to Chrome in the enrollment process. Chrome can store perBuyerSignalsSharing (i.e., a JS Object used by all DSPs) internally in its own code base. In rare situations where DSPs need to update perBuyerSignalsSharing (for example, company.com launches or deprecates DSPs), DSPs can update our attestation files with Chrome.

"perBuyerSignalsSharing": [
               {"https://www.dsp1.company.com": "https://www.company.com"},
               {"https://www.dsp2.company.com": "https://www.company.com"},
]

In generateBid(), Chrome supplies perBuyerSignals keyed by the interestGroup owner. In order to supply extra perBuyerSignals, we have two options. Note that Option 2 requires an interface change, while Option 1 does not. DSPs do not have preferences between these two options at this moment.

Option 1: We propose that Chrome concatenates perBuyerSignals keyed by both the interestGroup owner and perBuyerSignalsSharing into a JSON array, and then passes on the concatenated perBuyerSingals to generateBid(). In our example, Chrome concatenates (keyed by dsp1.company.com) and (keyed by company.com) and passes the concatenated perBuySignals [, ] to generateBid() for DSP1; Chrome concatenates (keyed by dsp2.company.com) and (keyed by company.com) and passes the concatenated perBuySignals [, ] to generateBid() for DSP2.

Option 2: We propose to change the signature of generateBid() by adding an additional argument sharedPerBuyerSignals, where Chrome can supply buyer data keyed by perBuyerSignalsSharing to sharedPerBuyerSignals.

generateBid(interestGroup, auctionSignals, perBuyerSignals, sharedPerBuyerSignals,
    trustedBiddingSignals, browserSignals, directFromSellerSignals) {
  …
}

In our example, Chrome passes (keyed by dsp1.company.com) to perBuyerSignals and (keyed by company.com) to sharedPerBuyerSignals for DSP1; Chrome passes (keyed by dsp2.company.com) to perBuyerSignals and (keyed by company.com) to sharedPerBuyerSignals for DSP2.

About backwards compatibility (using DSP1 as an example):

  1. Older Chrome versions without perBuyerSignalsSharing: Nothing changes.
  2. Older Chrome versions with perBuyerSignalsSharing: Chrome ignores the extra keys specified by perBuyerSignalsSharing, so DSP1 only gets even if DSP1 specifies common buyer data in perBuyerSignalsSharing. In Option 1, Chrome does not concatenate and . In Option 2, if Chrome does not supply to sharedPerBuyerSignals; thus even if DSP1 has the new version of generateBid(), DSP1 only gets .
  3. New Chrome versions without perBuyerSignalsSharing: In new Chrome versions that support buyer data sharing, nothing changes if perBuyerSignalsSharing is empty. Thus, nothing changes for DSPs that do not need to share buyer data.
  4. New Chrome versions with perBuyerSignalsSharing: DSP1 gets both and if DSP1 specifies common buyer data in perBuyerSignalsSharing.

Thus, the potential issue is: If Chrome rolls back to an older version after DSPs add common buyer data in perBuyerSignalsSharing, DSPs will miss common buyer data. In Option 1, DSP1 only gets because Chrome does not concatenate and . In Option 2, without Chrome supplying sharedPerBuyerSignals, DSP1 only gets even if DSP1 implements the new generateBid() with sharedPerBuyerSignals. However, Chrome rarely rolls back. Overall, DSPs that need to share common buyer data should check Chrome versions and point to the new versions.

In generateBid(), Chrome provides (common_per_buyer_signals and dsp1_specific_per_buyer_signals) for DSP1’s interest groups and (common_per_buyer_signals and dsp2_specific_per_buyer_signals) for DSP2’s interest groups.

The above only shows common and DSP-specific perBuyerSignals that DSP1 and DSP2 get in generateBid() with support on perBuyerSignals sharing. DSPs will follow Chrome’s implementation details to enable perBuyerSignals sharing from our side.

@michaelkleber
Copy link
Collaborator

Your proposal relies on a global sharing configuration that is submitted as part of the registration process and distributed to every browser. This is not a piece of infrastructure that exists today, and building a new distribution system for blobs of metadata would be a large task. (I acknowledge that we are already distributing the fact that a particular domain is set up to use the PA API in the first place, but I'm sure you recognize that a single bit for each domain is a world apart from what you're envisioning here.)

For now, I don't think that is a reasonable piece of infrastructure to expect browsers to provide. Maybe there will be a compelling reason to build the thing you're imagining; I can think of reasons why a single global config object would have privacy benefit, for example. But I certainly wouldn't count on it, and this use case will surely not justify it.

I continue to think that support from some SSP who builds the auction configuration object seems much more plausible than any sort of dedicated browser support for this kind of feature.

@ningwangpanda
Copy link
Author

Add perBuyerSignalsSharing in auctionConfig (preferred short-term solution)

In our current implementation, DSPs send perBuyerSignals to SSPs in contextual responses from our contextual servers. If Chrome adds a new field perBuyerSignalsSharing in auctionConfig, DSPs can send perBuyerSignalsSharing to SSPs in our contextual responses as well.

const myAuctionConfig = {
  'perBuyerSignals': {'https://www.company.com': ...,
                      'https://www.dsp1.company.com': ...,
                      'https://www.dsp2.company.com': ...,
                      ...},
  'perBuyerSignalsSharing': 
                    {'https://www.dsp1.company.com': 'https://www.company.com',
                     'https://www.dsp2.company.com': 'https://www.company.com',
                     ...},
};

In this proposal, SSPs enforce sharing mechanisms and permissions, i.e., which site or origin can share what data with which site or origin. Different SSPs might enforce different sharing mechanisms for an extended period of time until SSPs adopt a commonly shared open source library of enforcement.

Adding perBuyerSignalsSharing in interest groups (alternative short-term solution)

If Chrome adds a new field perBuyerSignalsSharing in interest groups, DSPs can populate them. Note that our DSPs are above the 10MB limit on kInterestGroupStorageMaxStoragePerOwner. Specifying perBuyerSignalsSharing in each interest group increases interest group sizes even further.

interestGroup1 = {
  'owner': 'https://www.dsp1.company.com',
  'perBuyerSignalsSharing': 'https://www.company.com',
  ...
}

interestGroup2 = {
  'owner': 'https://www.dsp2.company.com',
  'perBuyerSignalsSharing': 'https://www.company.com',
  ...
}

In this proposal, DSPs specify sharing mechanisms. It is less trustworthy for DSPs to impose enforcements on themselves. Chrome may consider rejecting invalid perBuyerSignalsSharing when DSPs join or update interest groups. However, validating perBuyerSignalsSharing repeatedly in all interest groups is less ideal.

Global configuration (long-term solution)

We have been exploring the idea of providing global configuration in the Private Aggregation API: patcg-individual-drafts/private-aggregation-api#143. Hopefully, Chrome can expand this idea to the Protected Audience API.

In this proposal, Chrome can enforce the same sharing mechanisms and permissions on all AdTechs.

Note that perBuyerSignalsSharing is subject to optimization, for example, reducing duplicates, depending on which party manages sharing mechanisms and permissions.

SSP support

Our DSPs send perBuyerSignals to 10+ SSPs in the same way. Sharing perBuyerSignals is a breaking change, namely, our DSPs cannot send perBuyerSignals with perBuyerSignalsSharing when SSPs do not support perBuyerSignalsSharing (refer to the discussion on backward compatibility in prior comments). This implies that all 10+ SSPs that our DSPs partner with have to support perBuyerSignalsSharing before our DSPs can enable it. We hope all or most SSPs can adopt a commonly shared open source library of sharing perBuyerSignals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants