Radicle node (decentralised git) not easily deployable (broken?)

I’m trying to host a Radicle node on NixOS with the web frontend being behind an nginx reverse proxy. It happens that radicle has quite a lot of attributes directly in nixpkgs, so it should be possible to configure it somewhat easily. I have not found that to be the case, though.

There is a blog post that describes this (without the reverse proxy): Setting up own Radicle Seed Node with NixoS – but it does not work out of the box, since the “settings” section no longer provides a valid config.json one year after the blog post. The structure of the example settings is the same as in man configuration.nix. I might be doing something wrong, but I would have thought copy-pasting the example would just work.

Here is a quick list of radicle attributes and defaults:

services.radicle.enable (false)
services.radicle.package (pkgs.radicle-node)
services.radicle.checkConfig (true)
services.radicle.node.extraArgs ([])
services.radicle.node.listenAddress ([::])
services.radicle.node.listenPort (8776)
services.radicle.node.openFirewall (false)
services.radicle.privateKeyFile
services.radicle.publicKey
services.radicle.settings ({})
...and 55 of `services.radicle.httpd.nginx.*` settings (!!!)
services.radicle.httpd.enable (false)
services.radicle.httpd.package (pkgs.radicle-httpd)
services.radicle.httpd.extraArgs ([])
services.radicle.httpd.listenAddress (127.0.0.1)
services.radicle.httpd.listenPort (8080)
services.radicle.httpd.nginx (null)
services.radicle.httpd.nginx.enableACME (false)
services.radicle.httpd.nginx.acmeFallbackHost (null)
services.radicle.httpd.nginx.acmeRoot (/var/lib/acme/acme-challenge)
services.radicle.httpd.nginx.addSSL (false)
services.radicle.httpd.nginx.basicAuth ({})
services.radicle.httpd.nginx.basicAuthFile (null)
services.radicle.httpd.nginx.default (false)
services.radicle.httpd.nginx.extraConfig ()
services.radicle.httpd.nginx.forceSSL (false)
services.radicle.httpd.nginx.globalRedirect (null)
services.radicle.httpd.nginx.http2 (true)
services.radicle.httpd.nginx.http3 (true)
services.radicle.httpd.nginx.http3_hq (false)
services.radicle.httpd.nginx.kTLS (false)
services.radicle.httpd.nginx.listen ([])
services.radicle.httpd.nginx.listen.*.addr
services.radicle.httpd.nginx.listen.*.extraParameters ([])
services.radicle.httpd.nginx.listen.*.port (null)
services.radicle.httpd.nginx.listen.*.proxyProtocol (false)
services.radicle.httpd.nginx.listen.*.ssl (false)
services.radicle.httpd.nginx.listenAddresses ([])
services.radicle.httpd.nginx.locations ({})
services.radicle.httpd.nginx.locations.<name>.alias (null)
services.radicle.httpd.nginx.locations.<name>.basicAuth ({})
services.radicle.httpd.nginx.locations.<name>.basicAuthFile (null)
services.radicle.httpd.nginx.locations.<name>.extraConfig ()
services.radicle.httpd.nginx.locations.<name>.fastcgiParams ({})
services.radicle.httpd.nginx.locations.<name>.index (null)
services.radicle.httpd.nginx.locations.<name>.priority (1000)
services.radicle.httpd.nginx.locations.<name>.proxyPass (null)
services.radicle.httpd.nginx.locations.<name>.proxyWebsockets (false)
services.radicle.httpd.nginx.locations.<name>.recommendedProxySettings (...)
services.radicle.httpd.nginx.locations.<name>.recommendedUwsgiSettings (...)
services.radicle.httpd.nginx.locations.<name>.return (null)
services.radicle.httpd.nginx.locations.<name>.root (null)
services.radicle.httpd.nginx.locations.<name>.tryFiles (null)
services.radicle.httpd.nginx.locations.<name>.uwsgiPass (null)
services.radicle.httpd.nginx.onlySSL (false)
services.radicle.httpd.nginx.quic (false)
services.radicle.httpd.nginx.redirectCode (301)
services.radicle.httpd.nginx.rejectSSL (false)
services.radicle.httpd.nginx.reuseport (false)
services.radicle.httpd.nginx.root (null)
services.radicle.httpd.nginx.serverAliases ([])
services.radicle.httpd.nginx.serverName (radicle-${hostname}.${domain})
services.radicle.httpd.nginx.sslCertificate
services.radicle.httpd.nginx.sslCertificateKey
services.radicle.httpd.nginx.sslTrustedCertificate (null)
services.radicle.httpd.nginx.useACMEHost (null)

Here is my attempt at a configuration without the reverse proxy part:

{ pkgs, config, lib, ... }: {
  config = lib.mkIf config.services.radicle.enable {
    services.radicle = {
      privateKeyFile = "/home/sshine/.ssh/id_ed25519";
      publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... sshine@...";
      node.openFirewall = true;
      node.listenAddress = "0.0.0.0";
      httpd.enable = true;
      httpd.listenPort = 3010;

      settings = {
        web.pinned.repositories = [
          "rad:z2eeB9LF8fDNJQaEcvAWxQmU7h2PG" # fedimint
        ];
      };
    };
  };
}

It currently fails with:

error: builder for '/nix/store/cgkiixvhi2wnphhky000a90y1vwplijw-config.json.drv' failed with exit code 1;
       last 12 log lines:
       >      1       {
       >      2          "web": {
       >      3           "pinned": {
       >      4        "repositories": [
       >      5          "rad:z2eeB9LF8fDNJQaEcvAWxQmU7h2PG"
       >      6            ]
       >      7      }
       >      8      }
       >      9      }
       > Invalid config.json according to rad.
       > Please double-check your services.radicle.settings (producing the config.json above),
       > some settings may be missing or have the wrong type.
       For full logs, run:
         nix log /nix/store/cgkiixvhi2wnphhky000a90y1vwplijw-config.json.drv

Variations I’ve tried that cause the same error:

  • Setting services.radicle.settings = {}; (default)
  • Setting services.radicle.httpd.enable = false; (default)

I tried to look for Radicle config.json examples, but I wasn’t able to find any.

Is Radicle broken in nixpkgs, or is the documentation insufficient?