1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7let
8 # Load default values from package. See https://github.com/bitvora/haven/blob/master/.env.example
9 defaultSettings = builtins.fromTOML (builtins.readFile "${cfg.package}/share/haven/.env.example");
10
11 import_relays_file = "${pkgs.writeText "import_relays.json" (builtins.toJSON cfg.importRelays)}";
12 blastr_relays_file = "${pkgs.writeText "blastr_relays.json" (builtins.toJSON cfg.blastrRelays)}";
13
14 mergedSettings = cfg.settings // {
15 IMPORT_SEED_RELAYS_FILE = import_relays_file;
16 BLASTR_RELAYS_FILE = blastr_relays_file;
17 };
18
19 cfg = config.services.haven;
20in
21{
22 options.services.haven = {
23 enable = lib.mkEnableOption "haven";
24
25 package = lib.mkPackageOption pkgs "haven" { };
26
27 blastrRelays = lib.mkOption {
28 type = lib.types.listOf lib.types.str;
29 default = [ ];
30 description = "List of relay configurations for blastr";
31 example = lib.literalExpression ''
32 [
33 "relay.example.com"
34 ]
35 '';
36 };
37
38 importRelays = lib.mkOption {
39 type = lib.types.listOf lib.types.str;
40 default = [ ];
41 description = "List of relay configurations for importing historical events";
42 example = lib.literalExpression ''
43 [
44 "relay.example.com"
45 ]
46 '';
47 };
48
49 settings = lib.mkOption {
50 default = defaultSettings;
51 defaultText = "See https://github.com/bitvora/haven/blob/master/.env.example";
52 apply = lib.recursiveUpdate defaultSettings;
53 description = "See https://github.com/bitvora/haven for documentation.";
54 example = lib.literalExpression ''
55 {
56 RELAY_URL = "relay.example.com";
57 OWNER_NPUB = "npub1...";
58 }
59 '';
60 };
61
62 environmentFile = lib.mkOption {
63 type = lib.types.nullOr lib.types.path;
64 default = null;
65 description = ''
66 Path to a file containing sensitive environment variables. See https://github.com/bitvora/haven for documentation.
67 The file should contain environment-variable assignments like:
68 S3_SECRET_KEY=mysecretkey
69 S3_ACCESS_KEY_ID=myaccesskey
70 '';
71 example = "/var/lib/haven/secrets.env";
72 };
73 };
74
75 config = lib.mkIf cfg.enable {
76 users.users.haven = {
77 description = "Haven daemon user";
78 group = "haven";
79 isSystemUser = true;
80 };
81
82 users.groups.haven = { };
83
84 systemd.services.haven = {
85 description = "haven";
86 wants = [ "network.target" ];
87 wantedBy = [ "multi-user.target" ];
88 environment = lib.attrsets.mapAttrs (
89 name: value: if builtins.isBool value then if value then "true" else "false" else toString value
90 ) mergedSettings;
91
92 serviceConfig = {
93 ExecStart = "${cfg.package}/bin/haven";
94 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
95 User = "haven";
96 Group = "haven";
97 Restart = "on-failure";
98
99 RuntimeDirectory = "haven";
100 StateDirectory = "haven";
101 WorkingDirectory = "/var/lib/haven";
102
103 # Create symlink to templates in the working directory
104 ExecStartPre = "+${pkgs.coreutils}/bin/ln -sfT ${cfg.package}/share/haven/templates /var/lib/haven/templates";
105
106 PrivateTmp = true;
107 PrivateUsers = true;
108 PrivateDevices = true;
109 ProtectSystem = "strict";
110 ProtectHome = true;
111 NoNewPrivileges = true;
112 MemoryDenyWriteExecute = true;
113 ProtectKernelTunables = true;
114 ProtectKernelModules = true;
115 ProtectKernelLogs = true;
116 ProtectClock = true;
117 ProtectProc = "invisible";
118 ProcSubset = "pid";
119 ProtectControlGroups = true;
120 LockPersonality = true;
121 RestrictSUIDSGID = true;
122 RemoveIPC = true;
123 RestrictRealtime = true;
124 ProtectHostname = true;
125 CapabilityBoundingSet = "";
126 SystemCallFilter = [
127 "@system-service"
128 ];
129 SystemCallArchitectures = "native";
130 };
131 };
132 };
133
134 meta.maintainers = with lib.maintainers; [
135 felixzieger
136 ];
137}