Skip to content
Get the kit

Firefox

Firefox distribution goes through addons.mozilla.org (AMO). The kit builds a Firefox-specific package from the same codebase, with the Chromium-only permissions and APIs filtered out.

Terminal window
pnpm build:firefox
Terminal window
pnpm zip:firefox

build:firefox is the local test build, zip:firefox the AMO upload zips.

Load a test build via about:debugging → This Firefox → Load Temporary Add-on. The add-on’s ID is browser_specific_settings.gecko.id in wxt.config.ts, and it is permanent once AMO has it. The setup wizard replaces the kit’s own (extension@extensionstart.com, which every clone would share and AMO would refuse) with a unique one, a GUID in braces. Change it to the name@your-domain form before your first upload if you prefer, never after.

What is different in the Firefox build (handled per-browser in wxt.config.ts):

  • Permissions are filtered to storage and identity; offscreen and sidePanel don’t exist on Firefox and AMO lint rejects unknown permissions.
  • On the Firebase stack, no offscreen document means Google sign-in on Firefox requires WXT_GOOGLE_OAUTH_CLIENT_ID (the web-auth-flow path; see Sign-in).
  • Chrome-only APIs are guarded in code (browser.sidePanel?.…).
  • strict_min_version is 140 (142 on Android), the first Firefox with built-in data consent (below).

Since November 3, 2025, AMO requires every new add-on to declare what it sends off the device, in Firefox’s own data types; Firefox shows the list at install. The kit declares it in wxt.config.ts (browser_specific_settings.gecko.data_collection_permissions):

Data typeKindWhy
personallyIdentifyingInforequiredthe account’s email (and name), when the user signs in
authenticationInforequiredthe sign-in itself: credentials and tokens go to Firebase or Supabase Auth
technicalAndInteractionoptionalbilling only: the paywall’s usage record (POST /gate/events)

Firefox lets the user decline an optional type at install, and technicalAndInteraction can only be optional. A Firefox user who declines it sends no usage events (entrypoints/background/usage-events.ts asks the browser first); the paywall’s local counters work either way.

Update the list when your product sends more: page text to an AI service is websiteContent, visited URLs browsingActivity, and so on (the data types). Data a feature keeps only on the device (chrome.storage) needs none. An add-on that sends nothing at all declares required: ["none"].

AMO reviews are human and stricter than CWS about build pipelines. Because the code is bundled and minified, you must upload the source code alongside the extension zip, plus instructions that reproduce the build.

pnpm zip:firefox produces both zips in apps/extension/.output/: the extension package and a -sources.zip with the source tree. Upload the sources zip when the AMO flow asks “Do you need to submit source code?” → Yes.

Paste into “Notes for Reviewers” and adjust versions. On Supabase, write Supabase where it says Firebase, and the PKCE flow for the implicit flow:

This extension is built from the included sources with WXT (Vite).
Build environment:
- Node 22.12 or newer, pnpm 12 (the exact version is pinned in the packageManager
field of package.json; pnpm 11 or newer switches to it on its own)
Reproduce the build:
1. unzip the sources
2. pnpm install --frozen-lockfile
3. pnpm zip:firefox
4. compare .output/*-firefox.zip with the submitted package
Notes:
- No remote code: all executed JS ships in the bundle. Remote requests
fetch JSON data only (announcements, prices, plan state) from our own
Firebase backend.
- Minification is Vite's default minifier (Oxc); no obfuscation.
- Sign-in uses browser.identity.launchWebAuthFlow with Firebase Auth
(OAuth implicit flow; no client secret in the bundle).

If you kept billing: add one line saying premium features require a subscription and include a test account (email/password sign-in is the easiest to hand a reviewer).

  • Versions are immutable: you can’t replace an uploaded version. Fix and bump.
  • CI already runs AMO’s linter (addons-linter) against the Firefox build, so lint-class surprises surface before you submit.
  • AMO asks for a privacy policy whenever data leaves the machine (auth qualifies); reuse the privacy disclosures content.
  • Updates auto-publish after review; there is no staged rollout on AMO.