What's the use case for `allowUnfreePredicate` and friends?

In Nixpkgs config, there are a couple of options which also have a more generic “predicate” variant, where instead of providing some list of items, you just write a predicate. This includes: allowUnfreePredicate, allowInsecurePredicate, allowNonSourcePredicate.

When designing RFC 127, I initially also had such a predicate option in mind, but then quickly moved away from it due to its downsides. The major downsides of such predicates are:

  • They are rather verbose to write
  • They are difficult to type check and give good error messages for mistakes
  • They don’t merge well (arguably merging Nixpkgs configuration is not that big of a deal, but it can still happen)
  • They interfere with the non-predicate options. Usually the implementation only uses the predicate internally, with a default predicate which checks the other configuration options. So when specifying a custom predicate, it overrides the default one. So users have to re-implement the default predicate themselves if they want their other related options to keep working.

Due to all of this, I think that such predicates should be avoided at all cost, and that we should find alternative solutions for their use cases. Which leads to my question: Have you ever used one of these options mentioned above, and if so, what for?

2 Likes

Quick addition: one obvious use case is “I don’t want to allow/disallow X in general except for a selected list of packages”. In this case, the obvious solution is to just have a list of package names for that. In fact we are already doing that in a lot of places (permittedInsecurePackages, although that uses both name and version, which is not desired in other scenarios), and I am mildly shocked to notice that there seems to be no equivalent for e.g. unfree packages. So, please assume this is a thing, I am looking for other use cases that would not be handled by that.

2 Likes

I use allowUnfreePredicate to allowlist the non-free packages used in my configuration. If there were an allowUnfreePackages that matched on pname, I’d switch to that.

6 Likes

I used it to filter by a prefix of pname and I vaguely remember that multiple Nvidia-related packages matched a single prefix (but it was just two or three)

The current use case is to filter by pname or a name-prefix as already mentioned, it is also used to workaround a bug in HM/standalaone/flake combination.

https://github.com/nix-community/home-manager/issues/2942

I do the following:

{ pkgs, ... }:
{
  allowAliases = false;
  allowNonSource = false;
  allowNonSourcePredicate = with pkgs.lib; pkg: !(any
    (p: !p.isSource && p != sourceTypes.binaryFirmware && (
      !builtins.elem (getName pkg) [
        "electron"
        "firefox-bin"
        "firefox-beta-bin"
        "firefox-beta-bin-unwrapped"
        "firefox-devedition-bin"
        "firefox-devedition-bin-unwrapped"
        "firefox-release-bin-unwrapped"
        "onlyoffice-documentserver"
        "procyon" # dependency of diffoscope with bloat
      ]
    ))
    (toList pkg.meta.sourceProvenance));
  allowUnfreePredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [
    "code" # vscode fhs
    "corefonts" # used in onlyoffice
    "intel-ocl"
    "steam"
    "steam-original" # steam without fhs
    "steam-run"
    "symbola" # better symbols font than unicode
    "vscode"
  ];
  checkMeta = true;
}

So basically you have a selected list of allowed binary packages, but also allow any firmware packages. This makes sense.

I could also imagine a use-case where certain unfree licenses are accepted. One might have a different opinion on the supposedly unfree SSPL and would like to allow all packages licensed under it. The predicate allows for such custom preferences and I think we should keep it.

I think having the option for pnames in addition to the predicate would be great (I built one myself for my config recently) but the predicate should stay for advanced users. It could be marked as internal perhaps but I don’t think that’s necessary; just state that setting the predicate yourself overrides the other options and that should be fine.

1 Like

A predicate would indeed allow for this, but this can also be solved without using allowlistedLicenses.

I agree it’s a convenient escape hatch to have, but so far I’m failing to see situations where one would absolutely need one. Even the prefix use case described by others could probably be solved through a prefix-matching option, or maybe some wildcarding.

There are none as long as they are considered in advance. The entire point of an escape hatch is not trusting the ability not to miss any cases.

3 Likes

so far I’m failing to see situations where one would absolutely need one.

I’m failing to see how we can be certain there are not situations where you might need one. I’m all for adding options to cover the most common cases more easily but removing the ability to customise at the lowest level because you once thought you had everything covered just seems short sighted to me.

It could, but I disagree on whether it should. Whitelists are overwhelmingly the most common case, so I think there should have been an option for that for a long time, but I’d argue not every possible use case should get its own option.

That just clutters the available whitelisting options, making it hard to figure out how they should be composed (does the prefix whitelist take priority or the infix or the postfix?).

1 Like

The use case is that you cannot combine ANDs and ORs in various combinations without a predicate. I also don’t see much in the way of evidence for the subjective claims in the original post.

1 Like

That’s not a use case, that’s a meta-use case. What would you require this for?

You could have asked me to elaborate more on those instead, and then gotten an answer :person_shrugging:.

This strikes me as the only subjective claim. Which is fair enough, that by itself would not be a good reason to get rid of predicate options.

The other arguments are pretty convincing though (though “difficult to type check” may have been misunderstood as subjective), those are simply real problems these options cause, for apparently minor benefit as the lack of use cases in this thread suggests.

I’d also like to add another somewhat subjective opinion: predicate is a very technical term. I don’t think it’s very inclusive of ESL users, and likely even some native speakers. Even if the term is understood, only a reasonably versed programmer understands how it should be used. A whitelist would be much easier to understand all around.

I also believe this matters, as I suspect things like steam and nvidia are much more common in less technical users’ configurations than other proprietary applications are in configurations intended for devops deployments, so the users most commonly encountering allowUnfreePredicate are likely also the ones who understand it the least.

2 Likes

What would you require this for?

My point is it does not matter, if you have a single AND and OR combined that is already outside the realm of any specialised option (unless we want to cause even more confusion in the API). In fact, even adding a simple version check to the predicate already makes it unreasonable to capture in a specialised option.

So I am 100% against removal of the “predicate” option, and I don’t like that you imply that you can cover every use case with specialised options.

Also, please elaborate on what you mean by “they don’t merge well”.

predicate is a very technical term

As opposed to the perfectly intuitive references to lambda calculus all over the manual?

To be clear I agree that calling things “predicates” is jargon, and we can choose a simpler word, but I would like to avoid bikeshedding the name in a thread where the option itself faces a more exisential threat…

Predicate is not jargon, it is a case where mathematical terminology didn’t even get mangled when translted to programming. Understandability of the word is more determined by knowledge of basic mathematical logic or familiarity with any of the programming paradigms where mentioning predicates is trendy, not so much by a specific language for most languages of European origin (note that it is enough to know the logic/programming in English/French/German/Russian/…, not have a European language as an everyday native one).

«We can’t provide error messages for last-resort functionality» and «last-resort functionality requires first copy-pasting in the implementation of the recommended way in its terms» are not really convincing. It’s not that hard to debug with traceVals when it’s worth the effort (and if it is not, then the recommended way should be used, sure)

«Verbose» well, the default implementation is a great starting point to cut and reassemble.

Oh, and by the way the current permittedInsecurePackages is bad practice, one needs a union of cartesian products of lists of names by lists of vulnerability identifiers. (This is a new usecase I have gained since the time I listed my use cases)

Agreed, the permittedInsecurePackages currently (badly) addresses that aspect by also matching on the package version.

Insecure packages would really benefit from RFC 127; I do have a good idea of how they should look but I’m still unsure about how to migrate towards that.

I sincerely hope this is just a leftover from pre-pname days, and not an attempt to address this, because it is like the exact opposite of what makes sense. Nixpkgs has design decisions that are plainly and clearly not even in the same space as something reasonable, but not that absurdly.

2 Likes