uBlock Origin settings

Can’t set some settings declaratively in Librewolf. “selectedFilterLists” is fine, but “userSettings” stays default. I’m doing the same according to Firefox NixOS wiki page and to uBlock Origin json syntax, I think. What am I doing wrong?

extensions = {
  force = true;
  settings = {
    "uBlock0@raymondhill.net".settings = {
      userSettings = rec {
# not working too userSettings = {
        uiTheme = "dark";
      };
      selectedFilterLists = [
        "JPN-1"
      ];
    };
  };
}

Check the “Support” panel in uBlock origin extension settings - at the bottom, theres a debug list. There, I can see the userSettings field. Maybe start with verifying if your settings from Nix show up there.

You’re saying that there’s no “uiTheme“ field, right? I didn’t know that settings that stored in backup file are different from settings in debug list. Okay, then it’s not all settings are reproducible? If so, that seems weird because I can set all of my settings from backup file.

I had debugged a similar issue and through much gnashing of teeth found that, only for the key-values under userSettings, everything under userSettings needs to be hoisted one level up.

extensions = {
  force = true;
  settings = {
    "uBlock0@raymondhill.net".settings = {

      # Do not do this
      # userSettings = rec {
      #  uiTheme = "dark";
      # };
      #
      # Instead, move the contents up a level
      uiTheme = "dark";

      # All other values (not under `userSettings`) stay the same
      selectedFilterLists = [
        "JPN-1"
      ];
    };
  };
}
5 Likes

Thank you, it worked, but did you figure out how to set user filters? I want it to be like this:

{
userFilters = “noai.duckduckgo.com##.set-browser-promo\nnoai.duckduckgo.com##.home-callouts-module-scss-module___U5DDa__callouts";
}

Put that string into a list toOverwrite.filters:
https://github.com/gorhill/uBlock/wiki/Deploying-uBlock-Origin:-configuration#filters

1 Like

That does not seem to work :frowning:

extensions.settings."uBlock0@raymondhill.net" = {
  force = true;
  settings = {
    toOverwrite.filters = "noai.duckduckgo.com##.set-browser-promo\nnoai.duckduckgo.com##.home-callouts-module-scss-module___U5DDa__callouts";
  };
}

Nevermind. Looks like you need to put "user-filters" in selectedFilterLists and put the user filters in a userFilters option adjacent to it (so like if you have foo.selectedFilterLists this would be foo.userFilters).
If that doesn’t work I don’t know :head_shaking_horizontally:.

1 Like

yeah i ran into this same wall with ublock settings, ended up just letting the extension keep its own state instead of forcing everything declaratively. not pretty but it stopped fighting me. the user-filters vs userFilters thing is easy to miss too

selectedFilterLists = [ “user-filters” ] is not required because that’s already default behaviour and userFilters = "<filters>" does not seem to work.

IDK then :person_shrugging:. Assuming everything is showing up right in uBO support/about:policies, then there might be a uBO bug.

I hit this issue when I was trying to declaratively turn on uBlock Origin’s Medium Mode via Home Manager’s module programs.librewolf.profiles.<my-profile-name>.extensions.settings."uBlock0@raymondhill.net".settings. To ensure the Nix code is actually doing what it should be, impermanence is wiping ~/.librewolf every time the computer reboots.

I also started with making a backup file through uBlock Origin’s “Back up to file” button in the extension’s GUI dashboard and tried simply translating the resulting JSON file settings to Nix and found userSettings.advancedUserEnabled was not sticking.

I initially solved the weirdness with userSettings by directly observing where uBlock Origin settings are stored. The Home Manager module for Librewolf (and similarly for Firefox and other Firefox derivatives) puts them in a JSON file at ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js (apparently this storage location and method is Firefox legacy behavior that the Home Manager module explicitly enables, and this may stop working due to Firefox changes in the future).

In short, you can first save storage.js to another path, ignoring certain uninteresting cache values and third-party lists values; then make a change to uBlock Origin settings (e.g. use element picker mode to select something to block on the current webpage and thus generate a user filter); then see what exact JSON values change by diffing the current storage.js with the previously-saved storage.js. The JSON objects that changed are exactly what you should put into programs.librewolf.profiles.<my-profile-name>.extensions.settings.

To directly answer the question about user filters, the solution is again placing a differently-named key in the correct position. “user-filters” go in the top level:

extensions = {
  force = true;
  settings = {
    "uBlock0@raymondhill.net".settings = {

      # Do not do this
      # userSettings = rec {
      #  uiTheme = "dark";
      # };
      #
      # Instead, move the contents up a level
      uiTheme = "dark";

      # All other values (not under `userSettings`) stay the same
      selectedFilterLists = [
        "JPN-1"
      ];

      # User filters go here
      # These appear in uBlock Origin's dashboard in "My filters" pane
      "user-filters" = "noai.duckduckgo.com##.set-browser-promo\nnoai.duckduckgo.com##.home-callouts-module-scss-module___U5DDa__callouts";
    };
  };
}


I’ll post my rabbit hole dive to potentially help someone else achieve declarative nirvana.

Versions:

  • Librewolf: 154.0.1-3
  • uBlock Origin: 1.74.0

(<my-profile-name> is a placeholder for the Librewolf profile name)

The goal is to have turn on Medium mode in uBlock Origin, and have that persist across reboots in the presence of Impermanece wiping ~/.librewolf at every reboot.

  1. Realize uBlock Origin has a “Back up to file” button whose output unfortunately does not directly map to programs.librewolf.profiles.<my-profile-name>.extensions.settings

I turned on Medium Mode and got this “backup” file from uBlock Origin by clicking the “Back up to file” button:

{
  "timeStamp": 1788134331842,
  "version": "1.74.0",
  "userSettings": {
    "advancedUserEnabled": true,
    "importedLists": [],
    "popupPanelSections": 31
  },
  "selectedFilterLists": [
    "user-filters",
    "ublock-filters",
    "ublock-badware",
    "ublock-privacy",
    "ublock-unbreak",
    "ublock-quick-fixes",
    "easylist",
    "adguard-spyware-url",
    "easyprivacy",
    "urlhaus-1",
    "curben-phishing",
    "plowe-0",
    "LegitimateURLShortener"
  ],
  "hiddenSettings": {},
  "whitelist": [
    "chrome-extension-scheme",
    "moz-extension-scheme"
  ],
  "dynamicFilteringString": "behind-the-scene * * noop\nbehind-the-scene * inline-script noop\nbehind-the-scene * 1p-script noop\nbehind-the-scene * 3p-script noop\nbehind-the-scene * 3p-frame noop\nbehind-the-scene * image noop\nbehind-the-scene * 3p noop\n* * 3p-script block\n* * 3p-frame block",
  "urlFilteringString": "",
  "hostnameSwitchesString": "no-large-media: behind-the-scene false\nno-csp-reports: * true",
  "userFilters": ""
}

The next step is to translate these settings into a Nix config using the Home Manager module for Librewolf.

At this point, `timestamp` and `version` do not look particularly useful for my purposes, so I left those out.


  # uBlock Origin extension settings, Attempt 1
  # Default uBlock Origin settings, except with medium mode
  programs.librewolf.profiles."<my-profile-name>".extensions = {
    force = true;
    settings."uBlock0@raymondhill.net".settings = {
      "userSettings" = {
        "advancedUserEnabled" = true;
        "importedLists" = [];
        "popupPanelSections" = 31;
      };
  
      "selectedFilterLists" = [
        "user-filters"
        "ublock-filters"
        "ublock-badware"
        "ublock-privacy"
        "ublock-unbreak"
        "ublock-quick-fixes"
        "easylist"
        "adguard-spyware-url"
        "easyprivacy"
        "urlhaus-1"
        "curben-phishing"
        "plowe-0"
        "LegitimateURLShortener"
      ];
      "hiddenSettings" = {};
      "whitelist" = [
        "chrome-extension-scheme"
        "moz-extension-scheme"
      ];
      "dynamicFilteringString" = "behind-the-scene * * noop\nbehind-the-scene * inline-script noop\nbehind-the-scene * 1p-script noop\nbehind-the-scene * 3p-script noop\nbehind-the-scene * 3p-frame noop\nbehind-the-scene * image noop\nbehind-the-scene * 3p noop\n* * 3p-script block\n* * 3p-frame block";
      "urlFilteringString" = "";
      "hostnameSwitchesString" = "no-large-media: behind-the-scene false\nno-csp-reports: * true";
      "userFilters" = "";
    };
  };
  

Run nixos-rebuild boot, reboot the machine to wipe ~/.librewolf, launch Librewolf, and… find out medium mode is no longer turned on.

Opening uBlock Origin’s dashboard immediately reveals “I am an advanced user” box is not checked anymore. However, I can see the two dynamic filters (under “My rules” pane) are still present.

(This is the first hint that all settings under userSettings in the Nix code do nothing, even though the backup file that uBlock Origin generates has userSettings).

  1. Realize uBlock Origin has a “Support” tab in its dashboard with “Troubleshooting Information” that also does not directly map to programs.librewolf.profiles.<my-profile-name>.extensions.settings

Essentially the same problem as “Back up to file” where the “Troubleshooting Information” values are formatted like JSON but directly translating these to Nix in the Home Manager module for uBlock Origin does not work. userSettings appears here too, and if I check “I am an advanced user” again, advancedUserEnabled: true shows up as a subkey to userSettings here as well. But this is exactly the same non-working situation as attempt 1.

With no other ideas, I decided to look at how Home Manager implements the Librewolf module.

  1. Inspect the Home Manager module for Librewolf

This file itself is quite sparse; notice that most of the heavy lifting is in the function mkFirefoxModule defined in another file.

In this massive module I searched for “extension”, backwards because I am looking for the implementation details of how the module translates Nix code to uBlock Origin settings and Nix modules usually put those at the end of modules. Line 1308 "${cfg.profilesPath}/${profile.path}/browser-extension-data/${name}/storage.js" = { has the crucial hint that this storage.js might contain extension settings (which ends up to be true).

  1. Inspect ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js

This is a rather large JSON file. Take a look at its contents with:

cat ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js | jq

This spits out loads of data, including what looks like the contents of third-party block lists like Easylist.

# Read extension store JSON and pipe to `jq`
# `--sort-keys` helps with the later step of diffing this JSON object with another, similar JSON object...
# The `jq` filter removes uninteresting cache data and massive third-party "Filter lists" that uBlock Origin automatically downloads. These are the user-selected lists under "Filter lists" tab in uBlock Origin's settings GUI
# Unfortunately, the result still has some uninteresting data.
cat ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js \
| jq --sort-keys 'with_entries(select( .key | startswith("cache/") | not ))'

I won’t copy the complete output of this command here because it is quite large and still contains data I do not care about (and my jq skills are too lacking to figure out how to filter those data out).

Inspecting the output manually, I find an interesting value…

  "userSettings": {
    "advancedUserEnabled": true,
    "importedLists": [],
    "popupPanelSections": 31
  },

So this is evidence that the Nix code is indeed setting advancedUserEnabled: true but it does not have the desired effect; “I am an advanced user” is still unchecked.

Regardless, storage.js does seem to be where uBlock Origin settings exist. Let’s see if checking “I am an advanced user” will change anything in storage.js

Save storage.js as it is now:

mkdir /tmp/ublock-origin-settings
cd /tmp/ublock-origin-settings

cat ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js \
| jq --sort-keys 'with_entries(select( .key | startswith("cache/") | not ))' > storage-before.js

Then, in uBlock Origin’s dashboard in Librewolf, check “I am an advanced user” and save storage.js again…

cat ~/.librewolf/<my-profile-name>/browser-extension-data/uBlock0@raymondhill.net/storage.js \
| jq --sort-keys 'with_entries(select( .key | startswith("cache/") | not ))' > storage-after.js

Then, look at the diff with diff storage-before.js storage-after.js. There is a lot of data that appears to be related to modification times and updates to block lists that I don’t care about, but right at the top of the diff…

>   "advancedUserEnabled": true,

…I see advancedUserEnabled by itself at the top level, not under “userSettings”. This is the big hint that all values under userSettings need to be hoisted to the top level.

Test the idea by changing the Home Manager module settings:

  # uBlock Origin extension settings, final attempt
  # Default uBlock Origin settings, except with medium mode
  programs.librewolf.profiles."<my-profile-name>".extensions = {
    force = true;
    settings."uBlock0@raymondhill.net".settings = {
      # Do not do this
      # "userSettings" = {
      #  "advancedUserEnabled" = true;
      #  "importedLists" = [];
      #  "popupPanelSections" = 31;
      # };
      #
      # Instead, hoist all values under `userSettings` to the top level
      "advancedUserEnable" = true;
      "importredLists" = [];
      "popupPanelSections" = 31;
  
      "selectedFilterLists" = [
        "user-filters"
        "ublock-filters"
        "ublock-badware"
        "ublock-privacy"
        "ublock-unbreak"
        "ublock-quick-fixes"
        "easylist"
        "adguard-spyware-url"
        "easyprivacy"
        "urlhaus-1"
        "curben-phishing"
        "plowe-0"
        "LegitimateURLShortener"
      ];
      "hiddenSettings" = {};
      "whitelist" = [
        "chrome-extension-scheme"
        "moz-extension-scheme"
      ];
      "dynamicFilteringString" = "behind-the-scene * * noop\nbehind-the-scene * inline-script noop\nbehind-the-scene * 1p-script noop\nbehind-the-scene * 3p-script noop\nbehind-the-scene * 3p-frame noop\nbehind-the-scene * image noop\nbehind-the-scene * 3p noop\n* * 3p-script block\n* * 3p-frame block";
      "urlFilteringString" = "";
      "hostnameSwitchesString" = "no-large-media: behind-the-scene false\nno-csp-reports: * true";
      "userFilters" = "";
    };
  };
  

Run nixos-rebuild boot, wipe the runtime Librewolf profile folder by rebooting the machine, launch Librewolf, and I can see that medium mode is now persistently turned on.

I did the same thing to discover user-filters is the correct attr to use for user filters (the stuff under “My filters” pane in uBlock Origin dashboard).


There is one thing left to do. Using medium mode means I often add dynamic filtering rules in order to unbreak many (almost all) websites. So let’s script the entire process!

(I wrote the script in Nushell because Bash scripting has been a thorn on my behind for far too long. The script should be simple enough to port to Bash if that is what you want.)

  programs.librewolf.profiles."<my-profile-name>".extensions = {
    force = true;
    settings."uBlock0@raymondhill.net".settings = {
      # The settings for uBlock Origin are designed to be modified through uBlock Origin's GUI within the browser and then exposed to Nix through a JSON file generated by an external script.
      settings =
      let
          ublockOriginSettingsJSONFile = ublock-origin_settings.json;

      in if (builtins.pathExists ublockOriginSettingsJSONFile) then {
          "uBlock0@raymondhill.net".settings = builtins.fromJSON (builtins.readFile ublockOriginSettingsJSONFile);
      } else {};

    };
  };
  
#! /usr/bin/env nu
#
# From the current user's Librewolf installation, get its uBlock Origin settings and update the equivalent declarative settings
#
# Currently supports only the Librewolf profile name "default"
#
# NOTE: This script's shebang line is deliberately not `#! /usr/bin/env nix-shell`. Although using a `nix-shell` shebang will ensure the running machine will have the necessary interpreter and dependencies, `nix-shell` was found to be significantly slower to launch than a plain `#! /usr/bin/env <interpreter>`.

const pathSelfDir = path self | path dirname


let runtimeUBlockSettings = $env.HOME | path join ".librewolf/default/browser-extension-data/uBlock0@raymondhill.net/storage.js"


let UBlockSettingsJSON = $pathSelfDir | path join $"ublock-origin_settings.json"

# This implementation uses a whitelist of values to get the interesting values from `storage.js`, and some desirable keys may be missing.
# Those missing keys will need to be manually found and added to this list.
open $runtimeUBlockSettings
| jq --sort-keys '{
	# The following key-value pairs are hoisted from `userSettings`
	advancedUserEnabled,
	importedLists,
	popupPanelSections,

	dynamicFilteringString,
	hiddenSettings,
	hostnameSwitchesString,
	selectedFilterLists,
	urlFilteringString,
	"user-filters",
	userFilters,
	whitelist,

}'
| save --force $UBlockSettingsJSON

Run the script whenever you change uBlock Origin settings, and the updated settings should appear in ublock-origin_settings.json and automatically picked up by the Nix config.

4 Likes

Did you use an LLM for this? Asking because from my personal experience they like to add unnecessary quotes around attrset keys with dashes like that, and that wall of text feels a bit suspicious. Nothing against LLM usage, but make sure to follow the Discourse LLM Policy.

And sorry for accusing you if you did not!

1 Like

No LLMs were used at all in my post, not in the writing of the post and not in the investigation of the issue. I am just very used to putting quotes around strings that have hyphens because some programming languages disallow hyphens in variable names (e.g. Python, https://stackoverflow.com/questions/2064329/why-does-python-disallow-usage-of-hyphens-within-function-and-variable-names).

I actually did not know Nix allowed hyphens in attr names until now.

4 Likes

OK, just someone making the same “mistake” (technically allowed but not idiomatic) for the same reason LLMs probably do. Sorry about that!