"A host list looked like configuration until one address moved. Then the same fact disagreed with itself in background, content, popup, side panel, and manifest. Anchor 10 is the repair: one JavaScript owner, plus an explicit border where JSON cannot inherit."
The bug that earned a new owner
ChromeEmbed talks to the same cwkPippa frontend from four extension realms. The service worker decides whether a tab is already a Pippa surface. The content script avoids self-capture. The popup builds its Frontend selector. The side panel probes candidate origins in order. Before v0.2.1, each realm carried its own host literals. A Tailnet address changed once, and partial replacement made the extension disagree with itself. That is not cosmetic duplication; it is one product fact with four consumers and a demonstrated failure mode.
Four classic-script loaders
- Service worker:
importScripts('pippa-hosts.js')before the router is evaluated. - Content script: manifest
content_scripts.jslistspippa-hosts.jsbeforecontent-script.js. - Popup:
popup.htmlloads the registry script beforepopup.js. - Side panel:
sidepanel.htmlloads the registry beforesidepanel.js.
They do not share one runtime global across the extension. Each realm loads the same file into its own classic-script global environment. That is why the declarations use var, not const: after an extension reload, Chrome can inject a content script into an isolated world that already saw the registry. Repeating a top-level const throws; repeating var is harmless.
The manifest is the irreducible duplicate
manifest.json is data, not a script, and MV3 gives it no import mechanism. Three groups therefore remain synchronized by review and verification: host_permissions, content_scripts.exclude_matches, and the connect-src plus frame-src origins inside extension-page CSP. Each has a different job. Permission makes the network surface legal. Exclusion prevents ChromeEmbed from capturing its own embed route. connect-src permits reachability probes; frame-src permits the iframe attempt. The remote response may still refuse framing.
Serialized functions have no closure
The worker also has a one-shot fallback using chrome.scripting.executeScript({ func }) for tabs that predate an extension reload. Chrome serializes that function; it cannot close over PIPPA_LOCAL_HOSTS from worker scope. The allowlist must travel through args. This boundary is easy to miss because ordinary nested JavaScript functions do close over their environment. Injected functions are code shipments, not closures.
Public material has one more invariant
The real office address is operational configuration, not quest content. Public examples use 100.x.x.x. A good consistency check proves two things at once: every public host surface agrees, and no private literal escaped. Consolidation is complete only when ownership, the unavoidable duplication, the loader order, and the redaction boundary are all visible.