{ config, lib, pkgs, ... }: let cfg = config.systant; settingsFormat = pkgs.formats.toml { }; in { options.systant = { enable = lib.mkEnableOption "systant system monitoring agent"; package = lib.mkOption { type = lib.types.package; default = pkgs.systant; defaultText = lib.literalExpression "pkgs.systant"; description = "The systant package to use."; }; configFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; description = '' Path to the systant configuration file (TOML). If set, this takes precedence over the settings option. ''; }; settings = lib.mkOption { type = settingsFormat.type; default = { }; description = '' Configuration for systant in Nix attribute set form. Will be converted to TOML. Ignored if configFile is set. ''; example = lib.literalExpression '' { mqtt = { broker = "mqtt://localhost:1883"; topicPrefix = "systant"; }; entities = { cpu_usage = { type = "sensor"; state_command = "awk '/^cpu / {u=$2+$4; t=$2+$4+$5; print int(u*100/t)}' /proc/stat"; unit = "%"; icon = "mdi:cpu-64-bit"; name = "CPU Usage"; }; }; homeassistant = { discovery = true; discoveryPrefix = "homeassistant"; }; } ''; }; user = lib.mkOption { type = lib.types.str; default = "systant"; description = "User account under which systant runs."; }; group = lib.mkOption { type = lib.types.str; default = "systant"; description = "Group under which systant runs."; }; }; config = lib.mkIf cfg.enable { # Create systant user/group if using defaults users.users.${cfg.user} = lib.mkIf (cfg.user == "systant") { isSystemUser = true; group = cfg.group; description = "Systant service user"; }; users.groups.${cfg.group} = lib.mkIf (cfg.group == "systant") { }; # Generate config file from settings if configFile not provided environment.etc."systant/config.toml" = lib.mkIf (cfg.configFile == null && cfg.settings != { }) { source = settingsFormat.generate "systant-config.toml" cfg.settings; }; systemd.services.systant = { description = "Systant system monitoring agent"; wantedBy = [ "multi-user.target" ]; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { Type = "simple"; User = cfg.user; Group = cfg.group; ExecStart = let configPath = if cfg.configFile != null then cfg.configFile else "/etc/systant/config.toml"; in "${cfg.package}/bin/systant run --config ${configPath}"; Restart = "on-failure"; RestartSec = "5s"; # Hardening NoNewPrivileges = true; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; # Allow reading system metrics ReadOnlyPaths = [ "/proc" "/sys" ]; }; }; }; }