← Browser Extensions
Browser Extensions 2026-10-12

Gpc Witness

Your browser sends the opt-out. This checks whether the site you're on acted on it, and keeps a dated record when it didn't.

Gpc Witness
Prototype

GPC Witness

Your browser sends the opt-out. This checks whether the site you're on acted on it, and keeps a dated record when it didn't.

Date: 2026-10-12 Form factor: Browser extension (Manifest V3) Browser surface: declarativeNetRequest (adds the Sec-GPC header), content scripts on https pages (main and isolated worlds), toolbar popup with badge, service worker, chrome.browsingData Status: Prototype

What it is

Global Privacy Control (GPC) is the browser signal that means "don't sell or share my data." Twelve US states now treat it as a legal opt-out. GPC Witness sends that signal on every https page, then looks at what the page did after it got the signal: did the site's own consent record say you're opted out, did ad pixels drop their cookies anyway, and does the site publish a gpc.json file promising to honor GPC? Each site gets a verdict of honored, unclear, or ignored, and goes into a dated log you can export.

Why it has to be an extension

The evidence only exists inside the page you're on. The usprivacy string, the OneTrust consent cookie, and the _fbp cookie a Meta Pixel sets are all in that site's own cookie jar. Sending the signal also needs the browser: a rule that adds Sec-GPC: 1 to every request, plus a script that sets navigator.globalPrivacyControl before the site's code runs. A website you visit can't do either of those things to another website. The extension also runs in your real, logged-in session. That matters because California's largest CCPA settlement so far, Disney's in February 2026, was about opt-outs that stopped working once you were logged in, and an outside crawler can't log in as you.

Who it serves

Two groups, and they want different things.

The first is the privacy-minded user in California or one of the other GPC states. They turned GPC on and have no idea whether it does anything. Right now the only way to find out is to read cookie names in DevTools.

The second is whoever is answerable when GPC doesn't work. That means the privacy or compliance lead at a mid-size company that publishes a gpc.json, an outside counsel's team, or a consultant running audits. They need to check that their own site and their vendors' tags behave after an opt-out, on real pages, including logged-in ones. California's regulator now scans sites for this on its own, so they'd rather find out first.

Why it could be profitable

The model is free for individuals and a paid team tier for the second group. The free version is everything in this prototype: the signal, the per-site check, the log, and the copyable summary. The team tier would add a shared log across a team, scheduled re-checks of a list of URLs from members' own browsers, and exports signed and timestamped by a small backend so they hold up as audit records. My guess at pricing is something like $15–30 per seat per month, but I haven't tested it.

The case for demand rests on enforcement, which has been steady. California's Attorney General announced the $2.75 million Disney settlement on February 11, 2026, and GPC handling was part of it. Earlier settlements with Healthline ($1.55 million), Tractor Supply ($1.35 million), and Honda all cited GPC. In April 2026 a webXray audit of more than 7,000 popular sites, as reported by The Markup, found Google continued to track users in 86% of cases after the signal. California's AB 566 requires every browser to offer an opt-out preference signal by January 1, 2027, so the number of people sending GPC is about to jump from the Brave and Firefox crowd to most Chrome users.

Where the case is thin:

  • Consumers won't pay much. OptMeowt from Wesleyan's privacy-tech lab already sends GPC for free and has a research "analysis mode." Privacy Badger sends it too. The free tier is how the product gets distribution, not revenue.
  • Compliance teams already have options. Consent-platform vendors and audit firms like webXray sell scanning. The extension's real advantage is checking from a real user's logged-in session, and I don't know yet whether buyers value that enough to pay a separate vendor.
  • The verdicts are a heuristic. A request to an ad network isn't proof of a sale, and the prototype doesn't decode GPP strings. A legal buyer will want the method written down and defensible before they rely on it.

How to load it in Chrome

  1. Open chrome://extensions and turn on Developer mode.
  2. Click Load unpacked and select this folder's extension/ directory.
  3. Visit any https site and wait a few seconds after it loads. The toolbar icon shows ! if the site ignored the signal and ? if the result is unclear. Click it for the details. For a fair test, press Clean retest: it clears that site's cookies and reloads, so nothing left over from an earlier visit counts against it.

How to try the demo

  1. Open index.html in any modern browser. The sample data is also inlined in the page, so it works from file:// without a server.
  2. Click the eye icon at the right of the mock toolbar, then switch between the six tabs.
  3. On the Copper Pot Kitchen tab, press Clean retest. Its Google Ads cookie was set on an earlier visit, so the verdict drops from ignored to unclear once old cookies are cleared.
  4. Open the Log tab in the popup to see two weeks of earlier checks.

The six sites are invented. Their cookies, network requests, and gpc.json files come from sample-data.json, and script.js stands in for the content script and service worker. The popup (popup.js, popup.css) and the scoring code (analyze.js) are the extension's own files.

Permissions, and why each one

Permission Why it's needed
declarativeNetRequest A static rule in rules.json adds Sec-GPC: 1 to every https request. This is the opt-out signal itself.
host_permissions: https://*/* GPC is meant to go to every site you visit, so the header rule and both content scripts have to run everywhere. The service worker also uses it to fetch each site's /.well-known/gpc.json without CORS getting in the way. Http pages are skipped.
storage Keeps the per-tab report (storage.session), the log, and the notice setting (storage.local). Nothing leaves the browser.
browsingData Clean retest clears one site's cookies and local storage before reloading, so any ad cookie that shows up afterwards was set after the opt-out.

What's in this prototype

  • rules.json: a static declarativeNetRequest rule that sets Sec-GPC: 1 on https requests.
  • gpc-main.js: a main-world content script that runs at document_start, sets navigator.globalPrivacyControl = true, and later asks the site's __uspapi and __gpp consent APIs what they recorded.
  • content.js: reads first-party cookies and the hosts the page loaded resources from, sends them to the service worker, and shows a dismissible notice (inside a shadow root) when a site ignored the signal.
  • analyze.js: the scoring, shared by the service worker and the demo. It reads the US Privacy string, the OneTrust OptanonConsent cookie, 10 ad-pixel cookie names, and 20 ad-network hosts.
  • background.js: fetches gpc.json, scores the page, sets the toolbar badge, keeps a log capped at 200 entries (one per site per day), and runs the clean retest.
  • Popup: the verdict, five evidence rows, Clean retest, Copy summary (plain text with a link to the CPPA complaint form), a log view with counts, and JSON export.
  • Stubbed or missing: GPP strings are detected but not decoded, there's no team sync, no signing of exports, and no checking of http pages.

Roadmap

  • Decode GPP strings (the US National and state sections) so sites that dropped the old US Privacy string still get a real "own record" check.
  • Per-state framing in the summary: which law applies, and which regulator to send it to.
  • A logged-in check that compares the account's privacy settings page before and after visiting with GPC on. This is the Disney failure, and the part an outside crawler can't do.
  • Team tier: a shared log, scheduled re-checks of a URL list, and signed exports through a small timestamping backend.
  • A Firefox build. Firefox sends GPC natively, so the port would drop the header rule and keep the checks.

Sources

Requirements

GPC Witness — Requirements

Goals

  • A user with GPC turned on can tell, in one click, whether the site they're on acted on it.
  • Every verdict shows its evidence. The user can see why a site was called honored, unclear, or ignored, and nothing gets called "ignored" on the strength of a network request alone.
  • A site that publishes gpc.json promising to honor GPC, then ignores it, is called out separately. That contradiction is the most useful finding the product makes.
  • Results build up into a dated, exportable log that someone could attach to a complaint or an audit.
  • Nothing the extension observes leaves the browser.

Primary user

A privacy-conscious adult in a state that legally recognizes GPC, most likely California. They turned on GPC because they read it was the legal "do not sell" switch. They browse news, shopping, streaming, and health sites in their ordinary logged-in session. What they need is to know whether the switch works, which sites ignore it, and to have something concrete to show if they decide to complain.

A secondary user is the compliance lead who has to make their own company's gpc.json claim true, and who wants to check it from a real browser session rather than from a crawler.

Functional requirements

  • FR1: The extension adds Sec-GPC: 1 to every https request through a static declarativeNetRequest rule.
  • FR2: Before any page script runs, the extension defines navigator.globalPrivacyControl as true on https pages, unless the browser already reports true.
  • FR3: About 2.5 seconds after page load, the extension queries __uspapi("getUSPData") and __gpp("ping") if the page defines them, and records the results.
  • FR4: The content script sends the page URL, the first-party cookies visible to script, the cross-origin hosts from the Resource Timing entries, and the FR3 results to the service worker. It sends once per page load, falling back to sending without FR3 data after 8 seconds.
  • FR5: The service worker fetches /.well-known/gpc.json once per origin per browser session, without credentials, and records true, false, or missing plus lastUpdate.
  • FR6: Scoring produces five evidence rows: signal sent, site declaration, the site's own consent record (US Privacy string or OneTrust OptanonConsent), ad-pixel cookies present, and requests to ad-network hosts.
  • FR7: The verdict is ignored if any row fails, unclear if none fail and any row warns, and honored otherwise. Requests to ad networks can only produce a warning.
  • FR8: A report is flagged as a contradiction when gpc.json says true and the verdict is ignored.
  • FR9: The toolbar badge shows ! for ignored, ? for unclear, and nothing for honored, per tab.
  • FR10: When the notice setting is on (the default) and a page is ignored, the content script shows a dismissible notice in a closed shadow root.
  • FR11: Clean retest clears the current site's cookies and local storage, reloads the tab, and marks the next report for that tab as a clean test.
  • FR12: Copy summary puts a plain-text summary on the clipboard: URL, timestamp, clean-test status, the evidence rows, the contradiction if there is one, and the CPPA complaint form URL.
  • FR13: The log stores one entry per host per calendar day, newest first, capped at 200 entries. A later check on the same day replaces the earlier one.
  • FR14: The popup's Log view shows counts by verdict and the entry list, and can export the full log as JSON.
  • FR15: The popup reads chrome.* only through a feature-detected adapter, so the same popup.js runs in the demo harness.

User stories

  • As a California resident with GPC on, I want to see whether the news site I'm reading treated me as opted out, so that I know whether the setting does anything.
  • As a user, I want sites that claim to honor GPC but don't to stand out, so that I know which promises to distrust.
  • As a user, I want to clear a site's old cookies and recheck, so that a cookie from before I turned GPC on doesn't count against the site.
  • As a user, I want to copy a plain summary of what was observed, so that I can paste it into a regulator's complaint form without rewriting it.
  • As a user, I want an exportable log of the sites I've checked, so that I can show a pattern and not a single visit.
  • As a user, I want to turn off the on-page notice, so that the extension stays quiet unless I open it.
  • As a compliance lead, I want to check our own logged-in pages from a real session, so that I find opt-out failures before a regulator's scanner does.

Extension surfaces

  • declarativeNetRequest — adds Sec-GPC: 1 to https requests (rules.json).
  • Content script, main world (gpc-main.js) — sets navigator.globalPrivacyControl and reads the page's consent APIs.
  • Content script, isolated world (content.js) — collects cookies and resource hosts, and injects the notice.
  • Service worker (background.js) — fetches gpc.json, scores the page, sets the badge, keeps the log, runs the clean retest.
  • Toolbar popup (popup.html) — verdict, evidence, actions, log, export.
  • chrome.browsingData — clears one origin's cookies and local storage for a clean retest.

Non-functional requirements

  • Privacy: all evidence and logs stay in chrome.storage. The only network request the extension makes on its own is the gpc.json fetch to the site already being visited, sent without credentials.
  • Permission minimalism: no tabs, cookies, scripting, or webRequest. Broad https host access is used only because the signal has to reach every site. The README states this.
  • Performance: no work on the page until document_idle, and one message per page load. The main-world script only defines one getter and adds one load listener.
  • Isolation: the notice lives in a closed shadow root, so page CSS can't reach it and its CSS can't leak out. The content script guards against double injection.
  • Accuracy: an ad-network request alone never yields ignored. Summaries say what was observed, not what the law requires.
  • Accessibility: the popup's controls are buttons and labels, verdicts aren't shown by color alone, and the notice has role="status".
  • MV3 compliance: no remote code, no eval, and a non-persistent service worker that keeps all state in storage.

Out of scope (for the prototype)

  • Decoding GPP strings (they're detected only).
  • Checking account-level privacy settings while logged in.
  • Http pages.
  • Team sync, scheduled re-checks, and signed or timestamped exports, which need a backend.
  • Telling the user which law applies to which site.

Open questions

  • How often do ad cookies set after GPC come from a consent platform's slow start, not from a policy of ignoring the signal? A short delay before reading cookies might cut false positives.
  • Should a site with no consent record and no ad cookies but heavy ad-network traffic be unclear, or does that undersell it?
  • Will the compliance buyer accept evidence gathered from a browser extension, or do they need a controlled environment (a fixed IP and a fresh profile) to trust it?
  • Once Chrome ships a built-in opt-out signal for AB 566, does the extension stop sending its own header and only check?
  • Is a per-site verdict the right unit, or do logged-in flows (checkout, account pages) need checking path by path?

More from Browser Extensions