32 enable = mkEnableOption "fedimintd";
34 package = mkPackageOption pkgs "fedimint" { };
36 environment = mkOption {
37 type = types.attrsOf types.str;
38 description = "Extra Environment variables to pass to the fedimintd.";
43 RUST_LOG = "info,fm=debug";
49 openFirewall = mkOption {
52 description = "Opens port in firewall for fedimintd's p2p port";
57 description = "Port to bind on for p2p connections from peers";
62 description = "Address to bind on for p2p connections from peers";
66 example = "fedimint://p2p.myfedimint.com:8173";
68 Public address for p2p connections from peers
73 openFirewall = mkOption {
76 description = "Opens port in firewall for fedimintd's api port";
81 description = "Port to bind on for API connections relied by the reverse proxy/tls terminator.";
85 default = "127.0.0.1";
86 description = "Address to bind on for API connections relied by the reverse proxy/tls terminator.";
91 Public URL of the API address of the reverse proxy/tls terminator. Usually starting with `wss://`.
100 description = "Bitcoin network to participate in.";
105 default = "http://127.0.0.1:38332";
107 description = "Bitcoin node (bitcoind/electrum/esplora) address to connect to";
112 default = "bitcoind";
113 example = "electrum";
114 description = "Kind of a bitcoin node.";
117 secretFile = mkOption {
118 type = types.nullOr types.path;
121 If set the URL specified in `bitcoin.rpc.url` will get the content of this file added
122 as an URL password, so `http://user@example.com` will turn into `http://user:SOMESECRET@example.com`.
126 `/etc/nix-bitcoin-secrets/bitcoin-rpcpassword-public` (for nix-bitcoin default)
132 consensus.finalityDelay = mkOption {
133 type = types.ints.unsigned;
135 description = "Consensus peg-in finality delay.";
140 default = "/var/lib/fedimintd-${name}/";
143 Path to the data dir fedimintd will use to store its data.
144 Note that due to using the DynamicUser feature of systemd, this value should not be changed
145 and is set to be read only.
154 Whether to configure nginx for fedimintd
159 example = "api.myfedimint.com";
160 description = "Public domain of the API address of the reverse proxy/tls terminator.";
166 description = "Path to host the API on and forward to the daemon's api port";
169 type = types.submodule (
170 recursiveUpdate (import ../web-servers/nginx/vhost-options.nix {
175 description = "Overrides to the nginx vhost section for api";
183 services.fedimintd = mkOption {
184 type = types.attrsOf (types.submodule fedimintdOpts);
186 description = "Specification of one or more fedimintd instances.";
192 eachFedimintd = filterAttrs (fedimintdName: cfg: cfg.enable) config.services.fedimintd;
193 eachFedimintdNginx = filterAttrs (fedimintdName: cfg: cfg.nginx.enable) eachFedimintd;
195 mkIf (eachFedimintd != { }) {
197 networking.firewall.allowedTCPPorts = concatLists (
200 (lib.optional cfg.api.openFirewall cfg.api.port ++ lib.optional cfg.p2p.openFirewall cfg.p2p.port)
204 systemd.services = mapAttrs' (
206 (nameValuePair "fedimintd-${fedimintdName}" (
208 startScript = pkgs.writeShellScript "fedimintd-start" (
210 if cfg.bitcoin.rpc.secretFile != null then
212 secret=$(${pkgs.coreutils}/bin/head -n 1 "${cfg.bitcoin.rpc.secretFile}")
213 prefix="''${FM_BITCOIN_RPC_URL%*@*}" # Everything before the last '@'
214 suffix="''${FM_BITCOIN_RPC_URL##*@}" # Everything after the last '@'
215 FM_BITCOIN_RPC_URL="''${prefix}:''${secret}@''${suffix}"
221 exec ${cfg.package}/bin/fedimintd
226 description = "Fedimint Server";
227 documentation = [ "https://github.com/fedimint/fedimint/" ];
228 wantedBy = [ "multi-user.target" ];
229 environment = lib.mkMerge [
231 FM_BIND_P2P = "${cfg.p2p.bind}:${toString cfg.p2p.port}";
232 FM_BIND_API = "${cfg.api.bind}:${toString cfg.api.port}";
233 FM_P2P_URL = cfg.p2p.url;
234 FM_API_URL = cfg.api.url;
235 FM_DATA_DIR = cfg.dataDir;
236 FM_BITCOIN_NETWORK = cfg.bitcoin.network;
237 FM_BITCOIN_RPC_URL = cfg.bitcoin.rpc.url;
238 FM_BITCOIN_RPC_KIND = cfg.bitcoin.rpc.kind;
245 StateDirectory = "fedimintd-${fedimintdName}";
246 StateDirectoryMode = "0700";
247 ExecStart = startScript;
253 LimitNOFILE = "100000";
255 LockPersonality = true;
256 MemoryDenyWriteExecute = true;
257 NoNewPrivileges = true;
258 PrivateDevices = true;
259 PrivateMounts = true;
262 ProtectControlGroups = true;
263 ProtectHostname = true;
264 ProtectKernelLogs = true;
265 ProtectKernelModules = true;
266 ProtectKernelTunables = true;
267 ProtectSystem = "full";
268 RestrictAddressFamilies = [
272 RestrictNamespaces = true;
273 RestrictRealtime = true;
274 SystemCallArchitectures = "native";
284 services.nginx.virtualHosts = mapAttrs' (
286 (nameValuePair cfg.nginx.fqdn (
291 # Note: we want by default to enable OpenSSL, but it seems anything 100 and above is
292 # overriden by default value from vhost-options.nix
293 enableACME = mkOverride 99 true;
294 forceSSL = mkOverride 99 true;
295 locations.${cfg.nginx.path} = {
296 proxyPass = "http://127.0.0.1:${toString cfg.api.port}/";
297 proxyWebsockets = true;
299 proxy_pass_header Authorization;
305 ) eachFedimintdNginx;
308 meta.maintainers = with lib.maintainers; [ dpc ];