1{
2 config,
3 lib,
4 options,
5 pkgs,
6 ...
7}:
8
9let
10 cfg = config.services.ergo;
11 opt = options.services.ergo;
12
13 inherit (lib)
14 literalExpression
15 mkEnableOption
16 mkIf
17 mkOption
18 optionalString
19 types
20 ;
21
22 configFile = pkgs.writeText "ergo.conf" (
23 ''
24 ergo {
25 directory = "${cfg.dataDir}"
26 node {
27 mining = false
28 }
29 wallet.secretStorage.secretDir = "${cfg.dataDir}/wallet/keystore"
30 }
31
32 scorex {
33 network {
34 bindAddress = "${cfg.listen.ip}:${toString cfg.listen.port}"
35 }
36 ''
37 + optionalString (cfg.api.keyHash != null) ''
38 restApi {
39 apiKeyHash = "${cfg.api.keyHash}"
40 bindAddress = "${cfg.api.listen.ip}:${toString cfg.api.listen.port}"
41 }
42 ''
43 + ''
44 }
45 ''
46 );
47
48in
49{
50
51 options = {
52
53 services.ergo = {
54 enable = mkEnableOption "Ergo service";
55
56 dataDir = mkOption {
57 type = types.path;
58 default = "/var/lib/ergo";
59 description = "The data directory for the Ergo node.";
60 };
61
62 listen = {
63 ip = mkOption {
64 type = types.str;
65 default = "0.0.0.0";
66 description = "IP address on which the Ergo node should listen.";
67 };
68
69 port = mkOption {
70 type = types.port;
71 default = 9006;
72 description = "Listen port for the Ergo node.";
73 };
74 };
75
76 api = {
77 keyHash = mkOption {
78 type = types.nullOr types.str;
79 default = null;
80 example = "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf";
81 description = "Hex-encoded Blake2b256 hash of an API key as a 64-chars long Base16 string.";
82 };
83
84 listen = {
85 ip = mkOption {
86 type = types.str;
87 default = "0.0.0.0";
88 description = "IP address that the Ergo node API should listen on if {option}`api.keyHash` is defined.";
89 };
90
91 port = mkOption {
92 type = types.port;
93 default = 9052;
94 description = "Listen port for the API endpoint if {option}`api.keyHash` is defined.";
95 };
96 };
97 };
98
99 testnet = mkOption {
100 type = types.bool;
101 default = false;
102 description = "Connect to testnet network instead of the default mainnet.";
103 };
104
105 user = mkOption {
106 type = types.str;
107 default = "ergo";
108 description = "The user as which to run the Ergo node.";
109 };
110
111 group = mkOption {
112 type = types.str;
113 default = cfg.user;
114 defaultText = literalExpression "config.${opt.user}";
115 description = "The group as which to run the Ergo node.";
116 };
117
118 openFirewall = mkOption {
119 type = types.bool;
120 default = false;
121 description = "Open ports in the firewall for the Ergo node as well as the API.";
122 };
123 };
124 };
125
126 config = mkIf cfg.enable {
127
128 systemd.tmpfiles.rules = [
129 "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
130 ];
131
132 systemd.services.ergo = {
133 description = "ergo server";
134 wantedBy = [ "multi-user.target" ];
135 wants = [ "network-online.target" ];
136 after = [ "network-online.target" ];
137 serviceConfig = {
138 User = cfg.user;
139 Group = cfg.group;
140 ExecStart = ''
141 ${pkgs.ergo}/bin/ergo \
142 ${optionalString (!cfg.testnet) "--mainnet"} \
143 -c ${configFile}'';
144 };
145 };
146
147 networking.firewall = mkIf cfg.openFirewall {
148 allowedTCPPorts = [ cfg.listen.port ] ++ [ cfg.api.listen.port ];
149 };
150
151 users.users.${cfg.user} = {
152 name = cfg.user;
153 group = cfg.group;
154 description = "Ergo daemon user";
155 home = cfg.dataDir;
156 isSystemUser = true;
157 };
158
159 users.groups.${cfg.group} = { };
160
161 };
162}