Shared Storage API for Cross-Site Data Aggregation
The Shared Storage API is a Privacy Sandbox proposal that allows websites to store and access unpartitioned cross-site data without using third-party tracking cookies. This article explains how the API enables developers to write cross-site data in JavaScript, process it within isolated worklets, and aggregate reporting metrics securely through integration with the Private Aggregation API, preventing cross-site user re-identification.
Understanding the Shared Storage Model
Traditional third-party cookies allowed arbitrary cross-site reading and writing, creating tracking vectors. The Shared Storage API solves this by separating the write and read operations:
- Unrestricted Writes: Any context (e.g., an embedded iframe or top-level site) can write key-value pairs into shared storage via standard client-side JavaScript.
- Restricted Reads: JavaScript running on the main page cannot directly read the stored keys or values. Instead, reading only occurs within a sandboxed, isolated JavaScript execution environment known as a Shared Storage Worklet.
Writing Data Across Sites
Writing data to shared storage is straightforward. An embedded script on Site A or Site B can set, append, or delete keys:
// Writing cross-site data to shared storage
await window.sharedStorage.set('campaign_id_123_views', '1', {
ignoreIfPresent: false
});Because these writes do not return values or expose cross-site identifiers to the calling context, user privacy is not compromised during data ingestion.
Processing Data in Worklets
To read and process data, developers define and register a Shared Storage Worklet. The worklet operates in a secure environment with no direct network or DOM access.
// Registering the worklet from the main thread
await window.sharedStorage.worklet.addModule('aggregation-worklet.js');
// Invoking a named operation inside the worklet
await window.sharedStorage.run('aggregate-frequency', {
data: { campaignId: 123 }
});Inside aggregation-worklet.js, the code can read the
shared keys to compute logic:
class AggregateFrequencyOperation {
async run(data) {
const key = `campaign_${data.campaignId}_views`;
const currentViews = parseInt(await sharedStorage.get(key) || '0', 10);
// Update view count
await sharedStorage.set(key, (currentViews + 1).toString());
// Send data to the Private Aggregation API
privateAggregation.contributeToHistogram({
bucket: BigInt(data.campaignId),
value: 1
});
}
}
register('aggregate-frequency', AggregateFrequencyOperation);Privacy-Preserving Output via Private Aggregation
The Shared Storage API does not output raw individual records. Instead, it aggregates cross-site data using the Private Aggregation API.
- Histogram Contributions: Inside the worklet,
privateAggregation.contributeToHistogram()creates encrypted aggregatable reports consisting of bucket IDs (keys) and values (metrics). - Contribution Budgets: The browser enforces strict contribution budgets (an upper limit on how much data can be reported per user over a given time window) to prevent high-entropy data leakage.
- Aggregation Service: The browser sends encrypted reports to an independent Aggregation Service running inside a Trusted Execution Environment (TEE).
- Differential Privacy: The Aggregation Service applies statistical noise to the aggregated values before producing summary reports, ensuring individual user actions cannot be reverse-engineered.
Core Use Cases
By pairing isolated execution with aggregation pipelines, the Shared Storage API supports several key measurement patterns:
- Frequency Capping: Count user impressions across different publisher sites and stop displaying an ad once a frequency limit is reached.
- Unique Reach Measurement: Measure how many unique individuals saw an ad campaign across multiple domains without building cross-site user profiles.
- Demographics and A/B Testing: Evaluate campaign effectiveness or assign consistent multi-site user buckets without passing user identity across origins.