1{ config, lib, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.tahoe; 6in 7 { 8 options.services.tahoe = { 9 introducers = mkOption { 10 default = {}; 11 type = with types; attrsOf (submodule { 12 options = { 13 nickname = mkOption { 14 type = types.str; 15 description = '' 16 The nickname of this Tahoe introducer. 17 ''; 18 }; 19 tub.port = mkOption { 20 default = 3458; 21 type = types.int; 22 description = '' 23 The port on which the introducer will listen. 24 ''; 25 }; 26 tub.location = mkOption { 27 default = null; 28 type = types.nullOr types.str; 29 description = '' 30 The external location that the introducer should listen on. 31 32 If specified, the port should be included. 33 ''; 34 }; 35 package = mkOption { 36 default = pkgs.tahoelafs; 37 defaultText = "pkgs.tahoelafs"; 38 type = types.package; 39 example = literalExample "pkgs.tahoelafs"; 40 description = '' 41 The package to use for the Tahoe LAFS daemon. 42 ''; 43 }; 44 }; 45 }); 46 description = '' 47 The Tahoe introducers. 48 ''; 49 }; 50 nodes = mkOption { 51 default = {}; 52 type = with types; attrsOf (submodule { 53 options = { 54 nickname = mkOption { 55 type = types.str; 56 description = '' 57 The nickname of this Tahoe node. 58 ''; 59 }; 60 tub.port = mkOption { 61 default = 3457; 62 type = types.int; 63 description = '' 64 The port on which the tub will listen. 65 66 This is the correct setting to tweak if you want Tahoe's storage 67 system to listen on a different port. 68 ''; 69 }; 70 tub.location = mkOption { 71 default = null; 72 type = types.nullOr types.str; 73 description = '' 74 The external location that the node should listen on. 75 76 This is the setting to tweak if there are multiple interfaces 77 and you want to alter which interface Tahoe is advertising. 78 79 If specified, the port should be included. 80 ''; 81 }; 82 web.port = mkOption { 83 default = 3456; 84 type = types.int; 85 description = '' 86 The port on which the Web server will listen. 87 88 This is the correct setting to tweak if you want Tahoe's WUI to 89 listen on a different port. 90 ''; 91 }; 92 client.introducer = mkOption { 93 default = null; 94 type = types.nullOr types.str; 95 description = '' 96 The furl for a Tahoe introducer node. 97 98 Like all furls, keep this safe and don't share it. 99 ''; 100 }; 101 client.helper = mkOption { 102 default = null; 103 type = types.nullOr types.str; 104 description = '' 105 The furl for a Tahoe helper node. 106 107 Like all furls, keep this safe and don't share it. 108 ''; 109 }; 110 client.shares.needed = mkOption { 111 default = 3; 112 type = types.int; 113 description = '' 114 The number of shares required to reconstitute a file. 115 ''; 116 }; 117 client.shares.happy = mkOption { 118 default = 7; 119 type = types.int; 120 description = '' 121 The number of distinct storage nodes required to store 122 a file. 123 ''; 124 }; 125 client.shares.total = mkOption { 126 default = 10; 127 type = types.int; 128 description = '' 129 The number of shares required to store a file. 130 ''; 131 }; 132 storage.enable = mkEnableOption "storage service"; 133 storage.reservedSpace = mkOption { 134 default = "1G"; 135 type = types.str; 136 description = '' 137 The amount of filesystem space to not use for storage. 138 ''; 139 }; 140 helper.enable = mkEnableOption "helper service"; 141 sftpd.enable = mkEnableOption "SFTP service"; 142 sftpd.port = mkOption { 143 default = null; 144 type = types.nullOr types.int; 145 description = '' 146 The port on which the SFTP server will listen. 147 148 This is the correct setting to tweak if you want Tahoe's SFTP 149 daemon to listen on a different port. 150 ''; 151 }; 152 sftpd.hostPublicKeyFile = mkOption { 153 default = null; 154 type = types.nullOr types.path; 155 description = '' 156 Path to the SSH host public key. 157 ''; 158 }; 159 sftpd.hostPrivateKeyFile = mkOption { 160 default = null; 161 type = types.nullOr types.path; 162 description = '' 163 Path to the SSH host private key. 164 ''; 165 }; 166 sftpd.accounts.file = mkOption { 167 default = null; 168 type = types.nullOr types.path; 169 description = '' 170 Path to the accounts file. 171 ''; 172 }; 173 sftpd.accounts.url = mkOption { 174 default = null; 175 type = types.nullOr types.str; 176 description = '' 177 URL of the accounts server. 178 ''; 179 }; 180 package = mkOption { 181 default = pkgs.tahoelafs; 182 defaultText = "pkgs.tahoelafs"; 183 type = types.package; 184 example = literalExample "pkgs.tahoelafs"; 185 description = '' 186 The package to use for the Tahoe LAFS daemon. 187 ''; 188 }; 189 }; 190 }); 191 description = '' 192 The Tahoe nodes. 193 ''; 194 }; 195 }; 196 config = mkMerge [ 197 (mkIf (cfg.introducers != {}) { 198 environment = { 199 etc = flip mapAttrs' cfg.introducers (node: settings: 200 nameValuePair "tahoe-lafs/introducer-${node}.cfg" { 201 mode = "0444"; 202 text = '' 203 # This configuration is generated by Nix. Edit at your own 204 # peril; here be dragons. 205 206 [node] 207 nickname = ${settings.nickname} 208 tub.port = ${toString settings.tub.port} 209 ${optionalString (settings.tub.location != null) 210 "tub.location = ${settings.tub.location}"} 211 ''; 212 }); 213 # Actually require Tahoe, so that we will have it installed. 214 systemPackages = flip mapAttrsToList cfg.introducers (node: settings: 215 settings.package 216 ); 217 }; 218 # Open up the firewall. 219 # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers 220 # (node: settings: settings.tub.port); 221 systemd.services = flip mapAttrs' cfg.introducers (node: settings: 222 let 223 pidfile = "/run/tahoe.introducer-${node}.pid"; 224 # This is a directory, but it has no trailing slash. Tahoe commands 225 # get antsy when there's a trailing slash. 226 nodedir = "/var/db/tahoe-lafs/introducer-${node}"; 227 in nameValuePair "tahoe.introducer-${node}" { 228 description = "Tahoe LAFS node ${node}"; 229 wantedBy = [ "multi-user.target" ]; 230 path = [ settings.package ]; 231 restartTriggers = [ 232 config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ]; 233 serviceConfig = { 234 Type = "simple"; 235 PIDFile = pidfile; 236 # Believe it or not, Tahoe is very brittle about the order of 237 # arguments to $(tahoe start). The node directory must come first, 238 # and arguments which alter Twisted's behavior come afterwards. 239 ExecStart = '' 240 ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} 241 ''; 242 }; 243 preStart = '' 244 if [ ! -d ${lib.escapeShellArg nodedir} ]; then 245 mkdir -p /var/db/tahoe-lafs 246 tahoe create-introducer ${lib.escapeShellArg nodedir} 247 fi 248 249 # Tahoe has created a predefined tahoe.cfg which we must now 250 # scribble over. 251 # XXX I thought that a symlink would work here, but it doesn't, so 252 # we must do this on every prestart. Fixes welcome. 253 # rm ${nodedir}/tahoe.cfg 254 # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg 255 cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg 256 ''; 257 }); 258 users.users = flip mapAttrs' cfg.introducers (node: _: 259 nameValuePair "tahoe.introducer-${node}" { 260 description = "Tahoe node user for introducer ${node}"; 261 isSystemUser = true; 262 }); 263 }) 264 (mkIf (cfg.nodes != {}) { 265 environment = { 266 etc = flip mapAttrs' cfg.nodes (node: settings: 267 nameValuePair "tahoe-lafs/${node}.cfg" { 268 mode = "0444"; 269 text = '' 270 # This configuration is generated by Nix. Edit at your own 271 # peril; here be dragons. 272 273 [node] 274 nickname = ${settings.nickname} 275 tub.port = ${toString settings.tub.port} 276 ${optionalString (settings.tub.location != null) 277 "tub.location = ${settings.tub.location}"} 278 # This is a Twisted endpoint. Twisted Web doesn't work on 279 # non-TCP. ~ C. 280 web.port = tcp:${toString settings.web.port} 281 282 [client] 283 ${optionalString (settings.client.introducer != null) 284 "introducer.furl = ${settings.client.introducer}"} 285 ${optionalString (settings.client.helper != null) 286 "helper.furl = ${settings.client.helper}"} 287 288 shares.needed = ${toString settings.client.shares.needed} 289 shares.happy = ${toString settings.client.shares.happy} 290 shares.total = ${toString settings.client.shares.total} 291 292 [storage] 293 enabled = ${boolToString settings.storage.enable} 294 reserved_space = ${settings.storage.reservedSpace} 295 296 [helper] 297 enabled = ${boolToString settings.helper.enable} 298 299 [sftpd] 300 enabled = ${boolToString settings.sftpd.enable} 301 ${optionalString (settings.sftpd.port != null) 302 "port = ${toString settings.sftpd.port}"} 303 ${optionalString (settings.sftpd.hostPublicKeyFile != null) 304 "host_pubkey_file = ${settings.sftpd.hostPublicKeyFile}"} 305 ${optionalString (settings.sftpd.hostPrivateKeyFile != null) 306 "host_privkey_file = ${settings.sftpd.hostPrivateKeyFile}"} 307 ${optionalString (settings.sftpd.accounts.file != null) 308 "accounts.file = ${settings.sftpd.accounts.file}"} 309 ${optionalString (settings.sftpd.accounts.url != null) 310 "accounts.url = ${settings.sftpd.accounts.url}"} 311 ''; 312 }); 313 # Actually require Tahoe, so that we will have it installed. 314 systemPackages = flip mapAttrsToList cfg.nodes (node: settings: 315 settings.package 316 ); 317 }; 318 # Open up the firewall. 319 # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes 320 # (node: settings: settings.tub.port); 321 systemd.services = flip mapAttrs' cfg.nodes (node: settings: 322 let 323 pidfile = "/run/tahoe.${node}.pid"; 324 # This is a directory, but it has no trailing slash. Tahoe commands 325 # get antsy when there's a trailing slash. 326 nodedir = "/var/db/tahoe-lafs/${node}"; 327 in nameValuePair "tahoe.${node}" { 328 description = "Tahoe LAFS node ${node}"; 329 wantedBy = [ "multi-user.target" ]; 330 path = [ settings.package ]; 331 restartTriggers = [ 332 config.environment.etc."tahoe-lafs/${node}.cfg".source ]; 333 serviceConfig = { 334 Type = "simple"; 335 PIDFile = pidfile; 336 # Believe it or not, Tahoe is very brittle about the order of 337 # arguments to $(tahoe start). The node directory must come first, 338 # and arguments which alter Twisted's behavior come afterwards. 339 ExecStart = '' 340 ${settings.package}/bin/tahoe start ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} 341 ''; 342 }; 343 preStart = '' 344 if [ ! -d ${lib.escapeShellArg nodedir} ]; then 345 mkdir -p /var/db/tahoe-lafs 346 tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir} 347 fi 348 349 # Tahoe has created a predefined tahoe.cfg which we must now 350 # scribble over. 351 # XXX I thought that a symlink would work here, but it doesn't, so 352 # we must do this on every prestart. Fixes welcome. 353 # rm ${nodedir}/tahoe.cfg 354 # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg 355 cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg 356 ''; 357 }); 358 users.users = flip mapAttrs' cfg.nodes (node: _: 359 nameValuePair "tahoe.${node}" { 360 description = "Tahoe node user for node ${node}"; 361 isSystemUser = true; 362 }); 363 }) 364 ]; 365 }