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 ## 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`. - 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. 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. 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`. 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"`. 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. 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`. - `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`. 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. 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" "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": { "firefox-gnome-theme": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -517,6 +539,7 @@
"inputs": { "inputs": {
"chaotic": "chaotic", "chaotic": "chaotic",
"disko": "disko", "disko": "disko",
"firefox-addons": "firefox-addons",
"gitea-axi": "gitea-axi", "gitea-axi": "gitea-axi",
"home-manager": "home-manager_3", "home-manager": "home-manager_3",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",

View File

@@ -16,6 +16,12 @@
inputs.nixpkgs.follows = "nixpkgs"; 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. # Follows our nixpkgs so its plugins build against the same package set.
nixvim = { nixvim = {
url = "github:nix-community/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. # Firefox as the desktop browser: stock mainline, hardened and de-monetized by policy.
let let
cfg = config.modules.desktop.firefox; cfg = config.modules.desktop.firefox;
user = config.user.name; user = config.user.name;
firefoxAddons = inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system};
# 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";
};
in in
{ {
options.modules.desktop.firefox.enable = lib.mkEnableOption "Firefox as the desktop browser"; options.modules.desktop.firefox.enable = lib.mkEnableOption "Firefox as the desktop browser";
@@ -34,21 +34,22 @@ in
SponsoredPocket = false; SponsoredPocket = false;
Snippets = 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 = { profiles.default = {
isDefault = true; isDefault = true;
# The Nord chrome theme is a declared extension setting, so home-manager extensions = {
# owns the extension-settings store, overwriting runtime changes to it. packages = with firefoxAddons; [
extensions.force = true; 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 # Stylix's Nord mapping paints the selected address-bar result a
# near-white grey, leaving its light text unreadable. Darken that one # near-white grey, leaving its light text unreadable. Darken that one