Skip to content
C.W.K.
Stream
Lesson 10 of 10 · published

Anchor 10 — One Host Registry, Four Script Realms

~20 min · host-registry, mv3, configuration, classic-scripts, v0.2.1

Level 0Extension Curious
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"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.js lists pippa-hosts.js before content-script.js.
  • Popup: popup.html loads the registry script before popup.js.
  • Side panel: sidepanel.html loads the registry before sidepanel.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.

Code

pippa-hosts.js — public checkpoint·javascript
var PIPPA_OFFICE_TAILNET_HOST = '100.x.x.x';
var PIPPA_FRONTEND_PORT = '5173';

var PIPPA_HOST_ENTRIES = [
  { host: 'localhost', label: 'Localhost' },
  { host: '127.0.0.1', label: 'Loopback' },
  { host: PIPPA_OFFICE_TAILNET_HOST, label: 'Office Tailnet' },
];

var PIPPA_LOCAL_HOSTS = PIPPA_HOST_ENTRIES.map((entry) => entry.host);
var PIPPA_PANEL_ORIGINS = PIPPA_HOST_ENTRIES.map(
  (entry) => `http://${entry.host}:${PIPPA_FRONTEND_PORT}`,
);
var PIPPA_PANEL_ORIGIN_OPTIONS = PIPPA_HOST_ENTRIES.map((entry, index) => ({
  value: PIPPA_PANEL_ORIGINS[index],
  label: entry.label,
}));
manifest.json — synchronized surfaces (abridged)·json
{
  "host_permissions": [
    "http://localhost:5173/*",
    "http://127.0.0.1:5173/*",
    "http://100.x.x.x:5173/*"
  ],
  "content_scripts": [{
    "exclude_matches": [
      "http://localhost:5173/embed/*",
      "http://127.0.0.1:5173/embed/*",
      "http://100.x.x.x:5173/embed/*"
    ],
    "js": ["pippa-hosts.js", "content-script.js"]
  }],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'; connect-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173; frame-src http://localhost:5173 http://127.0.0.1:5173 http://100.x.x.x:5173;"
  }
}
background.js — serialized function receives explicit args·javascript
const [result] = await chrome.scripting.executeScript({
  target: { tabId },
  args: [PIPPA_LOCAL_HOSTS],
  func: (localPippaHosts) => {
    const pageHost = location.hostname;
    return { isPippaHost: localPippaHosts.includes(pageHost) };
  },
});

External links

Exercise

Work in a disposable copy of the ChromeEmbed folder. Add a fourth placeholder host, dev.example.test, to PIPPA_HOST_ENTRIES and to the manifest's three synchronized groups: host_permissions, content_scripts.exclude_matches, and both CSP directives. Confirm popup and side-panel candidates derive it without another JavaScript literal. Then intentionally remove it from connect-src and explain which probe fails while frame-src still governs the iframe attempt. Restore the entry, reload the unpacked extension, and run a grep/agreement check that reports every expected placeholder occurrence and rejects any private IP literal.
Hint
Count semantics, not just strings: the registry owns JavaScript consumers; manifest JSON owns permission, self-capture exclusion, fetch, and frame policy. Also inspect loader order. If content-script.js appears before pippa-hosts.js, the registry can be correct on disk and still be undefined at runtime.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.