1{
2 config,
3 options,
4 lib,
5 pkgs,
6 ...
7}:
8
9let
10 inherit (lib) types;
11 cfg = config.services.nvme-rs;
12 opt = options.services.nvme-rs;
13 settingsFormat = pkgs.formats.toml { };
14in
15{
16 options.services.nvme-rs = {
17 enable = lib.mkEnableOption "nvme-rs, a monitoring service";
18
19 package = lib.mkPackageOption pkgs "nvme-rs" { };
20
21 settings = lib.mkOption {
22 type = types.submodule {
23 freeformType = settingsFormat.type;
24 options = {
25 check_interval_secs = lib.mkOption {
26 type = types.int;
27 default = 3600;
28 description = "Check interval in seconds";
29 example = 86400;
30 };
31
32 thresholds = lib.mkOption {
33 type = types.submodule {
34 freeformType = settingsFormat.type;
35 options = {
36 temp_warning = lib.mkOption {
37 type = types.int;
38 default = 55;
39 description = "Temperature warning threshold (°C)";
40 };
41
42 temp_critical = lib.mkOption {
43 type = types.int;
44 default = 65;
45 description = "Temperature critical threshold (°C)";
46 };
47
48 wear_warning = lib.mkOption {
49 type = types.int;
50 default = 20;
51 description = "Wear warning threshold (%)";
52 };
53
54 wear_critical = lib.mkOption {
55 type = types.int;
56 default = 50;
57 description = "Wear critical threshold (%)";
58 };
59
60 spare_warning = lib.mkOption {
61 type = types.int;
62 default = 50;
63 description = "Available spare warning threshold (%)";
64 };
65
66 error_threshold = lib.mkOption {
67 type = types.int;
68 default = 100;
69 description = "Error count warning threshold";
70 };
71 };
72 };
73 default = { };
74 description = "Threshold configuration for NVMe monitoring";
75 };
76
77 email = lib.mkOption {
78 type = types.nullOr (
79 types.submodule {
80 freeformType = settingsFormat.type;
81 options = {
82 smtp_server = lib.mkOption {
83 type = types.str;
84 default = "smtp.gmail.com";
85 description = "SMTP server address";
86 example = "mail.example.com";
87 };
88
89 smtp_port = lib.mkOption {
90 type = types.port;
91 default = 587;
92 description = "SMTP server port";
93 };
94
95 smtp_username = lib.mkOption {
96 type = types.str;
97 description = "SMTP username";
98 example = "your-email@gmail.com";
99 };
100
101 smtp_password_file = lib.mkOption {
102 type = types.path;
103 description = "File containing SMTP password";
104 example = "/run/secrets/smtp-password";
105 };
106
107 from = lib.mkOption {
108 type = types.str;
109 description = "Sender email address";
110 example = "nvme-monitor@example.com";
111 };
112
113 to = lib.mkOption {
114 type = types.str;
115 description = "Recipient email address";
116 example = "admin@example.com";
117 };
118
119 use_tls = lib.mkOption {
120 type = types.bool;
121 default = true;
122 description = "Use TLS for SMTP connection";
123 };
124 };
125 }
126 );
127 default = null;
128 description = "Email notification configuration";
129 };
130 };
131 };
132 default = { };
133 description = ''
134 Configuration for nvme-rs in TOML format.
135 See the config.toml example for all available options.
136 '';
137 };
138 };
139
140 config = lib.mkIf cfg.enable {
141 services.nvme-rs.settings = opt.settings.default;
142
143 systemd.services.nvme-rs = {
144 description = "NVMe health monitoring service";
145 after = [ "network.target" ];
146 wantedBy = [ "multi-user.target" ];
147
148 serviceConfig =
149 let
150 settingsWithoutNull =
151 if cfg.settings.email == null then lib.removeAttrs cfg.settings [ "email" ] else cfg.settings;
152 configFile = settingsFormat.generate "nvme-rs.toml" settingsWithoutNull;
153 in
154 {
155 ExecStart = lib.escapeShellArgs [
156 "${lib.getExe cfg.package}"
157 "daemon"
158 "--config"
159 "${configFile}"
160 ];
161
162 DynamicUser = true;
163 SupplementaryGroups = [ "disk" ];
164 CapabilityBoundingSet = [ "CAP_SYS_ADMIN" ];
165 AmbientCapabilities = [ "CAP_SYS_ADMIN" ];
166 LimitCORE = 0;
167 LimitNOFILE = 65535;
168 LockPersonality = true;
169 MemorySwapMax = 0;
170 MemoryZSwapMax = 0;
171 PrivateTmp = true;
172 ProcSubset = "pid";
173 ProtectClock = true;
174 ProtectControlGroups = true;
175 ProtectHome = true;
176 ProtectHostname = true;
177 ProtectKernelLogs = true;
178 ProtectKernelModules = true;
179 ProtectKernelTunables = true;
180 ProtectProc = "invisible";
181 ProtectSystem = "strict";
182 Restart = "on-failure";
183 RestartSec = "10s";
184 RestrictAddressFamilies = [
185 "AF_INET"
186 "AF_INET6"
187 "AF_UNIX"
188 ];
189 RestrictNamespaces = true;
190 RestrictRealtime = true;
191 SystemCallArchitectures = "native";
192 SystemCallFilter = [
193 "@system-service"
194 "@resources"
195 "~@privileged"
196 ];
197 NoNewPrivileges = true;
198 UMask = "0077";
199 };
200 };
201
202 environment.systemPackages = [ cfg.package ];
203 };
204}