| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| privacy:privacy_sandbox [2026/09/01 16:59] – Apply review findings: fix four folding false positives (FLEDGE/full-fledge, First Party Sets as a verb phrase, lowercase attribution reporting matching Apple SKAdNetwork), which moves the reach total 72->67 and several family counts; regenerate the by-ye karel.kubicek.claude | privacy:privacy_sandbox [2026/09/01 17:36] (current) – Second review round: restore the eighth call to the probe output box and explain why it throws (a probe artefact, not API behaviour); drop the unsupported 'reporting origin' clause from the headers table; name the exact devtools-protocol commit that remov karel.kubicek.claude |
|---|
| * On **2025-10-17** Google announced it would **retire ten Privacy Sandbox technologies**: "Attribution Reporting API (Chrome and Android), IP Protection, On-Device Personalization, Private Aggregation (including Shared Storage), Protected Audience (Chrome and Android), Protected App Signals, Related Website Sets (including requestStorageAccessFor and Related Website Partition), SelectURL, SDK Runtime and Topics (Chrome and Android)".((Anthony Chavez, VP Privacy Sandbox, [[https://privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies|"Update on Plans for Privacy Sandbox Technologies"]], 2025-10-17. Fetched and quoted verbatim 2026-09-01.)) **CHIPS, FedCM and Private State Tokens continue.** | * On **2025-10-17** Google announced it would **retire ten Privacy Sandbox technologies**: "Attribution Reporting API (Chrome and Android), IP Protection, On-Device Personalization, Private Aggregation (including Shared Storage), Protected Audience (Chrome and Android), Protected App Signals, Related Website Sets (including requestStorageAccessFor and Related Website Partition), SelectURL, SDK Runtime and Topics (Chrome and Android)".((Anthony Chavez, VP Privacy Sandbox, [[https://privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies|"Update on Plans for Privacy Sandbox Technologies"]], 2025-10-17. Fetched and quoted verbatim 2026-09-01.)) **CHIPS, FedCM and Private State Tokens continue.** |
| * The same day, the UK CMA **closed** its Privacy Sandbox investigation and released Google from the commitments that had constrained the project since 2022. Case opened 2021-01-07, closed **2025-10-17**.(([[https://www.gov.uk/cma-cases/investigation-into-googles-privacy-sandbox-browser-changes|CMA case page, "Investigation into Google's 'Privacy Sandbox' browser changes"]]: "Case state: Closed", "Opened: 7 January 2021", "Closed: 17 October 2025". Fetched 2026-09-01.)) | * The same day, the UK CMA **closed** its Privacy Sandbox investigation and released Google from the commitments that had constrained the project since 2022. Case opened 2021-01-07, closed **2025-10-17**.(([[https://www.gov.uk/cma-cases/investigation-into-googles-privacy-sandbox-browser-changes|CMA case page, "Investigation into Google's 'Privacy Sandbox' browser changes"]]: "Case state: Closed", "Opened: 7 January 2021", "Closed: 17 October 2025". Fetched 2026-09-01.)) |
| * **They have not been removed yet.** As of **2026-09-01**, every one of those Chrome Platform Status "Deprecate and remove" entries is still marked //Proposed//, and Chromium **151.0.7922.34** exposes and answers every one of the APIs — measured on this page. Chrome stable today is **M152** (2026-08-25). The removal milestone for Topics, Protected Audience, Attribution Reporting, Shared Storage, Related Website Sets and ''requestStorageAccessFor'' is **M153, whose stable date is 2026-09-08**. | * **They have not been removed yet.** As of **2026-09-01**, every one of those Chrome Platform Status "Deprecate and remove" entries is still marked //Proposed//, and Chromium **151.0.7922.34** exposes and answers every one of the APIs — measured on this page. Chrome stable today is **M152** (2026-08-25); this run had no M152 binary, so "not removed" in M152 rests on the //Proposed// flag and on M151, not on a probe. The removal milestone for Topics, Protected Audience, Attribution Reporting, Shared Storage, Related Website Sets and ''requestStorageAccessFor'' is **M153, whose stable date is 2026-09-08**. |
| | * **Removal is a staircase, not a date, and it slipped.** Chrome's own blink-dev intent for Topics said "Deprecate in M144 and then remove in M150"; the API was duly deprecated in M144, the removal did not happen in M150, and the current entry says M153. Treat "days" as the optimistic reading and check the Chrome Platform Status entry rather than this sentence. |
| | * **The behaviour changes before the code does, through field trials, and your crawler is exempt.** From **M150** Chrome removes the ads-API settings surfaces "via field trial", and — Chrome's own words — "after this point, all API invocations for field-trial enabled browsers will always reject". **M152** replaces the Topics implementation with "a lightweight stub API", rolled out through another field trial.((Yao Xia, blink-dev, "Intent to Deprecate and Remove: Topics API", thread opened 2025-11-07; the phased plan is in the message of 2026-06-12. [[https://groups.google.com/a/chromium.org/g/blink-dev/c/_R85yctz4Rs|groups.google.com/a/chromium.org/g/blink-dev/c/_R85yctz4Rs]], fetched 2026-09-01.)) A crawl driving an automation Chromium has **no variations seed**, so it is never in the field trial and will keep seeing the API work after real users have stopped seeing it. That is not a detail: it means the code-removal milestone is the //latest// possible end of the window for deployed behaviour, and your instrument systematically lags the population you are describing. |
| * So there is a window, it is short, and **the browser version is the single most important thing to pin and report** in any Privacy Sandbox measurement. See [[#What to Report]]. | * So there is a window, it is short, and **the browser version is the single most important thing to pin and report** in any Privacy Sandbox measurement. See [[#What to Report]]. |
| </WRAP> | </WRAP> |
| * **Fledging Will Continue Until Privacy Improves** {[calderonio2024_fledging]}, USENIX Security 2024 — the only deployment-scale crawl of Protected Audience in this corpus, and the model for how to instrument one. Read §3 for the method (wrapper functions injected by Puppeteer) before you read the results. | * **Fledging Will Continue Until Privacy Improves** {[calderonio2024_fledging]}, USENIX Security 2024 — the only deployment-scale crawl of Protected Audience in this corpus, and the model for how to instrument one. Read §3 for the method (wrapper functions injected by Puppeteer) before you read the results. |
| * **Exploiting the Shared Storage API** {[nisenoff2025_exploiting]}, CCS 2025 — the most recent Privacy Sandbox measurement in the corpus, and the one that shows what the //attestation// artefacts give you for free. | * **Exploiting the Shared Storage API** {[nisenoff2025_exploiting]}, CCS 2025 — the most recent Privacy Sandbox measurement in the corpus, and the one that shows what the //attestation// artefacts give you for free. |
| * **Interest-disclosing Mechanisms for Advertising are Privacy-Exposing (not Preserving)** {[beugin2024_interest]}, PoPETs 2024 — the systematic privacy analysis of Topics, with the re-identification numbers everyone cites. | * **Interest-disclosing Mechanisms for Advertising are Privacy-Exposing (not Preserving)** {[beugin2024_interest]}, PoPETs 2024 — the systematic privacy analysis of Topics, with the most-cited re-identification numbers in this area. |
| * **A First Look at Related Website Sets** {[mcquistin2024_first]}, IMC 2024 — the template for measuring a Privacy Sandbox //governance// artefact (a GitHub list and its pull requests) rather than a browser API. | * **A First Look at Related Website Sets** {[mcquistin2024_first]}, IMC 2024 — the template for measuring a Privacy Sandbox //governance// artefact (a GitHub list and its pull requests) rather than a browser API. |
| * Then the primary sources, because no paper is current: Google's own [[https://privacysandbox.google.com/overview/status|Privacy Sandbox feature status]] page and the per-feature entries on [[https://chromestatus.com/|Chrome Platform Status]]. | * Then the primary sources, because no paper is current: Google's own [[https://privacysandbox.google.com/overview/status|Privacy Sandbox feature status]] page and the per-feature entries on [[https://chromestatus.com/|Chrome Platform Status]]. |
| | **Private State Tokens** | Trust Tokens | M115, 2023-07-18 | **Continues.** Explicitly kept on 2025-10-17 | ''typeof document.hasPrivateToken'' | | | **Private State Tokens** | Trust Tokens | M115, 2023-07-18 | **Continues.** Explicitly kept on 2025-10-17 | ''typeof document.hasPrivateToken'' | |
| | **FedCM** | WebID | M108 | **Continues.** Explicitly kept on 2025-10-17 | ''%%'IdentityCredential' in window%%'' | | | **FedCM** | WebID | M108 | **Continues.** Explicitly kept on 2025-10-17 | ''%%'IdentityCredential' in window%%'' | |
| | **Bounce tracking mitigations** | — | M116, 2023-08-15 | **Continues.** Not an API — a heuristic that clears state | none | | | **Bounce tracking mitigations** | — | M116, 2023-08-15 (default-on for users who block third-party cookies followed later in 2023 — see [[Privacy:Browser storage#"We cleared cookies" is not a reset]]) | **Continues.** Not an API — a heuristic that clears state | none | |
| | **User-Agent Client Hints / UA reduction** | — | rollout from M89; deprecation trial ended 2023-09-23 | **Continues** | ''%%'userAgentData' in navigator%%'' | | | **User-Agent Client Hints / UA reduction** | — | rollout from M89; deprecation trial ended 2023-09-23 | **Continues** | ''%%'userAgentData' in navigator%%'' | |
| | **IP Protection** | Gnatcatcher | never shipped | **Discontinued** 2025-10-17. Its repository was archived 2025-11-03 | — | | | **IP Protection** | Gnatcatcher | never shipped | **Discontinued** 2025-10-17. Its repository was archived 2025-11-03 | — | |
| **Measured here, 2026-09-01, Chromium 151.0.7922.34 as shipped with Playwright 1.62.1.** Every Privacy Sandbox JS surface is present under the default flags of both Playwright and Puppeteer, and ''document.featurePolicy.allowsFeature()'' returns ''true'' for all six ads-API Permissions-Policy features (''browsing-topics'', ''join-ad-interest-group'', ''run-ad-auction'', ''attribution-reporting'', ''shared-storage'', ''private-aggregation''). Adding ''%%--enable-features=PrivacySandboxAdsAPIsOverride%%'' changed nothing, in either library. (Puppeteer was pointed at the same Chromium binary via ''chromium.executablePath()'', so this is a comparison of the two libraries' **default flag sets**, not of two browser builds.) | **Measured here, 2026-09-01, Chromium 151.0.7922.34 as shipped with Playwright 1.62.1.** Every Privacy Sandbox JS surface is present under the default flags of both Playwright and Puppeteer, and ''document.featurePolicy.allowsFeature()'' returns ''true'' for all six ads-API Permissions-Policy features (''browsing-topics'', ''join-ad-interest-group'', ''run-ad-auction'', ''attribution-reporting'', ''shared-storage'', ''private-aggregation''). Adding ''%%--enable-features=PrivacySandboxAdsAPIsOverride%%'' changed nothing, in either library. (Puppeteer was pointed at the same Chromium binary via ''chromium.executablePath()'', so this is a comparison of the two libraries' **default flag sets**, not of two browser builds.) |
| |
| That is the easy half. The hard half is that **calling them tells you nothing either**. On an HTTPS page served from a domain that is definitely not enrolled, every call returned a benign empty value rather than throwing: | That is the easy half. The hard half is that **calling them tells you nothing either**. On an HTTPS page served from a domain that is definitely not enrolled, seven of eight calls returned a benign empty value rather than throwing, and the eighth threw for a reason that has nothing to do with the Privacy Sandbox: |
| |
| <code> | <code> |
| navigator.runAdAuction() RETURNED null | navigator.runAdAuction() RETURNED null |
| sharedStorage.set() RETURNED undefined | sharedStorage.set() RETURNED undefined |
| | sharedStorage.worklet.addModule() OperationError: Rejecting load of https://…/w.js |
| | due to unexpected MIME type. |
| document.hasPrivateToken() RETURNED false | document.hasPrivateToken() RETURNED false |
| document.requestStorageAccess() RETURNED undefined | document.requestStorageAccess() RETURNED undefined |
| fetch(..., {attributionReporting}) RETURNED 200 | fetch(..., {attributionReporting}) RETURNED 200 |
| </code> | </code> |
| | |
| | The one error is an artefact of the probe, not of the API: that server returns ''text/html'' for every path, and a worklet module must be served as JavaScript. It is left in rather than trimmed out, because the trimmed version was on this page for one revision and a reviewer caught the difference between "no call threw" and "no call threw except the one I removed". |
| |
| The same four outputs came back from ''localhost'', from a mapped non-enrolled public-looking domain, with the ads-API override on, and with ''%%--privacy-sandbox-enrollment-overrides%%'' pointed at the origin. **A crawler cannot distinguish "the API is on and there was nothing to report" from "the API is off for this profile" from "this caller is not enrolled" by looking at the return value.** An empty Topics array is the expected result on a fresh profile with no history, and it is also what a disabled API gives you. | The same four outputs came back from ''localhost'', from a mapped non-enrolled public-looking domain, with the ads-API override on, and with ''%%--privacy-sandbox-enrollment-overrides%%'' pointed at the origin. **A crawler cannot distinguish "the API is on and there was nothing to report" from "the API is off for this profile" from "this caller is not enrolled" by looking at the return value.** An empty Topics array is the expected result on a fresh profile with no history, and it is also what a disabled API gives you. |
| | events ''interestGroupAccessed'', ''interestGroupAuctionEventOccurred'', ''interestGroupAuctionNetworkRequestCreated'', ''sharedStorageAccessed'', ''sharedStorageWorkletOperationExecutionFinished'' | yes | **gone** | | | events ''interestGroupAccessed'', ''interestGroupAuctionEventOccurred'', ''interestGroupAuctionNetworkRequestCreated'', ''sharedStorageAccessed'', ''sharedStorageWorkletOperationExecutionFinished'' | yes | **gone** | |
| | ''runBounceTrackingMitigations'', ''getRelatedWebsiteSets'' | yes | yes | | | ''runBounceTrackingMitigations'', ''getRelatedWebsiteSets'' | yes | yes | |
| | | ''setAttributionReportingTracking'', ''setAttributionReportingLocalTestingMode'', ''sendPendingAttributionReports'' and the events ''attributionReportingSourceRegistered'', ''attributionReportingTriggerRegistered'', ''attributionReportingReportSent'', ''attributionReportingVerboseDebugReportSent'' | **no — removed before this build** | gone | |
| | anything for **Topics** | **no** | no | | | anything for **Topics** | **no** | no | |
| | anything for **Attribution Reporting** | **no** | no | | |
| |
| Tip-of-tree is the ''ChromeDevTools/devtools-protocol'' ''master'' descriptor, fetched 2026-09-01: the Privacy Sandbox commands and **all five events** are already absent there. **The instrumentation is being removed with the API.** If you intend to record Protected Audience or Shared Storage activity, pin a Chromium at or before the removal milestone and say which one; a crawl on evergreen Chrome will silently stop producing events. | Tip-of-tree is the ''ChromeDevTools/devtools-protocol'' ''master'' descriptor, fetched 2026-09-01: the Privacy Sandbox commands and **all five events** are already absent there. **The instrumentation is being removed with the API, and it goes first.** If you intend to record Protected Audience or Shared Storage activity, pin a Chromium at or before the removal milestone and say which one; a crawl on evergreen Chrome will silently stop producing events. |
| | |
| | <WRAP important> |
| | **Attribution Reporting already lost its CDP surface, and this is the most actionable thing on the page.** The ''Storage'' domain carried three ARA commands and four ARA events — ''setAttributionReportingTracking'', ''setAttributionReportingLocalTestingMode'', ''sendPendingAttributionReports'', and ''attributionReportingSourceRegistered'', ''attributionReportingTriggerRegistered'', ''attributionReportingReportSent'', ''attributionReportingVerboseDebugReportSent''. Bisecting the ''devtools-protocol'' repository on 2026-09-01: all seven are present at commit ''1abe750809'' (2026-03-25) and gone at ''96e032552f'' (2026-04-02, "Roll protocol to r1608973"), so they left the protocol on **2026-04-02**. Chromium 151 rejects the toggle with a protocol error, measured. |
| | |
| | So the corpus's biggest gap — nobody has measured a deployed Attribution Reporting API — is **still closable with first-class instrumentation, but only on an older binary**. M147 branched 2026-03-09 and reached stable 2026-04-07, so it should still carry the surface; M148 branched 2026-04-06, inside the removal window, so it is a coin flip. **That milestone mapping is inferred from branch points, not tested** — this run had only Chromium 151 available. Check the binary's own ''/json/protocol'' before you build on it, which takes one HTTP request: |
| | |
| | ''%%chromium --headless --remote-debugging-port=9333 & curl -s localhost:9333/json/protocol | grep -c attributionReportingSourceRegistered%%'' |
| | </WRAP> |
| |
| This is the script this page ran, in full, not an abridged illustration: it stands up a local HTTPS origin with a bidding script, a decision script and a Shared Storage worklet, maps a non-''localhost'' name onto it, drives every API, and prints what CDP delivered. Pointing the same listeners at an ordinary site you do not control will usually print nothing, which is why the mock origin is part of the script rather than an exercise for the reader. | This is the script this page ran, in full, not an abridged illustration: it stands up a local HTTPS origin with a bidding script, a decision script and a Shared Storage worklet, maps a non-''localhost'' name onto it, drives every API, and prints what CDP delivered. Pointing the same listeners at an ordinary site you do not control will usually print nothing, which is why the mock origin is part of the script rather than an exercise for the reader. |
| 'Storage.sharedStorageAccessed', | 'Storage.sharedStorageAccessed', |
| 'Storage.sharedStorageWorkletOperationExecutionFinished', | 'Storage.sharedStorageWorkletOperationExecutionFinished', |
| | // The next two are deliberately left subscribed even though they are DEAD on |
| | // this build. The Attribution Reporting surface (3 commands, 4 events) was in |
| | // the CDP Storage domain until it was removed from the protocol between |
| | // 2026-04-01 and 2026-04-08. Chromium 151 no longer has it, and the |
| | // Storage.setAttributionReportingTracking toggle below is rejected. On a |
| | // Chromium from before that removal these two fire, and are the right way to |
| | // measure Attribution Reporting. |
| 'Storage.attributionReportingSourceRegistered', | 'Storage.attributionReportingSourceRegistered', |
| 'Storage.attributionReportingTriggerRegistered', | 'Storage.attributionReportingTriggerRegistered', |
| |
| A ''sharedStorageAccessed'' event carries ''accessTime'', ''scope'' (''window'' or the worklet), ''method'' (''set'', ''addModule'', ''run'', ''selectURL'', …), ''ownerOrigin'', ''ownerSite'' and the call's ''params'' — that is, everything needed to reproduce {[nisenoff2025_exploiting]}'s prevalence figure without writing a page-script wrapper at all. An auction produces ''interestGroupAuctionEventOccurred'' with a ''uniqueAuctionId'' and the full ''auctionConfig'', followed by ''interestGroupAuctionNetworkRequestCreated'' entries tagged ''bidderJs'' and ''sellerJs'' — the seller attribution that gives {[calderonio2024_fledging]}'s 99.8% figure. | A ''sharedStorageAccessed'' event carries ''accessTime'', ''scope'' (''window'' or the worklet), ''method'' (''set'', ''addModule'', ''run'', ''selectURL'', …), ''ownerOrigin'', ''ownerSite'' and the call's ''params'' — that is, everything needed to reproduce {[nisenoff2025_exploiting]}'s prevalence figure without writing a page-script wrapper at all. An auction produces ''interestGroupAuctionEventOccurred'' with a ''uniqueAuctionId'' and the full ''auctionConfig'', followed by ''interestGroupAuctionNetworkRequestCreated'' entries tagged ''bidderJs'' and ''sellerJs'' — the seller attribution that gives {[calderonio2024_fledging]}'s 99.8% figure. |
| | |
| | ==== The cheapest surface of all: HTTP headers ==== |
| | |
| | Two of these APIs are substantially **declarative**, and a crawler that already records request and response headers — which is to say any crawler built for [[Privacy:Requests]] — can observe them with no wrappers, no CDP and no dependence on the milestone that kills the CDP surface. |
| | |
| | ^ Header ^ Direction ^ What it tells you ^ |
| | | ''Attribution-Reporting-Eligible'' | request | The browser is willing to register a source or trigger on this request. Its presence marks the request as ARA-relevant | |
| | | ''Attribution-Reporting-Register-Source'' | response | A source (an ad view or click) is being registered. The JSON value names the ''destination'' site; the reporting origin is implicit — it is whichever origin sent the header | |
| | | ''Attribution-Reporting-Register-Trigger'' | response | A conversion is being registered | |
| | | ''Sec-Browsing-Topics'' | request | The topics the browser is disclosing to this caller | |
| | | ''Observe-Browsing-Topics'' | response | The caller is asking the browser to record this page visit toward future topic calculation — i.e. it is //building// a profile, not just reading one | |
| | |
| | ''Sec-Browsing-Topics'' has one property worth knowing before you count it: per MDN, if the calling site is not enrolled for Topics, "attempting to create or modify ''Sec-Browsing-Topics'' fails silently, and any existing ''Sec-Browsing-Topics'' header is deleted".((MDN, [[https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Browsing-Topics|''Sec-Browsing-Topics'']], fetched 2026-09-01. All five headers above have MDN reference pages as of that date.)) So a header count is a count of **enrolled** callers by construction — which is the opposite of the JS-surface problem, and makes the header the better instrument for "who is actually using this". |
| |
| ==== The wrapper-injection route, and why the corpus used it ==== | ==== The wrapper-injection route, and why the corpus used it ==== |
| |
| Topics and Attribution Reporting have no CDP surface at all, in any Chromium version checked. For those, the corpus's method is the one to copy: {[calderonio2024_fledging]} "used Puppeteer to orchestrate Chrome", visiting each domain in a fresh browser instance, and "injects wrapper functions into the page and logs all" calls to ''navigator.runAdAuction'', ''navigator.joinAdInterestGroup'' and ''navigator.leaveAdInterestGroup''. Inject the wrapper before any page script runs — ''page.addInitScript()'' in Playwright, ''page.evaluateOnNewDocument()'' in Puppeteer — and wrap ''document.browsingTopics'', the ''attributionReporting'' option on ''fetch'', and the ''attributionSrc'' setter on ''HTMLAnchorElement''/''HTMLImageElement'' the same way. | Topics has no CDP surface in any Chromium version checked, and Attribution Reporting no longer has one. For those, the corpus's method is the one to copy: {[calderonio2024_fledging]} "used Puppeteer to orchestrate Chrome", visiting each domain in a fresh browser instance, and "injects wrapper functions into the page and logs all" calls to ''navigator.runAdAuction'', ''navigator.joinAdInterestGroup'' and ''navigator.leaveAdInterestGroup''. Inject the wrapper before any page script runs — ''page.addInitScript()'' in Playwright, ''page.evaluateOnNewDocument()'' in Puppeteer — and wrap ''document.browsingTopics'' and the ''attributionReporting'' option on ''fetch'' the same way. |
| | |
| | **Do not try to catch ''attributionsrc'' by wrapping the IDL setter.** Wrapping ''HTMLAnchorElement.prototype.attributionSrc'' catches only the elements a script assigns to; markup that arrives as ''%%<img attributionsrc="…">%%'' in the server-rendered HTML is set by the parser, which never invokes the setter, so a crawl instrumented that way undercounts by however much of the ad markup is server-rendered — which for ad tags is most of it. Use a ''MutationObserver'' with ''attributeFilter'', a ''querySelectorAll('[attributionsrc]')'' sweep after load, or the header route above. |
| |
| Two things this costs you, both worth a sentence in your methods section: a wrapper is visible to page script and is therefore detectable ([[Programming:Crawler detection]]), and it sees only the main world unless you also install it into every frame and worklet. | Three things the wrapper route costs you, all worth a sentence in your methods section: a wrapper is visible to page script and is therefore detectable ([[Programming:Crawler detection]]); it sees only the main world unless you also install it into every frame and worklet; and it records //calls//, so an API a site uses purely declaratively is invisible to it. |
| |
| ==== Nothing outside Chromium implements the ads APIs ==== | ==== Nothing outside Chromium implements the ads APIs ==== |
| |
| ^ Artefact ^ Where ^ Size today ^ What it is good for ^ | ^ Artefact ^ Where ^ Size today ^ What it is good for ^ |
| | **Enrollment report** | ''privacysandbox/attestation'' → ''enrollment_report.csv'' | **326 rows** | The company-level population. Columns: ''Enrollment Id'', ''Company Name'', ''Android APIs'', ''Chrome APIs'', ''SDK Names'', ''Site'' | | | **Enrollment report** | ''privacysandbox/attestation'' → ''enrollment_report.csv'' | **326 rows**. Repository **archived**, last push 2026-01-22 | The company-level population. Columns: ''Enrollment Id'', ''Company Name'', ''Android APIs'', ''Chrome APIs'', ''SDK Names'', ''Site'' | |
| | **Preloaded attestation list** | ''PrivacySandboxAttestationsPreloaded/privacy-sandbox-attestations.dat'', inside your own Chromium install | **263 sites**, component version **2025.7.18.0** | The list the browser itself enforces against. No network access needed | | | **Preloaded attestation list** | ''PrivacySandboxAttestationsPreloaded/privacy-sandbox-attestations.dat'', inside your own Chromium install | **263 sites**, component version **2025.7.18.0** | The list the browser itself enforces against. No network access needed | |
| | **Per-site attestation file** | ''%%https://<site>/.well-known/privacy-sandbox-attestations.json%%'' | one per enrolled site | Which APIs each site declared. {[nisenoff2025_exploiting]} fetched 289 of these and got **245 (85%)** | | | **Per-site attestation file** | ''%%https://<site>/.well-known/privacy-sandbox-attestations.json%%'' | one per enrolled site | Which APIs each site declared. {[nisenoff2025_exploiting]} fetched 289 of these and got **245 (85%)** | |
| | **Related Website Sets list** | ''GoogleChrome/related-website-sets'' → ''related_website_sets.JSON'' | **70 sets, 320 member domains**, 66 sets (94.3%) with at least one associated site. Repository **archived** | A complete census, not a sample. Also reachable in-browser via CDP ''Storage.getRelatedWebsiteSets'' | | | **Related Website Sets list** | ''GoogleChrome/related-website-sets'' → ''related_website_sets.JSON'' | **70 sets, 320 member domains**, 66 sets (94.3%) with at least one associated site. Repository **archived**, last push 2025-11-21 | A complete census, not a sample. Also reachable in-browser via CDP ''Storage.getRelatedWebsiteSets'' | |
| | **Topics taxonomy** | ''patcg-individual-drafts/topics'' → ''taxonomy_v2.md'' | **469 topics**. There is no v3 | The label space. Note the Android taxonomy is curated separately — do not conflate them | | | **Topics taxonomy** | ''patcg-individual-drafts/topics'' → ''taxonomy_v2.md'' | **469 topics**. Repository **archived**, last push 2025-11-07 — which is why there is no v3 | The label space. Note the Android taxonomy is curated separately — do not conflate them | |
| | |
| | **All three GitHub artefacts are archived repositories** (checked through the GitHub API on 2026-09-01), so these are final snapshots rather than feeds. That is convenient for reproducibility and misleading if you assume they still track reality. **New enrollment is closed** — Google's own enrollment page carries the banner "Privacy Sandbox private advertising APIs are being phased out. We will no longer accept new account creation and enrollment"((''privacysandbox.google.com/private-advertising/enrollment'', fetched 2026-09-01.)) — so you cannot enrol a research origin to test against. Use ''%%--privacy-sandbox-enrollment-overrides=https://your.origin%%'' instead, and say in your paper that you did. |
| |
| The enrollment report is also a measurement in itself. Of its 326 rows, the Chrome APIs each enrollee asked for: | The enrollment report is also a measurement in itself. Of its 326 rows, the Chrome APIs each enrollee asked for: |
| | name "Privacy Sandbox" five or more times | **9** | 0.2% | | | name "Privacy Sandbox" five or more times | **9** | 0.2% | |
| |
| **This is the page's central limitation and it belongs at the top of the section rather than in a footnote.** A dozen papers in seven venues over sixteen years is not a literature; it is a handful of studies, and every generalisation on this page rests on them. Anything you want to know that the table below does not answer, the corpus cannot tell you — go to the specs and to Chrome Platform Status. | **This is the page's central limitation.** Eleven measuring papers across seven venues is not a literature; it is a handful of studies, and every generalisation on this page rests on them. Anything you want to know that the table below does not answer, the corpus cannot tell you — go to the specs and to Chrome Platform Status. |
| |
| ^ API family ^ Folded-in former names ^ Papers ^ | ^ API family ^ Folded-in former names ^ Papers ^ |
| |
| The field is crawling Chrome and not looking at what Chrome now does. | The field is crawling Chrome and not looking at what Chrome now does. |
| | |
| | The denominator is every web crawl, including the many with no reason to touch an ads API — a phishing crawl or a CSP census is not delinquent for ignoring Topics. So read the number as an upper bound on how far the field has //not// engaged, not as a defect rate. It is the magnitude that carries: fewer than one recent web crawl in twelve so much as names one of these APIs, in the years they were shipping by default in the browser most of those crawls drove. |
| |
| ^ Population ^ N ^ Name at least one API family ^ | ^ Population ^ N ^ Name at least one API family ^ |
| "Web crawl" is ''crawlConfig != null'' or ''studyTypes'' containing ''automated-web-crawl'', **and** ''platforms'' containing ''web''. The last row's denominator is papers that **stated** a browser: ''crawlConfig.browsers'' is stated by only 47.2% of the 1,120 crawling papers in the corpus, so 86 is a lower bound on Chrome-driving crawls, and 9.3% is a share of the ones that told you. | "Web crawl" is ''crawlConfig != null'' or ''studyTypes'' containing ''automated-web-crawl'', **and** ''platforms'' containing ''web''. The last row's denominator is papers that **stated** a browser: ''crawlConfig.browsers'' is stated by only 47.2% of the 1,120 crawling papers in the corpus, so 86 is a lower bound on Chrome-driving crawls, and 9.3% is a share of the ones that told you. |
| |
| If you are looking for a gap to fill, **Attribution Reporting is measured by nobody**. Three papers in 5,869 name it — a related-work list, a browser-permissions census that counts how often sites //feature-detect// it, and one arXiv citation in a reference list. Not one instruments it, crawls for it, or reports its deployment prevalence. Given the M153 target, the window to measure a deployed Attribution Reporting API in Chrome is now measured in days. | If you are looking for a gap to fill, **Attribution Reporting is measured by nobody**. Three papers in 5,869 name it — a related-work list, a browser-permissions census that counts how often sites //feature-detect// it, and one arXiv citation in a reference list. Not one instruments it, crawls for it, or reports its deployment prevalence. Given the M153 target, the window to measure a deployed Attribution Reporting API in Chrome is days — if M153 holds, and it has slipped twice. The instrument to use is not the current build: see the box in [[#Chrome DevTools Protocol: what it covers, and for how long]]. |
| | |
| | ==== The only post-2025 adoption numbers that exist are Chrome's own ==== |
| | |
| | The corpus stops in February 2025 (see [[#Open Questions]]). Chrome publishes usage figures as its justification for removal, inside the Chrome Platform Status "Deprecate and remove" entries, and as of 2026-09-01 they are the only measurement of what happened after the April 2025 reversal. **They are vendor telemetry, unaudited, with no stated methodology, and the unit is usually //page loads// rather than //sites//** — so they are not comparable with anything in the table below without saying which unit you mean. Quoted because there is nothing else, not because they are good: |
| | |
| | ^ API ^ What Chrome's own removal entry says ^ |
| | | Topics | "currently at 13% of page loads" in the entry filed 2025-10-22; the blink-dev follow-up of 2026-06-12 says "Currently the usage is 4.9% of page loads" | |
| | | Shared Storage | "currently at ~11% of page loads" | |
| | | Protected Audience | "Use of the ''joinAdInterestGroup()'' API has decreased by almost 100x and use of the ''runAdAuction()'' API has decreased by more than 10x. Of the auctions occurring today, virtually none of them have winners" | |
| | | Related Website Sets | "currently at 71 sets", and ''requestStorageAccessFor'' "at about 0.95% of pageloads" | |
| | | Attribution Reporting | no usage figure given — the entry argues from "we expect adoption to decrease over time" | |
| | |
| | All fetched from ''chromestatus.com/api/v0/features/'' on 2026-09-01. Two things a reader should notice. **The RWS set count in the entry (71) is not the count in the list today (70)**, because the entry is a snapshot from late 2025 and the list has since been archived — a one-set discrepancy, but a good illustration of why you date an artefact. And **Chrome's Shared Storage figure of ~11% of page loads sits next to {[nisenoff2025_exploiting]}'s 10.1% of //sites//** from February 2025; the closeness is a coincidence of units, not a corroboration. |
| |
| ==== What the corpus did measure ==== | ==== What the corpus did measure ==== |
| | |
| | **The inclusion rule, written down before the table rather than after it:** a paper earns a row if it //measured// a Privacy Sandbox mechanism — crawled for it, attacked it, simulated it against real traces, or analysed its deployed governance artefacts. The last two rows are deliberate exceptions and are marked as such: {[ali2023_navigating]} because it is the corpus's only cross-browser census of the storage surfaces these APIs sit on, and {[lin2024_browsing]} because it is the counterfactual — what actually changes when third-party cookies go — which is the premise the whole Privacy Sandbox rests on. Assignment was by hand, from the per-family paper lists printed in full on [[provenance:privacy:privacy_sandbox|the provenance page]]; disagree with it there. |
| |
| Each row names the paper's own population. Nothing here is a share of the corpus. | Each row names the paper's own population. Nothing here is a share of the corpus. |
| | {[lin2024_browsing]} IMC 2024 | what blocking third-party cookies actually changes | Tranco top 10,000; 7,490 domains crawled successfully | More than **90%** of domains showed **less than a 10%** screenshot difference with third-party cookies disabled — the "cookieless web looks different" premise is largely not true at the page level | | | {[lin2024_browsing]} IMC 2024 | what blocking third-party cookies actually changes | Tranco top 10,000; 7,490 domains crawled successfully | More than **90%** of domains showed **less than a 10%** screenshot difference with third-party cookies disabled — the "cookieless web looks different" premise is largely not true at the page level | |
| |
| One adjacent paper gives the CHIPS context without measuring the attribute: {[bahrami2025_cookieguard]} (IMC 2025) describes CHIPS as "an opt-in model for cookie partitioning" and contrasts it with Firefox's Total Cookie Protection and Safari's ITP, which partition by default — which is why a CHIPS adoption figure is a measure of //publisher and tracker uptake//, not of browser policy. ({[kancherla2025_least]} is cited on this page only for its third-party storage-access figures on [[Privacy:Browser storage]]; it never discusses CHIPS, and an earlier draft wrongly attached this claim to it.) The only dedicated CHIPS adoption study we found is outside these seven venues: Zöllner, Feldmann and Dao, //A First Look at Cookies Having Independent Partitioned State//, PAM 2025.((''doi.org/10.1007/978-3-031-85960-1_8'', pages 182–196. Verified against Crossref 2026-09-01. Note that {[rasaii2025_crumbs]} cites this paper with a different and incorrect DOI; use the one here.)) We have not read it and quote no figure from it. | One adjacent paper gives the CHIPS context without measuring the attribute: {[bahrami2025_cookieguard]} (IMC 2025) describes CHIPS as "an opt-in model for cookie partitioning" and contrasts it with Firefox's Total Cookie Protection, "isolating all storage (including cookies) on a per-site basis", and Safari's ITP. (The paper does not use the word "default" of Firefox and Safari; that gloss is ours, and it is supported by [[Privacy:Browser storage]] rather than by this paper.) — which is why a CHIPS adoption figure is a measure of //publisher and tracker uptake//, not of browser policy. ({[kancherla2025_least]} is cited on this page only for its third-party storage-access figures on [[Privacy:Browser storage]]; it never discusses CHIPS, and an earlier draft wrongly attached this claim to it.) The only dedicated CHIPS adoption study we found is outside these seven venues: Zöllner, Feldmann and Dao, //A First Look at Cookies Having Independent Partitioned State//, PAM 2025.((''doi.org/10.1007/978-3-031-85960-1_8'', pages 182–196. Verified against Crossref 2026-09-01. Note that {[rasaii2025_crumbs]} cites this paper with a different and incorrect DOI; use the one here.)) We have not read it and quote no figure from it. |
| |
| ===== Denominators and Pitfalls ===== | ===== Denominators and Pitfalls ===== |
| * **Your automation library is not a stock browser.** Playwright disables ''ThirdPartyStoragePartitioning'' by default, which is exactly the axis CHIPS is about — see [[Privacy:Browser storage#Partitioning: Your Crawler and a Real Browser Disagree]] and [[Programming:Stateful stateless#Since 2022 the engine decides, not you]]. Measured on this page: Playwright's default list does **not** disable any of the ads APIs. | * **Your automation library is not a stock browser.** Playwright disables ''ThirdPartyStoragePartitioning'' by default, which is exactly the axis CHIPS is about — see [[Privacy:Browser storage#Partitioning: Your Crawler and a Real Browser Disagree]] and [[Programming:Stateful stateless#Since 2022 the engine decides, not you]]. Measured on this page: Playwright's default list does **not** disable any of the ads APIs. |
| * **Cross-browser comparison is not available.** No non-Chromium engine implements the ads APIs, so "Chrome vs Firefox" on a Privacy Sandbox question compares an implementation with an absence. | * **Cross-browser comparison is not available.** No non-Chromium engine implements the ads APIs, so "Chrome vs Firefox" on a Privacy Sandbox question compares an implementation with an absence. |
| | * **A crawler profile is one point in a distribution of user settings.** Real Chrome users passed through the "Ad privacy" choice UI in 2023 and hold heterogeneous per-API toggle states; your fresh profile holds exactly one. Nothing in this corpus measures that distribution, so a site-level prevalence measured from a crawl is a measurement of what //your// profile was offered, not of what users experience. |
| * **What we could not establish**: whether Chrome's ads APIs are gated differently in the EEA or the UK than elsewhere. It would be reasonable to expect a DMA-driven consent gate, but reasonable is not measured: Google's two API-availability pages both return HTTP 404 as of 2026-09-01, we found no primary statement of the current regional behaviour anywhere on Google's documentation, and this run had a single vantage point. If your crawl is comparative across regions ([[Design:Crawling location]]), test this yourself and report what you found — do not assume either way. | * **What we could not establish**: whether Chrome's ads APIs are gated differently in the EEA or the UK than elsewhere. It would be reasonable to expect a DMA-driven consent gate, but reasonable is not measured: Google's two API-availability pages both return HTTP 404 as of 2026-09-01, we found no primary statement of the current regional behaviour anywhere on Google's documentation, and this run had a single vantage point. If your crawl is comparative across regions ([[Design:Crawling location]]), test this yourself and report what you found — do not assume either way. |
| |
| - **The exact Chromium/Chrome version**, not "Chrome". ''151.0.7922.34'', not "the latest Chrome". Everything on this page changes between milestones. | - **The exact Chromium/Chrome version**, not "Chrome". ''151.0.7922.34'', not "the latest Chrome". Everything on this page changes between milestones. |
| - **The automation library and version**, and the full ''%%--enable-features%%'' / ''%%--disable-features%%'' lists you passed, because the library passes its own. | - **The automation library and version**, and the full ''%%--enable-features%%'' / ''%%--disable-features%%'' lists you passed, because the library passes its own. |
| - **Which observation surface** you used: CDP ''Storage'' events, injected JS wrappers, or the enrollment/attestation artefacts. They see different things and none of them sees all of it. | - **Which observation surface** you used: request and response headers, CDP ''Storage'' events, injected JS wrappers, or the enrollment/attestation artefacts. They see different things and none of them sees all of it. |
| - **Whether you enrolled or overrode enrollment**, and for which origins. | - **Whether you enrolled or overrode enrollment**, and for which origins. |
| - **The distinct-caller count** alongside any site-level prevalence. | - **The distinct-caller count** alongside any site-level prevalence. |
| - **Per-snapshot counts**, not only the union, for any repeated crawl. | - **Per-snapshot counts**, not only the union, for any repeated crawl. |
| - **The date the artefact list was taken**, for the enrollment report, the attestation ''.dat'' and the RWS list — all three change under you, and two of the three are now frozen by archival. | - **The date the artefact list was taken**, for the enrollment report, the attestation ''.dat'' and the RWS list. All three GitHub repositories are now archived, so the lists are frozen — but the ''.dat'' inside a Chromium install still moves with the component updater, and an archived list is not the same as the list Chrome was enforcing on the day you crawled. |
| - **That the API still existed in your build**, stated as a fact you checked rather than assumed. After M153 this stops being pedantry. | - **That the API still existed in your build**, stated as a fact you checked rather than assumed. After M153 this stops being pedantry. |
| |
| <WRAP todo> | <WRAP todo> |
| * **Nobody has measured a deployed Attribution Reporting API.** Three passing mentions in 5,869 papers, no instrumentation, no prevalence. The removal target is M153. | * **Nobody has measured a deployed Attribution Reporting API.** Three passing mentions in 5,869 papers, no instrumentation, no prevalence. The removal target is M153. |
| * **Nobody has re-measured Protected Audience or Topics adoption after 2025-04-22.** {[calderonio2024_fledging]}'s crawl is from 2023 and {[nisenoff2025_exploiting]}'s from February 2025 — both before the retirement announcement. Chrome's own removal justification claims usage collapsed; no independent measurement of that collapse exists in this corpus. | * **Nobody independent has re-measured Protected Audience, Topics or Shared Storage adoption after February 2025.** {[calderonio2024_fledging]}'s crawl is from 2023 and {[nisenoff2025_exploiting]}'s from February 2025 — both before the retirement announcement. The only figures for the collapse are Chrome's own, quoted above; nobody has checked them, and the browser that produced them is the one being measured. |
| * **The RWS list has grown from 41 sets to 70** between {[mcquistin2024_first]}'s snapshot and today, and its repository is now archived. Nobody has published the end-state census or repeated the pull-request analysis on it. | * **The RWS list has grown from 41 sets to 70** between {[mcquistin2024_first]}'s snapshot and today, and its repository is now archived. Nobody has published the end-state census or repeated the pull-request analysis on it. |
| * **CHIPS adoption is measured once inside this corpus and once outside it**, both at around 1% of tracking cookies. CHIPS is one of the three APIs that survives; a proper longitudinal series on it is missing and now has a stable target to measure. | * **CHIPS adoption is measured once inside this corpus and once outside it**, both at around 1% of tracking cookies. CHIPS is one of the three APIs that survives; a proper longitudinal series on it is missing and now has a stable target to measure. |