Mein WeeChat-Setup: Deklarativ mit Nix, eingeklinkt am Bouncer

Wie ich meinen IRC-Client WeeChat reproduzierbar über ein Nix-Home-Manager-Modul ausrolle und mich darüber in einem Rutsch über meinen Bouncer in alle IRC-Netze klinke

Mal etwas, das nichts mit dem Lab zu tun hat: mein IRC-Client. Ich nutze seit Jahren WeeChat , und wie bei fast allem auf meinen Maschinen gilt auch hier — nichts wird von Hand zusammengeklickt, alles ist reproduzierbar. WeeChat samt Konfiguration kommt deshalb aus einem Home-Manager -Modul und verbindet sich ausschließlich mit meinem Bouncer.

WeeChat als Nix-Modul#

Der Client soll auf jeder meiner Maschinen identisch dastehen — gleiche Plugins, gleiche Skripte, gleiche Tastenbelegung. Genau dafür ist Nix gemacht. Mein Modul baut WeeChat über pkgs.weechat.override mit exakt den Bausteinen, die ich brauche, und nicht mehr:

1weechat = pkgs.weechat.override {
2  configure = { availablePlugins, ... }: {
3    plugins = [ (availablePlugins.python.withPackages (_ps: [ ])) ];
4    scripts = with pkgs.weechatScripts; [ autosort weechat-grep ];
5  };
6};

Das Python-Plugin und die beiden Skripte — autosort zum automatischen Einsortieren der Buffer und weechat-grep zum Durchsuchen der Logs — sind damit Teil des Build-Outputs, nicht etwas, das ich nach der Installation noch nachziehen müsste.

Die Konfiguration selbst lege ich deklarativ ab, indem das Modul den ./config-Baum nach ~/.config/weechat spiegelt:

1xdg.configFile.weechat = {
2  source = lib.cleanSourceWith { src = lib.cleanSource ./config; };
3  recursive = true;
4};

Das Ergebnis: Ein frisch ausgerolltes System hat sofort meinen kompletten WeeChat — Build und irc.conf, buflist.conf, trigger.conf und der ganze Rest. Aktiviert wird das Ganze über einen einzigen Schalter, fr0st.apps.weechat.enable.

Eingeklinkt am Bouncer#

Hier kommt der zweite Teil ins Spiel. WeeChat verbindet sich nie direkt mit Libera.Chat oder hackint, sondern immer nur mit meinem ZNC-Bouncer . Dieser hält die eigentlichen Netzwerk-Verbindungen dauerhaft offen — und routet sie, wie im verlinkten Beitrag beschrieben, über Tor.

Der Trick steckt in der ZNC-typischen Syntax des Passwort-Feldes: Beide „Server“ in meiner irc.conf zeigen auf dieselbe Bouncer-Adresse, und allein das Passwort entscheidet, in welches Netz ZNC die Verbindung weiterreicht — <znc-user>/<netz>:<passwort>:

1libera.addresses  = "${sec.data.bnc_addr}"
2libera.password   = "ff0x/libera:${sec.data.bnc_pass}"
3libera.capabilities = "server-time,znc.in/server-time-iso,znc.in/self-message"
4
5hackint.addresses = "${sec.data.bnc_addr}"
6hackint.password  = "ff0x/hackint:${sec.data.bnc_pass}"

Aus Sicht von WeeChat sind das zwei Server-Verbindungen; tatsächlich landen beide am selben Bouncer, der sie auf die richtigen Netze auffächert. Die znc.in-Capabilities sorgen dafür, dass beim Verbinden die gepufferte History samt korrekter Zeitstempel nachgespielt wird — ich steige also genau dort wieder ein, wo ich aufgehört habe.

flowchart LR WC["WeeChat (Nix)<br/>2× Server-Eintrag"] ZNC["ZNC-Bouncer<br/>ff0x/libera · ff0x/hackint"] LIB["Libera.Chat"] HAK["hackint"] WC -->|"TLS · password = user/netz:pass"| ZNC ZNC -->|"via Tor"| LIB ZNC -->|"via Tor"| HAK
Die eigentlichen Geheimnisse — Bouncer-Adresse und -Passwort — stehen nicht im Klartext in der eingecheckten irc.conf, sondern als ${sec.data.*} aus WeeChats eigenem secured-data-Store (sec.conf), der mit einer Master-Passphrase verschlüsselt ist. So kann der Config-Baum bedenkenlos versioniert werden.

Fazit#

Zwei einfache Ideen, die zusammen erstaunlich bequem sind: Der Client ist ein reproduzierbares Nix-Artefakt, das auf jeder Maschine identisch erscheint, und die Netzwerk-Logik liegt nicht im Client, sondern im Bouncer. Ich starte WeeChat, eine einzige verschlüsselte Verbindung pro Eintrag geht zum Bouncer — und ich bin sofort wieder in allen Kanälen, als wäre ich nie weg gewesen.