Radish alpha
r
rad:z254T5p17bdFPmzfDojsdjo4HjpoZ
Radicle Infrastructure as Code (NixOS, OpenTofu, …)
Radicle
Git
radicle-infra os mixin radicle.nix
{
  config,
  pkgs,
  lib,
  ...
}: let
  enableCache = config.networking.hostName == "iris" || config.networking.hostName == "seed";
in {
  networking.firewall.allowedTCPPorts = [
    8776 # radicle-node
    58776 # radicle-node
  ];

  services.radicle = {
    enable = true;
    package = pkgs.radicle-node-overlay;
    privateKeyFile = "/etc/ssh/ssh_host_ed25519_key";
    publicKey = "/etc/ssh/ssh_host_ed25519_key.pub";

    node.extraArgs = ["--listen" "[::]:58776"];

    settings = {
      node = {
        alias = config.networking.fqdn;
        externalAddresses = lib.mkBefore [
          "${config.networking.fqdn}:58776"
        ];
        connect = [
          "z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@rosa.radicle.xyz:58776"
          "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@iris.radicle.xyz:58776"
          "z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:58776"
        ];
        peers.type = "dynamic";
        db.journalMode = "wal";
        workers = 32;
        relay = "always";
        onion = lib.mkIf config.services.tor.enable {
          mode = "proxy";
          address = "127.0.0.1:9050";
        };
      };
      web = {
        name = config.networking.fqdn;
        imageUrl = "https://radicle.xyz/assets/images/radicle.svg";
        pinned.repositories = [
          # Pinned repositories are sorted alphanumerically by repository ID.
          "rad:z254T5p17bdFPmzfDojsdjo4HjpoZ" # radicle-infra
          "rad:z2UcCU1LgMshWvXj6hXSDDrwB8q8M" # radicle-job
          "rad:z371PVmDHdjJucejRoRYJcDEvD5pp" # radicle-docs
          "rad:z39mP9rQAaGmERfUMPULfPUi473tY" # radicle-tui
          "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5" # heartwood
          "rad:z3qg5TKmN83afz2fj9z3fQjU8vaYE" # radicle-native-ci
          "rad:z3trNYnLWS11cJWC6BbxDs5niGo82" # rips
          "rad:z4D5UCArafTzTQpDZNQRuqswh3ury" # radicle-desktop
          "rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5" # radicle-explorer
          "rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt" # radicle-git
          "rad:zwTxygwuz5LDGBq255RA2CbNGrz8" # radicle-ci-broker
        ];
      };
    };

    httpd = {
      enable = true;
      nginx = let
        proxyPass = "http://${config.services.radicle.httpd.listenAddress}:${toString config.services.radicle.httpd.listenPort}";
        recommendedProxySettings = true;
        cache = header:
          lib.mkForce {
            inherit proxyPass recommendedProxySettings;
            extraConfig = ''
              proxy_cache radicle;
              add_header Cache-Control "${header}";
            '';
          };
      in {
        enableACME = true;
        forceSSL = true;
        serverName = config.networking.fqdn;

        locations = lib.mkIf enableCache {
          "/" = lib.mkForce {
            inherit proxyPass recommendedProxySettings;
            extraConfig = ''
              proxy_cache radicle;
            '';
          };

          # Repository policy and scope. Does not change often,
          # we can wait two minutes.
          "^~ /api/v1/node/policies/repos/" = cache "public, max-age=120";

          # Data about a node, like alias and SSH key. Changes rarely.
          "^~ /api/v1/nodes/z6Mk.+(?!/)/" = cache "public, max-age=600";

          # General information about the repository, reports contents of
          # from the identity document and looks at some refs.
          # Should be relatively quick (especially refs).
          "~ /api/v1/repos/rad:.+(?!/)" = cache "public, max-age=30";

          # Job COBs are not cached, so are very slow to load.
          # Even though we would like to have them always refresh,
          # cache them for a bit for overall speed.
          "~ \"/api/v1/repos/rad:.+/jobs/([0-9a-f]{40}|[0-9a-f]{64})\"" = cache "public, max-age=60";

          # Explorer likes to request this *a lot*. Debounce server side.
          "~ /api/v1/repos/rad:.+/remotes" = cache "public, max-age=120";

          # Reports the number of repositories seeded, not so critical.
          "/api/v1/stats" = cache "public, max-age=600";

          # Reads from a commit. Responses never change.
          "^~ \"/raw/rad:.+/([0-9a-f]{40}|[0-9a-f]{64})\"" = cache "public, max-age=604800, immutable";
        };
      };
    };
  };

  services.nginx = lib.mkIf enableCache {
    # https://blog.nginx.org/blog/nginx-caching-guide
    # https://docs.nginx.com/nginx/admin-guide/content-cache/content-caching/
    appendHttpConfig = ''
      proxy_cache_path /var/cache/nginx/radicle
        inactive=24h
        keys_zone=radicle:10m
        max_size=4g
        use_temp_path=off
        levels=1:2
        ;
    '';
  };

  fileSystems."/var/cache/nginx/radicle" = {
    fsType = "tmpfs";
    options = [
      "owner=${config.services.nginx.user}"
      "group=${config.services.nginx.group}"
    ];
  };

  systemd.services.radicle-node.serviceConfig.SocketBindAllow = ["tcp:58776"];
}