diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 89cd015ccfc8..85523621e880 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -90,6 +90,13 @@
services.heisenbridge.
+
+
+ ergochat, a modern
+ IRC with IRCv3 features. Available as
+ services.ergochat.
+
+
PowerDNS-Admin,
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 2554292b6f12..e004de8f9162 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -30,6 +30,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [heisenbridge](https://github.com/hifi/heisenbridge), a bouncer-style Matrix IRC bridge. Available as [services.heisenbridge](options.html#opt-services.heisenbridge.enable).
+- [ergochat](https://ergo.chat), a modern IRC with IRCv3 features. Available as [services.ergochat](options.html#opt-services.ergochat.enable).
+
- [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin), a web interface for the PowerDNS server. Available at [services.powerdns-admin](options.html#opt-services.powerdns-admin.enable).
- [maddy](https://maddy.email), a composable all-in-one mail server. Available as [services.maddy](options.html#opt-services.maddy.enable).
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 438535555856..c46f60105fe6 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -740,6 +740,7 @@
./services/networking/ejabberd.nix
./services/networking/epmd.nix
./services/networking/ergo.nix
+ ./services/networking/ergochat.nix
./services/networking/eternal-terminal.nix
./services/networking/fakeroute.nix
./services/networking/ferm.nix
diff --git a/nixos/modules/services/networking/ergochat.nix b/nixos/modules/services/networking/ergochat.nix
new file mode 100644
index 000000000000..cfaf69fc6139
--- /dev/null
+++ b/nixos/modules/services/networking/ergochat.nix
@@ -0,0 +1,155 @@
+{ config, lib, options, pkgs, ... }: let
+ cfg = config.services.ergochat;
+in {
+ options = {
+ services.ergochat = {
+
+ enable = lib.mkEnableOption "Ergo IRC daemon";
+
+ openFilesLimit = lib.mkOption {
+ type = lib.types.int;
+ default = 1024;
+ description = ''
+ Maximum number of open files. Limits the clients and server connections.
+ '';
+ };
+
+ configFile = lib.mkOption {
+ type = lib.types.path;
+ default = (pkgs.formats.yaml {}).generate "ergo.conf" cfg.settings;
+ defaultText = "generated config file from .settings";
+ description = ''
+ Path to configuration file.
+ Setting this will skip any configuration done via .settings
+ '';
+ };
+
+ settings = lib.mkOption {
+ type = (pkgs.formats.yaml {}).type;
+ description = ''
+ Ergo IRC daemon configuration file.
+ https://raw.githubusercontent.com/ergochat/ergo/master/default.yaml
+ '';
+ default = {
+ network = {
+ name = "testnetwork";
+ };
+ server = {
+ name = "example.com";
+ listeners = {
+ ":6667" = {};
+ };
+ casemapping = "permissive";
+ enforce-utf = true;
+ lookup-hostnames = false;
+ ip-cloaking = {
+ enabled = false;
+ };
+ forward-confirm-hostnames = false;
+ check-ident = false;
+ relaymsg = {
+ enabled = false;
+ };
+ max-sendq = "1M";
+ ip-limits = {
+ count = false;
+ throttle = false;
+ };
+ };
+ datastore = {
+ autoupgrade = true;
+ # this points to the StateDirectory of the systemd service
+ path = "/var/lib/ergo/ircd.db";
+ };
+ accounts = {
+ authentication-enabled = true;
+ registration = {
+ enabled = true;
+ allow-before-connect = true;
+ throttling = {
+ enabled = true;
+ duration = "10m";
+ max-attempts = 30;
+ };
+ bcrypt-cost = 4;
+ email-verification.enabled = false;
+ };
+ multiclient = {
+ enabled = true;
+ allowed-by-default = true;
+ always-on = "opt-out";
+ auto-away = "opt-out";
+ };
+ };
+ channels = {
+ default-modes = "+ntC";
+ registration = {
+ enabled = true;
+ };
+ };
+ limits = {
+ nicklen = 32;
+ identlen = 20;
+ channellen = 64;
+ awaylen = 390;
+ kicklen = 390;
+ topiclen = 390;
+ };
+ history = {
+ enabled = true;
+ channel-length = 2048;
+ client-length = 256;
+ autoresize-window = "3d";
+ autoreplay-on-join = 0;
+ chathistory-maxmessages = 100;
+ znc-maxmessages = 2048;
+ restrictions = {
+ expire-time = "1w";
+ query-cutoff = "none";
+ grace-period = "1h";
+ };
+ retention = {
+ allow-individual-delete = false;
+ enable-account-indexing = false;
+ };
+ tagmsg-storage = {
+ default = false;
+ whitelist = [
+ "+draft/react"
+ "+react"
+ ];
+ };
+ };
+ };
+ };
+
+ };
+ };
+ config = lib.mkIf cfg.enable {
+
+ environment.etc."ergo.yaml".source = cfg.configFile;
+
+ # merge configured values with default values
+ services.ergochat.settings =
+ lib.mapAttrsRecursive (_: lib.mkDefault) options.services.ergochat.settings.default;
+
+ systemd.services.ergochat = {
+ description = "Ergo IRC daemon";
+ wantedBy = [ "multi-user.target" ];
+ # reload is not applying the changed config. further investigation is needed
+ # at some point this should be enabled, since we don't want to restart for
+ # every config change
+ # reloadIfChanged = true;
+ restartTriggers = [ cfg.configFile ];
+ serviceConfig = {
+ ExecStart = "${pkgs.ergochat}/bin/ergo run --conf /etc/ergo.yaml";
+ ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
+ DynamicUser = true;
+ StateDirectory = "ergo";
+ LimitNOFILE = toString cfg.openFilesLimit;
+ };
+ };
+
+ };
+ meta.maintainers = with lib.maintainers; [ lassulus tv ];
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 02f4e80ab7d1..8d6f6d2a5f53 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -125,6 +125,7 @@ in
enlightenment = handleTest ./enlightenment.nix {};
env = handleTest ./env.nix {};
ergo = handleTest ./ergo.nix {};
+ ergochat = handleTest ./ergochat.nix {};
etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
etebase-server = handleTest ./etebase-server.nix {};
diff --git a/nixos/tests/ergochat.nix b/nixos/tests/ergochat.nix
new file mode 100644
index 000000000000..2e9dc55e648e
--- /dev/null
+++ b/nixos/tests/ergochat.nix
@@ -0,0 +1,97 @@
+let
+ clients = [
+ "ircclient1"
+ "ircclient2"
+ ];
+ server = "ergochat";
+ ircPort = 6667;
+ channel = "nixos-cat";
+ iiDir = "/tmp/irc";
+in
+
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+ name = "ergochat";
+ nodes = {
+ "${server}" = {
+ networking.firewall.allowedTCPPorts = [ ircPort ];
+ services.ergochat = {
+ enable = true;
+ settings.server.motd = pkgs.writeText "ergo.motd" ''
+ The default MOTD doesn't contain the word "nixos" in it.
+ This one does.
+ '';
+ };
+ };
+ } // lib.listToAttrs (builtins.map (client: lib.nameValuePair client {
+ imports = [
+ ./common/user-account.nix
+ ];
+
+ systemd.services.ii = {
+ requires = [ "network.target" ];
+ wantedBy = [ "default.target" ];
+
+ serviceConfig = {
+ Type = "simple";
+ ExecPreStartPre = "mkdir -p ${iiDir}";
+ ExecStart = ''
+ ${lib.getBin pkgs.ii}/bin/ii -n ${client} -s ${server} -i ${iiDir}
+ '';
+ User = "alice";
+ };
+ };
+ }) clients);
+
+ testScript =
+ let
+ msg = client: "Hello, my name is ${client}";
+ clientScript = client: [
+ ''
+ ${client}.wait_for_unit("network.target")
+ ${client}.systemctl("start ii")
+ ${client}.wait_for_unit("ii")
+ ${client}.wait_for_file("${iiDir}/${server}/out")
+ ''
+ # look for the custom text in the MOTD.
+ ''
+ ${client}.wait_until_succeeds("grep 'nixos' ${iiDir}/${server}/out")
+ ''
+ # wait until first PING from server arrives before joining,
+ # so we don't try it too early
+ ''
+ ${client}.wait_until_succeeds("grep 'PING' ${iiDir}/${server}/out")
+ ''
+ # join ${channel}
+ ''
+ ${client}.succeed("echo '/j #${channel}' > ${iiDir}/${server}/in")
+ ${client}.wait_for_file("${iiDir}/${server}/#${channel}/in")
+ ''
+ # send a greeting
+ ''
+ ${client}.succeed(
+ "echo '${msg client}' > ${iiDir}/${server}/#${channel}/in"
+ )
+ ''
+ # check that all greetings arrived on all clients
+ ] ++ builtins.map (other: ''
+ ${client}.succeed(
+ "grep '${msg other}$' ${iiDir}/${server}/#${channel}/out"
+ )
+ '') clients;
+
+ # foldl', but requires a non-empty list instead of a start value
+ reduce = f: list:
+ builtins.foldl' f (builtins.head list) (builtins.tail list);
+ in ''
+ start_all()
+ ${server}.systemctl("status ergochat")
+ ${server}.wait_for_open_port(${toString ircPort})
+
+ # run clientScript for all clients so that every list
+ # entry is executed by every client before advancing
+ # to the next one.
+ '' + lib.concatStrings
+ (reduce
+ (lib.zipListsWith (cs: c: cs + c))
+ (builtins.map clientScript clients));
+})
diff --git a/pkgs/servers/irc/ergochat/default.nix b/pkgs/servers/irc/ergochat/default.nix
new file mode 100644
index 000000000000..1aa5f5158aa8
--- /dev/null
+++ b/pkgs/servers/irc/ergochat/default.nix
@@ -0,0 +1,26 @@
+{ buildGo117Module, fetchFromGitHub, lib, nixosTests }:
+
+buildGo117Module rec {
+ pname = "ergo";
+ version = "2.9.1";
+
+ src = fetchFromGitHub {
+ owner = "ergochat";
+ repo = "ergo";
+ rev = "v${version}";
+ sha256 = "sha256-RxsmkTfHymferS/FRW0sLnstKfvGXkW6cEb/JbeS4lc=";
+ };
+
+ vendorSha256 = null;
+
+ passthru.tests.ergochat = nixosTests.ergochat;
+
+ meta = {
+ changelog = "https://github.com/ergochat/ergo/blob/v${version}/CHANGELOG.md";
+ description = "A modern IRC server (daemon/ircd) written in Go";
+ homepage = "https://github.com/ergochat/ergo";
+ license = lib.licenses.mit;
+ maintainers = with lib.maintainers; [ lassulus tv ];
+ platforms = lib.platforms.linux;
+ };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 782f072bf6f8..f6cb53114a47 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -20953,6 +20953,8 @@ with pkgs;
gn = gn1924;
};
+ ergochat = callPackage ../servers/irc/ergochat { };
+
etcd = etcd_3_3;
etcd_3_3 = callPackage ../servers/etcd/3.3.nix { };
etcd_3_4 = callPackage ../servers/etcd/3.4.nix { };