Shared Storage and Private Aggregation Implementation Quickstart  |  Privacy Sandbox  |  Google for Developers (2024)

This document is a quickstart guide for using Shared Storage and PrivateAggregation. You'll need an understanding of both APIs because Shared Storagestores the values and Private Aggregation creates the aggregatable reports.

Target Audience: Ad techs and measurement providers.

Try the demo

Try out the live demo. Follow the stepsin the demo instructions to enable the Privacy Sandbox APIs. Opening ChromeDevTools helps you visualize the results of different use cases. Use casesavailable in the demo:

  • Private Aggregation
    • Unique Reach Measurement
    • Demographics measurement
    • K+ frequency measurement
  • General Usage
    • Measure hover-over event inside fenced frames
    • Top-level navigation
    • Controlling where third parties can write

To view what is stored in Shared Storage, use Chrome DevTools. Stored data canbe found in Application -> Shared Storage.

Shared Storage and Private Aggregation Implementation Quickstart | Privacy Sandbox | Google for Developers (1)

View reports for Private Aggregation

To view the aggregatable reports sent, navigate tochrome://private-aggregation-internals. When debug mode is enabled, a reportis sent immediately (without a delay) to[[YOUR_ORIGIN]]/.well-known/private-aggregation/debug/report-shared-storagealong with the time-delayed report to be sent to[[YOUR_ORIGIN]]/.well-known/private-aggregation/report-shared-storage.

To enable debugging, follow the instructions in the debuggingsection.

Shared Storage and Private Aggregation Implementation Quickstart | Privacy Sandbox | Google for Developers (2)

To prevent cross-site tracking, browsers have started partitioning all forms ofstorage, including local storage, cookies, and so forth. But there are use caseswhere unpartitioned storage is required. The Shared Storage API providesunlimited write access across different top-level sites with privacy-preservingread access.

Shared Storage is restricted to the context origin (the caller ofsharedStorage).

Shared Storage has a capacity limit per origin, with each entry limited to amaximum number of characters. If the limit is reached, no further inputs arestored. The data storage limits are outlined in the Shared Storageexplainer.

Ad techs can write to Shared Storage using JavaScript or response headers.Reading from Shared Storage only occurs within an isolated JavaScriptenvironment called a worklet.

  • Using JavaScript Ad techs can perform specific Shared Storage functionssuch as setting, appending, and deleting values outside of a JavaScriptworklet. However, functions such as reading Shared Storage and performingPrivate Aggregation have to be completed through a JavaScript worklet.Methods that can be used outside of a JavaScript worklet can be found inProposed API Surface - Outside theworklet.

    Methods that are used in the worklet during an operation can be found inProposed API Surface - In theworklet.

  • Using response headers

    Similar to JavaScript, only specific functions such as setting, appending,and deleting values in Shared Storage can be done using response headers. Towork with Shared Storage in a response header, Shared-Storage-Writable: ?1has to be included in the request header.

    To initiate a request from the client, run the following code, depending onyour chosen method:

    • Using fetch()
     fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
    • Using an iframe or img tag
     <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
    • Using an IDL attribute with an iframe or img tag
     let iframe = document.getElementById("my-iframe"); iframe.sharedStorageWritable = true; iframe.src = "https://a.example/path/for/updates";

Further information can be found in Shared Storage: ResponseHeaders.

To write to Shared Storage, call sharedStorage.set() from inside or outside aJavaScript worklet. If called from outside the worklet, the data is written tothe origin of the browsing context that the call was made from. If called frominside the worklet, the data is written to the origin of the browsing contextthat loaded the worklet. The keys that are set have an expiration date of 30days from last update.

The ignoreIfPresent field is optional. If present and set to true, the keyis not updated if it already exists. Key expiration is renewed to 30 days fromthe set() call even if the key is not updated.

If Shared Storage is accessed multiple times in the same page load with the samekey, the value for the key is overwritten. It's a good idea to usesharedStorage.append() if the key needs to maintain the previous value.

  • Using JavaScript

    Outside the worklet:

    window.sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });// Shared Storage: {'myKey': 'myValue1'}window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: true });// Shared Storage: {'myKey': 'myValue1'}window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: false });// Shared Storage: {'myKey': 'myValue2'}

    Similarly, inside the worklet:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
  • Using response headers

    You can also write to Shared Storage using response headers. To do so, useShared-Storage-Write in the response header along with the followingcommands:

    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present
    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present=?0

    Multiple items can be comma-separated and can combine set, append,delete, and clear.

    Shared-Storage-Write :set;key="hello";value="world";ignore_if_present, set;key="good";value="bye"

Appending a value

You can append a value to an existing key using the append method. If the keydoes not exist, calling append() creates the key and sets the value. This canbe accomplished using JavaScript or response headers.

  • Using JavaScript

    To update values of existing keys, use sharedStorage.append() from eitherinside or outside the worklet.

    window.sharedStorage.append('myKey', 'myValue1');// Shared Storage: {'myKey': 'myValue1'}window.sharedStorage.append('myKey', 'myValue2');// Shared Storage: {'myKey': 'myValue1myValue2'}window.sharedStorage.append('anotherKey', 'hello');// Shared Storage: {'myKey': 'myValue1myValue2', 'anotherKey': 'hello'}

    To append inside the worklet:

    sharedStorage.append('myKey', 'myValue1');
  • Using response headers

    Similar to setting a value in Shared Storage, you can use theShared-Storage-Write in the response header to pass in the key-value pair.

    Shared-Storage-Write : append;key="myKey";value="myValue2"

You can read from Shared Storage only from within a worklet.

await sharedStorage.get('mykey');

The origin of the browsing context that the worklet module was loaded fromdetermines whose Shared Storage is read.

You can perform deletes from Shared Storage using JavaScript from either insideor outside the worklet or by using response headers with delete(). To deleteall keys at once, use clear() from either.

  • Using JavaScript

    To delete from Shared Storage from outside the worklet:

    window.sharedStorage.delete('myKey');

    To delete from Shared Storage from inside the worklet:

    sharedStorage.delete('myKey');

    To delete all keys at once from outside the worklet:

    window.sharedStorage.clear();

    To delete all keys at once from inside the worklet:

    sharedStorage.clear();
  • Using response headers

    To delete values using response headers, you can also useShared-Storage-Write in the response header to pass the key to be deleted.

    delete;key="myKey"

    To delete all keys using response headers:

    clear;

Context switching

Shared Storage data is written to theorigin(for example, https://example.adtech.com) of the browsing context that the calloriginated from.

When you load the third-party code using a <script> tag, the code is executedin the browsing context of the embedder. Therefore, when the third-party codecalls sharedStorage.set(), the data is written to the embedder's SharedStorage. When you load the third-party code within an iframe, the code receivesa new browsing context, and its origin is the origin of the iframe. Therefore,the sharedStorage.set() call made from the iframe stores the data into theShared Storage of the iframe origin.

First-party context

If a first-party page has embedded third-party JavaScript code that callssharedStorage.set() or sharedStorage.delete(), the key-value pair is storedin the first-party context.

Shared Storage and Private Aggregation Implementation Quickstart | Privacy Sandbox | Google for Developers (3)

Third-party context

The key-value pair can be stored in the ad tech or third-party context bycreating an iframe and calling set() or delete() in the JavaScript code fromwithin the iframe.

Shared Storage and Private Aggregation Implementation Quickstart | Privacy Sandbox | Google for Developers (4)

Private Aggregation API

To measure aggregatable data stored in Shared Storage, you can use the PrivateAggregation API.

To create a report, call contributeToHistogram() inside a worklet with abucket and value. The bucket is represented by an unsigned 128-bit integer whichmust be passed into the function as a BigInt. The value is a positive integer.

To protect privacy, the report's payload, which contains the bucket and value,is encrypted in transit, and it can only be decrypted and aggregated using theAggregation Service.

The browser will also limit the contributions a site can make to an outputquery. Specifically, the contributionbudgetlimits the total of all reports from a single site for a given browser in agiven time window across all buckets. If the current budget is exceeded, areport will not be generated.

privateAggregation.contributeToHistogram({ bucket: BigInt(myBucket), value: parseInt(myBucketValue)});

Using cross-origin iframe

An iframe is needed to invoke the shared storage worklet.

In the ad's iframe, load the worklet module by calling addModule(). To run themethod that is registered in the sharedStorageWorklet.js worklet file, in thesame ad iframe JavaScript, call sharedStorage.run().

await window.sharedStorage.worklet.addModule('modules/sharedStorageWorklet.js');await window.sharedStorage.worklet.run('shared-storage-report', { data: { campaignId: '1234' },});

In the worklet script, you will need to create a class with an async runmethod. And register this class to be run in the ad's iframe. InsidesharedStorageWorklet.js:

class SharedStorageReportOperation { async run(data) { // Other code goes here. bucket = getBucket(...); value = getValue(...); privateAggregation.contributeToHistogram({ bucket: bucket, value: value }); }}register('shared-storage-report', SharedStorageReportOperation);

Using cross-origin request

Shared Storage and Private Aggregation allows creation of cross-origin workletswithout the need for cross-origin iframes.

The first-party page can invoke a createWorklet() call to the cross-originjavascript endpoint.

async function crossOriginCall() { let privateAggregationWorklet = await sharedStorage.createWorklet( 'https://cross-origin.dev/js/worklet.js', ); await privateAggregationWorklet.run('pa-worklet');}crossOriginCall();

The cross-origin javascript endpoint will have to respond with the headersShared-Storage-Cross-Origin-Worklet-Allowed and allow cross-origin usingAccess-Control-Allow-Origin.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1Access-Control-Allow-Origin : https://first-party.dev

Worklets created using the createWorklet() will have selectURL and run().addModule() is not available for this.

class CrossOriginWorklet { async run(data){ // Other code goes here. bucket = getBucket(...); value = getValue(...); privateAggregation.contributeToHistogram({ bucket: bucket, value: value }); }}

Debugging

To enable debugging, call the enableDebugMode() JavaScript method in the samecontext where Shared Storage and Private Aggregation is used. This will beapplied for future reports in the same context.

privateAggregation.enableDebugMode();

To associate the reports with the contexts that triggered them, you can set a64-bit unsigned integer debug key which is passed to the JavaScript call. ThedebugKey is a BigInt.

privateAggregation.enableDebugMode({debugKey: 1234});

Shared Storage returns a generic error message:

Promise is rejected without and explicit error message

You can debug Shared Storage by wrapping the calls withtry-catchblocks.

try { privateAggregation.contributeToHistogram({bucket, value});} catch (e){ console.log(e);}

Debugging Private Aggregation

Reports are sent to /.well-known/private-aggregation/report-shared-storage and/.well-known/private-aggregation/debug/report-shared-storage. Debug reportsreceive a payload similar to the following JSON. This payload defines the apifield as "shared-storage".

{ "aggregation_coordinator_origin": "https://publickeyservice.msmt.gcp.privacysandboxservices.com", "aggregation_service_payloads": [ { "debug_cleartext_payload": "omRkYXRhlKJldmFsdWVEAAAAgGZidWNrZXRQAAAAAAAAAAAAAAAAB1vNFaJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAKJldmFsdWVEAAAAAGZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAGlvcGVyYXRpb25paGlzdG9ncmFt", "key_id": "1569ab37-da44-4a26-80d9-5ed6524ab2a7", "payload": "/9nHrWn1MnJWRxFvanbubciWE9mPyIij6uYLi5k351eQCd3/TZpe2knaatUNcniq4a4e61tmKebv50OmMRZFnnCfcAwIdIgLHu1a3en97PojqWJBfO52RiVMIcP7KQTLzMxq2LhvPSdV4zjXo1Teu/JuIK3LIyis3vUMpS+tUAX0QV+I6X5SVmZFiNW9aMb8DwLOtqrBy5JJ/EkOIY0G+1Fi1/3R7UtKsqM1o71A/OzdmlNkwO7EV/VUNinGvWnd19FvDHe/kqkNdTHKbhAnMmbZzHW9bsEQS81leElCla6BTdbdbeeFU/jbTj0AOaoNOIe5r7FU5NG6nW4ULXTCbLLjTQ1mtl3id3IP41Zin1JvABCDC/HUSgLFz8EUqkmbMIOlMfNYA79aURq6FqE0GO0HtICYf0GPNdVv7p4jY3FNn6+JS4l5F3t+3lP9ceo4IpCE+31jzMtYJ+19xFh6C5ufteBR/iknZFcc1w3caQBhgRl5jt8DbaOzYcW4690H8Ul4Oh2wRO+6/njifk+pExLay/O5swLi2lUUph5OUEaaztwwzh2mnhwIBxMkPnfsGihiF+5KDEajVfMZ3NLsIDoZO+l4RTZrkqE+jVkAqaZGBiCIx42Edp/JV0DXfrryypCdQBZr8iEbSzCM9hKsMfLN7S/VkPe5rDwOZbhKCn5XXgfGz5tSx/KbZgsQf4OCEhwAyNPHAh3MHU7xmkQ3pKg4EIUC/WOtKAlVDOtDMmPPoQY1eAwJhw9SxZaYF1kHjUkTm3EnGhgXgOwCRWqeboNenSFaCyp6DbFNI3+ImONMi2oswrrZO+54Tyhca5mnLIiInI+C3SlP4Sv1jFECIUdf/mifJRM5hMj6OChzHf4sEifjqtD4A30c4OzGexWarie2xakdQej9Go4Lm0GZEDBfcAdWLT9HwmpeI2u4HDAblXDvLN8jYFDOOtzOl90oU7AwdhkumUCFLRadXAccXW9SvLfDswRkXMffMJLFqkRKVE1GPonFFtlzaRqp7IgE8L6AOtz6NDcxAjHnEuzDPPMcWMl1AFH0gq7h" } ], "debug_key": "1234", "shared_info": "{\"api\":\"shared-storage\",\"debug_mode\":\"enabled\",\"report_id\":\"80d93c0a-a94e-4ab7-aeb5-a4ecd4bfc598\",\"reporting_origin\":\"https://privacy-sandbox-demos-dsp.dev\",\"scheduled_report_time\":\"1717784740\",\"version\":\"0.1\"}"}

Debug cleartext payload

The debug_cleartext_payload isBase64CBOR-encoded. You can view the bucket andvalue using the decoder or usethe JavaScript code found in the Shared Storagedecoder.

Next steps

The following pages explain important aspects of the Shared Storage and PrivateAggregation APIs.

Once you are acquainted with the APIs, you can start collecting the reports,which are sent as a POST request to the following endpoints as JSON in therequest body.

  • Debug Reports -context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • Reports -context-origin/.well-known/private-aggregation/report-shared-storage

Once reports are collected, you can test using the local testingtoolor set up the Trusted Execution Environment for AggregationServiceto get the aggregated reports.

You can share your feedback on the APIs and documentation on GitHub.

Shared Storage and Private Aggregation Implementation Quickstart  |  Privacy Sandbox  |  Google for Developers (2024)
Top Articles
Latest Posts
Article information

Author: Gov. Deandrea McKenzie

Last Updated:

Views: 5531

Rating: 4.6 / 5 (66 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Gov. Deandrea McKenzie

Birthday: 2001-01-17

Address: Suite 769 2454 Marsha Coves, Debbieton, MS 95002

Phone: +813077629322

Job: Real-Estate Executive

Hobby: Archery, Metal detecting, Kitesurfing, Genealogy, Kitesurfing, Calligraphy, Roller skating

Introduction: My name is Gov. Deandrea McKenzie, I am a spotless, clean, glamorous, sparkling, adventurous, nice, brainy person who loves writing and wants to share my knowledge and understanding with you.