Nix change setting in an application

I am new to Nix, though I think I get the philosophy.
My setup (but that should not matter too much): Mac OS with Nix flake, one profile to control my whole system (just one user, using home manager). All nix files in git.

As I understand Nix’s philosophy, every change on the system should be controlled/done with the nix configuration, so that it is repeatable.
Only when using this one get’s how great this is :slight_smile:

However, some tools, even though having configuration files, make it very hard to do any change outside of the tool.
For example, Logseq’s plugin settings. They can be changed in Logseq’s UI with buttons and sliders - but when the plugin.edn file is managed by nix (and thus a symlink) Logseq complains that it can’t write and the change is not happening. So far so normal - but I don’t see what changes to make to the plugin.edn file via nix.

I wonder how this is handled usually? One is just supposed to know the relevant key-value pairs of a plugin’s developer?

Is there a way so that the tool can write to a file, which is controlled by nix? Thus I could see the delta via git and apply the changes to the nix configuration.
Or is there even a better way to handle this situation?
Or is “control everything via nix” not the right way? Is it in especially MacOS more “control what you can”? (Though Logseq and my problem with it is not MacOS specific.)
I could control the config files by git only - having them on their tool’s original place, and completely unknown to nix. On a Mac, this would work for installing apps as well, which would version-controlled by git as well. But that sounds like a poor-man’s nix, which might lead to problems nix solved already. Or not?

I am grateful for any hint :slight_smile:

I don’t think this is the best way on NixOS, but it is the easiest and seems practical for your “gui should edit the config file” use case. You can make a symlink from your flake to your config folder.

Assuming you have home manager installed,

home-manager.users.your-user-name.home.file = {
  ".config/path/to/logseq-config".source = config.home-manager.users.your-user-name.lib.file.mkOutOfStoreSymlink "${config.home-manager.users.your-user-name.homeDirectory}/path/to/your/flake/logseq-config";
};

After you rebuild, a symlink will be created at ~/.config/path/to/logseq-config that will point to ~/path/to/your/flake/logseq-config.

This way, when the application writes to the config file, you will see the git diff in your flake.

1 Like

Many thanks! Works like charm!

Though I had to make one change: The location of mkOutOfStorageSymLink moved, I refer to it in this way:

{ config, ... }:
let
  link = config.lib.file.mkOutOfStoreSymlink;
in
{ ... }

This even works with directories - in fact I symlinked the directory to the plugins’ settings files, so that I don’t forget one, when I add a new plugin.

I understand that there can be a gap between my recorded (nix & git) definition and the actual applied system configuration: Whenever I change some setting via the logseq gui, it is only known to nix and git after a ‘nix switch’ and ‘git commit’.
But this tradeoff is totally fine with me!

If anyone has another view (and practical solution) to this I am happy to hear it!
Many thanks again, @gepbird, for your brilliant suggestion!

I’m glad it helped you!

Though I had to make one change: The location of mkOutOfStorageSymLink moved, I refer to it in this way:

Oops I made a typo, I forgot a t from mkOutOfStoreSymlink, fixed! Or if you mean by moved, that you don’t need to type out the long home-manager.users.your-user-name, this is because you’re using the standalone home manager installation and in my example I used home manager as a NixOS module.

Whenever I change some setting via the logseq gui, it is only known to nix and git after a ‘nix switch’ and ‘git commit’.

I don’t think this should happen, maybe I’m misunderstanding it, can you explain it in more detail? Your logseq config from your ~/.config is symlinked to your flake, so whenever the config file gets written, you should see the change in git. I have no idea how could a nixos-rebuild switch save your config file. Perhaps it saves when you close the GUI?

Oh, yes, I did not notice the typo until it worked :slight_smile:
I got “error: attribute ‘file’ missing” and read here that the function moved.
Though your explanation (standalone vs module) sounds more correct.

Gladly. I meant this more theoretically: As I understand it, Nix’s philosophy is to declarative describe changes on a system, which are applied with ‘switch’. As with “mkOutOfStorageSymLink” the immutable store only has a symlink, any changes are not noticed by nix. I was wrong in my statement, that nix only notices a change after applying ‘nix switch’. At least I think so, as from nix’s POV the symlink did not change (if there is no other place, or fingerprint, which follows the symlink and records a change, in nix).

But ‘git commit’ would record the change, so that I can reproduce it by checking out the repository when/if I ever apply this to another machine. Using git, I can rollback, etc, as well.

What I meant as the tradeoff between using a tool’s gui to change settings and editing and applying them in nix (‘in store’) is that until I run ‘git commit’ any change done (via the tool) is not recorded.
For example, if my machine fails for good after doing a change and before 'git commit’ting it, that change is lost.

And that is totally fine with me :slight_smile:

I actually wonder, if nix’s approach is practical, not only for my special scenario: In theory I see the benefit of having one central place to declare a system, and apply changes afterwards, so that all is deterministically repeatable.
However, in practice I doubt that one could ever get all configurations of all tools and plugins in an up-to-date way configurable in this central place. Sure, everyone is invited to patch/create a nix config which generates a config file for a specific tool, but the learning curve is too steep to “just do it along the way”. Another alternative is ‘home.file’, which is superior for tools which expect configuration via any editor - because all is in one place, no remembering for locations needed. But it fails if a tool wants you to use its UI - like most bigger tools (IDEs, … probably anything with a UI in opposite to headless tools).

So, probably it would be a better approach to have nix watch the system for changes (maybe limited to specific places, to reduce effort, and either active with a daemon or triggered by a user) and record them, so they can be replayed to have a deterministic system. This way, all changes can be done the same way a user would do them without using nix - thus using nix would be much easier.

Additionally nix doesn’t need git - as it has its own revisioning system. But nearly everyone uses it in combination with git. So nix could as well rely on git.

However, as I am quite new to nix, I don’t claim I fully understand it. This was just my observation when adopting it as I was setting up a new Mac from scratch. Probably I am overlooking something, and with my suggestions above, some other use case might not work anymore. And implementing these might have quite drastically changes.
And it is great, that there is indeed a way (‘mkOutOfStoreageSymLink’) to make, what I described, already happen now! Theoretically I could change my whole configuration this way (but I stick to the traditional way, where I can).

I hope I did not bore you with this wall of text!
Thanks again!