Skip to content

Migrate modules from facts to vars.

For a high level overview about vars see our blog post.

This guide will help you migrate your modules that still use our facts backend to the vars backend.

The vars module and the clan command work in tandem, they should ideally be kept in sync.

Keep Existing Values

In order to keep existing values and move them from facts to vars we will need to set the corresponding option in the vars module:

migrateFact = "fact-name"

This will now check on vars generation if there is an existing fact with the name already present and if that is the case will migrate it to vars.

Let us look at the mapping a little closer. Suppose we have the following fact: facts.services.vaultwarden.secret.admin. This would read as follows: The vaultwarden fact service has the admin secret. In order to migrate this fact we would need to have the following vars configuration:

vars.generators.vaultwarden = {
    migrateFact = "vaultwarden";
    files.admin = {};
};

And this would read as follows: The vaultwarden vars module generates the admin file.

Prompts

Because prompts can be a necessity for certain systems vars have a shorthand for defining them. A prompt is a request for user input. Let us look how user input used to be handled in facts:

facts.services.forgejo-api = {
    secret.token = {};
    generator.prompt = "Please insert your forgejo api token";
    generator.script = "cp $prompt_value > $secret/token";
};
To have analogous functionality in vars:
vars.generators.forgejo-api = {
    prompts.token = {
        description = "Please insert your forgejo api token"
        persist = true;
    };
};
This does not only simplify prompting, it also now allows us to define multiple prompts in one generator. A more analogous way to the fact method is available, in case the module author needs more flexibility with the prompt input:

vars.generators.forgejo-api = {
    files.token = {};
    prompts.token.description = "Please insert your forgejo api token";
    script = "cp $prompts/<name> $out/<name>";
};

Migration of a complete module

Let us look closer at how we would migrate an existing generator for syncthing. This is the fact module of syncthing:

facts.services.syncthing = {
  secret.key = {};
  secret.cert = {};
  public.id = {};

  generator.path = [
    pkgs.coreutils
    pkgs.gnugrep
    pkgs.syncthing
  ];

  generator.script = ''
    syncthing generate --config "$out"
    mv "$out"/key.pem "$secret"/key
    mv "$out"/cert.pem "$secret"/cert
    cat "$out"/config.xml | grep -oP '(?<=<device id=")[^"]+' | uniq > "$public"/id
  '';
};

This would be the corresponding vars module, which also will migrate existing facts.

vars.generators.syncthing = {
  migrateFact = "syncthing";

  files.key = {};
  files.cert = {};
  files.id.secret = false;

  runtimeInputs = [
    pkgs.coreutils
    pkgs.gnugrep
    pkgs.syncthing
  ];

  script = ''
    syncthing generate --config "$out"
    mv "$out"/key.pem "$out"/key
    mv "$out"/cert.pem "$out"/cert
    cat "$out"/config.xml | grep -oP '(?<=<device id=")[^"]+' | uniq > "$out"/id
  '';
};
Most of the usage patterns stay the same, but vars have a more ergonomic interface. There are not two different ways to define files anymore (public/secret). Now files are defined under the files attribute and are secret by default.

Happy Migration

We hope this gives you a clear path to start and finish your migration from facts to vars. Please do not hesitate reaching out if something is still unclear - either through matrix or through our git forge.