Skip to content

rfc(browsers): create curated Firefox extension flake #227

@Bad3r

Description

@Bad3r

Context

The reference project is petrkozorezov/firefox-addons-nix, a broad AMO-to-Nix catalog flake. It fetches Firefox add-on metadata from the Mozilla Add-ons API, generates a Nix-consumable add-on catalog, exposes the catalog through an overlay, and lets Home Manager users install packaged Firefox extensions from pkgs.firefox-addons. The plan here is to create a similar independent project for this repo, but scoped to only the Firefox-family extensions used in this nixos config rather than mirroring the full public AMO catalog.

Important mechanics from that flake:

  • fetch-addons.py queries the Mozilla Add-ons API, filters public add-ons/current files, extracts slug/version/download URL/hash/GUID, and records metadata such as permissions, host permissions, optional permissions, payment flag, compatibility, categories, tags, EULA/privacy flags, and promoted category.
  • flake.nix exposes overlays.default.firefox-addons by importing the generated JSON and turning each record into an overridable derivation.
  • each add-on derivation fetches the signed AMO XPI and installs it under share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/${addonId}.xpi.
  • the flake has an allow helper for permission/metadata drift checks so consumers can fail evaluation when a new add-on release changes sensitive metadata unexpectedly.

This issue proposes creating a new repository/flake with the same useful shape, but not forking that project and not generating the full AMO catalog. The new flake should manage only the Firefox-family extensions used by this NixOS config.

Current local consumers

Current profile package lists are built from inputs.dedupe_nur.overlays.default.nur.repos.rycee.firefox-addons

Current extentions configured (ensure that its up to date when implementing):

  • ublock-origin
  • raindropio
  • cookie-autodelete
  • simplelogin
  • stylus
  • tab-stash
  • languagetool
  • violentmonkey
  • web-archives
  • tridactyl
  • darkreader
  • onepassword-password-manager
  • foxyproxy-standard
  • wappalyzer
  • print-edit-we
  • save-page-we

Design constraint: policy installs are browser-wide

Firefox/Gecko enterprise ExtensionSettings policy is not profile-scoped. If an extension is installed through policy, it applies to every profile in that browser.

That means the new flake should primarily provide package derivations and metadata. This nixos config (repo) should keep deciding profile membership through Home Manager profile package lists:

  • programs.<browser>.profiles.primary.extensions.packages
  • programs.<browser>.profiles.work.extensions.packages
  • programs.<browser>.profiles.ephemeral.extensions.packages

Use policy install entries only for extensions that are intentionally global across all profiles. Do not move profile-specific membership into ExtensionSettings.

Proposed new flake

Create a new repository, for example Bad3r/firefox-extensions-flake, with a curated manifest instead of a full AMO mirror.

Suggested outputs:

  • overlays.default: exposes pkgs.firefox-extensions.<name>.
  • packages.${system}: exposes every curated extension package for direct CI/build checks.
  • lib.buildFirefoxAddon: shared builder compatible with Home Manager extensions.packages.
  • lib.buildFirefoxAddonFromStore: AMO/signed-XPI builder for normal curated extensions.
  • lib.buildUblockOriginFromSource: source-build path for uBlock Origin.
  • optional checks.${system}: build every curated extension and verify metadata contracts.

Suggested source layout:

flake.nix
extensions.lock.json      # generated AMO metadata for the curated slug list
extensions.toml/json      # hand-owned allowlist and expected metadata contract
pkgs/firefox-addon.nix    # generic signed-XPI builder
pkgs/ublock-origin.nix    # upstream releases packaged uBO derivation
scripts/update-extensions # refresh only curated AMO records
scripts/check-metadata    # fail on unexpected permission/host-permission drift

Curated update workflow

The update script should accept an explicit slug list and refresh only those add-ons through the AMO API. It should not page through the whole add-on universe.

For each curated AMO add-on, persist at least:

  • pname
  • version
  • url
  • hash
  • addonId
  • permissions
  • host permissions
  • optional permissions
  • license
  • compatibility
  • promoted category
  • requires-payment flag
  • EULA/privacy flags

The generated package should expose enough metadata for Home Manager and local review:

  • addonId at top level, because Home Manager uses it to match extension settings.
  • metadata/passthru for permissions and host permissions.
  • an allow or equivalent contract checker to catch unexpected metadata changes.

uBlock Origin from source

uBlock Origin should not be fetched from AMO in the new flake. It should be packaged from upstream releases (uBlock/releases).

Source evidence from uBlock Origin repo:

  • Makefile target firefox builds dist/build/uBlock0.firefox.
  • tools/pull-assets.sh fetches uBlockOrigin/uAssets master and gh-pages into dist/build/uAssets.
  • tools/make-firefox.sh all copies common + Firefox platform files, generates metadata with tools/make-firefox-meta.py, and zips dist/build/uBlock0.firefox.xpi.
  • dist/README.md documents make firefox as the developer build path and notes that stable Firefox normally requires signature handling for XPI installation.

The Nix derivation should make the asset inputs explicit and reproducible. Prefer fixed-output fetches for both uBlock and uAssets.

Migration plan

  1. Create the new curated extension flake repository.
  2. Port the small builder pattern from firefox-addons-nix, but keep the implementation independent rather than forking that repository.
  3. Add the curated extension manifest from the current profile package lists.
  4. Implement AMO metadata refresh for only the curated set.
  5. Implement release ublock-origin from upstream uBlock.
  6. Add checks that build all curated packages and fail on unexpected permission/host-permission drift.
  7. Add the new flake as an input.
  8. Replace inputs.dedupe_nur.overlays.default.nur.repos.rycee.firefox-addons usage in Gecko profile code with the new curated flake packages.
  9. Keep per-profile package membership in Home Manager profile package lists.
  10. Revisit existing policy install entries and remove any policy install that is only compensating for missing package availability.
  11. Keep only truly browser-wide policy entries in ExtensionSettings.

Acceptance criteria

  • A new repo/flake exists; not a fork.
  • The flake manages only the curated extension set used by this NixOS config.
  • Normal AMO-backed extensions are pinned by URL/hash and generated metadata.
  • uBlock Origin is packaged from upstream releases, not downloaded from AMO.
  • Extension packages expose addonId and permission metadata suitable for Home Manager profile extension use and local drift checks.
  • Metadata drift checks fail when permissions, host permissions, optional permissions, payment status, or promoted category change unexpectedly.
  • This NixOS config flake.nix consumes the new flake input for Gecko profile extension packages.
  • Per-profile membership remains profile-scoped through profiles.<name>.extensions.packages; policy installs are used only for intentionally browser-wide extensions.
  • Existing uBO declarative settings continue to apply after the package source changes.
  • Existing 1Password native-messaging ownership remains unchanged: the GUI module owns the wrapper, and Gecko Home Manager config owns the browser trust manifest.
  • Validation covers package builds plus targeted eval for Firefox, and LibreWolf profiles on the managed hosts.

Related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    area(flake)Flake topology, inputs, outputs, lockfile management, or Nix CLI integration.area(home-manager)Home Manager modules, activation, or user-environment config.area(nixos)NixOS modules, host config, services, or system-level behavior.area(packages)Package definitions, overrides, overlays, or package-source selection.focus(hardening)Proactive attack-surface reduction or tighter defaults.input(nur)Flake input nix-community/NUR and dedupe_nur aliases.priority(p3)Normal priority.status(backlog)Accepted work that is intentionally unscheduled.type(migration)Moves systems, data, or integrations across boundaries.

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions