Build1 distinct publisher3 min readPublished
One developer's Chrome-to-Firefox port of a small countdown extension came down to four keys in manifest.json. The linter that enforces them runs long before you ever see a listing form.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Follow any of these and your For You feed starts watching them — no settings page required.
build
In Manifest V3 every message handler is a cold start, and the fix for lost state hides new failures1 distinct publisher
invest
Mozilla's pitch against Gemini-wired Chrome is an off switch and a search deal with Exa2 distinct publishers
build
Socket turns on continuous scanning for all 97,100 Firefox add-ons1 distinct publisher
build
The exit ping that never left: fetch in beforeunload loses to the browser's unload policy1 distinct publisher
An event page and a service worker read different manifest keys, and that is the whole of the background problem. Firefox runs the background as an event page where Chrome runs a service worker, and `background.service_worker` on its own is simply ignored, with a `BACKGROUND_SERVICE_WORKER_IGNORED` warning rather than an error [7]. The dual declaration covers both: `background.scripts` lists `core/lib.js` then `background.js`, and `background.service_worker` names the same entry file, so Chrome reads one key and Firefox the other off one file on disk [8].
Two traps sit inside that arrangement. Both pass a manifest review. `importScripts` does not exist in an event page, so a worker whose first line is `importScripts('core/lib.js')` throws a `ReferenceError` on Firefox; the author's fix is to list the dependency in `background.scripts` ahead of the entry file and guard the call on `typeof importScripts === 'function'` plus the symbol not already being defined, which also stops a double load if a future Firefox provides it [9]. The second trap is ordering. `background.scripts` runs top to bottom in one shared global, so a dependency listed after its consumer is `undefined` at module top level [10]. An array does not resolve a dependency graph on your behalf. Same discipline as a page full of script tags.
Two of the four changed keys are nested in the same object: the id and `data_collection_permissions` both live under `browser_specific_settings.gecko` [1]. Firefox does not derive the id for you, so MV3 requires you to state it [5]. The writeup uses an email-shaped value, notes an 8-4-4-4-12 GUID works too, and flags that this string is the add-on's update identity permanently, because changing it later produces a new listing rather than an update [6].
The data key is machine-readable and Firefox renders it in the install prompt [12]. For an add-on where nothing leaves the device, `required: ["none"]` is the entire declaration [13]. Otherwise you enumerate types such as `browsingActivity` and `personallyIdentifyingInfo`, and per the author they must match the privacy policy and the store description word for word, since a reviewer finding three versions of your data story treats it as grounds for rejection [13].
Version floor: 140 is the value that lints clean with no version warnings for a new MV3 add-on carrying the data declaration, while declaring that key under a lower minimum earns `KEY_FIREFOX_UNSUPPORTED_BY_MIN_VERSION` [15]. Below 121, Firefox refused to start the background page at all when `service_worker` was present [14]. The writeup says three floors stack and you take the highest that applies [16]; only two of the three numbers appear in the text I was given.
For the four-key figure to transfer, your background has to sit in the intersection of both runtimes already: no `window`, no `document`, no `self.clients` [11]. This particular extension is a two-file background with no analytics, no account, no telemetry, everything held in local extension storage [8][19], which is why the honest answer to the data question was `none` [13]. An extension that ships a bundler-driven worker, or one that reaches for DOM globals in the background, is doing code work, not manifest work. And `gecko_android` is opt-in: omit it and the listing is desktop-only [17].
Ranked by verification strength, evidence, and original report placement.
KH4 Companion is a small extension that counts down to Kingdom Hearts IV, shows days remaining on the toolbar badge, pulls series news and trailers from public feeds, carries a lore compendium and hides a three-lane rhythm minigame in the popup; it has been on the Chrome Web Store since 19 August.
As of that week the same extension is also listed on addons.mozilla.org, the author's first Mozilla listing, with the same build, same version number and same feature set; what changed was four keys in manifest.json.
AMO rejects the package before it ever shows a listing form, so the order of operations is: fix the manifest, get the linter to zero errors, then worry about icons, screenshots and copy; assets built against a package that cannot upload are wasted.
The gate is `npx addons-linter@latest <extension-dir>`, to be run before touching anything else.
Chrome derives an extension ID for you; Firefox does not, and in MV3 you must state it as browser_specific_settings.gecko.id.
The gecko id can be an email-like form such as [email protected] or an {8-4-4-4-12} GUID; the ID is the add-on's update identity forever, and changing it later means a new listing rather than an update.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 29, 2026
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Checkable in minutes, corroborated by nobody
The strongest thing about this reporting is that most of it can be falsified with one command. The developer prints the manifest blocks he shipped, names the warnings the tooling emits — BACKGROUND_SERVICE_WORKER_IGNORED, KEY_FIREFOX_UNSUPPORTED_BY_MIN_VERSION — and points at two live store listings. What he never does is cite Mozilla: the November 2025 data-collection mandate, the pre-121 background failure and the 140 floor arrive as recollection from one submission, with no policy text or second submitter behind them.
One add-on, two stores, no counts
Adoption evidence here is a sample of one: the author's own extension, now listed on both stores, with the Firefox side days old and no install, retention or crash figures on either. What the story does show is a real submission clearing a real gate — including the new data-collection declaration filed as "none" — which is more than a doc-reading would give you, and still not a pattern.
Four keys, one forgiving codebase
The headline promise is small and the piece keeps it — but 'four keys' is true of this project partly because of choices made months before Firefox came up. No bundler, so no source-code submission; pure logic modules with time passed in as an argument; a background relay holding no durable state, which is precisely what makes the event-page-versus-service-worker difference cheap. The developer says all of this himself in the closing section, which is why the overstatement is mild rather than misleading: a minified, framework-heavy extension reading this will not get off as lightly.
Own extension, own byline, no sponsor
The author is selling something, in the mild sense: the walkthrough is also a tour of his countdown extension, and the add-on ID he chose carries his own domain. dev.to publishes him without an editor. Against that, no vendor pays for this, the privacy 'none' rests entirely on his word about his own unbundled code, and every technical claim is cheap enough to check that shading one would be a poor trade.
Solid on mechanics, thin on policy
Split the story and confidence splits with it. The manifest mechanics — declared gecko.id, the dual background key, load order, the importScripts guard — are specific, internally consistent and demonstrated by a shipped listing, so they hold up well. The platform-policy layer, meaning the November date, the version floors and the review consequences of a mismatched data story, comes from one person's single pass through AMO and should be re-checked against Mozilla before anyone builds a process on it.