-
Notifications
You must be signed in to change notification settings - Fork 1
Technical Specification: Interest Groups
Interest Groups §
An interest group is a way to group users/browser sessions with shared properties for which ads can target allowing the owner of this group to act as the buyer in an auction.
In order to recall an interest group data in the auction, a storage mechanism is required. The storage of this needs to be keyed by some combination of the owner
and name
fields in order to create a unique ID to get or set values.
After some discussion with stakeholders (@brodrigu), we've determined to use owner:name
as the key for storing each interest group over something like owner
(with nested name
keys) since removing a group will remove all of its entry and leave no remnants (such as an empty owner) and presumptively match the browser’s implementation more closely.
After more discussion, we've determined to go back to using the top-level key of owner
since the only way to retrieve an interest group is through the interest_group_buyers
key in the auction configuration object and we won't know the interest group name. This also allows for the auction to run the *
(wild card) mechanism in order to allow all interest groups to bid.
The internal data model will be stored within the browser using an undetermined IndexedDB
, and it will require some form of Cross-Domain Sharing to exploit the same-origin policy restrictions that the browser enforces.
The following is the storage model and will be later referred in the document as InterestGroup
when referring to its data structure:
"<options.owner>":<options.name>": {
"_created": "<Date.now()>",
"_updated": "<unix_timestamp_in_ms_from_daily_update_url_call>",
"_expires": "<this._created + (expiry * 86400000)>",
"owner": "<options.owner>",
"name": "<options.name>",
"bidding_logic_url": "<options.bidding_logic_url>",
"daily_update_url": "<options.daily_update_url>",
"trusted_bidding_signals_url": "<options.tbs_url>",
"trusted_bidding_signals_keys": "<options.tbs_keys>",
"user_bidding_signals": "<options.user_bidding_signals>",
"ads": "<options.ads>"
}
The following is the data types when referring to the internal data model and anytime a function accepts an <InterestGroup>
as a parameter.
Any type that is suffixed with a ?
is meant to signify that its an optional parameter when creating an interest group.
-
owner: String<URL> (e.g.
"www.dsp.com"
) -
name: String (e.g.
"womens-running-shoes"
) -
bidding_logic_url: String<URL> (e.g.
"dsp.com/nike/bid.js"
) -
daily_update_url?: String<URL> (e.g.
"dsp.com/nike/update"
) -
trusted_bidding_signals_url?: String<URL> (e.g.
"kv-server.com"
) -
trusted_bidding_signals_keys?: Array<String> (e.g.
[ "budget", "size" ]
) -
user_bidding_signals?: Object (e.g.
{ timestamp: 123456789, hostname: "nike.com", path: "/w/womens-running-shoes" }
) -
ads?: Array<Object> (e.g.
[ { rendering_url: "s3.aws.com/ad1.html", meta: { … } }, ... ]
)
This is, as described by Wikipedia:
a number of seconds that have elapsed since the Unix epoch, minus leap seconds
So any mention of <UnixTime>
in this document is in reference to that format, with the exception that we're converting it to milliseconds as is suggested by the Fledge Proposal in their example code snippet.
- The specific type of data for the Ads field is left unstructured at this time until we can involve DSPs.
- There is no specific mention of any field besides owner and name being required, but we’re making the assumption that the data structures listed above would be meaningless without some of the fields, such as bidding logic URL, being required.
When a "user" lands on a "buyer's" page, this API method will allow them to create and/or create an interest group with some properties that define the group and subsequently allow the "user" to join the interest group. The method accepts two parameters, an options Object
that is of the type <InterestGroup>
, and an expiry Number
.
❗ As of today (2021-03-25), there is an issue filed with Chrome to determine the data structure of the expiry
and whether its required or optional and if there is a default value. This will impact the validation below and the internal functions.
- If no
<InterestGroup>
is passed in, throw anError
stating a generic message such as "missing fields" - If required fields are missing from
<InterestGroup>
, throw anError
stating a generic message such as "missing fields" - If, at some time we do handle permissions, then in the event there is missing permissions, throw an
Error
describing the reason. - If no
expiry
is passed in, throw anError
stating a generic message such as "missing fields"4. If anexpiry
is passed in and is not a validDate
, throw anError
stating a generic message such as "invalid Date"5. If noexpiry
is passed in, then default to 30 days
- If the same owner and name is passed in, then "merge" with the existing data, update the internal updated field to the current time and expires field will be 30 days past the current time
- If owner or name is passed in and doesn't match an existing group, then create an entirely new record with the options passed in, adding in the internal created field and updated field to the current date
- If, at some time we do handle permissions, then in the event there is missing permissions, throw an
Error
describing the reason.4. Ifexpiry
is passed in and it is beyond 30 days from the created day, then default to the maximum of 30 days - If
expiry
is passed in and it is beyond 30 days (2592000000), throw anError
, stating a generic message such as "number beyond maximum allowed"
- If successful, return
Boolean(true)
- If failure, throw
Error(<reason>)
Using the flow diagram as a guide, the following internal functions will be created in order to support joining an interest group:
The complementary API to leave an interest group. This can be called from within an ad to allow a user to report an ad or request to no longer see the ad or interest group that they've been placed in. One can imagine an scenario where Chrome will create a UI for the user to access the Interest Groups they're apart of and allow them to leave them.
- If both the
name
andowner
passed in exist, delete the record - If one of the
name
andowner
passed in is missing, throw anError
stating a generic method such as "missing fields" - If the owner and/or name passed in is not found, throw an
Error
stating a generic message such as "record not found"
- If an empty
Object
is passed within an ad already rendered on the page, delete the record. This is designed for an ad’s creative to allow for a user to hide that particular ad and never show it again.
One open question is how will the browser pass along the interest group information. It’s possible that this could be passed in via the browser_signals in the score_ad function within the auction.
- If successful, return
Boolean(true)
- If failure, throw
Error(reason)
Using the flow diagram as a guide, the following internal functions will be created in order to support joining an interest group:
This is a URL string that is provided in the <InterestGroup>
options Object
when creating or updating an interest group’s attributes. This URL should expose two functions that the buyer will need to provide that handle the bidding at auction time (generate_bid()
) as well as report the win (report_win()
) to the appropriate APIs for accounting purposes. As of right now, there is no information on how these functions need to be exposed, but one can suspect that it will be either an ES Module or windowed object/class that exposes the two functions. An open discussion has been created for tracking purposes.
An example: "dsp.com/nike/bid.js"
Note: generate_bid()
and report_win()
and are detailed in other specifications.
Detailed are the internal functions and their parameters that will be called at various stages of the two aforementioned API methods.
This function is designed to retrieve a specific record from the internal storage, including all private/internals from the interest group. Both parameters are required.
- Private/Public: Private
-
Return: If found, return full record. If no record found, return
null
This function is designed to create a new record in the internal storage, including all private/internals for the interest group. Only the options
parameter is required; if no expiry
is passed in, then the number will be set to the default value. This will also validate the existence of any required field, following the validation rules.
- Private/Public: Private
-
Return: If successful, return
true
. If failure, throw anError
with a message if it fails.
This function is designed to update an existing record in the internal storage, including all private/internals for the interest group. Only the options
parameter is required; if no expiry
is passed in, then the number will be set to the default value. This will also validate the existence of any required field, following the validation rules.
- Private/Public: Private
-
Return: If successful, return
true
. If failure, throw anError
with a message if it fails.
This function is designed to delete an existing record in the internal storage for the interest group based on it's owner and name. Both parameters are required. It should remove the name Object
and not the entire owner Object
, even if there are no more interest groups. It should also remove an interest group if the expires
key in an interest group has lapsed.
- Private/Public: Private
-
Return: If successful, return
true
. If failure, throw anError
with a message if it fails.