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