{ config, lib, pkgs, ... }: let dcrdEnabled = config.services.dcrd.enable or false; cfg = config.services.dcrwallet; in { options.services.dcrwallet = with lib; { enable = mkEnableOption "Decred wallet daemon"; package = mkOption { type = types.package; default = pkgs.dcrwallet; description = "Package providing the dcrwallet binary"; }; user = mkOption { type = types.str; default = "dcrwallet"; description = "User to run dcrwallet as"; }; group = mkOption { type = types.str; default = cfg.user; description = "Group to run dcrwallet as"; }; dataDir = mkOption { type = types.path; default = "/var/lib/dcrwallet"; description = "State directory for dcrwallet (appdata)"; }; configFile = mkOption { type = types.path; description = "Path to dcrwallet configuration file (runtime path)"; }; extraPackages = mkOption { type = types.listOf types.package; default = []; example = [ pkgs.dcrctl ]; description = "Additional packages to add to the dcrwallet user's environment"; }; operator = { enable = mkEnableOption "Install dcrwallet CLI packages for an operator user"; name = mkOption { type = types.str; default = "operator"; description = "Operator username to receive dcrwallet package(s) in their environment."; }; }; }; config = lib.mkIf cfg.enable (lib.mkMerge [ { users.users.${cfg.user} = { group = cfg.group; home = cfg.dataDir; isNormalUser = true; packages = [ cfg.package ] ++ cfg.extraPackages; # dcrwallet needs read access to the dcrd RPC certificate extraGroups = lib.optional (dcrdEnabled && config.services.dcrd.group != cfg.group) config.services.dcrd.group; }; users.groups.${cfg.group} = {}; systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} -" ]; systemd.services.dcrwallet = { description = "Decred wallet daemon"; after = [ "network-online.target" ] ++ lib.optional dcrdEnabled "dcrd.service"; wants = [ "network-online.target" ] ++ lib.optional dcrdEnabled "dcrd.service"; wantedBy = [ "multi-user.target" ]; serviceConfig = { User = cfg.user; Group = cfg.group; RuntimeDirectory = "dcrwallet"; StateDirectory = "dcrwallet"; StateDirectoryMode = "0750"; ExecStart = ''${lib.getExe cfg.package} --appdata=${cfg.dataDir} --configfile=${cfg.configFile}''; }; restartTriggers = [ cfg.configFile ]; }; } (lib.mkIf cfg.operator.enable { users.users.${cfg.operator.name} = { packages = [ cfg.package ] ++ cfg.extraPackages; # Add the operator user to the dcrwallet group and dcrd group extraGroups = [ cfg.group ] ++ lib.optional (dcrdEnabled && config.services.dcrd.group != cfg.group) config.services.dcrd.group; }; # Ensure operator (a member of cfg.group) can read rpc.cert which dcrwallet # writes with mode 0600 by default. We watch for the file and set g+r. systemd.services.dcrwallet-cert-perms = { description = "Ensure group-read on dcrwallet rpc.cert"; after = [ "dcrwallet.service" ]; serviceConfig = { Type = "oneshot"; }; script = '' if [ -e ${cfg.dataDir}/rpc.cert ]; then ${pkgs.coreutils}/bin/chmod g+r ${cfg.dataDir}/rpc.cert fi ''; }; systemd.paths.dcrwallet-cert-perms = { description = "Watch dcrwallet rpc.cert to fix permissions"; pathConfig = { # PathModified = "${cfg.dataDir}/rpc.cert"; PathChanged = "${cfg.dataDir}/rpc.cert"; }; wantedBy = [ "multi-user.target" ]; }; }) ]); }