1 diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
 
   2 index da4cd164bf2..30190788f33 100644
 
   3 --- a/nixos/lib/make-options-doc/options-to-docbook.xsl
 
   4 +++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
 
   6        <title>Configuration Options</title>
 
   7        <variablelist xml:id="configuration-variable-list">
 
   8          <xsl:for-each select="attrs">
 
   9 -          <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '<', '_'), '>', '_'))" />
 
  10 +          <xsl:variable name="id" select="concat('opt-', str:replace(str:replace(str:replace(str:replace(attr[@name = 'name']/string/@value, '*', '_'), '<', '_'), '>', '_'), ':', '_'))" />
 
  12              <term xlink:href="#{$id}">
 
  13                <xsl:attribute name="xml:id"><xsl:value-of select="$id"/></xsl:attribute>
 
  14 diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
 
  15 index 578d9d9ec8d..e7ca0d4e34c 100644
 
  16 --- a/nixos/modules/services/databases/redis.nix
 
  17 +++ b/nixos/modules/services/databases/redis.nix
 
  18 @@ -5,17 +5,18 @@ with lib;
 
  20    cfg = config.services.redis;
 
  22 -  ulimitNofile = cfg.maxclients + 32;
 
  24    mkValueString = value:
 
  25      if value == true then "yes"
 
  26      else if value == false then "no"
 
  27      else generators.mkValueStringDefault { } value;
 
  29 -  redisConfig = pkgs.writeText "redis.conf" (generators.toKeyValue {
 
  30 +  redisConfig = settings: pkgs.writeText "redis.conf" (generators.toKeyValue {
 
  31      listsAsDuplicateKeys = true;
 
  32      mkKeyValue = generators.mkKeyValueDefault { inherit mkValueString; } " ";
 
  36 +  redisName = name: "redis" + optionalString (name != "") ("-"+name);
 
  37 +  enabledServers = filterAttrs (name: conf: conf.enable) config.services.redis.servers;
 
  41 @@ -25,6 +26,27 @@ in {
 
  42      (mkRemovedOptionModule [ "services" "redis" "appendOnlyFilename" ] "This option was never used.")
 
  43      (mkRemovedOptionModule [ "services" "redis" "pidFile" ] "This option was removed.")
 
  44      (mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.settings instead.")
 
  45 +    (mkRenamedOptionModule [ "services" "redis" "enable"] [ "services" "redis" "servers" "" "enable" ])
 
  46 +    (mkRenamedOptionModule [ "services" "redis" "port"] [ "services" "redis" "servers" "" "port" ])
 
  47 +    (mkRenamedOptionModule [ "services" "redis" "openFirewall"] [ "services" "redis" "servers" "" "openFirewall" ])
 
  48 +    (mkRenamedOptionModule [ "services" "redis" "bind"] [ "services" "redis" "servers" "" "bind" ])
 
  49 +    (mkRenamedOptionModule [ "services" "redis" "unixSocket"] [ "services" "redis" "servers" "" "unixSocket" ])
 
  50 +    (mkRenamedOptionModule [ "services" "redis" "unixSocketPerm"] [ "services" "redis" "servers" "" "unixSocketPerm" ])
 
  51 +    (mkRenamedOptionModule [ "services" "redis" "logLevel"] [ "services" "redis" "servers" "" "logLevel" ])
 
  52 +    (mkRenamedOptionModule [ "services" "redis" "logfile"] [ "services" "redis" "servers" "" "logfile" ])
 
  53 +    (mkRenamedOptionModule [ "services" "redis" "syslog"] [ "services" "redis" "servers" "" "syslog" ])
 
  54 +    (mkRenamedOptionModule [ "services" "redis" "databases"] [ "services" "redis" "servers" "" "databases" ])
 
  55 +    (mkRenamedOptionModule [ "services" "redis" "maxclients"] [ "services" "redis" "servers" "" "maxclients" ])
 
  56 +    (mkRenamedOptionModule [ "services" "redis" "save"] [ "services" "redis" "servers" "" "save" ])
 
  57 +    (mkRenamedOptionModule [ "services" "redis" "slaveOf"] [ "services" "redis" "servers" "" "slaveOf" ])
 
  58 +    (mkRenamedOptionModule [ "services" "redis" "masterAuth"] [ "services" "redis" "servers" "" "masterAuth" ])
 
  59 +    (mkRenamedOptionModule [ "services" "redis" "requirePass"] [ "services" "redis" "servers" "" "requirePass" ])
 
  60 +    (mkRenamedOptionModule [ "services" "redis" "requirePassFile"] [ "services" "redis" "servers" "" "requirePassFile" ])
 
  61 +    (mkRenamedOptionModule [ "services" "redis" "appendOnly"] [ "services" "redis" "servers" "" "appendOnly" ])
 
  62 +    (mkRenamedOptionModule [ "services" "redis" "appendFsync"] [ "services" "redis" "servers" "" "appendFsync" ])
 
  63 +    (mkRenamedOptionModule [ "services" "redis" "slowLogLogSlowerThan"] [ "services" "redis" "servers" "" "slowLogLogSlowerThan" ])
 
  64 +    (mkRenamedOptionModule [ "services" "redis" "slowLogMaxLen"] [ "services" "redis" "servers" "" "slowLogMaxLen" ])
 
  65 +    (mkRenamedOptionModule [ "services" "redis" "settings"] [ "services" "redis" "servers" "" "settings" ])
 
  69 @@ -32,18 +54,6 @@ in {
 
  78 -          Whether to enable the Redis server. Note that the NixOS module for
 
  79 -          Redis disables kernel support for Transparent Huge Pages (THP),
 
  80 -          because this features causes major performance problems for Redis,
 
  81 -          e.g. (https://redis.io/topics/latency).
 
  88 @@ -51,176 +61,226 @@ in {
 
  89          description = "Which Redis derivation to use.";
 
  95 -        description = "The port for Redis to listen to.";
 
  97 +      vmOverCommit = mkEnableOption ''
 
  98 +        setting of vm.overcommit_memory to 1
 
  99 +        (Suggested for Background Saving: http://redis.io/topics/faq)
 
 102 -      vmOverCommit = mkOption {
 
 106 -          Set vm.overcommit_memory to 1 (Suggested for Background Saving: http://redis.io/topics/faq)
 
 110 -      openFirewall = mkOption {
 
 114 -          Whether to open ports in the firewall for the server.
 
 119 -        type = with types; nullOr str;
 
 120 -        default = "127.0.0.1";
 
 122 -          The IP interface to bind to.
 
 123 -          <literal>null</literal> means "all interfaces".
 
 125 -        example = "192.0.2.1";
 
 128 -      unixSocket = mkOption {
 
 129 -        type = with types; nullOr path;
 
 131 -        description = "The path to the socket to bind to.";
 
 132 -        example = "/run/redis/redis.sock";
 
 135 -      unixSocketPerm = mkOption {
 
 138 -        description = "Change permissions for the socket";
 
 142 -      logLevel = mkOption {
 
 144 -        default = "notice"; # debug, verbose, notice, warning
 
 146 -        description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
 
 149 -      logfile = mkOption {
 
 151 -        default = "/dev/null";
 
 152 -        description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
 
 153 -        example = "/var/log/redis.log";
 
 156 -      syslog = mkOption {
 
 159 -        description = "Enable logging to the system logger.";
 
 162 -      databases = mkOption {
 
 165 -        description = "Set the number of databases.";
 
 168 -      maxclients = mkOption {
 
 171 -        description = "Set the max number of connected clients at the same time.";
 
 175 -        type = with types; listOf (listOf int);
 
 176 -        default = [ [900 1] [300 10] [60 10000] ];
 
 177 -        description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
 
 180 -      slaveOf = mkOption {
 
 181 -        type = with types; nullOr (submodule ({ ... }: {
 
 182 +      servers = mkOption {
 
 183 +        type = with types; attrsOf (submodule ({config, name, ...}@args: {
 
 187 -              description = "IP of the Redis master";
 
 188 -              example = "192.168.1.100";
 
 189 +            enable = mkEnableOption ''
 
 192 +              Note that the NixOS module for Redis disables kernel support
 
 193 +              for Transparent Huge Pages (THP),
 
 194 +              because this features causes major performance problems for Redis,
 
 195 +              e.g. (https://redis.io/topics/latency).
 
 200 +              default = redisName name;
 
 201 +              defaultText = "\"redis\" or \"redis-\${name}\" if name != \"\"";
 
 202 +              description = "The username and groupname for redis-server.";
 
 207 -              description = "port of the Redis master";
 
 210 +              description = "The port for Redis to listen to.";
 
 213 +            openFirewall = mkOption {
 
 217 +                Whether to open ports in the firewall for the server.
 
 222 +              type = with types; nullOr str;
 
 223 +              default = if name == "" then "127.0.0.1" else null;
 
 224 +              defaultText = "127.0.0.1 or null if name != \"\"";
 
 226 +                The IP interface to bind to.
 
 227 +                <literal>null</literal> means "all interfaces".
 
 229 +              example = "192.0.2.1";
 
 232 +            unixSocket = mkOption {
 
 233 +              type = with types; nullOr path;
 
 234 +              default = "/run/${redisName name}/redis.sock";
 
 235 +              defaultText = "\"/run/redis/redis.sock\" or \"/run/redis-\${name}/redis.sock\" if name != \"\"";
 
 236 +              description = "The path to the socket to bind to.";
 
 239 +            unixSocketPerm = mkOption {
 
 242 +              description = "Change permissions for the socket";
 
 246 +            logLevel = mkOption {
 
 248 +              default = "notice"; # debug, verbose, notice, warning
 
 250 +              description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
 
 253 +            logfile = mkOption {
 
 255 +              default = "/dev/null";
 
 256 +              description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
 
 257 +              example = "/var/log/redis.log";
 
 260 +            syslog = mkOption {
 
 263 +              description = "Enable logging to the system logger.";
 
 266 +            databases = mkOption {
 
 269 +              description = "Set the number of databases.";
 
 272 +            maxclients = mkOption {
 
 275 +              description = "Set the max number of connected clients at the same time.";
 
 279 +              type = with types; listOf (listOf int);
 
 280 +              default = [ [900 1] [300 10] [60 10000] ];
 
 281 +              description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
 
 284 +            slaveOf = mkOption {
 
 285 +              type = with types; nullOr (submodule ({ ... }: {
 
 289 +                    description = "IP of the Redis master";
 
 290 +                    example = "192.168.1.100";
 
 295 +                    description = "port of the Redis master";
 
 302 +              description = "IP and port to which this redis instance acts as a slave.";
 
 303 +              example = { ip = "192.168.1.100"; port = 6379; };
 
 306 +            masterAuth = mkOption {
 
 307 +              type = with types; nullOr str;
 
 309 +              description = ''If the master is password protected (using the requirePass configuration)
 
 310 +              it is possible to tell the slave to authenticate before starting the replication synchronization
 
 311 +              process, otherwise the master will refuse the slave request.
 
 312 +              (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
 
 315 +            requirePass = mkOption {
 
 316 +              type = with types; nullOr str;
 
 319 +                Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
 
 320 +                Use requirePassFile to store it outside of the nix store in a dedicated file.
 
 322 +              example = "letmein!";
 
 325 +            requirePassFile = mkOption {
 
 326 +              type = with types; nullOr path;
 
 328 +              description = "File with password for the database.";
 
 329 +              example = "/run/keys/redis-password";
 
 332 +            appendOnly = mkOption {
 
 335 +              description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
 
 338 +            appendFsync = mkOption {
 
 340 +              default = "everysec"; # no, always, everysec
 
 341 +              description = "How often to fsync the append-only log, options: no, always, everysec.";
 
 344 +            slowLogLogSlowerThan = mkOption {
 
 347 +              description = "Log queries whose execution take longer than X in milliseconds.";
 
 351 +            slowLogMaxLen = mkOption {
 
 354 +              description = "Maximum number of items to keep in slow log.";
 
 357 +            settings = mkOption {
 
 358 +              # TODO: this should be converted to freeformType
 
 359 +              type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
 
 362 +                Redis configuration. Refer to
 
 363 +                <link xlink:href="https://redis.io/topics/config"/>
 
 364 +                for details on supported values.
 
 366 +              example = literalExpression ''
 
 368 +                  loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
 
 373 +          config.settings = mkMerge [
 
 375 +              port = if config.bind == null then 0 else config.port;
 
 377 +              supervised = "systemd";
 
 378 +              loglevel = config.logLevel;
 
 379 +              logfile = config.logfile;
 
 380 +              syslog-enabled = config.syslog;
 
 381 +              databases = config.databases;
 
 382 +              maxclients = config.maxclients;
 
 383 +              save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
 
 384 +              dbfilename = "dump.rdb";
 
 385 +              dir = "/var/lib/${redisName name}";
 
 386 +              appendOnly = config.appendOnly;
 
 387 +              appendfsync = config.appendFsync;
 
 388 +              slowlog-log-slower-than = config.slowLogLogSlowerThan;
 
 389 +              slowlog-max-len = config.slowLogMaxLen;
 
 391 +            (mkIf (config.bind != null) { bind = config.bind; })
 
 392 +            (mkIf (config.unixSocket != null) {
 
 393 +              unixsocket = config.unixSocket;
 
 394 +              unixsocketperm = toString config.unixSocketPerm;
 
 396 +            (mkIf (config.slaveOf != null) { slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}"; })
 
 397 +            (mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
 
 398 +            (mkIf (config.requirePass != null) { requirepass = config.requirePass; })
 
 403 -        description = "IP and port to which this redis instance acts as a slave.";
 
 404 -        example = { ip = "192.168.1.100"; port = 6379; };
 
 407 -      masterAuth = mkOption {
 
 408 -        type = with types; nullOr str;
 
 410 -        description = ''If the master is password protected (using the requirePass configuration)
 
 411 -        it is possible to tell the slave to authenticate before starting the replication synchronization
 
 412 -        process, otherwise the master will refuse the slave request.
 
 413 -        (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
 
 416 -      requirePass = mkOption {
 
 417 -        type = with types; nullOr str;
 
 420 -          Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
 
 421 -          Use requirePassFile to store it outside of the nix store in a dedicated file.
 
 423 -        example = "letmein!";
 
 426 -      requirePassFile = mkOption {
 
 427 -        type = with types; nullOr path;
 
 429 -        description = "File with password for the database.";
 
 430 -        example = "/run/keys/redis-password";
 
 433 -      appendOnly = mkOption {
 
 436 -        description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
 
 439 -      appendFsync = mkOption {
 
 441 -        default = "everysec"; # no, always, everysec
 
 442 -        description = "How often to fsync the append-only log, options: no, always, everysec.";
 
 445 -      slowLogLogSlowerThan = mkOption {
 
 448 -        description = "Log queries whose execution take longer than X in milliseconds.";
 
 452 -      slowLogMaxLen = mkOption {
 
 455 -        description = "Maximum number of items to keep in slow log.";
 
 458 -      settings = mkOption {
 
 459 -        type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
 
 460 +        description = "Configuration of multiple <literal>redis-server</literal> instances.";
 
 463 -          Redis configuration. Refer to
 
 464 -          <link xlink:href="https://redis.io/topics/config"/>
 
 465 -          for details on supported values.
 
 467 -        example = literalExpression ''
 
 469 -            loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
 
 475 @@ -229,78 +289,61 @@ in {
 
 477    ###### implementation
 
 479 -  config = mkIf config.services.redis.enable {
 
 481 -      assertion = cfg.requirePass != null -> cfg.requirePassFile == null;
 
 482 -      message = "You can only set one services.redis.requirePass or services.redis.requirePassFile";
 
 484 -    boot.kernel.sysctl = (mkMerge [
 
 485 +  config = mkIf (enabledServers != {}) {
 
 487 +    assertions = attrValues (mapAttrs (name: conf: {
 
 488 +      assertion = conf.requirePass != null -> conf.requirePassFile == null;
 
 490 +        You can only set one services.redis.servers.${name}.requirePass
 
 491 +        or services.redis.servers.${name}.requirePassFile
 
 493 +    }) enabledServers);
 
 495 +    boot.kernel.sysctl = mkMerge [
 
 496        { "vm.nr_hugepages" = "0"; }
 
 497        ( mkIf cfg.vmOverCommit { "vm.overcommit_memory" = "1"; } )
 
 501 -    networking.firewall = mkIf cfg.openFirewall {
 
 502 -      allowedTCPPorts = [ cfg.port ];
 
 505 -    users.users.redis = {
 
 506 -      description = "Redis database user";
 
 508 -      isSystemUser = true;
 
 510 -    users.groups.redis = {};
 
 511 +    networking.firewall.allowedTCPPorts = concatMap (conf:
 
 512 +      optional conf.openFirewall conf.port
 
 513 +    ) (attrValues enabledServers);
 
 515      environment.systemPackages = [ cfg.package ];
 
 517 -    services.redis.settings = mkMerge [
 
 521 -        supervised = "systemd";
 
 522 -        loglevel = cfg.logLevel;
 
 523 -        logfile = cfg.logfile;
 
 524 -        syslog-enabled = cfg.syslog;
 
 525 -        databases = cfg.databases;
 
 526 -        maxclients = cfg.maxclients;
 
 527 -        save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") cfg.save;
 
 528 -        dbfilename = "dump.rdb";
 
 529 -        dir = "/var/lib/redis";
 
 530 -        appendOnly = cfg.appendOnly;
 
 531 -        appendfsync = cfg.appendFsync;
 
 532 -        slowlog-log-slower-than = cfg.slowLogLogSlowerThan;
 
 533 -        slowlog-max-len = cfg.slowLogMaxLen;
 
 535 -      (mkIf (cfg.bind != null) { bind = cfg.bind; })
 
 536 -      (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
 
 537 -      (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
 
 538 -      (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
 
 539 -      (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
 
 541 +    users.users = mapAttrs' (name: conf: nameValuePair (redisName name) {
 
 542 +      description = "System user for the redis-server instance ${name}";
 
 543 +      isSystemUser = true;
 
 544 +      group = redisName name;
 
 546 +    users.groups = mapAttrs' (name: conf: nameValuePair (redisName name) {
 
 549 -    systemd.services.redis = {
 
 550 -      description = "Redis Server";
 
 551 +    systemd.services = mapAttrs' (name: conf: nameValuePair (redisName name) {
 
 552 +      description = "Redis Server - ${redisName name}";
 
 554        wantedBy = [ "multi-user.target" ];
 
 555        after = [ "network.target" ];
 
 558 -        install -m 600 ${redisConfig} /run/redis/redis.conf
 
 559 -      '' + optionalString (cfg.requirePassFile != null) ''
 
 560 -        password=$(cat ${escapeShellArg cfg.requirePassFile})
 
 561 -        echo "requirePass $password" >> /run/redis/redis.conf
 
 565 -        ExecStart = "${cfg.package}/bin/redis-server /run/redis/redis.conf";
 
 566 +        ExecStart = "${cfg.package}/bin/redis-server /run/${redisName name}/redis.conf";
 
 567 +        ExecStartPre = [("+"+pkgs.writeShellScript "${redisName name}-credentials" (''
 
 568 +            install -o '${conf.user}' -m 600 ${redisConfig conf.settings} /run/${redisName name}/redis.conf
 
 569 +          '' + optionalString (conf.requirePassFile != null) ''
 
 571 +              printf requirePass' '
 
 572 +              cat ${escapeShellArg conf.requirePassFile}
 
 573 +            } >>/run/${redisName name}/redis.conf
 
 582          # Runtime directory and mode
 
 583 -        RuntimeDirectory = "redis";
 
 584 +        RuntimeDirectory = redisName name;
 
 585          RuntimeDirectoryMode = "0750";
 
 586          # State directory and mode
 
 587 -        StateDirectory = "redis";
 
 588 +        StateDirectory = redisName name;
 
 589          StateDirectoryMode = "0700";
 
 590          # Access write directories
 
 592 @@ -309,7 +352,7 @@ in {
 
 594          NoNewPrivileges = true;
 
 596 -        LimitNOFILE = "${toString ulimitNofile}";
 
 597 +        LimitNOFILE = mkDefault "${toString (conf.maxclients + 32)}";
 
 599          ProtectSystem = "strict";
 
 601 @@ -322,7 +365,9 @@ in {
 
 602          ProtectKernelModules = true;
 
 603          ProtectKernelTunables = true;
 
 604          ProtectControlGroups = true;
 
 605 -        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
 
 606 +        RestrictAddressFamilies =
 
 607 +          optionals (conf.bind != null) ["AF_INET" "AF_INET6"] ++
 
 608 +          optional (conf.unixSocket != null) "AF_UNIX";
 
 609          RestrictNamespaces = true;
 
 610          LockPersonality = true;
 
 611          MemoryDenyWriteExecute = true;
 
 612 @@ -333,6 +378,7 @@ in {
 
 613          SystemCallArchitectures = "native";
 
 614          SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
 
 621 diff --git a/nixos/modules/services/misc/sourcehut/builds.nix b/nixos/modules/services/misc/sourcehut/builds.nix
 
 622 deleted file mode 100644
 
 623 index f806e8c51b9..00000000000
 
 624 --- a/nixos/modules/services/misc/sourcehut/builds.nix
 
 627 -{ config, lib, pkgs, ... }:
 
 631 -  cfg = config.services.sourcehut;
 
 633 -  rcfg = config.services.redis;
 
 634 -  iniKey = "builds.sr.ht";
 
 636 -  drv = pkgs.sourcehut.buildsrht;
 
 639 -  options.services.sourcehut.builds = {
 
 642 -      default = "buildsrht";
 
 644 -        User for builds.sr.ht.
 
 652 -        Port on which the "builds" module should listen.
 
 656 -    database = mkOption {
 
 658 -      default = "builds.sr.ht";
 
 660 -        PostgreSQL database name for builds.sr.ht.
 
 664 -    statePath = mkOption {
 
 666 -      default = "${cfg.statePath}/buildsrht";
 
 668 -        State path for builds.sr.ht.
 
 672 -    enableWorker = mkOption {
 
 676 -        Run workers for builds.sr.ht.
 
 680 -    images = mkOption {
 
 681 -      type = types.attrsOf (types.attrsOf (types.attrsOf types.package));
 
 683 -      example = lib.literalExpression ''(let
 
 684 -          # Pinning unstable to allow usage with flakes and limit rebuilds.
 
 685 -          pkgs_unstable = builtins.fetchGit {
 
 686 -              url = "https://github.com/NixOS/nixpkgs";
 
 687 -              rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
 
 688 -              ref = "nixos-unstable";
 
 690 -          image_from_nixpkgs = pkgs_unstable: (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
 
 691 -            pkgs = (import pkgs_unstable {});
 
 695 -          nixos.unstable.x86_64 = image_from_nixpkgs pkgs_unstable;
 
 699 -        Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
 
 705 -  config = with scfg; let
 
 706 -    image_dirs = lib.lists.flatten (
 
 707 -      lib.attrsets.mapAttrsToList
 
 709 -          lib.attrsets.mapAttrsToList
 
 711 -              lib.attrsets.mapAttrsToList
 
 713 -                  pkgs.runCommand "buildsrht-images" { } ''
 
 714 -                    mkdir -p $out/${distro}/${rev}/${arch}
 
 715 -                    ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
 
 720 -    image_dir_pre = pkgs.symlinkJoin {
 
 721 -      name = "builds.sr.ht-worker-images-pre";
 
 722 -      paths = image_dirs ++ [
 
 723 -        "${pkgs.sourcehut.buildsrht}/lib/images"
 
 726 -    image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
 
 727 -      mkdir -p $out/images
 
 728 -      cp -Lr ${image_dir_pre}/* $out/images
 
 731 -  lib.mkIf (cfg.enable && elem "builds" cfg.services) {
 
 735 -          isSystemUser = true;
 
 737 -          extraGroups = lib.optionals cfg.builds.enableWorker [ "docker" ];
 
 738 -          description = "builds.sr.ht user";
 
 747 -    services.postgresql = {
 
 748 -      authentication = ''
 
 749 -        local ${database} ${user} trust
 
 751 -      ensureDatabases = [ database ];
 
 755 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
 762 -        "d ${statePath} 0755 ${user} ${user} -"
 
 763 -      ] ++ (lib.optionals cfg.builds.enableWorker
 
 764 -        [ "d ${statePath}/logs 0775 ${user} ${user} - -" ]
 
 768 -        buildsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey
 
 770 -            after = [ "postgresql.service" "network.target" ];
 
 771 -            requires = [ "postgresql.service" ];
 
 772 -            wantedBy = [ "multi-user.target" ];
 
 774 -            description = "builds.sr.ht website service";
 
 776 -            serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
 778 -            # Hack to bypass this hack: https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/item/srht-update-profiles#L6
 
 779 -          } // { preStart = " "; };
 
 781 -        buildsrht-worker = {
 
 782 -          enable = scfg.enableWorker;
 
 783 -          after = [ "postgresql.service" "network.target" ];
 
 784 -          requires = [ "postgresql.service" ];
 
 785 -          wantedBy = [ "multi-user.target" ];
 
 786 -          partOf = [ "buildsrht.service" ];
 
 787 -          description = "builds.sr.ht worker service";
 
 788 -          path = [ pkgs.openssh pkgs.docker ];
 
 789 -          preStart = let qemuPackage = pkgs.qemu_kvm;
 
 791 -            if [[ "$(docker images -q qemu:latest 2> /dev/null)" == "" || "$(cat ${statePath}/docker-image-qemu 2> /dev/null || true)" != "${qemuPackage.version}" ]]; then
 
 792 -              # Create and import qemu:latest image for docker
 
 794 -                pkgs.dockerTools.streamLayeredImage {
 
 797 -                  contents = [ qemuPackage ];
 
 800 -              # Mark down current package version
 
 801 -              printf "%s" "${qemuPackage.version}" > ${statePath}/docker-image-qemu
 
 808 -            Restart = "always";
 
 810 -          serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
 
 815 -    services.sourcehut.settings = {
 
 816 -      # URL builds.sr.ht is being served at (protocol://domain)
 
 817 -      "builds.sr.ht".origin = mkDefault "http://builds.${cfg.originBase}";
 
 818 -      # Address and port to bind the debug server to
 
 819 -      "builds.sr.ht".debug-host = mkDefault "0.0.0.0";
 
 820 -      "builds.sr.ht".debug-port = mkDefault port;
 
 821 -      # Configures the SQLAlchemy connection string for the database.
 
 822 -      "builds.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
 823 -      # Set to "yes" to automatically run migrations on package upgrade.
 
 824 -      "builds.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
 825 -      # builds.sr.ht's OAuth client ID and secret for meta.sr.ht
 
 826 -      # Register your client at meta.example.org/oauth
 
 827 -      "builds.sr.ht".oauth-client-id = mkDefault null;
 
 828 -      "builds.sr.ht".oauth-client-secret = mkDefault null;
 
 829 -      # The redis connection used for the celery worker
 
 830 -      "builds.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/3";
 
 831 -      # The shell used for ssh
 
 832 -      "builds.sr.ht".shell = mkDefault "runner-shell";
 
 833 -      # Register the builds.sr.ht dispatcher
 
 834 -      "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys"} = mkDefault "${user}:${user}";
 
 836 -      # Location for build logs, images, and control command
 
 837 -    } // lib.attrsets.optionalAttrs scfg.enableWorker {
 
 838 -      # Default worker stores logs that are accessible via this address:port
 
 839 -      "builds.sr.ht::worker".name = mkDefault "127.0.0.1:5020";
 
 840 -      "builds.sr.ht::worker".buildlogs = mkDefault "${scfg.statePath}/logs";
 
 841 -      "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
 
 842 -      "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
 
 843 -      "builds.sr.ht::worker".timeout = mkDefault "3m";
 
 846 -    services.nginx.virtualHosts."logs.${cfg.originBase}" =
 
 847 -      if scfg.enableWorker then {
 
 848 -        listen = with builtins; let address = split ":" cfg.settings."builds.sr.ht::worker".name;
 
 849 -        in [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
 
 850 -        locations."/logs".root = "${scfg.statePath}";
 
 853 -    services.nginx.virtualHosts."builds.${cfg.originBase}" = {
 
 855 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
 856 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
 857 -      locations."/static".root = "${pkgs.sourcehut.buildsrht}/${pkgs.sourcehut.python.sitePackages}/buildsrht";
 
 861 diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
 
 862 index 9c812d6b043..e6565e292f7 100644
 
 863 --- a/nixos/modules/services/misc/sourcehut/default.nix
 
 864 +++ b/nixos/modules/services/misc/sourcehut/default.nix
 
 866  { config, pkgs, lib, ... }:
 
 870 +  inherit (config.services) nginx postfix postgresql redis;
 
 871 +  inherit (config.users) users groups;
 
 872    cfg = config.services.sourcehut;
 
 873 -  cfgIni = cfg.settings;
 
 874 -  settingsFormat = pkgs.formats.ini { };
 
 875 +  domain = cfg.settings."sr.ht".global-domain;
 
 876 +  settingsFormat = pkgs.formats.ini {
 
 877 +    listToValue = concatMapStringsSep "," (generators.mkValueStringDefault {});
 
 879 +      if v == null then ""
 
 880 +      else generators.mkKeyValueDefault {
 
 882 +          if v == true then "yes"
 
 883 +          else if v == false then "no"
 
 884 +          else generators.mkValueStringDefault {} v;
 
 887 +  configIniOfService = srv: settingsFormat.generate "sourcehut-${srv}-config.ini"
 
 888 +    # Each service needs access to only a subset of sections (and secrets).
 
 889 +    (filterAttrs (k: v: v != null)
 
 890 +    (mapAttrs (section: v:
 
 891 +      let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht(::.*)?$" section; in
 
 892 +      if srvMatch == null # Include sections shared by all services
 
 893 +      || head srvMatch == srv # Include sections for the service being configured
 
 895 +      # Enable Web links and integrations between services.
 
 896 +      else if tail srvMatch == [ null ] && elem (head srvMatch) cfg.services
 
 898 +        inherit (v) origin;
 
 899 +        # mansrht crashes without it
 
 900 +        oauth-client-id = v.oauth-client-id or null;
 
 902 +      # Drop sub-sections of other services
 
 904 +    (recursiveUpdate cfg.settings {
 
 905 +      # Those paths are mounted using BindPaths= or BindReadOnlyPaths=
 
 906 +      # for services needing access to them.
 
 907 +      "builds.sr.ht::worker".buildlogs = "/var/log/sourcehut/buildsrht/logs";
 
 908 +      "git.sr.ht".post-update-script = "/var/lib/sourcehut/gitsrht/bin/post-update-script";
 
 909 +      "git.sr.ht".repos = "/var/lib/sourcehut/gitsrht/repos";
 
 910 +      "hg.sr.ht".changegroup-script = "/var/lib/sourcehut/hgsrht/bin/changegroup-script";
 
 911 +      "hg.sr.ht".repos = "/var/lib/sourcehut/hgsrht/repos";
 
 912 +      # Making this a per service option despite being in a global section,
 
 913 +      # so that it uses the redis-server used by the service.
 
 914 +      "sr.ht".redis-host = cfg.${srv}.redis.host;
 
 916 +  commonServiceSettings = srv: {
 
 917 +    origin = mkOption {
 
 918 +      description = "URL ${srv}.sr.ht is being served at (protocol://domain)";
 
 920 +      default = "https://${srv}.${domain}";
 
 921 +      defaultText = "https://${srv}.example.com";
 
 923 +    debug-host = mkOption {
 
 924 +      description = "Address to bind the debug server to.";
 
 925 +      type = with types; nullOr str;
 
 928 +    debug-port = mkOption {
 
 929 +      description = "Port to bind the debug server to.";
 
 930 +      type = with types; nullOr str;
 
 933 +    connection-string = mkOption {
 
 934 +      description = "SQLAlchemy connection string for the database.";
 
 936 +      default = "postgresql:///localhost?user=${srv}srht&host=/run/postgresql";
 
 938 +    migrate-on-upgrade = mkEnableOption "automatic migrations on package upgrade" // { default = true; };
 
 939 +    oauth-client-id = mkOption {
 
 940 +      description = "${srv}.sr.ht's OAuth client id for meta.sr.ht.";
 
 943 +    oauth-client-secret = mkOption {
 
 944 +      description = "${srv}.sr.ht's OAuth client secret for meta.sr.ht.";
 
 946 +      apply = s: "<" + toString s;
 
 950    # Specialized python containing all the modules
 
 951    python = pkgs.sourcehut.python.withPackages (ps: with ps; [
 
 954 +    # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=5 flower
 
 959 @@ -19,69 +95,37 @@ let
 
 963 +    # Not a python package
 
 968 +  mkOptionNullOrStr = description: mkOption {
 
 969 +    inherit description;
 
 970 +    type = with types; nullOr str;
 
 987 -      (mkRemovedOptionModule [ "services" "sourcehut" "nginx" "enable" ] ''
 
 988 -        The sourcehut module supports `nginx` as a local reverse-proxy by default and doesn't
 
 989 -        support other reverse-proxies officially.
 
 991 -        However it's possible to use an alternative reverse-proxy by
 
 994 -          * adjusting the relevant settings for server addresses and ports directly
 
 996 -        Further details about this can be found in the `Sourcehut`-section of the NixOS-manual.
 
1000    options.services.sourcehut = {
 
1001 -    enable = mkOption {
 
1002 -      type = types.bool;
 
1005 -        Enable sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
 
1006 -        task dispatching, wiki and account management services
 
1009 +    enable = mkEnableOption ''
 
1010 +      sourcehut - git hosting, continuous integration, mailing list, ticket tracking,
 
1011 +      task dispatching, wiki and account management services
 
1014      services = mkOption {
 
1015 -      type = types.nonEmptyListOf (types.enum [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ]);
 
1016 -      default = [ "man" "meta" "paste" ];
 
1017 -      example = [ "builds" "dispatch" "git" "hub" "hg" "lists" "man" "meta" "paste" "todo" ];
 
1018 +      type = with types; listOf (enum
 
1019 +        [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
 
1020 +      defaultText = "locally enabled services";
 
1022 -        Services to enable on the sourcehut network.
 
1023 +        Services that may be displayed as links in the title bar of the Web interface.
 
1027 -    originBase = mkOption {
 
1028 +    listenAddress = mkOption {
 
1030 -      default = with config.networking; hostName + lib.optionalString (domain != null) ".${domain}";
 
1032 -        Host name used by reverse-proxy and for default settings. Will host services at git."''${originBase}". For example: git.sr.ht
 
1036 -    address = mkOption {
 
1038 -      default = "127.0.0.1";
 
1040 -        Address to bind to.
 
1042 +      default = "localhost";
 
1043 +      description = "Address to bind to.";
 
1047 @@ -94,105 +138,1212 @@ in
 
1051 -    statePath = mkOption {
 
1052 -      type = types.path;
 
1053 -      default = "/var/lib/sourcehut";
 
1055 -        Root state path for the sourcehut network. If left as the default value
 
1056 -        this directory will automatically be created before the sourcehut server
 
1057 -        starts, otherwise the sysadmin is responsible for ensuring the
 
1058 -        directory exists with appropriate ownership and permissions.
 
1061 +      enable = mkEnableOption ''local minio integration'';
 
1065 +      enable = mkEnableOption ''local nginx integration'';
 
1066 +      virtualHost = mkOption {
 
1067 +        type = types.attrs;
 
1069 +        description = "Virtual-host configuration merged with all Sourcehut's virtual-hosts.";
 
1074 +      enable = mkEnableOption ''local postfix integration'';
 
1078 +      enable = mkEnableOption ''local postgresql integration'';
 
1082 +      enable = mkEnableOption ''local redis integration in a dedicated redis-server'';
 
1085      settings = mkOption {
 
1086        type = lib.types.submodule {
 
1087          freeformType = settingsFormat.type;
 
1088 +        options."sr.ht" = {
 
1089 +          global-domain = mkOption {
 
1090 +            description = "Global domain name.";
 
1092 +            example = "example.com";
 
1094 +          environment = mkOption {
 
1095 +            description = "Values other than \"production\" adds a banner to each page.";
 
1096 +            type = types.enum [ "development" "production" ];
 
1097 +            default = "development";
 
1099 +          network-key = mkOption {
 
1101 +              An absolute file path (which should be outside the Nix-store)
 
1102 +              to a secret key to encrypt internal messages with. Use <code>srht-keygen network</code> to
 
1103 +              generate this key. It must be consistent between all services and nodes.
 
1105 +            type = types.path;
 
1106 +            apply = s: "<" + toString s;
 
1108 +          owner-email = mkOption {
 
1109 +            description = "Owner's email.";
 
1111 +            default = "contact@example.com";
 
1113 +          owner-name = mkOption {
 
1114 +            description = "Owner's name.";
 
1116 +            default = "John Doe";
 
1118 +          site-blurb = mkOption {
 
1119 +            description = "Blurb for your site.";
 
1121 +            default = "the hacker's forge";
 
1123 +          site-info = mkOption {
 
1124 +            description = "The top-level info page for your site.";
 
1126 +            default = "https://sourcehut.org";
 
1128 +          service-key = mkOption {
 
1130 +              An absolute file path (which should be outside the Nix-store)
 
1131 +              to a key used for encrypting session cookies. Use <code>srht-keygen service</code> to
 
1132 +              generate the service key. This must be shared between each node of the same
 
1133 +              service (e.g. git1.sr.ht and git2.sr.ht), but different services may use
 
1134 +              different keys. If you configure all of your services with the same
 
1135 +              config.ini, you may use the same service-key for all of them.
 
1137 +            type = types.path;
 
1138 +            apply = s: "<" + toString s;
 
1140 +          site-name = mkOption {
 
1141 +            description = "The name of your network of sr.ht-based sites.";
 
1143 +            default = "sourcehut";
 
1145 +          source-url = mkOption {
 
1146 +            description = "The source code for your fork of sr.ht.";
 
1148 +            default = "https://git.sr.ht/~sircmpwn/srht";
 
1152 +          smtp-host = mkOptionNullOrStr "Outgoing SMTP host.";
 
1153 +          smtp-port = mkOption {
 
1154 +            description = "Outgoing SMTP port.";
 
1155 +            type = with types; nullOr port;
 
1158 +          smtp-user = mkOptionNullOrStr "Outgoing SMTP user.";
 
1159 +          smtp-password = mkOptionNullOrStr "Outgoing SMTP password.";
 
1160 +          smtp-from = mkOptionNullOrStr "Outgoing SMTP FROM.";
 
1161 +          error-to = mkOptionNullOrStr "Address receiving application exceptions";
 
1162 +          error-from = mkOptionNullOrStr "Address sending application exceptions";
 
1163 +          pgp-privkey = mkOptionNullOrStr ''
 
1164 +            An absolute file path (which should be outside the Nix-store)
 
1165 +            to an OpenPGP private key.
 
1167 +            Your PGP key information (DO NOT mix up pub and priv here)
 
1168 +            You must remove the password from your secret key, if present.
 
1169 +            You can do this with <code>gpg --edit-key [key-id]</code>,
 
1170 +            then use the <code>passwd</code> command and do not enter a new password.
 
1172 +          pgp-pubkey = mkOptionNullOrStr "OpenPGP public key.";
 
1173 +          pgp-key-id = mkOptionNullOrStr "OpenPGP key identifier.";
 
1175 +        options.objects = {
 
1176 +          s3-upstream = mkOption {
 
1177 +            description = "Configure the S3-compatible object storage service.";
 
1178 +            type = with types; nullOr str;
 
1181 +          s3-access-key = mkOption {
 
1182 +            description = "Access key to the S3-compatible object storage service";
 
1183 +            type = with types; nullOr str;
 
1186 +          s3-secret-key = mkOption {
 
1188 +              An absolute file path (which should be outside the Nix-store)
 
1189 +              to the secret key of the S3-compatible object storage service.
 
1191 +            type = with types; nullOr path;
 
1193 +            apply = mapNullable (s: "<" + toString s);
 
1196 +        options.webhooks = {
 
1197 +          private-key = mkOption {
 
1199 +              An absolute file path (which should be outside the Nix-store)
 
1200 +              to a base64-encoded Ed25519 key for signing webhook payloads.
 
1201 +              This should be consistent for all *.sr.ht sites,
 
1202 +              as this key will be used to verify signatures
 
1203 +              from other sites in your network.
 
1204 +              Use the <code>srht-keygen webhook</code> command to generate a key.
 
1206 +            type = types.path;
 
1207 +            apply = s: "<" + toString s;
 
1211 +        options."dispatch.sr.ht" = commonServiceSettings "dispatch" // {
 
1213 +        options."dispatch.sr.ht::github" = {
 
1214 +          oauth-client-id = mkOptionNullOrStr "OAuth client id.";
 
1215 +          oauth-client-secret = mkOptionNullOrStr "OAuth client secret.";
 
1217 +        options."dispatch.sr.ht::gitlab" = {
 
1218 +          enabled = mkEnableOption "GitLab integration";
 
1219 +          canonical-upstream = mkOption {
 
1221 +            description = "Canonical upstream.";
 
1222 +            default = "gitlab.com";
 
1224 +          repo-cache = mkOption {
 
1226 +            description = "Repository cache directory.";
 
1227 +            default = "./repo-cache";
 
1229 +          "gitlab.com" = mkOption {
 
1230 +            type = with types; nullOr str;
 
1231 +            description = "GitLab id and secret.";
 
1233 +            example = "GitLab:application id:secret";
 
1237 +        options."builds.sr.ht" = commonServiceSettings "builds" // {
 
1238 +          allow-free = mkEnableOption "nonpaying users to submit builds";
 
1239 +          redis = mkOption {
 
1240 +            description = "The Redis connection used for the Celery worker.";
 
1242 +            default = "redis+socket:///run/redis-sourcehut-buildsrht/redis.sock?virtual_host=2";
 
1244 +          shell = mkOption {
 
1246 +              Scripts used to launch on SSH connection.
 
1247 +              <literal>/usr/bin/master-shell</literal> on master,
 
1248 +              <literal>/usr/bin/runner-shell</literal> on runner.
 
1249 +              If master and worker are on the same system
 
1250 +              set to <literal>/usr/bin/runner-shell</literal>.
 
1252 +            type = types.enum ["/usr/bin/master-shell" "/usr/bin/runner-shell"];
 
1253 +            default = "/usr/bin/master-shell";
 
1256 +        options."builds.sr.ht::worker" = {
 
1257 +          bind-address = mkOption {
 
1259 +              HTTP bind address for serving local build information/monitoring.
 
1262 +            default = "localhost:8080";
 
1264 +          buildlogs = mkOption {
 
1265 +            description = "Path to write build logs.";
 
1267 +            default = "/var/log/sourcehut/buildsrht";
 
1271 +              Listening address and listening port
 
1272 +              of the build runner (with HTTP port if not 80).
 
1275 +            default = "localhost:5020";
 
1277 +          timeout = mkOption {
 
1279 +              Max build duration.
 
1280 +              See <link xlink:href="https://golang.org/pkg/time/#ParseDuration"/>.
 
1287 +        options."git.sr.ht" = commonServiceSettings "git" // {
 
1288 +          outgoing-domain = mkOption {
 
1289 +            description = "Outgoing domain.";
 
1291 +            default = "https://git.localhost.localdomain";
 
1293 +          post-update-script = mkOption {
 
1295 +              A post-update script which is installed in every git repo.
 
1296 +              This setting is propagated to newer and existing repositories.
 
1298 +            type = types.path;
 
1299 +            default = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
 
1300 +            defaultText = "\${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
 
1301 +            # Git hooks are run relative to their repository's directory,
 
1302 +            # but gitsrht-update-hook looks up ../config.ini
 
1303 +            apply = p: pkgs.writeShellScript "update-hook-wrapper" ''
 
1305 +              test -e "''${PWD%/*}"/config.ini ||
 
1306 +              ln -s /run/sourcehut/gitsrht/config.ini "''${PWD%/*}"/config.ini
 
1307 +              exec -a "$0" '${p}' "$@"
 
1310 +          repos = mkOption {
 
1312 +              Path to git repositories on disk.
 
1313 +              If changing the default, you must ensure that
 
1314 +              the gitsrht's user as read and write access to it.
 
1317 +            default = "/var/lib/sourcehut/gitsrht/repos";
 
1319 +          webhooks = mkOption {
 
1320 +            description = "The Redis connection used for the webhooks worker.";
 
1322 +            default = "redis+socket:///run/redis-sourcehut-gitsrht/redis.sock?virtual_host=1";
 
1325 +        options."git.sr.ht::api" = {
 
1326 +          internal-ipnet = mkOption {
 
1328 +              Set of IP subnets which are permitted to utilize internal API
 
1329 +              authentication. This should be limited to the subnets
 
1330 +              from which your *.sr.ht services are running.
 
1331 +              See <xref linkend="opt-services.sourcehut.listenAddress"/>.
 
1333 +            type = with types; listOf str;
 
1334 +            default = [ "127.0.0.0/8" "::1/128" ];
 
1338 +        options."hg.sr.ht" = commonServiceSettings "hg" // {
 
1339 +          changegroup-script = mkOption {
 
1341 +              A changegroup script which is installed in every mercurial repo.
 
1342 +              This setting is propagated to newer and existing repositories.
 
1345 +            default = "${cfg.python}/bin/hgsrht-hook-changegroup";
 
1346 +            defaultText = "\${cfg.python}/bin/hgsrht-hook-changegroup";
 
1347 +            # Mercurial's changegroup hooks are run relative to their repository's directory,
 
1348 +            # but hgsrht-hook-changegroup looks up ./config.ini
 
1349 +            apply = p: pkgs.writeShellScript "hook-changegroup-wrapper" ''
 
1351 +              test -e "''$PWD"/config.ini ||
 
1352 +              ln -s /run/sourcehut/hgsrht/config.ini "''$PWD"/config.ini
 
1353 +              exec -a "$0" '${p}' "$@"
 
1356 +          repos = mkOption {
 
1358 +              Path to mercurial repositories on disk.
 
1359 +              If changing the default, you must ensure that
 
1360 +              the hgsrht's user as read and write access to it.
 
1363 +            default = "/var/lib/sourcehut/hgsrht/repos";
 
1365 +          srhtext = mkOptionNullOrStr ''
 
1366 +            Path to the srht mercurial extension
 
1367 +            (defaults to where the hgsrht code is)
 
1369 +          clone_bundle_threshold = mkOption {
 
1370 +            description = ".hg/store size (in MB) past which the nightly job generates clone bundles.";
 
1371 +            type = types.ints.unsigned;
 
1374 +          hg_ssh = mkOption {
 
1375 +            description = "Path to hg-ssh (if not in $PATH).";
 
1377 +            default = "${pkgs.mercurial}/bin/hg-ssh";
 
1378 +            defaultText = "\${pkgs.mercurial}/bin/hg-ssh";
 
1380 +          webhooks = mkOption {
 
1381 +            description = "The Redis connection used for the webhooks worker.";
 
1383 +            default = "redis+socket:///run/redis-sourcehut-hgsrht/redis.sock?virtual_host=1";
 
1387 +        options."hub.sr.ht" = commonServiceSettings "hub" // {
 
1390 +        options."lists.sr.ht" = commonServiceSettings "lists" // {
 
1391 +          allow-new-lists = mkEnableOption "Allow creation of new lists.";
 
1392 +          notify-from = mkOption {
 
1393 +            description = "Outgoing email for notifications generated by users.";
 
1395 +            default = "lists-notify@localhost.localdomain";
 
1397 +          posting-domain = mkOption {
 
1398 +            description = "Posting domain.";
 
1400 +            default = "lists.localhost.localdomain";
 
1402 +          redis = mkOption {
 
1403 +            description = "The Redis connection used for the Celery worker.";
 
1405 +            default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=2";
 
1407 +          webhooks = mkOption {
 
1408 +            description = "The Redis connection used for the webhooks worker.";
 
1410 +            default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=1";
 
1413 +        options."lists.sr.ht::worker" = {
 
1414 +          reject-mimetypes = mkOption {
 
1416 +              Comma-delimited list of Content-Types to reject. Messages with Content-Types
 
1417 +              included in this list are rejected. Multipart messages are always supported,
 
1418 +              and each part is checked against this list.
 
1420 +              Uses fnmatch for wildcard expansion.
 
1422 +            type = with types; listOf str;
 
1423 +            default = ["text/html"];
 
1425 +          reject-url = mkOption {
 
1426 +            description = "Reject URL.";
 
1428 +            default = "https://man.sr.ht/lists.sr.ht/etiquette.md";
 
1432 +              Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
 
1433 +              Alternatively, specify IP:PORT and an SMTP server will be run instead.
 
1436 +            default = "/tmp/lists.sr.ht-lmtp.sock";
 
1438 +          sock-group = mkOption {
 
1440 +              The lmtp daemon will make the unix socket group-read/write
 
1441 +              for users in this group.
 
1444 +            default = "postfix";
 
1448 +        options."man.sr.ht" = commonServiceSettings "man" // {
 
1451 +        options."meta.sr.ht" =
 
1452 +          removeAttrs (commonServiceSettings "meta")
 
1453 +            ["oauth-client-id" "oauth-client-secret"] // {
 
1454 +          api-origin = mkOption {
 
1455 +            description = "Origin URL for API, 100 more than web.";
 
1457 +            default = "http://${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
 
1458 +            defaultText = ''http://<xref linkend="opt-services.sourcehut.listenAddress"/>:''${toString (<xref linkend="opt-services.sourcehut.meta.port"/> + 100)}'';
 
1460 +          webhooks = mkOption {
 
1461 +            description = "The Redis connection used for the webhooks worker.";
 
1463 +            default = "redis+socket:///run/redis-sourcehut-metasrht/redis.sock?virtual_host=1";
 
1465 +          welcome-emails = mkEnableOption "sending stock sourcehut welcome emails after signup";
 
1467 +        options."meta.sr.ht::api" = {
 
1468 +          internal-ipnet = mkOption {
 
1470 +              Set of IP subnets which are permitted to utilize internal API
 
1471 +              authentication. This should be limited to the subnets
 
1472 +              from which your *.sr.ht services are running.
 
1473 +              See <xref linkend="opt-services.sourcehut.listenAddress"/>.
 
1475 +            type = with types; listOf str;
 
1476 +            default = [ "127.0.0.0/8" "::1/128" ];
 
1479 +        options."meta.sr.ht::aliases" = mkOption {
 
1480 +          description = "Aliases for the client IDs of commonly used OAuth clients.";
 
1481 +          type = with types; attrsOf int;
 
1483 +          example = { "git.sr.ht" = 12345; };
 
1485 +        options."meta.sr.ht::billing" = {
 
1486 +          enabled = mkEnableOption "the billing system";
 
1487 +          stripe-public-key = mkOptionNullOrStr "Public key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys";
 
1488 +          stripe-secret-key = mkOptionNullOrStr ''
 
1489 +            An absolute file path (which should be outside the Nix-store)
 
1490 +            to a secret key for Stripe. Get your keys at https://dashboard.stripe.com/account/apikeys
 
1492 +            apply = mapNullable (s: "<" + toString s);
 
1495 +        options."meta.sr.ht::settings" = {
 
1496 +          registration = mkEnableOption "public registration";
 
1497 +          onboarding-redirect = mkOption {
 
1498 +            description = "Where to redirect new users upon registration.";
 
1500 +            default = "https://meta.localhost.localdomain";
 
1502 +          user-invites = mkOption {
 
1504 +              How many invites each user is issued upon registration
 
1505 +              (only applicable if open registration is disabled).
 
1507 +            type = types.ints.unsigned;
 
1512 +        options."pages.sr.ht" = commonServiceSettings "pages" // {
 
1513 +          gemini-certs = mkOption {
 
1515 +              An absolute file path (which should be outside the Nix-store)
 
1516 +              to Gemini certificates.
 
1518 +            type = with types; nullOr path;
 
1521 +          max-site-size = mkOption {
 
1522 +            description = "Maximum size of any given site (post-gunzip), in MiB.";
 
1526 +          user-domain = mkOption {
 
1528 +              Configures the user domain, if enabled.
 
1529 +              All users are given <username>.this.domain.
 
1531 +            type = with types; nullOr str;
 
1535 +        options."pages.sr.ht::api" = {
 
1536 +          internal-ipnet = mkOption {
 
1538 +              Set of IP subnets which are permitted to utilize internal API
 
1539 +              authentication. This should be limited to the subnets
 
1540 +              from which your *.sr.ht services are running.
 
1541 +              See <xref linkend="opt-services.sourcehut.listenAddress"/>.
 
1543 +            type = with types; listOf str;
 
1544 +            default = [ "127.0.0.0/8" "::1/128" ];
 
1548 +        options."paste.sr.ht" = commonServiceSettings "paste" // {
 
1551 +        options."todo.sr.ht" = commonServiceSettings "todo" // {
 
1552 +          notify-from = mkOption {
 
1553 +            description = "Outgoing email for notifications generated by users.";
 
1555 +            default = "todo-notify@localhost.localdomain";
 
1557 +          webhooks = mkOption {
 
1558 +            description = "The Redis connection used for the webhooks worker.";
 
1560 +            default = "redis+socket:///run/redis-sourcehut-todosrht/redis.sock?virtual_host=1";
 
1563 +        options."todo.sr.ht::mail" = {
 
1564 +          posting-domain = mkOption {
 
1565 +            description = "Posting domain.";
 
1567 +            default = "todo.localhost.localdomain";
 
1571 +              Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
 
1572 +              Alternatively, specify IP:PORT and an SMTP server will be run instead.
 
1575 +            default = "/tmp/todo.sr.ht-lmtp.sock";
 
1577 +          sock-group = mkOption {
 
1579 +              The lmtp daemon will make the unix socket group-read/write
 
1580 +              for users in this group.
 
1583 +            default = "postfix";
 
1589          The configuration for the sourcehut network.
 
1594 -  config = mkIf cfg.enable {
 
1598 -          assertion = with cfgIni.webhooks; private-key != null && stringLength private-key == 44;
 
1599 -          message = "The webhook's private key must be defined and of a 44 byte length.";
 
1602 +      enableWorker = mkEnableOption "worker for builds.sr.ht";
 
1605 -          assertion = hasAttrByPath [ "meta.sr.ht" "origin" ] cfgIni && cfgIni."meta.sr.ht".origin != null;
 
1606 -          message = "meta.sr.ht's origin must be defined.";
 
1609 +      images = mkOption {
 
1610 +        type = with types; attrsOf (attrsOf (attrsOf package));
 
1612 +        example = lib.literalExpression ''(let
 
1613 +            # Pinning unstable to allow usage with flakes and limit rebuilds.
 
1614 +            pkgs_unstable = builtins.fetchGit {
 
1615 +                url = "https://github.com/NixOS/nixpkgs";
 
1616 +                rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
 
1617 +                ref = "nixos-unstable";
 
1619 +            image_from_nixpkgs = (import ("${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
 
1620 +              pkgs = (import pkgs_unstable {});
 
1624 +            nixos.unstable.x86_64 = image_from_nixpkgs;
 
1628 +          Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
 
1633 -    virtualisation.docker.enable = true;
 
1634 -    environment.etc."sr.ht/config.ini".source =
 
1635 -      settingsFormat.generate "sourcehut-config.ini" (mapAttrsRecursive
 
1637 -          path: v: if v == null then "" else v
 
1641 +      package = mkOption {
 
1642 +        type = types.package;
 
1643 +        default = pkgs.git;
 
1644 +        example = literalExpression "pkgs.gitFull";
 
1646 +          Git package for git.sr.ht. This can help silence collisions.
 
1649 +      fcgiwrap.preforkProcess = mkOption {
 
1650 +        description = "Number of fcgiwrap processes to prefork.";
 
1656 -    environment.systemPackages = [ pkgs.sourcehut.coresrht ];
 
1658 +      package = mkOption {
 
1659 +        type = types.package;
 
1660 +        default = pkgs.mercurial;
 
1662 +          Mercurial package for hg.sr.ht. This can help silence collisions.
 
1665 +      cloneBundles = mkOption {
 
1666 +        type = types.bool;
 
1669 +          Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
 
1674 -    # PostgreSQL server
 
1675 -    services.postgresql.enable = mkOverride 999 true;
 
1677 -    services.postfix.enable = mkOverride 999 true;
 
1679 -    services.cron.enable = mkOverride 999 true;
 
1681 -    services.redis.enable = mkOverride 999 true;
 
1682 -    services.redis.bind = mkOverride 999 "127.0.0.1";
 
1684 -    services.sourcehut.settings = {
 
1685 -      # The name of your network of sr.ht-based sites
 
1686 -      "sr.ht".site-name = mkDefault "sourcehut";
 
1687 -      # The top-level info page for your site
 
1688 -      "sr.ht".site-info = mkDefault "https://sourcehut.org";
 
1689 -      # {{ site-name }}, {{ site-blurb }}
 
1690 -      "sr.ht".site-blurb = mkDefault "the hacker's forge";
 
1691 -      # If this != production, we add a banner to each page
 
1692 -      "sr.ht".environment = mkDefault "development";
 
1693 -      # Contact information for the site owners
 
1694 -      "sr.ht".owner-name = mkDefault "Drew DeVault";
 
1695 -      "sr.ht".owner-email = mkDefault "sir@cmpwn.com";
 
1696 -      # The source code for your fork of sr.ht
 
1697 -      "sr.ht".source-url = mkDefault "https://git.sr.ht/~sircmpwn/srht";
 
1698 -      # A secret key to encrypt session cookies with
 
1699 -      "sr.ht".secret-key = mkDefault null;
 
1700 -      "sr.ht".global-domain = mkDefault null;
 
1702 -      # Outgoing SMTP settings
 
1703 -      mail.smtp-host = mkDefault null;
 
1704 -      mail.smtp-port = mkDefault null;
 
1705 -      mail.smtp-user = mkDefault null;
 
1706 -      mail.smtp-password = mkDefault null;
 
1707 -      mail.smtp-from = mkDefault null;
 
1708 -      # Application exceptions are emailed to this address
 
1709 -      mail.error-to = mkDefault null;
 
1710 -      mail.error-from = mkDefault null;
 
1711 -      # Your PGP key information (DO NOT mix up pub and priv here)
 
1712 -      # You must remove the password from your secret key, if present.
 
1713 -      # You can do this with gpg --edit-key [key-id], then use the passwd
 
1714 -      # command and do not enter a new password.
 
1715 -      mail.pgp-privkey = mkDefault null;
 
1716 -      mail.pgp-pubkey = mkDefault null;
 
1717 -      mail.pgp-key-id = mkDefault null;
 
1719 -      # base64-encoded Ed25519 key for signing webhook payloads. This should be
 
1720 -      # consistent for all *.sr.ht sites, as we'll use this key to verify signatures
 
1721 -      # from other sites in your network.
 
1723 -      # Use the srht-webhook-keygen command to generate a key.
 
1724 -      webhooks.private-key = mkDefault null;
 
1727 +        extraArgs = mkOption {
 
1728 +          type = with types; listOf str;
 
1729 +          default = [ "--loglevel DEBUG" "--pool eventlet" "--without-heartbeat" ];
 
1730 +          description = "Extra arguments passed to the Celery responsible for processing mails.";
 
1732 +        celeryConfig = mkOption {
 
1733 +          type = types.lines;
 
1735 +          description = "Content of the <literal>celeryconfig.py</literal> used by the Celery of <literal>listssrht-process</literal>.";
 
1741 +  config = mkIf cfg.enable (mkMerge [
 
1743 +      environment.systemPackages = [ pkgs.sourcehut.coresrht ];
 
1745 +      services.sourcehut.settings = {
 
1746 +        "git.sr.ht".outgoing-domain = mkDefault "https://git.${domain}";
 
1747 +        "lists.sr.ht".notify-from = mkDefault "lists-notify@${domain}";
 
1748 +        "lists.sr.ht".posting-domain = mkDefault "lists.${domain}";
 
1749 +        "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${domain}";
 
1750 +        "todo.sr.ht".notify-from = mkDefault "todo-notify@${domain}";
 
1751 +        "todo.sr.ht::mail".posting-domain = mkDefault "todo.${domain}";
 
1754 +    (mkIf cfg.postgresql.enable {
 
1756 +        { assertion = postgresql.enable;
 
1757 +          message = "postgresql must be enabled and configured";
 
1761 +    (mkIf cfg.postfix.enable {
 
1763 +        { assertion = postfix.enable;
 
1764 +          message = "postfix must be enabled and configured";
 
1767 +      # Needed for sharing the LMTP sockets with JoinsNamespaceOf=
 
1768 +      systemd.services.postfix.serviceConfig.PrivateTmp = true;
 
1770 +    (mkIf cfg.redis.enable {
 
1771 +      services.redis.vmOverCommit = mkDefault true;
 
1773 +    (mkIf cfg.nginx.enable {
 
1775 +        { assertion = nginx.enable;
 
1776 +          message = "nginx must be enabled and configured";
 
1779 +      # For proxyPass= in virtual-hosts for Sourcehut services.
 
1780 +      services.nginx.recommendedProxySettings = mkDefault true;
 
1782 +    (mkIf (cfg.builds.enable || cfg.git.enable || cfg.hg.enable) {
 
1783 +      services.openssh = {
 
1784 +        # Note that sshd will continue to honor AuthorizedKeysFile.
 
1785 +        # Note that you may want automatically rotate
 
1786 +        # or link to /dev/null the following log files:
 
1787 +        # - /var/log/gitsrht-dispatch
 
1788 +        # - /var/log/{build,git,hg}srht-keys
 
1789 +        # - /var/log/{git,hg}srht-shell
 
1790 +        # - /var/log/gitsrht-update-hook
 
1791 +        authorizedKeysCommand = ''/etc/ssh/sourcehut/subdir/srht-dispatch "%u" "%h" "%t" "%k"'';
 
1792 +        # srht-dispatch will setuid/setgid according to [git.sr.ht::dispatch]
 
1793 +        authorizedKeysCommandUser = "root";
 
1795 +          PermitUserEnvironment SRHT_*
 
1798 +      environment.etc."ssh/sourcehut/config.ini".source =
 
1799 +        settingsFormat.generate "sourcehut-dispatch-config.ini"
 
1800 +          (filterAttrs (k: v: k == "git.sr.ht::dispatch")
 
1802 +      environment.etc."ssh/sourcehut/subdir/srht-dispatch" = {
 
1803 +        # sshd_config(5): The program must be owned by root, not writable by group or others
 
1805 +        source = pkgs.writeShellScript "srht-dispatch" ''
 
1807 +          cd /etc/ssh/sourcehut/subdir
 
1808 +          ${cfg.python}/bin/gitsrht-dispatch "$@"
 
1811 +      systemd.services.sshd = {
 
1812 +        #path = optional cfg.git.enable [ cfg.git.package ];
 
1814 +          BindReadOnlyPaths =
 
1815 +            # Note that those /usr/bin/* paths are hardcoded in multiple places in *.sr.ht,
 
1816 +            # for instance to get the user from the [git.sr.ht::dispatch] settings.
 
1817 +            # *srht-keys needs to:
 
1818 +            # - access a redis-server in [sr.ht] redis-host,
 
1819 +            # - access the PostgreSQL server in [*.sr.ht] connection-string,
 
1820 +            # - query metasrht-api (through the HTTP API).
 
1821 +            # Using this has the side effect of creating empty files in /usr/bin/
 
1822 +            optionals cfg.builds.enable [
 
1823 +              "${pkgs.writeShellScript "buildsrht-keys-wrapper" ''
 
1825 +                cd /run/sourcehut/buildsrht/subdir
 
1826 +                exec -a "$0" ${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys "$@"
 
1827 +              ''}:/usr/bin/buildsrht-keys"
 
1828 +              "${pkgs.sourcehut.buildsrht}/bin/master-shell:/usr/bin/master-shell"
 
1829 +              "${pkgs.sourcehut.buildsrht}/bin/runner-shell:/usr/bin/runner-shell"
 
1831 +            optionals cfg.git.enable [
 
1832 +              # /path/to/gitsrht-keys calls /path/to/gitsrht-shell,
 
1833 +              # or [git.sr.ht] shell= if set.
 
1834 +              "${pkgs.writeShellScript "gitsrht-keys-wrapper" ''
 
1836 +                cd /run/sourcehut/gitsrht/subdir
 
1837 +                exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys "$@"
 
1838 +              ''}:/usr/bin/gitsrht-keys"
 
1839 +              "${pkgs.writeShellScript "gitsrht-shell-wrapper" ''
 
1841 +                cd /run/sourcehut/gitsrht/subdir
 
1842 +                exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-shell "$@"
 
1843 +              ''}:/usr/bin/gitsrht-shell"
 
1845 +            optionals cfg.hg.enable [
 
1846 +              # /path/to/hgsrht-keys calls /path/to/hgsrht-shell,
 
1847 +              # or [hg.sr.ht] shell= if set.
 
1848 +              "${pkgs.writeShellScript "hgsrht-keys-wrapper" ''
 
1850 +                cd /run/sourcehut/hgsrht/subdir
 
1851 +                exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-keys "$@"
 
1852 +              ''}:/usr/bin/hgsrht-keys"
 
1853 +              ":/usr/bin/hgsrht-shell"
 
1854 +              "${pkgs.writeShellScript "hgsrht-shell-wrapper" ''
 
1856 +                cd /run/sourcehut/hgsrht/subdir
 
1857 +                exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-shell "$@"
 
1858 +              ''}:/usr/bin/hgsrht-shell"
 
1867 +    (import ./service.nix "builds" {
 
1868 +      inherit configIniOfService;
 
1869 +      srvsrht = "buildsrht";
 
1871 +      # TODO: a celery worker on the master and worker are apparently needed
 
1872 +      extraServices.buildsrht-worker = let
 
1873 +        qemuPackage = pkgs.qemu_kvm;
 
1874 +        serviceName = "buildsrht-worker";
 
1875 +        statePath = "/var/lib/sourcehut/${serviceName}";
 
1876 +        in mkIf cfg.builds.enableWorker {
 
1877 +        path = [ pkgs.openssh pkgs.docker ];
 
1880 +          if test -z "$(docker images -q qemu:latest 2>/dev/null)" \
 
1881 +          || test "$(cat ${statePath}/docker-image-qemu)" != "${qemuPackage.version}"
 
1883 +            # Create and import qemu:latest image for docker
 
1884 +            ${pkgs.dockerTools.streamLayeredImage {
 
1887 +              contents = [ qemuPackage ];
 
1889 +            # Mark down current package version
 
1890 +            echo '${qemuPackage.version}' >${statePath}/docker-image-qemu
 
1894 +          ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
 
1895 +          RuntimeDirectory = [ "sourcehut/${serviceName}/subdir" ];
 
1896 +          # builds.sr.ht-worker looks up ../config.ini
 
1897 +          LogsDirectory = [ "sourcehut/${serviceName}" ];
 
1898 +          StateDirectory = [ "sourcehut/${serviceName}" ];
 
1899 +          WorkingDirectory = "-"+"/run/sourcehut/${serviceName}/subdir";
 
1903 +        image_dirs = flatten (
 
1904 +          mapAttrsToList (distro: revs:
 
1905 +            mapAttrsToList (rev: archs:
 
1906 +              mapAttrsToList (arch: image:
 
1907 +                pkgs.runCommand "buildsrht-images" { } ''
 
1908 +                  mkdir -p $out/${distro}/${rev}/${arch}
 
1909 +                  ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
 
1913 +          ) cfg.builds.images
 
1915 +        image_dir_pre = pkgs.symlinkJoin {
 
1916 +          name = "builds.sr.ht-worker-images-pre";
 
1917 +          paths = image_dirs;
 
1918 +            # FIXME: not working, apparently because ubuntu/latest is a broken link
 
1919 +            # ++ [ "${pkgs.sourcehut.buildsrht}/lib/images" ];
 
1921 +        image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
 
1922 +          mkdir -p $out/images
 
1923 +          cp -Lr ${image_dir_pre}/* $out/images
 
1927 +          users.users.${cfg.builds.user}.shell = pkgs.bash;
 
1929 +          virtualisation.docker.enable = true;
 
1931 +          services.sourcehut.settings = mkMerge [
 
1932 +            { # Note that git.sr.ht::dispatch is not a typo,
 
1933 +              # gitsrht-dispatch always use this section
 
1934 +              "git.sr.ht::dispatch"."/usr/bin/buildsrht-keys" =
 
1935 +                mkDefault "${cfg.builds.user}:${cfg.builds.group}";
 
1937 +            (mkIf cfg.builds.enableWorker {
 
1938 +              "builds.sr.ht::worker".shell = "/usr/bin/runner-shell";
 
1939 +              "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
 
1940 +              "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
 
1944 +        (mkIf cfg.builds.enableWorker {
 
1946 +            docker.members = [ cfg.builds.user ];
 
1949 +        (mkIf (cfg.builds.enableWorker && cfg.nginx.enable) {
 
1950 +          # Allow nginx access to buildlogs
 
1951 +          users.users.${nginx.user}.extraGroups = [ cfg.builds.group ];
 
1952 +          systemd.services.nginx = {
 
1953 +            serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."builds.sr.ht::worker".buildlogs}:/var/log/nginx/buildsrht/logs" ];
 
1955 +          services.nginx.virtualHosts."logs.${domain}" = mkMerge [ {
 
1956 +            /* FIXME: is a listen needed?
 
1957 +            listen = with builtins;
 
1958 +              # FIXME: not compatible with IPv6
 
1959 +              let address = split ":" cfg.settings."builds.sr.ht::worker".name; in
 
1960 +              [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
 
1962 +            locations."/logs/".alias = "/var/log/nginx/buildsrht/logs/";
 
1963 +          } cfg.nginx.virtualHost ];
 
1968 +    (import ./service.nix "dispatch" {
 
1969 +      inherit configIniOfService;
 
1973 +    (import ./service.nix "git" (let
 
1975 +        path = [ cfg.git.package ];
 
1976 +        serviceConfig.BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
 
1977 +        serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".post-update-script}:/var/lib/sourcehut/gitsrht/bin/post-update-script" ];
 
1980 +      inherit configIniOfService;
 
1981 +      mainService = mkMerge [ baseService {
 
1982 +        serviceConfig.StateDirectory = [ "sourcehut/gitsrht" "sourcehut/gitsrht/repos" ];
 
1986 +      extraTimers.gitsrht-periodic = {
 
1987 +        service = baseService;
 
1988 +        timerConfig.OnCalendar = ["20min"];
 
1990 +      extraConfig = mkMerge [
 
1992 +          # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
 
1993 +          # Probably could use gitsrht-shell if output is restricted to just parameters...
 
1994 +          users.users.${cfg.git.user}.shell = pkgs.bash;
 
1995 +          services.sourcehut.settings = {
 
1996 +            "git.sr.ht::dispatch"."/usr/bin/gitsrht-keys" =
 
1997 +              mkDefault "${cfg.git.user}:${cfg.git.group}";
 
1999 +          systemd.services.sshd = baseService;
 
2001 +        (mkIf cfg.nginx.enable {
 
2002 +          services.nginx.virtualHosts."git.${domain}" = {
 
2003 +            locations."/authorize" = {
 
2004 +              proxyPass = "http://${cfg.listenAddress}:${toString cfg.git.port}";
 
2006 +                proxy_pass_request_body off;
 
2007 +                proxy_set_header Content-Length "";
 
2008 +                proxy_set_header X-Original-URI $request_uri;
 
2011 +            locations."~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$" = {
 
2012 +              root = "/var/lib/sourcehut/gitsrht/repos";
 
2014 +                GIT_HTTP_EXPORT_ALL = "";
 
2015 +                GIT_PROJECT_ROOT = "$document_root";
 
2016 +                PATH_INFO = "$uri";
 
2017 +                SCRIPT_FILENAME = "${cfg.git.package}/bin/git-http-backend";
 
2020 +                auth_request /authorize;
 
2021 +                fastcgi_read_timeout 500s;
 
2022 +                fastcgi_pass unix:/run/gitsrht-fcgiwrap.sock;
 
2027 +          systemd.sockets.gitsrht-fcgiwrap = {
 
2028 +            before = [ "nginx.service" ];
 
2029 +            wantedBy = [ "sockets.target" "gitsrht.service" ];
 
2030 +            # This path remains accessible to nginx.service, which has no RootDirectory=
 
2031 +            socketConfig.ListenStream = "/run/gitsrht-fcgiwrap.sock";
 
2032 +            socketConfig.SocketUser = nginx.user;
 
2033 +            socketConfig.SocketMode = "600";
 
2037 +      extraServices.gitsrht-fcgiwrap = mkIf cfg.nginx.enable {
 
2039 +          # Socket is passed by gitsrht-fcgiwrap.socket
 
2040 +          ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${toString cfg.git.fcgiwrap.preforkProcess}";
 
2041 +          # No need for config.ini
 
2042 +          ExecStartPre = mkForce [];
 
2044 +          DynamicUser = true;
 
2045 +          BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
 
2046 +          IPAddressDeny = "any";
 
2047 +          InaccessiblePaths = [ "-+/run/postgresql" "-+/run/redis-sourcehut" ];
 
2048 +          PrivateNetwork = true;
 
2049 +          RestrictAddressFamilies = mkForce [ "none" ];
 
2050 +          SystemCallFilter = mkForce [
 
2052 +            "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@setuid"
 
2053 +            # @timer is needed for alarm()
 
2059 +    (import ./service.nix "hg" (let
 
2061 +        path = [ cfg.hg.package ];
 
2062 +        serviceConfig.BindPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/sourcehut/hgsrht/repos" ];
 
2063 +        serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."ht.sr.ht".changegroup-script}:/var/lib/sourcehut/hgsrht/bin/changegroup-script" ];
 
2066 +      inherit configIniOfService;
 
2067 +      mainService = mkMerge [ baseService {
 
2068 +        serviceConfig.StateDirectory = [ "sourcehut/hgsrht" "sourcehut/hgsrht/repos" ];
 
2072 +      extraTimers.hgsrht-periodic = {
 
2073 +        service = baseService;
 
2074 +        timerConfig.OnCalendar = ["20min"];
 
2076 +      extraTimers.hgsrht-clonebundles = mkIf cfg.hg.cloneBundles {
 
2077 +        service = baseService;
 
2078 +        timerConfig.OnCalendar = ["daily"];
 
2079 +        timerConfig.AccuracySec = "1h";
 
2081 +      extraConfig = mkMerge [
 
2083 +          users.users.${cfg.hg.user}.shell = pkgs.bash;
 
2084 +          services.sourcehut.settings = {
 
2085 +            # Note that git.sr.ht::dispatch is not a typo,
 
2086 +            # gitsrht-dispatch always uses this section.
 
2087 +            "git.sr.ht::dispatch"."/usr/bin/hgsrht-keys" =
 
2088 +              mkDefault "${cfg.hg.user}:${cfg.hg.group}";
 
2090 +          systemd.services.sshd = baseService;
 
2092 +        (mkIf cfg.nginx.enable {
 
2093 +          # Allow nginx access to repositories
 
2094 +          users.users.${nginx.user}.extraGroups = [ cfg.hg.group ];
 
2095 +          services.nginx.virtualHosts."hg.${domain}" = {
 
2096 +            locations."/authorize" = {
 
2097 +              proxyPass = "http://${cfg.listenAddress}:${toString cfg.hg.port}";
 
2099 +                proxy_pass_request_body off;
 
2100 +                proxy_set_header Content-Length "";
 
2101 +                proxy_set_header X-Original-URI $request_uri;
 
2104 +            # Let clients reach pull bundles. We don't really need to lock this down even for
 
2105 +            # private repos because the bundles are named after the revision hashes...
 
2106 +            # so someone would need to know or guess a SHA value to download anything.
 
2107 +            # TODO: proxyPass to an hg serve service?
 
2108 +            locations."~ ^/[~^][a-z0-9_]+/[a-zA-Z0-9_.-]+/\\.hg/bundles/.*$" = {
 
2109 +              root = "/var/lib/nginx/hgsrht/repos";
 
2111 +                auth_request /authorize;
 
2116 +          systemd.services.nginx = {
 
2117 +            serviceConfig.BindReadOnlyPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/nginx/hgsrht/repos" ];
 
2123 +    (import ./service.nix "hub" {
 
2124 +      inherit configIniOfService;
 
2127 +        services.nginx = mkIf cfg.nginx.enable {
 
2128 +          virtualHosts."hub.${domain}" = mkMerge [ {
 
2129 +            serverAliases = [ domain ];
 
2130 +          } cfg.nginx.virtualHost ];
 
2135 +    (import ./service.nix "lists" (let
 
2136 +      srvsrht = "listssrht";
 
2138 +      inherit configIniOfService;
 
2141 +      # Receive the mail from Postfix and enqueue them into Redis and PostgreSQL
 
2142 +      extraServices.listssrht-lmtp = {
 
2143 +        wants = [ "postfix.service" ];
 
2144 +        unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
 
2145 +        serviceConfig.ExecStart = "${cfg.python}/bin/listssrht-lmtp";
 
2146 +        # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
 
2147 +        serviceConfig.PrivateUsers = mkForce false;
 
2149 +      # Dequeue the mails from Redis and dispatch them
 
2150 +      extraServices.listssrht-process = {
 
2153 +            cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" cfg.lists.process.celeryConfig} \
 
2154 +               /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
 
2156 +          ExecStart = "${cfg.python}/bin/celery --app listssrht.process worker --hostname listssrht-process@%%h " + concatStringsSep " " cfg.lists.process.extraArgs;
 
2157 +          # Avoid crashing: os.getloadavg()
 
2158 +          ProcSubset = mkForce "all";
 
2161 +      extraConfig = mkIf cfg.postfix.enable {
 
2162 +        users.groups.${postfix.group}.members = [ cfg.lists.user ];
 
2163 +        services.sourcehut.settings."lists.sr.ht::mail".sock-group = postfix.group;
 
2164 +        services.postfix = {
 
2165 +          destination = [ "lists.${domain}" ];
 
2166 +          # FIXME: an accurate recipient list should be queried
 
2167 +          # from the lists.sr.ht PostgreSQL database to avoid backscattering.
 
2168 +          # But usernames are unfortunately not in that database but in meta.sr.ht.
 
2169 +          # Note that two syntaxes are allowed:
 
2170 +          # - ~username/list-name@lists.${domain}
 
2171 +          # - u.username.list-name@lists.${domain}
 
2172 +          localRecipients = [ "@lists.${domain}" ];
 
2174 +            lists.${domain} lmtp:unix:${cfg.settings."lists.sr.ht::worker".sock}
 
2180 +    (import ./service.nix "man" {
 
2181 +      inherit configIniOfService;
 
2185 +    (import ./service.nix "meta" {
 
2186 +      inherit configIniOfService;
 
2189 +      extraServices.metasrht-api = {
 
2190 +        serviceConfig.Restart = "always";
 
2191 +        serviceConfig.RestartSec = "2s";
 
2192 +        preStart = "set -x\n" + concatStringsSep "\n\n" (attrValues (mapAttrs (k: s:
 
2193 +          let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht$" k;
 
2194 +              srv = head srvMatch;
 
2196 +          # Configure client(s) as "preauthorized"
 
2197 +          optionalString (srvMatch != null && cfg.${srv}.enable && ((s.oauth-client-id or null) != null)) ''
 
2198 +            # Configure ${srv}'s OAuth client as "preauthorized"
 
2199 +            ${postgresql.package}/bin/psql '${cfg.settings."meta.sr.ht".connection-string}' \
 
2200 +              -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${s.oauth-client-id}'"
 
2203 +        serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
 
2205 +      extraTimers.metasrht-daily.timerConfig = {
 
2206 +        OnCalendar = ["daily"];
 
2207 +        AccuracySec = "1h";
 
2209 +      extraConfig = mkMerge [
 
2212 +            { assertion = let s = cfg.settings."meta.sr.ht::billing"; in
 
2213 +                          s.enabled == "yes" -> (s.stripe-public-key != null && s.stripe-secret-key != null);
 
2214 +              message = "If meta.sr.ht::billing is enabled, the keys must be defined.";
 
2217 +          environment.systemPackages = optional cfg.meta.enable
 
2218 +            (pkgs.writeShellScriptBin "metasrht-manageuser" ''
 
2220 +              if test "$(${pkgs.coreutils}/bin/id -n -u)" != '${cfg.meta.user}'
 
2221 +              then exec sudo -u '${cfg.meta.user}' "$0" "$@"
 
2223 +                # In order to load config.ini
 
2224 +                if cd /run/sourcehut/metasrht
 
2225 +                then exec ${cfg.python}/bin/metasrht-manageuser "$@"
 
2227 +                  Please run: sudo systemctl start metasrht
 
2234 +        (mkIf cfg.nginx.enable {
 
2235 +          services.nginx.virtualHosts."meta.${domain}" = {
 
2236 +            locations."/query" = {
 
2237 +              proxyPass = cfg.settings."meta.sr.ht".api-origin;
 
2239 +                if ($request_method = 'OPTIONS') {
 
2240 +                  add_header 'Access-Control-Allow-Origin' '*';
 
2241 +                  add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
 
2242 +                  add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
 
2243 +                  add_header 'Access-Control-Max-Age' 1728000;
 
2244 +                  add_header 'Content-Type' 'text/plain; charset=utf-8';
 
2245 +                  add_header 'Content-Length' 0;
 
2249 +                add_header 'Access-Control-Allow-Origin' '*';
 
2250 +                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
 
2251 +                add_header 'Access-Control-Allow-Headers' 'User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
 
2252 +                add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
 
2260 +    (import ./service.nix "pages" {
 
2261 +      inherit configIniOfService;
 
2264 +        srvsrht = "pagessrht";
 
2265 +        version = pkgs.sourcehut.${srvsrht}.version;
 
2266 +        stateDir = "/var/lib/sourcehut/${srvsrht}";
 
2267 +        iniKey = "pages.sr.ht";
 
2269 +        preStart = mkBefore ''
 
2271 +          # Use the /run/sourcehut/${srvsrht}/config.ini
 
2272 +          # installed by a previous ExecStartPre= in baseService
 
2273 +          cd /run/sourcehut/${srvsrht}
 
2275 +          if test ! -e ${stateDir}/db; then
 
2276 +            ${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f ${pkgs.sourcehut.pagessrht}/share/sql/schema.sql
 
2277 +            echo ${version} >${stateDir}/db
 
2280 +          ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
 
2281 +            # Just try all the migrations because they're not linked to the version
 
2282 +            for sql in ${pkgs.sourcehut.pagessrht}/share/sql/migrations/*.sql; do
 
2283 +              ${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f "$sql" || true
 
2288 +          touch ${stateDir}/webhook
 
2291 +          ExecStart = mkForce "${pkgs.sourcehut.pagessrht}/bin/pages.sr.ht -b ${cfg.listenAddress}:${toString cfg.pages.port}";
 
2296 +    (import ./service.nix "paste" {
 
2297 +      inherit configIniOfService;
 
2301 +    (import ./service.nix "todo" {
 
2302 +      inherit configIniOfService;
 
2305 +      extraServices.todosrht-lmtp = {
 
2306 +        wants = [ "postfix.service" ];
 
2307 +        unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
 
2308 +        serviceConfig.ExecStart = "${cfg.python}/bin/todosrht-lmtp";
 
2309 +        # Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
 
2310 +        serviceConfig.PrivateUsers = mkForce false;
 
2312 +      extraConfig = mkIf cfg.postfix.enable {
 
2313 +        users.groups.${postfix.group}.members = [ cfg.todo.user ];
 
2314 +        services.sourcehut.settings."todo.sr.ht::mail".sock-group = postfix.group;
 
2315 +        services.postfix = {
 
2316 +          destination = [ "todo.${domain}" ];
 
2317 +          # FIXME: an accurate recipient list should be queried
 
2318 +          # from the todo.sr.ht PostgreSQL database to avoid backscattering.
 
2319 +          # But usernames are unfortunately not in that database but in meta.sr.ht.
 
2320 +          # Note that two syntaxes are allowed:
 
2321 +          # - ~username/tracker-name@todo.${domain}
 
2322 +          # - u.username.tracker-name@todo.${domain}
 
2323 +          localRecipients = [ "@todo.${domain}" ];
 
2325 +            todo.${domain} lmtp:unix:${cfg.settings."todo.sr.ht::mail".sock}
 
2331 +    (mkRenamedOptionModule [ "services" "sourcehut" "originBase" ]
 
2332 +                           [ "services" "sourcehut" "settings" "sr.ht" "global-domain" ])
 
2333 +    (mkRenamedOptionModule [ "services" "sourcehut" "address" ]
 
2334 +                           [ "services" "sourcehut" "listenAddress" ])
 
2338    meta.doc = ./sourcehut.xml;
 
2339 -  meta.maintainers = with maintainers; [ tomberek ];
 
2340 +  meta.maintainers = with maintainers; [ julm tomberek ];
 
2342 diff --git a/nixos/modules/services/misc/sourcehut/dispatch.nix b/nixos/modules/services/misc/sourcehut/dispatch.nix
 
2343 deleted file mode 100644
 
2344 index a9db17bebe8..00000000000
 
2345 --- a/nixos/modules/services/misc/sourcehut/dispatch.nix
 
2348 -{ config, lib, pkgs, ... }:
 
2352 -  cfg = config.services.sourcehut;
 
2353 -  cfgIni = cfg.settings;
 
2354 -  scfg = cfg.dispatch;
 
2355 -  iniKey = "dispatch.sr.ht";
 
2357 -  drv = pkgs.sourcehut.dispatchsrht;
 
2360 -  options.services.sourcehut.dispatch = {
 
2363 -      default = "dispatchsrht";
 
2365 -        User for dispatch.sr.ht.
 
2370 -      type = types.port;
 
2373 -        Port on which the "dispatch" module should listen.
 
2377 -    database = mkOption {
 
2379 -      default = "dispatch.sr.ht";
 
2381 -        PostgreSQL database name for dispatch.sr.ht.
 
2385 -    statePath = mkOption {
 
2386 -      type = types.path;
 
2387 -      default = "${cfg.statePath}/dispatchsrht";
 
2389 -        State path for dispatch.sr.ht.
 
2394 -  config = with scfg; lib.mkIf (cfg.enable && elem "dispatch" cfg.services) {
 
2399 -          isSystemUser = true;
 
2401 -          description = "dispatch.sr.ht user";
 
2410 -    services.postgresql = {
 
2411 -      authentication = ''
 
2412 -        local ${database} ${user} trust
 
2414 -      ensureDatabases = [ database ];
 
2418 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
2424 -      tmpfiles.rules = [
 
2425 -        "d ${statePath} 0750 ${user} ${user} -"
 
2428 -      services.dispatchsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
2429 -        after = [ "postgresql.service" "network.target" ];
 
2430 -        requires = [ "postgresql.service" ];
 
2431 -        wantedBy = [ "multi-user.target" ];
 
2433 -        description = "dispatch.sr.ht website service";
 
2435 -        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
2439 -    services.sourcehut.settings = {
 
2440 -      # URL dispatch.sr.ht is being served at (protocol://domain)
 
2441 -      "dispatch.sr.ht".origin = mkDefault "http://dispatch.${cfg.originBase}";
 
2442 -      # Address and port to bind the debug server to
 
2443 -      "dispatch.sr.ht".debug-host = mkDefault "0.0.0.0";
 
2444 -      "dispatch.sr.ht".debug-port = mkDefault port;
 
2445 -      # Configures the SQLAlchemy connection string for the database.
 
2446 -      "dispatch.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
2447 -      # Set to "yes" to automatically run migrations on package upgrade.
 
2448 -      "dispatch.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
2449 -      # dispatch.sr.ht's OAuth client ID and secret for meta.sr.ht
 
2450 -      # Register your client at meta.example.org/oauth
 
2451 -      "dispatch.sr.ht".oauth-client-id = mkDefault null;
 
2452 -      "dispatch.sr.ht".oauth-client-secret = mkDefault null;
 
2454 -      # Github Integration
 
2455 -      "dispatch.sr.ht::github".oauth-client-id = mkDefault null;
 
2456 -      "dispatch.sr.ht::github".oauth-client-secret = mkDefault null;
 
2458 -      # Gitlab Integration
 
2459 -      "dispatch.sr.ht::gitlab".enabled = mkDefault null;
 
2460 -      "dispatch.sr.ht::gitlab".canonical-upstream = mkDefault "gitlab.com";
 
2461 -      "dispatch.sr.ht::gitlab".repo-cache = mkDefault "./repo-cache";
 
2462 -      # "dispatch.sr.ht::gitlab"."gitlab.com" = mkDefault "GitLab:application id:secret";
 
2465 -    services.nginx.virtualHosts."dispatch.${cfg.originBase}" = {
 
2467 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
2468 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
2469 -      locations."/static".root = "${pkgs.sourcehut.dispatchsrht}/${pkgs.sourcehut.python.sitePackages}/dispatchsrht";
 
2473 diff --git a/nixos/modules/services/misc/sourcehut/git.nix b/nixos/modules/services/misc/sourcehut/git.nix
 
2474 deleted file mode 100644
 
2475 index 2653d77876d..00000000000
 
2476 --- a/nixos/modules/services/misc/sourcehut/git.nix
 
2479 -{ config, lib, pkgs, ... }:
 
2483 -  cfg = config.services.sourcehut;
 
2485 -  iniKey = "git.sr.ht";
 
2487 -  rcfg = config.services.redis;
 
2488 -  drv = pkgs.sourcehut.gitsrht;
 
2491 -  options.services.sourcehut.git = {
 
2499 -        User for git.sr.ht.
 
2504 -      type = types.port;
 
2507 -        Port on which the "git" module should listen.
 
2511 -    database = mkOption {
 
2513 -      default = "git.sr.ht";
 
2515 -        PostgreSQL database name for git.sr.ht.
 
2519 -    statePath = mkOption {
 
2520 -      type = types.path;
 
2521 -      default = "${cfg.statePath}/gitsrht";
 
2523 -        State path for git.sr.ht.
 
2527 -    package = mkOption {
 
2528 -      type = types.package;
 
2529 -      default = pkgs.git;
 
2530 -      defaultText = literalExpression "pkgs.git";
 
2531 -      example = literalExpression "pkgs.gitFull";
 
2533 -        Git package for git.sr.ht. This can help silence collisions.
 
2538 -  config = with scfg; lib.mkIf (cfg.enable && elem "git" cfg.services) {
 
2539 -    # sshd refuses to run with `Unsafe AuthorizedKeysCommand ... bad ownership or modes for directory /nix/store`
 
2540 -    environment.etc."ssh/gitsrht-dispatch" = {
 
2543 -        #! ${pkgs.stdenv.shell}
 
2544 -        ${cfg.python}/bin/gitsrht-dispatch "$@"
 
2548 -    # Needs this in the $PATH when sshing into the server
 
2549 -    environment.systemPackages = [ cfg.git.package ];
 
2554 -          isSystemUser = true;
 
2556 -          # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
 
2557 -          # Probably could use gitsrht-shell if output is restricted to just parameters...
 
2558 -          shell = pkgs.bash;
 
2559 -          description = "git.sr.ht user";
 
2569 -      cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/gitsrht-periodic" ];
 
2570 -      fcgiwrap.enable = true;
 
2572 -      openssh.authorizedKeysCommand = ''/etc/ssh/gitsrht-dispatch "%u" "%h" "%t" "%k"'';
 
2573 -      openssh.authorizedKeysCommandUser = "root";
 
2574 -      openssh.extraConfig = ''
 
2575 -        PermitUserEnvironment SRHT_*
 
2579 -        authentication = ''
 
2580 -          local ${database} ${user} trust
 
2582 -        ensureDatabases = [ database ];
 
2586 -            ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
2593 -      tmpfiles.rules = [
 
2594 -        # /var/log is owned by root
 
2595 -        "f /var/log/git-srht-shell 0644 ${user} ${user} -"
 
2597 -        "d ${statePath} 0750 ${user} ${user} -"
 
2598 -        "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
 
2602 -        gitsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
2603 -          after = [ "redis.service" "postgresql.service" "network.target" ];
 
2604 -          requires = [ "redis.service" "postgresql.service" ];
 
2605 -          wantedBy = [ "multi-user.target" ];
 
2607 -          # Needs internally to create repos at the very least
 
2608 -          path = [ pkgs.git ];
 
2609 -          description = "git.sr.ht website service";
 
2611 -          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
2614 -        gitsrht-webhooks = {
 
2615 -          after = [ "postgresql.service" "network.target" ];
 
2616 -          requires = [ "postgresql.service" ];
 
2617 -          wantedBy = [ "multi-user.target" ];
 
2619 -          description = "git.sr.ht webhooks service";
 
2623 -            Restart = "always";
 
2626 -          serviceConfig.ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
 
2631 -    services.sourcehut.settings = {
 
2632 -      # URL git.sr.ht is being served at (protocol://domain)
 
2633 -      "git.sr.ht".origin = mkDefault "http://git.${cfg.originBase}";
 
2634 -      # Address and port to bind the debug server to
 
2635 -      "git.sr.ht".debug-host = mkDefault "0.0.0.0";
 
2636 -      "git.sr.ht".debug-port = mkDefault port;
 
2637 -      # Configures the SQLAlchemy connection string for the database.
 
2638 -      "git.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
2639 -      # Set to "yes" to automatically run migrations on package upgrade.
 
2640 -      "git.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
2641 -      # The redis connection used for the webhooks worker
 
2642 -      "git.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
 
2644 -      # A post-update script which is installed in every git repo.
 
2645 -      "git.sr.ht".post-update-script = mkDefault "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
 
2647 -      # git.sr.ht's OAuth client ID and secret for meta.sr.ht
 
2648 -      # Register your client at meta.example.org/oauth
 
2649 -      "git.sr.ht".oauth-client-id = mkDefault null;
 
2650 -      "git.sr.ht".oauth-client-secret = mkDefault null;
 
2651 -      # Path to git repositories on disk
 
2652 -      "git.sr.ht".repos = mkDefault "/var/lib/git";
 
2654 -      "git.sr.ht".outgoing-domain = mkDefault "http://git.${cfg.originBase}";
 
2656 -      # The authorized keys hook uses this to dispatch to various handlers
 
2657 -      # The format is a program to exec into as the key, and the user to match as the
 
2658 -      # value. When someone tries to log in as this user, this program is executed
 
2659 -      # and is expected to omit an AuthorizedKeys file.
 
2661 -      # Discard of the string context is in order to allow derivation-derived strings.
 
2662 -      # This is safe if the relevant package is installed which will be the case if the setting is utilized.
 
2663 -      "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys"} = mkDefault "${user}:${user}";
 
2666 -    services.nginx.virtualHosts."git.${cfg.originBase}" = {
 
2668 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
2669 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
2670 -      locations."/static".root = "${pkgs.sourcehut.gitsrht}/${pkgs.sourcehut.python.sitePackages}/gitsrht";
 
2672 -            location = /authorize {
 
2673 -            proxy_pass http://${cfg.address}:${toString port};
 
2674 -            proxy_pass_request_body off;
 
2675 -            proxy_set_header Content-Length "";
 
2676 -            proxy_set_header X-Original-URI $request_uri;
 
2678 -            location ~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$ {
 
2679 -                auth_request /authorize;
 
2680 -                root /var/lib/git;
 
2681 -                fastcgi_pass unix:/run/fcgiwrap.sock;
 
2682 -                fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;
 
2683 -                fastcgi_param PATH_INFO $uri;
 
2684 -                fastcgi_param GIT_PROJECT_ROOT $document_root;
 
2685 -                fastcgi_read_timeout 500s;
 
2686 -                include ${pkgs.nginx}/conf/fastcgi_params;
 
2694 diff --git a/nixos/modules/services/misc/sourcehut/hg.nix b/nixos/modules/services/misc/sourcehut/hg.nix
 
2695 deleted file mode 100644
 
2696 index 5cd36bb0455..00000000000
 
2697 --- a/nixos/modules/services/misc/sourcehut/hg.nix
 
2700 -{ config, lib, pkgs, ... }:
 
2704 -  cfg = config.services.sourcehut;
 
2706 -  iniKey = "hg.sr.ht";
 
2708 -  rcfg = config.services.redis;
 
2709 -  drv = pkgs.sourcehut.hgsrht;
 
2712 -  options.services.sourcehut.hg = {
 
2719 -        User for hg.sr.ht.
 
2724 -      type = types.port;
 
2727 -        Port on which the "hg" module should listen.
 
2731 -    database = mkOption {
 
2733 -      default = "hg.sr.ht";
 
2735 -        PostgreSQL database name for hg.sr.ht.
 
2739 -    statePath = mkOption {
 
2740 -      type = types.path;
 
2741 -      default = "${cfg.statePath}/hgsrht";
 
2743 -        State path for hg.sr.ht.
 
2747 -    cloneBundles = mkOption {
 
2748 -      type = types.bool;
 
2751 -        Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
 
2756 -  config = with scfg; lib.mkIf (cfg.enable && elem "hg" cfg.services) {
 
2757 -    # In case it ever comes into being
 
2758 -    environment.etc."ssh/hgsrht-dispatch" = {
 
2761 -        #! ${pkgs.stdenv.shell}
 
2762 -        ${cfg.python}/bin/gitsrht-dispatch $@
 
2766 -    environment.systemPackages = [ pkgs.mercurial ];
 
2771 -          isSystemUser = true;
 
2773 -          # Assuming hg.sr.ht needs this too
 
2774 -          shell = pkgs.bash;
 
2775 -          description = "hg.sr.ht user";
 
2785 -      cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/hgsrht-periodic" ]
 
2786 -        ++ optional cloneBundles "0 * * * * ${cfg.python}/bin/hgsrht-clonebundles";
 
2788 -      openssh.authorizedKeysCommand = ''/etc/ssh/hgsrht-dispatch "%u" "%h" "%t" "%k"'';
 
2789 -      openssh.authorizedKeysCommandUser = "root";
 
2790 -      openssh.extraConfig = ''
 
2791 -        PermitUserEnvironment SRHT_*
 
2795 -        authentication = ''
 
2796 -          local ${database} ${user} trust
 
2798 -        ensureDatabases = [ database ];
 
2802 -            ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
2809 -      tmpfiles.rules = [
 
2810 -        # /var/log is owned by root
 
2811 -        "f /var/log/hg-srht-shell 0644 ${user} ${user} -"
 
2813 -        "d ${statePath} 0750 ${user} ${user} -"
 
2814 -        "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
 
2817 -      services.hgsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
2818 -        after = [ "redis.service" "postgresql.service" "network.target" ];
 
2819 -        requires = [ "redis.service" "postgresql.service" ];
 
2820 -        wantedBy = [ "multi-user.target" ];
 
2822 -        path = [ pkgs.mercurial ];
 
2823 -        description = "hg.sr.ht website service";
 
2825 -        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
2829 -    services.sourcehut.settings = {
 
2830 -      # URL hg.sr.ht is being served at (protocol://domain)
 
2831 -      "hg.sr.ht".origin = mkDefault "http://hg.${cfg.originBase}";
 
2832 -      # Address and port to bind the debug server to
 
2833 -      "hg.sr.ht".debug-host = mkDefault "0.0.0.0";
 
2834 -      "hg.sr.ht".debug-port = mkDefault port;
 
2835 -      # Configures the SQLAlchemy connection string for the database.
 
2836 -      "hg.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
2837 -      # The redis connection used for the webhooks worker
 
2838 -      "hg.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
 
2839 -      # A post-update script which is installed in every mercurial repo.
 
2840 -      "hg.sr.ht".changegroup-script = mkDefault "${cfg.python}/bin/hgsrht-hook-changegroup";
 
2841 -      # hg.sr.ht's OAuth client ID and secret for meta.sr.ht
 
2842 -      # Register your client at meta.example.org/oauth
 
2843 -      "hg.sr.ht".oauth-client-id = mkDefault null;
 
2844 -      "hg.sr.ht".oauth-client-secret = mkDefault null;
 
2845 -      # Path to mercurial repositories on disk
 
2846 -      "hg.sr.ht".repos = mkDefault "/var/lib/hg";
 
2847 -      # Path to the srht mercurial extension
 
2848 -      # (defaults to where the hgsrht code is)
 
2849 -      # "hg.sr.ht".srhtext = mkDefault null;
 
2850 -      # .hg/store size (in MB) past which the nightly job generates clone bundles.
 
2851 -      # "hg.sr.ht".clone_bundle_threshold = mkDefault 50;
 
2852 -      # Path to hg-ssh (if not in $PATH)
 
2853 -      # "hg.sr.ht".hg_ssh = mkDefault /path/to/hg-ssh;
 
2855 -      # The authorized keys hook uses this to dispatch to various handlers
 
2856 -      # The format is a program to exec into as the key, and the user to match as the
 
2857 -      # value. When someone tries to log in as this user, this program is executed
 
2858 -      # and is expected to omit an AuthorizedKeys file.
 
2860 -      # Uncomment the relevant lines to enable the various sr.ht dispatchers.
 
2861 -      "hg.sr.ht::dispatch"."/run/current-system/sw/bin/hgsrht-keys" = mkDefault "${user}:${user}";
 
2864 -    # TODO: requires testing and addition of hg-specific requirements
 
2865 -    services.nginx.virtualHosts."hg.${cfg.originBase}" = {
 
2867 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
2868 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
2869 -      locations."/static".root = "${pkgs.sourcehut.hgsrht}/${pkgs.sourcehut.python.sitePackages}/hgsrht";
 
2873 diff --git a/nixos/modules/services/misc/sourcehut/hub.nix b/nixos/modules/services/misc/sourcehut/hub.nix
 
2874 deleted file mode 100644
 
2875 index be3ea21011c..00000000000
 
2876 --- a/nixos/modules/services/misc/sourcehut/hub.nix
 
2879 -{ config, lib, pkgs, ... }:
 
2883 -  cfg = config.services.sourcehut;
 
2884 -  cfgIni = cfg.settings;
 
2886 -  iniKey = "hub.sr.ht";
 
2888 -  drv = pkgs.sourcehut.hubsrht;
 
2891 -  options.services.sourcehut.hub = {
 
2894 -      default = "hubsrht";
 
2896 -        User for hub.sr.ht.
 
2901 -      type = types.port;
 
2904 -        Port on which the "hub" module should listen.
 
2908 -    database = mkOption {
 
2910 -      default = "hub.sr.ht";
 
2912 -        PostgreSQL database name for hub.sr.ht.
 
2916 -    statePath = mkOption {
 
2917 -      type = types.path;
 
2918 -      default = "${cfg.statePath}/hubsrht";
 
2920 -        State path for hub.sr.ht.
 
2925 -  config = with scfg; lib.mkIf (cfg.enable && elem "hub" cfg.services) {
 
2929 -          isSystemUser = true;
 
2931 -          description = "hub.sr.ht user";
 
2940 -    services.postgresql = {
 
2941 -      authentication = ''
 
2942 -        local ${database} ${user} trust
 
2944 -      ensureDatabases = [ database ];
 
2948 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
2954 -      tmpfiles.rules = [
 
2955 -        "d ${statePath} 0750 ${user} ${user} -"
 
2958 -      services.hubsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
2959 -        after = [ "postgresql.service" "network.target" ];
 
2960 -        requires = [ "postgresql.service" ];
 
2961 -        wantedBy = [ "multi-user.target" ];
 
2963 -        description = "hub.sr.ht website service";
 
2965 -        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
2969 -    services.sourcehut.settings = {
 
2970 -      # URL hub.sr.ht is being served at (protocol://domain)
 
2971 -      "hub.sr.ht".origin = mkDefault "http://hub.${cfg.originBase}";
 
2972 -      # Address and port to bind the debug server to
 
2973 -      "hub.sr.ht".debug-host = mkDefault "0.0.0.0";
 
2974 -      "hub.sr.ht".debug-port = mkDefault port;
 
2975 -      # Configures the SQLAlchemy connection string for the database.
 
2976 -      "hub.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
2977 -      # Set to "yes" to automatically run migrations on package upgrade.
 
2978 -      "hub.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
2979 -      # hub.sr.ht's OAuth client ID and secret for meta.sr.ht
 
2980 -      # Register your client at meta.example.org/oauth
 
2981 -      "hub.sr.ht".oauth-client-id = mkDefault null;
 
2982 -      "hub.sr.ht".oauth-client-secret = mkDefault null;
 
2985 -    services.nginx.virtualHosts."${cfg.originBase}" = {
 
2987 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
2988 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
2989 -      locations."/static".root = "${pkgs.sourcehut.hubsrht}/${pkgs.sourcehut.python.sitePackages}/hubsrht";
 
2991 -    services.nginx.virtualHosts."hub.${cfg.originBase}" = {
 
2992 -      globalRedirect = "${cfg.originBase}";
 
2997 diff --git a/nixos/modules/services/misc/sourcehut/lists.nix b/nixos/modules/services/misc/sourcehut/lists.nix
 
2998 deleted file mode 100644
 
2999 index 7b1fe9fd463..00000000000
 
3000 --- a/nixos/modules/services/misc/sourcehut/lists.nix
 
3003 -# Email setup is fairly involved, useful references:
 
3004 -# https://drewdevault.com/2018/08/05/Local-mail-server.html
 
3006 -{ config, lib, pkgs, ... }:
 
3010 -  cfg = config.services.sourcehut;
 
3011 -  cfgIni = cfg.settings;
 
3013 -  iniKey = "lists.sr.ht";
 
3015 -  rcfg = config.services.redis;
 
3016 -  drv = pkgs.sourcehut.listssrht;
 
3019 -  options.services.sourcehut.lists = {
 
3022 -      default = "listssrht";
 
3024 -        User for lists.sr.ht.
 
3029 -      type = types.port;
 
3032 -        Port on which the "lists" module should listen.
 
3036 -    database = mkOption {
 
3038 -      default = "lists.sr.ht";
 
3040 -        PostgreSQL database name for lists.sr.ht.
 
3044 -    statePath = mkOption {
 
3045 -      type = types.path;
 
3046 -      default = "${cfg.statePath}/listssrht";
 
3048 -        State path for lists.sr.ht.
 
3053 -  config = with scfg; lib.mkIf (cfg.enable && elem "lists" cfg.services) {
 
3057 -          isSystemUser = true;
 
3059 -          extraGroups = [ "postfix" ];
 
3060 -          description = "lists.sr.ht user";
 
3068 -    services.postgresql = {
 
3069 -      authentication = ''
 
3070 -        local ${database} ${user} trust
 
3072 -      ensureDatabases = [ database ];
 
3076 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
3082 -      tmpfiles.rules = [
 
3083 -        "d ${statePath} 0750 ${user} ${user} -"
 
3087 -        listssrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
3088 -          after = [ "postgresql.service" "network.target" ];
 
3089 -          requires = [ "postgresql.service" ];
 
3090 -          wantedBy = [ "multi-user.target" ];
 
3092 -          description = "lists.sr.ht website service";
 
3094 -          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
3097 -        listssrht-process = {
 
3098 -          after = [ "postgresql.service" "network.target" ];
 
3099 -          requires = [ "postgresql.service" ];
 
3100 -          wantedBy = [ "multi-user.target" ];
 
3102 -          description = "lists.sr.ht process service";
 
3106 -            Restart = "always";
 
3107 -            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.process worker --loglevel=info";
 
3111 -        listssrht-lmtp = {
 
3112 -          after = [ "postgresql.service" "network.target" ];
 
3113 -          requires = [ "postgresql.service" ];
 
3114 -          wantedBy = [ "multi-user.target" ];
 
3116 -          description = "lists.sr.ht process service";
 
3120 -            Restart = "always";
 
3121 -            ExecStart = "${cfg.python}/bin/listssrht-lmtp";
 
3126 -        listssrht-webhooks = {
 
3127 -          after = [ "postgresql.service" "network.target" ];
 
3128 -          requires = [ "postgresql.service" ];
 
3129 -          wantedBy = [ "multi-user.target" ];
 
3131 -          description = "lists.sr.ht webhooks service";
 
3135 -            Restart = "always";
 
3136 -            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
 
3142 -    services.sourcehut.settings = {
 
3143 -      # URL lists.sr.ht is being served at (protocol://domain)
 
3144 -      "lists.sr.ht".origin = mkDefault "http://lists.${cfg.originBase}";
 
3145 -      # Address and port to bind the debug server to
 
3146 -      "lists.sr.ht".debug-host = mkDefault "0.0.0.0";
 
3147 -      "lists.sr.ht".debug-port = mkDefault port;
 
3148 -      # Configures the SQLAlchemy connection string for the database.
 
3149 -      "lists.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
3150 -      # Set to "yes" to automatically run migrations on package upgrade.
 
3151 -      "lists.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
3152 -      # lists.sr.ht's OAuth client ID and secret for meta.sr.ht
 
3153 -      # Register your client at meta.example.org/oauth
 
3154 -      "lists.sr.ht".oauth-client-id = mkDefault null;
 
3155 -      "lists.sr.ht".oauth-client-secret = mkDefault null;
 
3156 -      # Outgoing email for notifications generated by users
 
3157 -      "lists.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
 
3158 -      # The redis connection used for the webhooks worker
 
3159 -      "lists.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/2";
 
3160 -      # The redis connection used for the celery worker
 
3161 -      "lists.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/4";
 
3163 -      "lists.sr.ht".network-key = mkDefault null;
 
3165 -      "lists.sr.ht".allow-new-lists = mkDefault "no";
 
3167 -      "lists.sr.ht".posting-domain = mkDefault "lists.${cfg.originBase}";
 
3169 -      # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
 
3170 -      # Alternatively, specify IP:PORT and an SMTP server will be run instead.
 
3171 -      "lists.sr.ht::worker".sock = mkDefault "/tmp/lists.sr.ht-lmtp.sock";
 
3172 -      # The lmtp daemon will make the unix socket group-read/write for users in this
 
3174 -      "lists.sr.ht::worker".sock-group = mkDefault "postfix";
 
3175 -      "lists.sr.ht::worker".reject-url = mkDefault "https://man.sr.ht/lists.sr.ht/etiquette.md";
 
3176 -      "lists.sr.ht::worker".reject-mimetypes = mkDefault "text/html";
 
3180 -    services.nginx.virtualHosts."lists.${cfg.originBase}" = {
 
3182 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
3183 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
3184 -      locations."/static".root = "${pkgs.sourcehut.listssrht}/${pkgs.sourcehut.python.sitePackages}/listssrht";
 
3188 diff --git a/nixos/modules/services/misc/sourcehut/man.nix b/nixos/modules/services/misc/sourcehut/man.nix
 
3189 deleted file mode 100644
 
3190 index 7693396d187..00000000000
 
3191 --- a/nixos/modules/services/misc/sourcehut/man.nix
 
3194 -{ config, lib, pkgs, ... }:
 
3198 -  cfg = config.services.sourcehut;
 
3199 -  cfgIni = cfg.settings;
 
3201 -  iniKey = "man.sr.ht";
 
3203 -  drv = pkgs.sourcehut.mansrht;
 
3206 -  options.services.sourcehut.man = {
 
3209 -      default = "mansrht";
 
3211 -        User for man.sr.ht.
 
3216 -      type = types.port;
 
3219 -        Port on which the "man" module should listen.
 
3223 -    database = mkOption {
 
3225 -      default = "man.sr.ht";
 
3227 -        PostgreSQL database name for man.sr.ht.
 
3231 -    statePath = mkOption {
 
3232 -      type = types.path;
 
3233 -      default = "${cfg.statePath}/mansrht";
 
3235 -        State path for man.sr.ht.
 
3240 -  config = with scfg; lib.mkIf (cfg.enable && elem "man" cfg.services) {
 
3244 -          assertion = hasAttrByPath [ "git.sr.ht" "oauth-client-id" ] cfgIni;
 
3245 -          message = "man.sr.ht needs access to git.sr.ht.";
 
3252 -          isSystemUser = true;
 
3254 -          description = "man.sr.ht user";
 
3263 -    services.postgresql = {
 
3264 -      authentication = ''
 
3265 -        local ${database} ${user} trust
 
3267 -      ensureDatabases = [ database ];
 
3271 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
3277 -      tmpfiles.rules = [
 
3278 -        "d ${statePath} 0750 ${user} ${user} -"
 
3281 -      services.mansrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
3282 -        after = [ "postgresql.service" "network.target" ];
 
3283 -        requires = [ "postgresql.service" ];
 
3284 -        wantedBy = [ "multi-user.target" ];
 
3286 -        description = "man.sr.ht website service";
 
3288 -        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
3292 -    services.sourcehut.settings = {
 
3293 -      # URL man.sr.ht is being served at (protocol://domain)
 
3294 -      "man.sr.ht".origin = mkDefault "http://man.${cfg.originBase}";
 
3295 -      # Address and port to bind the debug server to
 
3296 -      "man.sr.ht".debug-host = mkDefault "0.0.0.0";
 
3297 -      "man.sr.ht".debug-port = mkDefault port;
 
3298 -      # Configures the SQLAlchemy connection string for the database.
 
3299 -      "man.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
3300 -      # Set to "yes" to automatically run migrations on package upgrade.
 
3301 -      "man.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
3302 -      # man.sr.ht's OAuth client ID and secret for meta.sr.ht
 
3303 -      # Register your client at meta.example.org/oauth
 
3304 -      "man.sr.ht".oauth-client-id = mkDefault null;
 
3305 -      "man.sr.ht".oauth-client-secret = mkDefault null;
 
3308 -    services.nginx.virtualHosts."man.${cfg.originBase}" = {
 
3310 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
3311 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
3312 -      locations."/static".root = "${pkgs.sourcehut.mansrht}/${pkgs.sourcehut.python.sitePackages}/mansrht";
 
3316 diff --git a/nixos/modules/services/misc/sourcehut/meta.nix b/nixos/modules/services/misc/sourcehut/meta.nix
 
3317 deleted file mode 100644
 
3318 index 56127a824eb..00000000000
 
3319 --- a/nixos/modules/services/misc/sourcehut/meta.nix
 
3322 -{ config, lib, pkgs, ... }:
 
3326 -  cfg = config.services.sourcehut;
 
3327 -  cfgIni = cfg.settings;
 
3329 -  iniKey = "meta.sr.ht";
 
3331 -  rcfg = config.services.redis;
 
3332 -  drv = pkgs.sourcehut.metasrht;
 
3335 -  options.services.sourcehut.meta = {
 
3338 -      default = "metasrht";
 
3340 -        User for meta.sr.ht.
 
3345 -      type = types.port;
 
3348 -        Port on which the "meta" module should listen.
 
3352 -    database = mkOption {
 
3354 -      default = "meta.sr.ht";
 
3356 -        PostgreSQL database name for meta.sr.ht.
 
3360 -    statePath = mkOption {
 
3361 -      type = types.path;
 
3362 -      default = "${cfg.statePath}/metasrht";
 
3364 -        State path for meta.sr.ht.
 
3369 -  config = with scfg; lib.mkIf (cfg.enable && elem "meta" cfg.services) {
 
3373 -          assertion = with cfgIni."meta.sr.ht::billing"; enabled == "yes" -> (stripe-public-key != null && stripe-secret-key != null);
 
3374 -          message = "If meta.sr.ht::billing is enabled, the keys should be defined.";
 
3381 -          isSystemUser = true;
 
3383 -          description = "meta.sr.ht user";
 
3392 -    services.cron.systemCronJobs = [ "0 0 * * * ${cfg.python}/bin/metasrht-daily" ];
 
3393 -    services.postgresql = {
 
3394 -      authentication = ''
 
3395 -        local ${database} ${user} trust
 
3397 -      ensureDatabases = [ database ];
 
3401 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
3407 -      tmpfiles.rules = [
 
3408 -        "d ${statePath} 0750 ${user} ${user} -"
 
3412 -        metasrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
3413 -          after = [ "postgresql.service" "network.target" ];
 
3414 -          requires = [ "postgresql.service" ];
 
3415 -          wantedBy = [ "multi-user.target" ];
 
3417 -          description = "meta.sr.ht website service";
 
3420 -            # Configure client(s) as "preauthorized"
 
3421 -            ${concatMapStringsSep "\n\n"
 
3423 -                if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
 
3424 -                  # Configure ${attr}'s OAuth client as "preauthorized"
 
3425 -                  psql ${database} \
 
3426 -                    -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
 
3428 -                  printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
 
3431 -              (builtins.attrNames (filterAttrs
 
3432 -                (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
 
3436 -          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
3439 -        metasrht-api = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
3440 -          after = [ "postgresql.service" "network.target" ];
 
3441 -          requires = [ "postgresql.service" ];
 
3442 -          wantedBy = [ "multi-user.target" ];
 
3444 -          description = "meta.sr.ht api service";
 
3447 -            # Configure client(s) as "preauthorized"
 
3448 -            ${concatMapStringsSep "\n\n"
 
3450 -                if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
 
3451 -                  # Configure ${attr}'s OAuth client as "preauthorized"
 
3452 -                  psql ${database} \
 
3453 -                    -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
 
3455 -                  printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
 
3458 -              (builtins.attrNames (filterAttrs
 
3459 -                (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
 
3463 -          serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b :${toString (port + 100)}";
 
3466 -        metasrht-webhooks = {
 
3467 -          after = [ "postgresql.service" "network.target" ];
 
3468 -          requires = [ "postgresql.service" ];
 
3469 -          wantedBy = [ "multi-user.target" ];
 
3471 -          description = "meta.sr.ht webhooks service";
 
3475 -            Restart = "always";
 
3476 -            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
 
3483 -    services.sourcehut.settings = {
 
3484 -      # URL meta.sr.ht is being served at (protocol://domain)
 
3485 -      "meta.sr.ht".origin = mkDefault "https://meta.${cfg.originBase}";
 
3486 -      # Address and port to bind the debug server to
 
3487 -      "meta.sr.ht".debug-host = mkDefault "0.0.0.0";
 
3488 -      "meta.sr.ht".debug-port = mkDefault port;
 
3489 -      # Configures the SQLAlchemy connection string for the database.
 
3490 -      "meta.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
3491 -      # Set to "yes" to automatically run migrations on package upgrade.
 
3492 -      "meta.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
3493 -      # If "yes", the user will be sent the stock sourcehut welcome emails after
 
3494 -      # signup (requires cron to be configured properly). These are specific to the
 
3495 -      # sr.ht instance so you probably want to patch these before enabling this.
 
3496 -      "meta.sr.ht".welcome-emails = mkDefault "no";
 
3498 -      # The redis connection used for the webhooks worker
 
3499 -      "meta.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/6";
 
3501 -      # If "no", public registration will not be permitted.
 
3502 -      "meta.sr.ht::settings".registration = mkDefault "no";
 
3503 -      # Where to redirect new users upon registration
 
3504 -      "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${cfg.originBase}";
 
3505 -      # How many invites each user is issued upon registration (only applicable if
 
3506 -      # open registration is disabled)
 
3507 -      "meta.sr.ht::settings".user-invites = mkDefault 5;
 
3509 -      # Origin URL for API, 100 more than web
 
3510 -      "meta.sr.ht".api-origin = mkDefault "http://localhost:5100";
 
3512 -      # You can add aliases for the client IDs of commonly used OAuth clients here.
 
3515 -      "meta.sr.ht::aliases" = mkDefault { };
 
3516 -      # "meta.sr.ht::aliases"."git.sr.ht" = 12345;
 
3518 -      # "yes" to enable the billing system
 
3519 -      "meta.sr.ht::billing".enabled = mkDefault "no";
 
3520 -      # Get your keys at https://dashboard.stripe.com/account/apikeys
 
3521 -      "meta.sr.ht::billing".stripe-public-key = mkDefault null;
 
3522 -      "meta.sr.ht::billing".stripe-secret-key = mkDefault null;
 
3525 -    services.nginx.virtualHosts."meta.${cfg.originBase}" = {
 
3527 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
3528 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
3529 -      locations."/static".root = "${pkgs.sourcehut.metasrht}/${pkgs.sourcehut.python.sitePackages}/metasrht";
 
3533 diff --git a/nixos/modules/services/misc/sourcehut/paste.nix b/nixos/modules/services/misc/sourcehut/paste.nix
 
3534 deleted file mode 100644
 
3535 index b2d5151969e..00000000000
 
3536 --- a/nixos/modules/services/misc/sourcehut/paste.nix
 
3539 -{ config, lib, pkgs, ... }:
 
3543 -  cfg = config.services.sourcehut;
 
3544 -  cfgIni = cfg.settings;
 
3546 -  iniKey = "paste.sr.ht";
 
3548 -  rcfg = config.services.redis;
 
3549 -  drv = pkgs.sourcehut.pastesrht;
 
3552 -  options.services.sourcehut.paste = {
 
3555 -      default = "pastesrht";
 
3557 -        User for paste.sr.ht.
 
3562 -      type = types.port;
 
3565 -        Port on which the "paste" module should listen.
 
3569 -    database = mkOption {
 
3571 -      default = "paste.sr.ht";
 
3573 -        PostgreSQL database name for paste.sr.ht.
 
3577 -    statePath = mkOption {
 
3578 -      type = types.path;
 
3579 -      default = "${cfg.statePath}/pastesrht";
 
3581 -        State path for pastesrht.sr.ht.
 
3586 -  config = with scfg; lib.mkIf (cfg.enable && elem "paste" cfg.services) {
 
3590 -          isSystemUser = true;
 
3592 -          description = "paste.sr.ht user";
 
3601 -    services.postgresql = {
 
3602 -      authentication = ''
 
3603 -        local ${database} ${user} trust
 
3605 -      ensureDatabases = [ database ];
 
3609 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
3615 -      tmpfiles.rules = [
 
3616 -        "d ${statePath} 0750 ${user} ${user} -"
 
3620 -        pastesrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
3621 -          after = [ "postgresql.service" "network.target" ];
 
3622 -          requires = [ "postgresql.service" ];
 
3623 -          wantedBy = [ "multi-user.target" ];
 
3625 -          description = "paste.sr.ht website service";
 
3627 -          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
3630 -        pastesrht-webhooks = {
 
3631 -          after = [ "postgresql.service" "network.target" ];
 
3632 -          requires = [ "postgresql.service" ];
 
3633 -          wantedBy = [ "multi-user.target" ];
 
3635 -          description = "paste.sr.ht webhooks service";
 
3639 -            Restart = "always";
 
3640 -            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
 
3647 -    services.sourcehut.settings = {
 
3648 -      # URL paste.sr.ht is being served at (protocol://domain)
 
3649 -      "paste.sr.ht".origin = mkDefault "http://paste.${cfg.originBase}";
 
3650 -      # Address and port to bind the debug server to
 
3651 -      "paste.sr.ht".debug-host = mkDefault "0.0.0.0";
 
3652 -      "paste.sr.ht".debug-port = mkDefault port;
 
3653 -      # Configures the SQLAlchemy connection string for the database.
 
3654 -      "paste.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
3655 -      # Set to "yes" to automatically run migrations on package upgrade.
 
3656 -      "paste.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
3657 -      # paste.sr.ht's OAuth client ID and secret for meta.sr.ht
 
3658 -      # Register your client at meta.example.org/oauth
 
3659 -      "paste.sr.ht".oauth-client-id = mkDefault null;
 
3660 -      "paste.sr.ht".oauth-client-secret = mkDefault null;
 
3661 -      "paste.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/5";
 
3664 -    services.nginx.virtualHosts."paste.${cfg.originBase}" = {
 
3666 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
3667 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
3668 -      locations."/static".root = "${pkgs.sourcehut.pastesrht}/${pkgs.sourcehut.python.sitePackages}/pastesrht";
 
3672 diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
 
3673 index 65b4ad020f9..b3c0efc07dd 100644
 
3674 --- a/nixos/modules/services/misc/sourcehut/service.nix
 
3675 +++ b/nixos/modules/services/misc/sourcehut/service.nix
 
3677 -{ config, pkgs, lib }:
 
3678 -serviceCfg: serviceDrv: iniKey: attrs:
 
3680 +{ configIniOfService
 
3681 +, srvsrht ? "${srv}srht" # Because "buildsrht" does not follow that pattern (missing an "s").
 
3682 +, iniKey ? "${srv}.sr.ht"
 
3686 +, extraServices ? {}
 
3690 +{ config, lib, pkgs, ... }:
 
3694 +  inherit (config.services) postgresql;
 
3695 +  redis = config.services.redis.servers."sourcehut-${srvsrht}";
 
3696 +  inherit (config.users) users;
 
3697    cfg = config.services.sourcehut;
 
3698 -  cfgIni = cfg.settings."${iniKey}";
 
3699 -  pgSuperUser = config.services.postgresql.superUser;
 
3701 -  setupDB = pkgs.writeScript "${serviceDrv.pname}-gen-db" ''
 
3702 -    #! ${cfg.python}/bin/python
 
3703 -    from ${serviceDrv.pname}.app import db
 
3706 +  configIni = configIniOfService srv;
 
3707 +  srvCfg = cfg.${srv};
 
3708 +  baseService = serviceName: { allowStripe ? false }: extraService: let
 
3709 +    runDir = "/run/sourcehut/${serviceName}";
 
3710 +    rootDir = "/run/sourcehut/chroots/${serviceName}";
 
3712 +    mkMerge [ extraService {
 
3713 +    after = [ "network.target" ] ++
 
3714 +      optional cfg.postgresql.enable "postgresql.service" ++
 
3715 +      optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
 
3717 +      optional cfg.postgresql.enable "postgresql.service" ++
 
3718 +      optional cfg.redis.enable "redis-sourcehut-${srvsrht}.service";
 
3719 +    path = [ pkgs.gawk ];
 
3720 +    environment.HOME = runDir;
 
3722 +      User = mkDefault srvCfg.user;
 
3723 +      Group = mkDefault srvCfg.group;
 
3724 +      RuntimeDirectory = [
 
3725 +        "sourcehut/${serviceName}"
 
3726 +        # Used by *srht-keys which reads ../config.ini
 
3727 +        "sourcehut/${serviceName}/subdir"
 
3728 +        "sourcehut/chroots/${serviceName}"
 
3730 +      RuntimeDirectoryMode = "2750";
 
3731 +      # No need for the chroot path once inside the chroot
 
3732 +      InaccessiblePaths = [ "-+${rootDir}" ];
 
3733 +      # g+rx is for group members (eg. fcgiwrap or nginx)
 
3734 +      # to read Git/Mercurial repositories, buildlogs, etc.
 
3735 +      # o+x is for intermediate directories created by BindPaths= and like,
 
3736 +      # as they're owned by root:root.
 
3738 +      RootDirectory = rootDir;
 
3739 +      RootDirectoryStartOnly = true;
 
3740 +      PrivateTmp = true;
 
3741 +      MountAPIVFS = true;
 
3742 +      # config.ini is looked up in there, before /etc/srht/config.ini
 
3743 +      # Note that it fails to be set in ExecStartPre=
 
3744 +      WorkingDirectory = mkDefault ("-"+runDir);
 
3745 +      BindReadOnlyPaths = [
 
3748 +        "/run/booted-system"
 
3749 +        "/run/current-system"
 
3752 +        optional cfg.postgresql.enable "/run/postgresql" ++
 
3753 +        optional cfg.redis.enable "/run/redis-sourcehut-${srvsrht}";
 
3754 +      # LoadCredential= are unfortunately not available in ExecStartPre=
 
3755 +      # Hence this one is run as root (the +) with RootDirectoryStartOnly=
 
3756 +      # to reach credentials wherever they are.
 
3757 +      # Note that each systemd service gets its own ${runDir}/config.ini file.
 
3758 +      ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
 
3760 +        # Replace values begining with a '<' by the content of the file whose name is after.
 
3761 +        gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
 
3762 +        ${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
 
3763 +        install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
 
3765 +      # The following options are only for optimizing:
 
3766 +      # systemd-analyze security
 
3767 +      AmbientCapabilities = "";
 
3768 +      CapabilityBoundingSet = "";
 
3769 +      # ProtectClock= adds DeviceAllow=char-rtc r
 
3771 +      LockPersonality = true;
 
3772 +      MemoryDenyWriteExecute = true;
 
3773 +      NoNewPrivileges = true;
 
3774 +      PrivateDevices = true;
 
3775 +      PrivateMounts = true;
 
3776 +      PrivateNetwork = mkDefault false;
 
3777 +      PrivateUsers = true;
 
3778 +      ProcSubset = "pid";
 
3779 +      ProtectClock = true;
 
3780 +      ProtectControlGroups = true;
 
3781 +      ProtectHome = true;
 
3782 +      ProtectHostname = true;
 
3783 +      ProtectKernelLogs = true;
 
3784 +      ProtectKernelModules = true;
 
3785 +      ProtectKernelTunables = true;
 
3786 +      ProtectProc = "invisible";
 
3787 +      ProtectSystem = "strict";
 
3789 +      RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
 
3790 +      RestrictNamespaces = true;
 
3791 +      RestrictRealtime = true;
 
3792 +      RestrictSUIDSGID = true;
 
3793 +      #SocketBindAllow = [ "tcp:${toString srvCfg.port}" "tcp:${toString srvCfg.prometheusPort}" ];
 
3794 +      #SocketBindDeny = "any";
 
3795 +      SystemCallFilter = [
 
3797 +        "~@aio" "~@keyring" "~@memlock" "~@privileged" "~@resources" "~@timer"
 
3798 +        "@chown" "@setuid"
 
3800 +      SystemCallArchitectures = "native";
 
3804 -with serviceCfg; with lib; recursiveUpdate
 
3806 -  environment.HOME = statePath;
 
3807 -  path = [ config.services.postgresql.package ] ++ (attrs.path or [ ]);
 
3808 -  restartTriggers = [ config.environment.etc."sr.ht/config.ini".source ];
 
3813 -    Restart = "always";
 
3814 -    WorkingDirectory = statePath;
 
3815 -  } // (if (cfg.statePath == "/var/lib/sourcehut/${serviceDrv.pname}") then {
 
3816 -          StateDirectory = [ "sourcehut/${serviceDrv.pname}" ];
 
3819 +  options.services.sourcehut.${srv} = {
 
3820 +    enable = mkEnableOption "${srv} service";
 
3823 -    if ! test -e ${statePath}/db; then
 
3824 -      # Setup the initial database
 
3828 +      default = srvsrht;
 
3830 +        User for ${srv}.sr.ht.
 
3834 -      # Set the initial state of the database for future database upgrades
 
3835 -      if test -e ${cfg.python}/bin/${serviceDrv.pname}-migrate; then
 
3836 -        # Run alembic stamp head once to tell alembic the schema is up-to-date
 
3837 -        ${cfg.python}/bin/${serviceDrv.pname}-migrate stamp head
 
3839 +    group = mkOption {
 
3841 +      default = srvsrht;
 
3843 +        Group for ${srv}.sr.ht.
 
3844 +        Membership grants access to the Git/Mercurial repositories by default,
 
3845 +        but not to the config.ini file (where secrets are).
 
3849 -      printf "%s" "${serviceDrv.version}" > ${statePath}/db
 
3852 +      type = types.port;
 
3855 +        Port on which the "${srv}" backend should listen.
 
3859 -    # Update copy of each users' profile to the latest
 
3860 -    # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
 
3861 -    if ! test -e ${statePath}/webhook; then
 
3862 -      # Update ${iniKey}'s users' profile copy to the latest
 
3863 -      ${cfg.python}/bin/srht-update-profiles ${iniKey}
 
3867 +        default = "unix:/run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
 
3868 +        example = "redis://shared.wireguard:6379/0";
 
3870 +          The redis host URL. This is used for caching and temporary storage, and must
 
3871 +          be shared between nodes (e.g. git1.sr.ht and git2.sr.ht), but need not be
 
3872 +          shared between services. It may be shared between services, however, with no
 
3873 +          ill effect, if this better suits your infrastructure.
 
3878 -      touch ${statePath}/webhook
 
3881 +      database = mkOption {
 
3883 +        default = "${srv}.sr.ht";
 
3885 +          PostgreSQL database name for the ${srv}.sr.ht service,
 
3886 +          used if <xref linkend="opt-services.sourcehut.postgresql.enable"/> is <literal>true</literal>.
 
3891 -    ${optionalString (builtins.hasAttr "migrate-on-upgrade" cfgIni && cfgIni.migrate-on-upgrade == "yes") ''
 
3892 -      if [ "$(cat ${statePath}/db)" != "${serviceDrv.version}" ]; then
 
3893 -        # Manage schema migrations using alembic
 
3894 -        ${cfg.python}/bin/${serviceDrv.pname}-migrate -a upgrade head
 
3896 +      extraArgs = mkOption {
 
3897 +        type = with types; listOf str;
 
3898 +        default = ["--timeout 120" "--workers 1" "--log-level=info"];
 
3899 +        description = "Extra arguments passed to Gunicorn.";
 
3902 +  } // optionalAttrs webhooks {
 
3904 +      extraArgs = mkOption {
 
3905 +        type = with types; listOf str;
 
3906 +        default = ["--loglevel DEBUG" "--pool eventlet" "--without-heartbeat"];
 
3907 +        description = "Extra arguments passed to the Celery responsible for webhooks.";
 
3909 +      celeryConfig = mkOption {
 
3910 +        type = types.lines;
 
3912 +        description = "Content of the <literal>celeryconfig.py</literal> used by the Celery responsible for webhooks.";
 
3917 -        # Mark down current package version
 
3918 -        printf "%s" "${serviceDrv.version}" > ${statePath}/db
 
3921 +  config = lib.mkIf (cfg.enable && srvCfg.enable) (mkMerge [ extraConfig {
 
3924 +        "${srvCfg.user}" = {
 
3925 +          isSystemUser = true;
 
3926 +          group = mkDefault srvCfg.group;
 
3927 +          description = mkDefault "sourcehut user for ${srv}.sr.ht";
 
3931 +        "${srvCfg.group}" = { };
 
3932 +      } // optionalAttrs (cfg.postgresql.enable
 
3933 +        && hasSuffix "0" (postgresql.settings.unix_socket_permissions or "")) {
 
3934 +        "postgres".members = [ srvCfg.user ];
 
3935 +      } // optionalAttrs (cfg.redis.enable
 
3936 +        && hasSuffix "0" (redis.settings.unixsocketperm or "")) {
 
3937 +        "redis-sourcehut-${srvsrht}".members = [ srvCfg.user ];
 
3941 -    ${attrs.preStart or ""}
 
3943 +    services.nginx = mkIf cfg.nginx.enable {
 
3944 +      virtualHosts."${srv}.${cfg.settings."sr.ht".global-domain}" = mkMerge [ {
 
3946 +        locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
 
3947 +        locations."/static" = {
 
3948 +          root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
 
3949 +          extraConfig = mkDefault ''
 
3953 +      } cfg.nginx.virtualHost ];
 
3956 +    services.postgresql = mkIf cfg.postgresql.enable {
 
3957 +      authentication = ''
 
3958 +        local ${srvCfg.postgresql.database} ${srvCfg.user} trust
 
3960 +      ensureDatabases = [ srvCfg.postgresql.database ];
 
3961 +      ensureUsers = map (name: {
 
3963 +          ensurePermissions = { "DATABASE \"${srvCfg.postgresql.database}\"" = "ALL PRIVILEGES"; };
 
3967 +    services.sourcehut.services = mkDefault (filter (s: cfg.${s}.enable)
 
3968 +      [ "builds" "dispatch" "git" "hg" "hub" "lists" "man" "meta" "pages" "paste" "todo" ]);
 
3970 +    services.sourcehut.settings = mkMerge [
 
3972 +        "${srv}.sr.ht".origin = mkDefault "https://${srv}.${cfg.settings."sr.ht".global-domain}";
 
3975 +      (mkIf cfg.postgresql.enable {
 
3976 +        "${srv}.sr.ht".connection-string = mkDefault "postgresql:///${srvCfg.postgresql.database}?user=${srvCfg.user}&host=/run/postgresql";
 
3980 +    services.redis.servers."sourcehut-${srvsrht}" = mkIf cfg.redis.enable {
 
3984 +      # TODO: set a more informed value
 
3985 +      save = mkDefault [ [1800 10] [300 100] ];
 
3987 +        # TODO: set a more informed value
 
3988 +        maxmemory = "128MB";
 
3989 +        maxmemory-policy = "volatile-ttl";
 
3993 +    systemd.services = mkMerge [
 
3995 +        "${srvsrht}" = baseService srvsrht { allowStripe = srv == "meta"; } (mkMerge [
 
3997 +          description = "sourcehut ${srv}.sr.ht website service";
 
3998 +          before = optional cfg.nginx.enable "nginx.service";
 
3999 +          wants = optional cfg.nginx.enable "nginx.service";
 
4000 +          wantedBy = [ "multi-user.target" ];
 
4001 +          path = optional cfg.postgresql.enable postgresql.package;
 
4002 +          # Beware: change in credentials' content will not trigger restart.
 
4003 +          restartTriggers = [ configIni ];
 
4006 +            Restart = mkDefault "always";
 
4007 +            #RestartSec = mkDefault "2min";
 
4008 +            StateDirectory = [ "sourcehut/${srvsrht}" ];
 
4009 +            StateDirectoryMode = "2750";
 
4010 +            ExecStart = "${cfg.python}/bin/gunicorn ${srvsrht}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} " + concatStringsSep " " srvCfg.gunicorn.extraArgs;
 
4013 +            version = pkgs.sourcehut.${srvsrht}.version;
 
4014 +            stateDir = "/var/lib/sourcehut/${srvsrht}";
 
4017 +            # Use the /run/sourcehut/${srvsrht}/config.ini
 
4018 +            # installed by a previous ExecStartPre= in baseService
 
4019 +            cd /run/sourcehut/${srvsrht}
 
4021 +            if test ! -e ${stateDir}/db; then
 
4022 +              # Setup the initial database.
 
4023 +              # Note that it stamps the alembic head afterward
 
4024 +              ${cfg.python}/bin/${srvsrht}-initdb
 
4025 +              echo ${version} >${stateDir}/db
 
4028 +            ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
 
4029 +              if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
 
4030 +                # Manage schema migrations using alembic
 
4031 +                ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
 
4032 +                echo ${version} >${stateDir}/db
 
4036 +            # Update copy of each users' profile to the latest
 
4037 +            # See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
 
4038 +            if test ! -e ${stateDir}/webhook; then
 
4039 +              # Update ${iniKey}'s users' profile copy to the latest
 
4040 +              ${cfg.python}/bin/srht-update-profiles ${iniKey}
 
4041 +              touch ${stateDir}/webhook
 
4048 +        "${srvsrht}-webhooks" = baseService "${srvsrht}-webhooks" {}
 
4050 +            description = "sourcehut ${srv}.sr.ht webhooks service";
 
4051 +            after = [ "${srvsrht}.service" ];
 
4052 +            wantedBy = [ "${srvsrht}.service" ];
 
4053 +            partOf = [ "${srvsrht}.service" ];
 
4055 +              cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" srvCfg.webhooks.celeryConfig} \
 
4056 +                 /run/sourcehut/${srvsrht}-webhooks/celeryconfig.py
 
4060 +              Restart = "always";
 
4061 +              ExecStart = "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h " + concatStringsSep " " srvCfg.webhooks.extraArgs;
 
4062 +              # Avoid crashing: os.getloadavg()
 
4063 +              ProcSubset = mkForce "all";
 
4068 +      (mapAttrs (timerName: timer: (baseService timerName {} (mkMerge [
 
4070 +          description = "sourcehut ${timerName} service";
 
4071 +          after = [ "network.target" "${srvsrht}.service" ];
 
4074 +            ExecStart = "${cfg.python}/bin/${timerName}";
 
4077 +        (timer.service or {})
 
4080 +      (mapAttrs (serviceName: extraService: baseService serviceName {} (mkMerge [
 
4082 +          description = "sourcehut ${serviceName} service";
 
4083 +          # So that extraServices have the PostgreSQL database initialized.
 
4084 +          after = [ "${srvsrht}.service" ];
 
4085 +          wantedBy = [ "${srvsrht}.service" ];
 
4086 +          partOf = [ "${srvsrht}.service" ];
 
4089 +            Restart = mkDefault "always";
 
4093 +      ])) extraServices)
 
4096 +    systemd.timers = mapAttrs (timerName: timer:
 
4098 +        description = "sourcehut timer for ${timerName}";
 
4099 +        wantedBy = [ "timers.target" ];
 
4100 +        inherit (timer) timerConfig;
 
4104 -  (builtins.removeAttrs attrs [ "path" "preStart" ])
 
4105 diff --git a/nixos/modules/services/misc/sourcehut/sourcehut.xml b/nixos/modules/services/misc/sourcehut/sourcehut.xml
 
4106 index ab9a8c6cb4b..41094f65a94 100644
 
4107 --- a/nixos/modules/services/misc/sourcehut/sourcehut.xml
 
4108 +++ b/nixos/modules/services/misc/sourcehut/sourcehut.xml
 
4110    <title>Basic usage</title>
 
4112     Sourcehut is a Python and Go based set of applications.
 
4113 -   <literal><link linkend="opt-services.sourcehut.enable">services.sourcehut</link></literal>
 
4114 -   by default will use
 
4115 +   This NixOS module also provides basic configuration integrating Sourcehut into locally running
 
4116     <literal><link linkend="opt-services.nginx.enable">services.nginx</link></literal>,
 
4117 -   <literal><link linkend="opt-services.nginx.enable">services.redis</link></literal>,
 
4118 -   <literal><link linkend="opt-services.nginx.enable">services.cron</link></literal>,
 
4119 +   <literal><link linkend="opt-services.redis.servers">services.redis.servers.sourcehut</link></literal>,
 
4120 +   <literal><link linkend="opt-services.postfix.enable">services.postfix</link></literal>
 
4122 -   <literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal>.
 
4123 +   <literal><link linkend="opt-services.postgresql.enable">services.postgresql</link></literal> services.
 
4127 @@ -42,18 +41,23 @@ in {
 
4129    services.sourcehut = {
 
4130      <link linkend="opt-services.sourcehut.enable">enable</link> = true;
 
4131 -    <link linkend="opt-services.sourcehut.originBase">originBase</link> = fqdn;
 
4132 -    <link linkend="opt-services.sourcehut.services">services</link> = [ "meta" "man" "git" ];
 
4133 +    <link linkend="opt-services.sourcehut.git.enable">git.enable</link> = true;
 
4134 +    <link linkend="opt-services.sourcehut.man.enable">man.enable</link> = true;
 
4135 +    <link linkend="opt-services.sourcehut.meta.enable">meta.enable</link> = true;
 
4136 +    <link linkend="opt-services.sourcehut.nginx.enable">nginx.enable</link> = true;
 
4137 +    <link linkend="opt-services.sourcehut.postfix.enable">postfix.enable</link> = true;
 
4138 +    <link linkend="opt-services.sourcehut.postgresql.enable">postgresql.enable</link> = true;
 
4139 +    <link linkend="opt-services.sourcehut.redis.enable">redis.enable</link> = true;
 
4140      <link linkend="opt-services.sourcehut.settings">settings</link> = {
 
4142            environment = "production";
 
4143            global-domain = fqdn;
 
4144            origin = "https://${fqdn}";
 
4145            # Produce keys with srht-keygen from <package>sourcehut.coresrht</package>.
 
4146 -          network-key = "SECRET";
 
4147 -          service-key = "SECRET";
 
4148 +          network-key = "/run/keys/path/to/network-key";
 
4149 +          service-key = "/run/keys/path/to/service-key";
 
4151 -        webhooks.private-key= "SECRET";
 
4152 +        webhooks.private-key= "/run/keys/path/to/webhook-key";
 
4156 diff --git a/nixos/modules/services/misc/sourcehut/todo.nix b/nixos/modules/services/misc/sourcehut/todo.nix
 
4157 deleted file mode 100644
 
4158 index aec773b0669..00000000000
 
4159 --- a/nixos/modules/services/misc/sourcehut/todo.nix
 
4162 -{ config, lib, pkgs, ... }:
 
4166 -  cfg = config.services.sourcehut;
 
4167 -  cfgIni = cfg.settings;
 
4169 -  iniKey = "todo.sr.ht";
 
4171 -  rcfg = config.services.redis;
 
4172 -  drv = pkgs.sourcehut.todosrht;
 
4175 -  options.services.sourcehut.todo = {
 
4178 -      default = "todosrht";
 
4180 -        User for todo.sr.ht.
 
4185 -      type = types.port;
 
4188 -        Port on which the "todo" module should listen.
 
4192 -    database = mkOption {
 
4194 -      default = "todo.sr.ht";
 
4196 -        PostgreSQL database name for todo.sr.ht.
 
4200 -    statePath = mkOption {
 
4201 -      type = types.path;
 
4202 -      default = "${cfg.statePath}/todosrht";
 
4204 -        State path for todo.sr.ht.
 
4209 -  config = with scfg; lib.mkIf (cfg.enable && elem "todo" cfg.services) {
 
4213 -          isSystemUser = true;
 
4215 -          extraGroups = [ "postfix" ];
 
4216 -          description = "todo.sr.ht user";
 
4224 -    services.postgresql = {
 
4225 -      authentication = ''
 
4226 -        local ${database} ${user} trust
 
4228 -      ensureDatabases = [ database ];
 
4232 -          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
 
4238 -      tmpfiles.rules = [
 
4239 -        "d ${statePath} 0750 ${user} ${user} -"
 
4243 -        todosrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
 
4244 -          after = [ "postgresql.service" "network.target" ];
 
4245 -          requires = [ "postgresql.service" ];
 
4246 -          wantedBy = [ "multi-user.target" ];
 
4248 -          description = "todo.sr.ht website service";
 
4250 -          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
 
4254 -         after = [ "postgresql.service" "network.target" ];
 
4255 -         bindsTo = [ "postgresql.service" ];
 
4256 -         wantedBy = [ "multi-user.target" ];
 
4258 -         description = "todo.sr.ht process service";
 
4262 -           Restart = "always";
 
4263 -           ExecStart = "${cfg.python}/bin/todosrht-lmtp";
 
4267 -        todosrht-webhooks = {
 
4268 -          after = [ "postgresql.service" "network.target" ];
 
4269 -          requires = [ "postgresql.service" ];
 
4270 -          wantedBy = [ "multi-user.target" ];
 
4272 -          description = "todo.sr.ht webhooks service";
 
4276 -            Restart = "always";
 
4277 -            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
 
4284 -    services.sourcehut.settings = {
 
4285 -      # URL todo.sr.ht is being served at (protocol://domain)
 
4286 -      "todo.sr.ht".origin = mkDefault "http://todo.${cfg.originBase}";
 
4287 -      # Address and port to bind the debug server to
 
4288 -      "todo.sr.ht".debug-host = mkDefault "0.0.0.0";
 
4289 -      "todo.sr.ht".debug-port = mkDefault port;
 
4290 -      # Configures the SQLAlchemy connection string for the database.
 
4291 -      "todo.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
 
4292 -      # Set to "yes" to automatically run migrations on package upgrade.
 
4293 -      "todo.sr.ht".migrate-on-upgrade = mkDefault "yes";
 
4294 -      # todo.sr.ht's OAuth client ID and secret for meta.sr.ht
 
4295 -      # Register your client at meta.example.org/oauth
 
4296 -      "todo.sr.ht".oauth-client-id = mkDefault null;
 
4297 -      "todo.sr.ht".oauth-client-secret = mkDefault null;
 
4298 -      # Outgoing email for notifications generated by users
 
4299 -      "todo.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
 
4300 -      # The redis connection used for the webhooks worker
 
4301 -      "todo.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
 
4303 -      "todo.sr.ht".network-key = mkDefault null;
 
4305 -      # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
 
4306 -      # Alternatively, specify IP:PORT and an SMTP server will be run instead.
 
4307 -      "todo.sr.ht::mail".sock = mkDefault "/tmp/todo.sr.ht-lmtp.sock";
 
4308 -      # The lmtp daemon will make the unix socket group-read/write for users in this
 
4310 -      "todo.sr.ht::mail".sock-group = mkDefault "postfix";
 
4312 -      "todo.sr.ht::mail".posting-domain = mkDefault "todo.${cfg.originBase}";
 
4315 -    services.nginx.virtualHosts."todo.${cfg.originBase}" = {
 
4317 -      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
 
4318 -      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
 
4319 -      locations."/static".root = "${pkgs.sourcehut.todosrht}/${pkgs.sourcehut.python.sitePackages}/todosrht";
 
4323 diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
 
4324 index a6eb2c03258..eb0238c0ca1 100644
 
4325 --- a/nixos/tests/all-tests.nix
 
4326 +++ b/nixos/tests/all-tests.nix
 
4327 @@ -412,6 +412,7 @@ in
 
4328    solanum = handleTest ./solanum.nix {};
 
4329    solr = handleTest ./solr.nix {};
 
4330    sonarr = handleTest ./sonarr.nix {};
 
4331 +  sourcehut = handleTest ./sourcehut.nix {};
 
4332    spacecookie = handleTest ./spacecookie.nix {};
 
4333    spike = handleTest ./spike.nix {};
 
4334    sslh = handleTest ./sslh.nix {};
 
4335 diff --git a/nixos/tests/sourcehut.nix b/nixos/tests/sourcehut.nix
 
4336 index b56a14ebf85..6492250bd57 100644
 
4337 --- a/nixos/tests/sourcehut.nix
 
4338 +++ b/nixos/tests/sourcehut.nix
 
4339 @@ -12,10 +12,20 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
4340      services.sourcehut = {
 
4342        services = [ "meta" ];
 
4343 -      originBase = "sourcehut";
 
4344 -      settings."sr.ht".service-key =   "8888888888888888888888888888888888888888888888888888888888888888";
 
4345 -      settings."sr.ht".network-key = "0000000000000000000000000000000000000000000=";
 
4346 -      settings.webhooks.private-key = "0000000000000000000000000000000000000000000=";
 
4347 +      redis.enable = true;
 
4348 +      postgresql.enable = true;
 
4349 +      meta.enable = true;
 
4350 +      settings."sr.ht" = {
 
4351 +        global-domain = "sourcehut";
 
4352 +        service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
 
4353 +        network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
 
4355 +      settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
 
4357 +    services.postgresql = {
 
4359 +      enableTCPIP = false;
 
4360 +      settings.unix_socket_permissions = "0770";
 
4364 diff --git a/pkgs/applications/version-management/sourcehut/builds.nix b/pkgs/applications/version-management/sourcehut/builds.nix
 
4365 index c8163caf8ea..4192d7f54b6 100644
 
4366 --- a/pkgs/applications/version-management/sourcehut/builds.nix
 
4367 +++ b/pkgs/applications/version-management/sourcehut/builds.nix
 
4372 -  version = "0.66.7";
 
4374 -  buildWorker = src: buildGoModule {
 
4375 -    inherit src version;
 
4376 -    pname = "builds-sr-ht-worker";
 
4378 -    vendorSha256 = "sha256-giOaldV46aBqXyFH/cQVsbUr6Rb4VMhbBO86o48tRZY=";
 
4381 -buildPythonPackage rec {
 
4383 -  pname = "buildsrht";
 
4384 +  version = "0.71.6";
 
4386    src = fetchFromSourcehut {
 
4387      owner = "~sircmpwn";
 
4388      repo = "builds.sr.ht";
 
4390 -    sha256 = "sha256-2MLs/DOXHjEYarXDVUcPZe3o0fmZbzVxn528SE72lhM=";
 
4391 +    sha256 = "sha256-c2xp2uIP8+WeRMz0efA1H58Nkot65bc03e7rrrZk3jo=";
 
4394 +  worker = buildGoModule {
 
4395 +    inherit src version;
 
4396 +    sourceRoot = "source/worker";
 
4397 +    pname = "buildsrht-worker";
 
4399 +    vendorSha256 = "sha256-iMlCZPCIwhppPAYUi6E8td1BoNaqBuQCI5E83cnEBJ0=";
 
4401 +    # What follows is only to update go-redis,
 
4402 +    # and thus also using a patched srht-keys.
 
4403 +    # go.{mod,sum} could be patched directly but that would be less resilient
 
4404 +    # to changes from upstream, and thus harder to maintain the patching
 
4405 +    # while it hasn't been merged upstream.
 
4407 +    overrideModAttrs = old: {
 
4409 +        go get github.com/go-redis/redis/v8
 
4410 +        go get github.com/go-redis/redis@none
 
4413 +      # Pass updated go.{mod,sum} from go-modules to worker's vendor/go.{mod,sum}
 
4415 +        cp --reflink=auto go.* $out/
 
4420 +      # Update go-redis to support Unix sockets
 
4421 +      patches/redis-socket/build/v3-0001-worker-update-go-redis-to-support-Unix-sockets.patch
 
4423 +    patchFlags = ["-p2"];
 
4424 +    postConfigure = ''
 
4425 +      cp -v vendor/go.{mod,sum} .
 
4429 +buildPythonPackage rec {
 
4430 +  inherit src version;
 
4431 +  pname = "buildsrht";
 
4433    nativeBuildInputs = srht.nativeBuildInputs;
 
4435    propagatedBuildInputs = [
 
4436 @@ -53,13 +81,17 @@ buildPythonPackage rec {
 
4438      cp -r images $out/lib
 
4439      cp contrib/submit_image_build $out/bin/builds.sr.ht
 
4440 -    cp ${buildWorker "${src}/worker"}/bin/worker $out/bin/builds.sr.ht-worker
 
4441 +    cp ${worker}/bin/worker $out/bin/builds.sr.ht-worker
 
4444 +  pythonImportsCheck = [ "buildsrht" ];
 
4446 +  passthru = { inherit worker; };
 
4449      homepage = "https://git.sr.ht/~sircmpwn/builds.sr.ht";
 
4450      description = "Continuous integration service for the sr.ht network";
 
4451 -    license = licenses.agpl3;
 
4452 +    license = licenses.agpl3Only;
 
4453      maintainers = with maintainers; [ eadwu ];
 
4456 diff --git a/pkgs/applications/version-management/sourcehut/core.nix b/pkgs/applications/version-management/sourcehut/core.nix
 
4457 index 7c3a516ed9d..2b302d1441c 100644
 
4458 --- a/pkgs/applications/version-management/sourcehut/core.nix
 
4459 +++ b/pkgs/applications/version-management/sourcehut/core.nix
 
4467  buildPythonPackage rec {
 
4469 -  version = "0.67.4";
 
4470 +  version = "0.67.25";
 
4473      url = "https://git.sr.ht/~sircmpwn/core.sr.ht";
 
4475 -    sha256 = "sha256-XvzFfcBK5Mq8p7xEBAF/eupUE1kkUBh5k+ByM/WA9bc=";
 
4476 +    sha256 = "sha256-JZXIpNEY1/KUaYh0Vk5j/zVO9JQc2F1RmnZ/5TFN0PI=";
 
4477      fetchSubmodules = true;
 
4480 @@ -46,7 +45,10 @@ buildPythonPackage rec {
 
4484 -    ./disable-npm-install.patch
 
4485 +    # Disable check for npm
 
4486 +    patches/disable-npm-install.patch
 
4487 +    # Add Unix socket support for redis-host=
 
4488 +    patches/redis-socket/core/v3-0001-add-Unix-socket-support-for-redis-host.patch
 
4491    nativeBuildInputs = [
 
4492 @@ -87,6 +89,7 @@ buildPythonPackage rec {
 
4495    dontUseSetuptoolsCheck = true;
 
4496 +  pythonImportsCheck = [ "srht" ];
 
4499      homepage = "https://git.sr.ht/~sircmpwn/srht";
 
4500 diff --git a/pkgs/applications/version-management/sourcehut/default.nix b/pkgs/applications/version-management/sourcehut/default.nix
 
4501 index 401a1437b7d..7dde841b543 100644
 
4502 --- a/pkgs/applications/version-management/sourcehut/default.nix
 
4503 +++ b/pkgs/applications/version-management/sourcehut/default.nix
 
4504 @@ -22,10 +22,12 @@ let
 
4505        listssrht = self.callPackage ./lists.nix { };
 
4506        mansrht = self.callPackage ./man.nix { };
 
4507        metasrht = self.callPackage ./meta.nix { };
 
4508 +      pagessrht = self.callPackage ./pages.nix { };
 
4509        pastesrht = self.callPackage ./paste.nix { };
 
4510        todosrht = self.callPackage ./todo.nix { };
 
4512        scmsrht = self.callPackage ./scm.nix { };
 
4513 +      srht-keys = self.scmsrht.srht-keys;
 
4517 @@ -40,6 +42,8 @@ with python.pkgs; recurseIntoAttrs {
 
4518    listssrht = toPythonApplication listssrht;
 
4519    mansrht = toPythonApplication mansrht;
 
4520    metasrht = toPythonApplication metasrht;
 
4521 +  pagessrht = pagessrht;
 
4522    pastesrht = toPythonApplication pastesrht;
 
4523    todosrht = toPythonApplication todosrht;
 
4524 +  srht-keys = scmsrht.srht-keys;
 
4526 diff --git a/pkgs/applications/version-management/sourcehut/dispatch.nix b/pkgs/applications/version-management/sourcehut/dispatch.nix
 
4527 index 637c6f9c1df..9456d0c998c 100644
 
4528 --- a/pkgs/applications/version-management/sourcehut/dispatch.nix
 
4529 +++ b/pkgs/applications/version-management/sourcehut/dispatch.nix
 
4532  buildPythonPackage rec {
 
4533    pname = "dispatchsrht";
 
4534 -  version = "0.15.8";
 
4535 +  version = "0.15.32";
 
4537    src = fetchFromSourcehut {
 
4538      owner = "~sircmpwn";
 
4539      repo = "dispatch.sr.ht";
 
4541 -    sha256 = "sha256-zWCGPjIgMKHXHJUs9aciV7IFgo0rpahon6KXHDwcfss=";
 
4542 +    sha256 = "sha256-4P4cXhjcZ8IBzpRfmYIJkzl9U4Plo36a48Pf/KjmhFY=";
 
4545    nativeBuildInputs = srht.nativeBuildInputs;
 
4546 @@ -31,10 +31,12 @@ buildPythonPackage rec {
 
4547      export SRHT_PATH=${srht}/${python.sitePackages}/srht
 
4550 +  pythonImportsCheck = [ "dispatchsrht" ];
 
4553      homepage = "https://dispatch.sr.ht/~sircmpwn/dispatch.sr.ht";
 
4554      description = "Task dispatcher and service integration tool for the sr.ht network";
 
4555 -    license = licenses.agpl3;
 
4556 +    license = licenses.agpl3Only;
 
4557      maintainers = with maintainers; [ eadwu ];
 
4560 diff --git a/pkgs/applications/version-management/sourcehut/git.nix b/pkgs/applications/version-management/sourcehut/git.nix
 
4561 index e44fb9cd6c6..ce1a6f461a7 100644
 
4562 --- a/pkgs/applications/version-management/sourcehut/git.nix
 
4563 +++ b/pkgs/applications/version-management/sourcehut/git.nix
 
4571 -  version = "0.72.8";
 
4572 +  version = "0.72.47";
 
4574    src = fetchFromSourcehut {
 
4575      owner = "~sircmpwn";
 
4578 -    sha256 = "sha256-AB2uzajO5PtcpJfbOOTfuDFM6is5K39v3AZJ1hShRNc=";
 
4579 +    sha256 = "sha256-jk2DFC/fDYN88nofntJrBtYfCWr39YaNv2azH/tdZtQ=";
 
4582 -  buildShell = src: buildGoModule {
 
4583 +  gitsrht-shell = buildGoModule {
 
4584      inherit src version;
 
4585 +    sourceRoot = "source/gitsrht-shell";
 
4586      pname = "gitsrht-shell";
 
4587      vendorSha256 = "sha256-aqUFICp0C2reqb2p6JCPAUIRsxzSv0t9BHoNWrTYfqk=";
 
4590 -  buildDispatcher = src: buildGoModule {
 
4591 +  gitsrht-dispatch = buildGoModule {
 
4592      inherit src version;
 
4593 -    pname = "gitsrht-dispatcher";
 
4594 +    sourceRoot = "source/gitsrht-dispatch";
 
4595 +    pname = "gitsrht-dispatch";
 
4596      vendorSha256 = "sha256-qWXPHo86s6iuRBhRMtmD5jxnAWKdrWHtA/iSUkdw89M=";
 
4598 +      # Add support for supplementary groups
 
4599 +      patches/redis-socket/git/v3-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
 
4601 +    patchFlags = ["-p2"];
 
4604 -  buildKeys = src: buildGoModule {
 
4605 +  gitsrht-keys = buildGoModule {
 
4606      inherit src version;
 
4607 +    sourceRoot = "source/gitsrht-keys";
 
4608      pname = "gitsrht-keys";
 
4609 -    vendorSha256 = "1d94cqy7x0q0agwg515xxsbl70b3qrzxbzsyjhn1pbyj532brn7f";
 
4610 +    vendorSha256 = "sha256-m6uIrYDWqGagi+jjfYo4C59SjLqaaXwDq9vO0b9EW6M=";
 
4612 +    # What follows is only to update go-redis,
 
4613 +    # and thus also using a patched srht-keys.
 
4614 +    # go.{mod,sum} could be patched directly but that would be less resilient
 
4615 +    # to changes from upstream, and thus harder to maintain the patching
 
4616 +    # while it hasn't been merged upstream.
 
4618 +    overrideModAttrs = old: {
 
4620 +        # This is a fixed-output derivation so it is not allowed to reference other derivations,
 
4621 +        # but here srht-keys will be copied to vendor/ by go mod vendor
 
4622 +        ln -s ${srht-keys} srht-keys
 
4623 +        go mod edit -replace git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys=$PWD/srht-keys
 
4624 +        go get github.com/go-redis/redis/v8
 
4625 +        go get github.com/go-redis/redis@none
 
4628 +      # Pass updated go.{mod,sum} from go-modules to gitsrht-keys' vendor/go.{mod,sum}
 
4630 +        cp --reflink=auto go.* $out/
 
4635 +      # Update go-redis to support Unix sockets
 
4636 +      patches/redis-socket/git/v3-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
 
4638 +    patchFlags = ["-p2"];
 
4639 +    postConfigure = ''
 
4640 +      cp -v vendor/go.{mod,sum} .
 
4644 -  buildUpdateHook = src: buildGoModule {
 
4645 +  gitsrht-update-hook = buildGoModule {
 
4646      inherit src version;
 
4647 +    sourceRoot = "source/gitsrht-update-hook";
 
4648      pname = "gitsrht-update-hook";
 
4649 -    vendorSha256 = "0fwzqpjv8x5y3w3bfjd0x0cvqjjak23m0zj88hf32jpw49xmjkih";
 
4651 +    vendorSha256 = "sha256-UoHxGVYEgTDqFzVQ2Dv6BRT4jVt+/QpNqEH3G2UWFjs=";
 
4653 -  updateHook = buildUpdateHook "${src}/gitsrht-update-hook";
 
4654 +    # What follows is only to update go-redis
 
4655 +    # and thus also using a patched srht-keys.
 
4657 +    overrideModAttrs = old: {
 
4659 +        # This is a fixed-output derivation so it is not allowed to reference other derivations,
 
4660 +        # but here srht-keys will be copied to vendor/ by go mod vendor
 
4661 +        ln -s ${srht-keys} srht-keys
 
4662 +        go mod edit -replace git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys=$PWD/srht-keys
 
4663 +        go get github.com/go-redis/redis/v8
 
4664 +        go get github.com/go-redis/redis@none
 
4667 +      # Pass updated go.{mod,sum} from go-modules to gitsrht-keys' vendor/go.{mod,sum}
 
4669 +        cp --reflink=auto go.* $out/
 
4674 +      # Update go-redis to support Unix sockets
 
4675 +      patches/redis-socket/git/v3-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
 
4677 +    patchFlags = ["-p2"];
 
4678 +    postConfigure = ''
 
4679 +      cp -v vendor/go.{mod,sum} .
 
4684  buildPythonPackage rec {
 
4685 @@ -63,19 +130,21 @@ buildPythonPackage rec {
 
4689 -    cp ${buildShell "${src}/gitsrht-shell"}/bin/gitsrht-shell $out/bin/gitsrht-shell
 
4690 -    cp ${buildDispatcher "${src}/gitsrht-dispatch"}/bin/gitsrht-dispatch $out/bin/gitsrht-dispatch
 
4691 -    cp ${buildKeys "${src}/gitsrht-keys"}/bin/gitsrht-keys $out/bin/gitsrht-keys
 
4692 -    cp ${updateHook}/bin/gitsrht-update-hook $out/bin/gitsrht-update-hook
 
4693 +    cp ${gitsrht-shell}/bin/gitsrht-shell $out/bin/gitsrht-shell
 
4694 +    cp ${gitsrht-dispatch}/bin/gitsrht-dispatch $out/bin/gitsrht-dispatch
 
4695 +    cp ${gitsrht-keys}/bin/gitsrht-keys $out/bin/gitsrht-keys
 
4696 +    cp ${gitsrht-update-hook}/bin/gitsrht-update-hook $out/bin/gitsrht-update-hook
 
4699 -    inherit updateHook;
 
4700 +    inherit gitsrht-shell gitsrht-dispatch gitsrht-keys gitsrht-update-hook;
 
4703 +  pythonImportsCheck = [ "gitsrht" ];
 
4706      homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
 
4707      description = "Git repository hosting service for the sr.ht network";
 
4708 -    license = licenses.agpl3;
 
4709 +    license = licenses.agpl3Only;
 
4710      maintainers = with maintainers; [ eadwu ];
 
4713 diff --git a/pkgs/applications/version-management/sourcehut/hg.nix b/pkgs/applications/version-management/sourcehut/hg.nix
 
4714 index cddb76cabf2..1d6062d81cc 100644
 
4715 --- a/pkgs/applications/version-management/sourcehut/hg.nix
 
4716 +++ b/pkgs/applications/version-management/sourcehut/hg.nix
 
4719  buildPythonPackage rec {
 
4721 -  version = "0.27.4";
 
4722 +  version = "0.27.6";
 
4725      url = "https://hg.sr.ht/~sircmpwn/hg.sr.ht";
 
4727 -    sha256 = "1c0qfi0gmbfngvds6917fy9ii2iglawn429757rh7b4bvzn7n6mr";
 
4728 +    sha256 = "ibijvKjS4CiWTYrO6Qdh3RkD0EUE7BY8wjdPwrD6vkA=";
 
4731    nativeBuildInputs = srht.nativeBuildInputs;
 
4732 @@ -32,10 +32,12 @@ buildPythonPackage rec {
 
4733      export SRHT_PATH=${srht}/${python.sitePackages}/srht
 
4736 +  pythonImportsCheck = [ "hgsrht" ];
 
4739      homepage = "https://git.sr.ht/~sircmpwn/hg.sr.ht";
 
4740      description = "Mercurial repository hosting service for the sr.ht network";
 
4741 -    license = licenses.agpl3;
 
4742 +    license = licenses.agpl3Only;
 
4743      maintainers = with maintainers; [ eadwu ];
 
4746 diff --git a/pkgs/applications/version-management/sourcehut/hub.nix b/pkgs/applications/version-management/sourcehut/hub.nix
 
4747 index 17cb3fe4b61..31975b305c5 100644
 
4748 --- a/pkgs/applications/version-management/sourcehut/hub.nix
 
4749 +++ b/pkgs/applications/version-management/sourcehut/hub.nix
 
4752  buildPythonPackage rec {
 
4754 -  version = "0.13.1";
 
4755 +  version = "0.13.11";
 
4757    src = fetchFromSourcehut {
 
4758      owner = "~sircmpwn";
 
4761 -    sha256 = "sha256-Kqzy4mh5Nn1emzHBco/LVuXro/tW3NX+OYqdEwBSQ/U=";
 
4762 +    sha256 = "sha256-AIpP7gfXoBvl6s8+dA3XrjuUHsPTtKFsZqwqbjBKYUk=";
 
4765    nativeBuildInputs = srht.nativeBuildInputs;
 
4766 @@ -26,11 +26,12 @@ buildPythonPackage rec {
 
4769    dontUseSetuptoolsCheck = true;
 
4770 +  pythonImportsCheck = [ "hubsrht" ];
 
4773      homepage = "https://git.sr.ht/~sircmpwn/hub.sr.ht";
 
4774      description = "Project hub service for the sr.ht network";
 
4775 -    license = licenses.agpl3;
 
4776 +    license = licenses.agpl3Only;
 
4777      maintainers = with maintainers; [ eadwu ];
 
4780 diff --git a/pkgs/applications/version-management/sourcehut/lists.nix b/pkgs/applications/version-management/sourcehut/lists.nix
 
4781 index b419b49f7b5..51b1a3627db 100644
 
4782 --- a/pkgs/applications/version-management/sourcehut/lists.nix
 
4783 +++ b/pkgs/applications/version-management/sourcehut/lists.nix
 
4786  buildPythonPackage rec {
 
4787    pname = "listssrht";
 
4788 -  version = "0.48.19";
 
4789 +  version = "0.50.1";
 
4791    src = fetchFromSourcehut {
 
4792      owner = "~sircmpwn";
 
4793      repo = "lists.sr.ht";
 
4795 -    sha256 = "sha256-bsakEMyvWaxiE4/SGcAP4mlGG9jkdHfFxpt9H+TJn/8=";
 
4796 +    sha256 = "sha256-FrC3Au/ZdDtJuczGgaAhAEZu0hVa74LOM8z3aOiqGZc=";
 
4799    nativeBuildInputs = srht.nativeBuildInputs;
 
4800 @@ -37,10 +37,12 @@ buildPythonPackage rec {
 
4801      export SRHT_PATH=${srht}/${python.sitePackages}/srht
 
4804 +  pythonImportsCheck = [ "listssrht" ];
 
4807      homepage = "https://git.sr.ht/~sircmpwn/lists.sr.ht";
 
4808      description = "Mailing list service for the sr.ht network";
 
4809 -    license = licenses.agpl3;
 
4810 +    license = licenses.agpl3Only;
 
4811      maintainers = with maintainers; [ eadwu ];
 
4814 diff --git a/pkgs/applications/version-management/sourcehut/man.nix b/pkgs/applications/version-management/sourcehut/man.nix
 
4815 index bd331f000a7..47c6bb0ac4f 100644
 
4816 --- a/pkgs/applications/version-management/sourcehut/man.nix
 
4817 +++ b/pkgs/applications/version-management/sourcehut/man.nix
 
4820  buildPythonPackage rec {
 
4822 -  version = "0.15.12";
 
4823 +  version = "0.15.20";
 
4825    src = fetchFromSourcehut {
 
4826      owner = "~sircmpwn";
 
4829 -    sha256 = "sha256-MqH/8K9XRvEg6P7GHE6XXtWnhDP3wT8iGoNaFtYQbio=";
 
4830 +    sha256 = "sha256-ulwdrVrw2bwdafgc3NrJ1J15evQ5btpHLTaiqsyA58U=";
 
4833    nativeBuildInputs = srht.nativeBuildInputs;
 
4834 @@ -29,10 +29,12 @@ buildPythonPackage rec {
 
4835      export SRHT_PATH=${srht}/${python.sitePackages}/srht
 
4838 +  pythonImportsCheck = [ "mansrht" ];
 
4841      homepage = "https://git.sr.ht/~sircmpwn/man.sr.ht";
 
4842      description = "Wiki service for the sr.ht network";
 
4843 -    license = licenses.agpl3;
 
4844 +    license = licenses.agpl3Only;
 
4845      maintainers = with maintainers; [ eadwu ];
 
4848 diff --git a/pkgs/applications/version-management/sourcehut/meta.nix b/pkgs/applications/version-management/sourcehut/meta.nix
 
4849 index 86d293973d7..0964960fea0 100644
 
4850 --- a/pkgs/applications/version-management/sourcehut/meta.nix
 
4851 +++ b/pkgs/applications/version-management/sourcehut/meta.nix
 
4856 -  version = "0.53.14";
 
4857 +  version = "0.56.0";
 
4859    src = fetchFromSourcehut {
 
4860      owner = "~sircmpwn";
 
4861      repo = "meta.sr.ht";
 
4863 -    sha256 = "sha256-/+r/XLDkcSTW647xPMh5bcJmR2xZNNH74AJ5jemna2k=";
 
4864 +    sha256 = "sha256-+fCNtW+k9fmjh1TLK0WUv0EgWKGlhURYS6atUH5EOjM=";
 
4867    buildApi = src: buildGoModule {
 
4868      inherit src version;
 
4869      pname = "metasrht-api";
 
4870 -    vendorSha256 = "sha256-eZyDrr2VcNMxI++18qUy7LA1Q1YDlWCoRtl00L8lfR4=";
 
4871 +    vendorSha256 = "sha256-m9j9tmss+utTVMywI9wB1n9EK8sbElW0ej2YqK1yL5M=";
 
4875 @@ -66,10 +66,12 @@ buildPythonPackage rec {
 
4876      cp ${buildApi "${src}/api/"}/bin/api $out/bin/metasrht-api
 
4879 +  pythonImportsCheck = [ "metasrht" ];
 
4882      homepage = "https://git.sr.ht/~sircmpwn/meta.sr.ht";
 
4883      description = "Account management service for the sr.ht network";
 
4884 -    license = licenses.agpl3;
 
4885 +    license = licenses.agpl3Only;
 
4886      maintainers = with maintainers; [ eadwu ];
 
4889 diff --git a/pkgs/applications/version-management/sourcehut/pages.nix b/pkgs/applications/version-management/sourcehut/pages.nix
 
4890 new file mode 100644
 
4891 index 00000000000..4a3d9f8c7d0
 
4893 +++ b/pkgs/applications/version-management/sourcehut/pages.nix
 
4896 +, fetchFromSourcehut
 
4900 +  version = "0.4.10";
 
4902 +  src = fetchFromSourcehut {
 
4903 +    owner = "~sircmpwn";
 
4904 +    repo = "pages.sr.ht";
 
4906 +    sha256 = "sha256-Lq/xCCAywxxjX5nHbOvmCaQ4wtLgjcMo3Qc7xO1fdAs=";
 
4911 +  inherit src version;
 
4912 +  pname = "pagessrht";
 
4913 +  vendorSha256 = "sha256-YFRBoflFy48ipTvXdZ4qPSEgTIYvm4752JRZSzRG++U=";
 
4916 +    mkdir -p $out/share/sql/
 
4917 +    cp -r -t $out/share/sql/ schema.sql migrations
 
4920 +  meta = with lib; {
 
4921 +    homepage = "https://git.sr.ht/~sircmpwn/pages.sr.ht";
 
4922 +    description = "Web hosting service for the sr.ht network";
 
4923 +    license = licenses.agpl3Only;
 
4924 +    maintainers = with maintainers; [ eadwu ];
 
4927 diff --git a/pkgs/applications/version-management/sourcehut/paste.nix b/pkgs/applications/version-management/sourcehut/paste.nix
 
4928 index 0d8c9135493..ecd31c25deb 100644
 
4929 --- a/pkgs/applications/version-management/sourcehut/paste.nix
 
4930 +++ b/pkgs/applications/version-management/sourcehut/paste.nix
 
4933  buildPythonPackage rec {
 
4934    pname = "pastesrht";
 
4935 -  version = "0.12.1";
 
4936 +  version = "0.12.4";
 
4938    src = fetchFromSourcehut {
 
4939      owner = "~sircmpwn";
 
4940      repo = "paste.sr.ht";
 
4942 -    sha256 = "sha256-QQhd2LeH9BLmlHilhsv+9fZ+RPNmEMSmOpFA3dsMBFc=";
 
4943 +    sha256 = "sha256-hFjWa7L7JiQoG3Hm9NyoP2FNypDiW+nGDmQ2DoZkAIw=";
 
4946    nativeBuildInputs = srht.nativeBuildInputs;
 
4947 @@ -29,10 +29,12 @@ buildPythonPackage rec {
 
4948      export SRHT_PATH=${srht}/${python.sitePackages}/srht
 
4951 +  pythonImportsCheck = [ "pastesrht" ];
 
4954      homepage = "https://git.sr.ht/~sircmpwn/paste.sr.ht";
 
4955      description = "Ad-hoc text file hosting service for the sr.ht network";
 
4956 -    license = licenses.agpl3;
 
4957 +    license = licenses.agpl3Only;
 
4958      maintainers = with maintainers; [ eadwu ];
 
4961 diff --git a/pkgs/applications/version-management/sourcehut/disable-npm-install.patch b/pkgs/applications/version-management/sourcehut/patches/disable-npm-install.patch
 
4962 similarity index 100%
 
4963 rename from pkgs/applications/version-management/sourcehut/disable-npm-install.patch
 
4964 rename to pkgs/applications/version-management/sourcehut/patches/disable-npm-install.patch
 
4965 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0001-worker-update-go-redis-to-support-Unix-sockets.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0001-worker-update-go-redis-to-support-Unix-sockets.patch
 
4966 new file mode 100644
 
4967 index 00000000000..4efd12be875
 
4969 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0001-worker-update-go-redis-to-support-Unix-sockets.patch
 
4971 +From 5991960a5d412f0e1bdc505b970248c68b44a720 Mon Sep 17 00:00:00 2001
 
4972 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
4973 +Date: Wed, 15 Sep 2021 19:45:41 +0200
 
4974 +Subject: [PATCH builds.sr.ht v3 1/2] worker: update go-redis to support Unix
 
4978 + worker/context.go |  4 ++--
 
4979 + worker/main.go    | 11 +++++++----
 
4980 + worker/tasks.go   | 10 +++++-----
 
4981 + 3 files changed, 14 insertions(+), 11 deletions(-)
 
4983 +diff --git a/worker/context.go b/worker/context.go
 
4984 +index f84a60c..be54717 100644
 
4985 +--- a/worker/context.go
 
4986 ++++ b/worker/context.go
 
4987 +@@ -14,7 +14,7 @@ import (
 
4991 +-      "github.com/go-redis/redis"
 
4992 ++      goredis "github.com/go-redis/redis/v8"
 
4993 +       "github.com/google/shlex"
 
4994 +       "github.com/pkg/errors"
 
4995 +       "github.com/prometheus/client_golang/prometheus"
 
4996 +@@ -41,7 +41,7 @@ var (
 
4998 + type WorkerContext struct {
 
5000 +-      Redis *redis.Client
 
5001 ++      Redis *goredis.Client
 
5002 +       Conf  func(section, key string) string
 
5005 +diff --git a/worker/main.go b/worker/main.go
 
5006 +index 274ba68..e22ab6b 100644
 
5007 +--- a/worker/main.go
 
5008 ++++ b/worker/main.go
 
5017 +@@ -9,7 +10,7 @@ import (
 
5021 +-      "github.com/go-redis/redis"
 
5022 ++      goredis "github.com/go-redis/redis/v8"
 
5023 +       "github.com/vaughan0/go-ini"
 
5024 +       "git.sr.ht/~sircmpwn/core-go/crypto"
 
5026 +@@ -26,6 +27,8 @@ var (
 
5027 +       jobsMutex sync.Mutex
 
5030 ++var redisctx = context.Background()
 
5033 +       flag.IntVar(&workers, "workers", runtime.NumCPU(),
 
5034 +               "configure number of workers")
 
5035 +@@ -68,12 +71,12 @@ func main() {
 
5037 +               redisHost = "redis://localhost:6379"
 
5039 +-      ropts, err := redis.ParseURL(redisHost)
 
5040 ++      ropts, err := goredis.ParseURL(redisHost)
 
5044 +-      localRedis := redis.NewClient(ropts)
 
5045 +-      if _, err := localRedis.Ping().Result(); err != nil {
 
5046 ++      localRedis := goredis.NewClient(ropts)
 
5047 ++      if _, err := localRedis.Ping(redisctx).Result(); err != nil {
 
5051 +diff --git a/worker/tasks.go b/worker/tasks.go
 
5052 +index d27bf33..d0c28f1 100644
 
5053 +--- a/worker/tasks.go
 
5054 ++++ b/worker/tasks.go
 
5055 +@@ -19,7 +19,7 @@ import (
 
5058 +       "git.sr.ht/~sircmpwn/core-go/auth"
 
5059 +-      "github.com/go-redis/redis"
 
5060 ++      goredis "github.com/go-redis/redis/v8"
 
5061 +       "github.com/kr/pty"
 
5062 +       "github.com/minio/minio-go/v6"
 
5063 +       "github.com/pkg/errors"
 
5064 +@@ -39,12 +39,12 @@ var (
 
5065 +       }, []string{"image", "arch"})
 
5068 +-func (ctx *JobContext) Boot(r *redis.Client) func() {
 
5069 +-      port, err := r.Incr("builds.sr.ht.ssh-port").Result()
 
5070 ++func (ctx *JobContext) Boot(r *goredis.Client) func() {
 
5071 ++      port, err := r.Incr(ctx.Context, "builds.sr.ht.ssh-port").Result()
 
5072 +       if err == nil && port < 22000 {
 
5073 +-              err = r.Set("builds.sr.ht.ssh-port", 22100, 0).Err()
 
5074 ++              err = r.Set(ctx.Context, "builds.sr.ht.ssh-port", 22100, 0).Err()
 
5075 +       } else if err == nil && port >= 23000 {
 
5076 +-              err = r.Set("builds.sr.ht.ssh-port", 22000, 0).Err()
 
5077 ++              err = r.Set(ctx.Context, "builds.sr.ht.ssh-port", 22000, 0).Err()
 
5080 +               panic(errors.Wrap(err, "assign port"))
 
5084 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0002-worker-update-go.-mod-sum-for-go-redis.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0002-worker-update-go.-mod-sum-for-go-redis.patch
 
5085 new file mode 100644
 
5086 index 00000000000..c9368d05c18
 
5088 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/build/v3-0002-worker-update-go.-mod-sum-for-go-redis.patch
 
5090 +From eac18e913e4ee48895b94acfa56cf1c6a3fb49fa Mon Sep 17 00:00:00 2001
 
5091 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5092 +Date: Wed, 15 Sep 2021 20:11:49 +0200
 
5093 +Subject: [PATCH builds.sr.ht v3 2/2] worker: update go.{mod,sum} for go-redis
 
5096 + worker/go.mod |  8 +------
 
5097 + worker/go.sum | 64 +++++++++++++++++++--------------------------------
 
5098 + 2 files changed, 25 insertions(+), 47 deletions(-)
 
5100 +diff --git a/worker/go.mod b/worker/go.mod
 
5101 +index 6e9a11c..4893dfa 100644
 
5102 +--- a/worker/go.mod
 
5103 ++++ b/worker/go.mod
 
5104 +@@ -2,24 +2,18 @@ module git.sr.ht/~sircmpwn/builds.sr.ht/worker
 
5107 +       git.sr.ht/~sircmpwn/core-go v0.0.0-20210108160653-070566136c1a
 
5108 +-      github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
 
5109 +-      github.com/go-redis/redis v6.15.2+incompatible
 
5110 ++      github.com/go-redis/redis/v8 v8.2.3
 
5111 +       github.com/gocelery/gocelery v0.0.0-20201111034804-825d89059344
 
5112 +-      github.com/gomodule/redigo v2.0.0+incompatible // indirect
 
5113 +       github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 
5114 +-      github.com/jpillora/longestcommon v0.0.0-20161227235612-adb9d91ee629
 
5115 +       github.com/kr/pty v1.1.3
 
5116 +       github.com/lib/pq v1.8.0
 
5117 +       github.com/martinlindhe/base36 v1.1.0
 
5118 +-      github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
 
5119 +       github.com/minio/minio-go/v6 v6.0.49
 
5120 +       github.com/mitchellh/mapstructure v1.1.2
 
5121 +       github.com/pkg/errors v0.9.1
 
5122 +       github.com/prometheus/client_golang v1.7.1
 
5123 +-      github.com/shicky/gocelery v0.0.0-20180807061531-b2f0dd7ec05b
 
5124 +       github.com/streadway/amqp v1.0.0 // indirect
 
5125 +       github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
 
5126 +-      golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb
 
5127 +       gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 
5128 +       gopkg.in/mail.v2 v2.3.1
 
5129 +       gopkg.in/yaml.v2 v2.3.0
 
5130 +diff --git a/worker/go.sum b/worker/go.sum
 
5131 +index 1cd3989..4a2d6d9 100644
 
5132 +--- a/worker/go.sum
 
5133 ++++ b/worker/go.sum
 
5134 +@@ -10,6 +10,7 @@ git.sr.ht/~sircmpwn/go-bare v0.0.0-20200812160916-d2c72e1a5018/go.mod h1:BVJwbDf
 
5135 + github.com/99designs/gqlgen v0.13.0 h1:haLTcUp3Vwp80xMVEg5KRNwzfUrgFdRmtBY8fuB8scA=
 
5136 + github.com/99designs/gqlgen v0.13.0/go.mod h1:NV130r6f4tpRWuAI+zsrSdooO/eWUv+Gyyoi3rEfXIk=
 
5137 + github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 
5138 ++github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
 
5139 + github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 
5140 + github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
 
5141 + github.com/Masterminds/squirrel v1.4.0 h1:he5i/EXixZxrBUWcxzDYMiju9WZ3ld/l7QBNuo/eN3w=
 
5142 +@@ -25,6 +26,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
 
5143 + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 
5144 + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 
5145 + github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 
5146 ++github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
 
5147 + github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 
5148 + github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 
5149 + github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 
5150 +@@ -36,7 +38,6 @@ github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6l
 
5151 + github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 
5152 + github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 
5153 + github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 
5154 +-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
 
5155 + github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 
5156 + github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 
5157 + github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 
5158 +@@ -58,8 +59,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
 
5159 + github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 
5160 + github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 
5161 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
5162 ++github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 
5163 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
5164 + github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 
5165 ++github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 
5166 + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 
5167 + github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
 
5168 + github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 
5169 +@@ -84,22 +87,18 @@ github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001/go.mod h1:2H9hjfb
 
5170 + github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
 
5171 + github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 
5172 + github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 
5173 ++github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 
5174 + github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 
5175 + github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 
5176 + github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 
5177 + github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
 
5178 +-github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
 
5179 +-github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
 
5180 + github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 
5181 + github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 
5182 + github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 
5183 + github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 
5184 + github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 
5185 + github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 
5186 +-github.com/go-redis/redis v6.14.1+incompatible h1:kSJohAREGMr344uMa8PzuIg5OU6ylCbyDkWkkNOfEik=
 
5187 +-github.com/go-redis/redis v6.14.1+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 
5188 +-github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
 
5189 +-github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 
5190 ++github.com/go-redis/redis/v8 v8.2.3 h1:eNesND+DWt/sjQOtPFxAbQkTIXaXX00qNLxjVWkZ70k=
 
5191 + github.com/go-redis/redis/v8 v8.2.3/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw=
 
5192 + github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 
5193 + github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 
5194 +@@ -114,7 +113,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
 
5195 + github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 
5196 + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 
5197 + github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 
5198 +-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 
5199 + github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 
5200 + github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 
5201 + github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 
5202 +@@ -136,12 +134,14 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 
5203 + github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 
5204 + github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5205 + github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5206 ++github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 
5207 + github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5208 + github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 
5209 + github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 
5210 + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 
5211 + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 
5212 + github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 
5213 ++github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 
5214 + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 
5215 + github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 
5216 + github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 
5217 +@@ -180,12 +180,11 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod
 
5218 + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 
5219 + github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 
5220 + github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 
5221 +-github.com/jpillora/longestcommon v0.0.0-20161227235612-adb9d91ee629 h1:1dSBUfGlorLAua2CRx0zFN7kQsTpE2DQSmr7rrTNgY8=
 
5222 +-github.com/jpillora/longestcommon v0.0.0-20161227235612-adb9d91ee629/go.mod h1:mb5nS4uRANwOJSZj8rlCWAfAcGi72GGMIXx+xGOjA7M=
 
5223 + github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 
5224 + github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 
5225 + github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 
5226 + github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 
5227 ++github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 
5228 + github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 
5229 + github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 
5230 + github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 
5231 +@@ -200,20 +199,18 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 
5232 + github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk=
 
5233 + github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 
5234 + github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 
5235 ++github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 
5236 + github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 
5237 + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
 
5238 + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
 
5239 + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
 
5240 + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
 
5241 +-github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 
5242 +-github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 
5243 + github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
 
5244 + github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 
5245 + github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 
5246 + github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 
5247 + github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 
5248 + github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 
5249 +-github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
 
5250 + github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
 
5251 + github.com/martinlindhe/base36 v1.1.0 h1:cIwvvwYse/0+1CkUPYH5ZvVIYG3JrILmQEIbLuar02Y=
 
5252 + github.com/martinlindhe/base36 v1.1.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
 
5253 +@@ -228,7 +225,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
 
5254 + github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 
5255 + github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 
5256 + github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 
5257 +-github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o=
 
5258 + github.com/minio/minio-go/v6 v6.0.49 h1:bU4kIa/qChTLC1jrWZ8F+8gOiw1MClubddAJVR4gW3w=
 
5259 + github.com/minio/minio-go/v6 v6.0.49/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
 
5260 + github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
 
5261 +@@ -242,8 +238,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
 
5262 + github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 
5263 + github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 
5264 + github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 
5265 +-github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
 
5266 +-github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 
5267 + github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 
5268 + github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 
5269 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 
5270 +@@ -259,7 +253,9 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
 
5271 + github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 
5272 + github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 
5273 + github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 
5274 ++github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 
5275 + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 
5276 ++github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 
5277 + github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 
5278 + github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 
5279 + github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 
5280 +@@ -267,10 +263,12 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
 
5281 + github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 
5282 + github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 
5283 + github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 
5284 ++github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
 
5285 + github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 
5286 + github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 
5287 + github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 
5288 + github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 
5289 ++github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
 
5290 + github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 
5291 + github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 
5292 + github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
 
5293 +@@ -287,22 +285,20 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
 
5294 + github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 
5295 + github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 
5296 + github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 
5297 +-github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 
5298 + github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 
5299 + github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 
5300 + github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 
5301 + github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 
5302 + github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 
5303 ++github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 
5304 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 
5305 + github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 
5306 +-github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno=
 
5307 + github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 
5308 + github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 
5309 + github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 
5310 + github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
 
5311 + github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
 
5312 + github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 
5313 +-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
 
5314 + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 
5315 + github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 
5316 + github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 
5317 +@@ -310,15 +306,12 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
 
5318 + github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 
5319 + github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 
5320 + github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 
5321 +-github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 h1:Cto4X6SVMWRPBkJ/3YHn1iDGDGc/Z+sW+AEMKHMVvN4=
 
5322 +-github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 
5323 + github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 
5324 + github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 
5325 + github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
 
5326 + github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 
5327 + github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
 
5328 + github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
 
5329 +-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
 
5330 + github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 
5331 + github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 
5332 + github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 
5333 +@@ -333,36 +326,28 @@ github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 
5334 + github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 
5335 + github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 
5336 + github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 
5337 +-github.com/satori/go.uuid v1.1.0 h1:B9KXyj+GzIpJbV7gmr873NsY6zpbxNy24CBtGrk7jHo=
 
5338 +-github.com/satori/go.uuid v1.1.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 
5339 +-github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 
5340 +-github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 
5341 +-github.com/satori/go.uuid v1.2.1-0.20180103174451-36e9d2ebbde5 h1:Jw7W4WMfQDxsXvfeFSaS2cHlY7bAF4MGrgnbd0+Uo78=
 
5342 +-github.com/satori/go.uuid v1.2.1-0.20180103174451-36e9d2ebbde5/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 
5343 + github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
 
5344 + github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 
5345 + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 
5346 + github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 
5347 ++github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 
5348 + github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 
5349 +-github.com/shicky/gocelery v0.0.0-20180807061531-b2f0dd7ec05b h1:7kJLeBNcPG1orS3ksAFN0qoJGtf8jvwgOh5Q+bsNZvc=
 
5350 +-github.com/shicky/gocelery v0.0.0-20180807061531-b2f0dd7ec05b/go.mod h1:kn4CkFIzvsrXBvbNk2hX9DpIM8xo/74mYhiYTpGhYXE=
 
5351 + github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 
5352 + github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 
5353 + github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
 
5354 + github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 
5355 + github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 
5356 + github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 
5357 ++github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 
5358 + github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 
5359 + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 
5360 ++github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 
5361 + github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 
5362 + github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 
5363 + github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 
5364 + github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 
5365 + github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 
5366 +-github.com/streadway/amqp v0.0.0-20180806233856-70e15c650864 h1:Oj3PUEs+OUSYUpn35O+BE/ivHGirKixA3+vqA0Atu9A=
 
5367 +-github.com/streadway/amqp v0.0.0-20180806233856-70e15c650864/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
 
5368 + github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 
5369 +-github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw=
 
5370 + github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 
5371 + github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
 
5372 + github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 
5373 +@@ -373,6 +358,7 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 
5374 + github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 
5375 + github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 
5376 + github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 
5377 ++github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 
5378 + github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 
5379 + github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 
5380 + github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 
5381 +@@ -391,6 +377,7 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI
 
5382 + go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 
5383 + go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 
5384 + go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 
5385 ++go.opentelemetry.io/otel v0.11.0 h1:IN2tzQa9Gc4ZVKnTaMbPVcHjvzOdg5n9QfnmlqiET7E=
 
5386 + go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
 
5387 + go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 
5388 + go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 
5389 +@@ -403,7 +390,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 
5390 + golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 
5391 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 
5392 + golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 
5393 +-golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
 
5394 + golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 
5395 + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 
5396 + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 
5397 +@@ -431,7 +417,6 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r
 
5398 + golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 
5399 + golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 
5400 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 
5401 +-golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
 
5402 + golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 
5403 + golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 
5404 + golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 
5405 +@@ -451,7 +436,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 
5406 + golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5407 + golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5408 + golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5409 +-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
 
5410 + golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5411 + golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5412 + golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5413 +@@ -460,7 +444,6 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
 
5414 + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5415 + golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5416 + golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5417 +-golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
 
5418 + golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5419 + golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5420 + golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5421 +@@ -477,7 +460,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
 
5422 + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5423 + golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb h1:HS9IzC4UFbpMBLQUDSQcU+ViVT1vdFCQVjdPVpTlZrs=
 
5424 + golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5425 +-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 
5426 + golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 
5427 + golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 
5428 + golang.org/x/text v0.3.4-0.20201021145329-22f1617af38e h1:0kyKOEC0chG7FKmnf/1uNwvDLc3NtNTRip2rXAN9nwI=
 
5429 +@@ -502,6 +484,7 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
 
5430 + golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 
5431 + golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5432 + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5433 ++golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 
5434 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5435 + google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 
5436 + google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 
5437 +@@ -539,6 +522,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod
 
5438 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
5439 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
5440 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
5441 ++gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 
5442 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
5443 + gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 
5444 + gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 
5445 +@@ -549,6 +533,7 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 
5446 + gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
 
5447 + gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
 
5448 + gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 
5449 ++gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 
5450 + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 
5451 + gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 
5452 + gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 
5453 +@@ -556,10 +541,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5454 + gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5455 + gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5456 + gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5457 +-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 
5458 +-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5459 + gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 
5460 + gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5461 ++gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 
5462 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 
5463 + honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 
5464 + honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 
5468 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v3-0001-add-Unix-socket-support-for-redis-host.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v3-0001-add-Unix-socket-support-for-redis-host.patch
 
5469 new file mode 100644
 
5470 index 00000000000..299ad3d83c5
 
5472 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/core/v3-0001-add-Unix-socket-support-for-redis-host.patch
 
5474 +From c0ccc8db051a2f8278edf59b41ed238fa71aa4c0 Mon Sep 17 00:00:00 2001
 
5475 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5476 +Date: Mon, 23 Aug 2021 18:43:18 +0200
 
5477 +Subject: [PATCH core.sr.ht v3] add Unix socket support for redis-host=
 
5480 + srht/redis.py | 11 ++---------
 
5481 + 1 file changed, 2 insertions(+), 9 deletions(-)
 
5483 +diff --git a/srht/redis.py b/srht/redis.py
 
5484 +index 8a9347c..2e91c35 100644
 
5485 +--- a/srht/redis.py
 
5486 ++++ b/srht/redis.py
 
5488 +-from redis import Redis
 
5489 ++from redis import from_url
 
5490 + from srht.config import cfg
 
5491 +-from urllib.parse import urlparse
 
5493 +-url = cfg("sr.ht", "redis-host", "redis://localhost")
 
5494 +-url = urlparse(url)
 
5496 +-redis = Redis(host=url.hostname,
 
5497 +-        port=(url.port or 6379),
 
5498 +-        password=url.password,
 
5499 +-        db=int(url.path[1:]) if url.path else 0)
 
5500 ++redis = from_url(cfg("sr.ht", "redis-host", "redis://localhost"))
 
5504 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
 
5505 new file mode 100644
 
5506 index 00000000000..48e91ac739b
 
5508 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0001-gitsrht-keys-update-go-redis-to-support-Unix-sock.patch
 
5510 +From 083e4791771d998c9a6c881a4101d24296e38252 Mon Sep 17 00:00:00 2001
 
5511 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5512 +Date: Fri, 27 Aug 2021 15:38:28 +0200
 
5513 +Subject: [PATCH git.sr.ht v3 1/3] gitsrht-keys: update go-redis to support
 
5517 + gitsrht-keys/main.go | 2 +-
 
5518 + 1 file changed, 1 insertion(+), 1 deletion(-)
 
5520 +diff --git a/gitsrht-keys/main.go b/gitsrht-keys/main.go
 
5521 +index 0c1aea1..fa17183 100644
 
5522 +--- a/gitsrht-keys/main.go
 
5523 ++++ b/gitsrht-keys/main.go
 
5524 +@@ -5,7 +5,7 @@ import (
 
5528 +-      goredis "github.com/go-redis/redis"
 
5529 ++      goredis "github.com/go-redis/redis/v8"
 
5530 +       "github.com/vaughan0/go-ini"
 
5531 +       "git.sr.ht/~sircmpwn/scm.sr.ht/srht-keys"
 
5536 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
 
5537 new file mode 100644
 
5538 index 00000000000..41847ac8717
 
5540 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0002-gitsrht-update-hook-update-go-redis-to-support-Un.patch
 
5542 +From d9683aced0dc3a94c56de2fde31c1765054900fa Mon Sep 17 00:00:00 2001
 
5543 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5544 +Date: Fri, 27 Aug 2021 15:39:29 +0200
 
5545 +Subject: [PATCH git.sr.ht v3 2/3] gitsrht-update-hook: update go-redis to
 
5546 + support Unix sockets
 
5549 + gitsrht-update-hook/options.go     | 12 +++++++-----
 
5550 + gitsrht-update-hook/post-update.go |  4 ++--
 
5551 + gitsrht-update-hook/update.go      |  4 ++--
 
5552 + 3 files changed, 11 insertions(+), 9 deletions(-)
 
5554 +diff --git a/gitsrht-update-hook/options.go b/gitsrht-update-hook/options.go
 
5555 +index 8efbb0a..962502a 100644
 
5556 +--- a/gitsrht-update-hook/options.go
 
5557 ++++ b/gitsrht-update-hook/options.go
 
5569 +-      goredis "github.com/go-redis/redis"
 
5570 ++      goredis "github.com/go-redis/redis/v8"
 
5573 ++var ctx = context.Background()
 
5574 + var options map[string]string
 
5576 + func loadOptions() {
 
5577 +@@ -35,10 +37,10 @@ func loadOptions() {
 
5579 +       if nopts, ok := os.LookupEnv("GIT_PUSH_OPTION_COUNT"); ok {
 
5580 +               n, _ = strconv.Atoi(nopts)
 
5581 +-              redis.Set(fmt.Sprintf("git.sr.ht.options.%s", uuid),
 
5582 ++              redis.Set(ctx, fmt.Sprintf("git.sr.ht.options.%s", uuid),
 
5583 +                       nopts, 10*time.Minute)
 
5585 +-              nopts, err := redis.Get(fmt.Sprintf(
 
5586 ++              nopts, err := redis.Get(ctx, fmt.Sprintf(
 
5587 +                       "git.sr.ht.options.%s", uuid)).Result()
 
5590 +@@ -51,12 +53,12 @@ func loadOptions() {
 
5591 +               opt, ok := os.LookupEnv(fmt.Sprintf("GIT_PUSH_OPTION_%d", i))
 
5592 +               optkey := fmt.Sprintf("git.sr.ht.options.%s.%d", uuid, i)
 
5594 +-                      opt, err = redis.Get(optkey).Result()
 
5595 ++                      opt, err = redis.Get(ctx, optkey).Result()
 
5600 +-                      redis.Set(optkey, opt, 10*time.Minute)
 
5601 ++                      redis.Set(ctx, optkey, opt, 10*time.Minute)
 
5603 +               parts := strings.SplitN(opt, "=", 2)
 
5604 +               if len(parts) == 1 {
 
5605 +diff --git a/gitsrht-update-hook/post-update.go b/gitsrht-update-hook/post-update.go
 
5606 +index d14d616..fcd7864 100644
 
5607 +--- a/gitsrht-update-hook/post-update.go
 
5608 ++++ b/gitsrht-update-hook/post-update.go
 
5609 +@@ -15,7 +15,7 @@ import (
 
5610 +       "github.com/go-git/go-git/v5/plumbing"
 
5611 +       "github.com/go-git/go-git/v5/plumbing/object"
 
5612 +       "github.com/go-git/go-git/v5/plumbing/storer"
 
5613 +-      goredis "github.com/go-redis/redis"
 
5614 ++      goredis "github.com/go-redis/redis/v8"
 
5615 +       _ "github.com/lib/pq"
 
5618 +@@ -220,7 +220,7 @@ func postUpdate() {
 
5619 +               var oldref, newref string
 
5620 +               var oldobj, newobj object.Object
 
5621 +               updateKey := fmt.Sprintf("update.%s.%s", pushUuid, refname)
 
5622 +-              update, err := redis.Get(updateKey).Result()
 
5623 ++              update, err := redis.Get(ctx, updateKey).Result()
 
5624 +               if update == "" || err != nil {
 
5625 +                       logger.Println("redis.Get: missing key")
 
5627 +diff --git a/gitsrht-update-hook/update.go b/gitsrht-update-hook/update.go
 
5628 +index 72c661a..e33fd4b 100644
 
5629 +--- a/gitsrht-update-hook/update.go
 
5630 ++++ b/gitsrht-update-hook/update.go
 
5631 +@@ -5,7 +5,7 @@ import (
 
5635 +-      goredis "github.com/go-redis/redis"
 
5636 ++      goredis "github.com/go-redis/redis/v8"
 
5639 + // XXX: This is run once for every single ref that's pushed. If someone pushes
 
5640 +@@ -31,6 +31,6 @@ func update() {
 
5641 +               logger.Fatalf("Failed to parse redis host: %v", err)
 
5643 +       redis := goredis.NewClient(ropts)
 
5644 +-      redis.Set(fmt.Sprintf("update.%s.%s", pushUuid, refname),
 
5645 ++      redis.Set(ctx, fmt.Sprintf("update.%s.%s", pushUuid, refname),
 
5646 +               fmt.Sprintf("%s:%s", oldref, newref), 10*time.Minute)
 
5651 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
 
5652 new file mode 100644
 
5653 index 00000000000..9a4b0300921
 
5655 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/git/v3-0003-gitsrht-dispatch-add-support-for-supplementary-gr.patch
 
5657 +From fcbec39a406562c29dfcf7eeef6f284da28bc619 Mon Sep 17 00:00:00 2001
 
5658 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5659 +Date: Fri, 27 Aug 2021 17:42:33 +0200
 
5660 +Subject: [PATCH git.sr.ht v3 3/3] gitsrht-dispatch: add support for
 
5661 + supplementary groups
 
5664 + gitsrht-dispatch/main.go | 17 ++++++++++++++---
 
5665 + 1 file changed, 14 insertions(+), 3 deletions(-)
 
5667 +diff --git a/gitsrht-dispatch/main.go b/gitsrht-dispatch/main.go
 
5668 +index d7aee14..5f17b75 100644
 
5669 +--- a/gitsrht-dispatch/main.go
 
5670 ++++ b/gitsrht-dispatch/main.go
 
5671 +@@ -17,6 +17,7 @@ type Dispatcher struct {
 
5679 +@@ -70,11 +71,20 @@ AuthorizedKeysUser=root`, os.Args[0])
 
5681 +                       logger.Fatalf("Error looking up group %s: %v", spec[1], err)
 
5683 ++              groups, err := user.GroupIds()
 
5685 ++                      logger.Fatalf("Error looking up supplementary groups of user %s: %v", spec[0], err)
 
5687 ++              gids := make([]int, len(groups))
 
5688 ++              for i, grp := range groups {
 
5689 ++                      sgid, _ := strconv.Atoi(grp)
 
5692 +               uid, _ := strconv.Atoi(user.Uid)
 
5693 +               gid, _ := strconv.Atoi(group.Gid)
 
5694 +-              dispatchers[uid] = Dispatcher{cmd, uid, gid}
 
5695 +-              logger.Printf("Registered dispatcher for %s(%d):%s(%d): %s",
 
5696 +-                      spec[0], uid, spec[1], gid, cmd)
 
5697 ++              dispatchers[uid] = Dispatcher{cmd, uid, gid, gids}
 
5698 ++              logger.Printf("Registered dispatcher for %s(%d):%s(%d):(%s): %s",
 
5699 ++                      spec[0], uid, spec[1], gid, strings.Join(groups, ","), cmd)
 
5702 +       var user *osuser.User
 
5703 +@@ -93,6 +103,7 @@ AuthorizedKeysUser=root`, os.Args[0])
 
5705 +       if dispatcher, ok := dispatchers[uid]; ok {
 
5706 +               logger.Printf("Dispatching to %s", dispatcher.cmd)
 
5707 ++              syscall.Setgroups(dispatcher.gids)
 
5708 +               syscall.Setgid(dispatcher.gid)
 
5709 +               syscall.Setuid(dispatcher.uid)
 
5710 +               if err := syscall.Exec(dispatcher.cmd, append([]string{
 
5714 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
 
5715 new file mode 100644
 
5716 index 00000000000..191ff61b826
 
5718 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
 
5720 +From e244cb7398758f91cc6deaabf278a1b6412ee477 Mon Sep 17 00:00:00 2001
 
5721 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5722 +Date: Fri, 27 Aug 2021 12:48:56 +0200
 
5723 +Subject: [PATCH scm.sr.ht v3 1/2] srht-keys: update go-redis to support Unix
 
5727 + srht-keys/srhtkeys.go | 9 ++++++---
 
5728 + 1 file changed, 6 insertions(+), 3 deletions(-)
 
5730 +diff --git a/srht-keys/srhtkeys.go b/srht-keys/srhtkeys.go
 
5731 +index be925ed..1a300d5 100644
 
5732 +--- a/srht-keys/srhtkeys.go
 
5733 ++++ b/srht-keys/srhtkeys.go
 
5742 +@@ -12,7 +13,7 @@ import (
 
5746 +-      goredis "github.com/go-redis/redis"
 
5747 ++      goredis "github.com/go-redis/redis/v8"
 
5748 +       "github.com/google/uuid"
 
5749 +       _ "github.com/lib/pq"
 
5750 +       "github.com/vaughan0/go-ini"
 
5751 +@@ -37,6 +38,8 @@ type MetaSSHKey struct {
 
5752 +       Owner       MetaUser `json:"owner"`
 
5755 ++var ctx = context.Background()
 
5757 + // Stores the SSH key in the database and returns the user's ID.
 
5758 + func storeKey(logger *log.Logger, db *sql.DB, key *MetaSSHKey) (int, error) {
 
5759 +       logger.Println("Storing meta.sr.ht key in database")
 
5760 +@@ -145,7 +148,7 @@ func fetchKeysFromMeta(logger *log.Logger, config ini.File,
 
5762 +               logger.Printf("Caching SSH key in redis failed: %v", err)
 
5764 +-              redis.Set(cacheKey, cacheBytes, 7*24*time.Hour)
 
5765 ++              redis.Set(ctx, cacheKey, cacheBytes, 7*24*time.Hour)
 
5768 +       return key.Owner.Username, userId
 
5769 +@@ -168,7 +171,7 @@ func UserFromKey(logger *log.Logger, config ini.File,
 
5771 +       cacheKey := fmt.Sprintf("%s.ssh-keys.%s", service, b64key)
 
5772 +       logger.Printf("Cache key for SSH key lookup: %s", cacheKey)
 
5773 +-      cacheBytes, err := redis.Get(cacheKey).Bytes()
 
5774 ++      cacheBytes, err := redis.Get(ctx, cacheKey).Bytes()
 
5781 diff --git a/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch
 
5782 new file mode 100644
 
5783 index 00000000000..c5407a26491
 
5785 +++ b/pkgs/applications/version-management/sourcehut/patches/redis-socket/scm/v3-0002-srht-keys-update-go.-mod-sum-for-go-redis.patch
 
5787 +From aeb3e0dc2270e6ab3cd0f651ea735275e527e7ce Mon Sep 17 00:00:00 2001
 
5788 +From: Julien Moutinho <julm+srht@sourcephile.fr>
 
5789 +Date: Fri, 27 Aug 2021 13:06:27 +0200
 
5790 +Subject: [PATCH scm.sr.ht v3 2/2] srht-keys: update go.{mod,sum} for go-redis
 
5793 + srht-keys/go.mod |   2 +-
 
5794 + srht-keys/go.sum | 103 ++++++++++++++++++++++++++++++++++++++++++++---
 
5795 + 2 files changed, 99 insertions(+), 6 deletions(-)
 
5797 +diff --git a/srht-keys/go.mod b/srht-keys/go.mod
 
5798 +index d275913..8d1c10a 100644
 
5799 +--- a/srht-keys/go.mod
 
5800 ++++ b/srht-keys/go.mod
 
5801 +@@ -4,7 +4,7 @@ go 1.13
 
5804 +       git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6
 
5805 +-      github.com/go-redis/redis v6.15.9+incompatible
 
5806 ++      github.com/go-redis/redis/v8 v8.11.3
 
5807 +       github.com/google/uuid v1.1.1
 
5808 +       github.com/lib/pq v1.8.0
 
5809 +       github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
 
5810 +diff --git a/srht-keys/go.sum b/srht-keys/go.sum
 
5811 +index 974326e..a264a26 100644
 
5812 +--- a/srht-keys/go.sum
 
5813 ++++ b/srht-keys/go.sum
 
5815 +-git.sr.ht/~sircmpwn/core-go v0.0.0-20200820135923-98806e712f5e h1:TJqf/neVU5peFAS9WcR1aADXcflPOvAd7ABEirmU7m0=
 
5816 +-git.sr.ht/~sircmpwn/core-go v0.0.0-20200820135923-98806e712f5e/go.mod h1:aXSNgRsGoI3tTFKlwD0xm2htbEzKlR2xUm1osRxfhOM=
 
5817 + git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6 h1:Ky6HzcRmbMUxOrWXv04+mb97GkyxO/Nx7v8uJBUdpNk=
 
5818 + git.sr.ht/~sircmpwn/core-go v0.0.0-20201005173246-a9e49d17a1e6/go.mod h1:HpPX22ilJUWKOA4NDhrOcIyblQhdiKHPg4oMJFYdh0Y=
 
5819 ++github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 
5820 ++github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 
5821 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
5822 ++github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 
5823 ++github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
5824 ++github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 
5825 ++github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 
5826 ++github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001 h1:/UMxx5lGDg30aioUL9e7xJnbJfJeX7vhcm57fa5udaI=
 
5827 + github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
 
5828 +-github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
 
5829 +-github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 
5830 ++github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 
5831 ++github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 
5832 ++github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 
5833 ++github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8=
 
5834 ++github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
 
5835 ++github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 
5836 ++github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 
5837 ++github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 
5838 ++github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 
5839 ++github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 
5840 ++github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 
5841 ++github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 
5842 ++github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 
5843 ++github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 
5844 ++github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 
5845 ++github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 
5846 ++github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 
5847 ++github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 
5848 ++github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5849 ++github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5850 ++github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 
5851 ++github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 
5852 + github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 
5853 + github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 
5854 ++github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 
5855 + github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
 
5856 + github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 
5857 ++github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 
5858 ++github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 
5859 ++github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 
5860 ++github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 
5861 ++github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 
5862 ++github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
 
5863 ++github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 
5864 ++github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 
5865 ++github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 
5866 ++github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
 
5867 ++github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
 
5868 ++github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 
5869 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 
5870 + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 
5871 ++github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 
5872 ++github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 
5873 + github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 
5874 + github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
 
5875 + github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
 
5876 ++github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 
5877 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 
5878 +-golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
 
5879 ++golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 
5880 ++golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 
5881 + golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 
5882 ++golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 
5883 ++golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 
5884 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 
5885 ++golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 
5886 ++golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 
5887 ++golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 
5888 ++golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
 
5889 ++golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 
5890 ++golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 
5891 ++golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 
5892 ++golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 
5893 ++golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5894 + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
5895 + golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5896 ++golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5897 ++golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5898 ++golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5899 ++golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5900 ++golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5901 ++golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5902 ++golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5903 ++golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
 
5904 ++golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 
5905 ++golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 
5906 + golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 
5907 ++golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 
5908 ++golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 
5909 ++golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 
5910 ++golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 
5911 ++golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 
5912 ++golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 
5913 ++golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5914 ++golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5915 ++golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5916 ++golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 
5917 ++golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
5918 ++google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 
5919 ++google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 
5920 ++google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 
5921 ++google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 
5922 ++google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 
5923 ++google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 
5924 ++google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 
5925 ++google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
 
5926 ++google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 
5927 ++gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 
5928 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 
5929 ++gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 
5930 ++gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 
5931 ++gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 
5932 ++gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5933 ++gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5934 ++gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 
5935 ++gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 
5936 ++gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 
5937 ++gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 
5938 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 
5942 diff --git a/pkgs/applications/version-management/sourcehut/scm.nix b/pkgs/applications/version-management/sourcehut/scm.nix
 
5943 index 1f385265360..0d7a0cdc782 100644
 
5944 --- a/pkgs/applications/version-management/sourcehut/scm.nix
 
5945 +++ b/pkgs/applications/version-management/sourcehut/scm.nix
 
5948  , fetchFromSourcehut
 
5950  , buildPythonPackage
 
5959  buildPythonPackage rec {
 
5961 -  version = "0.22.9";
 
5962 +  version = "0.22.13";
 
5964    src = fetchFromSourcehut {
 
5965      owner = "~sircmpwn";
 
5968 -    sha256 = "sha256-327G6C8FW+iZx+167D7TQsFtV6FGc8MpMVo9L/cUUqU=";
 
5969 +    sha256 = "sha256-9iRmQBt4Cxr5itTk34b8iDRyXYDHTDfZjV0SIDT/kkM=";
 
5973 +    srht-keys = buildGoModule {
 
5974 +      inherit src version;
 
5975 +      sourceRoot = "source/srht-keys";
 
5976 +      pname = "srht-keys";
 
5977 +      vendorSha256 = "sha256-lQk1dymMCefHMFJhO3yC/muBP/cxI//5Yz991D2YZY4=";
 
5979 +      # What follows is only to update go-redis
 
5980 +      # go.{mod,sum} could be patched directly but that would be less resilient
 
5981 +      # to changes from upstream, and thus harder to maintain the patching
 
5982 +      # while it hasn't been merged upstream.
 
5984 +      overrideModAttrs = old: {
 
5986 +          go get github.com/go-redis/redis/v8
 
5987 +          go get github.com/go-redis/redis@none
 
5990 +        # Pass updated go.{mod,sum} from go-modules to srht-keys's vendor/go.{mod,sum}
 
5992 +          cp --reflink=auto go.* $out
 
5997 +        # Update go-redis to support Unix sockets
 
5998 +        patches/redis-socket/scm/v3-0001-srht-keys-update-go-redis-to-support-Unix-sockets.patch
 
6000 +      patchFlags = ["-p2"];
 
6002 +        cp --reflink=auto *.go vendor/go.* $out
 
6007    nativeBuildInputs = srht.nativeBuildInputs;
 
6008 @@ -25,7 +60,6 @@ buildPythonPackage rec {
 
6016 @@ -33,11 +67,12 @@ buildPythonPackage rec {
 
6019    dontUseSetuptoolsCheck = true;
 
6020 +  pythonImportsCheck = [ "scmsrht" ];
 
6023      homepage = "https://git.sr.ht/~sircmpwn/git.sr.ht";
 
6024      description = "Shared support code for sr.ht source control services.";
 
6025 -    license = licenses.agpl3;
 
6026 +    license = licenses.agpl3Only;
 
6027      maintainers = with maintainers; [ eadwu ];
 
6030 diff --git a/pkgs/applications/version-management/sourcehut/todo.nix b/pkgs/applications/version-management/sourcehut/todo.nix
 
6031 index 85e1f5637b6..88b7a33495a 100644
 
6032 --- a/pkgs/applications/version-management/sourcehut/todo.nix
 
6033 +++ b/pkgs/applications/version-management/sourcehut/todo.nix
 
6036  buildPythonPackage rec {
 
6038 -  version = "0.64.14";
 
6039 +  version = "0.64.28";
 
6041    src = fetchFromSourcehut {
 
6042      owner = "~sircmpwn";
 
6043      repo = "todo.sr.ht";
 
6045 -    sha256 = "sha256-huIAhn6h1F5w5ST4/yBwr82kAzyYwhLu+gpRuOQgnsE=";
 
6046 +    sha256 = "sha256-uClWcR0saH4dUGRRTueLv7T4IBefMVSI5khCdeDBRv4=";
 
6049    nativeBuildInputs = srht.nativeBuildInputs;
 
6050 @@ -42,11 +42,12 @@ buildPythonPackage rec {
 
6053    dontUseSetuptoolsCheck = true;
 
6054 +  pythonImportsCheck = [ "todosrht" ];
 
6057      homepage = "https://todo.sr.ht/~sircmpwn/todo.sr.ht";
 
6058      description = "Ticket tracking service for the sr.ht network";
 
6059 -    license = licenses.agpl3;
 
6060 +    license = licenses.agpl3Only;
 
6061      maintainers = with maintainers; [ eadwu ];
 
6064 diff --git a/pkgs/applications/version-management/sourcehut/update.sh b/pkgs/applications/version-management/sourcehut/update.sh
 
6065 index 156d4cc35e4..6733046d000 100755
 
6066 --- a/pkgs/applications/version-management/sourcehut/update.sh
 
6067 +++ b/pkgs/applications/version-management/sourcehut/update.sh
 
6069  #! /usr/bin/env nix-shell
 
6070  #! nix-shell -i bash -p git mercurial common-updater-scripts
 
6073 -cd "$(dirname "${BASH_SOURCE[0]}")"
 
6074 +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
 
6077 +trap 'rm -rf "$tmp"' EXIT
 
6080    (cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.meta.position" | sed -re 's/^"(.*):[0-9]+"$/\1/')
 
6081 @@ -13,19 +16,18 @@ version() {
 
6085 -  (cd "$root" && nix-instantiate --eval --strict -A "sourcehut.python.pkgs.$1.src.drvAttrs.url" | tr -d '"')
 
6086 +  nix-instantiate --eval --strict --expr " with import $root {}; let src = sourcehut.python.pkgs.$1.drvAttrs.src; in src.url or src.meta.homepage" | tr -d '"'
 
6089  get_latest_version() {
 
6090    src="$(src_url "$1")"
 
6094    if [ "$1" = "hgsrht" ]; then
 
6095 -    hg clone "$src" "$tmp" &> /dev/null
 
6096 +    hg clone "$src" "$tmp" >/dev/null
 
6097      printf "%s" "$(cd "$tmp" && hg log --limit 1 --template '{latesttag}')"
 
6099 -    git clone "$src" "$tmp"
 
6100 -    printf "%s" "$(cd "$tmp" && git describe $(git rev-list --tags --max-count=1))"
 
6101 +    git clone "$src" "$tmp" >/dev/null
 
6102 +    printf "%s" "$(cd "$tmp" && git describe "$(git rev-list --tags --max-count=1)")"
 
6106 @@ -36,19 +38,33 @@ update_version() {
 
6108    (cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
 
6110 +  # Update vendorSha256 of Go modules
 
6111 +  nixFile="${1%srht}".nix
 
6112 +  nixFile="${nixFile/build/builds}"
 
6114 +  while "$retry"; do
 
6116 +    exec < <(exec nix -L build -f "$root" sourcehut.python.pkgs."$1" 2>&1)
 
6117 +    while IFS=' :' read -r origin hash; do
 
6119 +        (expected|specified) oldHash="$hash";;
 
6120 +        (got) sed -i "s|$oldHash|$(nix hash to-sri --type sha256 "$hash")|" "$nixFile"; retry=true; break;;
 
6121 +        (*) printf >&2 "%s\n" "$origin${hash:+:$hash}"
 
6126    git add "$default_nix"
 
6127 -  git commit -m "$1: $version_old -> $version"
 
6128 +  git commit -m "sourcehut.$1: $version_old -> $version"
 
6131 -services=( "srht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
 
6132 -           "metasrht" "pastesrht" "todosrht" "scmsrht" )
 
6134 -# Whether or not a specific service is requested
 
6135 -if [ -n "$1" ]; then
 
6136 -  version="$(get_latest_version "$1")"
 
6137 -  (cd "$root" && update-source-version "sourcehut.python.pkgs.$1" "$version")
 
6138 +if [ $# -gt 0 ]; then
 
6141 -  for service in "${services[@]}"; do
 
6142 -    update_version "$service"
 
6144 +  services=( "srht" "buildsrht" "dispatchsrht" "gitsrht" "hgsrht" "hubsrht" "listssrht" "mansrht"
 
6145 +             "metasrht" "pagessrht" "pastesrht" "todosrht" "scmsrht" )
 
6148 +for service in "${services[@]}"; do
 
6149 +  update_version "$service"
 
6151 diff --git a/pkgs/development/go-modules/generic/default.nix b/pkgs/development/go-modules/generic/default.nix
 
6152 index 3b645f9ce8b..f00ca1984ec 100644
 
6153 --- a/pkgs/development/go-modules/generic/default.nix
 
6154 +++ b/pkgs/development/go-modules/generic/default.nix
 
6155 @@ -71,6 +71,7 @@ let
 
6156      inherit (go) GOOS GOARCH;
 
6158      patches = args.patches or [];
 
6159 +    patchFlags = args.patchFlags or [];
 
6160      preBuild = args.preBuild or "";
 
6161      sourceRoot = args.sourceRoot or "";