feat(firefox): pin signed browser extensions

This commit is contained in:
2026-07-30 08:31:11 -04:00
parent ef1eebda58
commit b8bb26da75
4 changed files with 52 additions and 18 deletions

View File

@@ -18,6 +18,8 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
## Gotchas
- ADR bodies are immutable records of decisions as they were made, while status frontmatter is mutable.
When a decision changes or its premise proves wrong, preserve the original body, update its status, and add a new ADR that supersedes it.
- This repo pins no Nix formatter, and its committed `.nix` files are not clean under current `nixfmt-rfc-style`.
Running `nixfmt` across a file reflows untouched code (for example `lib.nix`'s `deriveMac` list and multi-line assertion messages) and injects churn unrelated to the change.
Format only the lines being written or changed, matching the surrounding style by hand.
@@ -62,6 +64,8 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
Omission alone does not prune a built-in engine, since Firefox reconciles its app-provided engines back in, so remove one by listing it with `<engine>.metaData.hidden = true`.
Engines are referenced by their current id, so the default is `default = "ddg"`, not `"DuckDuckGo"`.
Decode the built file with `mozlz4a -d <search.json.mozlz4>` to check the result.
- Any non-empty Home Manager Firefox `profiles.<name>.extensions.settings.<id>.settings` causes Home Manager to set `extensions.webextensions.ExtensionStorageIDB.enabled = false` globally for that profile.
This repo's Stylix Firefox `colorTheme` settings trigger it, so every extension in the profile uses the legacy extension-storage backend regardless of how it is installed.
- `home.sessionVariables` do **not** reach the Hyprland session, since UWSM does not source `hm-session-vars.sh`.
The cursor is therefore set through Hyprland's own `env = KEY,VALUE` in `modules/desktop/hyprland/hyprland.nix`, sourced from `config.stylix.cursor`.
Bibata ships XCursor format only (no `hyprcursor/` dir), rendered through Hyprland's XCursor fallback, so `XCURSOR_*` and `HYPRCURSOR_*` naming the same theme are both safe.

23
flake.lock generated
View File

@@ -109,6 +109,28 @@
"type": "github"
}
},
"firefox-addons": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"dir": "pkgs/firefox-addons",
"lastModified": 1785384175,
"narHash": "sha256-sWSJPXpQwKJstL4rdhpAQYCYlHK5wOkEHR8/lNHBVb4=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "db607f3d0afe811bcb3b16266f28b2fc5af4e74f",
"type": "gitlab"
},
"original": {
"dir": "pkgs/firefox-addons",
"owner": "rycee",
"repo": "nur-expressions",
"type": "gitlab"
}
},
"firefox-gnome-theme": {
"flake": false,
"locked": {
@@ -517,6 +539,7 @@
"inputs": {
"chaotic": "chaotic",
"disko": "disko",
"firefox-addons": "firefox-addons",
"gitea-axi": "gitea-axi",
"home-manager": "home-manager_3",
"nixos-hardware": "nixos-hardware",

View File

@@ -16,6 +16,12 @@
inputs.nixpkgs.follows = "nixpkgs";
};
# Signed AMO extensions, pinned by version and hash.
firefox-addons = {
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
inputs.nixpkgs.follows = "nixpkgs";
};
# Follows our nixpkgs so its plugins build against the same package set.
nixvim = {
url = "github:nix-community/nixvim";

View File

@@ -1,15 +1,15 @@
{ config, lib, ... }:
{
config,
inputs,
lib,
pkgs,
...
}:
# Firefox as the desktop browser: stock mainline, hardened and de-monetized by policy.
let
cfg = config.modules.desktop.firefox;
user = config.user.name;
# A force-installed extension, keyed at the call site by the add-on's own id.
# Firefox fetches the signed add-on from Mozilla's site and enables it automatically.
forceInstalled = slug: {
install_url = "https://addons.mozilla.org/firefox/downloads/latest/${slug}/latest.xpi";
installation_mode = "force_installed";
};
firefoxAddons = inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system};
in
{
options.modules.desktop.firefox.enable = lib.mkEnableOption "Firefox as the desktop browser";
@@ -34,21 +34,22 @@ in
SponsoredPocket = false;
Snippets = false;
};
# An ad blocker, a password manager, and a video sponsor-skipper.
ExtensionSettings = {
"uBlock0@raymondhill.net" = forceInstalled "ublock-origin";
"78272b6fa58f4a1abaac99321d503a20@proton.me" = forceInstalled "proton-pass";
"sponsorBlocker@ajay.app" = forceInstalled "sponsorblock";
};
};
profiles.default = {
isDefault = true;
# The Nord chrome theme is a declared extension setting, so home-manager
# owns the extension-settings store, overwriting runtime changes to it.
extensions.force = true;
extensions = {
packages = with firefoxAddons; [
ublock-origin
proton-pass
sponsorblock
];
# The Nord chrome theme is a declared extension setting, so home-manager
# owns the extension-settings store, overwriting runtime changes to it.
force = true;
};
# Stylix's Nord mapping paints the selected address-bar result a
# near-white grey, leaving its light text unreadable. Darken that one