anchor.diff:        { name = "rippled";
           description = "Ripple server user";
           uid = config.ids.uids.rippled;
  -	home = "/var/lib/rippled";
  +	home = cfg.databasePath;
  +	createHome = true;
         };
   
       systemd.services.rippled = {
  -      path = [ pkgs.rippled ];
  -
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
   
         serviceConfig = {
  -        ExecStart = "${pkgs.rippled}/bin/rippled --fg -q --conf ${rippledStateCfgFile}";
  -	WorkingDirectory = "/var/lib/rippled";
  -      };
  -    };
  -
  -    networking.firewall.allowedTCPPorts = mkIf (cfg.peerIp != null) [ cfg.peerPort ];
  -
  -    system.activationScripts.rippled = ''
  -      mkdir -p /var/{lib,log}/rippled
  -      chown -R rippled /var/{lib,log}/rippled
  -      ln -sf ${rippledCfgFile} ${rippledStateCfgFile}
  -    '';
  +        ExecStart = "${cfg.package}/bin/rippled --fg --conf ${cfg.config}";
  +        User = "rippled";
  +      };
  +    };
  +
  +    environment.systemPackages = [ cfg.package ];
  +
     };
   }
  
positive.code: # configuration building is commented out until better tested.
  
  { config, lib, pkgs, ... }:
  
  with lib;
  
negative.code: let
    cfg = config.services.rippled;
  
    b2i = val: if val then "1" else "0";
  
    dbCfg = db: ''
      type=${db.type}
      path=${db.path}
      ${optionalString (db.compression != null) ("compression=${b2i db.compression}") }
      ${optionalString (db.onlineDelete != null) ("online_delete=${toString db.onlineDelete}")}
      ${optionalString (db.advisoryDelete != null) ("advisory_delete=${toString db.advisoryDelete}")}
      ${db.extraOpts}
    '';
  
    rippledCfg = ''
      [server]
      ${concatMapStringsSep "\n" (n: "port_${n}") (attrNames cfg.ports)}
  
      ${concatMapStrings (p: ''
      [port_${p.name}]
      ip=${p.ip}
      port=${toString p.port}
      protocol=${concatStringsSep "," p.protocol}
      ${optionalString (p.user != "") "user=${p.user}"}
      ${optionalString (p.password != "") "user=${p.password}"}
      admin=${if p.admin then "allow" else "no"}
      ${optionalString (p.ssl.key != null) "ssl_key=${p.ssl.key}"}
      ${optionalString (p.ssl.cert != null) "ssl_cert=${p.ssl.cert}"}
      ${optionalString (p.ssl.chain != null) "ssl_chain=${p.ssl.chain}"}
      '') (attrValues cfg.ports)}
  
      [database_path]
      ${cfg.databasePath}
  
      [node_db]
      ${dbCfg cfg.nodeDb}
  
      ${optionalString (cfg.tempDb != null) ''
      [temp_db]
      ${dbCfg cfg.tempDb}''}
  
      ${optionalString (cfg.importDb != null) ''
      [import_db]
      ${dbCfg cfg.importDb}''}
  
      [ips]
      ${concatStringsSep "\n" cfg.ips}
  
      [ips_fixed]
      ${concatStringsSep "\n" cfg.ipsFixed}
  
      [validators]
      ${concatStringsSep "\n" cfg.validators}
  
      [node_size]
      ${cfg.nodeSize}
  
      [ledger_history]
      ${toString cfg.ledgerHistory}
  
      [fetch_depth]
      ${toString cfg.fetchDepth}
  
      [validation_quorum]
      ${toString cfg.validationQuorum}
  
      [sntp_servers]
      ${concatStringsSep "\n" cfg.sntpServers}
  
      [rpc_startup]
      { "command": "log_level", "severity": "${cfg.logLevel}" }
    '' + cfg.extraConfig;
  
    portOptions = { name, ...}: {
      options = {
        name = mkOption {
  	internal = true;
  	default = name;
        };
  
        ip = mkOption {
  	default = "127.0.0.1";
  	description = "Ip where rippled listens.";
  	type = types.str;
        };
  
        port = mkOption {
  	description = "Port where rippled listens.";
  	type = types.int;
        };
  
        protocol = mkOption {
  	description = "Protocols expose by rippled.";
  	type = types.listOf (types.enum ["http" "https" "ws" "wss" "peer"]);
        };
  
        user = mkOption {
  	description = "When set, these credentials will be required on HTTP/S requests.";
  	type = types.str;
  	default = "";
        };
  
        password = mkOption {
  	description = "When set, these credentials will be required on HTTP/S requests.";
  	type = types.str;
  	default = "";
        };
  
        admin = mkOption {
  	description = "Controls whether or not administrative commands are allowed.";
  	type = types.bool;
  	default = false;
        };
  
        ssl = {
  	key = mkOption {
  	  description = ''
  	    Specifies the filename holding the SSL key in PEM format.
  	  '';
  	  default = null;
  	  type = types.nullOr types.path;
  	};
  
  	cert = mkOption {
  	  description = ''
  	    Specifies the path to the SSL certificate file in PEM format.
  	    This is not needed if the chain includes it.
  	  '';
  	  default = null;
  	  type = types.nullOr types.path;
  	};
  
  	chain = mkOption {
  	  description = ''
  	    If you need a certificate chain, specify the path to the
  	    certificate chain here. The chain may include the end certificate.
  	  '';
  	  default = null;
  	  type = types.nullOr types.path;
  	};
  
        };
      };
    };
  
    dbOptions = {
      type = mkOption {
        description = "Rippled database type.";
        type = types.enum ["rocksdb" "nudb" "sqlite"];
        default = "rocksdb";
      };
  
      path = mkOption {
        description = "Location to store the database.";
        type = types.path;
        default = cfg.databasePath;
      };
  
      compression = mkOption {
        description = "Whether to enable snappy compression.";
        type = types.nullOr types.bool;
        default = null;
      };
  
      onlineDelete = mkOption {
        description = "Enable automatic purging of older ledger information.";
        type = types.addCheck (types.nullOr types.int) (v: v > 256);
        default = cfg.ledgerHistory;
      };
  
      advisoryDelete = mkOption {
        description = ''
  	If set, then require administrative RPC call "can_delete"
  	to enable online deletion of ledger records.
        '';
        type = types.nullOr types.bool;
        default = null;
      };
  
      extraOpts = mkOption {
        description = "Extra database options.";
        type = types.lines;
        default = "";
      };
    };
  
  in
  
  {
  