···11+{ config, lib, pkgs, ... }:
22+33+with lib;
44+let
55+ cfg = config.services.tahoe;
66+in
77+ {
88+ options.services.tahoe = {
99+ introducers = mkOption {
1010+ default = {};
1111+ type = types.loaOf types.optionSet;
1212+ description = ''
1313+ The Tahoe introducers.
1414+ '';
1515+ options = {
1616+ nickname = mkOption {
1717+ type = types.str;
1818+ description = ''
1919+ The nickname of this Tahoe introducer.
2020+ '';
2121+ };
2222+ tub.port = mkOption {
2323+ default = 3458;
2424+ type = types.int;
2525+ description = ''
2626+ The port on which the introducer will listen.
2727+ '';
2828+ };
2929+ package = mkOption {
3030+ default = pkgs.tahoelafs;
3131+ defaultText = "pkgs.tahoelafs";
3232+ type = types.package;
3333+ example = literalExample "pkgs.tahoelafs";
3434+ description = ''
3535+ The package to use for the Tahoe LAFS daemon.
3636+ '';
3737+ };
3838+ };
3939+ };
4040+ nodes = mkOption {
4141+ default = {};
4242+ type = types.loaOf types.optionSet;
4343+ description = ''
4444+ The Tahoe nodes.
4545+ '';
4646+ options = {
4747+ nickname = mkOption {
4848+ type = types.str;
4949+ description = ''
5050+ The nickname of this Tahoe node.
5151+ '';
5252+ };
5353+ tub.port = mkOption {
5454+ default = 3457;
5555+ type = types.int;
5656+ description = ''
5757+ The port on which the tub will listen.
5858+5959+ This is the correct setting to tweak if you want Tahoe's storage
6060+ system to listen on a different port.
6161+ '';
6262+ };
6363+ web.port = mkOption {
6464+ default = 3456;
6565+ type = types.int;
6666+ description = ''
6767+ The port on which the Web server will listen.
6868+6969+ This is the correct setting to tweak if you want Tahoe's WUI to
7070+ listen on a different port.
7171+ '';
7272+ };
7373+ client.introducer = mkOption {
7474+ default = null;
7575+ type = types.nullOr types.str;
7676+ description = ''
7777+ The furl for a Tahoe introducer node.
7878+7979+ Like all furls, keep this safe and don't share it.
8080+ '';
8181+ };
8282+ client.helper = mkOption {
8383+ default = null;
8484+ type = types.nullOr types.str;
8585+ description = ''
8686+ The furl for a Tahoe helper node.
8787+8888+ Like all furls, keep this safe and don't share it.
8989+ '';
9090+ };
9191+ client.shares.needed = mkOption {
9292+ default = 3;
9393+ type = types.int;
9494+ description = ''
9595+ The number of shares required to reconstitute a file.
9696+ '';
9797+ };
9898+ client.shares.happy = mkOption {
9999+ default = 7;
100100+ type = types.int;
101101+ description = ''
102102+ The number of distinct storage nodes required to store
103103+ a file.
104104+ '';
105105+ };
106106+ client.shares.total = mkOption {
107107+ default = 10;
108108+ type = types.int;
109109+ description = ''
110110+ The number of shares required to store a file.
111111+ '';
112112+ };
113113+ storage.enable = mkEnableOption "storage service";
114114+ storage.reservedSpace = mkOption {
115115+ default = "1G";
116116+ type = types.str;
117117+ description = ''
118118+ The amount of filesystem space to not use for storage.
119119+ '';
120120+ };
121121+ helper.enable = mkEnableOption "helper service";
122122+ package = mkOption {
123123+ default = pkgs.tahoelafs;
124124+ defaultText = "pkgs.tahoelafs";
125125+ type = types.package;
126126+ example = literalExample "pkgs.tahoelafs";
127127+ description = ''
128128+ The package to use for the Tahoe LAFS daemon.
129129+ '';
130130+ };
131131+ };
132132+ };
133133+ };
134134+ config = mkMerge [
135135+ (mkIf (cfg.introducers != {}) {
136136+ environment = {
137137+ etc = flip mapAttrs' cfg.introducers (node: settings:
138138+ nameValuePair "tahoe-lafs/introducer-${node}.cfg" {
139139+ mode = "0444";
140140+ text = ''
141141+ # This configuration is generated by Nix. Edit at your own
142142+ # peril; here be dragons.
143143+144144+ [node]
145145+ nickname = ${settings.nickname}
146146+ tub.port = ${toString settings.tub.port}
147147+ '';
148148+ });
149149+ # Actually require Tahoe, so that we will have it installed.
150150+ systemPackages = flip mapAttrsToList cfg.introducers (node: settings:
151151+ settings.package
152152+ );
153153+ };
154154+ # Open up the firewall.
155155+ # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers
156156+ # (node: settings: settings.tub.port);
157157+ systemd.services = flip mapAttrs' cfg.introducers (node: settings:
158158+ let
159159+ pidfile = "/run/tahoe.introducer-${node}.pid";
160160+ # This is a directory, but it has no trailing slash. Tahoe commands
161161+ # get antsy when there's a trailing slash.
162162+ nodedir = "/var/db/tahoe-lafs/introducer-${node}";
163163+ in nameValuePair "tahoe.introducer-${node}" {
164164+ description = "Tahoe LAFS node ${node}";
165165+ wantedBy = [ "multi-user.target" ];
166166+ path = [ settings.package ];
167167+ restartTriggers = [
168168+ config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ];
169169+ serviceConfig = {
170170+ Type = "simple";
171171+ PIDFile = pidfile;
172172+ };
173173+ preStart = ''
174174+ if [ \! -d ${nodedir} ]; then
175175+ mkdir -p /var/db/tahoe-lafs
176176+ tahoe create-introducer ${nodedir}
177177+ fi
178178+179179+ # Tahoe has created a predefined tahoe.cfg which we must now
180180+ # scribble over.
181181+ # XXX I thought that a symlink would work here, but it doesn't, so
182182+ # we must do this on every prestart. Fixes welcome.
183183+ # rm ${nodedir}/tahoe.cfg
184184+ # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
185185+ cp /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg
186186+ '';
187187+ # Believe it or not, Tahoe is very brittle about the order of
188188+ # arguments to $(tahoe start). The node directory must come first,
189189+ # and arguments which alter Twisted's behavior come afterwards.
190190+ script = ''
191191+ tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
192192+ '';
193193+ });
194194+ users.extraUsers = flip mapAttrs' cfg.introducers (node: _:
195195+ nameValuePair "tahoe.introducer-${node}" {
196196+ description = "Tahoe node user for introducer ${node}";
197197+ isSystemUser = true;
198198+ });
199199+ })
200200+ (mkIf (cfg.nodes != {}) {
201201+ environment = {
202202+ etc = flip mapAttrs' cfg.nodes (node: settings:
203203+ nameValuePair "tahoe-lafs/${node}.cfg" {
204204+ mode = "0444";
205205+ text = ''
206206+ # This configuration is generated by Nix. Edit at your own
207207+ # peril; here be dragons.
208208+209209+ [node]
210210+ nickname = ${settings.nickname}
211211+ tub.port = ${toString settings.tub.port}
212212+ # This is a Twisted endpoint. Twisted Web doesn't work on
213213+ # non-TCP. ~ C.
214214+ web.port = tcp:${toString settings.web.port}
215215+216216+ [client]
217217+ ${optionalString (settings.client.introducer != null)
218218+ "introducer.furl = ${settings.client.introducer}"}
219219+ ${optionalString (settings.client.helper != null)
220220+ "helper.furl = ${settings.client.helper}"}
221221+222222+ shares.needed = ${toString settings.client.shares.needed}
223223+ shares.happy = ${toString settings.client.shares.happy}
224224+ shares.total = ${toString settings.client.shares.total}
225225+226226+ [storage]
227227+ enabled = ${if settings.storage.enable then "true" else "false"}
228228+ reserved_space = ${settings.storage.reservedSpace}
229229+230230+ [helper]
231231+ enabled = ${if settings.helper.enable then "true" else "false"}
232232+ '';
233233+ });
234234+ # Actually require Tahoe, so that we will have it installed.
235235+ systemPackages = flip mapAttrsToList cfg.nodes (node: settings:
236236+ settings.package
237237+ );
238238+ };
239239+ # Open up the firewall.
240240+ # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes
241241+ # (node: settings: settings.tub.port);
242242+ systemd.services = flip mapAttrs' cfg.nodes (node: settings:
243243+ let
244244+ pidfile = "/run/tahoe.${node}.pid";
245245+ # This is a directory, but it has no trailing slash. Tahoe commands
246246+ # get antsy when there's a trailing slash.
247247+ nodedir = "/var/db/tahoe-lafs/${node}";
248248+ in nameValuePair "tahoe.${node}" {
249249+ description = "Tahoe LAFS node ${node}";
250250+ wantedBy = [ "multi-user.target" ];
251251+ path = [ settings.package ];
252252+ restartTriggers = [
253253+ config.environment.etc."tahoe-lafs/${node}.cfg".source ];
254254+ serviceConfig = {
255255+ Type = "simple";
256256+ PIDFile = pidfile;
257257+ };
258258+ preStart = ''
259259+ if [ \! -d ${nodedir} ]; then
260260+ mkdir -p /var/db/tahoe-lafs
261261+ tahoe create-node ${nodedir}
262262+ fi
263263+264264+ # Tahoe has created a predefined tahoe.cfg which we must now
265265+ # scribble over.
266266+ # XXX I thought that a symlink would work here, but it doesn't, so
267267+ # we must do this on every prestart. Fixes welcome.
268268+ # rm ${nodedir}/tahoe.cfg
269269+ # ln -s /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
270270+ cp /etc/tahoe-lafs/${node}.cfg ${nodedir}/tahoe.cfg
271271+ '';
272272+ # Believe it or not, Tahoe is very brittle about the order of
273273+ # arguments to $(tahoe start). The node directory must come first,
274274+ # and arguments which alter Twisted's behavior come afterwards.
275275+ script = ''
276276+ tahoe start ${nodedir} -n -l- --pidfile=${pidfile}
277277+ '';
278278+ });
279279+ users.extraUsers = flip mapAttrs' cfg.nodes (node: _:
280280+ nameValuePair "tahoe.${node}" {
281281+ description = "Tahoe node user for node ${node}";
282282+ isSystemUser = true;
283283+ });
284284+ })
285285+ ];
286286+ }