feat(firefox): add the desktop Firefox browser module (task 0032) #25

Merged
alexion merged 8 commits from task-0032-firefox-browser into main 2026-07-24 09:03:34 -04:00
Owner

Task: .claude/tasks/0032-firefox-browser.md

Summary

Adds Firefox as a single-purpose desktop Module (modules/desktop/firefox.nix), configured entirely through home-manager's programs.firefox and fanned out by the desktop aggregator at lib.mkDefault true, so any Host with modules.desktop.enable brings the browser up (neogaia inherits it through the desktop flag, no per-Host line).

  • Stock mainline Firefox (pkgs.firefox, built firefox-152.0.6), no package override, no NixOS-level program integration.
  • Three extensions force-installed by enterprise policy, keyed by their AMO-verified add-on ids: uBlock Origin (uBlock0@raymondhill.net), Proton Pass (78272b6fa58f4a1abaac99321d503a20@proton.me), SponsorBlock (sponsorBlocker@ajay.app). No native messaging host.
  • Hardening split by intent: locked policies (telemetry, studies, Pocket, offer-to-save-logins, default-browser check, Firefox accounts and sync, sponsored new-tab surfaces) and profile prefs (sponsored address-bar suggestions, new-tab tidy). Fingerprinting resistance left off.
  • Search: DuckDuckGo default over a lean list; the commercial engines are explicitly hidden with metaData.hidden (omission alone does not prune — Firefox reconciles them back in), with search.force = true.
  • Theming: Nord chrome from the shared Stylix scheme via stylix.targets.firefox.colorTheme — the base target only themes fonts and reader view, so colorTheme (the Stylix-managed, signed Firefox Color add-on) supplies the chrome colors. No hand-written CSS, no flake input of ours.
  • Default browser: registered for text/html, application/xhtml+xml, and the http/https schemes via xdg.mimeApps.
  • Decision recorded as ADR 0005, whose no-pinned-add-on rule is scoped to the functional extensions with the signed theming add-on as a deliberate exception.

Fixes found during live confirmation

The manual launch on neogaia surfaced four things the build could not, each fixed and verified by deploying the home generation and living in the browser:

  1. profiles.ini clobber — Firefox writes its own profiles.ini on first launch, and home-manager refused to overwrite it, failing the entire activation, so the declarative profile never deployed (search and theming absent; policy extensions and mime handlers still worked because they don't touch the profile). Fixed by force = true on the generated profiles.ini home.file entry.
  2. Base Stylix target doesn't colour the chrome — it themes only fonts and reader view. Fixed with colorTheme.enable (see above), plus profiles.default.extensions.force = true to acknowledge the managed extension-settings store.
  3. Firefox Color installed but disabled — Firefox disables profile-sideloaded extensions by default. Fixed with extensions.autoDisableScopes = 0; the policy-installed functional extensions are force-enabled by policy and unaffected.
  4. Unreadable address-bar result highlight — Stylix's Nord Firefox Color mapping paints the selected result in base04 (near-white) against base05 text, light-on-light. Fixed by overriding that one highlight to the Nord selection grey (base03), derived from the same Stylix scheme.

Live result: browser present on desktop, Nord chrome, DuckDuckGo default, extensions enabled, links land in it, and the address-bar highlight is legible.

Commits

Commit What
ee672d2 feat The Firefox module: package, policy extensions, hardening, search, Stylix, mime + ADR 0005
3e64bd0 docs Gotcha: search.force does not prune builtins by omission
e24f808 fix home-manager takes ownership of profiles.ini so the profile deploys
09eb9a9 docs Corrected the home.file key gotcha (writer's path form, not always absolute)
e42101e feat Nord chrome via Stylix colorTheme + scoped ADR note
6484f94 docs Recorded the profiles.ini and chrome-theming fixes in the task
bd32795 fix autoDisableScopes = 0 so the sideloaded Firefox Color add-on auto-enables
7f7fc32 fix Darkened the unreadable address-bar result highlight to base03

Review

Risk

Overall: MEDIUM

  • Blast radius: Low — one new self-contained module plus a single mkDefault line in the desktop aggregator; no existing module or caller is modified.
  • Reversibility: Medium — config rollback is trivial, but search.force = true, extensions.force, profiles.ini ownership, and xdg.mimeApps authoritatively overwrite runtime browser state, which a plain revert does not restore.
  • Test coverage: Medium — no automated tests; correctness rests on the toplevel build, and the browser experience is only verifiable by manual launch (which is where the four fixes above came from).
  • Sensitive domain: Medium — touches security-adjacent surface (locked policies, OfferToSaveLogins = false, a password-manager extension, default-handler registration), though no auth/secret material is introduced.
  • Size & complexity: Low — ~110-line module, flat declarative attrsets, no control flow beyond a single mkIf.
  • Runtime criticality: Low — desktop browser on the operator's own machine, gated behind the desktop flag.

Unaddressed findings

Standards — one, deliberately left: semicolons and multiple-sentences-per-line in .claude/spec/firefox.md and the pre-existing body of .claude/tasks/0032-firefox-browser.md breach the repo's authoring rules, left as-is because those files are generated by the spec/to-tasks skills, not authored by this change (the repo rule is to not reflow pre-existing prose). Prose authored here — ADR 0005, the module comments, the task's Implementation Notes — follows the rules.

Spec — none outstanding. The original review's substantive gap (commercial engines not removed) is fixed; the four live-confirmation issues above are all fixed.

The final acceptance criterion — manual launch confirmation — is now satisfied on neogaia: Nord chrome, DuckDuckGo default, extensions enabled, links land in the browser, address-bar highlight legible.

— Claude

Task: `.claude/tasks/0032-firefox-browser.md` ## Summary Adds Firefox as a single-purpose desktop Module (`modules/desktop/firefox.nix`), configured entirely through home-manager's `programs.firefox` and fanned out by the desktop aggregator at `lib.mkDefault true`, so any Host with `modules.desktop.enable` brings the browser up (neogaia inherits it through the desktop flag, no per-Host line). - **Stock mainline Firefox** (`pkgs.firefox`, built `firefox-152.0.6`), no package override, no NixOS-level program integration. - **Three extensions force-installed by enterprise policy**, keyed by their AMO-verified add-on ids: uBlock Origin (`uBlock0@raymondhill.net`), Proton Pass (`78272b6fa58f4a1abaac99321d503a20@proton.me`), SponsorBlock (`sponsorBlocker@ajay.app`). No native messaging host. - **Hardening** split by intent: locked policies (telemetry, studies, Pocket, offer-to-save-logins, default-browser check, Firefox accounts and sync, sponsored new-tab surfaces) and profile prefs (sponsored address-bar suggestions, new-tab tidy). Fingerprinting resistance left off. - **Search**: DuckDuckGo default over a lean list; the commercial engines are explicitly hidden with `metaData.hidden` (omission alone does not prune — Firefox reconciles them back in), with `search.force = true`. - **Theming**: Nord chrome from the shared Stylix scheme via `stylix.targets.firefox.colorTheme` — the base target only themes fonts and reader view, so `colorTheme` (the Stylix-managed, signed Firefox Color add-on) supplies the chrome colors. No hand-written CSS, no flake input of ours. - **Default browser**: registered for `text/html`, `application/xhtml+xml`, and the http/https schemes via `xdg.mimeApps`. - Decision recorded as **ADR 0005**, whose no-pinned-add-on rule is scoped to the functional extensions with the signed theming add-on as a deliberate exception. ## Fixes found during live confirmation The manual launch on neogaia surfaced four things the build could not, each fixed and verified by deploying the home generation and living in the browser: 1. **`profiles.ini` clobber** — Firefox writes its own `profiles.ini` on first launch, and home-manager refused to overwrite it, failing the *entire* activation, so the declarative profile never deployed (search and theming absent; policy extensions and mime handlers still worked because they don't touch the profile). Fixed by `force = true` on the generated `profiles.ini` home.file entry. 2. **Base Stylix target doesn't colour the chrome** — it themes only fonts and reader view. Fixed with `colorTheme.enable` (see above), plus `profiles.default.extensions.force = true` to acknowledge the managed extension-settings store. 3. **Firefox Color installed but disabled** — Firefox disables profile-sideloaded extensions by default. Fixed with `extensions.autoDisableScopes = 0`; the policy-installed functional extensions are force-enabled by policy and unaffected. 4. **Unreadable address-bar result highlight** — Stylix's Nord Firefox Color mapping paints the selected result in base04 (near-white) against base05 text, light-on-light. Fixed by overriding that one highlight to the Nord selection grey (base03), derived from the same Stylix scheme. Live result: browser present on desktop, Nord chrome, DuckDuckGo default, extensions enabled, links land in it, and the address-bar highlight is legible. ## Commits | Commit | What | | --- | --- | | `ee672d2` feat | The Firefox module: package, policy extensions, hardening, search, Stylix, mime + ADR 0005 | | `3e64bd0` docs | Gotcha: `search.force` does not prune builtins by omission | | `e24f808` fix | home-manager takes ownership of `profiles.ini` so the profile deploys | | `09eb9a9` docs | Corrected the `home.file` key gotcha (writer's path form, not always absolute) | | `e42101e` feat | Nord chrome via Stylix `colorTheme` + scoped ADR note | | `6484f94` docs | Recorded the `profiles.ini` and chrome-theming fixes in the task | | `bd32795` fix | `autoDisableScopes = 0` so the sideloaded Firefox Color add-on auto-enables | | `7f7fc32` fix | Darkened the unreadable address-bar result highlight to base03 | ## Review ### Risk **Overall: MEDIUM** - Blast radius: Low — one new self-contained module plus a single `mkDefault` line in the desktop aggregator; no existing module or caller is modified. - Reversibility: Medium — config rollback is trivial, but `search.force = true`, `extensions.force`, `profiles.ini` ownership, and `xdg.mimeApps` authoritatively overwrite runtime browser state, which a plain revert does not restore. - Test coverage: Medium — no automated tests; correctness rests on the toplevel build, and the browser experience is only verifiable by manual launch (which is where the four fixes above came from). - Sensitive domain: Medium — touches security-adjacent surface (locked policies, `OfferToSaveLogins = false`, a password-manager extension, default-handler registration), though no auth/secret material is introduced. - Size & complexity: Low — ~110-line module, flat declarative attrsets, no control flow beyond a single `mkIf`. - Runtime criticality: Low — desktop browser on the operator's own machine, gated behind the desktop flag. ### Unaddressed findings **Standards** — one, deliberately left: semicolons and multiple-sentences-per-line in `.claude/spec/firefox.md` and the pre-existing body of `.claude/tasks/0032-firefox-browser.md` breach the repo's authoring rules, left as-is because those files are generated by the spec/to-tasks skills, not authored by this change (the repo rule is to not reflow pre-existing prose). Prose authored here — ADR 0005, the module comments, the task's Implementation Notes — follows the rules. **Spec** — none outstanding. The original review's substantive gap (commercial engines not removed) is fixed; the four live-confirmation issues above are all fixed. The final acceptance criterion — manual launch confirmation — is now satisfied on neogaia: Nord chrome, DuckDuckGo default, extensions enabled, links land in the browser, address-bar highlight legible. — Claude
alexion added 1 commit 2026-07-24 08:33:26 -04:00
The base Stylix Firefox target themes only fonts and the reader view, leaving
the toolbar and tabs at Firefox's default look. Enable colorTheme so Stylix
recolours the chrome Nord from the shared scheme through its managed Firefox
Color add-on, with no hand-written chrome CSS and no flake input of ours.

Scope ADR 0005's no-pinned-add-on rule to the three functional extensions and
record the signed, Stylix-managed theming add-on as a deliberate exception.
alexion force-pushed task-0032-firefox-browser from 018d4becd1 to e42101e08b 2026-07-24 08:33:26 -04:00 Compare
alexion added 1 commit 2026-07-24 08:33:58 -04:00
alexion added 1 commit 2026-07-24 08:41:37 -04:00
Firefox disables profile-sideloaded extensions by default, so the Firefox
Color add-on that carries the Nord chrome theme installed but stayed disabled
and the chrome kept its default look. Set extensions.autoDisableScopes = 0 so
it enables on detection; the policy-installed functional extensions are
force-enabled by policy and unaffected.
alexion added 1 commit 2026-07-24 08:51:26 -04:00
Stylix's Nord Firefox Color mapping paints the selected address-bar result in
base04, a near-white grey, against base05 text — light on light, so the text
of the highlighted result is illegible. Override that one highlight to the Nord
selection grey (base03), derived from the same Stylix scheme, restoring
contrast without touching the rest of the theme.
alexion merged commit 7f7fc327fd into main 2026-07-24 09:03:34 -04:00
alexion deleted branch task-0032-firefox-browser 2026-07-24 09:03:34 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alexion/dotfiles#25