summary refs log tree commit diff
path: root/nixos/doc/manual/development/settings-options.section.md
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/doc/manual/development/settings-options.section.md')
-rw-r--r--nixos/doc/manual/development/settings-options.section.md237
1 files changed, 237 insertions, 0 deletions
diff --git a/nixos/doc/manual/development/settings-options.section.md b/nixos/doc/manual/development/settings-options.section.md
new file mode 100644
index 00000000000..f9bb6ff9cc4
--- /dev/null
+++ b/nixos/doc/manual/development/settings-options.section.md
@@ -0,0 +1,237 @@
+# Options for Program Settings {#sec-settings-options}
+
+Many programs have configuration files where program-specific settings
+can be declared. File formats can be separated into two categories:
+
+-   Nix-representable ones: These can trivially be mapped to a subset of
+    Nix syntax. E.g. JSON is an example, since its values like
+    `{"foo":{"bar":10}}` can be mapped directly to Nix:
+    `{ foo = { bar = 10; }; }`. Other examples are INI, YAML and TOML.
+    The following section explains the convention for these settings.
+
+-   Non-nix-representable ones: These can\'t be trivially mapped to a
+    subset of Nix syntax. Most generic programming languages are in this
+    group, e.g. bash, since the statement `if true; then echo hi; fi`
+    doesn\'t have a trivial representation in Nix.
+
+    Currently there are no fixed conventions for these, but it is common
+    to have a `configFile` option for setting the configuration file
+    path directly. The default value of `configFile` can be an
+    auto-generated file, with convenient options for controlling the
+    contents. For example an option of type `attrsOf str` can be used
+    for representing environment variables which generates a section
+    like `export FOO="foo"`. Often it can also be useful to also include
+    an `extraConfig` option of type `lines` to allow arbitrary text
+    after the autogenerated part of the file.
+
+## Nix-representable Formats (JSON, YAML, TOML, INI, \...) {#sec-settings-nix-representable}
+
+By convention, formats like this are handled with a generic `settings`
+option, representing the full program configuration as a Nix value. The
+type of this option should represent the format. The most common formats
+have a predefined type and string generator already declared under
+`pkgs.formats`:
+
+`pkgs.formats.json` { }
+
+:   A function taking an empty attribute set (for future extensibility)
+    and returning a set with JSON-specific attributes `type` and
+    `generate` as specified [below](#pkgs-formats-result).
+
+`pkgs.formats.yaml` { }
+
+:   A function taking an empty attribute set (for future extensibility)
+    and returning a set with YAML-specific attributes `type` and
+    `generate` as specified [below](#pkgs-formats-result).
+
+`pkgs.formats.ini` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \... }
+
+:   A function taking an attribute set with values
+
+    `listsAsDuplicateKeys`
+
+    :   A boolean for controlling whether list values can be used to
+        represent duplicate INI keys
+
+    `listToValue`
+
+    :   A function for turning a list of values into a single value.
+
+    It returns a set with INI-specific attributes `type` and `generate`
+    as specified [below](#pkgs-formats-result).
+
+`pkgs.formats.toml` { }
+
+:   A function taking an empty attribute set (for future extensibility)
+    and returning a set with TOML-specific attributes `type` and
+    `generate` as specified [below](#pkgs-formats-result).
+
+`pkgs.formats.elixirConf { elixir ? pkgs.elixir }`
+
+:   A function taking an attribute set with values
+
+    `elixir`
+
+    :   The Elixir package which will be used to format the generated output
+
+    It returns a set with Elixir-Config-specific attributes `type`, `lib`, and
+    `generate` as specified [below](#pkgs-formats-result).
+
+    The `lib` attribute contains functions to be used in settings, for
+    generating special Elixir values:
+
+    `mkRaw elixirCode`
+
+    :   Outputs the given string as raw Elixir code
+
+    `mkGetEnv { envVariable, fallback ? null }`
+
+    :   Makes the configuration fetch an environment variable at runtime
+
+    `mkAtom atom`
+
+    :   Outputs the given string as an Elixir atom, instead of the default
+        Elixir binary string. Note: lowercase atoms still needs to be prefixed
+        with `:`
+
+    `mkTuple array`
+
+    :   Outputs the given array as an Elixir tuple, instead of the default
+        Elixir list
+
+    `mkMap attrset`
+
+    :   Outputs the given attribute set as an Elixir map, instead of the
+        default Elixir keyword list
+
+
+::: {#pkgs-formats-result}
+These functions all return an attribute set with these values:
+:::
+
+`type`
+
+:   A module system type representing a value of the format
+
+`lib`
+
+:   Utility functions for convenience, or special interactions with the format.
+    This attribute is optional. It may contain inside a `types` attribute
+    containing types specific to this format.
+
+`generate` *`filename jsonValue`*
+
+:   A function that can render a value of the format to a file. Returns
+    a file path.
+
+    ::: {.note}
+    This function puts the value contents in the Nix store. So this
+    should be avoided for secrets.
+    :::
+
+::: {#ex-settings-nix-representable .example}
+::: {.title}
+**Example: Module with conventional `settings` option**
+:::
+The following shows a module for an example program that uses a JSON
+configuration file. It demonstrates how above values can be used, along
+with some other related best practices. See the comments for
+explanations.
+
+```nix
+{ options, config, lib, pkgs, ... }:
+let
+  cfg = config.services.foo;
+  # Define the settings format used for this program
+  settingsFormat = pkgs.formats.json {};
+in {
+
+  options.services.foo = {
+    enable = lib.mkEnableOption "foo service";
+
+    settings = lib.mkOption {
+      # Setting this type allows for correct merging behavior
+      type = settingsFormat.type;
+      default = {};
+      description = ''
+        Configuration for foo, see
+        <link xlink:href="https://example.com/docs/foo"/>
+        for supported settings.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    # We can assign some default settings here to make the service work by just
+    # enabling it. We use `mkDefault` for values that can be changed without
+    # problems
+    services.foo.settings = {
+      # Fails at runtime without any value set
+      log_level = lib.mkDefault "WARN";
+
+      # We assume systemd's `StateDirectory` is used, so we require this value,
+      # therefore no mkDefault
+      data_path = "/var/lib/foo";
+
+      # Since we use this to create a user we need to know the default value at
+      # eval time
+      user = lib.mkDefault "foo";
+    };
+
+    environment.etc."foo.json".source =
+      # The formats generator function takes a filename and the Nix value
+      # representing the format value and produces a filepath with that value
+      # rendered in the format
+      settingsFormat.generate "foo-config.json" cfg.settings;
+
+    # We know that the `user` attribute exists because we set a default value
+    # for it above, allowing us to use it without worries here
+    users.users.${cfg.settings.user} = { isSystemUser = true; };
+
+    # ...
+  };
+}
+```
+:::
+
+### Option declarations for attributes {#sec-settings-attrs-options}
+
+Some `settings` attributes may deserve some extra care. They may need a
+different type, default or merging behavior, or they are essential
+options that should show their documentation in the manual. This can be
+done using [](#sec-freeform-modules).
+
+We extend above example using freeform modules to declare an option for
+the port, which will enforce it to be a valid integer and make it show
+up in the manual.
+
+::: {#ex-settings-typed-attrs .example}
+::: {.title}
+**Example: Declaring a type-checked `settings` attribute**
+:::
+```nix
+settings = lib.mkOption {
+  type = lib.types.submodule {
+
+    freeformType = settingsFormat.type;
+
+    # Declare an option for the port such that the type is checked and this option
+    # is shown in the manual.
+    options.port = lib.mkOption {
+      type = lib.types.port;
+      default = 8080;
+      description = ''
+        Which port this service should listen on.
+      '';
+    };
+
+  };
+  default = {};
+  description = ''
+    Configuration for Foo, see
+    <link xlink:href="https://example.com/docs/foo"/>
+    for supported values.
+  '';
+};
+```
+:::