1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.pumpio;
7 dataDir = "/var/lib/pump.io";
8 runDir = "/run/pump.io";
9 user = "pumpio";
10
11 optionalSet = condition: value: if condition then value else {};
12
13 configScript = ./pump.io-configure.js;
14 configOptions = {
15 outputFile = "${runDir}/config.json";
16 config =
17 (optionalSet (cfg.driver != "disk") {
18 driver = cfg.driver;
19 }) //
20 {
21 params = (optionalSet (cfg.driver == "disk") { dir = dataDir; }) //
22 (optionalSet (cfg.driver == "mongodb" || cfg.driver == "redis") {
23 host = cfg.dbHost;
24 port = cfg.dbPort;
25 dbname = cfg.dbName;
26 dbuser = cfg.dbUser;
27 dbpass = cfg.dbPassword;
28 }) //
29 (optionalSet (cfg.driver == "memcached") {
30 host = cfg.dbHost;
31 port = cfg.dbPort;
32 }) // cfg.driverParams;
33 secret = cfg.secret;
34
35 address = cfg.address;
36 port = cfg.port;
37
38 noweb = false;
39 urlPort = cfg.urlPort;
40 hostname = cfg.hostname;
41 favicon = cfg.favicon;
42
43 site = cfg.site;
44 owner = cfg.owner;
45 ownerURL = cfg.ownerURL;
46
47 key = cfg.sslKey;
48 cert = cfg.sslCert;
49 bounce = false;
50
51 spamhost = cfg.spamHost;
52 spamclientid = cfg.spamClientId;
53 spamclientsecret = cfg.spamClientSecret;
54
55 requireEmail = cfg.requireEmail;
56 smtpserver = cfg.smtpHost;
57 smtpport = cfg.smtpPort;
58 smtpuser = cfg.smtpUser;
59 smtppass = cfg.smtpPassword;
60 smtpusessl = cfg.smtpUseSSL;
61 smtpfrom = cfg.smtpFrom;
62
63 nologger = false;
64 enableUploads = cfg.enableUploads;
65 datadir = dataDir;
66 debugClient = false;
67 firehose = cfg.firehose;
68 disableRegistration = cfg.disableRegistration;
69
70 inherit (cfg) secretFile dbPasswordFile smtpPasswordFile spamClientSecretFile;
71 } //
72 (optionalSet (cfg.port < 1024) {
73 serverUser = user; # have pump.io listen then drop privileges
74 }) // cfg.extraConfig;
75}; in {
76 options = {
77
78 services.pumpio = {
79
80 enable = mkEnableOption "Pump.io social streams server";
81
82 secret = mkOption {
83 type = types.nullOr types.str;
84 default = null;
85 example = "my dog has fleas";
86 description = ''
87 A session-generating secret, server-wide password. Warning:
88 this is stored in cleartext in the Nix store!
89 '';
90 };
91
92 secretFile = mkOption {
93 type = types.nullOr types.path;
94 default = null;
95 example = "/run/keys/pump.io-secret";
96 description = ''
97 A file containing the session-generating secret,
98 server-wide password.
99 '';
100 };
101
102 site = mkOption {
103 type = types.str;
104 example = "Awesome Sauce";
105 description = "Name of the server";
106 };
107
108 owner = mkOption {
109 type = types.str;
110 default = "";
111 example = "Awesome Inc.";
112 description = "Name of owning entity, if you want to link to it.";
113 };
114
115 ownerURL = mkOption {
116 type = types.str;
117 default = "";
118 example = "https://pump.io";
119 description = "URL of owning entity, if you want to link to it.";
120 };
121
122 address = mkOption {
123 type = types.str;
124 default = "localhost";
125 description = ''
126 Web server listen address.
127 '';
128 };
129
130 port = mkOption {
131 type = types.int;
132 default = 31337;
133 description = ''
134 Port to listen on. Defaults to 31337, which is suitable for
135 running behind a reverse proxy. For a standalone server,
136 use 443.
137 '';
138 };
139
140 hostname = mkOption {
141 type = types.nullOr types.str;
142 default = "localhost";
143 description = ''
144 The hostname of the server, used for generating
145 URLs. Defaults to "localhost" which doesn't do much for you.
146 '';
147 };
148
149 urlPort = mkOption {
150 type = types.int;
151 default = 443;
152 description = ''
153 Port to use for generating URLs. This basically has to be
154 either 80 or 443 because the host-meta and Webfinger
155 protocols don't make any provision for HTTP/HTTPS servers
156 running on other ports.
157 '';
158 };
159
160 favicon = mkOption {
161 type = types.nullOr types.path;
162 default = null;
163 description = ''
164 Local filesystem path to the favicon.ico file to use. This
165 will be served as "/favicon.ico" by the server.
166 '';
167 };
168
169 enableUploads = mkOption {
170 type = types.bool;
171 default = true;
172 description = ''
173 If you want to disable file uploads, set this to false. Uploaded files will be stored
174 in ${dataDir}/uploads.
175 '';
176 };
177
178 sslKey = mkOption {
179 type = types.path;
180 example = "${dataDir}/myserver.key";
181 default = "";
182 description = ''
183 The path to the server certificate private key. The
184 certificate is required, but it can be self-signed.
185 '';
186 };
187
188 sslCert = mkOption {
189 type = types.path;
190 example = "${dataDir}/myserver.crt";
191 default = "";
192 description = ''
193 The path to the server certificate. The certificate is
194 required, but it can be self-signed.
195 '';
196 };
197
198 firehose = mkOption {
199 type = types.str;
200 default = "ofirehose.com";
201 description = ''
202 Firehose host running the ofirehose software. Defaults to
203 "ofirehose.com". Public notices will be ping this firehose
204 server and from there go out to search engines and the
205 world. If you want to disconnect from the public web, set
206 this to something falsy.
207 '';
208 };
209
210 disableRegistration = mkOption {
211 type = types.bool;
212 default = false;
213 description = ''
214 Disables registering new users on the site through the Web
215 or the API.
216 '';
217 };
218
219 requireEmail = mkOption {
220 type = types.bool;
221 default = false;
222 description = "Require an e-mail address to register.";
223 };
224
225 extraConfig = mkOption {
226 default = { };
227 description = ''
228 Extra configuration options which are serialized to json and added
229 to the pump.io.json config file.
230 '';
231 };
232
233 driver = mkOption {
234 type = types.enum [ "mongodb" "disk" "lrucache" "memcached" "redis" ];
235 default = "mongodb";
236 description = "Type of database. Corresponds to a nodejs databank driver.";
237 };
238
239 driverParams = mkOption {
240 default = { };
241 description = "Extra parameters for the driver.";
242 };
243
244 dbHost = mkOption {
245 type = types.str;
246 default = "localhost";
247 description = "The database host to connect to.";
248 };
249
250 dbPort = mkOption {
251 type = types.int;
252 default = 27017;
253 description = "The port that the database is listening on.";
254 };
255
256 dbName = mkOption {
257 type = types.str;
258 default = "pumpio";
259 description = "The name of the database to use.";
260 };
261
262 dbUser = mkOption {
263 type = types.nullOr types.str;
264 default = null;
265 description = ''
266 The username. Defaults to null, meaning no authentication.
267 '';
268 };
269
270 dbPassword = mkOption {
271 type = types.nullOr types.str;
272 default = null;
273 description = ''
274 The password corresponding to dbUser. Warning: this is
275 stored in cleartext in the Nix store!
276 '';
277 };
278
279 dbPasswordFile = mkOption {
280 type = types.nullOr types.path;
281 default = null;
282 example = "/run/keys/pump.io-dbpassword";
283 description = ''
284 A file containing the password corresponding to dbUser.
285 '';
286 };
287
288 smtpHost = mkOption {
289 type = types.nullOr types.str;
290 default = null;
291 example = "localhost";
292 description = ''
293 Server to use for sending transactional email. If it's not
294 set up, no email is sent and features like password recovery
295 and email notification won't work.
296 '';
297 };
298
299 smtpPort = mkOption {
300 type = types.int;
301 default = 25;
302 description = ''
303 Port to connect to on SMTP server.
304 '';
305 };
306
307 smtpUser = mkOption {
308 type = types.nullOr types.str;
309 default = null;
310 description = ''
311 Username to use to connect to SMTP server. Might not be
312 necessary for some servers.
313 '';
314 };
315
316 smtpPassword = mkOption {
317 type = types.nullOr types.str;
318 default = null;
319 description = ''
320 Password to use to connect to SMTP server. Might not be
321 necessary for some servers. Warning: this is stored in
322 cleartext in the Nix store!
323 '';
324 };
325
326 smtpPasswordFile = mkOption {
327 type = types.nullOr types.path;
328 default = null;
329 example = "/run/keys/pump.io-smtppassword";
330 description = ''
331 A file containing the password used to connect to SMTP
332 server. Might not be necessary for some servers.
333 '';
334 };
335
336
337 smtpUseSSL = mkOption {
338 type = types.bool;
339 default = false;
340 description = ''
341 Only use SSL with the SMTP server. By default, a SSL
342 connection is negotiated using TLS. You may need to change
343 the smtpPort value if you set this.
344 '';
345 };
346
347 smtpFrom = mkOption {
348 type = types.nullOr types.str;
349 default = null;
350 description = ''
351 Email address to use in the "From:" header of outgoing
352 notifications. Defaults to 'no-reply@' plus the site
353 hostname.
354 '';
355 };
356
357 spamHost = mkOption {
358 type = types.nullOr types.str;
359 default = null;
360 description = ''
361 Host running activityspam software to use to test updates
362 for spam.
363 '';
364 };
365 spamClientId = mkOption {
366 type = types.nullOr types.str;
367 default = null;
368 description = "OAuth pair for spam server.";
369 };
370 spamClientSecret = mkOption {
371 type = types.nullOr types.str;
372 default = null;
373 description = ''
374 OAuth pair for spam server. Warning: this is
375 stored in cleartext in the Nix store!
376 '';
377 };
378 spamClientSecretFile = mkOption {
379 type = types.nullOr types.path;
380 default = null;
381 example = "/run/keys/pump.io-spamclientsecret";
382 description = ''
383 A file containing the OAuth key for the spam server.
384 '';
385 };
386 };
387
388 };
389
390 config = mkIf cfg.enable {
391 warnings = let warn = k: optional (cfg.${k} != null)
392 "config.services.pumpio.${k} is insecure. Use ${k}File instead.";
393 in concatMap warn [ "secret" "dbPassword" "smtpPassword" "spamClientSecret" ];
394
395 assertions = [
396 { assertion = !(isNull cfg.secret && isNull cfg.secretFile);
397 message = "pump.io needs a secretFile configured";
398 }
399 ];
400
401 systemd.services."pump.io" =
402 { description = "Pump.io - stream server that does most of what people really want from a social network";
403 after = [ "network.target" ];
404 wantedBy = [ "multi-user.target" ];
405
406 preStart = ''
407 mkdir -p ${dataDir}/uploads
408 mkdir -p ${runDir}
409 chown pumpio:pumpio ${dataDir}/uploads ${runDir}
410 chmod 770 ${dataDir}/uploads ${runDir}
411
412 ${pkgs.nodejs}/bin/node ${configScript} <<EOF
413 ${builtins.toJSON configOptions}
414 EOF
415
416 chgrp pumpio ${configOptions.outputFile}
417 chmod 640 ${configOptions.outputFile}
418 '';
419
420 serviceConfig = {
421 ExecStart = "${pkgs.pumpio}/bin/pump -c ${configOptions.outputFile}";
422 PermissionsStartOnly = true;
423 User = if cfg.port < 1024 then "root" else user;
424 Group = user;
425 };
426 environment = { NODE_ENV = "production"; };
427 };
428
429 users.extraGroups.pumpio.gid = config.ids.gids.pumpio;
430 users.extraUsers.pumpio = {
431 group = "pumpio";
432 uid = config.ids.uids.pumpio;
433 description = "Pump.io user";
434 home = dataDir;
435 createHome = true;
436 };
437 };
438}