Skip to main content

Browser Extension Deployment

This guide is for admins rolling out the Control Zero browser extension to a fleet of developer or employee machines. It covers the three deployment modes, the MDM integration for each platform, the compliance checklist EU orgs need before flipping the switch, and the heartbeat-based uninstall alert you pair with the force-install policy.

If you are a developer just trying to install the extension to see it work, skip to the sign-up flow and install from the Chrome Web Store instead.


What the extension does, briefly

The Control Zero browser extension scans text typed or pasted into AI chat composers on supported sites (Claude.ai, ChatGPT, Gemini, Perplexity) against your org's data-loss-prevention rules. Matches are logged to your org's audit feed. When a rule is configured to mask or block, the extension rewrites the text inline or cancels the paste.

What the extension captures:

  • The matched text span (e.g., a specific SSN that tripped a rule)
  • 40 characters of context before and after the match
  • The host the user was on (e.g., claude.ai)
  • A device ID unique to this browser profile
  • Hostname, user agent, extension version
  • Optionally, the user's work email (opt-in)

What the extension does NOT capture:

  • The full contents of the composer when no rule fires
  • Keystrokes when the extension is paused (10-minute user override)
  • Any content on non-AI-chat surfaces (search bars, password fields, etc.)
  • Any content on sites outside the explicit allowlist

Network-level guard (v0.2.1)

Starting in extension version 0.2.1, the extension installs a second line of defense alongside the DOM-level overlay: a network-level guard that observes every outgoing AI request and can block it before it leaves the browser.

The guard requires three additional Chrome permissions in the manifest:

  • webRequest -- used in observer-only mode. Manifest V3 forbids blocking webRequest listeners, so the listener only reads the request body, scans it against the org's DLP rules, and emits an audit entry on a hit.
  • declarativeNetRequest -- the only MV3-supported way to actually block a request. The extension synchronizes a dynamic ruleset against any DLP rule scoped to browser_ext_network with action=block. The browser evaluates these rules in the network stack, which means a block still works even if the page disabled JavaScript or rewrote the textarea around the DOM overlay.
  • declarativeNetRequestFeedback -- lets the extension log which dynamic rule fired, so the audit entry can name the policy that blocked the call.

Why both: the DOM-level overlay is the user-facing UX (Grammarly-style underlines and tooltips). The network guard is the safety net for when the overlay is bypassed. Together they cover composer typing and XHR-level egress.

The new DLP rule scope browser_ext_network is distinct from the existing browser_ext (DOM-only). Admins can scope a rule to either, both, or neither -- the editor in the dashboard accepts both scopes.

What declarativeNetRequest cannot do

  • It does not inspect request bodies. The dynamic ruleset matches on URL only, so the network-level block is a hammer ("block all browser AI calls that match a DLP block rule"), not a scalpel. Body-aware decisions still come from the DOM overlay.
  • The 30,000 dynamic rule cap (and 5,000 regex rule cap) is shared across all extensions in the profile. The extension only writes one rule per supported AI surface, so this is not a concern in practice.
  • Rules are evaluated at the network stack, not in the page, so a successful block surfaces to the page as a network error, not a friendly message. The audit entry is the source of truth for what happened.

Cross-browser caveats

  • Chrome / Edge / Brave / other Chromium: full support, this is the target.
  • Firefox: Firefox still supports blocking webRequest listeners under MV2 semantics, and its declarativeNetRequest implementation is partial. A Firefox port of the extension would use blocking webRequest directly and skip the DNR sync entirely. The manifest.json shipped here is Chromium-MV3-only.
  • Safari: Safari Web Extensions support declarativeNetRequest but not declarativeNetRequestFeedback. A Safari port would drop that permission and lose the per-rule attribution in audit entries.

Enterprise bundle download (Teams tier)

Teams-tier admins can download a pre-configured extension bundle from the dashboard or via API. The bundle is a ZIP containing the full extension source with an embedded config.json that pre-populates the org ID, API key, and backend URL. This eliminates the need for users to manually configure the extension.

Dashboard

  1. Navigate to Integrations > Browser Extension in the dashboard.
  2. Select the target platform (Chrome or Firefox).
  3. Click Download Bundle. A ZIP file downloads with the extension source and embedded configuration.
  4. Distribute the ZIP via your MDM tool (see platform-specific instructions below).

API

# Download the Chrome bundle
curl -H "Authorization: Bearer <FIREBASE_TOKEN>" \
-o controlzero-extension-chrome.zip \
"https://api.controlzero.ai/api/orgs/<ORG_ID>/browser-extension/bundle?platform=chrome"

# Download the Firefox bundle
curl -H "Authorization: Bearer <FIREBASE_TOKEN>" \
-o controlzero-extension-firefox.zip \
"https://api.controlzero.ai/api/orgs/<ORG_ID>/browser-extension/bundle?platform=firefox"

The ZIP contains a config.json with the structure:

{
"api_url": "https://api.controlzero.ai",
"org_id": "<ORG_ID>",
"api_key": "cz_extension_ext_<generated>"
}

Each bundle download generates a fresh extension-scoped API key (scoped to browser_ext:report and browser_ext:config). Revoke unused bundle keys from the dashboard or API.


Bulk key generation (Teams tier)

For large fleet deployments where each device needs a unique key, use the bulk key generation API.

Generate keys

curl -X POST \
-H "Authorization: Bearer <FIREBASE_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"count": 50, "prefix": "fleet", "expiry_days": 365}' \
"https://api.controlzero.ai/api/orgs/<ORG_ID>/browser-extension/keys"

Response:

{
"keys": [
{
"id": "uuid",
"key_prefix": "cz_extension_fleet_abcd1234",
"secret": "cz_extension_fleet_<full_secret>",
"scopes": ["browser_ext:report", "browser_ext:config"],
"expires_at": "2027-04-11T00:00:00Z",
"created_at": "2026-04-11T00:00:00Z"
}
],
"count": 50
}

Parameters:

  • count (required): number of keys to generate, 1-100
  • prefix (optional): key name prefix, default "ext"
  • expiry_days (optional): days until expiry, default 365

List keys

curl -H "Authorization: Bearer <FIREBASE_TOKEN>" \
"https://api.controlzero.ai/api/orgs/<ORG_ID>/browser-extension/keys"

Returns all extension keys for the org (prefix only, never the full secret).

Revoke a key

curl -X DELETE \
-H "Authorization: Bearer <FIREBASE_TOKEN>" \
"https://api.controlzero.ai/api/orgs/<ORG_ID>/browser-extension/keys/<KEY_ID>"

Key rotation procedure

  1. Generate a new batch of keys via the bulk endpoint.
  2. Push the new keys to devices via MDM managed-storage update.
  3. Monitor the audit feed for 24 hours to confirm devices are reporting with the new keys.
  4. Revoke the old keys.

Deployment modes

The extension supports three deployment modes, in order of operational strength:

1. Optional self-install

Users visit the Chrome Web Store and install the extension themselves, then paste their org ID and a cz_live_* API key into the options page. Best for teams experimenting with Control Zero before a company-wide rollout.

IT admin pushes the extension via Chrome Enterprise policy. Users cannot disable, uninstall, or load a different version. Config (org ID, API key, admin-protected ignore categories) is pre-populated via enterprise policy so users never see the options page.

3. MDM force-install + heartbeat alert

Mode 2 plus the cooperative heartbeat loop: the extension POSTs to /api/browser/heartbeat every 5 minutes. If no heartbeat is received for 15 minutes while a user is active on a supported chat surface (inferred from the audit feed), the backend raises an alert to the org's on-call channel. Catches the edge case where a user uses a dev build of Chrome without enterprise policy loaded.


Chrome Enterprise policy (the core mechanism)

The canonical policy is ExtensionInstallForcelist. This policy has two forms: the classic comma-separated entries and the newer JSON-based ExtensionSettings policy. We use ExtensionSettings because it gives us per-extension configuration.

Policy JSON

Replace <CONTROL_ZERO_EXTENSION_ID> with the extension ID from the Chrome Web Store listing. The update URL is the default for Chrome Web Store extensions.

{
"ExtensionSettings": {
"<CONTROL_ZERO_EXTENSION_ID>": {
"installation_mode": "force_installed",
"update_url": "https://clients2.google.com/service/update2/crx",
"blocked_permissions": [],
"runtime_blocked_hosts": [],
"runtime_allowed_hosts": [
"*://claude.ai/*",
"*://chat.openai.com/*",
"*://chatgpt.com/*",
"*://gemini.google.com/*",
"*://www.perplexity.ai/*",
"*://perplexity.ai/*"
]
}
}
}

installation_mode: force_installed is the mode that prevents the user from disabling or uninstalling the extension. runtime_allowed_hosts restricts the extension to the supported chat surfaces, even if a future update expands the host_permissions field.

Pre-configuring the org ID and API key via managed storage

Users should never paste API keys into the options page in a managed deployment. The extension reads its config from chrome.storage.managed first, falling back to chrome.storage.local only when managed is empty. Admins populate managed storage via the 3rdparty section of the Chrome Enterprise policy.

{
"3rdparty": {
"extensions": {
"<CONTROL_ZERO_EXTENSION_ID>": {
"apiUrl": "https://api.controlzero.ai",
"orgId": "00000000-0000-0000-0000-000000000000",
"apiKey": "cz_live_your_org_api_key_here",
"ignore_disabled_categories": ["secret", "pii"]
}
}
}
}

The ignore_disabled_categories field is the admin lever that prevents users from dismissing warnings for specific rule categories. Users can still ignore custom and ip rules, but not secret or pii.


MDM platform setup

Jamf Pro (macOS)

  1. Create a new Configuration Profile targeting the com.google.Chrome preference domain.
  2. Add a Custom Settings payload with the policy JSON above merged under the com.google.Chrome PList key.
  3. Scope to the target computer group.
  4. Push via Self Service or automatic deployment.
<plist version="1.0">
<dict>
<key>ExtensionSettings</key>
<dict>
<key><CONTROL_ZERO_EXTENSION_ID></key>
<dict>
<key>installation_mode</key><string>force_installed</string>
<key>update_url</key><string>https://clients2.google.com/service/update2/crx</string>
</dict>
</dict>
</dict>
</plist>

Microsoft Intune (Windows / macOS)

  1. Microsoft Edge and Chrome share the same policy keys (ExtensionSettings, ExtensionInstallForcelist) because Edge is Chromium-based.
  2. In the Intune admin center, create a Configuration Profile targeting Windows or macOS with the Administrative Templates (for Windows) or Settings Catalog (for macOS).
  3. Search for "Extension management settings" in the template, paste the policy JSON.
  4. Assign to the device group.

Intune's Settings Catalog surfaces the policy as "Extension management settings" under the Chrome category. The JSON you paste is the same as the Chrome Enterprise JSON above.

Google Workspace Admin Console

  1. Go to Devices → Chrome → Apps & extensions → Users & browsers.
  2. Click the + button and add by extension ID.
  3. Set Installation policy to Force install.
  4. Under Policy for extensions, paste the managed-storage JSON from the section above.
  5. Save and apply to the target OU.

Changes propagate to managed Chrome instances within a few minutes.

Kandji (macOS)

  1. Create a Library item of type "Custom Profile".
  2. Upload a .mobileconfig file that sets the com.google.Chrome preferences with the policy JSON.
  3. Assign to the target Blueprint.

Cross-browser parity

BrowserPolicy mechanismNotes
Google ChromeExtensionSettings / ExtensionInstallForcelistNative, tested, supported
Microsoft EdgeSame keys as Chrome (Chromium-based)Native, tested
BraveSame keys as Chrome (Chromium-based)Tested on Brave 1.65+
FirefoxExtensions.Install policy in policies.jsonRequires the signed .xpi distributed via Firefox Add-ons. See Firefox Enterprise docs.
SafariApple Developer signed extension via App Store + MDMThe Safari Web Extensions model uses Apple MDM (Jamf, Kandji, Intune for Mac). Currently out of scope for v0.2; planned for v1.0.

Compliance checklist (pre-rollout)

Do not deploy this extension to employee devices without completing this checklist. Monitoring software, even cooperative DLP, has legal implications in many jurisdictions.

EU (GDPR, ePrivacy, codetermination law)

  • Works council approval (Germany, Austria, France, Italy) — codetermination law requires Betriebsrat / Conseil Social / RSU approval before deploying any system that can log employee activity. Have a signed minutes entry approving the rollout.
  • Data Processing Addendum (DPA) updated — your DPA with employees (or union agreement) must name keystroke scanning in AI chat composers as a processing activity.
  • Privacy notice updated — add a section to your employee privacy notice describing what the extension captures, retention, and employee rights.
  • Data residency verified — if EU employees' audit entries cross a border (e.g., US-hosted backend), ensure SCCs are in place or use an EU-hosted backend instance.
  • DPO (Data Protection Officer) sign-off — document that the DPO has reviewed the deployment.

US (CCPA, BIPA, state-specific monitoring laws)

  • California employees — CCPA requires disclosure of data collection at or before collection. The extension's first-run tutorial serves as this disclosure, but add it to your employee handbook too.
  • Illinois employees — BIPA covers biometric data, not keystrokes directly, but the Illinois Electronic Monitoring Act (2022) requires written notice of electronic monitoring. Signed acknowledgment from the employee is recommended.
  • Connecticut employees — similar notice requirements under C.G.S. § 31-48d.
  • New York employees — NY Labor Law § 52-c requires prior written notice of electronic monitoring.

Universal

  • User disclosure banner — the extension's options page shows a monitoring notice. Verify it is present and accurate for your org.
  • First-run tutorial reviewed — the extension opens the options page on first install and shows a tutorial. Comms / enablement team should approve the copy.
  • Rule review — no rule has a pattern matching >1% of a sample-content corpus (the backend enforces an 8-char minimum pattern length, but you should still review manually for overly-broad rules).
  • Ignore list policy — decide which rule categories users are allowed to ignore. Default: only custom and ip ignorable; secret and pii always enforced.
  • Audit retention — set a retention cap on the audit log. Default: 90 days. Longer than 1 year is not recommended.
  • Heartbeat alert runbook — assign an on-call rotation to respond to "browser extension silenced" alerts from the backend.
  • Panic delete procedure documented — when an employee typos a password into a chat composer and a rule captures it, what's the process to remove that audit entry? Document the SOP.

The extension POSTs to POST /api/browser/heartbeat every 5 minutes with its device_id, extension version, hostname, user agent, and session counters. The backend raises an alert when:

  1. The device has posted at least one heartbeat in the past 24 hours (device is known).
  2. AND no heartbeat has been received in the past 15 minutes.
  3. AND the user was active on a supported chat surface (inferred from a recent audit entry from the same device OR a recent gateway/SDK call attributed to the same user email).

The alert fires on the org's configured notification channel (Slack, email, PagerDuty) with the device_id, hostname, last heartbeat time, and last-seen chat surface. An on-call engineer should:

  1. Check the Fleet dashboard (/governance/fleet) for the device's last-seen state.
  2. Contact the user to confirm whether they intentionally disabled the extension (in a test environment, for example) or whether the MDM policy has drifted.
  3. If the user is reachable and the extension was uninstalled, re-deploy via MDM (self-heal).
  4. If the user is unreachable, escalate per the org's incident response.

Troubleshooting

"The extension shows 'Not configured' even though I set managed storage"

  1. Verify the extension ID in your ExtensionSettings key matches the actual ID from the Chrome Web Store listing.
  2. Run chrome://policy on the target device and confirm the policy has propagated.
  3. Restart Chrome. Managed storage is re-read on extension load, so a browser restart is needed after the first policy push.

"Users can still disable the extension"

installation_mode must be exactly force_installed. Any other value (allowed, normal_installed, blocked) does not pin the extension. Also verify the device is managed — on unmanaged Chrome profiles, enterprise policies are ignored.

"Heartbeat alerts are firing but the user is still using Claude.ai"

Check whether the extension is in paused state (user has hit the "Pause 10 min" button in the popup). Paused extensions continue posting heartbeats so this should not happen, but it's worth verifying. Also check the device clock — if it is more than 15 minutes skewed, the backend may see the heartbeats as stale.

"I need to roll the extension back to an older version"

  1. Set installation_mode: normal_installed temporarily.
  2. Have users manually install the older version from a signed CRX you serve from a self-hosted update URL.
  3. Set installation_mode back to force_installed with the update_url pointed at your internal CRX.

What's NOT in v0.2

  • LLM-assisted rewrite — "scrub this for me" button that uses the org's gateway to rewrite captured content. Deferred to v0.3.
  • Safari support — Apple's extension model requires a different build and distribution path. Planned for v1.0.
  • Mobile browser support — iOS / Android extensions are a different platform. Planned for v1.0.
  • Per-employee identity via SSO/SCIM — current v0.2 identifies devices, not users. SSO-claimed identity comes in v1.0.
  • Admin-controlled continuous-scan toggle — v0.2 always enables continuous scan on the 4 supported surfaces when the extension is configured. v0.3 adds a managed-storage flag to disable it for orgs that want paste-only mode.

Operational defaults

SettingDefaultOverride
Heartbeat interval5 minutesHardcoded
Heartbeat-missing alert threshold15 minutesBackend config
Rule cache refresh5 minutesHardcoded
Scan debounce150 msHardcoded
Scan performance budget16 ms per passHardcoded
Circuit breaker3 consecutive scans > 50 msHardcoded
Session activity buffer cap10 entriesHardcoded
Audit entry context window40 chars before + 40 afterHardcoded
Default audit retention90 daysBackend config