1 { config, lib, options, pkgs, ... }:
3 cfg = config.services.rippled;
4 opt = options.services.rippled;
6 b2i = val: if val then "1" else "0";
11 ${lib.optionalString (db.compression != null) ("compression=${b2i db.compression}") }
12 ${lib.optionalString (db.onlineDelete != null) ("online_delete=${toString db.onlineDelete}")}
13 ${lib.optionalString (db.advisoryDelete != null) ("advisory_delete=${b2i db.advisoryDelete}")}
19 ${lib.concatMapStringsSep "\n" (n: "port_${n}") (lib.attrNames cfg.ports)}
21 ${lib.concatMapStrings (p: ''
24 port=${toString p.port}
25 protocol=${lib.concatStringsSep "," p.protocol}
26 ${lib.optionalString (p.user != "") "user=${p.user}"}
27 ${lib.optionalString (p.password != "") "user=${p.password}"}
28 admin=${lib.concatStringsSep "," p.admin}
29 ${lib.optionalString (p.ssl.key != null) "ssl_key=${p.ssl.key}"}
30 ${lib.optionalString (p.ssl.cert != null) "ssl_cert=${p.ssl.cert}"}
31 ${lib.optionalString (p.ssl.chain != null) "ssl_chain=${p.ssl.chain}"}
32 '') (lib.attrValues cfg.ports)}
40 ${lib.optionalString (cfg.tempDb != null) ''
42 ${dbCfg cfg.tempDb}''}
44 ${lib.optionalString (cfg.importDb != null) ''
46 ${dbCfg cfg.importDb}''}
49 ${lib.concatStringsSep "\n" cfg.ips}
52 ${lib.concatStringsSep "\n" cfg.ipsFixed}
55 ${lib.concatStringsSep "\n" cfg.validators}
61 ${toString cfg.ledgerHistory}
64 ${toString cfg.fetchDepth}
67 ${toString cfg.validationQuorum}
70 ${lib.concatStringsSep "\n" cfg.sntpServers}
72 ${lib.optionalString cfg.statsd.enable ''
75 address=${cfg.statsd.address}
76 prefix=${cfg.statsd.prefix}
80 { "command": "log_level", "severity": "${cfg.logLevel}" }
83 portOptions = { name, ...}: {
91 default = "127.0.0.1";
92 description = "Ip where rippled listens.";
97 description = "Port where rippled listens.";
98 type = lib.types.port;
101 protocol = lib.mkOption {
102 description = "Protocols expose by rippled.";
103 type = lib.types.listOf (lib.types.enum ["http" "https" "ws" "wss" "peer"]);
106 user = lib.mkOption {
107 description = "When set, these credentials will be required on HTTP/S requests.";
108 type = lib.types.str;
112 password = lib.mkOption {
113 description = "When set, these credentials will be required on HTTP/S requests.";
114 type = lib.types.str;
118 admin = lib.mkOption {
119 description = "A comma-separated list of admin IP addresses.";
120 type = lib.types.listOf lib.types.str;
121 default = ["127.0.0.1"];
127 Specifies the filename holding the SSL key in PEM format.
130 type = lib.types.nullOr lib.types.path;
133 cert = lib.mkOption {
135 Specifies the path to the SSL certificate file in PEM format.
136 This is not needed if the chain includes it.
139 type = lib.types.nullOr lib.types.path;
142 chain = lib.mkOption {
144 If you need a certificate chain, specify the path to the
145 certificate chain here. The chain may include the end certificate.
148 type = lib.types.nullOr lib.types.path;
156 type = lib.mkOption {
157 description = "Rippled database type.";
158 type = lib.types.enum ["rocksdb" "nudb"];
162 path = lib.mkOption {
163 description = "Location to store the database.";
164 type = lib.types.path;
165 default = cfg.databasePath;
166 defaultText = lib.literalExpression "config.${opt.databasePath}";
169 compression = lib.mkOption {
170 description = "Whether to enable snappy compression.";
171 type = lib.types.nullOr lib.types.bool;
175 onlineDelete = lib.mkOption {
176 description = "Enable automatic purging of older ledger information.";
177 type = lib.types.nullOr (lib.types.addCheck lib.types.int (v: v > 256));
178 default = cfg.ledgerHistory;
179 defaultText = lib.literalExpression "config.${opt.ledgerHistory}";
182 advisoryDelete = lib.mkOption {
184 If set, then require administrative RPC call "can_delete"
185 to enable online deletion of ledger records.
187 type = lib.types.nullOr lib.types.bool;
191 extraOpts = lib.mkOption {
192 description = "Extra database options.";
193 type = lib.types.lines;
207 enable = lib.mkEnableOption "rippled, a decentralized cryptocurrency blockchain daemon implementing the XRP Ledger protocol in C++";
209 package = lib.mkPackageOption pkgs "rippled" { };
211 ports = lib.mkOption {
212 description = "Ports exposed by rippled";
213 type = with lib.types; attrsOf (submodule portOptions);
217 admin = ["127.0.0.1"];
230 protocol = ["ws" "wss"];
235 nodeDb = lib.mkOption {
236 description = "Rippled main database options.";
237 type = with lib.types; nullOr (submodule dbOptions);
250 tempDb = lib.mkOption {
251 description = "Rippled temporary database options.";
252 type = with lib.types; nullOr (submodule dbOptions);
256 importDb = lib.mkOption {
257 description = "Settings for performing a one-time import.";
258 type = with lib.types; nullOr (submodule dbOptions);
262 nodeSize = lib.mkOption {
264 Rippled size of the node you are running.
265 "tiny", "small", "medium", "large", and "huge"
267 type = lib.types.enum ["tiny" "small" "medium" "large" "huge"];
273 List of hostnames or ips where the Ripple protocol is served.
274 For a starter list, you can either copy entries from:
275 https://ripple.com/ripple.txt or if you prefer you can let it
276 default to r.ripple.com 51235
278 A port may optionally be specified after adding a space to the
279 address. By convention, if known, IPs are listed in from most
282 type = lib.types.listOf lib.types.str;
283 default = ["r.ripple.com 51235"];
286 ipsFixed = lib.mkOption {
288 List of IP addresses or hostnames to which rippled should always
289 attempt to maintain peer connections with. This is useful for
290 manually forming private networks, for example to configure a
291 validation server that connects to the Ripple network through a
292 public-facing server, or for building a set of cluster peers.
294 A port may optionally be specified after adding a space to the address
296 type = lib.types.listOf lib.types.str;
300 validators = lib.mkOption {
302 List of nodes to always accept as validators. Nodes are specified by domain
305 type = lib.types.listOf lib.types.str;
307 "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1"
308 "n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2"
309 "n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3"
310 "n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4"
311 "n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5"
315 databasePath = lib.mkOption {
317 Path to the ripple database.
319 type = lib.types.path;
320 default = "/var/lib/rippled";
323 validationQuorum = lib.mkOption {
325 The minimum number of trusted validations a ledger must have before
326 the server considers it fully validated.
328 type = lib.types.int;
332 ledgerHistory = lib.mkOption {
334 The number of past ledgers to acquire on server startup and the minimum
335 to maintain while running.
337 type = lib.types.either lib.types.int (lib.types.enum ["full"]);
338 default = 1296000; # 1 month
341 fetchDepth = lib.mkOption {
343 The number of past ledgers to serve to other peers that request historical
344 ledger data (or "full" for no limit).
346 type = lib.types.either lib.types.int (lib.types.enum ["full"]);
350 sntpServers = lib.mkOption {
352 IP address or domain of NTP servers to use for time synchronization.;
354 type = lib.types.listOf lib.types.str;
363 logLevel = lib.mkOption {
364 description = "Logging verbosity.";
365 type = lib.types.enum ["debug" "error" "info"];
370 enable = lib.mkEnableOption "statsd monitoring for rippled";
372 address = lib.mkOption {
373 description = "The UDP address and port of the listening StatsD server.";
374 default = "127.0.0.1:8125";
375 type = lib.types.str;
378 prefix = lib.mkOption {
379 description = "A string prepended to each collected metric.";
381 type = lib.types.str;
385 extraConfig = lib.mkOption {
387 type = lib.types.lines;
389 Extra lines to be added verbatim to the rippled.cfg configuration file.
393 config = lib.mkOption {
395 default = pkgs.writeText "rippled.conf" rippledCfg;
396 defaultText = lib.literalMD "generated config file";
402 ###### implementation
404 config = lib.mkIf cfg.enable {
406 users.users.rippled = {
407 description = "Ripple server user";
410 home = cfg.databasePath;
413 users.groups.rippled = {};
415 systemd.services.rippled = {
416 after = [ "network.target" ];
417 wantedBy = [ "multi-user.target" ];
420 ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}";
422 Restart = "on-failure";
427 environment.systemPackages = [ cfg.package ];