about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--machines/aszlig/dnyarri.nix10
-rw-r--r--machines/aszlig/slylandro.nix21
-rw-r--r--machines/devhell/herja.nix2
-rw-r--r--machines/profpatsch/haku.nix293
-rw-r--r--machines/profpatsch/pkgs.nix2
-rw-r--r--machines/sternenseemann/base-laptop.nix10
-rw-r--r--machines/sternenseemann/base.nix19
-rw-r--r--machines/sternenseemann/wolfgang.nix5
-rw-r--r--modules/core/tests.nix16
-rw-r--r--modules/programs/foot/default.nix69
-rw-r--r--modules/user/aszlig/profiles/base.nix1
-rw-r--r--modules/user/aszlig/profiles/workstation/default.nix15
-rw-r--r--modules/user/aszlig/profiles/workstation/packages.nix1
-rw-r--r--modules/user/aszlig/programs/flameshot/default.nix1
-rw-r--r--modules/user/devhell/profiles/base.nix8
-rw-r--r--modules/user/devhell/profiles/packages.nix11
-rw-r--r--modules/user/devhell/profiles/services.nix10
-rw-r--r--modules/user/profpatsch/programs/scanning.nix2
-rw-r--r--modules/user/profpatsch/services/gonic.nix1
-rw-r--r--modules/user/sternenseemann/profiles/desktop-sway.nix32
-rw-r--r--modules/user/sternenseemann/services/sway.nix3
-rw-r--r--pkgs/aszlig/gpodder/default.nix6
-rw-r--r--pkgs/aszlig/psi/config.patch50
-rw-r--r--pkgs/aszlig/psi/default.nix19
-rw-r--r--pkgs/profpatsch/default.nix68
-rw-r--r--pkgs/profpatsch/display-infos/default.nix10
-rw-r--r--pkgs/profpatsch/xdg-open/default.nix11
-rw-r--r--pkgs/profpatsch/xdg-open/purs/Config.purs8
-rw-r--r--pkgs/sternenseemann/scripts/default.nix3
-rw-r--r--pkgs/tvl/default.nix2
-rw-r--r--tests/aszlig/programs/psi.nix2
31 files changed, 501 insertions, 210 deletions
diff --git a/machines/aszlig/dnyarri.nix b/machines/aszlig/dnyarri.nix
index 64fccb9a..116ae053 100644
--- a/machines/aszlig/dnyarri.nix
+++ b/machines/aszlig/dnyarri.nix
@@ -22,6 +22,12 @@ in {
 
   nix.settings.max-jobs = 24;
 
+  # XXX: This machine has a pretty complicated audio setup, so until this works
+  #      properly with PipeWire, let's stay with PulseAudio for now.
+  services.pipewire.enable = lib.mkOverride 90 false;
+  hardware.pulseaudio.enable = true;
+  hardware.pulseaudio.package = pkgs.pulseaudioFull;
+
   hardware.printers.ensureDefaultPrinter = "Bunti";
   hardware.printers.ensurePrinters = lib.singleton {
     name = "Bunti";
@@ -76,9 +82,7 @@ in {
     "/" = {
       label = "dnyarri-root";
       fsType = "btrfs";
-      options = [
-        "autodefrag" "space_cache" "compress=zstd" "noatime" "discard=async"
-      ];
+      options = [ "space_cache" "compress=zstd" "noatime" "discard=async" ];
     };
   };
 
diff --git a/machines/aszlig/slylandro.nix b/machines/aszlig/slylandro.nix
index 85bd8bc6..c53cad05 100644
--- a/machines/aszlig/slylandro.nix
+++ b/machines/aszlig/slylandro.nix
@@ -28,7 +28,7 @@
   vuizvui.hardware.tuxedo.pulse15.gen2.enable = true;
 
   services.xserver.dpi = 188;
-  services.xserver.xkbOptions = "caps:none";
+  services.xserver.xkb.options = "caps:none";
 
   networking.hostName = "slylandro";
   networking.wireless.enable = lib.mkForce true;
@@ -36,17 +36,6 @@
 
   location.provider = "geoclue2";
 
-  # XXX: Only on Slylandro for now since Dnyarri has a more complicated setup.
-  hardware.pulseaudio.enable = lib.mkForce false;
-  security.rtkit.enable = true;
-  services.pipewire = {
-    enable = true;
-    alsa.enable = true;
-    alsa.support32Bit = true;
-    pulse.enable = true;
-    jack.enable = true;
-  };
-
   # This is because the "primary" option below is only supported for the
   # scripted networking configuration.
   systemd.network.networks."40-enp1s0" = {
@@ -71,9 +60,7 @@
   fileSystems."/" = {
     device = "/dev/disk/by-uuid/87a842a8-e2c5-45b6-8e67-58fec33b5eee";
     fsType = "btrfs";
-    options = [
-      "autodefrag" "space_cache=v2" "compress=zstd" "noatime" "discard=async"
-    ];
+    options = [ "space_cache=v2" "compress=zstd" "noatime" "discard=async" ];
   };
 
   swapDevices = lib.singleton {
@@ -83,9 +70,9 @@
   services.tlp.enable = true;
 
   services.xserver.videoDrivers = [ "amdgpu" ];
-  services.xserver.libinput.enable = true;
 
-  services.xserver.libinput.touchpad = {
+  services.libinput.enable = true;
+  services.libinput.touchpad = {
     clickMethod = "clickfinger";
     sendEventsMode = "disabled-on-external-mouse";
     middleEmulation = false;
diff --git a/machines/devhell/herja.nix b/machines/devhell/herja.nix
index e5f99cd1..8db14649 100644
--- a/machines/devhell/herja.nix
+++ b/machines/devhell/herja.nix
@@ -31,7 +31,7 @@
       enable = true;
       emulateWheel = true;
     };
-    opengl = {
+    graphics = {
       enable = true;
       extraPackages = with pkgs; [
         libvdpau-va-gl
diff --git a/machines/profpatsch/haku.nix b/machines/profpatsch/haku.nix
index 026b32a7..9d4dc536 100644
--- a/machines/profpatsch/haku.nix
+++ b/machines/profpatsch/haku.nix
@@ -5,17 +5,24 @@ let
   myPkgs = import ./pkgs.nix { inherit pkgs lib myLib; };
 
   hakuHostName = "haku.profpatsch.de";
+  testHostName = "test.profpatsch.de";
+  matrixHostName = "matrix.decentsoftwa.re";
 
   youtube2audiopodcastPort = 1339;
   youtube2audiopodcastSubdir = "/halp";
 
   sshPort = 7001;
   warpspeedPort = 1338;
+  httzipPort = 7070;
+  openlabToolsPort = 9099;
   wireguardPortUdp = 6889;
   tailscaleInterface = "tailscale0";
-  tailscaleAddress = "100.76.60.85";
+  tailscaleAddress = "100.122.12.129";
   gonicPortTailscale = 4747;
+  whatcdResolverPortTailscale = 9093;
+  whatcdResolverJaegerPortTailscale = 16686;
   sambaPortTailscale = 445;
+  dentritePort = 8008;
 
   ethernetInterface = "enp0s20";
   wireguard = {
@@ -94,15 +101,34 @@ in
       # pkgs.vuizvui.profpatsch.warpspeed # trivial http file server
     ];
 
-    # users.groups.data-seeding = {};
+    users.groups.data-seeding = {};
+    users.groups.whatcd-resolver = {};
+    users.groups.openlab-tools = {};
 
     users.users = {
       root.openssh.authorizedKeys.keys = [ myKey ];
 
-      # rtorrent = {
-      #   isNormalUser = true;
-      #   extraGroups = [ "data-seeding" ];
-      # };
+      seed = {
+        isNormalUser = true;
+        extraGroups = [ "data-seeding" ];
+        openssh.authorizedKeys.keys = [ myKey ];
+      };
+      zipped-transmission = {
+        isSystemUser = true;
+        group = "transmission";
+      };
+      whatcd-resolver = {
+        isSystemUser = true;
+        home = "/var/lib/whatcd-resolver";
+        createHome = true;
+        group = "whatcd-resolver";
+      };
+      openlab-tools = {
+        isSystemUser = true;
+        home = "/var/lib/openlab-tools";
+        createHome = true;
+        group = "openlab-tools";
+      };
 
       # youtube2audiopodcast = {
       #   isSystemUser = true;
@@ -160,16 +186,63 @@ in
     # systemd.services.samba-smbd.wants = [ "tailscaled.service" ];
     # systemd.services.samba-smbd.after = [ "tailscaled.service" ];
 
-    # systemd.services.warpspeed =
-    #   let user = config.users.users.rtorrent;
-    #   in {
-    #     description = "internally served public files (see nginx)";
-    #     wantedBy = [ "default.target" ];
-    #     serviceConfig.WorkingDirectory = "${user.home}/public";
-    #     # *6: all hosts, v6 preferred
-    #     script = ''${pkgs.vuizvui.profpatsch.warpspeed}/bin/warpspeed "*6" ${toString warpspeedPort}'';
-    #     serviceConfig.User = config.users.users.rtorrent.name;
-    #   };
+    systemd.services.warpspeed =
+      let user = config.users.users.seed;
+      in {
+        description = "internally served zipped stuff (see nginx)";
+        wantedBy = [ "default.target" ];
+        serviceConfig.WorkingDirectory = "${user.home}/public";
+        # *6: all hosts, v6 preferred
+        script = ''${pkgs.vuizvui.profpatsch.warpspeed}/bin/warpspeed "*6" ${toString warpspeedPort}'';
+        serviceConfig.User = user.name;
+      };
+
+    # TODO: this is horrible lol
+    systemd.services.httzip =
+      let user = config.users.users.zipped-transmission;
+      in {
+        description = "internally served public files (see nginx)";
+        wantedBy = [ "default.target" ];
+        serviceConfig.WorkingDirectory = "/var/lib/transmission/Downloads";
+        script = ''${pkgs.vuizvui.profpatsch.tvl.users.Profpatsch.httzip}'';
+        serviceConfig.User = user.name;
+      };
+
+
+    # TODO: this is horrible lol
+    systemd.services.whatcd-resolver =
+      let user = config.users.users.whatcd-resolver;
+      in {
+        description = "what?";
+        wantedBy = [ "default.target" ];
+        serviceConfig.WorkingDirectory = "/var/lib/whatcd-resolver";
+        script = "${pkgs.vuizvui.profpatsch.writeExecline "run-whatcd-resolver-jaeger" {} [
+          "envfile" "/var/lib/whatcd-resolver/whatcd-resolver-env"
+          pkgs.vuizvui.profpatsch.tvl.users.Profpatsch.whatcd-resolver
+        ]}";
+        serviceConfig.User = user.name;
+      };
+    systemd.services.whatcd-resolver-jaeger =
+      let user = config.users.users.whatcd-resolver;
+      in {
+        description = "what? jaeger";
+        wantedBy = [ "default.target" "whatcd-resolver.service" ];
+        serviceConfig.WorkingDirectory = "/var/lib/whatcd-resolver/jaeger";
+        # webui: 16686, otel: 4318
+        script = ''${pkgs.vuizvui.profpatsch.jaeger}/bin/jaeger-all-in-one'';
+        serviceConfig.User = user.name;
+      };
+
+    # TODO: this is horrible lol
+    systemd.services.openlab-tools =
+      let user = config.users.users.openlab-tools;
+      in {
+        description = "tooling for openlabs";
+        wantedBy = [ "default.target" ];
+        serviceConfig.WorkingDirectory = "/var/lib/openlab-tools";
+        script = ''${pkgs.vuizvui.profpatsch.tvl.users.Profpatsch.openlab-tools}'';
+        serviceConfig.User = user.name;
+      };
 
     # systemd.services.youtube2audiopodcast =
     #   let user = config.users.users.youtube2audiopodcast;
@@ -187,35 +260,74 @@ in
     security.acme.acceptTerms = true;
     security.acme.defaults.email = "mail@profpatsch.de";
 
-    # services.nginx = {
-    #   enable = true;
-    #   virtualHosts.${hakuHostName} = {
-    #     forceSSL = true;
-    #     enableACME = true;
-    #     locations."/pub/" = {
-    #       proxyPass = "http://127.0.0.1:${toString warpspeedPort}/";
-    #     };
-    #     locations."${youtube2audiopodcastSubdir}/" = {
-    #       proxyPass = "http://127.0.0.1:${toString youtube2audiopodcastPort}/";
-    #     };
-    #     locations."/".root =
-    #       let lojbanistanSrc = pkgs.fetchFromGitHub {
-    #         owner = "lojbanistan";
-    #         repo = "lojbanistan.de";
-    #         rev = "ef02aa8f074d0d5209839cd12ba7a67685fdaa05";
-    #         sha256 = "1hr2si73lam463pcf25napfbk0zb30kgv3ncc0ahv6wndjpsvg7z";
-    #       };
-    #       in pkgs.runCommandLocal "lojbanistan-www" {} ''
-    #         mkdir $out
-    #         echo "coi do" > $out/index.html
-    #         ${pkgs.imagemagick}/bin/convert \
-    #           ${lojbanistanSrc}/design/flag-of-lojbanistan-icon.svg \
-    #           -define icon:auto-resize=64,48,32,16 \
-    #           $out/favicon.ico
-    #       '';
-    #     serverAliases = [ "lojbanistan.de" ];
-    #   };
-    # };
+    services.nginx = {
+      enable = true;
+      virtualHosts.${hakuHostName} = {
+        forceSSL = true;
+        enableACME = true;
+        locations."/public/" = {
+          proxyPass = "http://127.0.0.1:${toString warpspeedPort}/";
+        };
+        locations."/zipped/" = {
+          proxyPass = "http://127.0.0.1:${toString httzipPort}/";
+        };
+        locations."/openlab-tools/" = {
+          proxyPass = "http://127.0.0.1:${toString openlabToolsPort}/";
+        };
+        # locations."${youtube2audiopodcastSubdir}/" = {
+        #   proxyPass = "http://127.0.0.1:${toString youtube2audiopodcastPort}/";
+        # };
+        locations."/".root =
+          let lojbanistanSrc = pkgs.fetchFromGitHub {
+            owner = "lojbanistan";
+            repo = "lojbanistan.de";
+            rev = "ef02aa8f074d0d5209839cd12ba7a67685fdaa05";
+            sha256 = "1hr2si73lam463pcf25napfbk0zb30kgv3ncc0ahv6wndjpsvg7z";
+          };
+          in pkgs.runCommandLocal "lojbanistan-www" {} ''
+            mkdir $out
+            echo "coi do" > $out/index.html
+            ${pkgs.imagemagick}/bin/convert \
+              ${lojbanistanSrc}/design/flag-of-lojbanistan-icon.svg \
+              -define icon:auto-resize=64,48,32,16 \
+              $out/favicon.ico
+          '';
+        serverAliases = [ "lojbanistan.de" ];
+      };
+      virtualHosts.${testHostName} = {
+        forceSSL = true;
+        enableACME = true;
+        locations."/" = {
+          proxyPass = "http://shiki:9999";
+          extraConfig = ''
+            # forward original host so we can validate mastodon http header signatures
+            proxy_set_header Host $host;
+          '';
+        };
+      };
+      virtualHosts.${matrixHostName} = {
+        forceSSL = true;
+        enableACME = true;
+        locations."/" = {
+          proxyPass = "http://localhost:${toString dentritePort}";
+          extraConfig = ''
+            # forward original host (necessary?)
+            proxy_set_header Host $host;
+          '';
+        };
+      };
+      virtualHosts.${"decentsoftwa.re"} = {
+        forceSSL = true;
+        enableACME = true;
+        locations."/.well-known/matrix/".root = pkgs.linkFarm "well-known-decentsoftwa.re-matrix" [
+          { name = ".well-known/matrix/server";
+            path = pkgs.writers.writeJSON "matrix-server-well-known" {
+              "m.server" = "matrix.decentsoftwa.re:443";
+            };
+          }
+        ];
+      };
+    };
 
     networking = {
       hostName = "haku";
@@ -227,7 +339,7 @@ in
       firewall = {
         allowedTCPPorts = [
           80 443
-          6882
+          # 6882
           1337 2342 4223
           60100
         ];
@@ -241,20 +353,97 @@ in
         interfaces.${tailscaleInterface} = {
           allowedTCPPorts = [
             gonicPortTailscale
-            sambaPortTailscale
+            whatcdResolverPortTailscale
+            whatcdResolverJaegerPortTailscale
+            # sambaPortTailscale
           ];
         };
       };
 
-      nameservers = [
-        "62.210.16.6"
-        "62.210.16.7"
-      ];
+      # nameservers = [
+      #   "62.210.16.6"
+      #   "62.210.16.7"
+      # ];
     };
 
     services.tailscale = {
       enable = true;
-      # interfaceName = tailscaleInterface;
+      interfaceName = tailscaleInterface;
+    };
+
+    services.transmission = {
+      enable = true;
+      user = "transmission";
+      group = "transmission";
+      settings = {
+        rpc-port = 9091;
+        peer-port-random-on-start = true;
+        peer-port-random-low = 50000;
+        peer-port-random-high = 50010;
+      };
+      openFirewall = true;
+      openRPCPort = false;
+    };
+
+
+    services.dendrite =
+      let database = {
+        connection_string = "postgresql:///dendrite?host=/run/postgresql";
+        max_open_conns = 90;
+        max_idle_conns = 5;
+        conn_max_lifetime = (-1);
+      };
+      in {
+        enable = true;
+        httpPort = 8008;
+
+        loadCredential = [ "matrix-key:/var/lib/dendrite/matrix-key" ];
+        settings.global.private_key = "$CREDENTIALS_DIRECTORY/matrix-key";
+        settings.global.server_name = "decentsoftwa.re";
+        settings.global.database = database;
+        settings.app_service_api.database = database;
+        settings.federation_api.database = database;
+        settings.key_server.database = database;
+        settings.media_api.database = database;
+        settings.mscs.database = database;
+        settings.relay_api.database = database;
+        settings.room_server.database = database;
+        settings.sync_api.database = database;
+        settings.user_api.account_database.database = database;
+        settings.user_api.device_database.database = database;
+        settings.sync_api.search.enable = true;
+
+        settings.logging = [ { type = "std"; level = "debug"; } ];
+
+        # shared secret config
+        openRegistration = false;
+        environmentFile = "/var/lib/dendrite/registration_secret";
+        settings.client_api.registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
+      };
+    systemd.services.dendrite = {
+      after = [ "postgresql.service" ];
+      serviceConfig = {
+        User = "dendrite";
+        Group = "dendrite";
+      };
+    };
+
+    services.postgresql = {
+      enable = true;
+      enableTCPIP = false;
+      package = pkgs.postgresql_15;
+
+      ensureDatabases = [
+        "dendrite"
+      ];
+      ensureUsers = [
+        {
+          name = "dendrite";
+          ensureDBOwnership = true;
+        }
+
+      ];
     };
   };
+
 }
diff --git a/machines/profpatsch/pkgs.nix b/machines/profpatsch/pkgs.nix
index b54b249b..f576259b 100644
--- a/machines/profpatsch/pkgs.nix
+++ b/machines/profpatsch/pkgs.nix
@@ -6,7 +6,7 @@ assert withUnfree -> unfreeAndNonDistributablePkgs != null;
 
 let
 
-  mpv = pkgs.wrapMpv pkgs.mpv-unwrapped {
+  mpv = pkgs.mpv.override {
     scripts = [
       (unfreeAndNonDistributablePkgs.mpvScripts.convert)
       (pkgs.mpvScripts.mpris)
diff --git a/machines/sternenseemann/base-laptop.nix b/machines/sternenseemann/base-laptop.nix
index 50cb43a8..9a4388a1 100644
--- a/machines/sternenseemann/base-laptop.nix
+++ b/machines/sternenseemann/base-laptop.nix
@@ -46,8 +46,7 @@
         lowdown
         lynx
         zip unzip
-        stow
-        silver-searcher
+        ripgrep
         nmap
         ffmpeg graphicsmagick
         pavucontrol
@@ -58,6 +57,13 @@
       variables = {
         EDITOR = "${emacs}/bin/emacsclient";
         VISUAL = "${emacs}/bin/emacsclient";
+        RIPGREP_CONFIG_PATH = pkgs.writeText "ripgreprc" ''
+          --max-columns=150
+          --max-columns-preview
+          --smart-case
+          --hidden
+          --glob=!.git/*
+        '';
       };
     };
 
diff --git a/machines/sternenseemann/base.nix b/machines/sternenseemann/base.nix
index ac555e24..7eb977fc 100644
--- a/machines/sternenseemann/base.nix
+++ b/machines/sternenseemann/base.nix
@@ -131,5 +131,24 @@ in {
       GIT_PAGER = bins.less;
       LESS = "-R";
     };
+
+    environment.etc."gitconfig".text = ''
+      [user]
+          email = sternenseemann@systemli.org
+          name = sternenseemann
+      [push]
+          default = matching
+      [pull]
+          rebase = true
+      [init]
+          defaultBranch = canon
+      [sendemail]
+          smtpEncryption = tls
+          smtpServer = mail.systemli.org
+          smtpUser = sternenseemann@systemli.org
+          smtpServerPort = 587
+      [merge]
+          conflictstyle = diff3
+    '';
   };
 }
diff --git a/machines/sternenseemann/wolfgang.nix b/machines/sternenseemann/wolfgang.nix
index 66779e4c..9e47e4a7 100644
--- a/machines/sternenseemann/wolfgang.nix
+++ b/machines/sternenseemann/wolfgang.nix
@@ -8,6 +8,7 @@ let
 
 in {
   imports = [
+    ((import ../../nixos-hardware-path.nix) + "/lenovo/thinkpad/x270")
     ./base-laptop.nix
     ./wireguard.nix
     ./user-lukas.nix
@@ -51,6 +52,7 @@ in {
   };
 
   boot.loader.systemd-boot.enable = true;
+  boot.loader.systemd-boot.memtest86.enable = true;
   boot.loader.efi.canTouchEfiVariables = true;
 
   networking.hostName = "wolfgang";
@@ -84,8 +86,7 @@ in {
     gpxsee
     msr-tools
     quasselClient
-    # hunspell
-    # (with hunspellDicts; [ de-de en-gb-large en-us ])
+    anki
   ];
 
   environment.variables = {
diff --git a/modules/core/tests.nix b/modules/core/tests.nix
index 0926bc5a..abfaa590 100644
--- a/modules/core/tests.nix
+++ b/modules/core/tests.nix
@@ -5,7 +5,13 @@ let
 
   isLatestKernel = config.boot.kernelPackages.kernel.version
                 == pkgs.linuxPackages_latest.kernel.version;
-  wgTestSuffix = "linux-${if isLatestKernel then "latest" else "5_4"}";
+  # Assumes nixpkgs has a test for the latest and default kernel
+  wgTestSuffix = "linux-" + (
+    if isLatestKernel
+    then "latest"
+    else lib.replaceStrings [ "." ] [ "_" ]
+      (lib.versions.majorMinor pkgs.linuxPackages.kernel.version)
+  );
 
   mkTest = attrs: if attrs.check then attrs.paths or [ attrs.path ] else [];
 
@@ -828,8 +834,12 @@ let
       path  = ["nixos" "yggdrasil"];
     }
     { check = config.boot.supportedFilesystems.zfs or false
-           && config.boot.zfs.package.version == pkgs.zfs.version;
-      path  = ["nixos" "zfs" "stable"];
+           && config.boot.zfs.package.version == pkgs.zfs_2_1.version;
+      path  = ["nixos" "zfs" "series_2_1"];
+    }
+    { check = config.boot.supportedFilesystems.zfs or false
+           && config.boot.zfs.package.version == pkgs.zfs_2_2.version;
+      path  = ["nixos" "zfs" "series_2_2"];
     }
     { check = config.boot.supportedFilesystems.zfs or false
            && config.boot.zfs.package.version == pkgs.zfs_unstable.version;
diff --git a/modules/programs/foot/default.nix b/modules/programs/foot/default.nix
index 5e10f7f8..92af6dbc 100644
--- a/modules/programs/foot/default.nix
+++ b/modules/programs/foot/default.nix
@@ -12,10 +12,9 @@ let
 
   cfg = config.vuizvui.programs.foot;
 
-  # We don't allow null, since we use null as
-  # a “fall back to foot's defaults” value for defined
-  # options in the freeform module so no null may be
-  # present in the resulting ini file.
+  # We don't allow null, since we use null as a “fall back to foot's defaults”
+  # value for defined options in the freeform module so no null may be present
+  # in the resulting ini file.
   iniAtom = with lib.types; (oneOf [
     bool
     int
@@ -25,27 +24,8 @@ let
     description = "INI atom (bool, int, float or string)";
   };
 
-  # pkgs.formats.ini doesn't allow top-level bindings
-  # without a section, so we have to wrap it a bit
   # TODO(sterni): multiple binds
-  format = {
-    type = with lib.types;
-      (attrsOf (either iniAtom (attrsOf iniAtom))) // {
-        description = ''
-          attribute set of either top-level INI atoms (bool, int, float or string) or attribute sets (sections) of INI atoms
-        '';
-      };
-    generate = name: value:
-      let
-        isSection = builtins.isAttrs;
-        topLevel = lib.filterAttrs (_: v: !(isSection v)) value;
-        sections = lib.filterAttrs (_: v: isSection v) value;
-      in
-        pkgs.writeText name ''
-          ${toKeyValue {} topLevel}
-          ${toINI {} sections}
-        '';
-  };
+  format = pkgs.formats.ini {};
 
   prettyPrint = lib.generators.toPretty {};
 
@@ -86,7 +66,9 @@ let
         else assert (wellFormedFontSet font);
           "${font.font}${formatOptions (font.options or {})}";
     in
-      lib.concatMapStringsSep "," fontconfigFont fonts;
+      if builtins.isNull fonts
+      then null # indicates default
+      else lib.concatMapStringsSep "," fontconfigFont fonts;
 
   mkFontOption = name: lib.mkOption {
     type = with lib.types; nullOr (nonEmptyListOf (either str attrs));
@@ -109,6 +91,7 @@ let
       "monospace"
     ]);
     default = null;
+    apply = buildIniFontList;
   };
 
   commandBindOptions = [
@@ -127,7 +110,9 @@ let
       "command bind set must contain a cmd and a bind attr: ${prettyPrint exampleCommandBindSet}";
 
   buildIniCommandBind = bind:
-    if builtins.isString bind
+    if builtins.isNull bind
+    then null # indicates default
+    else if builtins.isString bind
     then bind
     else assert wellformedCommandBindSet bind;
       "[${bind.cmd}] ${bind.bind}";
@@ -141,23 +126,10 @@ let
       '';
       example = lib.literalExample (prettyPrint exampleCommandBindSet);
       default = null;
+      apply = buildIniCommandBind;
     };
 
-  # convert some fancy options we support to a format formats.ini can deal
-  # with and remove all optional options (in this case options which default
-  # to null), so we don't have to track upstreams defaults, but foot can
-  # decide for itself while we still can treat some options specially.
-  iniReady = settings:
-    let
-      withoutNulls =
-        lib.filterAttrsRecursive (_: x: x != null) settings;
-      attrTransforms =
-        (lib.genAttrs fontOptions (n: (_: buildIniFontList))) // {
-          key-bindings =
-            lib.genAttrs commandBindOptions (n: (_: buildIniCommandBind));
-        };
-    in
-      mapAttrsByAttrs withoutNulls attrTransforms;
+  withoutNulls = lib.filterAttrsRecursive (_: x: x != null);
 
 in {
   options.vuizvui.programs.foot = {
@@ -166,7 +138,9 @@ in {
     settings = lib.mkOption {
       type = lib.types.submodule {
         freeformType = format.type;
-        options = (lib.genAttrs fontOptions mkFontOption) // {
+        options = {
+          main =
+            lib.genAttrs fontOptions mkFontOption;
           key-bindings =
             lib.genAttrs commandBindOptions mkCommandBindOption;
         };
@@ -199,6 +173,15 @@ in {
     environment.systemPackages = [ pkgs.foot pkgs.foot.terminfo ];
 
     environment.etc."xdg/foot/foot.ini".source =
-      format.generate "foot.ini" (iniReady cfg.settings);
+      # null indicates default value for defined options. We need to remove
+      # those from the final config so foot will use its default.
+      format.generate "foot.ini" (withoutNulls cfg.settings);
+
+    # TODO(sterni): bash, zsh
+    programs.fish = lib.mkIf config.programs.fish.enable {
+      interactiveShellInit = ''
+        source "${pkgs.path + "/nixos/modules/programs/foot/config.fish"}"
+      '';
+    };
   };
 }
diff --git a/modules/user/aszlig/profiles/base.nix b/modules/user/aszlig/profiles/base.nix
index c84e37ae..b84fe05b 100644
--- a/modules/user/aszlig/profiles/base.nix
+++ b/modules/user/aszlig/profiles/base.nix
@@ -61,6 +61,7 @@ in {
 
     environment.systemPackages = with pkgs; [
       binutils
+      bpftrace
       cacert
       ddrescue
       file
diff --git a/modules/user/aszlig/profiles/workstation/default.nix b/modules/user/aszlig/profiles/workstation/default.nix
index 586701c1..d6e264fa 100644
--- a/modules/user/aszlig/profiles/workstation/default.nix
+++ b/modules/user/aszlig/profiles/workstation/default.nix
@@ -49,14 +49,17 @@ in {
 
     vuizvui.lazyPackages = import ./lazy-packages.nix pkgs;
 
-    hardware = {
-      pulseaudio.enable = true;
-      pulseaudio.package = pkgs.pulseaudioFull;
-      opengl = {
-        driSupport32Bit = true;
-      };
+    security.rtkit.enable = true;
+    services.pipewire = {
+      enable = true;
+      alsa.enable = true;
+      alsa.support32Bit = true;
+      pulse.enable = true;
+      jack.enable = true;
     };
 
+    hardware.graphics.enable32Bit = true;
+
     fonts = {
       fontDir.enable = true;
       enableGhostscriptFonts = true;
diff --git a/modules/user/aszlig/profiles/workstation/packages.nix b/modules/user/aszlig/profiles/workstation/packages.nix
index 8beda8fc..83e7eec9 100644
--- a/modules/user/aszlig/profiles/workstation/packages.nix
+++ b/modules/user/aszlig/profiles/workstation/packages.nix
@@ -48,7 +48,6 @@ pkgs: with pkgs; [
   openssl
   p7zip
   pavucontrol
-  pulseaudio
   vuizvui.aszlig.pvolctrl
   pv
   python3
diff --git a/modules/user/aszlig/programs/flameshot/default.nix b/modules/user/aszlig/programs/flameshot/default.nix
index 51799aa5..f261a8ce 100644
--- a/modules/user/aszlig/programs/flameshot/default.nix
+++ b/modules/user/aszlig/programs/flameshot/default.nix
@@ -10,7 +10,6 @@ let
     drawThickness = 2;
     savePath = "$HOME/screenshots";
     savePathFixed = true;
-    checkForUpdates = false;
   };
 
 in {
diff --git a/modules/user/devhell/profiles/base.nix b/modules/user/devhell/profiles/base.nix
index 34c54270..ecb1fe41 100644
--- a/modules/user/devhell/profiles/base.nix
+++ b/modules/user/devhell/profiles/base.nix
@@ -32,8 +32,8 @@ in {
     hardware = {
       enableAllFirmware = true;
       nitrokey.enable = true;
-      opengl = {
-        driSupport32Bit = true;
+      graphics = {
+        enable32Bit = true;
       };
     };
 
@@ -97,7 +97,7 @@ in {
         startAgent = false;
       };
       bash = {
-        enableCompletion = true;
+        completion.enable = true;
       };
       singularity = {
         enable = true;
@@ -108,7 +108,7 @@ in {
       starship = {
         enable = true;
         settings = {
-          command_timeout = 1000;
+          command_timeout = 2000;
           nix_shell.disabled = false;
         };
       };
diff --git a/modules/user/devhell/profiles/packages.nix b/modules/user/devhell/profiles/packages.nix
index 510634de..f198ca1a 100644
--- a/modules/user/devhell/profiles/packages.nix
+++ b/modules/user/devhell/profiles/packages.nix
@@ -43,14 +43,17 @@ in {
 
     environment.systemPackages = with pkgs; [
       #dogdns
+      #gomuks
       #mnamer
       #nixopsUnstable
       #onefetch
       #spek
+      #visidata
       #wordgrinder
       #wuzz
       abook
       accountsservice
+      adwaita-icon-theme
       alacritty
       apg
       aria2
@@ -122,11 +125,9 @@ in {
       gitui
       glab
       glow
-      gnome.adwaita-icon-theme
       gnufdisk
       gnumake
       gnupg
-      gomuks
       gopass
       gpg-tui
       gpgme
@@ -198,8 +199,10 @@ in {
       ntfsprogs
       nvtopPackages.amd
       oculante
+      ollama
       oneshot
       openssl
+      oterm
       p7zip
       pamixer
       pandoc
@@ -252,9 +255,8 @@ in {
       steam-tui
       stow
       strace
-      tasksh
-      taskwarrior
       taskwarrior-tui
+      taskwarrior3
       tealdeer
       termdown
       termshark
@@ -277,7 +279,6 @@ in {
       vanilla-dmz
       virt-manager
       virt-viewer
-      visidata
       viu
       vivid
       vlock
diff --git a/modules/user/devhell/profiles/services.nix b/modules/user/devhell/profiles/services.nix
index 6a448287..f5eac5a7 100644
--- a/modules/user/devhell/profiles/services.nix
+++ b/modules/user/devhell/profiles/services.nix
@@ -57,12 +57,22 @@ in {
     services.dbus.packages = [ pkgs.mako ];
 
     services = {
+      avahi.enable = true;
       pcscd.enable = true;
       gpm.enable = true;
       openssh.enable = true;
       udisks2.enable = true;
       haveged.enable = true;
       automatic-timezoned.enable = true;
+      geoclue2 = {
+        enable = true;
+        geoProviderUrl = "https://beacondb.net/v1/geolocate";
+        enableWifi = true;
+        enableNmea = true;
+        enableModemGPS = false;
+        enableCDMA = false;
+        enable3G = false;
+      };
       globalprotect = {
         enable = true;
         csdWrapper = "${pkgs.openconnect}/libexec/openconnect/hipreport.sh";
diff --git a/modules/user/profpatsch/programs/scanning.nix b/modules/user/profpatsch/programs/scanning.nix
index 1b5e1f02..e4d14715 100644
--- a/modules/user/profpatsch/programs/scanning.nix
+++ b/modules/user/profpatsch/programs/scanning.nix
@@ -23,7 +23,7 @@ in {
     hardware.sane = {
       enable = true;
       netConf = cfg.remoteScanners;
-      extraBackends = [ unfreeAndNonDistributablePkgs.hplipWithPlugin ];
+      # extraBackends = [ unfreeAndNonDistributablePkgs.hplipWithPlugin ];
 
       drivers.scanSnap = {
         enable = true;
diff --git a/modules/user/profpatsch/services/gonic.nix b/modules/user/profpatsch/services/gonic.nix
index c5801081..ee34e333 100644
--- a/modules/user/profpatsch/services/gonic.nix
+++ b/modules/user/profpatsch/services/gonic.nix
@@ -80,6 +80,7 @@ in {
            "-music-path" cfg.musicDir
            "-podcast-path" cfg.podcastDir
            "-playlists-path" cfg.playlistsDir
+           "-scan-watcher-enabled"
          ]
          ++ lib.optionals (cfg.scanIntervalMinutes != null) [
            "-scan-interval" (toString cfg.scanIntervalMinutes)
diff --git a/modules/user/sternenseemann/profiles/desktop-sway.nix b/modules/user/sternenseemann/profiles/desktop-sway.nix
index 530255ac..06442c78 100644
--- a/modules/user/sternenseemann/profiles/desktop-sway.nix
+++ b/modules/user/sternenseemann/profiles/desktop-sway.nix
@@ -82,7 +82,7 @@ in
         xwayland qt5.qtwayland
         wl-clipboard               # instead of xsel
         mako                       # notifications
-        gnome.adwaita-icon-theme
+        adwaita-icon-theme
         wdisplays                  # display layout GUI
       ];
 
@@ -96,19 +96,21 @@ in
       vuizvui.programs.foot = {
         enable = true;
         settings = {
-          include = "${pkgs.foot.themes}/share/foot/themes/selenized-white";
-
-          dpi-aware = true;
-          font = [
-            {
-              font = defaultFont;
-              options = { size = 8; };
-            }
-            {
-              font = defaultEmojiFont;
-              options = { size = 8; };
-            }
-          ];
+          main = {
+            include = "${pkgs.foot.themes}/share/foot/themes/selenized-white";
+
+            dpi-aware = true;
+            font = [
+              {
+                font = defaultFont;
+                options = { size = 8; };
+              }
+              {
+                font = defaultEmojiFont;
+                options = { size = 8; };
+              }
+            ];
+          };
 
           tweak = {
             grapheme-shaping = "yes";
@@ -152,7 +154,7 @@ in
 
     (lib.mkIf cfg.nextcloud.enable {
       vuizvui.user.sternenseemann.services.sway.extraConfig = ''
-        exec ${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon --start --components=secrets
+        exec ${pkgs.gnome-keyring}/bin/gnome-keyring-daemon --start --components=secrets
         exec ${pkgs.nextcloud-client}/bin/nextcloud
       '';
 
diff --git a/modules/user/sternenseemann/services/sway.nix b/modules/user/sternenseemann/services/sway.nix
index 673e9a03..7891c79f 100644
--- a/modules/user/sternenseemann/services/sway.nix
+++ b/modules/user/sternenseemann/services/sway.nix
@@ -204,7 +204,7 @@ in {
 
     security.pam.services.swaylock = {};
 
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
 
     environment.etc = {
       "sway/config".text = ''
@@ -356,6 +356,7 @@ in {
           format = "%status: %percentage"
           status_chr = "⚡"
           status_bat = "🔋"
+          status_idle = "💤"
           status_unk = "❓"
           status_full = "💯"
           low_threshold = 10
diff --git a/pkgs/aszlig/gpodder/default.nix b/pkgs/aszlig/gpodder/default.nix
index 627d888e..7599f7b4 100644
--- a/pkgs/aszlig/gpodder/default.nix
+++ b/pkgs/aszlig/gpodder/default.nix
@@ -1,4 +1,4 @@
-{ gpodder, fetchFromGitHub, python3Packages, buildSandbox }:
+{ gpodder, fetchFromGitHub, python311Packages, buildSandbox }:
 
 buildSandbox (gpodder.overridePythonAttrs (drv: {
   version = "git-2023-07-24";
@@ -12,7 +12,7 @@ buildSandbox (gpodder.overridePythonAttrs (drv: {
 
   patches = [ ./disable-autoupdate.patch ];
 
-  propagatedBuildInputs = with python3Packages; [
+  propagatedBuildInputs = with python311Packages; [
     dbus-python
     mygpoclient
     pygobject3
@@ -23,7 +23,7 @@ buildSandbox (gpodder.overridePythonAttrs (drv: {
     yt-dlp
   ];
 
-  checkInputs = with python3Packages; [
+  checkInputs = with python311Packages; [
     pytest pytest-httpserver minimock
   ];
 
diff --git a/pkgs/aszlig/psi/config.patch b/pkgs/aszlig/psi/config.patch
index 0fe49d66..93e5810a 100644
--- a/pkgs/aszlig/psi/config.patch
+++ b/pkgs/aszlig/psi/config.patch
@@ -1,5 +1,5 @@
 diff --git a/options/default.xml b/options/default.xml
-index f4b71f53..387793a0 100644
+index 970d3d63..723cc817 100644
 --- a/options/default.xml
 +++ b/options/default.xml
 @@ -19,7 +19,7 @@
@@ -11,7 +11,7 @@ index f4b71f53..387793a0 100644
          </auto-update>
          <enable-multicast comment="Enable multicasting messages to multiple recipients" type="bool">false</enable-multicast>
          <html comment="Hypertext markup options">
-@@ -89,7 +89,7 @@
+@@ -88,7 +88,7 @@
                  <security comment="Options related to the seciruty UI">
                      <show comment="Show the security UI" type="bool">true</show>
                  </security>
@@ -20,7 +20,7 @@ index f4b71f53..387793a0 100644
              </account>
              <message comment="Message options">
                  <enabled comment="Enable message (i.e. non-chat) functionality" type="bool">true</enabled>
-@@ -137,7 +137,7 @@ QWidget#bottomFrame>QWidget>QTextEdit[correction="true"] {
+@@ -136,7 +136,7 @@ QWidget#bottomFrame>QWidget>QTextEdit[correction="true"] {
                  <default-jid-mode comment="Default jid mode: barejid | auto" type="QString">auto</default-jid-mode>
                  <default-jid-mode-ignorelist comment="Default autojid mode ignore list: jid1,jid2,..." type="QString"></default-jid-mode-ignorelist>
                  <history comment="Message history options">
@@ -29,7 +29,7 @@ index f4b71f53..387793a0 100644
                  </history>
              </chat>
              <save>
-@@ -217,7 +217,7 @@ QLineEdit#le_status_text {
+@@ -221,7 +221,7 @@ QLineEdit#le_status_text {
                  <always-on-top type="bool">false</always-on-top>
                  <automatically-resize-roster type="bool">false</automatically-resize-roster>
                  <grow-roster-upwards type="bool">true</grow-roster-upwards>
@@ -38,7 +38,7 @@ index f4b71f53..387793a0 100644
                  <contact-sort-style type="QString">status</contact-sort-style>
                  <disable-service-discovery type="bool">false</disable-service-discovery>
                  <enable-groups type="bool">true</enable-groups>
-@@ -257,7 +257,7 @@ QLineEdit#le_status_text {
+@@ -261,7 +261,7 @@ QLineEdit#le_status_text {
                  <use-left-click type="bool">false</use-left-click>
                  <use-single-click type="bool">false</use-single-click>
                  <use-status-change-animation type="bool">true</use-status-change-animation>
@@ -47,7 +47,7 @@ index f4b71f53..387793a0 100644
                  <use-transport-icons type="bool">true</use-transport-icons>
                  <saved-window-geometry type="QRect" >
                      <x>64</x>
-@@ -293,7 +293,7 @@ QLineEdit#le_status_text {
+@@ -297,7 +297,7 @@ QLineEdit#le_status_text {
                      <custom-picture comment="Show the 'picture' menu" type="bool">true</custom-picture>
                  </contact>
                  <main comment="Options for the main menu">
@@ -56,7 +56,7 @@ index f4b71f53..387793a0 100644
                  </main>
                  <status comment="Options for the status menu">
                      <chat comment="Enable free for chat" type="bool">true</chat>
-@@ -344,7 +344,7 @@ QLineEdit#le_status_text {
+@@ -348,7 +348,7 @@ QLineEdit#le_status_text {
              </spell-check>
              <disable-send-button type="bool">true</disable-send-button>
              <systemtray comment="Options related to the system tray">
@@ -65,7 +65,7 @@ index f4b71f53..387793a0 100644
                  <use-double-click type="bool">false</use-double-click>
              </systemtray>
              <flash-windows comment="Allow windows to flash upon activity" type="bool">true</flash-windows>
-@@ -362,8 +362,8 @@ QLineEdit#le_status_text {
+@@ -366,8 +366,8 @@ QLineEdit#le_status_text {
                      <contactlist>
                          <background type="QColor"/>
                          <grouping>
@@ -76,7 +76,7 @@ index f4b71f53..387793a0 100644
                          </grouping>
                          <profile>
                              <header-background type="QColor">#969696</header-background>
-@@ -373,16 +373,16 @@ QLineEdit#le_status_text {
+@@ -377,16 +377,16 @@ QLineEdit#le_status_text {
                              <away type="QColor">#004bb4</away>
                              <do-not-disturb type="QColor">#7e0000</do-not-disturb>
                              <offline type="QColor">#646464</offline>
@@ -96,7 +96,7 @@ index f4b71f53..387793a0 100644
                      </tooltip>
                      <muc>
                          <nick-colors type="QStringList" >
-@@ -393,21 +393,21 @@ QLineEdit#le_status_text {
+@@ -397,21 +397,21 @@ QLineEdit#le_status_text {
                              <item>Red</item>
                          </nick-colors>
                          <role-moderator type="QColor">#910000</role-moderator>
@@ -123,7 +123,7 @@ index f4b71f53..387793a0 100644
                          <unread-message-color type="QColor">red</unread-message-color>
                          <inactive-color type="QColor">grey</inactive-color>
                      </chat>
-@@ -420,10 +420,10 @@ QLineEdit#le_status_text {
+@@ -424,10 +424,10 @@ QLineEdit#le_status_text {
                      <use-slim-group-headings type="bool">false</use-slim-group-headings>
                  </contactlist>
                  <font>
@@ -138,7 +138,7 @@ index f4b71f53..387793a0 100644
                  </font>
                  <css type="QString" />
              </look>
-@@ -471,20 +471,20 @@ QLineEdit#le_status_text {
+@@ -475,20 +475,20 @@ QLineEdit#le_status_text {
                      <suppress-while-away type="bool">false</suppress-while-away>
                  </popup-dialogs>
                  <sounds>
@@ -172,7 +172,7 @@ index f4b71f53..387793a0 100644
                      <unix-sound-player type="QString"/>
                  </sounds>
                  <successful-subscription type="bool">true</successful-subscription>
-@@ -504,7 +504,7 @@ QLineEdit#le_status_text {
+@@ -508,7 +508,7 @@ QLineEdit#le_status_text {
                  <mouse-middle-button type="QString">close</mouse-middle-button> <!-- hide|close|detach -->
                  <mouse-doubleclick-action type="QString">detach</mouse-doubleclick-action>
                  <size type="QString"></size> <!-- will be invalid when converted to QSize so we can detect first load -->
@@ -181,7 +181,7 @@ index f4b71f53..387793a0 100644
                  <group-state comment="Saved state data of the tabsets defined by options.ui.tabs.grouping">
                      <CM>
                          <size-frame type="QRect">
-@@ -735,7 +735,7 @@ QLineEdit#le_status_text {
+@@ -739,7 +739,7 @@ QLineEdit#le_status_text {
              <last-activity type="bool">true</last-activity>
          </service-discovery>
          <status>
@@ -190,7 +190,7 @@ index f4b71f53..387793a0 100644
              <ask-for-message-on-online type="bool">false</ask-for-message-on-online>
              <ask-for-message-on-chat type="bool">true</ask-for-message-on-chat>
              <ask-for-message-on-away type="bool">true</ask-for-message-on-away>
-@@ -758,7 +758,20 @@ QLineEdit#le_status_text {
+@@ -762,7 +762,20 @@ QLineEdit#le_status_text {
                  <by-template type="bool">true</by-template>
                  <by-status type="bool">false</by-status>
              </last-overwrite>
@@ -212,7 +212,7 @@ index f4b71f53..387793a0 100644
              <presets-in-status-menus type="QString" comment="'yes', 'no' or 'submenu'">submenu</presets-in-status-menus>
              <show-only-online-offline type="bool">false</show-only-online-offline>
              <show-choose type="bool">true</show-choose>
-@@ -797,5 +810,9 @@ QLineEdit#le_status_text {
+@@ -801,5 +814,9 @@ QLineEdit#le_status_text {
          </keychain>
      </options>
      <accounts comment="Account definitions and options"/>
@@ -224,21 +224,21 @@ index f4b71f53..387793a0 100644
 +    </plugins>
  </psi>
 diff --git a/src/psi_profiles.cpp b/src/psi_profiles.cpp
-index 97c44218..d5fa9d53 100644
+index c80f108f..fba2eee1 100644
 --- a/src/psi_profiles.cpp
 +++ b/src/psi_profiles.cpp
-@@ -74,8 +74,8 @@ void UserAccount::reset()
+@@ -66,8 +66,8 @@ void UserAccount::reset()
+     storeSaltedHashedPassword = false;
      req_mutual_auth           = false;
-     legacy_ssl_probe          = false;
      security_level            = QCA::SL_None;
--    ssl                       = SSL_Auto;
+-    ssl                       = TLS_Auto;
 -    jid                       = "";
-+    ssl                       = SSL_Yes;
++    ssl                       = TLS_Yes;
 +    jid                       = "@jid@";
      pass                      = "";
      scramSaltedHashPassword   = "";
      opt_pass                  = false;
-@@ -85,7 +85,7 @@ void UserAccount::reset()
+@@ -77,7 +77,7 @@ void UserAccount::reset()
      opt_automatic_resource    = true;
      priority_dep_on_status    = true;
      ignore_global_actions     = false;
@@ -247,9 +247,9 @@ index 97c44218..d5fa9d53 100644
      priority                  = 55;
      ibbOnly                   = false;
      opt_keepAlive             = true;
-@@ -128,7 +128,7 @@ void UserAccount::reset()
-               << "stun.voipbuster.com"
-               << "stun.voxgratia.org";
+@@ -106,7 +106,7 @@ void UserAccount::reset()
+               << "stun.phoneserve.com" << "stun.rnktel.com" << "stun.softjoys.com" << "stun.sipgate.net"
+               << "stun.sipgate.net:10000" << "stun.stunprotocol.org" << "stun.voipbuster.com" << "stun.voxgratia.org";
  
 -    stunHost = stunHosts[0];
 +    stunHost = "";
diff --git a/pkgs/aszlig/psi/default.nix b/pkgs/aszlig/psi/default.nix
index 54730838..bc7e9830 100644
--- a/pkgs/aszlig/psi/default.nix
+++ b/pkgs/aszlig/psi/default.nix
@@ -1,5 +1,5 @@
 { stdenv, lib, fetchFromGitHub, cmake, makeWrapper
-, hunspell, libgcrypt, libgpg-error, libidn, libotr, libsForQt5
+, hunspell, libgcrypt, libgpg-error, libidn, libomemo-c, libotr, libsForQt5
 , libsignal-protocol-c, html-tidy, qt5
 
 , substituteAll
@@ -18,13 +18,13 @@ let
 
   usrsctp = stdenv.mkDerivation {
     pname = "usrsctp";
-    version = "git20240111";
+    version = "git20240510";
 
     src = fetchFromGitHub {
       owner = "sctplab";
       repo = "usrsctp";
-      rev = "265f20562e4d3fa977c6d9e09d0631b8125ac949";
-      hash = "sha256-4EH67xFeLSad7klQNPKV0Nyq0KAV8LcqK9Wx9QsuboA";
+      rev = "e711f82ad09eddef42859073c66242887c24a016";
+      hash = "sha256-lU0TVKdObF+sNa9BfSQN2D4bHqw80WsEOjNkrEcmOhM";
     };
 
     nativeBuildInputs = [ cmake ];
@@ -32,21 +32,21 @@ let
 
 in stdenv.mkDerivation rec {
   name = "psi-${version}";
-  version = "2.0git20231104aszlig";
+  version = "2.0git20240717aszlig";
 
   src = fetchFromGitHub {
     owner = "psi-im";
     repo = "psi";
-    rev = "83a8dfb87ad0b882be49fd43de75c805464a8c2b";
-    hash = "sha256-ya5qMh5kIMvPUOcfY+B3952Q8SBg+QstGI9/8GsoYRk";
+    rev = "b98f3936c4b6fa758668567f8460b922d2527b21";
+    hash = "sha256-43lcBbjtk6Q7jV5OmZ21bOeZHZROtjG/Pc4DyiB63mA";
     fetchSubmodules = true;
   };
 
   plugins = fetchFromGitHub {
     owner = "psi-im";
     repo = "plugins";
-    rev = "dfdb6d962ac92920861af900f41e8f590e500206";
-    hash = "sha256-xrKIP34aXkBX/H31m1Z9dWRcp9JkuQonLTlxX0UA+g4";
+    rev = "347230bf240992c74a7de2a7ac9c28545fa34401";
+    hash = "sha256-CL+m9yw0Dv28SPS/cOwYitXnWOKXAjpyzRMRZSHkMwM";
   };
 
   patches = [
@@ -76,6 +76,7 @@ in stdenv.mkDerivation rec {
     libgcrypt
     libgpg-error
     libidn
+    libomemo-c
     libotr
     libsForQt5.qca-qt5
     libsignal-protocol-c
diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix
index 3d30defb..f99424ff 100644
--- a/pkgs/profpatsch/default.nix
+++ b/pkgs/profpatsch/default.nix
@@ -110,6 +110,7 @@ let
   writeExeclineFns = callPackage ./execline/write-execline.nix {};
 
 in rec {
+  # tvl = import /home/philip/depot {};
 
   inherit tvl;
 
@@ -156,10 +157,15 @@ in rec {
     inherit writeExecline writeHaskellInterpret getBins runInEmptyEnv sandbox;
   };
 
+  weechat = callPackage ./tmp.nix {
+    inherit writeExecline getBins;
+  };
+
   xrandr = import ./xrandr.nix { inherit pkgs getBins runExeclineLocal writeExecline toNetstringKeyVal; };
 
   inherit (callPackage ./utils-hs {})
-    until watch-server
+    until
+    # watch-server
     haskellPackages;
 
   query-audio-streams = callPackage ./query-album-streams {
@@ -238,8 +244,16 @@ in rec {
     nix-eval
     ;
 
+  # s6 = pkgs.callPackage ./s6 {
+  #   inherit (haskellPackages) dhall-nix;
+  #   inherit runExeclineLocal;
+  # };
 
+  # dhall-experiment = pkgs.callPackage ./dhall-experiment {
+  #   inherit (haskellPackages) dhall-nix;
+  # };
 
+  xmonad = pkgs.callPackage ./xmonad { };
 
   inherit (import ./importPurescript.nix { inherit pkgs exactSource; haskellPackages = haskellPackagesPurescript; })
     importPurescript
@@ -266,6 +280,8 @@ in rec {
     record-get
     ;
 
+  # inherit (import ./execline/el-semicolon.nix { inherit writeRustSimpleLib; });
+
   inherit (import ./execline/default.nix { inherit pkgs writeRustSimpleLib rust-deps; })
     el-semicolon
     el-exec
@@ -281,6 +297,56 @@ in rec {
 
   backup = import ./backup { inherit pkgs writeExecline getBins; };
 
+  jaeger = import ./jaeger { inherit pkgs writeExecline; };
+
+  # ate = import ./ate {
+  #   inherit pkgs;
+  #   inherit getBins runExeclineLocal dhall dhall-nix;
+  # };
+
+  shotgun =
+    let
+        naersk = pkgs.callPackage (pkgs.fetchFromGitHub {
+          owner = "nmattia";
+          repo = "naersk";
+          rev = "f17317465e43ad7b9945e6492295e190946fb4ac";
+          sha256 = "1hp1l86qlkmipcas90p4s4q5bhgh0531nl3lkignz1q455vrga0f";
+        }) {};
+        shotgun = (naersk.buildPackage (pkgs.fetchFromGitHub {
+          owner = "neXromancers";
+          repo = "shotgun";
+          rev = "abc3c468b2964baf190a003247ac29cf61cf5f0c";
+          sha256 = "0fpc09yvxjcvjkai7afyig4gyc7inaqxxrwzs17mh8wdgzawb6dl";
+        }) {
+          doDoc = false;
+          buildInputs = [ pkgs.xorg.libX11 pkgs.xorg.libXrandr pkgs.pkg-config ];
+        }).overrideAttrs (old: {
+          prePatch = ''
+            rm build.rs
+            sed -e "/build =/d" -i Cargo.toml
+          '';
+        });
+    in shotgun;
+
+  shadowenv = pkgs.rustPlatform.buildRustPackage rec {
+    name = "shadowenv";
+    src = pkgs.fetchFromGitHub {
+      owner = "Shopify";
+      repo = "shadowenv";
+      rev = "1.3.1";
+      sha256 = "1s59ra99wcyyqz8gzly4qmcq5rh22c50c75cdi2kyajm7ghgryy9";
+    };
+    cargoSha256 = "0mg1m5hfvzm1n4xh3xsps7f2id48gwr3k22833mzqy2qz4v93c0z";
+  };
+
+  tmp = import ./tmp.nix {
+    inherit pkgs getBins writeExecline;
+    pkgsStatic = pkgs.pkgsStatic; };
+
   gpg-private-offline-key = import ./gpg-private-offline-key { inherit pkgs writeExecline getBins; };
 
+  # business = import ./business.nix { inherit pkgs; };
+
+  # mes = import ./mes { inherit pkgs; };
+
 }
diff --git a/pkgs/profpatsch/display-infos/default.nix b/pkgs/profpatsch/display-infos/default.nix
index 18340204..52d799a2 100644
--- a/pkgs/profpatsch/display-infos/default.nix
+++ b/pkgs/profpatsch/display-infos/default.nix
@@ -21,6 +21,10 @@ let
         p.terminate()
         return str(sft.strip().decode())
 
+    def get_5_min_load():
+        with open('/proc/loadavg', 'r') as f:
+            return f.read().split(' ')[1]
+
     charging = readint("/sys/class/power_supply/AC/online")
 
     full = 0
@@ -48,13 +52,15 @@ let
     bat = round( now/full, 2 )
     ac = "⚡ " if charging else ""
     sft_remaining = seconds_to_sft(seconds_remaining)
-    date = sub.run(["date", "+%d.%m. [%V] %a %T"], stdout=sub.PIPE).stdout.strip().decode()
+    date = sub.run(["date", "+%d.%m. KW%V %a %T"], stdout=sub.PIPE).stdout.strip().decode()
     dottime = sub.run(["date", "--utc", "+%H·%M"], stdout=sub.PIPE).stdout.strip().decode()
     sftdate = sub.run(["@sfttime@"], stdout=sub.PIPE).stdout.strip().decode()
-    notify = "BAT: {percent}% {ac}{charge}| {date} | {sftdate} | {dottime}".format(
+    load = get_5_min_load()
+    notify = "BAT: {percent}% {ac}{charge}{{{load}}} | {date} | {sftdate} | {dottime}".format(
       percent = int(bat*100),
       ac = ac,
       charge = "{} ".format(sft_remaining) if seconds_remaining else "",
+      load = load,
       date = date,
       sftdate = sftdate,
       dottime = dottime
diff --git a/pkgs/profpatsch/xdg-open/default.nix b/pkgs/profpatsch/xdg-open/default.nix
index 72f2622f..3adf33e9 100644
--- a/pkgs/profpatsch/xdg-open/default.nix
+++ b/pkgs/profpatsch/xdg-open/default.nix
@@ -22,10 +22,7 @@ let
       // getBins pkgs.libressl.nc [ "nc" ]
       // getBins pkgs.dmenu [ "dmenu" "dmenu_path" ]
       # TODO: make sure these are the ones from the environment
-      // getBins pkgs.emacs [ "emacsclient" ]
-      // getBins pkgs.firefox [ "firefox" ]
       // getBins pkgs.ranger [ "ranger" ]
-      // getBins pkgs.khal [ "khal" ]
       // getBins show-qr-code [ "show-qr-code" ]
       ;
 
@@ -46,7 +43,7 @@ let
 
   composeMailTo = {
     exe = writeExecline "emacs-mail" { readNArgs = 1; } [
-      bins.emacsclient
+      "emacsclient"
         "--create-frame"
         "--eval"
         # TODO: this obviously fails if the mail address contains "
@@ -68,12 +65,12 @@ let
   # };
 
   openInBrowser = {
-    exe = bins.firefox;
+    exe = "firefox";
     args = file: [ file ];
   };
 
   openInEditor = {
-    exe = bins.emacsclient;
+    exe = "emacsclient";
     args = file: [ file ];
   };
 
@@ -129,7 +126,7 @@ let
       pkgs = {
         pkg = ({binary, package}: "${lib.getBin pkgs.${package}}/bin/${binary}");
         pkgOnDemand = ({binary, package}: "${lazy-packages.mkWrapper {
-          package = (lib.getBin pkgs.${package});
+          package = pkgs.${package};
         }}/bin/${binary}");
       };
       special = {
diff --git a/pkgs/profpatsch/xdg-open/purs/Config.purs b/pkgs/profpatsch/xdg-open/purs/Config.purs
index eb0f94b0..e31e4fc0 100644
--- a/pkgs/profpatsch/xdg-open/purs/Config.purs
+++ b/pkgs/profpatsch/xdg-open/purs/Config.purs
@@ -35,17 +35,19 @@ mime pkgs special = do
     pkgSameOnDemand :: String -> Executable
     pkgSameOnDemand name = pkgs.pkgOnDemand { package: name, binary: name }
 
+    pkgFromPath p = p
+
   let
     m =
       { text:
           { html: { mime: [ "text", "html" ], cmd: special.openInBrowser }
           , gemini:
               { mime: [ "text", "gemini" ]
-              , cmd: oneArg (pkgSame "lagrange")
+              , cmd: oneArg (pkgSameOnDemand "lagrange")
               }
           , gopher:
               { mime: [ "text", "gopher" ]
-              , cmd: oneArg (pkgSame "lagrange")
+              , cmd: oneArg (pkgSameOnDemand "lagrange")
               }
           , xml:
               { mime: [ "text", "xml" ], cmd: special.openInBrowser }
@@ -80,7 +82,7 @@ mime pkgs special = do
               { mime: [ "image", "gif" ], cmd: special.openInBrowser }
           , svg:
               { mime: [ "image", "svg+xml" ]
-              , cmd: oneArg (pkgSame "inkscape")
+              , cmd: oneArg (pkgFromPath "inkscape")
               }
           , any:
               { mime: [ "image", "*" ], cmd: oneArg (pkgSame "imv") }
diff --git a/pkgs/sternenseemann/scripts/default.nix b/pkgs/sternenseemann/scripts/default.nix
index 2fe623b4..d322950a 100644
--- a/pkgs/sternenseemann/scripts/default.nix
+++ b/pkgs/sternenseemann/scripts/default.nix
@@ -18,6 +18,9 @@ let
     /home/lukas/.stack
     /home/lukas/.notmuch
     /home/lukas/.local/share/
+    /home/lukas/.local/state/cabal
+    /home/lukas/.local/state/wireplumber
+    /home/lukas/.local/state/pipewire
     /home/lukas/Mail/.notmuch
     /home/lukas/.npm
     /home/lukas/.gem
diff --git a/pkgs/tvl/default.nix b/pkgs/tvl/default.nix
index db24c271..267e0fb0 100644
--- a/pkgs/tvl/default.nix
+++ b/pkgs/tvl/default.nix
@@ -1,7 +1,7 @@
 { tvlSrc ? builtins.fetchGit {
     name = "tvl-depot";
     url = "https://code.tvl.fyi";
-    rev = "d5b6704d3d82c1c1acf45aba71ab1a337f94defd"; # 2024-03-14
+    rev = "f648f17ec3dd9b99d6271f607d62004c321b7c8f"; # 2024-08-08
     ref = "canon";
   }
 , pkgs
diff --git a/tests/aszlig/programs/psi.nix b/tests/aszlig/programs/psi.nix
index b926f0c0..9431a913 100644
--- a/tests/aszlig/programs/psi.nix
+++ b/tests/aszlig/programs/psi.nix
@@ -1,7 +1,7 @@
 { nixpkgsPath, ... }:
 
 {
-  name = "psi-test";
+  name = "psi";
 
   machine = { pkgs, ... }: {
     imports = [