diff --git a/home/browsers/mullvad-browser.nix b/home/browsers/mullvad-browser.nix
index e0fbcc8..17b6154 100644
--- a/home/browsers/mullvad-browser.nix
+++ b/home/browsers/mullvad-browser.nix
@@ -39,6 +39,12 @@ let
# Use native file picker instead of GTK file picker
"widget.use-xdg-desktop-portal.file-picker" = 1;
+
+ # enable WebAuthn
+ "security.webauth.webauthn" = true;
+
+ # video acceleration
+ "media.ffmpeg.vaapi.enabled" = true;
};
commonExtensions = with firefox-addons; [
diff --git a/home/modules/firefox/common.nix b/home/modules/firefox/common.nix
index 8fda3fe..449cc0f 100644
--- a/home/modules/firefox/common.nix
+++ b/home/modules/firefox/common.nix
@@ -1,751 +1,18 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-
-with lib;
+{ lib, ... }:
let
- jsonFormat = pkgs.formats.json { };
-
- # The extensions path shared by all profiles; will not be supported
- # by future Firefox versions.
- extensionPath = "extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
-
userPrefValue =
pref:
- builtins.toJSON (if isBool pref || isInt pref || isString pref then pref else builtins.toJSON pref);
-
+ builtins.toJSON (
+ if lib.isBool pref || lib.isInt pref || lib.isString pref then pref else builtins.toJSON pref
+ );
+in
+{
mkConfig =
prefs:
- concatStrings (
- mapAttrsToList (name: value: ''
+ lib.concatStrings (
+ lib.mapAttrsToList (name: value: ''
user_pref("${name}", ${userPrefValue value});
'') prefs
);
-
- mkUserJs =
- prefs: extraPrefs: bookmarks:
- let
- prefs' =
- lib.optionalAttrs ([ ] != bookmarks) {
- "browser.bookmarks.file" = toString (firefoxBookmarksFile bookmarks);
- "browser.places.importBookmarksHTML" = true;
- }
- // prefs;
- in
- ''
- // Generated by Home Manager.
-
- ${mkConfig prefs'}
-
- ${extraPrefs}
- '';
-
- firefoxBookmarksFile =
- bookmarks:
- let
- indent = level: lib.concatStringsSep "" (map (lib.const " ") (lib.range 1 level));
-
- bookmarkToHTML =
- indentLevel: bookmark:
- ''${indent indentLevel}
${escapeXML bookmark.name}'';
-
- directoryToHTML = indentLevel: directory: ''
- ${indent indentLevel}${
- if directory.toolbar then
- ''Bookmarks Toolbar''
- else
- "${escapeXML directory.name}"
- }
- ${indent indentLevel}
- ${allItemsToHTML (indentLevel + 1) directory.bookmarks}
- ${indent indentLevel}
'';
-
- itemToHTMLOrRecurse =
- indentLevel: item:
- if item ? "url" then bookmarkToHTML indentLevel item else directoryToHTML indentLevel item;
-
- allItemsToHTML =
- indentLevel: bookmarks: lib.concatStringsSep "\n" (map (itemToHTMLOrRecurse indentLevel) bookmarks);
-
- bookmarkEntries = allItemsToHTML 1 bookmarks;
- in
- pkgs.writeText "firefox-bookmarks.html" ''
-
-
-
- Bookmarks
- Bookmarks Menu
-
- ${bookmarkEntries}
-
- '';
-in
-{
- inherit mkConfig;
-
- mkModule =
- browser:
- let
- cfg = config.programs.${browser.name};
- inherit (browser)
- displayName
- dataConfigPath
- defaultPackage
- defaultPackageName
- isSecure
- ;
-
- profilesPath = dataConfigPath;
-
- profiles =
- flip mapAttrs' cfg.profiles (
- _: profile:
- nameValuePair "Profile${toString profile.id}" {
- Name = profile.name;
- Path = profile.path;
- IsRelative = 1;
- Default = if profile.isDefault then 1 else 0;
- }
- )
- // {
- General = {
- StartWithLastProfile = 1;
- };
- };
-
- profilesIni = generators.toINI { } profiles;
-
- mkProfileBin =
- profile:
- let
- name = "${browser.name}-${profile}";
- scriptBin = pkgs.writeScriptBin name ''
- ${browser.name} -P "${profile}" --name="${name}" $@
- '';
- desktopFile = pkgs.makeDesktopItem {
- inherit name;
- exec = "${scriptBin}/bin/${name} %U";
- icon = browser.name;
- extraConfig.StartupWMClass = name;
- desktopName = "${displayName} (${profile})";
- genericName = "Web Browser";
- categories = [
- "Network"
- "WebBrowser"
- ] ++ optional isSecure "Security";
- };
- in
- pkgs.runCommand name { } ''
- mkdir -p $out/{bin,share}
- cp -r ${scriptBin}/bin/${name} $out/bin/${name}
- cp -r ${desktopFile}/share/applications $out/share/applications
- '';
- in
- {
- options = {
- programs.${browser.name} = {
- enable = mkEnableOption displayName;
-
- createProfileBins = mkOption {
- type = types.bool;
- default = false;
- description = ''
- When enabled installs a binary for all non-default profiles named `${browser.name}-''${profile}`.
- This also includes a `.desktop` file that is configured to show separate icons (on GNOME at least).
- '';
- };
-
- package = mkOption {
- type = types.package;
- default = defaultPackage;
- defaultText = literalExpression defaultPackageName;
- example = literalExpression ''
- ${defaultPackageName}.override {
- # See nixpkgs' ${defaultPackageName} to check which options you can use
- pulseaudioSupport = true;
- }
- '';
- description = "The ${displayName} package to use.";
- };
-
- profiles = mkOption {
- type = types.attrsOf (
- types.submodule (
- { config, name, ... }:
- {
- options = {
- name = mkOption {
- type = types.str;
- default = name;
- description = "Profile name.";
- };
-
- id = mkOption {
- type = types.ints.unsigned;
- default = 0;
- description = ''
- Profile ID. This should be set to a unique number per profile.
- '';
- };
-
- settings = mkOption {
- type = types.attrsOf (
- jsonFormat.type
- // {
- description = "Firefox preference (int, bool, string, and also attrs, list, float as a JSON string)";
- }
- );
- default = { };
- example = literalExpression ''
- {
- "browser.startup.homepage" = "https://nixos.org";
- "browser.search.region" = "GB";
- "browser.search.isUS" = false;
- "distribution.searchplugins.defaultLocale" = "en-GB";
- "general.useragent.locale" = "en-GB";
- "browser.bookmarks.showMobileBookmarks" = true;
- "browser.newtabpage.pinned" = [{
- title = "NixOS";
- url = "https://nixos.org";
- }];
- }
- '';
- description = ''
- Attribute set of Firefox preferences.
-
- Firefox only supports int, bool, and string types for
- preferences, but home-manager will automatically
- convert all other JSON-compatible values into strings.
- '';
- };
-
- extraConfig = mkOption {
- type = types.lines;
- default = "";
- description = ''
- Extra preferences to add to user.js.
- '';
- };
-
- userChrome = mkOption {
- type = types.lines;
- default = "";
- description = "Custom Firefox user chrome CSS.";
- example = ''
- /* Hide tab bar in FF Quantum */
- @-moz-document url("chrome://browser/content/browser.xul") {
- #TabsToolbar {
- visibility: collapse !important;
- margin-bottom: 21px !important;
- }
-
- #sidebar-box[sidebarcommand="treestyletab_piro_sakura_ne_jp-sidebar-action"] #sidebar-header {
- visibility: collapse !important;
- }
- }
- '';
- };
-
- userContent = mkOption {
- type = types.lines;
- default = "";
- description = "Custom Firefox user content CSS.";
- example = ''
- /* Hide scrollbar in FF Quantum */
- *{scrollbar-width:none !important}
- '';
- };
-
- bookmarks = mkOption {
- type =
- let
- bookmarkSubmodule =
- types.submodule (
- { config, name, ... }:
- {
- options = {
- name = mkOption {
- type = types.str;
- default = name;
- description = "Bookmark name.";
- };
-
- keyword = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = "Bookmark search keyword.";
- };
-
- url = mkOption {
- type = types.str;
- description = "Bookmark url, use %s for search terms.";
- };
- };
- }
- )
- // {
- description = "bookmark submodule";
- };
-
- bookmarkType = types.addCheck bookmarkSubmodule (x: x ? "url");
-
- directoryType =
- types.submodule (
- { config, name, ... }:
- {
- options = {
- name = mkOption {
- type = types.str;
- default = name;
- description = "Directory name.";
- };
-
- bookmarks = mkOption {
- type = types.listOf nodeType;
- default = [ ];
- description = "Bookmarks within directory.";
- };
-
- toolbar = mkOption {
- type = types.bool;
- default = false;
- description = "If directory should be shown in toolbar.";
- };
- };
- }
- )
- // {
- description = "directory submodule";
- };
-
- nodeType = types.either bookmarkType directoryType;
- in
- with types;
- coercedTo (attrsOf nodeType) attrValues (listOf nodeType);
- default = [ ];
- example = literalExpression ''
- [
- {
- name = "wikipedia";
- keyword = "wiki";
- url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
- }
- {
- name = "kernel.org";
- url = "https://www.kernel.org";
- }
- {
- name = "Nix sites";
- bookmarks = [
- {
- name = "homepage";
- url = "https://nixos.org/";
- }
- {
- name = "wiki";
- url = "https://nixos.wiki/";
- }
- ];
- }
- ]
- '';
- description = ''
- Preloaded bookmarks. Note, this may silently overwrite any
- previously existing bookmarks!
- '';
- };
-
- path = mkOption {
- type = types.str;
- default = name;
- description = "Profile path.";
- };
-
- isDefault = mkOption {
- type = types.bool;
- default = config.id == 0;
- defaultText = "true if profile ID is 0";
- description = "Whether this is a default profile.";
- };
-
- search = {
- force = mkOption {
- type = with types; bool;
- default = false;
- description = ''
- Whether to force replace the existing search
- configuration. This is recommended since Firefox will
- replace the symlink for the search configuration on every
- launch, but note that you'll lose any existing
- configuration by enabling this.
- '';
- };
-
- default = mkOption {
- type = with types; nullOr str;
- default = null;
- example = "DuckDuckGo";
- description = ''
- The default search engine used in the address bar and search bar.
- '';
- };
-
- order = mkOption {
- type = with types; uniq (listOf str);
- default = [ ];
- example = [
- "DuckDuckGo"
- "Google"
- ];
- description = ''
- The order the search engines are listed in. Any engines
- that aren't included in this list will be listed after
- these in an unspecified order.
- '';
- };
-
- engines = mkOption {
- type = with types; attrsOf (attrsOf jsonFormat.type);
- default = { };
- example = literalExpression ''
- {
- "Nix Packages" = {
- urls = [{
- template = "https://search.nixos.org/packages";
- params = [
- { name = "type"; value = "packages"; }
- { name = "query"; value = "{searchTerms}"; }
- ];
- }];
-
- icon = "''${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
- definedAliases = [ "@np" ];
- };
-
- "NixOS Wiki" = {
- urls = [{ template = "https://nixos.wiki/index.php?search={searchTerms}"; }];
- iconUpdateURL = "https://nixos.wiki/favicon.png";
- updateInterval = 24 * 60 * 60 * 1000; # every day
- definedAliases = [ "@nw" ];
- };
-
- "Bing".metaData.hidden = true;
- "Google".metaData.alias = "@g"; # builtin engines only support specifying one additional alias
- }
- '';
- description = ''
- Attribute set of search engine configurations. Engines
- that only have metaData specified will
- be treated as builtin to Firefox.
-
- See SearchEngine.jsm
- in Firefox's source for available options. We maintain a
- mapping to let you specify all options in the referenced
- link without underscores, but it may fall out of date with
- future options.
-
- Note, icon is also a special option
- added by Home Manager to make it convenient to specify
- absolute icon paths.
- '';
- };
- };
-
- extensions = mkOption {
- type = types.listOf types.package;
- default = [ ];
- example = literalExpression ''
- with firefox-addons; [
- privacy-badger
- ]
- '';
- description = ''
- List of Firefox add-on packages to install for this profile.
- Some pre-packaged add-ons are accessible from NUR,
- .
- Once you have NUR installed run
-
-
- $ nix-env -f '<nixpkgs>' -qaP -A firefox-addons
-
-
- to list the available Firefox add-ons.
-
-
-
- Note that it is necessary to manually enable these extensions
- inside Firefox after the first installation.
- '';
- };
- };
- }
- )
- );
- default = { };
- description = "Attribute set of Firefox profiles.";
- };
- };
- };
-
- config = mkIf cfg.enable {
- assertions = [
- (
- let
- defaults = catAttrs "name" (filter (a: a.isDefault) (attrValues cfg.profiles));
- in
- {
- assertion = cfg.profiles == { } || length defaults == 1;
- message =
- "Must have exactly one default Firefox profile but found "
- + toString (length defaults)
- + optionalString (length defaults > 1) (", namely " + concatStringsSep ", " defaults);
- }
- )
-
- (
- let
- duplicates = filterAttrs (_: v: length v != 1) (
- zipAttrs (mapAttrsToList (n: v: { "${toString v.id}" = n; }) (cfg.profiles))
- );
-
- mkMsg = n: v: " - ID ${n} is used by ${concatStringsSep ", " v}";
- in
- {
- assertion = duplicates == { };
- message =
- ''
- Must not have Firefox profiles with duplicate IDs but
- ''
- + concatStringsSep "\n" (mapAttrsToList mkMsg duplicates);
- }
- )
- ];
-
- warnings = optional (cfg.enableGnomeExtensions or false) ''
- Using 'programs.${browser.name}.enableGnomeExtensions' has been deprecated and
- will be removed in the future. Please change to overriding the package
- configuration using 'programs.${browser.name}.package' instead. You can refer to
- its example for how to do this.
- '';
-
- home.packages =
- [ cfg.package ]
- ++ (
- if cfg.createProfileBins then
- (mapAttrsToList (name: profile: (mkProfileBin name)) (
- filterAttrs (_: p: !p.isDefault) cfg.profiles
- ))
- else
- [ ]
- );
-
- home.file = mkMerge (
- [ { "${dataConfigPath}/profiles.ini" = mkIf (cfg.profiles != { }) { text = profilesIni; }; } ]
- ++ flip mapAttrsToList cfg.profiles (
- _: profile: {
- "${profilesPath}/${profile.path}/.keep".text = "";
-
- "${profilesPath}/${profile.path}/chrome/userChrome.css" = mkIf (profile.userChrome != "") {
- text = profile.userChrome;
- };
-
- "${profilesPath}/${profile.path}/chrome/userContent.css" = mkIf (profile.userContent != "") {
- text = profile.userContent;
- };
-
- "${profilesPath}/${profile.path}/user.js" = mkIf (
- profile.settings != { } || profile.extraConfig != "" || profile.bookmarks != [ ]
- ) { text = mkUserJs profile.settings profile.extraConfig profile.bookmarks; };
-
- "${profilesPath}/${profile.path}/search.json.mozlz4" =
- mkIf
- (profile.search.default != null || profile.search.order != [ ] || profile.search.engines != { })
- {
- force = profile.search.force;
- source =
- let
- settings = {
- version = 6;
- engines =
- let
- # Map of nice field names to internal field names.
- # This is intended to be exhaustive and should be
- # updated at every version bump.
- internalFieldNames =
- (genAttrs [
- "name"
- "isAppProvided"
- "loadPath"
- "hasPreferredIcon"
- "updateInterval"
- "updateURL"
- "iconUpdateURL"
- "iconURL"
- "iconMapObj"
- "metaData"
- "orderHint"
- "definedAliases"
- "urls"
- ] (name: "_${name}"))
- // {
- searchForm = "__searchForm";
- };
-
- processCustomEngineInput =
- input:
- (removeAttrs input [ "icon" ])
- // optionalAttrs (input ? icon) {
- # Convenience to specify absolute path to icon
- iconURL = "file://${input.icon}";
- }
- // (
- optionalAttrs (input ? iconUpdateURL) {
- # Convenience to default iconURL to iconUpdateURL so
- # the icon is immediately downloaded from the URL
- iconURL = input.iconURL or input.iconUpdateURL;
- }
- // {
- # Required for custom engine configurations, loadPaths
- # are unique identifiers that are generally formatted
- # like: [source]/path/to/engine.xml
- loadPath = ''[home-manager]/programs.${browser.name}.profiles.${profile.name}.search.engines."${
- replaceStrings [ "\\" ] [ "\\\\" ] input.name
- }"'';
- }
- );
-
- processEngineInput =
- name: input:
- let
- requiredInput = {
- inherit name;
- isAppProvided = input.isAppProvided or removeAttrs input [ "metaData" ] == { };
- metaData = input.metaData or { };
- };
- in
- if requiredInput.isAppProvided then
- requiredInput
- else
- processCustomEngineInput (input // requiredInput);
-
- buildEngineConfig =
- name: input:
- mapAttrs' (name: value: {
- name = internalFieldNames.${name} or name;
- inherit value;
- }) (processEngineInput name input);
-
- sortEngineConfigs =
- configs:
- let
- buildEngineConfigWithOrder =
- order: name:
- let
- config =
- configs.${name} or {
- _name = name;
- _isAppProvided = true;
- _metaData = { };
- };
- in
- config
- // {
- _metaData = config._metaData // {
- inherit order;
- };
- };
-
- engineConfigsWithoutOrder = attrValues (removeAttrs configs profile.search.order);
-
- sortedEngineConfigs =
- (imap buildEngineConfigWithOrder profile.search.order) ++ engineConfigsWithoutOrder;
- in
- sortedEngineConfigs;
-
- engineInput = profile.search.engines // {
- # Infer profile.search.default as an app provided
- # engine if it's not in profile.search.engines
- ${profile.search.default} = profile.search.engines.${profile.search.default} or { };
- };
- in
- sortEngineConfigs (mapAttrs buildEngineConfig engineInput);
-
- metaData =
- optionalAttrs (profile.search.default != null) {
- current = profile.search.default;
- hash = "@hash@";
- }
- // {
- useSavedOrder = profile.search.order != [ ];
- };
- };
-
- # Home Manager doesn't circumvent user consent and isn't acting
- # maliciously. We're modifying the search outside of Firefox, but
- # a claim by Mozilla to remove this would be very anti-user, and
- # is unlikely to be an issue for our use case.
- disclaimer =
- appName:
- "By modifying this file, I agree that I am doing so "
- + "only within ${appName} itself, using official, user-driven search "
- + "engine selection processes, and in a way which does not circumvent "
- + "user consent. I acknowledge that any attempt to change this file "
- + "from outside of ${appName} is a malicious act, and will be responded "
- + "to accordingly.";
-
- salt =
- if profile.search.default != null then
- profile.path + profile.search.default + disclaimer "Firefox"
- else
- null;
- in
- pkgs.runCommand "search.json.mozlz4"
- {
- nativeBuildInputs = with pkgs; [
- mozlz4a
- openssl
- ];
- json = builtins.toJSON settings;
- inherit salt;
- }
- ''
- if [[ -n $salt ]]; then
- export hash=$(echo -n "$salt" | openssl dgst -sha256 -binary | base64)
- mozlz4a <(substituteStream json search.json.in --subst-var hash) "$out"
- else
- mozlz4a <(echo "$json") "$out"
- fi
- '';
- };
-
- "${profilesPath}/${profile.path}/extensions" = mkIf (profile.extensions != [ ]) {
- source =
- let
- extensionsEnvPkg = pkgs.buildEnv {
- name = "hm-${browser.name}-extensions";
- paths = profile.extensions;
- };
- in
- "${extensionsEnvPkg}/share/mozilla/${extensionPath}";
- recursive = true;
- force = true;
- onChange = ''
- # Includes references to old versions that are shown in UI.
- rm -f "${profilesPath}/${profile.path}/extensions.json"
- '';
- };
- }
- )
- );
- };
- };
}
diff --git a/home/modules/firefox/firefox.nix b/home/modules/firefox/firefox.nix
index 4e2ab4c..0aad241 100644
--- a/home/modules/firefox/firefox.nix
+++ b/home/modules/firefox/firefox.nix
@@ -1,18 +1,20 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
+_:
let
- common = import ./common.nix { inherit config lib pkgs; };
+ mkFirefoxProfileBinModule = import ./mkFirefoxProfileBinModule.nix;
+
+ modulePath = [
+ "programs"
+ "firefox"
+ ];
+ name = "Firefox";
+ packageName = "firefox";
in
-common.mkModule {
- name = "firefox";
- displayName = "Firefox";
- dataConfigPath = ".mozilla/firefox";
- defaultPackage = pkgs.firefox;
- defaultPackageName = "pkgs.firefox";
- isSecure = false;
+{
+ imports = [
+ (mkFirefoxProfileBinModule {
+ inherit modulePath name packageName;
+ isSecure = true;
+ })
+ ];
}
diff --git a/home/modules/firefox/mkFirefoxModuleCompat.nix b/home/modules/firefox/mkFirefoxModuleCompat.nix
new file mode 100644
index 0000000..dabeac4
--- /dev/null
+++ b/home/modules/firefox/mkFirefoxModuleCompat.nix
@@ -0,0 +1,67 @@
+{ modulePath, ... }@moduleArgs:
+
+{
+ inputs,
+ config,
+ lib,
+ ...
+}:
+
+let
+ mkFirefoxModule = import "${inputs.home-manager.outPath}/modules/programs/firefox/mkFirefoxModule.nix";
+
+ cfg = lib.getAttrFromPath modulePath config;
+
+ # HINT home-manager's Firefox module uses a read-only `finalPackage` option
+ # that creates a wrapper around `package`. However, this wrapper is not
+ # compatible with all Firefox-based browser packages. Thus, we adjust the module
+ # to always set `finalPackage` to `package` & remove unsupported options.
+ fixFirefoxModuleCompat =
+ module:
+ {
+ config,
+ lib,
+ pkgs,
+ ...
+ }:
+ let
+ optionsPath = [ "options" ] ++ modulePath;
+ configPath = [
+ "config"
+ "content" # due to mkIf
+ ] ++ modulePath;
+ in
+ lib.updateManyAttrsByPath
+ [
+ {
+ path = optionsPath ++ [ "languagePacks" ];
+ update = old: { };
+ }
+ {
+ path = configPath ++ [ "finalPackage" ];
+ update = old: cfg.package;
+ }
+ {
+ path = configPath ++ [ "policies" ];
+ update = old: { };
+ }
+ ]
+ (module {
+ inherit config lib pkgs;
+ });
+in
+{
+ imports = [
+ (fixFirefoxModuleCompat (mkFirefoxModule moduleArgs))
+ ];
+
+ options = lib.setAttrByPath modulePath { };
+
+ config = lib.mkIf cfg.enable (
+ { }
+ // lib.setAttrByPath modulePath {
+ # Tor & Mullvad Browser don't support profile version 2 yet
+ profileVersion = null;
+ }
+ );
+}
diff --git a/home/modules/firefox/mkFirefoxProfileBinModule.nix b/home/modules/firefox/mkFirefoxProfileBinModule.nix
new file mode 100644
index 0000000..ebae797
--- /dev/null
+++ b/home/modules/firefox/mkFirefoxProfileBinModule.nix
@@ -0,0 +1,67 @@
+{
+ modulePath,
+ name,
+ packageName,
+ isSecure ? false,
+}:
+
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ appName = name;
+ cfg = lib.getAttrFromPath modulePath config;
+
+ mkProfileBin =
+ profileName: profile:
+ let
+ pname = "${packageName}-${profileName}";
+ scriptBin = pkgs.writeScriptBin pname ''
+ ${packageName} -P "${profileName}" --name="${pname}" $@
+ '';
+ desktopFile = pkgs.makeDesktopItem {
+ name = pname;
+ exec = "${scriptBin}/bin/${pname} %U";
+ icon = packageName;
+ extraConfig.StartupWMClass = pname;
+ desktopName = "${appName} (${profileName})";
+ genericName = "Web Browser";
+ categories = [
+ "Network"
+ "WebBrowser"
+ ] ++ lib.optional isSecure "Security";
+ };
+ in
+ pkgs.runCommand pname { } ''
+ mkdir -p $out/{bin,share}
+ cp -r ${scriptBin}/bin/${pname} $out/bin/${pname}
+ cp -r ${desktopFile}/share/applications $out/share/applications
+ '';
+in
+{
+ options = lib.setAttrByPath modulePath {
+ createProfileBins = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ When enabled installs a binary for all non-default profiles named `${packageName}-''${profile}`.
+ This also includes a `.desktop` file that is configured to show separate icons (on GNOME at least).
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable (
+ {
+ home.packages =
+ if cfg.createProfileBins then
+ (lib.mapAttrsToList mkProfileBin) (lib.filterAttrs (_: p: !p.isDefault) cfg.profiles)
+ else
+ [ ];
+ }
+ // lib.setAttrByPath modulePath { }
+ );
+}
diff --git a/home/modules/firefox/mullvad-browser.nix b/home/modules/firefox/mullvad-browser.nix
index 7ad5f77..8542da4 100644
--- a/home/modules/firefox/mullvad-browser.nix
+++ b/home/modules/firefox/mullvad-browser.nix
@@ -1,18 +1,31 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
+_:
let
- common = import ./common.nix { inherit config lib pkgs; };
+ mkFirefoxModuleCompat = import ./mkFirefoxModuleCompat.nix;
+ mkFirefoxProfileBinModule = import ./mkFirefoxProfileBinModule.nix;
+
+ modulePath = [
+ "programs"
+ "mullvad-browser"
+ ];
+ name = "Mullvad Browser";
+ packageName = "mullvad-browser";
in
-common.mkModule {
- name = "mullvad-browser";
- displayName = "Mullvad Browser";
- dataConfigPath = ".mullvad/mullvadbrowser";
- defaultPackage = pkgs.mullvad-browser;
- defaultPackageName = "pkgs.mullvad-browser";
- isSecure = true;
+{
+ imports = [
+ (mkFirefoxModuleCompat {
+ inherit modulePath name;
+ description = "Privacy-focused browser made in a collaboration between The Tor Project and Mullvad";
+ unwrappedPackageName = packageName;
+ visible = true;
+ platforms.linux = rec {
+ vendorPath = ".mullvad";
+ configPath = "${vendorPath}/mullvadbrowser";
+ };
+ })
+ (mkFirefoxProfileBinModule {
+ inherit modulePath name packageName;
+ isSecure = true;
+ })
+ ];
}
diff --git a/home/modules/firefox/tor-browser.nix b/home/modules/firefox/tor-browser.nix
index 9626100..ee9ec23 100644
--- a/home/modules/firefox/tor-browser.nix
+++ b/home/modules/firefox/tor-browser.nix
@@ -1,18 +1,31 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
+_:
let
- common = import ./common.nix { inherit config lib pkgs; };
+ mkFirefoxModuleCompat = import ./mkFirefoxModuleCompat.nix;
+ mkFirefoxProfileBinModule = import ./mkFirefoxProfileBinModule.nix;
+
+ modulePath = [
+ "programs"
+ "tor-browser"
+ ];
+ name = "Tor Browser";
+ packageName = "tor-browser";
in
-common.mkModule {
- name = "tor-browser";
- displayName = "Tor Browser";
- dataConfigPath = ".tor project/firefox";
- defaultPackage = pkgs.tor-browser;
- defaultPackageName = "pkgs.tor-browser";
- isSecure = true;
+{
+ imports = [
+ (mkFirefoxModuleCompat {
+ inherit modulePath name;
+ description = "Privacy-focused browser routing traffic through the Tor network";
+ unwrappedPackageName = packageName;
+ visible = true;
+ platforms.linux = rec {
+ vendorPath = ".tor project";
+ configPath = "${vendorPath}/firefox";
+ };
+ })
+ (mkFirefoxProfileBinModule {
+ inherit modulePath name packageName;
+ isSecure = true;
+ })
+ ];
}
diff --git a/hosts/home-server.nix b/hosts/home-server.nix
index 760e49c..db971ed 100644
--- a/hosts/home-server.nix
+++ b/hosts/home-server.nix
@@ -81,8 +81,8 @@ in
''}";
services.inadyn.ipv6.enable = true;
services.inadyn.ipv6.command = "${pkgs.writeScript "get-ipv6" ''
- ${pkgs.tailscale}/bin/tailscale status --json \
- | ${pkgs.jq}/bin/jq -r '.Self.Addrs' \
+ ${pkgs.iproute2}/bin/ip -6 addr show scope global \
+ | ${pkgs.grepcidr}/bin/grepcidr '2000::/3' \
| grep -o '[0-9a-f:]*::102'
''}";
services.inadyn.domains = [