about summary refs log tree commit diff stats
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkgs/by-name/at/atuin-dvorak/package.nix9
-rw-r--r--pkgs/by-name/at/atuin-dvorak/set-dvorak-keybindings.patch.license9
-rwxr-xr-xpkgs/by-name/au/aumo/aumo.sh57
-rw-r--r--pkgs/by-name/au/aumo/package.nix27
-rwxr-xr-xpkgs/by-name/ba/battery/battery.sh11
-rw-r--r--pkgs/by-name/ba/battery/package.nix25
-rw-r--r--pkgs/by-name/be/beetsExtraPlugins/package.nix9
-rw-r--r--pkgs/by-name/be/beetsExtraPlugins/xtractor.nix9
-rwxr-xr-xpkgs/by-name/br/brightness/brightness.sh38
-rw-r--r--pkgs/by-name/br/brightness/package.nix29
-rwxr-xr-xpkgs/by-name/co/con2pdf/con2pdf.sh29
-rw-r--r--pkgs/by-name/co/con2pdf/package.nix26
-rw-r--r--pkgs/by-name/ct/ctpv-64-types/allow-up-to-64-types.patch.license9
-rw-r--r--pkgs/by-name/ct/ctpv-64-types/package.nix9
-rwxr-xr-xpkgs/by-name/fd/fd_list/fd_list.sh40
-rw-r--r--pkgs/by-name/fd/fd_list/package.nix28
-rwxr-xr-xpkgs/by-name/fu/fupdate-flake/fupdate-flake.sh194
-rw-r--r--pkgs/by-name/fu/fupdate-flake/package.nix37
-rwxr-xr-xpkgs/by-name/fu/fupdate-sys/fupdate-sys.sh168
-rw-r--r--pkgs/by-name/fu/fupdate-sys/package.nix38
-rw-r--r--pkgs/by-name/fu/fupdate/.envrc13
-rw-r--r--pkgs/by-name/fu/fupdate/.gitignore12
-rw-r--r--pkgs/by-name/fu/fupdate/Cargo.lock310
-rw-r--r--pkgs/by-name/fu/fupdate/Cargo.toml81
-rw-r--r--pkgs/by-name/fu/fupdate/flake.lock27
-rw-r--r--pkgs/by-name/fu/fupdate/flake.lock.license9
-rw-r--r--pkgs/by-name/fu/fupdate/flake.nix34
-rw-r--r--pkgs/by-name/fu/fupdate/fupdate.1.md70
-rwxr-xr-xpkgs/by-name/fu/fupdate/fupdate.sh197
-rw-r--r--pkgs/by-name/fu/fupdate/package.nix64
-rw-r--r--pkgs/by-name/fu/fupdate/src/cli.rs56
-rw-r--r--pkgs/by-name/fu/fupdate/src/main.rs57
-rwxr-xr-xpkgs/by-name/fu/fupdate/update.sh14
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/.envrc2
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/.gitignore3
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/Cargo.lock1652
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/Cargo.toml14
-rwxr-xr-xpkgs/by-name/ge/generate_moz_extension/examples/generate_extensions.sh17
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/flake.lock98
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/flake.nix75
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/package.nix20
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/res/generate_extensions.py44
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/res/reference.json30
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/res/test.json30
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/src/main.rs138
-rw-r--r--pkgs/by-name/ge/generate_moz_extension/src/types.rs71
-rwxr-xr-xpkgs/by-name/ge/generate_moz_extension/update.sh6
-rwxr-xr-xpkgs/by-name/gi/git-cm/git-cm.sh13
-rw-r--r--pkgs/by-name/gi/git-cm/package.nix23
-rwxr-xr-xpkgs/by-name/gi/git-edit-index/git-edit-index.sh61
-rw-r--r--pkgs/by-name/gi/git-edit-index/package.nix24
-rwxr-xr-xpkgs/by-name/hi/hibernate/hibernate.sh13
-rw-r--r--pkgs/by-name/hi/hibernate/package.nix20
-rw-r--r--pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch110
-rw-r--r--pkgs/by-name/i3/i3bar-river-patched/package.nix54
-rw-r--r--pkgs/by-name/i3/i3status-rust-patched/package.nix34
-rw-r--r--pkgs/by-name/lf/lf-make-map/.envrc11
-rw-r--r--pkgs/by-name/lf/lf-make-map/.gitignore10
-rw-r--r--pkgs/by-name/lf/lf-make-map/Cargo.lock152
-rw-r--r--pkgs/by-name/lf/lf-make-map/Cargo.toml17
-rw-r--r--pkgs/by-name/lf/lf-make-map/README.md12
-rw-r--r--pkgs/by-name/lf/lf-make-map/flake.lock120
-rw-r--r--pkgs/by-name/lf/lf-make-map/flake.lock.license9
-rw-r--r--pkgs/by-name/lf/lf-make-map/flake.nix137
-rw-r--r--pkgs/by-name/lf/lf-make-map/package.nix9
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/cli.rs10
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/main.rs70
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/lf_mapping.rs35
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/map_key.rs274
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/display.rs91
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/iterator.rs53
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/lf_mapping.rs24
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/mod.rs402
-rw-r--r--pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs337
-rwxr-xr-xpkgs/by-name/lf/lf-make-map/tests/base.sh25
-rwxr-xr-xpkgs/by-name/lf/lf-make-map/tests/cases/child_insert/test.sh27
-rw-r--r--pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old46
-rw-r--r--pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old.license9
-rwxr-xr-xpkgs/by-name/lf/lf-make-map/tests/cases/simple/test.sh49
-rwxr-xr-xpkgs/by-name/lf/lf-make-map/update.sh10
-rwxr-xr-xpkgs/by-name/ll/ll/ll.sh19
-rw-r--r--pkgs/by-name/ll/ll/package.nix9
-rwxr-xr-xpkgs/by-name/lm/lm/lm.sh12
-rw-r--r--pkgs/by-name/lm/lm/package.nix9
-rwxr-xr-xpkgs/by-name/lo/lock/lock.sh10
-rw-r--r--pkgs/by-name/lo/lock/package.nix9
-rwxr-xr-xpkgs/by-name/mp/mpp-beetrm/mpp-beetrm.sh11
-rw-r--r--pkgs/by-name/mp/mpp-beetrm/package.nix21
-rwxr-xr-xpkgs/by-name/mp/mpp-lyrics/mpp-lyrics.sh22
-rw-r--r--pkgs/by-name/mp/mpp-lyrics/package.nix26
-rwxr-xr-xpkgs/by-name/mp/mpp-searchadd/mpp-searchadd.sh18
-rw-r--r--pkgs/by-name/mp/mpp-searchadd/package.nix21
-rwxr-xr-xpkgs/by-name/mp/mpp/mpp.sh13
-rw-r--r--pkgs/by-name/mp/mpp/package.nix41
-rw-r--r--pkgs/by-name/qu/qutebrowser-patched/0001-fix-standardpaths-Continue-to-work-with-xdg-while-ba.patch54
-rw-r--r--pkgs/by-name/qu/qutebrowser-patched/package.nix6
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/.envrc10
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/.gitignore10
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/Cargo.lock761
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/Cargo.toml26
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/TODO1
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/contrib/example.json7
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/contrib/example.json.license9
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/contrib/init.json261
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/flake.lock6
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/flake.lock.license9
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/flake.nix21
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/package.nix27
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/resources/river-control-unstable-v1.xml85
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/resources/river-status-unstable-v1.xml148
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/cli.rs28
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs386
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs125
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/main.rs72
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/ansi/mod.rs173
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/dispatches.rs214
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/mod.rs272
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs57
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs129
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/river/mod.rs1
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/river/protocols.rs28
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/mod.rs21
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/multi.rs437
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/raw.rs290
-rw-r--r--pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/slot.rs596
-rwxr-xr-xpkgs/by-name/ri/river-mk-keymap/update.sh13
-rw-r--r--pkgs/by-name/ri/river-start/package.nix14
-rwxr-xr-xpkgs/by-name/ri/river-start/river-start.sh10
-rw-r--r--pkgs/by-name/sc/screenshot_persistent/package.nix9
-rwxr-xr-xpkgs/by-name/sc/screenshot_persistent/screenshot_persistent.sh10
-rw-r--r--pkgs/by-name/sc/screenshot_temporary/package.nix21
-rwxr-xr-xpkgs/by-name/sc/screenshot_temporary/screenshot_temporary.sh11
-rw-r--r--pkgs/by-name/sn/snap-sync-forked/package.nix36
-rwxr-xr-xpkgs/by-name/sn/snap-sync-forked/snap-sync-forked.sh534
-rw-r--r--pkgs/by-name/st/stamp/package.nix29
-rwxr-xr-xpkgs/by-name/st/stamp/stamp.sh16
-rw-r--r--pkgs/by-name/tr/tree-sitter-yts/package.nix9
-rw-r--r--pkgs/by-name/ts/tskm/.envrc14
-rw-r--r--pkgs/by-name/ts/tskm/.gitignore10
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.lock550
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.toml44
-rw-r--r--pkgs/by-name/ts/tskm/build.rs52
-rw-r--r--pkgs/by-name/ts/tskm/flake.lock6
-rw-r--r--pkgs/by-name/ts/tskm/flake.lock.license9
-rw-r--r--pkgs/by-name/ts/tskm/flake.nix9
-rw-r--r--pkgs/by-name/ts/tskm/package.nix21
-rw-r--r--pkgs/by-name/ts/tskm/src/browser/mod.rs172
-rw-r--r--pkgs/by-name/ts/tskm/src/cli.rs231
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/input/handle.rs124
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/input/mod.rs195
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs30
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs12
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs318
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/mod.rs222
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/project/handle.rs22
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/project/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/main.rs56
-rw-r--r--pkgs/by-name/ts/tskm/src/rofi/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/state.rs12
-rw-r--r--pkgs/by-name/ts/tskm/src/task/mod.rs27
-rwxr-xr-xpkgs/by-name/ts/tskm/update.sh13
-rw-r--r--pkgs/by-name/up/update-sys/package.nix29
-rwxr-xr-xpkgs/by-name/up/update-sys/update-sys.sh85
-rw-r--r--pkgs/by-name/ya/yambar-modules/.envrc2
-rw-r--r--pkgs/by-name/ya/yambar-modules/.gitignore1
-rw-r--r--pkgs/by-name/ya/yambar-modules/Cargo.lock131
-rw-r--r--pkgs/by-name/ya/yambar-modules/Cargo.toml9
-rw-r--r--pkgs/by-name/ya/yambar-modules/flake.lock61
-rw-r--r--pkgs/by-name/ya/yambar-modules/flake.nix32
-rw-r--r--pkgs/by-name/ya/yambar-modules/package.nix14
-rw-r--r--pkgs/by-name/ya/yambar-modules/src/cpu.rs21
-rw-r--r--pkgs/by-name/ya/yambar-modules/src/main.rs26
-rw-r--r--pkgs/by-name/ya/yambar-modules/src/memory.rs29
-rw-r--r--pkgs/by-name/yt/yt/package.nix121
-rw-r--r--pkgs/default.nix26
-rwxr-xr-xpkgs/update_pkgs.sh10
177 files changed, 8414 insertions, 5821 deletions
diff --git a/pkgs/by-name/at/atuin-dvorak/package.nix b/pkgs/by-name/at/atuin-dvorak/package.nix
index c651eb75..d3f92961 100644
--- a/pkgs/by-name/at/atuin-dvorak/package.nix
+++ b/pkgs/by-name/at/atuin-dvorak/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {atuin}:
 atuin.overrideAttrs (finalAttrs: previousAttrs: {
   pname = previousAttrs.pname + "-dvorak";
diff --git a/pkgs/by-name/at/atuin-dvorak/set-dvorak-keybindings.patch.license b/pkgs/by-name/at/atuin-dvorak/set-dvorak-keybindings.patch.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/at/atuin-dvorak/set-dvorak-keybindings.patch.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/au/aumo/aumo.sh b/pkgs/by-name/au/aumo/aumo.sh
index 84d39deb..dba5ef63 100755
--- a/pkgs/by-name/au/aumo/aumo.sh
+++ b/pkgs/by-name/au/aumo/aumo.sh
@@ -1,28 +1,73 @@
 #! /usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+NAME="aumo"
+
+error() {
+    printf "\033[1;91m==> ERROR:\033[0m \033[1;93m%s\033[0m\n" "$*" >&2
+}
+
+die() {
+    error "$1"
+    exit "${2-1}"
+}
+
+usage() {
+    echo "Usage: $NAME mount|unmount"
+}
+
+get_mounted_labels() {
+    findmnt --output label --json | jq '.filesystems | map(.label) | sort | unique | map(select(. != null))'
+}
+get_unmounted_labels() {
+    first=true
+
+    find /dev/disk/by-label -printf "%P\n" | while read -r label; do
+        if ! get_mounted_labels | jq 'join("\n")' --raw-output | grep "$label" --quiet; then
+            if [ "$first" = "true" ]; then
+                first=false
+            else
+                printf "|"
+            fi
+            printf "%s" "$label"
+        fi
+    done
+}
 
 unmounting() {
-    disk_name="$(find /dev/disk/by-label -type l -printf "%P|" | rofi -sep "|" -dmenu -p "Select disk to mount")"
+    disk_name="$(get_mounted_labels | jq 'join("|")' --join-output | rofi -sep "|" -dmenu -p "Select disk to unmount")"
 
     udisksctl unmount --block-device "/dev/disk/by-label/$disk_name"
 }
 
 mounting() {
-    disk_name="$(find /dev/disk/by-label -type l -printf "%P|" | rofi -sep "|" -dmenu -p "Select disk to mount")"
+    disk_name="$(get_unmounted_labels | rofi -sep "|" -dmenu -p "Select disk to mount")"
 
     udisksctl mount --block-device "/dev/disk/by-label/$disk_name"
 }
 
-case "$1" in
+case "${1-unset}" in
 "mount")
     mounting
     ;;
 "unmount" | "umount")
     unmounting
     ;;
+"unset")
+    usage
+    die "You need to provide one argument."
+    ;;
 *)
-    die "Usage: $NAME mount|unmount"
+    usage
+    die "Unknown command: '$1'"
     ;;
 esac
diff --git a/pkgs/by-name/au/aumo/package.nix b/pkgs/by-name/au/aumo/package.nix
index 20054bb5..8132a15a 100644
--- a/pkgs/by-name/au/aumo/package.nix
+++ b/pkgs/by-name/au/aumo/package.nix
@@ -1,17 +1,32 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
   udisks,
   findutils,
   rofi,
+  jq,
+  gnugrep,
+  util-linux,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "aumo";
-  src = ./aumo.sh;
-  generateCompletions = false;
-  keepPath = false;
-  dependencies = [
+  text = builtins.readFile ./aumo.sh;
+  inheritPath = false;
+  runtimeInputs = [
     udisks
     findutils
     rofi
+    jq
+    gnugrep
+    util-linux # for findmnt
   ];
 }
diff --git a/pkgs/by-name/ba/battery/battery.sh b/pkgs/by-name/ba/battery/battery.sh
index e650ba5d..5ccb303d 100755
--- a/pkgs/by-name/ba/battery/battery.sh
+++ b/pkgs/by-name/ba/battery/battery.sh
@@ -1,7 +1,14 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 capacity="$(cat /sys/class/power_supply/BAT0/capacity)"
 status="$(cat /sys/class/power_supply/BAT0/status)"
diff --git a/pkgs/by-name/ba/battery/package.nix b/pkgs/by-name/ba/battery/package.nix
index 9c0e194b..560ec59b 100644
--- a/pkgs/by-name/ba/battery/package.nix
+++ b/pkgs/by-name/ba/battery/package.nix
@@ -1,9 +1,20 @@
-{sysLib}:
-sysLib.writeShellScript {
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  writeShellApplication,
+  # Dependencies
+  coreutils,
+}:
+writeShellApplication {
   name = "battery";
-  src = ./battery.sh;
-  generateCompletions = false;
-  keepPath = false;
-  dependencies = [
-  ];
+  text = builtins.readFile ./battery.sh;
+  inheritPath = false;
+  runtimeInputs = [coreutils];
 }
diff --git a/pkgs/by-name/be/beetsExtraPlugins/package.nix b/pkgs/by-name/be/beetsExtraPlugins/package.nix
index f019922e..b310715b 100644
--- a/pkgs/by-name/be/beetsExtraPlugins/package.nix
+++ b/pkgs/by-name/be/beetsExtraPlugins/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {...}: {
   # xtractor = pkgs.callPackage ./xtractor.nix {beets = pkgs.beetsPackages.beets-minimal;};
 }
diff --git a/pkgs/by-name/be/beetsExtraPlugins/xtractor.nix b/pkgs/by-name/be/beetsExtraPlugins/xtractor.nix
index 17981cdb..c6b942fd 100644
--- a/pkgs/by-name/be/beetsExtraPlugins/xtractor.nix
+++ b/pkgs/by-name/be/beetsExtraPlugins/xtractor.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   lib,
   fetchFromGitHub,
diff --git a/pkgs/by-name/br/brightness/brightness.sh b/pkgs/by-name/br/brightness/brightness.sh
index 887dbb1e..f38bb5a5 100755
--- a/pkgs/by-name/br/brightness/brightness.sh
+++ b/pkgs/by-name/br/brightness/brightness.sh
@@ -1,24 +1,33 @@
-#!/usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+#!/usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+die() {
+    echo "ERROR: $1"
+    exit 1
+}
 
 help() {
     cat <<EOF
 This is a system brightness manager
 
 USAGE:
-    $NAME up [VALUE] | down [VALUE]
+    $NAME set VALUE
 
 OPTIONS:
     --help   | -h
                                 Output this help and exit.
 
-    --version   | -v
-                                Output the version and exit.
-
 COMMANDS:
-    set [VALUE]
+    set VALUE
                                 Set the brightness to the specified percentage.
 
 ARGUMENTS:
@@ -27,17 +36,14 @@ ARGUMENTS:
 EOF
 }
 
-BACKLIGHT="/sys/class/backlight/%BACKLIGHT_NAME"
+BACKLIGHT="/sys/class/backlight/$BACKLIGHT_NAME"
 
 brightness() {
     perc="$1"
 
-    max="$(cat $BACKLIGHT/max_brightness)"
-
+    max="$(cat "$BACKLIGHT/max_brightness")"
     new="$(echo | awk --assign=perc="$perc" '{printf (perc / 100)}')"
-
     output="$(echo | awk --assign=new="$new" --assign=max="$max" '{printf max * new}')"
-
     echo "$output" >"$BACKLIGHT/brightness"
 }
 
@@ -47,10 +53,6 @@ for arg in "$@"; do
         help
         exit 0
         ;;
-    "--version" | "-v")
-        version
-        exit 0
-        ;;
     esac
 done
 
diff --git a/pkgs/by-name/br/brightness/package.nix b/pkgs/by-name/br/brightness/package.nix
index c2e31a0c..f3797d43 100644
--- a/pkgs/by-name/br/brightness/package.nix
+++ b/pkgs/by-name/br/brightness/package.nix
@@ -1,14 +1,29 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Arguments
   backlightName ? "intel_backlight", # nixosConfig.soispha.laptop.backlight
+  # Dependencies
+  gawk,
+  coreutils,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "brightness";
-  src = ./brightness.sh;
-  generateCompletions = true;
-  keepPath = false;
+  text = builtins.readFile ./brightness.sh;
+  inheritPath = false;
 
-  replacementStrings = {BACKLIGHT_NAME = backlightName;};
+  runtimeEnv = {BACKLIGHT_NAME = backlightName;};
 
-  dependencies = [];
+  runtimeInputs = [
+    gawk
+    coreutils
+  ];
 }
diff --git a/pkgs/by-name/co/con2pdf/con2pdf.sh b/pkgs/by-name/co/con2pdf/con2pdf.sh
index 27c9d092..ebe35ad3 100755
--- a/pkgs/by-name/co/con2pdf/con2pdf.sh
+++ b/pkgs/by-name/co/con2pdf/con2pdf.sh
@@ -1,19 +1,18 @@
 #! /usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-# needed for help() and version
-# shellcheck disable=2034
-AUTHORS="Soispha"
-# shellcheck disable=2034
-YEARS="2023"
-# shellcheck disable=2034
-VERSION="1.0.0"
-
-# NAME is from the wrapper
-# shellcheck disable=SC2269
-NAME="$NAME"
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# TODO(@bpeetz): This should probably be rewritten in rust. <2025-04-14>
+
+NAME="con2pdf"
 help() {
     cat <<EOF
 Scan images and turn them into a pdf.
@@ -52,7 +51,7 @@ ARGUMENTS:
     NUM | *([0-9]) := 0 | 1 | 2 | 3 | 4
                             Possible numbers of pages, can be more than 4
 
-    DEVICE := [[$(cat %DEVICE_FUNCTION)]]
+    DEVICE := [[$(cat "$DEVICE_FUNCTION")]]
                             Possible scanner names
 
     METHOD := ADF | Flatbed
diff --git a/pkgs/by-name/co/con2pdf/package.nix b/pkgs/by-name/co/con2pdf/package.nix
index 8eb994fd..11d45ab5 100644
--- a/pkgs/by-name/co/con2pdf/package.nix
+++ b/pkgs/by-name/co/con2pdf/package.nix
@@ -1,24 +1,34 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
   writeText,
-  # dependencies
+  # Dependencies
   sane-backends,
   imagemagick,
   coreutils,
   fd,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "con2pdf";
-  src = ./con2pdf.sh;
-  generateCompletions = true;
-  keepPath = false;
-  dependencies = [
+  text = builtins.readFile ./con2pdf.sh;
+  inheritPath = false;
+
+  runtimeInputs = [
     sane-backends
     imagemagick
     coreutils
     fd
   ];
-  replacementStrings = {
+
+  runtimeEnv = {
     DEVICE_FUNCTION =
       # This is here, because escaping the whole function, to use it in the shell script
       # directly just isn't possible
diff --git a/pkgs/by-name/ct/ctpv-64-types/allow-up-to-64-types.patch.license b/pkgs/by-name/ct/ctpv-64-types/allow-up-to-64-types.patch.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/ct/ctpv-64-types/allow-up-to-64-types.patch.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/ct/ctpv-64-types/package.nix b/pkgs/by-name/ct/ctpv-64-types/package.nix
index 763b0325..087e1d28 100644
--- a/pkgs/by-name/ct/ctpv-64-types/package.nix
+++ b/pkgs/by-name/ct/ctpv-64-types/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {ctpv}:
 ctpv.overrideAttrs (finalAttrs: previousAttrs: {
   pname = previousAttrs.pname + "-64-types";
diff --git a/pkgs/by-name/fd/fd_list/fd_list.sh b/pkgs/by-name/fd/fd_list/fd_list.sh
new file mode 100755
index 00000000..568d73c8
--- /dev/null
+++ b/pkgs/by-name/fd/fd_list/fd_list.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# Lists all fds in use with their respective associated programs.
+# This can be useful, if you need to debug, why your system is out of fds.
+
+# This script is not POSIX shell compatible, as the `ulimit` flags `-H` and `-n` are not defined by POSIX (thus we use `bash` instead).
+
+find /proc/ -maxdepth 1 | while read -r path; do
+    pid="${path#/proc/}"
+    fd_dir="$path/fd/"
+
+    [ -d "$fd_dir" ] && echo "PID = $pid with $(find "$fd_dir" | wc -l) file descriptors"
+done | sort -rn -k5 | head | while read -r _ _ pid _ fdcount _; do
+    command_execution="$(ps -o cmd -p "$pid" -h)"
+
+    command_arguments="$(echo "$command_execution" | awk '{$1=""; print $0}')"
+    command_name="$(basename "$(echo "$command_execution" | awk '{print $1}')")"
+
+    command="$command_name$command_arguments"
+
+    printf "PID %8d with %4d file descriptors: %s\n" "$pid" "$fdcount" "$command"
+done || true
+# ^ Ignore SIGPIPE
+
+printf "\nThe Kernel has %7d file handles allocated.\n" "$(awk '{print $1}' /proc/sys/fs/file-nr)"
+printf "    -> The per-process fd maxium is: %d\n" "$(ulimit -Hn)"
+printf "    -> The per-process fd soft-maximum is: %d\n" "$(ulimit -n)"
+printf "    -> The global fd maximum is: %d\n" "$(cat /proc/sys/fs/file-max)"
+
+# vim: ft=sh
diff --git a/pkgs/by-name/fd/fd_list/package.nix b/pkgs/by-name/fd/fd_list/package.nix
new file mode 100644
index 00000000..34129b10
--- /dev/null
+++ b/pkgs/by-name/fd/fd_list/package.nix
@@ -0,0 +1,28 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  writeShellApplication,
+  findutils,
+  gawk,
+  coreutils,
+  procps,
+}:
+writeShellApplication {
+  name = "fd_list";
+  text = builtins.readFile ./fd_list.sh;
+
+  inheritPath = false;
+  runtimeInputs = [
+    findutils
+    gawk
+    coreutils
+    procps
+  ];
+}
diff --git a/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh b/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh
new file mode 100755
index 00000000..5b8d9469
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-flake/fupdate-flake.sh
@@ -0,0 +1,194 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+UPDATE_SCRIPT_NAME="update.sh"
+
+info() {
+    echo "Info: $1"
+}
+dbg() {
+    if [ "${DEBUG_ENABLE-unset}" != "unset" ]; then
+        echo "Debug: $1" >&2
+    fi
+}
+die() {
+    echo "Error: $1"
+    exit 1
+}
+
+# Search for a file “upwards”.
+# This will return the relative path from "$1" to the found file.
+#
+# # Type
+# upfind :: Path -> String -> Path
+#
+# # Arguments
+# $1
+# : The directory to use as start of your search.
+#
+# $2
+# : The file to search for.
+#
+# # Example
+# upfind "/home/user1" "/usr"
+# => /usr
+upfind() {
+    starting_directory="$(readlink --canonicalize "$1")"
+    search_string="$2"
+
+    current_directory="$starting_directory"
+
+    while
+        search_result=$(fd "$search_string" "$current_directory/" --max-depth 1)
+        dbg "upfind - search in $current_directory gives: $search_result"
+        [ -z "$search_result" ] && [ "$current_directory" != "/" ]
+    do current_directory=$(dirname "$current_directory"); done
+
+    realpath --relative-to="$1" "$search_result"
+}
+
+# Construct the storage path for the update script allowed hashes.
+#
+# # Type
+# get_storage_path :: Path -> Path
+#
+# # Arguments
+# $1
+# : The path to the update script
+#
+# # Returns
+# The constructed storage path.
+get_storage_path() {
+    update_script="$(realpath "$1")"
+
+    storage_path="$XDG_DATA_HOME/fupdate-flake/$update_script"
+    echo "$storage_path"
+}
+
+# Checks if a given path to the update script is allowed.
+#
+# # Type
+# is_allowed :: Path -> bool
+#
+# # Arguments
+# $1
+# : The path to the update script to check.
+#
+# # Return exit code
+# 0
+# : If the update script is allowed
+#
+# 1
+# : If it is not.
+is_allowed() {
+    update_script="$(realpath "$1")"
+
+    storage_path="$(get_storage_path "$update_script")"
+
+    # Use this invocation, to also include the path to the `$update_script`
+    update_script_hash="$(sha256sum "$update_script")"
+
+    if [ -f "$storage_path" ]; then
+        if [ "$(cat "$storage_path")" = "$update_script_hash" ]; then
+            return 0
+        else
+            return 1
+        fi
+    else
+        return 1
+    fi
+}
+
+# Asks the user if they want to allow a given script.
+#
+# # Type
+# ask_to_allow_update_script :: Path
+#
+# # Arguments
+# $1
+# : The path to the update script to ask for.
+ask_to_allow_update_script() {
+    update_script="$(realpath "$1")"
+
+    printf "\033[2J" # clear the screen
+    cat "$update_script"
+
+    printf "Do you want to allow this script?[N/y]: "
+    read -r allow
+
+    case "$allow" in
+    [yY])
+        info "Update script allowed."
+
+        storage_path="$(get_storage_path "$update_script")"
+        update_script_hash="$(sha256sum "$update_script")"
+
+        mkdir --parents "$(dirname "$storage_path")"
+        printf "%s" "$update_script_hash" >"$storage_path"
+        ;;
+    *)
+        info "Update script not allowed."
+        ;;
+    esac
+}
+
+# Performs a full update.
+# This consists of running an update script.
+# Additionally, it also checks for duplicated inputs in a `flake.lock` file, if it exists.
+#
+# # Type
+# update :: Path -> Path -> [String]
+#
+# # Arguments
+# $1
+# : The path to the update script to execute.
+#
+# $2
+# : The base directory from which to start the update.
+#
+# $3
+# : Arguments to pass to the update script.
+update() {
+    update_script="$1"
+    base_directory="$2"
+    shift 2
+
+    cd "$base_directory" || die "The provided base directory '$base_directory' cannot be accessed"
+    dbg "Changed directory to: $base_directory"
+
+    dbg "Executing update script ('$update_script') following args: '$*'"
+    "$update_script" "$@"
+
+    if [ -f "flake.lock" ] && grep '[^0-9]_[0-9]' flake.lock --quiet; then
+        batgrep '[^0-9]_[0-9]' flake.lock
+        die "Your flake.nix contains duplicate inputs!"
+    fi
+}
+
+main() {
+    base_directory="$(git rev-parse --show-toplevel)"
+    update_script="$(upfind "$PWD" "$UPDATE_SCRIPT_NAME")"
+    dbg "update_script is: $update_script"
+
+    if [ "$update_script" = "" ]; then
+        die "Failed to find update script."
+    elif is_allowed "$update_script"; then
+        update "$update_script" "$base_directory" "$@"
+    else
+        ask_to_allow_update_script "$update_script"
+        is_allowed "$update_script" && main "$@"
+    fi
+}
+
+main "$@"
+
+# vim: ft=sh
diff --git a/pkgs/by-name/fu/fupdate-flake/package.nix b/pkgs/by-name/fu/fupdate-flake/package.nix
new file mode 100644
index 00000000..c8646f26
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-flake/package.nix
@@ -0,0 +1,37 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  writeShellApplication,
+  # Dependencies
+  coreutils,
+  fd,
+  gnugrep,
+  bat-extras, # For `batgrep`
+  bat, # used by batgrep
+  gnused, # required by batgrep
+  git,
+}:
+writeShellApplication {
+  name = "fupdate-flake";
+  text = builtins.readFile ./fupdate-flake.sh;
+
+  # The `update.sh` script might actually want to keep the path.
+  inheritPath = true;
+
+  runtimeInputs = [
+    coreutils
+    fd
+    gnugrep
+    bat-extras.batgrep
+    bat # Used by `batgrep`
+    gnused # Required by `batgrep`
+    git
+  ];
+}
diff --git a/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh b/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh
new file mode 100755
index 00000000..4ec3e9e8
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-sys/fupdate-sys.sh
@@ -0,0 +1,168 @@
+#!/usr/bin/env dash
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# FIXME(@bpeetz): Ideally I could replace this script with a deployment tool. Thus we
+# would have the same tool on the server as I use in my config. <2025-04-14>
+
+# Shell library {{{
+die() {
+    error "$1"
+    if [ -n "$2" ]; then
+        exit "$2"
+    else
+        exit 1
+    fi
+}
+print() {
+    # shellcheck disable=SC2059
+    printf "$*"
+}
+println() {
+    # shellcheck disable=SC2059
+    printf "$*\n"
+}
+eprint() {
+    >&2 print "$@"
+}
+eprintln() {
+    >&2 println "$@"
+}
+if [ "${NO_COLOR-unset}" != "unset" ]; then
+    error() {
+        eprintln "==> ERROR:" "$*"
+    }
+    warning() {
+        eprintln "==> WARNING:" "$*"
+    }
+    debug() {
+        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "==> [Debug:]" "$*"
+    }
+    debug2() {
+        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln " -> [Debug:]" "$*"
+    }
+    msg() {
+        eprintln "==>" "$*"
+    }
+    msg2() {
+        eprintln " ->" "$*"
+    }
+    prompt() {
+        eprint "..>" "$*"
+    }
+else
+    error() {
+        eprintln "\033[1;91m==> ERROR:\033[0m" "\033[1;93m$*\033[0m"
+    }
+    warning() {
+        eprintln "\033[1;91m==> WARNING:\033[0m" "\033[1;93m$*\033[0m"
+    }
+    debug() {
+        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m==> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
+    }
+    debug2() {
+        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m -> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
+    }
+    msg() {
+        eprintln "\033[1;96m==>\033[0m" "\033[1;93m$*\033[0m"
+    }
+    msg2() {
+        eprintln "\033[1;96m ->\033[0m" "\033[1;93m$*\033[0m"
+    }
+    prompt() {
+        eprint "\033[1;96m..>\033[0m" "\033[1;93m$*\033[0m"
+    }
+fi
+# }}}
+
+NAME="update-sys"
+help() {
+    cat <<EOF
+This is a NixOS System flake update manager.
+
+USAGE:
+    $NAME [--branch <branchname>] [--help]
+
+OPTIONS:
+    --branch | -b  BRANCHNAME
+                                select a branch to update from.
+
+    --mode   | -m  MODE
+                                select a mode to update with
+
+    --help   | -h
+                                output this help.
+ARGUMENTS:
+    BRANCHNAME := [[ git branch --list --format '%(refname:short)' ]]
+                                The name of the branch to deploy the config from
+
+    MODE := switch|boot|test|build|dry-build|dry-activate|edit|repl|build-vm|build-vm-with-bootloader
+                                See the 'nixos-rebuild' manpage for more information about these modes.
+EOF
+    exit "$1"
+}
+BRANCH=""
+
+while [ "$#" -gt 0 ]; do
+    case "$1" in
+    "--help" | "-h")
+        help 0
+        ;;
+    "--branch" | "-b")
+        if [ "${2-unset}" != "unset" ]; then
+            BRANCH="$2"
+        else
+            error "$1 requires an argument"
+            help 1
+        fi
+        shift 2
+        ;;
+    "--mode" | "-m")
+        if [ "${2-unset}" != "unset" ]; then
+            MODE="$2"
+        else
+            error "$1 requires an argument"
+            help 1
+        fi
+        shift 2
+        ;;
+    *)
+        error "the option $1 does not exist!"
+        help 1
+        ;;
+    esac
+done
+
+cd /etc/nixos || die "No /etc/nixos"
+msg "Starting system update..."
+git remote update origin --prune >/dev/null 2>&1
+if ! [ "$BRANCH" = "" ]; then
+    git switch "$BRANCH" >/dev/null 2>&1 && msg2 "Switched to branch '$BRANCH'"
+fi
+msg2 "Updating git repository..."
+git pull --rebase
+
+# We use a tempfile, to make this truly async.
+default_branch=$(mktemp)
+cleanup() {
+    rm "$default_branch"
+}
+trap cleanup EXIT
+
+git remote show origin | grep 'HEAD' | cut -d':' -f2 | sed -e 's/^ *//g' -e 's/ *$//g' >"$default_branch" &
+
+msg2 "Updating system..."
+nixos-rebuild "${MODE-switch}"
+
+git switch "$(cat "$default_branch")" >/dev/null 2>&1 && msg2 "Switched to branch '$(cat "$default_branch")'"
+msg "Finished Update!"
+
+# vim: ft=sh
diff --git a/pkgs/by-name/fu/fupdate-sys/package.nix b/pkgs/by-name/fu/fupdate-sys/package.nix
new file mode 100644
index 00000000..7fd4674b
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate-sys/package.nix
@@ -0,0 +1,38 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  writeShellApplication,
+  # Dependencies
+  git,
+  nixos-rebuild,
+  sudo,
+  openssh,
+  coreutils,
+  mktemp,
+  gnugrep,
+  gnused,
+  systemd,
+}:
+writeShellApplication {
+  name = "fupdate-sys";
+  text = builtins.readFile ./fupdate-sys.sh;
+  inheritPath = false;
+  runtimeInputs = [
+    git
+    nixos-rebuild
+    sudo
+    openssh
+    coreutils
+    mktemp
+    gnugrep
+    gnused
+    systemd
+  ];
+}
diff --git a/pkgs/by-name/fu/fupdate/.envrc b/pkgs/by-name/fu/fupdate/.envrc
new file mode 100644
index 00000000..294de504
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/.envrc
@@ -0,0 +1,13 @@
+#!/usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use flake
diff --git a/pkgs/by-name/fu/fupdate/.gitignore b/pkgs/by-name/fu/fupdate/.gitignore
new file mode 100644
index 00000000..f255eebd
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/.gitignore
@@ -0,0 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+/target
+.direnv
diff --git a/pkgs/by-name/fu/fupdate/Cargo.lock b/pkgs/by-name/fu/fupdate/Cargo.lock
new file mode 100644
index 00000000..d2d07c28
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/Cargo.lock
@@ -0,0 +1,310 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+version = 4
+
+[[package]]
+name = "anstream"
+version = "0.6.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "clap"
+version = "4.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_complete"
+version = "4.5.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
+dependencies = [
+ "clap",
+ "clap_lex",
+ "is_executable",
+ "shlex",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "fupdate"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "clap_complete",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "is_executable"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/pkgs/by-name/fu/fupdate/Cargo.toml b/pkgs/by-name/fu/fupdate/Cargo.toml
new file mode 100644
index 00000000..3acd3bca
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/Cargo.toml
@@ -0,0 +1,81 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[package]
+name = "fupdate"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.98"
+clap = { version = "4.5.40", features = ["derive"] }
+clap_complete = { version = "4.5.54", features = ["unstable-dynamic"] }
+
+[profile.release]
+lto = true
+codegen-units = 1
+panic = "abort"
+split-debuginfo = "off"
+
+[lints.rust]
+# rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html
+warnings = "warn"
+future_incompatible = { level = "warn", priority = -1 }
+let_underscore = { level = "warn", priority = -1 }
+nonstandard_style = { level = "warn", priority = -1 }
+rust_2018_compatibility = { level = "warn", priority = -1 }
+rust_2018_idioms = { level = "warn", priority = -1 }
+rust_2021_compatibility = { level = "warn", priority = -1 }
+unused = { level = "warn", priority = -1 }
+# rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html
+# missing_docs = "warn"
+macro_use_extern_crate = "warn"
+meta_variable_misuse = "warn"
+missing_abi = "warn"
+missing_copy_implementations = "warn"
+missing_debug_implementations = "warn"
+non_ascii_idents = "warn"
+noop_method_call = "warn"
+single_use_lifetimes = "warn"
+trivial_casts = "warn"
+trivial_numeric_casts = "warn"
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "warn"
+unused_crate_dependencies = "warn"
+unused_import_braces = "warn"
+unused_lifetimes = "warn"
+unused_qualifications = "warn"
+variant_size_differences = "warn"
+
+[lints.rustdoc]
+# rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html
+broken_intra_doc_links = "warn"
+private_intra_doc_links = "warn"
+missing_crate_level_docs = "warn"
+private_doc_tests = "warn"
+invalid_codeblock_attributes = "warn"
+invalid_rust_codeblocks = "warn"
+bare_urls = "warn"
+
+[lints.clippy]
+# clippy allowed by default
+dbg_macro = "warn"
+# clippy categories https://doc.rust-lang.org/clippy/
+all = { level = "warn", priority = -1 }
+correctness = { level = "warn", priority = -1 }
+suspicious = { level = "warn", priority = -1 }
+style = { level = "warn", priority = -1 }
+complexity = { level = "warn", priority = -1 }
+perf = { level = "warn", priority = -1 }
+pedantic = { level = "warn", priority = -1 }
+missing_panics_doc = "allow"
+missing_errors_doc = "allow"
diff --git a/pkgs/by-name/fu/fupdate/flake.lock b/pkgs/by-name/fu/fupdate/flake.lock
new file mode 100644
index 00000000..a267d6fb
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/flake.lock
@@ -0,0 +1,27 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1750731501,
+        "narHash": "sha256-Ah4qq+SbwMaGkuXCibyg+Fwn00el4KmI3XFX6htfDuk=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "69dfebb3d175bde602f612915c5576a41b18486b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/pkgs/by-name/fu/fupdate/flake.lock.license b/pkgs/by-name/fu/fupdate/flake.lock.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/flake.lock.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/fu/fupdate/flake.nix b/pkgs/by-name/fu/fupdate/flake.nix
new file mode 100644
index 00000000..0ebceece
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/flake.nix
@@ -0,0 +1,34 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  description = "This is a Nix flake update manager.";
+
+  inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+  };
+
+  outputs = {nixpkgs, ...}: let
+    system = "x86_64-linux";
+    pkgs = nixpkgs.legacyPackages."${system}";
+  in {
+    devShells."${system}".default = pkgs.mkShell {
+      packages = with pkgs; [
+        cargo
+        clippy
+        rustc
+        rustfmt
+
+        cargo-edit
+      ];
+    };
+  };
+}
+# vim: ts=2
+
diff --git a/pkgs/by-name/fu/fupdate/fupdate.1.md b/pkgs/by-name/fu/fupdate/fupdate.1.md
deleted file mode 100644
index 710e8fb7..00000000
--- a/pkgs/by-name/fu/fupdate/fupdate.1.md
+++ /dev/null
@@ -1,70 +0,0 @@
-% FUPDATE(1) fupdate 1.0.0
-% Soispha
-% May 2023
-
-# NAME
-
-fupdate - updates your flake, while checking for common mistakes
-
-# SYNOPSIS
-
-**fupdate** list of \[*flake*|*\<some word>*|*--help*|*-h*\]
-
-# DESCRIPTION
-
-Argument can be stacked, this makes it possible to specify multiple targets to be updated in succession. See the Examples section for further details.
-
-No argument or *flake*
-: **fupdate**, when executed without arguments or with *flake*, will update your *flake.lock*, check for duplicate flake inputs, i.e., an input has an input declared, which you have also declared as input, and will run a script called *update.sh*, if you allow it.
-The allowance for the script is asked, when you run **fupdate** and the found script is not yet allowed. Furthermore, the allowance is based on the concrete sha256 hash of the script, so any changes will require another allowance.
-
-**\<some word>** as argument
-: If the executable **update-\<some word>** is reachable thought the PATH variable, than this is run. Otherwise, the program will exit.
-
-# OPTIONS
-
-**--help**, **-h**
-: Displays a help message and exit.
-
-**--version**, **-v**
-: Displays the software version and exit.
-
-# EXAMPLES
-
-**fupdate** or **fupdate flake**
-: Updates your *flake.lock*. See the Description section for further details.
-
-**fupdate sys**
-: Run the executable **update-sys**, if it exists. See the Description section for further details.
-
-**fupdate flake sys docs**
-: First updates your flake, then, if the command succeeded, runs **update-sys**, afterweich **update-docs** is run.
-
-# FILES
-
-*update.sh*
-: This is supposed to be a shell script located in your flake base directory, i.e., the directory which contains both a *flake.nix* and a *flake.lock* file.
-
-*~/.local/share/flake-update/*
-: **fupdate** will store the hashes to the allowed *update.sh* files here.
-
-# BUGS
-
-Report bugs to <https://codeberg.org/soispha/flake_update/issues>.
-
-# COPYRIGHT
-
-Copyright (C) 2023  Soispha
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <https://www.gnu.org/licenses/>.
diff --git a/pkgs/by-name/fu/fupdate/fupdate.sh b/pkgs/by-name/fu/fupdate/fupdate.sh
deleted file mode 100755
index 4322610a..00000000
--- a/pkgs/by-name/fu/fupdate/fupdate.sh
+++ /dev/null
@@ -1,197 +0,0 @@
-#! /usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-UPDATE_SCRIPT_NAME="update.sh"
-CONFIG_DIRECTORY_PATH="$HOME/.local/share/flake-update"
-
-# Both are used in version()
-# shellcheck disable=SC2034
-AUTHORS="Soispha"
-# shellcheck disable=SC2034
-YEARS="2023"
-
-UPDATE_SCRIPT_NOT_WANTED=false
-
-# Searches upward for a `UPDATE_SCRIPT_NAME` script
-# Returns a path to the script if it exists, otherwise nothing is returned
-check_for_update_script() {
-    dirname="$(search_upward_files "$UPDATE_SCRIPT_NAME")"
-    if [ "$dirname" ]; then
-        printf "%s/%s" "$dirname" "$UPDATE_SCRIPT_NAME"
-    fi
-}
-
-# Checks if a given path to the update script is allowed.
-# Takes the path as input
-# Return 0, if allowed, 1 if not.
-check_for_allowed_update_script() {
-    update_script="$1"
-    config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
-    update_script_hash="$(sha256sum "$update_script")"
-    if [ -f "$config_path" ]; then
-        if [ "$(cat "$config_path")" = "$update_script_hash" ]; then
-            dbg "Recorded hash matches"
-            return 0
-        else
-            dbg "Recorded hash \'$(cat "$config_path")\' does not match real hash \'$update_script_hash\', assuming not allowed"
-            return 1
-        fi
-    else
-        dbg "Path \'$config_path\' does not exist, assuming not allowed"
-        return 1
-    fi
-}
-
-# Asks the user if they want to allow a given script.
-# Takes the path as input
-ask_to_allow_update_script() {
-    update_script="$1"
-    config_path="${CONFIG_DIRECTORY_PATH}${update_script}"
-    update_script_hash="$(sha256sum "$update_script")"
-    println "\033[2J" # clear the screen
-    cat "$update_script"
-    readp "Do you want to allow this script?[N/y]: " allow
-    # shellcheck disable=SC2154
-    dbg "allow is: $allow"
-    case "$allow" in
-    [yY])
-        dbg "allowed script"
-        dbg "storing contents in: $config_path"
-        mkdir --parents "$(dirname "$config_path")"
-        print "$update_script_hash" >"$config_path"
-        ;;
-    *)
-        UPDATE_SCRIPT_NOT_ALLOWED=true
-        ;;
-    esac
-}
-
-# Runs the provided script and continues to update the nix flake
-# Takes the path to the script and the directory to the flake as arguments
-# If the path to the update script is empty, it will be ignored
-update() {
-    update_script="$1"
-    flake_base_dir="$2"
-    shift 2
-    dbg "Provided following args to update script: '$*'"
-
-    cd "$flake_base_dir" || die "Provided dir \'$flake_base_dir\' can not be accessed"
-    dbg "changed directory to: $flake_base_dir"
-
-    nix flake update
-
-    if ! [ "$update_script" = "" ] && ! [ "$UPDATE_SCRIPT_NOT_WANTED" = "true" ]; then
-        "$update_script" "$@"
-    fi
-
-    if grep '[^0-9]_[0-9]' flake.lock >/dev/null; then
-        batgrep '[^0-9]_[0-9]' flake.lock
-        die "Your flake.nix contains duplicate inputs!"
-    fi
-}
-
-help() {
-    cat <<EOF
-This is a Nix flake update manager.
-
-USAGE:
-    $NAME [--help | --version] [flake [--no-script] | <some other command>]
-
-OPTIONS:
-    --help   | -h
-                            Display this help and exit.
-
-    --version   | -v
-                            Display version and copyright information and exit.
-
-    --no-script
-                            Avoid running the 'update.sh' script
-COMMANDS:
-    flake
-                            update the flake project
-
-    <some other command>
-                            runs a executable called "update-<some other command>", if it exists
-EOF
-}
-
-main() {
-    if ! [ "$UPDATE_SCRIPT_NOT_ALLOWED" = true ]; then
-        update_script="$(check_for_update_script)"
-        flake_base_dir="$(search_flake_base_dir)" # Assume, that the update script is in the base dir
-        dbg "update_script is: $update_script"
-        dbg "flake_base_dir is: $flake_base_dir"
-
-        if [ "$update_script" = "" ]; then
-            update "" "$flake_base_dir" "$@"
-        elif check_for_allowed_update_script "$update_script" && ! [ "$update_script" = "" ]; then
-            update "$update_script" "$flake_base_dir" "$@"
-        else
-            ask_to_allow_update_script "$update_script"
-            main "$@"
-        fi
-    fi
-}
-
-if [ "$#" -eq 0 ]; then
-    main
-    exit 0
-fi
-
-for input in "$@"; do
-    case "$input" in
-    "--help" | "-h")
-        help
-        exit 0
-        ;;
-    "--version" | "-v")
-        version
-        exit 0
-        ;;
-    "--no-script" | "-n")
-        UPDATE_SCRIPT_NOT_WANTED=true
-        ;;
-    "--")
-        end_of_cli_options=true
-
-        # Stop processing args after that marker.
-        break
-        ;;
-    esac
-    [ "$end_of_cli_options" = "true" ] && break
-done
-
-case "$1" in
-"flake")
-    shift 1
-
-    # Filter out fupdate specific flags
-    while [ "$1" != "--" ]; do
-        # FIXME: This check allows to add a flag multiple times, but this should probably
-        # not be allowed <2024-03-29>
-        case "$1" in
-        "--no-script" | "-n")
-            shift 1
-            ;;
-        *)
-            break
-            ;;
-        esac
-    done
-
-    [ "$1" = "--" ] && shift 1
-    main "$@"
-    ;;
-*)
-    command="$1"
-    shift 1
-    [ "$1" = "--" ] && shift 1
-    if which update-"$command" >/dev/null 2>&1; then
-        update-"$command" "$@"
-    else
-        die "command \"update-$command\" is not executable, or does not exist"
-    fi
-    ;;
-esac
diff --git a/pkgs/by-name/fu/fupdate/package.nix b/pkgs/by-name/fu/fupdate/package.nix
index 66372add..86eccaf7 100644
--- a/pkgs/by-name/fu/fupdate/package.nix
+++ b/pkgs/by-name/fu/fupdate/package.nix
@@ -1,29 +1,41 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
-  dash,
-  lix,
-  gnugrep,
-  fd,
-  coreutils,
-  bat, # used by batgrep
-  bat-extras,
-  gnused, # required by batgrep
-  git, # needed to fetch through git
+  rustPlatform,
+  installShellFiles,
+  makeWrapper,
 }:
-sysLib.writeShellScript {
-  name = "fupdate";
-  src = ./fupdate.sh;
-  generateCompletions = true;
-  keepPath = true;
-  dependencies = [
-    dash
-    lix
-    gnugrep
-    fd
-    coreutils
-    bat # used by batgrep
-    bat-extras.batgrep
-    gnused # required by batgrep
-    git # needed to fetch through git
+rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "fupdate";
+  version = "0.1.0";
+
+  src = ./.;
+  cargoLock = {
+    lockFile = ./Cargo.lock;
+  };
+
+  buildInputs = [];
+
+  nativeBuildInputs = [
+    installShellFiles
+    makeWrapper
   ];
-}
+
+  postInstall = ''
+    installShellCompletion --cmd fupdate \
+      --bash <(COMPLETE=bash $out/bin/fupdate) \
+      --fish <(COMPLETE=fish $out/bin/fupdate) \
+      --zsh <(COMPLETE=zsh $out/bin/fupdate)
+  '';
+
+  meta = {
+    mainProgram = "fupdate";
+  };
+})
diff --git a/pkgs/by-name/fu/fupdate/src/cli.rs b/pkgs/by-name/fu/fupdate/src/cli.rs
new file mode 100644
index 00000000..6ebd1bc4
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/src/cli.rs
@@ -0,0 +1,56 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{env, ffi::OsStr, fs::read_dir};
+
+use clap::Parser;
+use clap_complete::{engine::ArgValueCompleter, CompletionCandidate};
+
+/// This is a Nix flake update manager.
+#[derive(Parser, Debug)]
+#[command(author, version, about)]
+pub struct CliArgs {
+    /// The command to execute.
+    #[arg(add = ArgValueCompleter::new(get_fupdate_commands))]
+    pub command: Vec<String>,
+}
+
+fn get_fupdate_commands(current: &OsStr) -> Vec<CompletionCandidate> {
+    let mut output = vec![];
+    let path = env::var("PATH").unwrap_or_default();
+
+    let Some(current) = current.to_str() else {
+        return output;
+    };
+
+    for directory in path.split(':') {
+        if let Ok(mut read) = read_dir(directory) {
+            for value in read.by_ref().flatten() {
+                let file_name = value.file_name();
+                let name = file_name.to_string_lossy();
+                let Some(stripped) = name.strip_prefix("fupdate-") else {
+                    continue;
+                };
+
+                if stripped.starts_with(current) {
+                    output.push(CompletionCandidate::new(
+                        value
+                            .file_name()
+                            .to_string_lossy()
+                            .strip_prefix("fupdate-")
+                            .expect("Exists"),
+                    ));
+                }
+            }
+        }
+    }
+
+    output
+}
diff --git a/pkgs/by-name/fu/fupdate/src/main.rs b/pkgs/by-name/fu/fupdate/src/main.rs
new file mode 100644
index 00000000..b4af6cd6
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/src/main.rs
@@ -0,0 +1,57 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::process::Command;
+
+use anyhow::{bail, Context, Result};
+use clap::{CommandFactory, Parser};
+
+pub mod cli;
+
+use crate::cli::CliArgs;
+
+fn main() -> Result<(), anyhow::Error> {
+    clap_complete::CompleteEnv::with_factory(CliArgs::command).complete();
+
+    let args = CliArgs::parse();
+
+    let command = args.command.first().map_or("flake", String::as_str);
+
+    {
+        let args = if args.command.len() > 1 {
+            &args.command[1..]
+        } else {
+            &[]
+        };
+
+        // println!("Running: `fupdate-{command} {}`", args.join(" "));
+
+        let child = Command::new(format!("fupdate-{command}"))
+            .args(args)
+            .status()
+            .with_context(|| format!("Failed to spawn `fupdate-{command}`"))?;
+
+        if !child.success() {
+            bail!("Command `fupdate-{command} {}` failed!", args.join(" "));
+        }
+    }
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod test {
+    use clap::CommandFactory;
+
+    #[test]
+    fn verify_cli() {
+        super::CliArgs::command().debug_assert();
+    }
+}
diff --git a/pkgs/by-name/fu/fupdate/update.sh b/pkgs/by-name/fu/fupdate/update.sh
new file mode 100755
index 00000000..8e36e13e
--- /dev/null
+++ b/pkgs/by-name/fu/fupdate/update.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[ "$1" = "upgrade" ] && cargo upgrade
+cargo update
diff --git a/pkgs/by-name/ge/generate_moz_extension/.envrc b/pkgs/by-name/ge/generate_moz_extension/.envrc
deleted file mode 100644
index 2f9f1a81..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/.envrc
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env sh
-use flake
diff --git a/pkgs/by-name/ge/generate_moz_extension/.gitignore b/pkgs/by-name/ge/generate_moz_extension/.gitignore
deleted file mode 100644
index f717ddd7..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target
-/result
-.direnv
diff --git a/pkgs/by-name/ge/generate_moz_extension/Cargo.lock b/pkgs/by-name/ge/generate_moz_extension/Cargo.lock
deleted file mode 100644
index 9ca10746..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/Cargo.lock
+++ /dev/null
@@ -1,1652 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "addr2line"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
-
-[[package]]
-name = "anyhow"
-version = "1.0.97"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
-
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
-[[package]]
-name = "autocfg"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
-
-[[package]]
-name = "backtrace"
-version = "0.3.74"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "base64"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-
-[[package]]
-name = "bitflags"
-version = "2.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
-
-[[package]]
-name = "bumpalo"
-version = "3.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
-
-[[package]]
-name = "bytes"
-version = "1.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
-
-[[package]]
-name = "cc"
-version = "1.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
-dependencies = [
- "shlex",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "core-foundation"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-
-[[package]]
-name = "displaydoc"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "encoding_rs"
-version = "0.8.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "equivalent"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
-
-[[package]]
-name = "errno"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
-dependencies = [
- "libc",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "fastrand"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "foreign-types"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-dependencies = [
- "foreign-types-shared",
-]
-
-[[package]]
-name = "foreign-types-shared"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "futures"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
-
-[[package]]
-name = "futures-task"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
-
-[[package]]
-name = "futures-util"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "generate_extensions"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "futures",
- "reqwest",
- "serde",
- "serde_json",
- "tokio",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
- "wasi 0.14.2+wasi-0.2.4",
-]
-
-[[package]]
-name = "gimli"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
-
-[[package]]
-name = "h2"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.15.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
-
-[[package]]
-name = "http"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
-dependencies = [
- "bytes",
- "http",
-]
-
-[[package]]
-name = "http-body-util"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
-dependencies = [
- "bytes",
- "futures-core",
- "http",
- "http-body",
- "pin-project-lite",
-]
-
-[[package]]
-name = "httparse"
-version = "1.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
-
-[[package]]
-name = "hyper"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "itoa",
- "pin-project-lite",
- "smallvec",
- "tokio",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.27.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
-dependencies = [
- "futures-util",
- "http",
- "hyper",
- "hyper-util",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-tls"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
-dependencies = [
- "bytes",
- "http-body-util",
- "hyper",
- "hyper-util",
- "native-tls",
- "tokio",
- "tokio-native-tls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "libc",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "icu_collections"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
-dependencies = [
- "displaydoc",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
-dependencies = [
- "displaydoc",
- "litemap",
- "tinystr",
- "writeable",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
-
-[[package]]
-name = "icu_normalizer"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_normalizer_data",
- "icu_properties",
- "icu_provider",
- "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
- "zerovec",
-]
-
-[[package]]
-name = "icu_normalizer_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
-
-[[package]]
-name = "icu_properties"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_locid_transform",
- "icu_properties_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_properties_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
-
-[[package]]
-name = "icu_provider"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_provider_macros",
- "stable_deref_trait",
- "tinystr",
- "writeable",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "idna"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
-dependencies = [
- "idna_adapter",
- "smallvec",
- "utf8_iter",
-]
-
-[[package]]
-name = "idna_adapter"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
-dependencies = [
- "icu_normalizer",
- "icu_properties",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
-dependencies = [
- "equivalent",
- "hashbrown",
-]
-
-[[package]]
-name = "ipnet"
-version = "2.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
-
-[[package]]
-name = "itoa"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
-
-[[package]]
-name = "js-sys"
-version = "0.3.77"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
-dependencies = [
- "once_cell",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.171"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
-
-[[package]]
-name = "litemap"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
-
-[[package]]
-name = "log"
-version = "0.4.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
-
-[[package]]
-name = "memchr"
-version = "2.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-
-[[package]]
-name = "mime"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-
-[[package]]
-name = "miniz_oxide"
-version = "0.8.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
-dependencies = [
- "adler2",
-]
-
-[[package]]
-name = "mio"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
-dependencies = [
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "native-tls"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
-dependencies = [
- "libc",
- "log",
- "openssl",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "security-framework",
- "security-framework-sys",
- "tempfile",
-]
-
-[[package]]
-name = "object"
-version = "0.36.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.21.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
-
-[[package]]
-name = "openssl"
-version = "0.10.72"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
-dependencies = [
- "bitflags",
- "cfg-if",
- "foreign-types",
- "libc",
- "once_cell",
- "openssl-macros",
- "openssl-sys",
-]
-
-[[package]]
-name = "openssl-macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "openssl-probe"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
-
-[[package]]
-name = "openssl-sys"
-version = "0.9.107"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "percent-encoding"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.94"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "r-efi"
-version = "5.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
-
-[[package]]
-name = "reqwest"
-version = "0.12.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
-dependencies = [
- "base64",
- "bytes",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-rustls",
- "hyper-tls",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "native-tls",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "rustls-pemfile",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "system-configuration",
- "tokio",
- "tokio-native-tls",
- "tower",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "windows-registry",
-]
-
-[[package]]
-name = "ring"
-version = "0.17.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
-dependencies = [
- "cc",
- "cfg-if",
- "getrandom 0.2.15",
- "libc",
- "untrusted",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
-
-[[package]]
-name = "rustix"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.23.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
-dependencies = [
- "once_cell",
- "rustls-pki-types",
- "rustls-webpki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
-dependencies = [
- "rustls-pki-types",
-]
-
-[[package]]
-name = "rustls-pki-types"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
-
-[[package]]
-name = "rustls-webpki"
-version = "0.103.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
-dependencies = [
- "ring",
- "rustls-pki-types",
- "untrusted",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
-
-[[package]]
-name = "ryu"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
-
-[[package]]
-name = "schannel"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "security-framework"
-version = "2.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
-dependencies = [
- "bitflags",
- "core-foundation",
- "core-foundation-sys",
- "libc",
- "security-framework-sys",
-]
-
-[[package]]
-name = "security-framework-sys"
-version = "2.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "serde"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.219"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.140"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
-dependencies = [
- "itoa",
- "memchr",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "slab"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
-
-[[package]]
-name = "socket2"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "subtle"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-
-[[package]]
-name = "syn"
-version = "2.0.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "sync_wrapper"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
-dependencies = [
- "futures-core",
-]
-
-[[package]]
-name = "synstructure"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "system-configuration"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
-dependencies = [
- "bitflags",
- "core-foundation",
- "system-configuration-sys",
-]
-
-[[package]]
-name = "system-configuration-sys"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.19.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
-dependencies = [
- "fastrand",
- "getrandom 0.3.2",
- "once_cell",
- "rustix",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "tinystr"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
-dependencies = [
- "displaydoc",
- "zerovec",
-]
-
-[[package]]
-name = "tokio"
-version = "1.44.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
-dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "pin-project-lite",
- "socket2",
- "tokio-macros",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tokio-native-tls"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.26.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
-dependencies = [
- "rustls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tower"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project-lite",
- "sync_wrapper",
- "tokio",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
-
-[[package]]
-name = "tower-service"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
-
-[[package]]
-name = "tracing"
-version = "0.1.41"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
-dependencies = [
- "pin-project-lite",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
-
-[[package]]
-name = "untrusted"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-
-[[package]]
-name = "url"
-version = "2.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
-]
-
-[[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
-name = "utf8_iter"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
-[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasi"
-version = "0.14.2+wasi-0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
-dependencies = [
- "wit-bindgen-rt",
-]
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
-dependencies = [
- "cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
-dependencies = [
- "cfg-if",
- "js-sys",
- "once_cell",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "web-sys"
-version = "0.3.77"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "windows-link"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
-
-[[package]]
-name = "windows-registry"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
-dependencies = [
- "windows-result",
- "windows-strings",
- "windows-targets 0.53.0",
-]
-
-[[package]]
-name = "windows-result"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "windows-strings"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm 0.52.6",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
-dependencies = [
- "windows_aarch64_gnullvm 0.53.0",
- "windows_aarch64_msvc 0.53.0",
- "windows_i686_gnu 0.53.0",
- "windows_i686_gnullvm 0.53.0",
- "windows_i686_msvc 0.53.0",
- "windows_x86_64_gnu 0.53.0",
- "windows_x86_64_gnullvm 0.53.0",
- "windows_x86_64_msvc 0.53.0",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
-
-[[package]]
-name = "wit-bindgen-rt"
-version = "0.39.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "write16"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
-
-[[package]]
-name = "writeable"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
-
-[[package]]
-name = "yoke"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
-dependencies = [
- "serde",
- "stable_deref_trait",
- "yoke-derive",
- "zerofrom",
-]
-
-[[package]]
-name = "yoke-derive"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "synstructure",
-]
-
-[[package]]
-name = "zerofrom"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
-dependencies = [
- "zerofrom-derive",
-]
-
-[[package]]
-name = "zerofrom-derive"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "synstructure",
-]
-
-[[package]]
-name = "zeroize"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
-
-[[package]]
-name = "zerovec"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
-dependencies = [
- "yoke",
- "zerofrom",
- "zerovec-derive",
-]
-
-[[package]]
-name = "zerovec-derive"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
diff --git a/pkgs/by-name/ge/generate_moz_extension/Cargo.toml b/pkgs/by-name/ge/generate_moz_extension/Cargo.toml
deleted file mode 100644
index 8a07d055..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "generate_extensions"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-anyhow = "1.0.97"
-futures = "0.3.31"
-reqwest = "0.12.15"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
diff --git a/pkgs/by-name/ge/generate_moz_extension/examples/generate_extensions.sh b/pkgs/by-name/ge/generate_moz_extension/examples/generate_extensions.sh
deleted file mode 100755
index 96802992..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/examples/generate_extensions.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-tmp=$(mktemp)
-cat <<EOF | awk '!/^\s*#/' >"$tmp"
-    darkreader:navbar
-    keepassxc-browser:navbar
-    vhack-libredirect:navbar
-    # torproject-snowflake:navbar
-    tridactyl-vim:menupanel
-    ublock-origin:menupanel
-EOF
-
-# The cat execution should be unquoted;
-# shellcheck disable=SC2046
-cargo run -- $(cat "$tmp")
-
-rm "$tmp"
diff --git a/pkgs/by-name/ge/generate_moz_extension/flake.lock b/pkgs/by-name/ge/generate_moz_extension/flake.lock
deleted file mode 100644
index b6478e18..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/flake.lock
+++ /dev/null
@@ -1,98 +0,0 @@
-{
-  "nodes": {
-    "crane": {
-      "locked": {
-        "lastModified": 1743908961,
-        "narHash": "sha256-e1idZdpnnHWuosI3KsBgAgrhMR05T2oqskXCmNzGPq0=",
-        "owner": "ipetkov",
-        "repo": "crane",
-        "rev": "80ceeec0dc94ef967c371dcdc56adb280328f591",
-        "type": "github"
-      },
-      "original": {
-        "owner": "ipetkov",
-        "repo": "crane",
-        "type": "github"
-      }
-    },
-    "flake-utils": {
-      "inputs": {
-        "systems": "systems"
-      },
-      "locked": {
-        "lastModified": 1731533236,
-        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "nixpkgs": {
-      "locked": {
-        "lastModified": 1744096231,
-        "narHash": "sha256-kUfx3FKU1Etnua3EaKvpeuXs7zoFiAcli1gBwkPvGSs=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "b2b0718004cc9a5bca610326de0a82e6ea75920b",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixpkgs-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "root": {
-      "inputs": {
-        "crane": "crane",
-        "flake-utils": "flake-utils",
-        "nixpkgs": "nixpkgs",
-        "rust-overlay": "rust-overlay"
-      }
-    },
-    "rust-overlay": {
-      "inputs": {
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1744166053,
-        "narHash": "sha256-mpI7OzFwp+fUeDtZYQbVZ2YmtxTN2UNrrOwbYD27xKU=",
-        "owner": "oxalica",
-        "repo": "rust-overlay",
-        "rev": "896158be1835589db6f42f45ef0a49b8b492cc66",
-        "type": "github"
-      },
-      "original": {
-        "owner": "oxalica",
-        "repo": "rust-overlay",
-        "type": "github"
-      }
-    },
-    "systems": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    }
-  },
-  "root": "root",
-  "version": 7
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/flake.nix b/pkgs/by-name/ge/generate_moz_extension/flake.nix
deleted file mode 100644
index 5575f90b..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/flake.nix
+++ /dev/null
@@ -1,75 +0,0 @@
-{
-  description = "A simple way to query the mozialla api for extension data";
-
-  inputs = {
-    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
-
-    crane = {
-      url = "github:ipetkov/crane";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-      };
-    };
-
-    flake-utils.url = "github:numtide/flake-utils";
-
-    rust-overlay = {
-      url = "github:oxalica/rust-overlay";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-        flake-utils.follows = "flake-utils";
-      };
-    };
-  };
-
-  outputs = {
-    self,
-    nixpkgs,
-    crane,
-    flake-utils,
-    rust-overlay,
-    ...
-  }:
-    flake-utils.lib.eachDefaultSystem (system: let
-      pkgs = import nixpkgs {
-        inherit system;
-        overlays = [(import rust-overlay)];
-      };
-
-      rust-stable = pkgs.rust-bin.stable.latest.default;
-      rust-minimal = pkgs.rust-bin.stable.latest.minimal;
-
-      craneLib = (crane.mkLib pkgs).overrideToolchain rust-minimal;
-
-      buildInputs = [
-        pkgs.openssl # needed for openssl
-      ];
-      nativeBuildInputs = [
-        pkgs.pkg-config # needed for openssl
-      ];
-
-      craneBuild = craneLib.buildPackage {
-        src = craneLib.cleanCargoSource ./.;
-        inherit buildInputs nativeBuildInputs;
-
-        doCheck = true;
-      };
-    in {
-      packages.default = craneBuild;
-      app.default = {
-        type = "app";
-        program = "${self.packages.${system}.default}/bin/generate_extensions";
-      };
-      devShells.default = pkgs.mkShell {
-        packages = with pkgs; [
-          cocogitto
-
-          rust-stable
-          cargo-edit
-        ];
-        inherit buildInputs nativeBuildInputs;
-      };
-    });
-}
-# vim: ts=2
-
diff --git a/pkgs/by-name/ge/generate_moz_extension/package.nix b/pkgs/by-name/ge/generate_moz_extension/package.nix
deleted file mode 100644
index abd95c77..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/package.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  rustPlatform,
-  openssl,
-  pkg-config,
-}:
-rustPlatform.buildRustPackage {
-  pname = "generate_firefox_extensions";
-  version = "0.1.0";
-
-  src = ./.;
-  cargoLock = {
-    lockFile = ./Cargo.lock;
-  };
-  buildInputs = [
-    openssl # needed for openssl-sys crate
-  ];
-  nativeBuildInputs = [
-    pkg-config # needed for openssl dependency
-  ];
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/res/generate_extensions.py b/pkgs/by-name/ge/generate_moz_extension/res/generate_extensions.py
deleted file mode 100644
index ee8cc966..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/res/generate_extensions.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-# source: https://github.com/etu/nixconfig/blob/ba47d577c8bfb4a1c06927c34ece34118f4a0460/modules/graphical/firefox/generate.py
-
-from concurrent.futures import ThreadPoolExecutor
-import json
-import requests
-
-EXTENSIONS = sorted(
-    [
-        "darkreader",
-        "firenvim",
-        "keepassxc-browser",
-        "simple-tab-groups",
-    ]
-)
-
-
-def index_ext(ext: str):
-    # print(f"Indexing {ext}...")
-
-    resp = requests.get(f"https://addons.mozilla.org/api/v5/addons/addon/{ext}/").json()
-    rel = resp["current_version"]
-
-    if not rel["file"]["hash"].startswith("sha256:"):
-        raise ValueError("Unhandled hash type")
-
-    return {
-        "pname": ext,
-        "version": rel["version"],
-        "addonId": resp["guid"],
-        "url": rel["file"]["url"],
-        "sha256": rel["file"]["hash"],
-    }
-
-
-if __name__ == "__main__":
-    # outfile = os.path.dirname(os.path.realpath(__file__)) + "/extensions.json"
-
-    with ThreadPoolExecutor() as e:
-        extensions = {ext: e.submit(index_ext, ext) for ext in EXTENSIONS}
-        extensions = {k: v.result() for k, v in extensions.items()}
-
-    # with open(outfile, "w") as f:
-    print(json.dumps(extensions, indent=2))
diff --git a/pkgs/by-name/ge/generate_moz_extension/res/reference.json b/pkgs/by-name/ge/generate_moz_extension/res/reference.json
deleted file mode 100644
index f46ea8ec..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/res/reference.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "darkreader": {
-    "pname": "darkreader",
-    "version": "4.9.62",
-    "addonId": "addon@darkreader.org",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4053589/darkreader-4.9.62.xpi",
-    "sha256": "sha256:e537a2cee45ed7c26f79ecd3ed362620e3f00d24c158532a58e163a63a3d60cc"
-  },
-  "firenvim": {
-    "pname": "firenvim",
-    "version": "0.2.14",
-    "addonId": "firenvim@lacamb.re",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4026386/firenvim-0.2.14.xpi",
-    "sha256": "sha256:a8c495a59e30eaabbb3fcd188db9b5e28b40bffefe41a3f0fa22ecc58c80c2b6"
-  },
-  "keepassxc-browser": {
-    "pname": "keepassxc-browser",
-    "version": "1.8.4",
-    "addonId": "keepassxc-browser@keepassxc.org",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4045866/keepassxc_browser-1.8.4.xpi",
-    "sha256": "sha256:cc39aa058cb8915cfc88424e2e1cebe3ccfc3f95d7bddb2abd0c4905d2b17719"
-  },
-  "simple-tab-groups": {
-    "pname": "simple-tab-groups",
-    "version": "4.7.2.1",
-    "addonId": "simple-tab-groups@drive4ik",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/3873608/simple_tab_groups-4.7.2.1.xpi",
-    "sha256": "sha256:75077589098ca62c00b86cf9554c6120bf8dc04c5f916fe26f84915f5147b2a4"
-  }
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/res/test.json b/pkgs/by-name/ge/generate_moz_extension/res/test.json
deleted file mode 100644
index daa1d19a..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/res/test.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "darkreader": {
-    "addon_id": "addon@darkreader.org",
-    "pname": "darkreader",
-    "sha256": "sha256:e537a2cee45ed7c26f79ecd3ed362620e3f00d24c158532a58e163a63a3d60cc",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4053589/darkreader-4.9.62.xpi",
-    "version": "4.9.62"
-  },
-  "firenvim": {
-    "addon_id": "firenvim@lacamb.re",
-    "pname": "firenvim",
-    "sha256": "sha256:a8c495a59e30eaabbb3fcd188db9b5e28b40bffefe41a3f0fa22ecc58c80c2b6",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4026386/firenvim-0.2.14.xpi",
-    "version": "0.2.14"
-  },
-  "keepassxc-browser": {
-    "addon_id": "keepassxc-browser@keepassxc.org",
-    "pname": "keepassxc-browser",
-    "sha256": "sha256:cc39aa058cb8915cfc88424e2e1cebe3ccfc3f95d7bddb2abd0c4905d2b17719",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/4045866/keepassxc_browser-1.8.4.xpi",
-    "version": "1.8.4"
-  },
-  "simple-tab-groups": {
-    "addon_id": "simple-tab-groups@drive4ik",
-    "pname": "simple-tab-groups",
-    "sha256": "sha256:75077589098ca62c00b86cf9554c6120bf8dc04c5f916fe26f84915f5147b2a4",
-    "url": "https://addons.mozilla.org/firefox/downloads/file/3873608/simple_tab_groups-4.7.2.1.xpi",
-    "version": "4.7.2.1"
-  }
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/src/main.rs b/pkgs/by-name/ge/generate_moz_extension/src/main.rs
deleted file mode 100644
index bde986a3..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/src/main.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-use std::env::args;
-
-use anyhow::{bail, Context};
-use futures::StreamExt;
-use reqwest::Client;
-use serde_json::{json, Map, Value};
-
-pub mod types;
-
-macro_rules! get_json_value {
-    ($key:expr, $json_value:ident, $type:ident, $get:ident) => {
-        match $json_value.get($key) {
-            Some(resp) => {
-                let resp = resp.to_owned();
-                if resp.$type() {
-                    resp.$get().expect(
-                        "The should have been checked in the if guard, so unpacking here is fine",
-                    ).to_owned()
-                } else {
-                    bail!(
-                        "Value {} => \n{}\n is not of type: {}",
-                        $key,
-                        resp,
-                        stringify!($type)
-                    );
-                }
-            }
-            None => {
-                bail!(
-                    "There seems to be no '{}' in your json data (json value: '{}')\n Has the api changend?",
-                    $key, serde_json::to_string_pretty(&$json_value).expect("Will always work")
-                );
-            }
-        }
-    };
-}
-
-use futures::stream::futures_unordered::FuturesUnordered;
-use types::{Extension, InputExtension};
-
-#[tokio::main]
-async fn main() -> anyhow::Result<()> {
-    let mut extensions: Vec<InputExtension> = vec![];
-    for input_extension in args()
-        .skip(1)
-        .map(|str| InputExtension::try_from(str))
-        .collect::<Vec<anyhow::Result<InputExtension>>>()
-    {
-        extensions.push(input_extension?);
-    }
-
-    let resulting_extensions = process_extensions(extensions).await?;
-
-    let mut output = Map::new();
-    for extension in resulting_extensions {
-        output.insert(extension.pname.clone(), json!(extension));
-    }
-
-    println!(
-        "{}",
-        serde_json::to_string_pretty(&serde_json::Value::Object(output)).expect(
-            "This is constructed from json, it should also be possible to serialize it again"
-        )
-    );
-    Ok(())
-}
-
-async fn process_extensions(extensions: Vec<InputExtension>) -> anyhow::Result<Vec<Extension>> {
-    let mut output = Vec::with_capacity(extensions.len());
-
-    let client = Client::new();
-    for extension in extensions
-        .iter()
-        .map(|ext| {
-            let local_client = &client;
-            index_extension(ext, local_client)
-        })
-        .collect::<FuturesUnordered<_>>()
-        .collect::<Vec<_>>()
-        .await
-    {
-        output.push(extension?);
-    }
-    Ok(output)
-}
-
-async fn index_extension(extension: &InputExtension, client: &Client) -> anyhow::Result<Extension> {
-    let response = client
-        .get(format!(
-            "https://addons.mozilla.org/api/v5/addons/addon/{}",
-            extension,
-        ))
-        .send()
-        .await
-        .context("Accessing the mozzila extenios api failed with error: {e}")?;
-
-    eprintln!("Indexing {} ({})...", extension, response.status());
-    let response: Value = serde_json::from_str(
-        &response
-            .text()
-            .await
-            .context("Turning the response to text fail with error: {e}")?,
-    )
-    .context("Deserializing the response failed! Error: {e}")?;
-
-    if let Some(detail) = response.get("detail") {
-        if detail == "Not found." {
-            bail!("Your extension ('{}') was not found!", extension);
-        }
-    };
-
-    let release = { get_json_value!("current_version", response, is_object, as_object) };
-
-    #[allow(non_snake_case)]
-    let addonId = { get_json_value!("guid", response, is_string, as_str) };
-
-    let version = { get_json_value!("version", release, is_string, as_str) };
-    let file = { get_json_value!("file", release, is_object, as_object) };
-
-    let url = { get_json_value!("url", file, is_string, as_str) };
-    let sha256 = {
-        let hash = get_json_value!("hash", file, is_string, as_str);
-        if hash.starts_with("sha256:") {
-            hash
-        } else {
-            bail!("This hash type is unhandled: {}", hash);
-        }
-    };
-
-    Ok(Extension {
-        pname: extension.moz_name.clone(),
-        default_area: extension.default_area,
-        version,
-        addonId,
-        url,
-        sha256,
-    })
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/src/types.rs b/pkgs/by-name/ge/generate_moz_extension/src/types.rs
deleted file mode 100644
index b830fe0d..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/src/types.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use std::fmt::Display;
-
-use anyhow::anyhow;
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize)]
-#[allow(non_snake_case)]
-pub struct Extension {
-    pub pname: String,
-    pub default_area: DefaultArea,
-    pub version: String,
-    pub addonId: String,
-    pub url: String,
-    pub sha256: String,
-}
-
-#[derive(Debug, Clone)]
-pub struct InputExtension {
-    pub moz_name: String,
-    pub default_area: DefaultArea,
-}
-#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
-#[allow(non_camel_case_types)]
-pub enum DefaultArea {
-    navbar,
-    menupanel,
-}
-
-impl Display for DefaultArea {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            DefaultArea::navbar => f.write_str("navbar"),
-            DefaultArea::menupanel => f.write_str("menupanel"),
-        }
-    }
-}
-
-impl TryFrom<&str> for DefaultArea {
-    type Error = anyhow::Error;
-
-    fn try_from(value: &str) -> Result<Self, Self::Error> {
-        match value {
-            "navbar" => Ok(Self::navbar),
-            "menupanel" => Ok(Self::menupanel),
-            _ => Err(anyhow!(
-                "Your <default_area> needs to be one of 'navbar' or 'menupanel', but is: '{}'",
-                value
-            )),
-        }
-    }
-}
-
-impl Display for InputExtension {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(&self.moz_name)
-    }
-}
-impl TryFrom<String> for InputExtension {
-    type Error = anyhow::Error;
-
-    fn try_from(value: String) -> Result<Self, Self::Error> {
-        if let Some((moz_name, default_area)) = value.split_once(':') {
-            Ok(Self {
-                moz_name: moz_name.to_owned(),
-                default_area: default_area.try_into()?,
-            })
-        } else {
-            Err(anyhow!("Can't parse the input string as a InputExtension!\n Needs to be: '<moz_name>:<default_area>'"))
-        }
-    }
-}
diff --git a/pkgs/by-name/ge/generate_moz_extension/update.sh b/pkgs/by-name/ge/generate_moz_extension/update.sh
deleted file mode 100755
index b9404867..00000000
--- a/pkgs/by-name/ge/generate_moz_extension/update.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env sh
-
-[ "$1" = "upgrade" ] && cargo upgrade
-cargo update
-
-# vim: ft=sh
diff --git a/pkgs/by-name/gi/git-cm/git-cm.sh b/pkgs/by-name/gi/git-cm/git-cm.sh
index 2204e4d6..7ab957df 100755
--- a/pkgs/by-name/gi/git-cm/git-cm.sh
+++ b/pkgs/by-name/gi/git-cm/git-cm.sh
@@ -1,7 +1,14 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 ROOT="$(git rev-parse --show-toplevel)"
 
@@ -13,6 +20,6 @@ else
 fi
 sed '1d' "$(git config commit.template)" >>"$ROOT/.git/COMMIT_TEMPLATE"
 
-git commit --template "$ROOT/.git/COMMIT_TEMPLATE" --verbose "$@"
+git commit --template "$ROOT/.git/COMMIT_TEMPLATE" "$@"
 
 # vim: ft=sh
diff --git a/pkgs/by-name/gi/git-cm/package.nix b/pkgs/by-name/gi/git-cm/package.nix
index a9949783..f124ce90 100644
--- a/pkgs/by-name/gi/git-cm/package.nix
+++ b/pkgs/by-name/gi/git-cm/package.nix
@@ -1,13 +1,26 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
   git,
   gnused,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "git-cm";
-  src = ./git-cm.sh;
-  keepPath = true;
-  dependencies = [
+  text = builtins.readFile ./git-cm.sh;
+
+  # We need access to the $EDITOR
+  inheritPath = true;
+
+  runtimeInputs = [
     git
     gnused
   ];
diff --git a/pkgs/by-name/gi/git-edit-index/git-edit-index.sh b/pkgs/by-name/gi/git-edit-index/git-edit-index.sh
index e73dc53c..46fbc9c5 100755
--- a/pkgs/by-name/gi/git-edit-index/git-edit-index.sh
+++ b/pkgs/by-name/gi/git-edit-index/git-edit-index.sh
@@ -1,19 +1,20 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-# needed for help() and version
-# shellcheck disable=2034
-AUTHORS="Soispha"
-# shellcheck disable=2034
-YEARS="2024"
-# shellcheck disable=2034
-VERSION="1.0.0"
-
-# NAME is from the wrapper
-# shellcheck disable=SC2269
-NAME="$NAME"
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+NAME="git-edit-index"
+
+warn() {
+    echo "WARNING: $1"
+}
 
 help() {
     cat <<EOF
@@ -55,37 +56,35 @@ materialize_file() {
 }
 
 edit() {
-    files_to_add="$(mktmp)"
-    realpath --relative-to=. "$@" >"$files_to_add"
+    files_to_add="$(mktemp)"
+    cleanup() {
+        rm "$files_to_add"
+    }
+    trap cleanup EXIT
 
-    index_files="$(mktmp)"
-    git diff --name-only --cached --diff-filter=AM >"$index_files"
+    realpath --relative-to=. "$@" >"$files_to_add"
 
-    while read -r file; do
-        if grep -q "$file" "$files_to_add"; then
-            sed -i "s|$file||" "$files_to_add"
-            materialize_file "$file"
+    git diff --name-only --cached --diff-filter=AM | while read -r index_file; do
+        if grep -q "$index_file" "$files_to_add"; then
+            sed -i "s|$index_file||" "$files_to_add"
+            materialize_file "$index_file"
         fi
-    done <"$index_files"
+    done
 
-    files_to_check="$(mktmp)"
-    clean "$files_to_add" >"$files_to_check"
-    if [ "$(wc -l <"$files_to_check")" -gt 0 ]; then
-        warn "Could not edit every file:"
+    unedided_files="$(sed '/^\s*$/d' "$files_to_add" | wc -l)"
+    if [ "$unedided_files" -gt 0 ]; then
+        warn "Failed to edit $unedided_files file(s):"
         cat "$files_to_add"
     fi
 }
 
+end_of_cli_options=false
 for arg in "$@"; do
     case "$arg" in
     "--help" | "-h")
         help
         exit 0
         ;;
-    "--version" | "-v")
-        version
-        exit 0
-        ;;
     "--")
         end_of_cli_options=true
         ;;
diff --git a/pkgs/by-name/gi/git-edit-index/package.nix b/pkgs/by-name/gi/git-edit-index/package.nix
index 8ac085bf..5e855b49 100644
--- a/pkgs/by-name/gi/git-edit-index/package.nix
+++ b/pkgs/by-name/gi/git-edit-index/package.nix
@@ -1,19 +1,29 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
+  coreutils,
   git,
   gnused,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "git-edit-index";
-  src = ./git-edit-index.sh;
-  generateCompletions = true;
+  text = builtins.readFile ./git-edit-index.sh;
 
   # `git-edit-index` starts neovim, wich might want to shell out from
-  keepPath = true;
+  inheritPath = true;
 
-  dependencies = [
+  runtimeInputs = [
+    coreutils
     git
     gnused
-    # $EDITOR
   ];
 }
diff --git a/pkgs/by-name/hi/hibernate/hibernate.sh b/pkgs/by-name/hi/hibernate/hibernate.sh
index 30868fd1..4a68e0d7 100755
--- a/pkgs/by-name/hi/hibernate/hibernate.sh
+++ b/pkgs/by-name/hi/hibernate/hibernate.sh
@@ -1,7 +1,16 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# TODO(@bpeetz): This functionality could be moved to `tskm`. <2025-04-14>
 
 context="$(task _get rc.context)"
 [ "$context" ] && task context none
diff --git a/pkgs/by-name/hi/hibernate/package.nix b/pkgs/by-name/hi/hibernate/package.nix
index 54716606..c2e8c0b6 100644
--- a/pkgs/by-name/hi/hibernate/package.nix
+++ b/pkgs/by-name/hi/hibernate/package.nix
@@ -1,14 +1,22 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
   systemd,
   taskwarrior3,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "hibernate";
-  src = ./hibernate.sh;
-  generateCompletions = false;
-  keepPath = false;
-  dependencies = [
+  text = builtins.readFile ./hibernate.sh;
+  inheritPath = false;
+  runtimeInputs = [
     systemd
     taskwarrior3
   ];
diff --git a/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch b/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
new file mode 100644
index 00000000..7bfdd7bc
--- /dev/null
+++ b/pkgs/by-name/i3/i3bar-river-patched/0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
@@ -0,0 +1,110 @@
+From 8ae692a461fad2f23231d50b78bb706408facfe6 Mon Sep 17 00:00:00 2001
+From: Benedikt Peetz <benedikt.peetz@b-peetz.de>
+Date: Tue, 20 May 2025 19:58:57 +0200
+Subject: [PATCH] feat(crate::bar): Put the leftmost block in the middle of the
+ bar
+
+This is a workaround for the limitation in the i3 blocks protocol, as
+this does not allow for centred blocks.
+---
+ src/bar.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 52 insertions(+), 11 deletions(-)
+
+diff --git a/src/bar.rs b/src/bar.rs
+index 96533e3..76f8025 100644
+--- a/src/bar.rs
++++ b/src/bar.rs
+@@ -338,16 +338,55 @@ impl Bar {
+         }
+ 
+         // Display the blocks
+-        render_blocks(
+-            &cairo_ctx,
+-            &ss.config,
+-            palette,
+-            ss.blocks_cache.get_computed(),
+-            &mut self.blocks_btns,
+-            offset_left,
+-            width_f,
+-            height_f,
+-        );
++        {
++            if !ss.blocks_cache.get_computed().is_empty() {
++                let first_block = &ss.blocks_cache.get_computed()[0];
++
++                let blocks = &ss.blocks_cache.get_computed()[1..];
++
++                let other_start = render_blocks(
++                    &cairo_ctx,
++                    &ss.config,
++                    palette,
++                    blocks,
++                    &mut self.blocks_btns,
++                    offset_left,
++                    width_f,
++                    height_f,
++                );
++
++                // Draw the first block _after_ the other ones, so that we can nudge it more to the
++                // left, if the others are spanning over the middle.
++                let mut start = (width_f / 2.0) - (first_block.full.width / 2.0);
++                if start + first_block.full.width > other_start {
++                    start = other_start
++                        - first_block.full.width
++                        - first_block.block.separator_block_width as f64;
++                }
++
++                first_block.full.render(
++                    &cairo_ctx,
++                    RenderOptions {
++                        x_offset: start,
++                        bar_height: height_f,
++                        fg_color: first_block.block.color.unwrap_or(palette.color),
++                        bg_color: first_block.block.background,
++                        r_left: ss.config.blocks_r,
++                        r_right: ss.config.blocks_r,
++                        overlap: ss.config.blocks_overlap,
++                    },
++                );
++
++                self.blocks_btns.push(
++                    start,
++                    first_block.full.width,
++                    (
++                        first_block.block.name.clone(),
++                        first_block.block.instance.clone(),
++                    ),
++                );
++            }
++        }
+ 
+         self.viewport
+             .set_destination(conn, self.width as i32, self.height as i32);
+@@ -422,7 +461,7 @@ fn render_blocks(
+     offset_left: f64,
+     full_width: f64,
+     full_height: f64,
+-) {
++) -> f64 {
+     context.rectangle(offset_left, 0.0, full_width - offset_left, full_height);
+     context.clip();
+ 
+@@ -507,6 +546,7 @@ fn render_blocks(
+     }
+ 
+     // Render blocks
++    let leftmost_start = full_width - blocks_width;
+     buttons.clear();
+     for series in blocks_computed {
+         let s_len = series.blocks.len();
+@@ -550,6 +590,7 @@ fn render_blocks(
+     }
+ 
+     context.reset_clip();
++    leftmost_start
+ }
+ 
+ fn layer_surface_cb(ctx: EventCtx<State, ZwlrLayerSurfaceV1>) {
+-- 
+2.49.0
+
diff --git a/pkgs/by-name/i3/i3bar-river-patched/package.nix b/pkgs/by-name/i3/i3bar-river-patched/package.nix
new file mode 100644
index 00000000..f6c3b5fd
--- /dev/null
+++ b/pkgs/by-name/i3/i3bar-river-patched/package.nix
@@ -0,0 +1,54 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  lib,
+  fetchFromGitHub,
+  rustPlatform,
+  pkg-config,
+  pango,
+}:
+rustPlatform.buildRustPackage {
+  pname = "i3bar-river-patched";
+  version = "1.1.0-unstable-2025-05-20";
+
+  src = fetchFromGitHub {
+    owner = "bpeetz";
+    repo = "i3bar-river";
+    rev = "bdaf362f24c143beeb92b783af15d3b99a0490e4";
+    hash = "sha256-jOv/DmXBpUCV/zbkWSKSYQ+yXcZZQY+T03rNre9hjn8=";
+  };
+
+  cargoHash = "sha256-jIB4XH67FmtPxAatHkuW8v5mNgr/KsyriaBNZ5t2dLo=";
+
+  cargoPatches = [
+    # TODO(@bpeetz): Open an issues, whether something like that could be up-streamed. <2025-05-20>
+    ./0001-feat-crate-bar-Put-the-leftmost-block-in-the-middle-.patch
+  ];
+
+  # Remove the WMs that I don't use.
+  buildNoDefaultFeatures = true;
+  buildFeatures = [
+    # "hyprland"
+    # "niri"
+    "river"
+  ];
+
+  nativeBuildInputs = [pkg-config];
+  buildInputs = [pango];
+
+  meta = with lib; {
+    description = "Port of i3bar for river";
+    homepage = "https://github.com/MaxVerevkin/i3bar-river";
+    license = licenses.gpl3Only;
+    maintainers = with maintainers; [nicegamer7];
+    mainProgram = "i3bar-river";
+    platforms = platforms.linux;
+  };
+}
diff --git a/pkgs/by-name/i3/i3status-rust-patched/package.nix b/pkgs/by-name/i3/i3status-rust-patched/package.nix
new file mode 100644
index 00000000..9f172d49
--- /dev/null
+++ b/pkgs/by-name/i3/i3status-rust-patched/package.nix
@@ -0,0 +1,34 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  i3status-rust,
+  fetchpatch2,
+}:
+i3status-rust.overrideAttrs (final: prev: {
+  pname = "${prev.pname}-patched";
+
+  patches =
+    (prev.patches or [])
+    ++ [
+      # Btrfs support for disk_space block.
+      (fetchpatch2 {
+        name = "disk_space: Support btrfs backend";
+        url = "https://patch-diff.githubusercontent.com/raw/greshake/i3status-rust/pull/2159.patch";
+        hash = "sha256-S2/biX6FTLJNfI9QVgwr+V8IGMRnSFIZnTrhc+1LvqQ=";
+      })
+
+      # Correctly calculate the used memory.
+      (fetchpatch2 {
+        name = "memory: Avoid estimating available memory, use kernel estimate instead";
+        url = "https://patch-diff.githubusercontent.com/raw/greshake/i3status-rust/pull/2160.patch";
+        hash = "sha256-1wB2KpXhC/UIxAgRioOYj/bnrzRSuaHAdbeoZ2O5E/Y=";
+      })
+    ];
+})
diff --git a/pkgs/by-name/lf/lf-make-map/.envrc b/pkgs/by-name/lf/lf-make-map/.envrc
index 2b742cf6..880b1809 100644
--- a/pkgs/by-name/lf/lf-make-map/.envrc
+++ b/pkgs/by-name/lf/lf-make-map/.envrc
@@ -1,4 +1,15 @@
 #!/usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use flake || use nix
 watch_file flake.nix
 
diff --git a/pkgs/by-name/lf/lf-make-map/.gitignore b/pkgs/by-name/lf/lf-make-map/.gitignore
index cb87f36f..8f29eabf 100644
--- a/pkgs/by-name/lf/lf-make-map/.gitignore
+++ b/pkgs/by-name/lf/lf-make-map/.gitignore
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 # build
 /target
 /result
diff --git a/pkgs/by-name/lf/lf-make-map/Cargo.lock b/pkgs/by-name/lf/lf-make-map/Cargo.lock
index 3e651a8b..05d59f92 100644
--- a/pkgs/by-name/lf/lf-make-map/Cargo.lock
+++ b/pkgs/by-name/lf/lf-make-map/Cargo.lock
@@ -1,5 +1,14 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 version = 4
 
 [[package]]
@@ -19,9 +28,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.18"
+version = "0.6.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -34,77 +43,77 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
 dependencies = [
  "windows-sys",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
 dependencies = [
  "anstyle",
- "once_cell",
+ "once_cell_polyfill",
  "windows-sys",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[package]]
 name = "bumpalo"
-version = "3.17.0"
+version = "3.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
 
 [[package]]
 name = "cc"
-version = "1.2.18"
+version = "1.2.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
+checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
 dependencies = [
  "shlex",
 ]
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "chrono"
-version = "0.4.40"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -116,9 +125,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -126,9 +135,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -138,9 +147,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -150,15 +159,15 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
 
 [[package]]
 name = "core-foundation-sys"
@@ -174,9 +183,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 
 [[package]]
 name = "iana-time-zone"
@@ -230,11 +239,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "keymaps"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea59e8e461942cf1d6a7ad938848d6fd2e40eb43799c21192c09226ecc86710f"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
 name = "lf-make-map"
 version = "0.1.0"
 dependencies = [
  "anyhow",
  "clap",
+ "keymaps",
  "log",
  "stderrlog",
  "walkdir",
@@ -242,9 +261,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 
 [[package]]
 name = "log"
@@ -268,10 +287,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -287,9 +312,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.20"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
 
 [[package]]
 name = "same-file"
@@ -327,9 +352,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.100"
+version = "2.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -346,13 +371,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
 dependencies = [
  "cfg-if",
- "once_cell",
 ]
 
 [[package]]
@@ -446,9 +490,9 @@ dependencies = [
 
 [[package]]
 name = "windows-core"
-version = "0.61.0"
+version = "0.61.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
 dependencies = [
  "windows-implement",
  "windows-interface",
@@ -481,24 +525,24 @@ dependencies = [
 
 [[package]]
 name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
 [[package]]
 name = "windows-result"
-version = "0.3.2"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
 dependencies = [
  "windows-link",
 ]
 
 [[package]]
 name = "windows-strings"
-version = "0.4.0"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
 dependencies = [
  "windows-link",
 ]
diff --git a/pkgs/by-name/lf/lf-make-map/Cargo.toml b/pkgs/by-name/lf/lf-make-map/Cargo.toml
index 4266ce0c..57dc61a5 100644
--- a/pkgs/by-name/lf/lf-make-map/Cargo.toml
+++ b/pkgs/by-name/lf/lf-make-map/Cargo.toml
@@ -1,14 +1,25 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 [package]
 name = "lf-make-map"
 description = "An automatic lf cd mapping generator"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.35", features = ["derive", "env"] }
+anyhow = "1.0.98"
+clap = { version = "4.5.40", features = ["derive", "env"] }
+keymaps = "1.2.0"
 log = "0.4.27"
 stderrlog = "0.6.0"
 walkdir = "2.5.0"
diff --git a/pkgs/by-name/lf/lf-make-map/README.md b/pkgs/by-name/lf/lf-make-map/README.md
index 0c57cede..4d5dc95c 100644
--- a/pkgs/by-name/lf/lf-make-map/README.md
+++ b/pkgs/by-name/lf/lf-make-map/README.md
@@ -1,3 +1,15 @@
+<!--
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+-->
+
 # Lf make map
 
 > An automatic lf cd mapping generator
diff --git a/pkgs/by-name/lf/lf-make-map/flake.lock b/pkgs/by-name/lf/lf-make-map/flake.lock
index 2e19a47e..95f58ef9 100644
--- a/pkgs/by-name/lf/lf-make-map/flake.lock
+++ b/pkgs/by-name/lf/lf-make-map/flake.lock
@@ -1,63 +1,12 @@
 {
   "nodes": {
-    "crane": {
-      "locked": {
-        "lastModified": 1743908961,
-        "narHash": "sha256-e1idZdpnnHWuosI3KsBgAgrhMR05T2oqskXCmNzGPq0=",
-        "owner": "ipetkov",
-        "repo": "crane",
-        "rev": "80ceeec0dc94ef967c371dcdc56adb280328f591",
-        "type": "github"
-      },
-      "original": {
-        "owner": "ipetkov",
-        "repo": "crane",
-        "type": "github"
-      }
-    },
-    "flake-compat": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1733328505,
-        "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
-        "owner": "edolstra",
-        "repo": "flake-compat",
-        "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
-        "type": "github"
-      },
-      "original": {
-        "owner": "edolstra",
-        "repo": "flake-compat",
-        "type": "github"
-      }
-    },
-    "flake-utils": {
-      "inputs": {
-        "systems": [
-          "systems"
-        ]
-      },
-      "locked": {
-        "lastModified": 1731533236,
-        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1744096231,
-        "narHash": "sha256-kUfx3FKU1Etnua3EaKvpeuXs7zoFiAcli1gBwkPvGSs=",
+        "lastModified": 1750994206,
+        "narHash": "sha256-3u6rEbIX9CN/5A5/mc3u0wIO1geZ0EhjvPBXmRDHqWM=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "b2b0718004cc9a5bca610326de0a82e6ea75920b",
+        "rev": "80d50fc87924c2a0d346372d242c27973cf8cdbf",
         "type": "github"
       },
       "original": {
@@ -69,68 +18,7 @@
     },
     "root": {
       "inputs": {
-        "crane": "crane",
-        "flake-compat": "flake-compat",
-        "flake-utils": "flake-utils",
-        "nixpkgs": "nixpkgs",
-        "rust-overlay": "rust-overlay",
-        "systems": "systems",
-        "treefmt-nix": "treefmt-nix"
-      }
-    },
-    "rust-overlay": {
-      "inputs": {
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1744166053,
-        "narHash": "sha256-mpI7OzFwp+fUeDtZYQbVZ2YmtxTN2UNrrOwbYD27xKU=",
-        "owner": "oxalica",
-        "repo": "rust-overlay",
-        "rev": "896158be1835589db6f42f45ef0a49b8b492cc66",
-        "type": "github"
-      },
-      "original": {
-        "owner": "oxalica",
-        "repo": "rust-overlay",
-        "type": "github"
-      }
-    },
-    "systems": {
-      "locked": {
-        "lastModified": 1680978846,
-        "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
-        "owner": "nix-systems",
-        "repo": "x86_64-linux",
-        "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "x86_64-linux",
-        "type": "github"
-      }
-    },
-    "treefmt-nix": {
-      "inputs": {
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1743748085,
-        "narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=",
-        "owner": "numtide",
-        "repo": "treefmt-nix",
-        "rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "treefmt-nix",
-        "type": "github"
+        "nixpkgs": "nixpkgs"
       }
     }
   },
diff --git a/pkgs/by-name/lf/lf-make-map/flake.lock.license b/pkgs/by-name/lf/lf-make-map/flake.lock.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/flake.lock.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/lf/lf-make-map/flake.nix b/pkgs/by-name/lf/lf-make-map/flake.nix
index dc8c24cc..20925aca 100644
--- a/pkgs/by-name/lf/lf-make-map/flake.nix
+++ b/pkgs/by-name/lf/lf-make-map/flake.nix
@@ -1,125 +1,34 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   description = "An automatic lf cd mapping generator";
 
   inputs = {
     nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+  };
 
-    treefmt-nix = {
-      url = "github:numtide/treefmt-nix";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-      };
-    };
-
-    crane = {
-      url = "github:ipetkov/crane";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-      };
-    };
-    rust-overlay = {
-      url = "github:oxalica/rust-overlay";
-      inputs = {
-        nixpkgs.follows = "nixpkgs";
-        flake-utils.follows = "flake-utils";
-      };
-    };
-
-    # inputs for following
-    systems = {
-      url = "github:nix-systems/x86_64-linux"; # only evaluate for this system
-    };
-    flake-compat = {
-      url = "github:edolstra/flake-compat";
-      flake = false;
-    };
-    flake-utils = {
-      url = "github:numtide/flake-utils";
-      inputs = {
-        systems.follows = "systems";
-      };
+  outputs = {nixpkgs, ...}: let
+    system = "x86_64-linux";
+    pkgs = nixpkgs.legacyPackages."${system}";
+  in {
+    devShells."${system}".default = pkgs.mkShell {
+      packages = with pkgs; [
+        cargo
+        clippy
+        rustc
+        rustfmt
+
+        cargo-edit
+      ];
     };
   };
-
-  outputs = {
-    self,
-    nixpkgs,
-    flake-utils,
-    treefmt-nix,
-    crane,
-    rust-overlay,
-    ...
-  }:
-    flake-utils.lib.eachDefaultSystem (system: let
-      pkgs = import nixpkgs {
-        inherit system;
-        overlays = [(import rust-overlay)];
-      };
-
-      nightly = false;
-      rust_minimal =
-        if nightly
-        then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal)
-        else pkgs.rust-bin.stable.latest.minimal;
-      rust_default =
-        if nightly
-        then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default)
-        else pkgs.rust-bin.stable.latest.default;
-
-      cargo_toml = craneLib.cleanCargoToml {cargoToml = ./Cargo.toml;};
-      pname = cargo_toml.package.name;
-
-      craneLib = (crane.mkLib pkgs).overrideToolchain rust_minimal;
-      craneBuild = craneLib.buildPackage {
-        src = craneLib.cleanCargoSource ./.;
-
-        doCheck = true;
-      };
-
-      manual = pkgs.stdenv.mkDerivation {
-        name = "${pname}-manual";
-        inherit (cargo_toml.package) version;
-
-        src = ./docs;
-        nativeBuildInputs = with pkgs; [pandoc];
-
-        buildPhase = ''
-          mkdir --parents $out/docs;
-
-          pandoc "./${pname}.1.md" -s -t man > $out/docs/${pname}.1
-        '';
-
-        installPhase = ''
-          install -D $out/docs/${pname}.1  $out/share/man/man1/${pname};
-        '';
-      };
-
-      treefmtEval = import ./treefmt.nix {inherit treefmt-nix pkgs;};
-    in {
-      packages.default = pkgs.symlinkJoin {
-        inherit (cargo_toml.package) name;
-
-        paths = [manual craneBuild];
-      };
-
-      checks = {
-        inherit craneBuild;
-        formatting = treefmtEval.config.build.check self;
-      };
-
-      formatter = treefmtEval.config.build.wrapper;
-
-      devShells.default = pkgs.mkShell {
-        packages = with pkgs; [
-          cocogitto
-
-          rust_default
-          cargo-edit
-
-          licensure
-        ];
-      };
-    });
 }
 # vim: ts=2
 
diff --git a/pkgs/by-name/lf/lf-make-map/package.nix b/pkgs/by-name/lf/lf-make-map/package.nix
index 8404927f..8f77c843 100644
--- a/pkgs/by-name/lf/lf-make-map/package.nix
+++ b/pkgs/by-name/lf/lf-make-map/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {rustPlatform}:
 rustPlatform.buildRustPackage {
   pname = "lf-make-map";
diff --git a/pkgs/by-name/lf/lf-make-map/src/cli.rs b/pkgs/by-name/lf/lf-make-map/src/cli.rs
index a398e451..70746984 100644
--- a/pkgs/by-name/lf/lf-make-map/src/cli.rs
+++ b/pkgs/by-name/lf/lf-make-map/src/cli.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::path::PathBuf;
 
 use clap::{ArgAction, Parser, Subcommand};
diff --git a/pkgs/by-name/lf/lf-make-map/src/main.rs b/pkgs/by-name/lf/lf-make-map/src/main.rs
index aaf79b20..d5d934e1 100644
--- a/pkgs/by-name/lf/lf-make-map/src/main.rs
+++ b/pkgs/by-name/lf/lf-make-map/src/main.rs
@@ -1,13 +1,23 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::path::{Path, PathBuf};
 
 use anyhow::{Context, Result};
 use clap::Parser;
 use cli::{Args, Command};
 use log::trace;
-use mapping::map_tree::MappingTree;
+use mapping::map_key::MapKey;
 use walkdir::{DirEntry, WalkDir};
 
-use crate::mapping::MapKey;
+use crate::mapping::MappingsTrie;
 
 mod cli;
 mod mapping;
@@ -24,7 +34,7 @@ fn main() -> anyhow::Result<()> {
         .timestamp(stderrlog::Timestamp::Off)
         .init()?;
 
-    let mut mappings = MappingTree::new();
+    let mut mappings = MappingsTrie::new();
 
     let relevant_directories = match &args.command {
         Command::Visualize { options } => &options.relevant_directories,
@@ -32,68 +42,62 @@ fn main() -> anyhow::Result<()> {
     };
 
     for dir in relevant_directories {
-        trace!("Processing '{}'..", dir.display());
-        let path = strip_path(&dir, &args.home_name)?;
+        trace!("START Processing '{}'..", dir.display());
+        let path = strip_path(dir, &args.home_name)?;
 
         mappings
             .include(path_to_str(path)?)
             .with_context(|| format!("Failed to include path: '{}'", path.display()))?;
+        trace!("END Finished processing {}.", dir.display());
+    }
+
+    trace!("Generated mappings for the relevant directories. Starting expanding to max depth.");
+    if log::log_enabled!(log::Level::Trace) {
+        eprintln!("{mappings}");
     }
 
     let home = path_to_str(&args.home_name)?.to_owned();
 
     let mut current_depth = 1;
     while current_depth != args.depth {
-        for (key, value) in mappings.iter(false) {
-            trace!(
-                "Adding to child ('{}' -> '{}')",
-                MapKey::display(&key),
-                value
-            );
-
-            let mut local_mappings = MappingTree::new();
-            for dir in WalkDir::new(extend(&home, &value)?)
+        for (keys, child) in mappings.0.iter().filter(|(_, child)| child.expendable) {
+            trace!("Adding to child '{}' ('{}')", MapKey::display(&keys), child);
+
+            let mut local_mappings = MappingsTrie::new();
+            for dir in WalkDir::new(extend(&home, &child.path)?)
                 .min_depth(1)
                 .max_depth(1)
                 .into_iter()
                 .filter_entry(|e| is_dir(e) && !is_hidden(e))
             {
-                let directory = dir
-                    .with_context(|| format!("Failed to read dir ('{}')", home.clone() + &value))?;
-                let path_to_strip = &PathBuf::from(extend(&home, &value)?);
-                let path = strip_path(&directory.path(), &path_to_strip)?;
+                let directory = dir.with_context(|| {
+                    format!("Failed to read dir ('{}')", home.clone() + &child.path)
+                })?;
+                let path_to_strip = &PathBuf::from(extend(&home, &child.path)?);
+                let path = strip_path(directory.path(), path_to_strip)?;
                 trace!(
-                    "Including: '{}' (after stripping '{}' from '{}' -> '{}' + '/' + '{}')",
+                    "Including: '{}' (after stripping '{}' from '{}')",
                     path.display(),
-                    directory.path().display(),
                     path_to_strip.display(),
-                    home,
-                    value
+                    directory.path().display(),
                 );
 
                 let gen_key = MapKey::new_ones_from_path(path_to_str(path)?, 1);
                 local_mappings
                     .insert(
                         &gen_key,
-                        path_to_str(strip_path(&directory.path(), &PathBuf::from(&home))?)?,
+                        path_to_str(strip_path(directory.path(), &PathBuf::from(&home))?)?,
                     )
                     .with_context(|| format!("Failed to include path: '{}'", path.display()))?;
             }
 
-            trace!("{}", local_mappings);
-
-            trace!(
-                "'{}' -> '{:#?}'",
-                MapKey::display(&key),
-                local_mappings.root_node()
-            );
-            mappings.interleave(&key, local_mappings.root_node().to_owned())?;
+            mappings.add_trie(&keys, local_mappings)?;
         }
         current_depth += 1;
     }
 
     match args.command {
-        Command::Visualize { .. } => println!("{}", mappings),
+        Command::Visualize { .. } => println!("{}", mappings.0),
         Command::Generate { .. } => println!("{}", mappings.to_lf_mappings(args.home_name)),
     }
 
@@ -120,7 +124,7 @@ fn is_dir(entry: &DirEntry) -> bool {
 }
 
 fn strip_path<'a>(path: &'a Path, to_strip: &Path) -> Result<&'a Path> {
-    path.strip_prefix(&to_strip).with_context(|| {
+    path.strip_prefix(to_strip).with_context(|| {
         format!(
             "'{}' is not under the specified home path ('{}')!",
             path.display(),
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/lf_mapping.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/lf_mapping.rs
new file mode 100644
index 00000000..f8a6182e
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/src/mapping/lf_mapping.rs
@@ -0,0 +1,35 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::path::PathBuf;
+
+use crate::mapping::MapKey;
+
+use super::MappingsTrie;
+
+impl MappingsTrie {
+    pub fn to_lf_mappings(&self, home_path: PathBuf) -> String {
+        let mut raw = self
+            .0
+            .iter()
+            .map(|(key, value)| {
+                format!(
+                    "map g{} cd \"{}\"\n",
+                    MapKey::display(&key),
+                    home_path.join(&value.path).display()
+                )
+            })
+            .collect::<Vec<String>>();
+
+        raw.sort();
+
+        raw.into_iter().collect()
+    }
+}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/map_key.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/map_key.rs
new file mode 100644
index 00000000..10b612fd
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/src/mapping/map_key.rs
@@ -0,0 +1,274 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::fmt::Write;
+
+use anyhow::bail;
+use log::debug;
+
+#[derive(Clone, Debug)]
+pub struct MapKey {
+    pub key: char,
+
+    pub(crate) resolution: usize,
+
+    /// Part of the path, used to derive the key
+    pub(crate) part_path: String,
+}
+
+impl std::hash::Hash for MapKey {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.key.hash(state)
+    }
+}
+
+impl Eq for MapKey {}
+impl PartialEq for MapKey {
+    fn eq(&self, other: &Self) -> bool {
+        self.key == other.key
+    }
+}
+
+impl Ord for MapKey {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.key.cmp(&other.key)
+    }
+}
+impl PartialOrd for MapKey {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl MapKey {
+    pub fn new_from_part_path(part_path: &str, resolution: usize) -> Vec<Self> {
+        let key = Self::part_path_to_key(part_path, resolution);
+
+        key.chars()
+            .map(|ch| Self {
+                key: ch,
+                resolution,
+                part_path: part_path.to_owned(),
+            })
+            .collect()
+    }
+
+    pub fn new_ones_from_path(path: &str, number_of_chars: usize) -> Vec<Self> {
+        let key: Vec<MapKey> = path
+            .split('/')
+            .flat_map(|part| Self::new_from_part_path(part, number_of_chars))
+            .collect();
+
+        debug!(
+            "Generated full MapKeys: '{}' -> '{}'",
+            path,
+            MapKey::display(&key)
+        );
+        key
+    }
+
+    pub fn increment(&self, target_resolution: usize) -> Vec<Self> {
+        let new_resolution = target_resolution;
+
+        // debug!("Incrementing: '{}' ('{}')", &self, &self.part_path);
+
+        let added_chars = if new_resolution < self.part_path.len() {
+            MapKey::part_path_to_key(&self.part_path, new_resolution)
+        } else {
+            let mut generated_chars =
+                MapKey::part_path_to_key(&self.part_path, self.part_path.len());
+
+            generated_chars.extend(
+                (0..(new_resolution - self.part_path.len()))
+                    .into_iter()
+                    .map(|_| self.part_path.chars().last().expect("This will exists")),
+            );
+
+            generated_chars
+        };
+
+        let part_path = self.part_path.clone();
+        let output: Vec<Self> = added_chars
+            .chars()
+            .enumerate()
+            .map(|(res, ch)| MapKey {
+                key: ch,
+                resolution: res + 1,
+                part_path: part_path.clone(),
+            })
+            .collect();
+
+        // debug!("Finished increment: '{}' ('{}')", MapKey::display(&output), output[0].part_path);
+        output
+    }
+
+    pub fn display(values: &[Self]) -> String {
+        values.iter().map(|value| value.key.clone()).collect()
+    }
+
+    fn part_path_to_key(part: &str, number_of_chars: usize) -> String {
+        fn make(pat: char, part: &str, number_of_chars: usize) -> String {
+            let mut acc = String::new();
+
+            if !part.split(pat).all(|part| part.len() > 0) {
+                panic!(
+                    "\
+Can't turn this path '{}' to a mapping.
+This should not happen, please report the bug!",
+                    part
+                )
+            }
+
+            let mut last_working = None;
+            for i in 0..number_of_chars {
+                for str in part.split(pat) {
+                    if acc.len() != number_of_chars {
+                        acc.push(match str.chars().nth(i) {
+                            Some(ch) => ch,
+                            None => {
+                                if let Some(last) = last_working {
+                                    str.chars().nth(last).expect("This should always exist")
+                                } else {
+                                    last_working = Some(i - 1);
+                                    str.chars().nth(i - 1).expect("This should always exist")
+                                }
+                            }
+                        })
+                    }
+                }
+            }
+
+            acc
+        }
+
+        let value = if part.contains('_') && !part.starts_with('_') && !part.ends_with('_') {
+            make('_', part, number_of_chars)
+        } else if part.contains('-') && !part.starts_with('-') && !part.ends_with('-') {
+            make('-', part, number_of_chars)
+        } else {
+            part.chars().take(number_of_chars).collect::<String>()
+        };
+
+        assert_eq!(
+            value.len(),
+            number_of_chars,
+            "'{}' does not have expected length of: {}",
+            value,
+            number_of_chars
+        );
+        value
+    }
+
+    /// Checks whether a tiebreak via the [`Self::increment`] function can result in unique keys.
+    pub fn can_tiebreak_with(&self, other: &Self) -> anyhow::Result<()> {
+        /// Check whether the `input` &str is composed of only one character.
+        /// If so, returns this character, otherwise returns None.
+        fn reduce_string(input: &str) -> Option<char> {
+            let first_char = input
+                .chars()
+                .take(1)
+                .last()
+                .expect("Should contain one char");
+
+            if input.chars().all(|ch| ch == first_char) {
+                Some(first_char)
+            } else {
+                None
+            }
+        }
+
+        /// Check whether `a` is a subset of `b` or `b` is a subset of `a`.
+        fn is_subset_either(a: &str, b: &str) -> bool {
+            /// Checks if `subset` is a subset of `set`.
+            ///
+            /// # Examples
+            /// ```
+            /// let a = "a";
+            /// let b = "aa";
+            /// assert!(is_subset(a, b))
+            /// ```
+            ///
+            /// ```
+            /// let a = "abc";
+            /// let b = "def";
+            /// assert!(!is_subset(a, b))
+            /// ```
+            fn is_subset(subset: &str, set: &str) -> bool {
+                let prefix: String = set.chars().take(subset.len()).collect();
+                let suffix: String = set.chars().skip(subset.len()).collect();
+
+                if prefix == subset {
+                    let clean_suffix = reduce_string(&suffix);
+                    if let Some(ch) = clean_suffix {
+                        ch == subset.chars().last().expect("Will exists")
+                    } else {
+                        false
+                    }
+                } else {
+                    false
+                }
+            }
+
+            match a.len().cmp(&b.len()) {
+                std::cmp::Ordering::Less => {
+                    // `b` is the longer string. As such we need to check if `a` is a subset of `b`.
+                    is_subset(a, b)
+                }
+                std::cmp::Ordering::Greater => {
+                    // `a` is the longer string. As such we need to check if `b` is a subset of `a`.
+                    is_subset(b, a)
+                }
+                std::cmp::Ordering::Equal => a == b,
+            }
+        }
+
+        if reduce_string(&other.part_path)
+            .is_some_and(|a| Some(a) == reduce_string(&self.part_path))
+        {
+            bail!(
+                "\
+The foreign_key ('{}', path_part: '{}' -> '{}') and our_key ('{}', path_part: '{}' -> '{}') \
+have an identical path_part (when duplicated chars are removed)!
+I cannot extended them via incrementation.
+Please rename the paths to fix this.
+                        ",
+                other,
+                &other.part_path,
+                reduce_string(&other.part_path).expect("Is some here"),
+                self,
+                &self.part_path,
+                reduce_string(&self.part_path).expect("Is some here"),
+            );
+        }
+
+        if is_subset_either(&other.part_path, &self.part_path) {
+            bail!(
+                "\
+The foreign_key ('{}', path_part: '{}') and our_key ('{}', path_part: '{}') \
+are subsets of one another!
+A discrimination through incrementation will not work!
+Please rename the paths to fix this.
+                        ",
+                other,
+                &other.part_path,
+                self,
+                &self.part_path,
+            );
+        }
+
+        Ok(())
+    }
+}
+
+impl std::fmt::Display for MapKey {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_char(self.key)
+    }
+}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/display.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/display.rs
deleted file mode 100644
index 65302e1e..00000000
--- a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/display.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-use std::fmt::Display;
-
-use crate::mapping::{
-    map_tree::{Node, NodeValue},
-    MapKey,
-};
-
-use super::MappingTree;
-
-impl Display for MappingTree {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        fn write_node(
-            f: &mut std::fmt::Formatter<'_>,
-            node: &Node,
-            indention: String,
-            location: Vec<MapKey>,
-            is_last: bool,
-            is_root: bool,
-        ) -> std::fmt::Result {
-            let node_value = match &node.value {
-                NodeValue::Parent { children: _ } => "<Parent>".to_owned(),
-                NodeValue::Child { path, extandable } => {
-                    path.to_owned() + if *extandable { " [exten.]" } else { " [stop]" }
-                }
-            };
-
-            let new_idention = indention.clone()
-                + if is_root {
-                    ""
-                } else {
-                    match is_last {
-                        true => "    ",
-                        false => "│   ",
-                    }
-                };
-
-            let bullet = match is_last {
-                true => String::from("└── "),
-                false => String::from("├── "),
-            };
-
-            if is_root {
-                write!(f, ": {}\n", node_value)?;
-            } else {
-                write!(
-                    f,
-                    "{}{}\x1b[1;33m{}\x1b[0m: {}\n",
-                    indention,
-                    bullet,
-                    MapKey::display(&location),
-                    node_value,
-                )?;
-            };
-
-            match &node.value {
-                NodeValue::Parent { children } => {
-                    let mut children_vec: Vec<(&MapKey, &Node)> = children.iter().collect();
-                    children_vec.sort_by(|(a, _), (b, _)| a.key.cmp(&b.key));
-
-                    let mut counter = 1;
-                    for (key, child) in &children_vec {
-                        let mut new_location = location.clone();
-                        new_location.push((*key).to_owned());
-
-                        write_node(
-                            f,
-                            child,
-                            new_idention.clone(),
-                            new_location.clone(),
-                            counter == children_vec.len(),
-                            false,
-                        )?;
-                        counter += 1;
-                    }
-                }
-                NodeValue::Child {
-                    path: _,
-                    extandable: _,
-                } => {
-                    // Do nothing and stop the recursion
-                }
-            }
-
-            Ok(())
-        }
-
-        write_node(f, &self.root, String::new(), vec![], false, true)?;
-
-        Ok(())
-    }
-}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/iterator.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/iterator.rs
deleted file mode 100644
index 4364bb2b..00000000
--- a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/iterator.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use crate::mapping::MapKey;
-
-use super::{MappingTree, Node, NodeValue};
-
-pub struct MappingTreeIterator {
-    children: Vec<(Vec<MapKey>, String)>,
-}
-
-impl MappingTreeIterator {
-    pub fn new(tree: &MappingTree, ignore_extendable: bool) -> Self {
-        let children = extract_child(vec![], &tree.root, ignore_extendable);
-
-        Self { children }
-    }
-}
-
-fn extract_child(
-    current_key: Vec<MapKey>,
-    node: &Node,
-    ignore_extendable: bool,
-) -> Vec<(Vec<MapKey>, String)> {
-    match &node.value {
-        NodeValue::Parent { children } => children
-            .iter()
-            .map(|(key, value)| {
-                let mut new_key = current_key.clone();
-                new_key.push(key.to_owned());
-
-                extract_child(new_key, value, ignore_extendable)
-            })
-            .flatten()
-            .collect(),
-        NodeValue::Child { path, extandable } => {
-            if ignore_extendable {
-                vec![(current_key, path.to_string())]
-            } else {
-                if *extandable {
-                    vec![(current_key, path.to_string())]
-                } else {
-                    vec![]
-                }
-            }
-        }
-    }
-}
-
-impl Iterator for MappingTreeIterator {
-    type Item = (Vec<MapKey>, String);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.children.pop()
-    }
-}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/lf_mapping.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/lf_mapping.rs
deleted file mode 100644
index ba485dc2..00000000
--- a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/lf_mapping.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use std::path::PathBuf;
-
-use crate::mapping::MapKey;
-
-use super::MappingTree;
-
-impl MappingTree {
-    pub fn to_lf_mappings(self, home_path: PathBuf) -> String {
-        let mut raw = self
-            .iter(true)
-            .map(|(key, value)| {
-                format!(
-                    "map g{} cd \"{}\"\n",
-                    MapKey::display(&key),
-                    home_path.join(&value).display()
-                )
-            })
-            .collect::<Vec<String>>();
-
-        raw.sort();
-
-        raw.into_iter().collect()
-    }
-}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/mod.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/mod.rs
deleted file mode 100644
index 35e6d91d..00000000
--- a/pkgs/by-name/lf/lf-make-map/src/mapping/map_tree/mod.rs
+++ /dev/null
@@ -1,402 +0,0 @@
-use std::{collections::HashMap, mem};
-
-use anyhow::{bail, Result};
-use log::debug;
-
-use self::iterator::MappingTreeIterator;
-
-use super::MapKey;
-
-pub mod display;
-pub mod iterator;
-pub mod lf_mapping;
-
-/// A prefix tree
-#[derive(Debug)]
-pub struct MappingTree {
-    root: Node,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum NodeValue {
-    Parent { children: HashMap<MapKey, Node> },
-    Child { path: String, extandable: bool },
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct Node {
-    value: NodeValue,
-}
-
-impl MappingTree {
-    pub fn new() -> Self {
-        Self {
-            root: Node::new_parent(),
-        }
-    }
-
-    pub fn root_node(&self) -> &Node {
-        &self.root
-    }
-
-    pub fn iter(&self, ignore_extendable: bool) -> MappingTreeIterator {
-        MappingTreeIterator::new(&self, ignore_extendable)
-    }
-
-    /// Returns the node at the key, otherwise None. The node can be changed
-    pub fn get_mut(&mut self, key: &[MapKey]) -> Option<&mut Node> {
-        let mut current_node = &mut self.root;
-        for ch in key.iter() {
-            if let NodeValue::Parent { children } = &mut current_node.value {
-                current_node = children.get_mut(&ch)?
-            } else {
-                return None;
-            }
-        }
-
-        Some(current_node)
-    }
-
-    /// Returns the node at the key, otherwise the last node that matched.
-    pub fn try_get(&self, key: &[MapKey]) -> (&Node, Vec<MapKey>) {
-        let mut current_node = &self.root;
-        let mut current_key = vec![];
-
-        for ch in key.iter() {
-            if let NodeValue::Parent { children } = &current_node.value {
-                current_node = if let Some(node) = children.get(&ch) {
-                    let (key, _value) = children
-                        .get_key_value(&ch)
-                        .expect("This exists, we checked");
-                    current_key.push(key.clone());
-
-                    node
-                } else {
-                    return (current_node, current_key);
-                };
-            } else {
-                return (current_node, current_key);
-            }
-        }
-
-        (current_node, current_key)
-    }
-
-    pub fn include(&mut self, path: &str) -> Result<()> {
-        let associated_key = MapKey::new_ones_from_path(path, 1);
-        self.insert(&associated_key, path)
-    }
-
-    pub fn insert(&mut self, key: &[MapKey], path: &str) -> Result<()> {
-        self.insert_node(key, Node::new_child(path.to_owned()))
-    }
-
-    pub fn interleave(&mut self, key: &[MapKey], node: Node) -> Result<()> {
-        let want_to_be_parent = self.get_mut(&key).expect("This value exists");
-        let (parent_value, _parent_children) = if let NodeValue::Parent { children } = node.value {
-            (
-                NodeValue::Parent {
-                    children: children.clone(),
-                },
-                children,
-            )
-        } else {
-            unreachable!("This value will be a parent")
-        };
-
-        let child_value = mem::replace(&mut want_to_be_parent.value, parent_value);
-        assert!(matches!(
-            child_value,
-            NodeValue::Child {
-                path: _,
-                extandable: _
-            }
-        ));
-
-        let child_value = if let NodeValue::Child {
-            path,
-            extandable: _,
-        } = child_value
-        {
-            NodeValue::Child {
-                path,
-                extandable: false,
-            }
-        } else {
-            unreachable!("This is only a child value")
-        };
-
-        let child = Node { value: child_value };
-
-        let mut new_key = key.to_vec();
-        new_key.push(MapKey {
-            key: '.',
-            part_path: ".".to_owned(),
-            resolution: 1,
-        });
-        self.insert_node(&new_key, child)?;
-        Ok(())
-    }
-
-    pub fn insert_node(&mut self, key: &[MapKey], node: Node) -> Result<()> {
-        let (_node, found_key) = self.try_get(key).clone();
-
-        if found_key != key {
-            let needed_nodes_key = key
-                .strip_prefix(&found_key[..])
-                .expect("The node's location is a prefix");
-
-            let needed_nodes_length = needed_nodes_key.iter().count();
-
-            let mut current_node = self
-                .get_mut(&found_key[..])
-                .expect("This should always exists");
-            let mut current_location = found_key.clone();
-            let mut counter = 1;
-
-            for ch in needed_nodes_key.iter() {
-                current_location.push(ch.to_owned());
-
-                let next_node = if counter == needed_nodes_length {
-                    node.clone()
-                } else {
-                    Node::new_parent()
-                };
-
-                current_node = match &current_node.value {
-                    NodeValue::Parent { children } => {
-                        assert_eq!(children.get(&ch), None);
-
-                        let children =
-                            if let NodeValue::Parent { children } = &mut current_node.value {
-                                children
-                            } else {
-                                unreachable!("This is a parent, we cheched")
-                            };
-
-                        children.insert(ch.to_owned(), next_node);
-                        children.get_mut(&ch).expect("Was just inserted")
-                    }
-                    NodeValue::Child {
-                        path,
-                        extandable: _,
-                    } => {
-                        // A node that should be a parent was classified
-                        // as child before:
-                        //
-                        //  1. Remove the child node and replace it with a parent one.
-                        //  2. Add the child node to the parent node as child, but with a '.' as MapKey.
-                        //  3. Add the original node also as child to the parent node.
-
-                        let mut children = HashMap::new();
-                        let move_child_node = Node::new_child(path.to_owned());
-
-                        children.insert(
-                            MapKey {
-                                key: '.',
-                                part_path: ".".to_owned(),
-                                resolution: 1,
-                            },
-                            move_child_node,
-                        );
-                        children.insert(ch.to_owned(), next_node);
-
-                        current_node.value = NodeValue::Parent { children };
-
-                        let children =
-                            if let NodeValue::Parent { children } = &mut current_node.value {
-                                children
-                            } else {
-                                unreachable!("We just inserted the parent value.")
-                            };
-
-                        children.get_mut(&ch).expect("Was just inserted")
-                    }
-                };
-
-                counter += 1;
-            }
-        } else {
-            fn reduce_string(a: &str) -> Option<char> {
-                let first_char = a.chars().take(1).last().expect("Should contain one char");
-
-                if a.chars().all(|ch| ch == first_char) {
-                    return Some(first_char);
-                } else {
-                    return None;
-                }
-            }
-            fn check_subset(a: &str, b: &str) -> bool {
-                if a.len() > b.len() {
-                    let a_prefix: String = a.chars().take(b.len()).collect();
-                    let a_suffix: String = a.chars().skip(b.len()).collect();
-
-                    if a_prefix == b {
-                        let clean_suffix = reduce_string(&a_suffix);
-                        if let Some(ch) = clean_suffix {
-                            ch == b.chars().last().expect("Will match")
-                        } else {
-                            false
-                        }
-                    } else {
-                        false
-                    }
-                } else if b.len() > a.len() {
-                    let b_prefix: String = b.chars().take(a.len()).collect();
-                    let b_suffix: String = b.chars().skip(a.len()).collect();
-
-                    if b_prefix == a {
-                        let clean_suffix = reduce_string(&b_suffix);
-                        if let Some(ch) = clean_suffix {
-                            ch == a.chars().last().expect("Will match")
-                        } else {
-                            false
-                        }
-                    } else {
-                        false
-                    }
-                } else {
-                    a == b
-                }
-            }
-
-            // Another node was already inserted with the same key!
-            // So we simple increase the resolution of the other node and this node, until their
-            // keys are not the same anymore.
-            // This only includes the last segment of the `MapKey`
-            //
-            // 1. Change both keys, until they are not equal any more
-            // 2. Move the wrongly placed node to the new place.
-            // 3. Insert our node.
-            let mut foreign_key = vec![found_key.last().expect("This will exist").clone()];
-            let mut our_key = vec![key.last().expect("This will exist").clone()];
-
-            debug!(
-                "'{}' ('{}') and '{}' ('{}') are the same, try to find a better combination!",
-                MapKey::display(&our_key),
-                our_key[0].part_path,
-                MapKey::display(&foreign_key),
-                foreign_key[0].part_path,
-            );
-
-            // The 'a' and 'b' stuff is here, to ensure that both returning None will not match
-            // this condition.
-            if reduce_string(&foreign_key[0].part_path).unwrap_or('a')
-                == reduce_string(&our_key[0].part_path).unwrap_or('b')
-            {
-                bail!(
-                    "\
-The foreign_key ('{}', path_part: '{}' -> '{}') and our_key ('{}', path_part: '{}' -> '{}') \
-have an identical path_part (when duplicated chars are removed)!
-I cannot extended them via incrementation.
-Please rename the paths to fix this.
-                        ",
-                    MapKey::display(&foreign_key),
-                    &foreign_key[0].part_path,
-                    reduce_string(&foreign_key[0].part_path).expect("Is some here"),
-                    MapKey::display(&our_key),
-                    &our_key[0].part_path,
-                    reduce_string(&our_key[0].part_path).expect("Is some here"),
-                );
-            }
-
-            if check_subset(&foreign_key[0].part_path, &our_key[0].part_path) {
-                bail!(
-                    "\
-The foreign_key ('{}', path_part: '{}') and our_key ('{}', path_part: '{}') \
-are subsets of one another!
-A discrimination through incrementation will not work!
-Please rename the paths to fix this.
-                        ",
-                    MapKey::display(&foreign_key),
-                    &foreign_key[0].part_path,
-                    MapKey::display(&our_key),
-                    &our_key[0].part_path,
-                );
-            }
-
-            while our_key == foreign_key {
-                our_key = our_key[0].increment(our_key[our_key.len() - 1].resolution + 1);
-                foreign_key =
-                    foreign_key[0].increment(foreign_key[foreign_key.len() - 1].resolution + 1);
-                debug!(
-                    "Now its: '{}' ('{}') and '{}' ('{}')",
-                    MapKey::display(&our_key),
-                    our_key[0].part_path,
-                    MapKey::display(&foreign_key),
-                    foreign_key[0].part_path,
-                );
-            }
-
-            debug!(
-                "Found a better one: '{}' ('{}') and '{}' ('{}')",
-                MapKey::display(&our_key),
-                our_key[0].part_path,
-                MapKey::display(&foreign_key),
-                foreign_key[0].part_path,
-            );
-
-            let parent = self
-                .get_mut(&found_key[..&found_key.len() - 1])
-                .expect("This will exist");
-
-            if let NodeValue::Parent { children } = &mut parent.value {
-                if let NodeValue::Child {
-                    path: _,
-                    extandable: _,
-                } = children
-                    .get(found_key.last().expect("Exists"))
-                    .expect("This node also exists")
-                    .value
-                {
-                    let old = children
-                        .remove(found_key.last().expect("This will exist"))
-                        .expect("This will be there");
-
-                    let full_foreign_key: Vec<_> = found_key
-                        .clone()
-                        .into_iter()
-                        .rev()
-                        .skip(1)
-                        .rev()
-                        .chain(foreign_key.clone().into_iter())
-                        .collect();
-                    self.insert_node(&full_foreign_key, old.clone())?;
-                }
-
-                let full_our_key: Vec<_> = key
-                    .to_vec()
-                    .into_iter()
-                    .rev()
-                    .skip(1)
-                    .rev()
-                    .chain(our_key.clone().into_iter())
-                    .collect();
-
-                self.insert_node(&full_our_key, node.clone())?;
-            } else {
-                unreachable!("This node will be a parent");
-            }
-        }
-
-        Ok(())
-    }
-}
-
-impl Node {
-    pub fn new_child(path: String) -> Self {
-        Self {
-            value: NodeValue::Child {
-                path,
-                extandable: true,
-            },
-        }
-    }
-    pub fn new_parent() -> Self {
-        Self {
-            value: NodeValue::Parent {
-                children: HashMap::new(),
-            },
-        }
-    }
-}
diff --git a/pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs b/pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs
index 114fdca0..21392388 100644
--- a/pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs
+++ b/pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs
@@ -1,156 +1,231 @@
-use std::{
-    fmt::{Display, Write},
-    hash::Hash,
-};
-
-use log::debug;
-
-pub mod map_tree;
-
-#[derive(Clone, Debug, Eq)]
-pub struct MapKey {
-    pub key: char,
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use anyhow::Result;
+use keymaps::map_tree::{Node, Trie};
+use log::{Level, debug, log_enabled, trace};
+use map_key::MapKey;
+
+pub mod lf_mapping;
+pub mod map_key;
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct MapChild {
+    pub path: String,
+    pub expendable: bool,
+}
 
-    resolution: usize,
+impl std::fmt::Display for MapChild {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(&self.path)?;
+        if !self.expendable {
+            f.write_str(" [stop]")?;
+        }
 
-    /// Part of the path, used to derive the key
-    part_path: String,
+        Ok(())
+    }
 }
 
-impl Hash for MapKey {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.key.hash(state)
+pub struct MappingsTrie(pub Trie<MapKey, MapChild>);
+impl std::fmt::Display for MappingsTrie {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.0.fmt(f)
     }
 }
 
-impl PartialEq for MapKey {
-    fn eq(&self, other: &Self) -> bool {
-        self.key == other.key
+impl MappingsTrie {
+    pub fn new() -> Self {
+        Self(Trie::new())
     }
-}
 
-impl MapKey {
-    pub fn new_from_part_path(part_path: &str, resolution: usize) -> Vec<Self> {
-        let key = Self::part_path_to_key(&part_path, resolution);
-
-        key.chars()
-            .map(|ch| Self {
-                key: ch,
-                resolution,
-                part_path: part_path.to_owned(),
-            })
-            .collect()
+    pub(crate) fn include(&mut self, path: &str) -> Result<()> {
+        let associated_key = MapKey::new_ones_from_path(path, 1);
+        self.insert(&associated_key, path)
     }
 
-    pub fn new_ones_from_path(path: &str, number_of_chars: usize) -> Vec<Self> {
-        let key: Vec<MapKey> = path
-            .split('/')
-            .map(|part| Self::new_from_part_path(part, number_of_chars))
-            .flatten()
-            .collect();
-
-        debug!(
-            "Generated full MapKeys: '{}' -> '{}'",
-            path,
-            MapKey::display(&key)
-        );
-        key
+    pub(crate) fn insert(&mut self, keys: &[MapKey], path: &str) -> Result<()> {
+        let value = Node::new_child(MapChild {
+            path: path.to_owned(),
+            expendable: true,
+        });
+        self.insert_node(keys, value)
     }
 
-    pub fn increment(&self, target_resolution: usize) -> Vec<Self> {
-        let new_resolution = target_resolution;
+    pub(crate) fn insert_node(
+        &mut self,
+        keys: &[MapKey],
+        node: Node<MapKey, MapChild>,
+    ) -> Result<()> {
+        if let Err(err) = self.0.insert_node(keys, &node) {
+            match err {
+                keymaps::error::TrieInsert::KeyAlreadySet(found_keys) => {
+                    // Another node was already inserted with the same key!
+                    // So we simple increase the resolution of the other node and this node, until their
+                    // keys are no longer equal.
+                    // This only includes the last segment of the `MapKey`
+                    //
+                    // 1. Change both keys, until they are not equal any more
+                    // 2. Move the wrongly placed node to the new place.
+                    // 3. Insert our node.
+                    assert_eq!(keys, found_keys);
+
+                    let mut foreign_keys =
+                        vec![found_keys.last().expect("This will exist").clone()];
+                    let mut our_keys = vec![keys.last().expect("This will exist").clone()];
+
+                    debug!(
+                        "'{}' ('{}') and '{}' ('{}') are the same, trying to find a better combination!",
+                        MapKey::display(&our_keys),
+                        our_keys[0].part_path,
+                        MapKey::display(&foreign_keys),
+                        foreign_keys[0].part_path,
+                    );
+
+                    our_keys[0].can_tiebreak_with(&foreign_keys[0])?;
+
+                    while our_keys == foreign_keys {
+                        our_keys =
+                            our_keys[0].increment(our_keys[our_keys.len() - 1].resolution + 1);
+                        foreign_keys = foreign_keys[0]
+                            .increment(foreign_keys[foreign_keys.len() - 1].resolution + 1);
+                        debug!(
+                            "Now its: '{}' ('{}') and '{}' ('{}')",
+                            MapKey::display(&our_keys),
+                            our_keys[0].part_path,
+                            MapKey::display(&foreign_keys),
+                            foreign_keys[0].part_path,
+                        );
+                    }
+                    debug!(
+                        "Found a better one: '{}' ('{}') and '{}' ('{}')",
+                        MapKey::display(&our_keys),
+                        our_keys[0].part_path,
+                        MapKey::display(&foreign_keys),
+                        foreign_keys[0].part_path,
+                    );
+
+                    let parent_keys = &found_keys[..&found_keys.len() - 1];
+
+                    {
+                        if self
+                            .0
+                            .get(&found_keys)
+                            .expect("This will exist")
+                            .value()
+                            .is_some()
+                        {
+                            // This is a child, we must replace it with a parent.
+                            let other_node = self
+                                .0
+                                .replace_node(&found_keys, Node::new_parent())
+                                .expect("This node exists");
+
+                            {
+                                let mut full_foreign_keys = parent_keys.to_vec();
+                                full_foreign_keys.append(&mut foreign_keys);
+                                self.insert_node(&full_foreign_keys, other_node)?;
+                            }
+                        }
+                    }
 
-        // debug!("Incrementing: '{}' ('{}')", &self, &self.part_path);
+                    {
+                        let mut full_our_keys = parent_keys.to_vec();
+                        full_our_keys.append(&mut our_keys);
+                        self.insert_node(&full_our_keys, node)?;
+                    }
 
-        let added_chars = if new_resolution < self.part_path.len() {
-            MapKey::part_path_to_key(&self.part_path, new_resolution)
+                    Ok(())
+                }
+                keymaps::error::TrieInsert::KeyIncludesChild {
+                    child_key: key,
+                    child_value,
+                } => {
+                    // A node that should be a parent was classified
+                    // as child before:
+                    //
+                    //  1. Remove the child node and replace it with a parent one.
+                    //  2. Add the child node to the parent node as child, but with a '.' as MapKey.
+                    //  3. Add the original node also as child to the parent node.
+
+                    assert_eq!(key, keys);
+
+                    let (fetched_child_value, mut child_key) = self.0.try_get(keys);
+                    assert_eq!(fetched_child_value.value(), Some(&child_value));
+
+                    trace!(
+                        "Replacing child ('{}') with a parent, so that we can continue from this point.",
+                        MapKey::display(&child_key)
+                    );
+
+                    let child = self
+                        .0
+                        .replace_node(&child_key, Node::new_parent())
+                        .expect("Node exists");
+                    assert_eq!(child.value(), Some(&child_value));
+
+                    child_key.push(MapKey {
+                        key: '.',
+                        part_path: ".".to_owned(),
+                        resolution: 1,
+                    });
+                    self.0
+                        .insert_node(&child_key, &child)
+                        .expect("We just created a parent here");
+
+                    // Recursive call, because this key could have hit the previous child directly
+                    // (thus it will now trigger the `KeyAlreadySet` error.)
+                    self.insert_node(keys, node)
+                }
+            }
         } else {
-            let mut generated_chars =
-                MapKey::part_path_to_key(&self.part_path, self.part_path.len());
-
-            generated_chars.extend(
-                (0..(new_resolution - self.part_path.len()))
-                    .into_iter()
-                    .map(|_| self.part_path.chars().last().expect("This will exists")),
-            );
-
-            generated_chars
-        };
-
-        let part_path = self.part_path.clone();
-        let output: Vec<Self> = added_chars
-            .chars()
-            .enumerate()
-            .map(|(res, ch)| MapKey {
-                key: ch,
-                resolution: res + 1,
-                part_path: part_path.clone(),
-            })
-            .collect();
-
-        // debug!("Finished increment: '{}' ('{}')", MapKey::display(&output), output[0].part_path);
-        output
+            Ok(())
+        }
     }
 
-    pub fn display(values: &[Self]) -> String {
-        values.iter().map(|value| value.key.clone()).collect()
-    }
-    fn part_path_to_key(part: &str, number_of_chars: usize) -> String {
-        fn make(pat: char, part: &str, number_of_chars: usize) -> String {
-            let mut acc = String::new();
-
-            if !part.split(pat).all(|part| part.len() > 0) {
-                panic!(
-                    "\
-Can't turn this path '{}' to a mapping.
-This should not happen, please report the bug!",
-                    part
-                )
-            }
+    /// Add a new [`MappingsTrie`] at the position `keys` into this Trie.
+    pub(crate) fn add_trie(&mut self, keys: &[MapKey], trie: Self) -> Result<()> {
+        if log_enabled!(Level::Trace) {
+            trace!("Adding mappings under '{}':", MapKey::display(keys));
+            eprintln!("{trie}");
 
-            let mut last_working = None;
-            for i in 0..number_of_chars {
-                for str in part.split(pat) {
-                    if acc.len() != number_of_chars {
-                        acc.push(match str.chars().nth(i) {
-                            Some(ch) => ch,
-                            None => {
-                                if let Some(last) = last_working {
-                                    str.chars().nth(last).expect("This should always exist")
-                                } else {
-                                    last_working = Some(i - 1);
-                                    str.chars().nth(i - 1).expect("This should always exist")
-                                }
-                            }
-                        })
-                    }
-                }
-            }
+            trace!("Self is:");
+            eprintln!("{self}");
+        }
+
+        let replaced = self
+            .0
+            .replace_node(keys, trie.0.root_node().to_owned())
+            .expect("This value exists");
 
-            acc
+        if log_enabled!(Level::Trace) {
+            trace!("After replace adding the new trie");
+            eprintln!("{self}");
         }
 
-        let value = if part.contains('_') && !part.starts_with('_') && !part.ends_with('_') {
-            make('_', part, number_of_chars)
-        } else if part.contains('-') && !part.starts_with('-') && !part.ends_with('-') {
-            make('-', part, number_of_chars)
-        } else {
-            part.chars().take(number_of_chars).collect::<String>()
-        };
-
-        assert_eq!(
-            value.len(),
-            number_of_chars,
-            "'{}' does not have expected length of: {}",
-            value,
-            number_of_chars
-        );
-        value
-    }
-}
+        {
+            let mut new_keys = keys.to_vec();
+            new_keys.push(MapKey {
+                key: '.',
+                part_path: ".".to_owned(),
+                resolution: 1,
+            });
+
+            let mut value = replaced.value().expect("Is a child").clone();
+            value.expendable = false;
+
+            trace!("Re-inserting '{}' into self.", MapKey::display(keys));
+            self.0
+                .insert_node(&new_keys, &Node::new_child(value))
+                .expect("This key is not used.");
+        }
 
-impl Display for MapKey {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_char(self.key)
+        Ok(())
     }
 }
diff --git a/pkgs/by-name/lf/lf-make-map/tests/base.sh b/pkgs/by-name/lf/lf-make-map/tests/base.sh
new file mode 100755
index 00000000..c7694985
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/tests/base.sh
@@ -0,0 +1,25 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+cd "$(dirname "$0")" || exit 2
+
+cargo build
+execute_make_maps() {
+    ../target/debug/lf-make-map "$@"
+}
+
+fd . cases --max-depth 1 --type directory | while read -r case; do
+    echo "Executing '$case/test.sh'"
+
+    # shellcheck source=/dev/null
+    LOCATION="$case/test.sh" . "$case/test.sh"
+done
diff --git a/pkgs/by-name/lf/lf-make-map/tests/cases/child_insert/test.sh b/pkgs/by-name/lf/lf-make-map/tests/cases/child_insert/test.sh
new file mode 100755
index 00000000..90ebe1ce
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/tests/cases/child_insert/test.sh
@@ -0,0 +1,27 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+test="$(mktemp --directory)"
+
+cleanup() {
+    rm --recursive "$test"
+}
+trap cleanup EXIT
+
+cat <<EOF | while read -r name; do mkdir --parents "$test/media/books/${name}"; done
+Andre A
+Anton B
+Andon C
+Anton D
+EOF
+
+execute_make_maps --home-name "$test" --depth 100 generate "$test"/* >/dev/null
diff --git a/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old
new file mode 100644
index 00000000..90591f16
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old
@@ -0,0 +1,46 @@
+map gdd. cd "/tmp/tmp.DfcgjemfCG/d"
+map gddcc. cd "/tmp/tmp.DfcgjemfCG/d/c"
+map gddccb. cd "/tmp/tmp.DfcgjemfCG/d/c/b"
+map gddccbc. cd "/tmp/tmp.DfcgjemfCG/d/c/b/c"
+map gddccbcf. cd "/tmp/tmp.DfcgjemfCG/d/c/b/c/file.test"
+map gddccd. cd "/tmp/tmp.DfcgjemfCG/d/c/d"
+map gddccdf. cd "/tmp/tmp.DfcgjemfCG/d/c/d/f"
+map gddccdff. cd "/tmp/tmp.DfcgjemfCG/d/c/d/f/file.test2"
+map gddcco. cd "/tmp/tmp.DfcgjemfCG/d/c/other"
+map gddccof. cd "/tmp/tmp.DfcgjemfCG/d/c/other/file.test3"
+map gddct. cd "/tmp/tmp.DfcgjemfCG/d/cll_the-things"
+map gddcto. cd "/tmp/tmp.DfcgjemfCG/d/cll_the-things/other"
+map gddctof. cd "/tmp/tmp.DfcgjemfCG/d/cll_the-things/other/file.test4"
+map gddm. cd "/tmp/tmp.DfcgjemfCG/d/mcybe some whitespcce"
+map gddmt. cd "/tmp/tmp.DfcgjemfCG/d/mcybe some whitespcce/test.file5"
+map gdi. cd "/tmp/tmp.DfcgjemfCG/dir"
+map gdicc. cd "/tmp/tmp.DfcgjemfCG/dir/c"
+map gdiccb. cd "/tmp/tmp.DfcgjemfCG/dir/c/b"
+map gdiccbc. cd "/tmp/tmp.DfcgjemfCG/dir/c/b/c"
+map gdiccbcf. cd "/tmp/tmp.DfcgjemfCG/dir/c/b/c/file.test"
+map gdiccd. cd "/tmp/tmp.DfcgjemfCG/dir/c/d"
+map gdiccdf. cd "/tmp/tmp.DfcgjemfCG/dir/c/d/f"
+map gdiccdff. cd "/tmp/tmp.DfcgjemfCG/dir/c/d/f/file.test2"
+map gdicco. cd "/tmp/tmp.DfcgjemfCG/dir/c/other"
+map gdiccof. cd "/tmp/tmp.DfcgjemfCG/dir/c/other/file.test3"
+map gdict. cd "/tmp/tmp.DfcgjemfCG/dir/cll_the-things"
+map gdicto. cd "/tmp/tmp.DfcgjemfCG/dir/cll_the-things/other"
+map gdictof. cd "/tmp/tmp.DfcgjemfCG/dir/cll_the-things/other/file.test4"
+map gdim. cd "/tmp/tmp.DfcgjemfCG/dir/mcybe some whitespcce"
+map gdimt. cd "/tmp/tmp.DfcgjemfCG/dir/mcybe some whitespcce/test.file5"
+map gdo. cd "/tmp/tmp.DfcgjemfCG/dor"
+map gdocc. cd "/tmp/tmp.DfcgjemfCG/dor/c"
+map gdoccb. cd "/tmp/tmp.DfcgjemfCG/dor/c/b"
+map gdoccbc. cd "/tmp/tmp.DfcgjemfCG/dor/c/b/c"
+map gdoccbcf. cd "/tmp/tmp.DfcgjemfCG/dor/c/b/c/file.test"
+map gdoccd. cd "/tmp/tmp.DfcgjemfCG/dor/c/d"
+map gdoccdf. cd "/tmp/tmp.DfcgjemfCG/dor/c/d/f"
+map gdoccdff. cd "/tmp/tmp.DfcgjemfCG/dor/c/d/f/file.test2"
+map gdocco. cd "/tmp/tmp.DfcgjemfCG/dor/c/other"
+map gdoccof. cd "/tmp/tmp.DfcgjemfCG/dor/c/other/file.test3"
+map gdoct. cd "/tmp/tmp.DfcgjemfCG/dor/cll_the-things"
+map gdocto. cd "/tmp/tmp.DfcgjemfCG/dor/cll_the-things/other"
+map gdoctof. cd "/tmp/tmp.DfcgjemfCG/dor/cll_the-things/other/file.test4"
+map gdom. cd "/tmp/tmp.DfcgjemfCG/dor/mcybe some whitespcce"
+map gdomt. cd "/tmp/tmp.DfcgjemfCG/dor/mcybe some whitespcce/test.file5"
+
diff --git a/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old.license b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/output.old.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/lf/lf-make-map/tests/cases/simple/test.sh b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/test.sh
new file mode 100755
index 00000000..6e127d28
--- /dev/null
+++ b/pkgs/by-name/lf/lf-make-map/tests/cases/simple/test.sh
@@ -0,0 +1,49 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+# We need to hard code this, so that our output matches the golden sample.
+base="/tmp/tmp.DfcgjemfCG"
+test="$(mktemp --directory)"
+
+[ -d "$base" ] && {
+    echo "$base already exists!"
+    exit 1
+}
+
+mkdir "$base"
+
+cleanup() {
+    rm --recursive "$base" "$test"
+}
+trap cleanup EXIT
+
+mkdir --parents "$base/dir/c/b/c/file.test"
+mkdir --parents "$base/dir/c/d/f/file.test2"
+mkdir --parents "$base/dir/c/other/file.test3"
+mkdir --parents "$base/dir/cll_the-things/other/file.test4"
+mkdir --parents "$base/dir/mcybe some whitespcce/test.file5"
+
+mkdir --parents "$base/dor/c/b/c/file.test"
+mkdir --parents "$base/dor/c/d/f/file.test2"
+mkdir --parents "$base/dor/c/other/file.test3"
+mkdir --parents "$base/dor/cll_the-things/other/file.test4"
+mkdir --parents "$base/dor/mcybe some whitespcce/test.file5"
+
+mkdir --parents "$base/d/c/b/c/file.test"
+mkdir --parents "$base/d/c/d/f/file.test2"
+mkdir --parents "$base/d/c/other/file.test3"
+mkdir --parents "$base/d/cll_the-things/other/file.test4"
+mkdir --parents "$base/d/mcybe some whitespcce/test.file5"
+
+execute_make_maps --home-name "$base" --depth 100 generate "$base"/* >"$test/output.new"
+
+diff "$test/output.new" "$(dirname "$LOCATION")/output.old"
diff --git a/pkgs/by-name/lf/lf-make-map/update.sh b/pkgs/by-name/lf/lf-make-map/update.sh
index 7d517e8b..23d90a86 100755
--- a/pkgs/by-name/lf/lf-make-map/update.sh
+++ b/pkgs/by-name/lf/lf-make-map/update.sh
@@ -1,4 +1,14 @@
 #!/usr/bin/env sh
 
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 [ "$1" = "upgrade" ] && cargo upgrade
 cargo update
diff --git a/pkgs/by-name/ll/ll/ll.sh b/pkgs/by-name/ll/ll/ll.sh
deleted file mode 100755
index 73328e3e..00000000
--- a/pkgs/by-name/ll/ll/ll.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-last_directory="$(mktemp)"
-
-command lf -last-dir-path="$last_directory" "$@"
-
-dir="$(cat "$last_directory")"
-if cd "$dir"; then
-    [ -d "$XDG_RUNTIME_DIR/ll" ] || mkdir "$XDG_RUNTIME_DIR/ll"
-    echo "$dir" >"$XDG_RUNTIME_DIR/ll/last_directory"
-else
-    die "$dir does not exist!"
-fi
-
-rm "$last_directory"
-# vim: ft=sh
diff --git a/pkgs/by-name/ll/ll/package.nix b/pkgs/by-name/ll/ll/package.nix
deleted file mode 100644
index 4c13b40e..00000000
--- a/pkgs/by-name/ll/ll/package.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{sysLib}:
-sysLib.writeShellScript {
-  name = "ll";
-  src = ./ll.sh;
-  generateCompletions = false;
-
-  # `ll` must be able to change the path of the running shell.
-  wrap = false;
-}
diff --git a/pkgs/by-name/lm/lm/lm.sh b/pkgs/by-name/lm/lm/lm.sh
deleted file mode 100755
index d5fdca10..00000000
--- a/pkgs/by-name/lm/lm/lm.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-if [ -f "$XDG_RUNTIME_DIR/ll/last_directory" ]; then
-    last_dir="$(cat "$XDG_RUNTIME_DIR/ll/last_directory")"
-    cd "$last_dir" || die "$last_dir does not exist!"
-else
-    msg "No last directory saved (try using ll instead)."
-fi
-# vim: ft=sh
diff --git a/pkgs/by-name/lm/lm/package.nix b/pkgs/by-name/lm/lm/package.nix
deleted file mode 100644
index ef417cd2..00000000
--- a/pkgs/by-name/lm/lm/package.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{sysLib}:
-sysLib.writeShellScript {
-  name = "lm";
-  src = ./lm.sh;
-  generateCompletions = false;
-
-  # `ll` must be able to change the path of the running shell.
-  wrap = false;
-}
diff --git a/pkgs/by-name/lo/lock/lock.sh b/pkgs/by-name/lo/lock/lock.sh
index 4c5e1c01..1c068e57 100755
--- a/pkgs/by-name/lo/lock/lock.sh
+++ b/pkgs/by-name/lo/lock/lock.sh
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 # shellcheck shell=bash
 
 context="$(task _get rc.context)"
diff --git a/pkgs/by-name/lo/lock/package.nix b/pkgs/by-name/lo/lock/package.nix
index a59fbdd0..572ebb0b 100644
--- a/pkgs/by-name/lo/lock/package.nix
+++ b/pkgs/by-name/lo/lock/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   writeShellApplication,
   taskwarrior3,
diff --git a/pkgs/by-name/mp/mpp-beetrm/mpp-beetrm.sh b/pkgs/by-name/mp/mpp-beetrm/mpp-beetrm.sh
index 3209503c..83784743 100755
--- a/pkgs/by-name/mp/mpp-beetrm/mpp-beetrm.sh
+++ b/pkgs/by-name/mp/mpp-beetrm/mpp-beetrm.sh
@@ -1,7 +1,14 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 beet remove --delete \
     title:"$(mpc --format '%title%' current)" \
diff --git a/pkgs/by-name/mp/mpp-beetrm/package.nix b/pkgs/by-name/mp/mpp-beetrm/package.nix
index 24b56606..00672838 100644
--- a/pkgs/by-name/mp/mpp-beetrm/package.nix
+++ b/pkgs/by-name/mp/mpp-beetrm/package.nix
@@ -1,15 +1,24 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
   mpc,
   beets,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "mpp-beetrm";
-  src = ./mpp-beetrm.sh;
-  generateCompletions = false;
-  keepPath = false;
+  text = builtins.readFile ./mpp-beetrm.sh;
+  inheritPath = false;
 
-  dependencies = [
+  runtimeInputs = [
     mpc
     beets
   ];
diff --git a/pkgs/by-name/mp/mpp-lyrics/mpp-lyrics.sh b/pkgs/by-name/mp/mpp-lyrics/mpp-lyrics.sh
index 004c67c7..fa1cac49 100755
--- a/pkgs/by-name/mp/mpp-lyrics/mpp-lyrics.sh
+++ b/pkgs/by-name/mp/mpp-lyrics/mpp-lyrics.sh
@@ -1,11 +1,21 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-(
-    cd "%MPD_MUSIC_DIR" || die "No music dir!"
-    exiftool "$(mpc --format '%file%' current)" -json | jq '.[0].Lyrics' -r | less
-)
+die() {
+    echo "Error: $1"
+    exit 1
+}
+
+cd "$XDG_MUSIC_DIR/beets" || die "No music dir!"
+exiftool "$(mpc --format '%file%' current)" -json | jq '.[0].Lyrics' --raw-output | less
 
 # vim: ft=sh
diff --git a/pkgs/by-name/mp/mpp-lyrics/package.nix b/pkgs/by-name/mp/mpp-lyrics/package.nix
index 76b590c7..0b6d8993 100644
--- a/pkgs/by-name/mp/mpp-lyrics/package.nix
+++ b/pkgs/by-name/mp/mpp-lyrics/package.nix
@@ -1,23 +1,27 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
   exiftool,
   mpc,
   jq,
   less,
   locale, # dependency of less
-  mpd_music_dir ? "\${XDG_MUSIC_DIR}",
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "mpp-lyrics";
-  src = ./mpp-lyrics.sh;
-  generateCompletions = false;
-  keepPath = false;
+  text = builtins.readFile ./mpp-lyrics.sh;
+  inheritPath = false;
 
-  replacementStrings = {
-    MPD_MUSIC_DIR = mpd_music_dir;
-  };
-
-  dependencies = [
+  runtimeInputs = [
     exiftool
     mpc
     jq
diff --git a/pkgs/by-name/mp/mpp-searchadd/mpp-searchadd.sh b/pkgs/by-name/mp/mpp-searchadd/mpp-searchadd.sh
index 3fe9a6b6..d76e73b8 100755
--- a/pkgs/by-name/mp/mpp-searchadd/mpp-searchadd.sh
+++ b/pkgs/by-name/mp/mpp-searchadd/mpp-searchadd.sh
@@ -1,14 +1,18 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-tracks="$(mktmp)"
-beet list "$@" --path >"$tracks"
-
-while read -r track; do
+beet list "$@" --path | while read -r track; do
     mpc add "$track"
-done <"$tracks"
+done
 
 mpc playlist
 # vim: ft=sh
diff --git a/pkgs/by-name/mp/mpp-searchadd/package.nix b/pkgs/by-name/mp/mpp-searchadd/package.nix
index a98472d1..91ff05e9 100644
--- a/pkgs/by-name/mp/mpp-searchadd/package.nix
+++ b/pkgs/by-name/mp/mpp-searchadd/package.nix
@@ -1,15 +1,24 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies.
   mpc,
   beets,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "mpp-searchadd";
-  src = ./mpp-searchadd.sh;
-  generateCompletions = false;
-  keepPath = false;
+  text = builtins.readFile ./mpp-searchadd.sh;
+  inheritPath = false;
 
-  dependencies = [
+  runtimeInputs = [
     mpc
     beets
   ];
diff --git a/pkgs/by-name/mp/mpp/mpp.sh b/pkgs/by-name/mp/mpp/mpp.sh
index 538a56ee..3ac98ddb 100755
--- a/pkgs/by-name/mp/mpp/mpp.sh
+++ b/pkgs/by-name/mp/mpp/mpp.sh
@@ -1,9 +1,16 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-case "$1" in
+case "${1-}" in
 "searchadd")
     shift 1
     mpp-searchadd "$@"
diff --git a/pkgs/by-name/mp/mpp/package.nix b/pkgs/by-name/mp/mpp/package.nix
index 9c5315b0..f0e454f7 100644
--- a/pkgs/by-name/mp/mpp/package.nix
+++ b/pkgs/by-name/mp/mpp/package.nix
@@ -1,20 +1,35 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
-  mpc,
-  fd,
+  writeShellApplication,
   symlinkJoin,
   stdenv,
+  # Dependencies
+  mpc,
+  mpp-searchadd,
+  mpp-lyrics,
+  mpp-beetrm,
+  # Build dependencies
+  fd,
   zsh,
 }: let
-  script = sysLib.writeShellScript {
+  script = writeShellApplication {
     name = "mpp";
-    src = ./mpp.sh;
-    generateCompletions = false;
-    # We source the wrappers from the environment, to ensure that they have the same
-    # configurations (e.g. MPD_MUSIC_DIR in `mpc-lyrics`)
-    keepPath = true;
-    dependencies = [
+    text = builtins.readFile ./mpp.sh;
+    inheritPath = false;
+
+    runtimeInputs = [
       mpc
+      mpp-searchadd
+      mpp-lyrics
+      mpp-beetrm
     ];
   };
 
@@ -55,6 +70,8 @@
       fd "." --hidden --type file | while read -r file_path; do
         sed --in-place 's/mpc/mpp/g' "$file_path"
       done
+
+      # TODO(@bpeetz): Also change this in man-pages. <2025-05-20>
     '';
 
     installPhase = ''
@@ -64,6 +81,8 @@
   };
 in
   symlinkJoin {
-    name = "mpp-merged";
+    name = "mpp";
     paths = [script mpcShare];
+
+    inherit (script) meta;
   }
diff --git a/pkgs/by-name/qu/qutebrowser-patched/0001-fix-standardpaths-Continue-to-work-with-xdg-while-ba.patch b/pkgs/by-name/qu/qutebrowser-patched/0001-fix-standardpaths-Continue-to-work-with-xdg-while-ba.patch
new file mode 100644
index 00000000..fa2e2482
--- /dev/null
+++ b/pkgs/by-name/qu/qutebrowser-patched/0001-fix-standardpaths-Continue-to-work-with-xdg-while-ba.patch
@@ -0,0 +1,54 @@
+From 8a0aa0e244fa565b8c55aab38cc5e84323c3b481 Mon Sep 17 00:00:00 2001
+From: Benedikt Peetz <benedikt.peetz@b-peetz.de>
+Date: Tue, 3 Jun 2025 12:43:44 +0200
+Subject: [PATCH] fix(standardpaths): Continue to work with xdg, while
+ `--basedir` is set
+
+This can be used to simulate firefox's profiles feature (i.e., completely separated
+data/runtime/cache dirs), while still keeping to the xdg basedir standard.
+---
+ qutebrowser/utils/standarddir.py | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py
+index b82845a96..61319daed 100644
+--- a/qutebrowser/utils/standarddir.py
++++ b/qutebrowser/utils/standarddir.py
+@@ -285,12 +285,11 @@ def _from_args(
+         The overridden path, or None if there is no override.
+     """
+     basedir_suffix = {
+-        QStandardPaths.StandardLocation.ConfigLocation: 'config',
+-        QStandardPaths.StandardLocation.AppDataLocation: 'data',
+-        QStandardPaths.StandardLocation.AppLocalDataLocation: 'data',
+-        QStandardPaths.StandardLocation.CacheLocation: 'cache',
+-        QStandardPaths.StandardLocation.DownloadLocation: 'download',
+-        QStandardPaths.StandardLocation.RuntimeLocation: 'runtime',
++        QStandardPaths.StandardLocation.ConfigLocation: ('config', False),
++        QStandardPaths.StandardLocation.AppDataLocation: ('data', False),
++        QStandardPaths.StandardLocation.AppLocalDataLocation: ('data', False),
++        QStandardPaths.StandardLocation.CacheLocation: ('cache', True),
++        QStandardPaths.StandardLocation.RuntimeLocation: ('runtime', True),
+     }
+ 
+     if getattr(args, 'basedir', None) is None:
+@@ -298,10 +297,14 @@ def _from_args(
+     assert args is not None
+ 
+     try:
+-        suffix = basedir_suffix[typ]
++        (suffix, extend) = basedir_suffix[typ]
+     except KeyError:  # pragma: no cover
+         return None
+-    return os.path.abspath(os.path.join(args.basedir, suffix))
++
++    if extend:
++        return os.path.abspath(os.path.join(_writable_location(typ), os.path.basename(args.basedir)))
++    else:
++        return os.path.abspath(os.path.join(args.basedir, suffix))
+ 
+ 
+ def _create(path: str) -> None:
+-- 
+2.49.0
+
diff --git a/pkgs/by-name/qu/qutebrowser-patched/package.nix b/pkgs/by-name/qu/qutebrowser-patched/package.nix
new file mode 100644
index 00000000..1f2ea889
--- /dev/null
+++ b/pkgs/by-name/qu/qutebrowser-patched/package.nix
@@ -0,0 +1,6 @@
+{qutebrowser}:
+qutebrowser.overrideAttrs (final: prev: {
+  pname = "${prev.pname}-patched";
+
+  patches = (prev.patches or []) ++ [./0001-fix-standardpaths-Continue-to-work-with-xdg-while-ba.patch];
+})
diff --git a/pkgs/by-name/ri/river-mk-keymap/.envrc b/pkgs/by-name/ri/river-mk-keymap/.envrc
index fdd3e9d8..294de504 100644
--- a/pkgs/by-name/ri/river-mk-keymap/.envrc
+++ b/pkgs/by-name/ri/river-mk-keymap/.envrc
@@ -1,3 +1,13 @@
 #!/usr/bin/env sh
 
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use flake
diff --git a/pkgs/by-name/ri/river-mk-keymap/.gitignore b/pkgs/by-name/ri/river-mk-keymap/.gitignore
index 2d5df85d..f255eebd 100644
--- a/pkgs/by-name/ri/river-mk-keymap/.gitignore
+++ b/pkgs/by-name/ri/river-mk-keymap/.gitignore
@@ -1,2 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 /target
 .direnv
diff --git a/pkgs/by-name/ri/river-mk-keymap/Cargo.lock b/pkgs/by-name/ri/river-mk-keymap/Cargo.lock
index c555a367..5640dc9b 100644
--- a/pkgs/by-name/ri/river-mk-keymap/Cargo.lock
+++ b/pkgs/by-name/ri/river-mk-keymap/Cargo.lock
@@ -1,12 +1,37 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 version = 4
 
 [[package]]
+name = "ab_glyph"
+version = "0.2.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"
+
+[[package]]
 name = "anstream"
-version = "0.6.18"
+version = "0.6.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -19,50 +44,89 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
 dependencies = [
  "anstyle",
- "once_cell",
- "windows-sys",
+ "once_cell_polyfill",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cc"
+version = "1.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "clap"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -70,9 +134,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -82,9 +146,9 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -94,15 +158,205 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "core-text"
+version = "20.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"
+dependencies = [
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "dwrote"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfe1f192fcce01590bd8d839aca53ce0d11d803bf291b2a6c4ad925a8f0024be"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "winapi",
+ "wio",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
+dependencies = [
+ "libc",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "float-ord"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
+
+[[package]]
+name = "font-kit"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3"
+dependencies = [
+ "bitflags 2.9.1",
+ "byteorder",
+ "core-foundation",
+ "core-graphics",
+ "core-text",
+ "dirs",
+ "dwrote",
+ "float-ord",
+ "freetype-sys",
+ "lazy_static",
+ "libc",
+ "log",
+ "pathfinder_geometry",
+ "pathfinder_simd",
+ "walkdir",
+ "winapi",
+ "yeslogic-fontconfig-sys",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "freetype-sys"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
 
 [[package]]
 name = "heck"
@@ -124,19 +378,78 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "keymaps"
-version = "1.0.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cec33e805ecc09c4e4f91ca26e536978ad1ae28f2e1dc02fadafeec6d2f8504"
+checksum = "ea59e8e461942cf1d6a7ad938848d6fd2e40eb43799c21192c09226ecc86710f"
 dependencies = [
  "serde",
  "thiserror",
 ]
 
 [[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+
+[[package]]
+name = "libloading"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.53.2",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
+dependencies = [
+ "bitflags 2.9.1",
+ "libc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "memmap2"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "once_cell"
@@ -145,15 +458,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "pathfinder_geometry"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
+dependencies = [
+ "log",
+ "pathfinder_simd",
+]
+
+[[package]]
+name = "pathfinder_simd"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
+name = "quick-xml"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "quote"
 version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -163,14 +531,70 @@ dependencies = [
 ]
 
 [[package]]
+name = "redox_users"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
 name = "river-mk-keymap"
 version = "0.1.0"
 dependencies = [
+ "ab_glyph",
  "anyhow",
  "clap",
+ "font-kit",
  "keymaps",
+ "memmap2",
+ "rustix 1.0.7",
  "serde",
  "serde_json",
+ "shlex",
+ "thiserror",
+ "vte",
+ "wayland-client",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+dependencies = [
+ "bitflags 2.9.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.4",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -180,6 +604,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
 
 [[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
+
+[[package]]
 name = "serde"
 version = "1.0.219"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -212,6 +651,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
 name = "strsim"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -219,9 +670,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.100"
+version = "2.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -249,6 +700,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -261,12 +718,148 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
+name = "vte"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
+dependencies = [
+ "arrayvec",
+ "memchr",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 0.38.44",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
+dependencies = [
+ "bitflags 2.9.1",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
+dependencies = [
+ "bitflags 2.9.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
+dependencies = [
+ "bitflags 2.9.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
+dependencies = [
+ "pkg-config",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
 name = "windows-sys"
 version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.2",
 ]
 
 [[package]]
@@ -275,14 +868,30 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
 ]
 
 [[package]]
@@ -292,43 +901,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
+name = "wio"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "yeslogic-fontconfig-sys"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd"
+dependencies = [
+ "dlib",
+ "once_cell",
+ "pkg-config",
+]
diff --git a/pkgs/by-name/ri/river-mk-keymap/Cargo.toml b/pkgs/by-name/ri/river-mk-keymap/Cargo.toml
index 19b3cbcb..9d8d2bba 100644
--- a/pkgs/by-name/ri/river-mk-keymap/Cargo.toml
+++ b/pkgs/by-name/ri/river-mk-keymap/Cargo.toml
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 [package]
 name = "river-mk-keymap"
 version = "0.1.0"
@@ -6,11 +16,21 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.35", features = ["derive"] }
-keymaps = { version = "1.0.0", features = ["serde", "mouse-keys"] }
+ab_glyph = "0.2.30"
+anyhow = "1.0.98"
+clap = { version = "4.5.40", features = ["derive"] }
+font-kit = "0.14.3"
+keymaps = { version = "1.2.0", features = ["serde", "mouse-keys", "modifier-keys"] }
+memmap2 = "0.9.5"
+rustix = { version = "1.0.7", features = ["fs", "shm"] }
 serde = { version = "1.0.219", features = ["derive"] }
 serde_json = "1.0.140"
+shlex = "1.3.0"
+thiserror = "2.0.12"
+vte = "0.15.0"
+wayland-client = {version = "0.31.10", default-features = false}
+wayland-protocols-wlr = { version = "0.3.8", features = ["client"] }
+wayland-scanner = {version = "0.31.6", default-features = false}
 
 [profile.release]
 lto = true
diff --git a/pkgs/by-name/ri/river-mk-keymap/TODO b/pkgs/by-name/ri/river-mk-keymap/TODO
deleted file mode 100644
index be77953e..00000000
--- a/pkgs/by-name/ri/river-mk-keymap/TODO
+++ /dev/null
@@ -1 +0,0 @@
-Look at https://github.com/stefur/flow for river wayland inclusion
diff --git a/pkgs/by-name/ri/river-mk-keymap/contrib/example.json b/pkgs/by-name/ri/river-mk-keymap/contrib/example.json
index c8673f9a..bddd61c0 100644
--- a/pkgs/by-name/ri/river-mk-keymap/contrib/example.json
+++ b/pkgs/by-name/ri/river-mk-keymap/contrib/example.json
@@ -1,5 +1,8 @@
 {
-    "<M-a>": {
-        "command": ["focus-view", "next"]
+    "Kbad": {
+        "command": ["spawn", "/nix/store/1xfyw9c5ala73y8sayrsf98vcrr3jrww-libnotify-0.8.6/bin/notify-send hi"]
+    },
+    "Kbae": {
+        "command": ["e"]
     }
 }
diff --git a/pkgs/by-name/ri/river-mk-keymap/contrib/example.json.license b/pkgs/by-name/ri/river-mk-keymap/contrib/example.json.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/contrib/example.json.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/ri/river-mk-keymap/contrib/init.json b/pkgs/by-name/ri/river-mk-keymap/contrib/init.json
new file mode 100644
index 00000000..a5f24307
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/contrib/init.json
@@ -0,0 +1,261 @@
+{
+  "<Alt+Ctrl+Super+Shift-Z>": [
+    "spawn",
+    "/nix/store/h71ca2rxlnlcyv4604ih2b2gla5ly27d-qmk-unicode-type-1.0.0/bin/qmk-unicode-type 106 65377"
+  ],
+  "<MEDIA_LOWERVOLUME>": {
+    "allow_locked": true,
+    "command": [
+      "spawn",
+      "/nix/store/7h9w2sycqj3i2lp61nibgv7qhvwy3pi9-wireplumber-0.5.10/bin/wpctl set-volume @DEFAULT_SINK@ 5%-"
+    ]
+  },
+  "<MEDIA_MUTEVOLUME>": {
+    "allow_locked": true,
+    "command": [
+      "spawn",
+      "/nix/store/08bgv5x7gfhkczf0lgrpim1rw51jlxvn-mpp/bin/mpp toggle"
+    ]
+  },
+  "<MEDIA_RAISEVOLUME>": {
+    "allow_locked": true,
+    "command": [
+      "spawn",
+      "/nix/store/7h9w2sycqj3i2lp61nibgv7qhvwy3pi9-wireplumber-0.5.10/bin/wpctl set-volume @DEFAULT_SINK@ 5%+"
+    ]
+  },
+  "<LEFT_SUPER>": {
+    "c": {
+      "<ENTER>": [
+        "zoom"
+      ],
+      " ": [
+        "toggle-float"
+      ],
+      "c": [
+        "close"
+      ],
+      "f": [
+        "toggle-fullscreen"
+      ],
+      "n": [
+        "swap",
+        "previous"
+      ],
+      "o": [
+        "send-to-output",
+        "next"
+      ],
+      "t": [
+        "swap",
+        "next"
+      ]
+    },
+    "f": {
+      "0": [
+        "set-focused-tags",
+        "4294967295"
+      ],
+      "1": [
+        "set-focused-tags",
+        "1"
+      ],
+      "2": [
+        "set-focused-tags",
+        "2"
+      ],
+      "3": [
+        "set-focused-tags",
+        "4"
+      ],
+      "4": [
+        "set-focused-tags",
+        "8"
+      ],
+      "5": [
+        "set-focused-tags",
+        "16"
+      ],
+      "6": [
+        "set-focused-tags",
+        "32"
+      ],
+      "7": [
+        "set-focused-tags",
+        "64"
+      ],
+      "8": [
+        "set-focused-tags",
+        "128"
+      ],
+      "9": [
+        "set-focused-tags",
+        "256"
+      ],
+      "<Ctrl-n>": [
+        "focus-output",
+        "previous"
+      ],
+      "<Ctrl-t>": [
+        "focus-output",
+        "next"
+      ],
+      "n": [
+        "focus-view",
+        "previous"
+      ],
+      "p": [
+        "focus-previous-tags"
+      ],
+      "t": [
+        "focus-view",
+        "next"
+      ]
+    },
+    "m": {
+      "l": {
+        "command": [
+        "spawn",
+        "/nix/store/7h9w2sycqj3i2lp61nibgv7qhvwy3pi9-wireplumber-0.5.10/bin/wpctl set-volume @DEFAULT_SINK@ 5%-"
+      ],
+        "description": "wpctl set-volume @DEFAULT_SINK@ 5%-"
+    },
+      "m": [
+        "spawn",
+        "/nix/store/08bgv5x7gfhkczf0lgrpim1rw51jlxvn-mpp/bin/mpp toggle"
+      ],
+      "r": [
+        "spawn",
+        "/nix/store/7h9w2sycqj3i2lp61nibgv7qhvwy3pi9-wireplumber-0.5.10/bin/wpctl set-volume @DEFAULT_SINK@ 5%+"
+      ]
+    },
+    "r": {
+      "a": [
+        "spawn",
+        "/nix/store/h601phmb09d9dwwziwsim6m0r31qajr3-alacritty-0.15.1/bin/alacritty"
+      ],
+      "b": [
+        "spawn",
+        "/nix/store/k8gfhk1lglwr8k6477ygkr9hh037a4kw-tskm-0.1.0/bin/tskm open select"
+      ],
+      "k": [
+        "spawn",
+        "/nix/store/xpinf75gxhl8aglw2z7631k89iiml7rz-keepassxc-2.7.10/bin/keepassxc"
+      ],
+      "p": [
+        "spawn",
+        "/nix/store/skgvjhmqp3jbmaw70xlz86a66lg13395-screenshot_persistent/bin/screenshot_persistent"
+      ],
+      "s": [
+        "spawn",
+        "/nix/store/zvzr8cj57jhxyrzjym2rv3w95w7zw901-signal-desktop-7.56.1/bin/signal-desktop"
+      ]
+    },
+    "v": {
+      "0": [
+        "set-view-tags",
+        "4294967295"
+      ],
+      "1": [
+        "set-view-tags",
+        "1"
+      ],
+      "2": [
+        "set-view-tags",
+        "2"
+      ],
+      "3": [
+        "set-view-tags",
+        "4"
+      ],
+      "4": [
+        "set-view-tags",
+        "8"
+      ],
+      "5": [
+        "set-view-tags",
+        "16"
+      ],
+      "6": [
+        "set-view-tags",
+        "32"
+      ],
+      "7": [
+        "set-view-tags",
+        "64"
+      ],
+      "8": [
+        "set-view-tags",
+        "128"
+      ],
+      "9": [
+        "set-view-tags",
+        "256"
+      ],
+      "a": {
+        "1": [
+          "toggle-view-tags",
+          "1"
+        ],
+        "2": [
+          "toggle-view-tags",
+          "2"
+        ],
+        "3": [
+          "toggle-view-tags",
+          "4"
+        ],
+        "4": [
+          "toggle-view-tags",
+          "8"
+        ],
+        "5": [
+          "toggle-view-tags",
+          "16"
+        ],
+        "6": [
+          "toggle-view-tags",
+          "32"
+        ],
+        "7": [
+          "toggle-view-tags",
+          "64"
+        ],
+        "8": [
+          "toggle-view-tags",
+          "128"
+        ],
+        "9": [
+          "toggle-view-tags",
+          "256"
+        ]
+      },
+      "p": [
+        "send-to-previous-tags"
+      ]
+    },
+    "x": {
+      "l": [
+        "spawn",
+        "/nix/store/4gp8yj8cz3d78hn01firv7dlqf4ap1fj-lock/bin/lock"
+      ],
+      "q": [
+        "exit"
+      ]
+    }
+  },
+  "<Super-<MOUSE_LEFT>>": [
+    "move-view"
+  ],
+  "<Super-<MOUSE_RIGHT>>": [
+    "resize-view"
+  ],
+  "<Super-L>": [
+    "spawn",
+    "/nix/store/4gp8yj8cz3d78hn01firv7dlqf4ap1fj-lock/bin/lock"
+  ],
+  "<PRINTSCREEN>": [
+    "spawn",
+    "/nix/store/skgvjhmqp3jbmaw70xlz86a66lg13395-screenshot_persistent/bin/screenshot_persistent"
+  ]
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/flake.lock b/pkgs/by-name/ri/river-mk-keymap/flake.lock
index eb3616c8..95f58ef9 100644
--- a/pkgs/by-name/ri/river-mk-keymap/flake.lock
+++ b/pkgs/by-name/ri/river-mk-keymap/flake.lock
@@ -2,11 +2,11 @@
   "nodes": {
     "nixpkgs": {
       "locked": {
-        "lastModified": 1744096231,
-        "narHash": "sha256-kUfx3FKU1Etnua3EaKvpeuXs7zoFiAcli1gBwkPvGSs=",
+        "lastModified": 1750994206,
+        "narHash": "sha256-3u6rEbIX9CN/5A5/mc3u0wIO1geZ0EhjvPBXmRDHqWM=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "b2b0718004cc9a5bca610326de0a82e6ea75920b",
+        "rev": "80d50fc87924c2a0d346372d242c27973cf8cdbf",
         "type": "github"
       },
       "original": {
diff --git a/pkgs/by-name/ri/river-mk-keymap/flake.lock.license b/pkgs/by-name/ri/river-mk-keymap/flake.lock.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/flake.lock.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/ri/river-mk-keymap/flake.nix b/pkgs/by-name/ri/river-mk-keymap/flake.nix
index 95a14456..b7e2a0c4 100644
--- a/pkgs/by-name/ri/river-mk-keymap/flake.nix
+++ b/pkgs/by-name/ri/river-mk-keymap/flake.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   description = "A smart way to configure river keybindings";
 
@@ -8,8 +17,20 @@
   outputs = {nixpkgs, ...}: let
     system = "x86_64-linux";
     pkgs = nixpkgs.legacyPackages."${system}";
+
+    nativeBuildInputs = [
+      pkgs.pkg-config
+    ];
+
+    buildInputs = [
+      pkgs.wayland
+      pkgs.libxkbcommon
+      pkgs.fontconfig
+    ];
   in {
     devShells."${system}".default = pkgs.mkShell {
+      inherit nativeBuildInputs buildInputs;
+
       packages = with pkgs; [
         cargo
         clippy
diff --git a/pkgs/by-name/ri/river-mk-keymap/package.nix b/pkgs/by-name/ri/river-mk-keymap/package.nix
index d9519d48..bb3dc285 100644
--- a/pkgs/by-name/ri/river-mk-keymap/package.nix
+++ b/pkgs/by-name/ri/river-mk-keymap/package.nix
@@ -1,4 +1,19 @@
-{rustPlatform}:
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+  rustPlatform,
+  pkg-config,
+  wayland,
+  libxkbcommon,
+  fontconfig,
+}:
 rustPlatform.buildRustPackage {
   pname = "river-mk-keymap";
   version = "0.1.0";
@@ -8,6 +23,16 @@ rustPlatform.buildRustPackage {
     lockFile = ./Cargo.lock;
   };
 
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    wayland
+    libxkbcommon
+    fontconfig
+  ];
+
   meta = {
     mainProgram = "river-mk-keymap";
   };
diff --git a/pkgs/by-name/ri/river-mk-keymap/resources/river-control-unstable-v1.xml b/pkgs/by-name/ri/river-mk-keymap/resources/river-control-unstable-v1.xml
new file mode 100644
index 00000000..aa5fc4dc
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/resources/river-control-unstable-v1.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="river_control_unstable_v1">
+  <copyright>
+    Copyright 2020 The River Developers
+
+    Permission to use, copy, modify, and/or distribute this software for any
+    purpose with or without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies.
+
+    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  </copyright>
+
+  <interface name="zriver_control_v1" version="1">
+    <description summary="run compositor commands">
+      This interface allows clients to run compositor commands and receive a
+      success/failure response with output or a failure message respectively.
+
+      Each command is built up in a series of add_argument requests and
+      executed with a run_command request. The first argument is the command
+      to be run.
+
+      A complete list of commands should be made available in the man page of
+      the compositor.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the river_control object">
+        This request indicates that the client will not use the
+        river_control object any more. Objects that have been created
+        through this instance are not affected.
+      </description>
+    </request>
+
+    <request name="add_argument">
+      <description summary="add an argument to the current command">
+        Arguments are stored by the server in the order they were sent until
+        the run_command request is made.
+      </description>
+      <arg name="argument" type="string" summary="the argument to add"/>
+    </request>
+
+    <request name="run_command">
+      <description summary="run the current command">
+        Execute the command built up using the add_argument request for the
+        given seat.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"/>
+      <arg name="callback" type="new_id" interface="zriver_command_callback_v1"
+        summary="callback object"/>
+    </request>
+  </interface>
+
+  <interface name="zriver_command_callback_v1" version="1">
+    <description summary="callback object">
+      This object is created by the run_command request. Exactly one of the
+      success or failure events will be sent. This object will be destroyed
+      by the compositor after one of the events is sent.
+    </description>
+
+    <event name="success" type="destructor">
+      <description summary="command successful">
+        Sent when the command has been successfully received and executed by
+        the compositor. Some commands may produce output, in which case the
+        output argument will be a non-empty string.
+      </description>
+      <arg name="output" type="string" summary="the output of the command"/>
+    </event>
+
+    <event name="failure" type="destructor">
+      <description summary="command failed">
+        Sent when the command could not be carried out. This could be due to
+        sending a non-existent command, no command, not enough arguments, too
+        many arguments, invalid arguments, etc.
+      </description>
+      <arg name="failure_message" type="string"
+        summary="a message explaining why failure occurred"/>
+    </event>
+  </interface>
+</protocol>
diff --git a/pkgs/by-name/ri/river-mk-keymap/resources/river-status-unstable-v1.xml b/pkgs/by-name/ri/river-mk-keymap/resources/river-status-unstable-v1.xml
new file mode 100644
index 00000000..e9629dde
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/resources/river-status-unstable-v1.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="river_status_unstable_v1">
+  <copyright>
+    Copyright 2020 The River Developers
+
+    Permission to use, copy, modify, and/or distribute this software for any
+    purpose with or without fee is hereby granted, provided that the above
+    copyright notice and this permission notice appear in all copies.
+
+    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+  </copyright>
+
+  <interface name="zriver_status_manager_v1" version="4">
+    <description summary="manage river status objects">
+      A global factory for objects that receive status information specific
+      to river. It could be used to implement, for example, a status bar.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the river_status_manager object">
+        This request indicates that the client will not use the
+        river_status_manager object any more. Objects that have been created
+        through this instance are not affected.
+      </description>
+    </request>
+
+    <request name="get_river_output_status">
+      <description summary="create an output status object">
+        This creates a new river_output_status object for the given wl_output.
+      </description>
+      <arg name="id" type="new_id" interface="zriver_output_status_v1"/>
+      <arg name="output" type="object" interface="wl_output"/>
+    </request>
+
+    <request name="get_river_seat_status">
+      <description summary="create a seat status object">
+        This creates a new river_seat_status object for the given wl_seat.
+      </description>
+      <arg name="id" type="new_id" interface="zriver_seat_status_v1"/>
+      <arg name="seat" type="object" interface="wl_seat"/>
+    </request>
+  </interface>
+
+  <interface name="zriver_output_status_v1" version="4">
+    <description summary="track output tags and focus">
+      This interface allows clients to receive information about the current
+      windowing state of an output.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the river_output_status object">
+        This request indicates that the client will not use the
+        river_output_status object any more.
+      </description>
+    </request>
+
+    <event name="focused_tags">
+      <description summary="focused tags of the output">
+        Sent once binding the interface and again whenever the tag focus of
+        the output changes.
+      </description>
+      <arg name="tags" type="uint" summary="32-bit bitfield"/>
+    </event>
+
+    <event name="view_tags">
+      <description summary="tag state of an output's views">
+        Sent once on binding the interface and again whenever the tag state
+        of the output changes.
+      </description>
+      <arg name="tags" type="array" summary="array of 32-bit bitfields"/>
+    </event>
+
+    <event name="urgent_tags" since="2">
+      <description summary="tags of the output with an urgent view">
+        Sent once on binding the interface and again whenever the set of
+        tags with at least one urgent view changes.
+      </description>
+      <arg name="tags" type="uint" summary="32-bit bitfield"/>
+    </event>
+
+    <event name="layout_name" since="4">
+      <description summary="name of the layout">
+        Sent once on binding the interface should a layout name exist and again
+        whenever the name changes.
+      </description>
+      <arg name="name" type="string" summary="layout name"/>
+    </event>
+
+    <event name="layout_name_clear" since="4">
+      <description summary="name of the layout">
+        Sent when the current layout name has been removed without a new one
+        being set, for example when the active layout generator disconnects.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="zriver_seat_status_v1" version="3">
+    <description summary="track seat focus">
+      This interface allows clients to receive information about the current
+      focus of a seat. Note that (un)focused_output events will only be sent
+      if the client has bound the relevant wl_output globals.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the river_seat_status object">
+        This request indicates that the client will not use the
+        river_seat_status object any more.
+      </description>
+    </request>
+
+    <event name="focused_output">
+      <description summary="the seat focused an output">
+        Sent on binding the interface and again whenever an output gains focus.
+      </description>
+      <arg name="output" type="object" interface="wl_output"/>
+    </event>
+
+    <event name="unfocused_output">
+      <description summary="the seat unfocused an output">
+        Sent whenever an output loses focus.
+      </description>
+      <arg name="output" type="object" interface="wl_output"/>
+    </event>
+
+    <event name="focused_view">
+      <description summary="information on the focused view">
+        Sent once on binding the interface and again whenever the focused
+        view or a property thereof changes. The title may be an empty string
+        if no view is focused or the focused view did not set a title.
+      </description>
+      <arg name="title" type="string" summary="title of the focused view"/>
+    </event>
+
+    <event name="mode" since="3">
+      <description summary="the active mode changed">
+        Sent once on binding the interface and again whenever a new mode
+        is entered (e.g. with riverctl enter-mode foobar).
+      </description>
+      <arg name="name" type="string" summary="name of the mode"/>
+    </event>
+  </interface>
+</protocol>
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/cli.rs b/pkgs/by-name/ri/river-mk-keymap/src/cli.rs
index 55b87e1a..ad872cc9 100644
--- a/pkgs/by-name/ri/river-mk-keymap/src/cli.rs
+++ b/pkgs/by-name/ri/river-mk-keymap/src/cli.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::path::PathBuf;
 
 use clap::Parser;
@@ -6,6 +16,20 @@ use clap::Parser;
 #[command(author, version, about, long_about = None)]
 /// A tool to manage your key mappings for the river window manager
 pub(super) struct Args {
-    /// Path to mappings JSON file
-    pub path: PathBuf,
+    #[command(subcommand)]
+    pub command: SubCommand,
+
+    #[arg(long, short)]
+    /// Path to mapping config JSON file
+    pub keymap: PathBuf,
+}
+
+#[derive(clap::Subcommand, Clone, Debug)]
+pub(super) enum SubCommand {
+    Init {
+        #[arg(short, long, default_value_t = false)]
+        /// Only show what would be done, don't actually perform the init.
+        dry_run: bool,
+    },
+    ShowHelp {},
 }
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs b/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs
index a4ac0ebd..058606c9 100644
--- a/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs
+++ b/pkgs/by-name/ri/river-mk-keymap/src/key_map/commands.rs
@@ -1,109 +1,299 @@
-use std::process::Command;
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
-use keymaps::key_repr::{KeyValue, MediaKeyCode, MouseKeyValue};
+use std::{env::current_exe, path::Path, process::Command};
 
-use super::{KeyMap, MapMode};
+use anyhow::{bail, Result};
+use keymaps::key_repr::{Key, KeyValue, Keys, MediaKeyCode, ModifierKeyCode, MouseKeyValue};
+use rustix::path::Arg;
+
+use super::KeyMap;
 
 impl KeyMap {
-    #[must_use]
-    pub fn to_commands(self) -> Vec<Command> {
-        self.0
-            .iter()
-            .flat_map(|(key, value)| {
-                let key = key.last().expect("Will exist");
-                let mods = {
-                    let modifiers = key.modifiers();
-                    let mut output = vec![];
-
-                    if modifiers.alt() {
-                        output.push("Alt");
-                    }
-                    if modifiers.ctrl() {
-                        output.push("Control");
-                    }
-                    if modifiers.meta() {
-                        output.push("Super");
-                    }
-                    if modifiers.shift() {
-                        output.push("Shift");
-                    }
-                    if output.is_empty() {
-                        "None".to_owned()
-                    } else {
-                        output.join("+")
-                    }
-                };
-                let key_value = match key.value() {
-                    KeyValue::Backspace => "BackSpace".to_owned(),
-                    KeyValue::Enter => "Enter".to_owned(),
-                    KeyValue::Left => "Left".to_owned(),
-                    KeyValue::Right => "Right".to_owned(),
-                    KeyValue::Up => "Up".to_owned(),
-                    KeyValue::Down => "Down".to_owned(),
-                    KeyValue::Home => "Home".to_owned(),
-                    KeyValue::End => "End".to_owned(),
-                    KeyValue::PageUp => "Page_Up".to_owned(),
-                    KeyValue::PageDown => "Page_Down".to_owned(),
-                    KeyValue::Tab => "Tab".to_owned(),
-                    KeyValue::BackTab => "BackTab".to_owned(),
-                    KeyValue::Delete => "Delete".to_owned(),
-                    KeyValue::Insert => "Insert".to_owned(),
-                    KeyValue::F(num) => format!("F{num}"),
-                    KeyValue::Char(a) => a.to_string(),
-                    KeyValue::Null => "Null".to_owned(),
-                    KeyValue::Esc => "Esc".to_owned(),
-                    KeyValue::CapsLock => "CapsLock".to_owned(),
-                    KeyValue::ScrollLock => "ScrollLock".to_owned(),
-                    KeyValue::NumLock => "NumLock".to_owned(),
-                    KeyValue::PrintScreen => "Print".to_owned(),
-                    KeyValue::Pause => "Pause".to_owned(),
-                    KeyValue::Menu => "Menu".to_owned(),
-                    KeyValue::KeypadBegin => "KeypadBegin".to_owned(),
-                    KeyValue::Media(media_key_code) => match media_key_code {
-                        MediaKeyCode::Play => "XF86AudioPlay".to_owned(),
-                        MediaKeyCode::Pause => "XF86AudioPause".to_owned(),
-                        MediaKeyCode::PlayPause => "XF86AudioPlayPause".to_owned(),
-                        MediaKeyCode::Reverse => "XF86AudioReverse".to_owned(),
-                        MediaKeyCode::Stop => "XF86AudioStop".to_owned(),
-                        MediaKeyCode::FastForward => "XF86AudioFastForward".to_owned(),
-                        MediaKeyCode::Rewind => "XF86AudioRewind".to_owned(),
-                        MediaKeyCode::TrackNext => "XF86AudioTrackNext".to_owned(),
-                        MediaKeyCode::TrackPrevious => "XF86AudioTrackPrevious".to_owned(),
-                        MediaKeyCode::Record => "XF86AudioRecord".to_owned(),
-                        MediaKeyCode::LowerVolume => "XF86AudioLowerVolume".to_owned(),
-                        MediaKeyCode::RaiseVolume => "XF86AudioRaiseVolume".to_owned(),
-                        MediaKeyCode::MuteVolume => "XF86AudioMuteVolume".to_owned(),
-                    },
-                    KeyValue::MouseKey(mouse_key_value) => match mouse_key_value {
-                        MouseKeyValue::Left => "BTN_LEFT".to_owned(),
-                        MouseKeyValue::Right => "BTN_RIGHT".to_owned(),
-                        MouseKeyValue::Middle => "BTN_MIDDLE".to_owned(),
-                    },
-                    _ => todo!(),
-                };
-
-                value
-                    .modes
-                    .iter()
-                    .map(|mode| {
-                        let mut riverctl = Command::new("riverctl");
-                        riverctl.args([value.map_mode.as_command(), mode, &mods, &key_value]);
-
-                        riverctl.args(value.command.iter().map(String::as_str));
-                        riverctl
-                    })
-                    .collect::<Vec<_>>()
+    /// # Errors
+    /// If impossible requests are made.
+    ///
+    /// # Panics
+    /// If internal assertions fail.
+    #[allow(clippy::too_many_lines)]
+    pub fn to_commands(self, keymap_path: &Path) -> Result<Vec<Command>> {
+        self.0.iter().try_for_each(|(keys, value)| {
+            let (prefix, last) = keys.split_at(keys.len() - 1);
+            let prefix = prefix.to_owned();
+
+            if value.allow_locked && !prefix.is_empty() {
+                bail!(
+                    "Only single key mappings can be used \
+                        in locked mode, but '{}' contains multiple ('{}').",
+                    Keys::from(keys),
+                    Keys::from(prefix),
+                )
+            }
+
+            if !prefix.is_empty()
+                && [
+                    "<ESC>".parse().expect("hardcoded"),
+                    "<BACKSPACE>".parse().expect("hardcoded"),
+                ]
+                .contains(&last[0])
+            {
+                bail!(
+                    "You cannot use <ESC> or <BACKSPACE> as the final part of a \
+                        prefixed mapping, as that is used to return \
+                        to 'normal' or the upper mode; found in '{}'",
+                    Keys::from(keys),
+                )
+            }
+
+            Ok(())
+        })?;
+
+        let mut output: Vec<_> = self
+            .0
+            .into_iter()
+            .flat_map(|(keys, value)| {
+                let (prefix, mapping) = keys.split_at(keys.len() - 1);
+
+                let (final_mode, mut base): (Option<String>, _) =
+                    prefix
+                        .iter()
+                        .fold((None, vec![]), |(acc_mode, mut acc_vec), key| {
+                            // Declare intermediate modes for each key.
+                            let mode_name: String = {
+                                let base = key.to_string_repr();
+
+                                if let Some(result) = &acc_mode {
+                                    result.to_owned() + base.as_str()
+                                } else {
+                                    base
+                                }
+                            };
+
+                            let mut riverctl = Command::new("riverctl");
+                            riverctl.args(["declare-mode", mode_name.as_str()]);
+
+                            let mut output = vec![riverctl];
+
+                            // Provide keymaps for entering and leaving the mode
+                            if let Some(acc_mode) = acc_mode.clone() {
+                                output.extend(key_to_command(
+                                    key.to_owned(),
+                                    &["enter-mode".to_owned(), mode_name.clone()],
+                                    &acc_mode,
+                                    false,
+                                ));
+                            } else {
+                                // Also spawn the help display if we start from the “normal” mode.
+                                output.extend(key_to_command(
+                                    key.to_owned(),
+                                    &[
+                                        "spawn".to_owned(),
+                                        format!(
+                                            "{} && sleep 1 && {}",
+                                            shlex::try_join([
+                                                "riverctl",
+                                                "enter-mode",
+                                                mode_name.as_str()
+                                            ])
+                                            .expect("Should work"),
+                                            shlex::try_join([
+                                                current_exe()
+                                                    .expect("Should have a current exe")
+                                                    .as_os_str()
+                                                    .as_str()
+                                                    .expect("Should be valid utf8"),
+                                                "--keymap",
+                                                keymap_path.to_str().expect("Should be valid utf8"),
+                                                "show-help",
+                                            ])
+                                            .expect("Should work"),
+                                        ),
+                                    ],
+                                    "normal",
+                                    false,
+                                ));
+                            }
+
+                            // Provide a mapping for going up a mode
+                            output.extend(key_to_command(
+                                "<BACKSPACE>".parse().expect("Hardcoded"),
+                                &[
+                                    "enter-mode".to_owned(),
+                                    acc_mode.unwrap_or("normal".to_owned()),
+                                ],
+                                &mode_name,
+                                false,
+                            ));
+
+                            // Another one for going back to normal.
+                            output.extend(key_to_command(
+                                "<ESC>".parse().expect("Hardcoded"),
+                                &["enter-mode".to_owned(), "normal".to_owned()],
+                                &mode_name,
+                                false,
+                            ));
+
+                            acc_vec.extend(output);
+
+                            (Some(mode_name), acc_vec)
+                        });
+
+                base.extend(key_to_command(
+                    mapping[0],
+                    &value.command,
+                    final_mode.as_ref().map_or("normal", |v| v.as_str()),
+                    value.allow_locked,
+                ));
+
+                base
             })
-            .collect()
+            .collect();
+
+        output.sort_by_cached_key(|cmd| format!("{cmd:?}"));
+        output.dedup_by_key(|cmd| format!("{cmd:?}"));
+
+        Ok(output)
     }
 }
 
-impl MapMode {
-    pub(crate) fn as_command(self) -> &'static str {
-        match self {
-            MapMode::Map => "map",
-            MapMode::MapMouse => "map-pointer",
-            MapMode::Unmap => "unmap",
+fn key_value_to_xkb_common_name(value: KeyValue) -> (String, Vec<&'static str>) {
+    let mut extra_modifiers = vec![];
+
+    let output = match value {
+        KeyValue::Backspace => "BackSpace".to_owned(),
+        KeyValue::Enter => "Return".to_owned(),
+        KeyValue::Left => "Left".to_owned(),
+        KeyValue::Right => "Right".to_owned(),
+        KeyValue::Up => "Up".to_owned(),
+        KeyValue::Down => "Down".to_owned(),
+        KeyValue::Home => "Home".to_owned(),
+        KeyValue::End => "End".to_owned(),
+        KeyValue::PageUp => "Page_Up".to_owned(),
+        KeyValue::PageDown => "Page_Down".to_owned(),
+        KeyValue::Tab => "Tab".to_owned(),
+        KeyValue::BackTab => "BackTab".to_owned(),
+        KeyValue::Delete => "Delete".to_owned(),
+        KeyValue::Insert => "Insert".to_owned(),
+        KeyValue::F(num) => format!("F{num}"),
+        KeyValue::Char(a) => {
+            // River does not differentiate between 'a' and 'A',
+            // so we need to do it beforehand.
+            if a.is_ascii_uppercase() {
+                extra_modifiers.push("Shift");
+            }
+
+            if a == ' ' {
+                "Space".to_string()
+            } else {
+                a.to_string()
+            }
+        }
+        KeyValue::Null => "Null".to_owned(),
+        KeyValue::Esc => "Escape".to_owned(),
+        KeyValue::CapsLock => "CapsLock".to_owned(),
+        KeyValue::ScrollLock => "ScrollLock".to_owned(),
+        KeyValue::NumLock => "NumLock".to_owned(),
+        KeyValue::PrintScreen => "Print".to_owned(),
+        KeyValue::Pause => "Pause".to_owned(),
+        KeyValue::Menu => "Menu".to_owned(),
+        KeyValue::KeypadBegin => "KeypadBegin".to_owned(),
+        KeyValue::Media(media_key_code) => match media_key_code {
+            MediaKeyCode::Play => "XF86AudioPlay".to_owned(),
+            MediaKeyCode::Pause => "XF86AudioPause".to_owned(),
+            MediaKeyCode::PlayPause => "XF86AudioPlayPause".to_owned(),
+            MediaKeyCode::Reverse => "XF86AudioReverse".to_owned(),
+            MediaKeyCode::Stop => "XF86AudioStop".to_owned(),
+            MediaKeyCode::FastForward => "XF86AudioFastForward".to_owned(),
+            MediaKeyCode::Rewind => "XF86AudioRewind".to_owned(),
+            MediaKeyCode::TrackNext => "XF86AudioTrackNext".to_owned(),
+            MediaKeyCode::TrackPrevious => "XF86AudioTrackPrevious".to_owned(),
+            MediaKeyCode::Record => "XF86AudioRecord".to_owned(),
+            MediaKeyCode::LowerVolume => "XF86AudioLowerVolume".to_owned(),
+            MediaKeyCode::RaiseVolume => "XF86AudioRaiseVolume".to_owned(),
+            MediaKeyCode::MuteVolume => "XF86AudioMute".to_owned(),
+        },
+        KeyValue::MouseKey(mouse_key_value) => match mouse_key_value {
+            MouseKeyValue::Left => "BTN_LEFT".to_owned(),
+            MouseKeyValue::Right => "BTN_RIGHT".to_owned(),
+            MouseKeyValue::Middle => "BTN_MIDDLE".to_owned(),
+        },
+        KeyValue::ModifierKey(modifier_key_code) => match modifier_key_code {
+            ModifierKeyCode::LeftAlt => "ALT_L".to_owned(),
+            ModifierKeyCode::RightAlt => "ALT_R".to_owned(),
+            ModifierKeyCode::LeftCtrl => "CTRL_L".to_owned(),
+            ModifierKeyCode::RightCtrl => "CTRL_R".to_owned(),
+            ModifierKeyCode::LeftMeta => "SUPER_L".to_owned(),
+            ModifierKeyCode::RightMeta => "SUPER_R".to_owned(),
+            ModifierKeyCode::LeftShift => "SHIFT_L".to_owned(),
+            ModifierKeyCode::RightShift => "SHIFT_R".to_owned(),
+        },
+        other => todo!("Key value: {other} not known."),
+    };
+
+    (output, extra_modifiers)
+}
+
+fn key_to_command(key: Key, command: &[String], mode: &str, allow_locked: bool) -> Vec<Command> {
+    let mut modifiers = {
+        let modifiers = key.modifiers();
+        let mut output = vec![];
+
+        if modifiers.alt() {
+            output.push("Alt");
+        }
+        if modifiers.ctrl() {
+            output.push("Control");
+        }
+        if modifiers.meta() {
+            output.push("Super");
         }
+        if modifiers.shift() {
+            output.push("Shift");
+        }
+        output
+    };
+
+    let (key_value, extra_modifiers) = key_value_to_xkb_common_name(key.value());
+    modifiers.extend(extra_modifiers);
+
+    let map_mode = if let KeyValue::MouseKey(_) = key.value() {
+        "map-pointer"
+    } else {
+        "map"
+    };
+
+    let modifiers = if modifiers.is_empty() {
+        "None".to_owned()
+    } else {
+        modifiers.join("+")
+    };
+
+    let mut output = vec![{
+        let mut riverctl = Command::new("riverctl");
+        riverctl.args([map_mode, mode, &modifiers, &key_value]);
+
+        riverctl.args(command.iter().map(String::as_str));
+
+        riverctl
+    }];
+
+    if allow_locked {
+        output.push({
+            let mut riverctl = Command::new("riverctl");
+            riverctl.args([map_mode, "locked", &modifiers, &key_value]);
+
+            riverctl.args(command.iter().map(String::as_str));
+
+            riverctl
+        });
     }
+
+    output
 }
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
index 84a16c9d..5c89c2e2 100644
--- a/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
+++ b/pkgs/by-name/ri/river-mk-keymap/src/key_map/mod.rs
@@ -1,37 +1,102 @@
-use std::{collections::HashMap, fmt::Display, ops::Deref, str::FromStr};
-
-use anyhow::Context;
-use keymaps::{key_repr::Key, map_tree::MapTrie};
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{fmt::Display, ops::Deref, str::FromStr};
+
+use anyhow::{anyhow, bail, Context, Result};
+use keymaps::{
+    key_repr::{Key, Keys},
+    map_tree::MapTrie,
+};
 use serde::{Deserialize, Serialize};
+use serde_json::{Map, Value};
 
 pub mod commands;
 
-#[derive(Deserialize, Serialize, Debug)]
-#[allow(clippy::module_name_repetitions)]
-pub struct RawKeyMap(HashMap<Key, KeyConfig>);
-
 #[derive(Clone, Deserialize, Serialize, Debug, PartialEq, PartialOrd)]
-/// What values to use for: `riverctl <map_mode> <mode> <mods> <key> <command..>`
+/// What values to use for: `riverctl <command..>`
+#[serde(deny_unknown_fields)]
 pub struct KeyConfig {
     command: Vec<String>,
 
-    #[serde(default = "default_mode")]
-    modes: Vec<String>,
+    /// Whether to allow this key mapping in the “locked” mode.
+    #[serde(default)]
+    allow_locked: bool,
 
-    #[serde(default = "MapMode::default")]
-    map_mode: MapMode,
+    /// Use a different description to display this command, instead of the `command`.
+    description: Option<String>,
 }
 
 impl FromStr for KeyMap {
     type Err = anyhow::Error;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let raw: RawKeyMap =
-            serde_json::from_str(s).context("Failed to parse the keymap config file as json.")?;
+        fn decode_value(
+            output: &mut MapTrie<KeyConfig>,
+            current_key: Vec<Key>,
+            value: &Value,
+        ) -> Result<()> {
+            let key_config = if let Some(value) = value.as_array() {
+                KeyConfig {
+                    command: value
+                        .iter()
+                        .map(|v| v.as_str().map(ToOwned::to_owned))
+                        .collect::<Option<_>>()
+                        .ok_or(anyhow!("A array contained a non-string value: {value:#?}"))?,
+                    allow_locked: false,
+                    description: None,
+                }
+            } else if let Some(object) = value.as_object() {
+                if object.contains_key("command") {
+                    serde_json::from_value(value.to_owned())
+                        .with_context(|| format!("Failed to parse key config: {value:#?}"))?
+                } else {
+                    for (key, value) in object {
+                        let mut local_current_key = current_key.clone();
+                        local_current_key.push(
+                            Key::from_str(key)
+                                .with_context(|| format!("Failed to parse key '{key}'"))?,
+                        );
+
+                        decode_value(output, local_current_key, value)?;
+                    }
+                    return Ok(());
+                }
+            } else {
+                bail!("Value ({}) is invalid (not array or object).", value)
+            };
+
+            output
+                .insert(&current_key, key_config.clone())
+                .with_context(|| {
+                    format!(
+                        "Failed to insert mapping {} -> {key_config}",
+                        Keys::from(current_key)
+                    )
+                })?;
+
+            Ok(())
+        }
+
         let mut out = MapTrie::<KeyConfig>::new();
-        for (key, value) in raw.0 {
-            out.insert(&[key], value.clone())
-                .with_context(|| format!("Failed to insert mapping {key} -> {value}"))?;
+
+        let raw: Map<String, Value> =
+            serde_json::from_str(s).context("Failed to parse the keymap config file as json.")?;
+
+        for (key, value) in raw {
+            decode_value(
+                &mut out,
+                vec![Key::from_str(&key)
+                    .with_context(|| format!("Failed to parse key ('{key}')"))?],
+                &value,
+            )?;
         }
 
         Ok(Self(out))
@@ -39,25 +104,11 @@ impl FromStr for KeyMap {
 }
 impl Display for KeyConfig {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(self.command.join(" ").as_str())
-    }
-}
-
-fn default_mode() -> Vec<String> {
-    vec!["normal".to_owned()]
-}
-
-#[derive(Copy, Deserialize, Serialize, Debug, Clone, Default, PartialEq, PartialOrd)]
-enum MapMode {
-    #[default]
-    Map,
-    MapMouse,
-    Unmap,
-}
-
-impl Display for MapMode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        <Self as std::fmt::Debug>::fmt(self, f)
+        if let Some(desc) = &self.description {
+            f.write_str(desc)
+        } else {
+            f.write_str(self.command.join(" ").as_str())
+        }
     }
 }
 
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/main.rs b/pkgs/by-name/ri/river-mk-keymap/src/main.rs
index 5cb99f74..18c291cf 100644
--- a/pkgs/by-name/ri/river-mk-keymap/src/main.rs
+++ b/pkgs/by-name/ri/river-mk-keymap/src/main.rs
@@ -1,33 +1,69 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::fs;
 
 use anyhow::Context;
 use clap::Parser;
 
-mod cli;
+pub mod cli;
 pub mod key_map;
+pub mod wayland;
 
 use crate::{cli::Args, key_map::KeyMap};
 
 fn main() -> Result<(), anyhow::Error> {
     let args = Args::parse();
-    let keymap_file = fs::read_to_string(&args.path)
-        .with_context(|| format!("Failed to open keymap file at: '{}'.", args.path.display()))?;
-
-    let keymap: KeyMap = keymap_file
-        .parse()
-        .with_context(|| format!("Failed to parse keymap file at: {}", args.path.display()))?;
-
-    // println!("{keymap}");
-    // println!("Commands:");
-    for mut command in keymap.to_commands() {
-        // println!("Executing {command:?}");
-        let status = command
-            .status()
-            .with_context(|| format!("Failed to run command: '{command:?}'"))?;
-
-        if !status.success() {
-            eprintln!("Command ('{command:?}') returned with non zero exit code: {status}");
+
+    let keymap_path = &args.keymap.canonicalize().with_context(|| {
+        format!(
+            "Failed to canonicalize kepmay path: '{}'",
+            args.keymap.display()
+        )
+    })?;
+
+    let config = {
+        let keymap_file = fs::read_to_string(keymap_path).with_context(|| {
+            format!(
+                "Failed to open keymap file at: '{}'.",
+                keymap_path.display()
+            )
+        })?;
+
+        let keymap: KeyMap = keymap_file.parse().with_context(|| {
+            format!("Failed to parse keymap file at: {}", keymap_path.display())
+        })?;
+
+        keymap
+    };
+
+    match args.command {
+        cli::SubCommand::Init { dry_run } => {
+            println!("{config}");
+            for mut command in config.to_commands(keymap_path)? {
+                if dry_run {
+                    println!("{command:?}");
+                } else {
+                    let status = command
+                        .status()
+                        .with_context(|| format!("Failed to run command: '{command:?}'"))?;
+
+                    if !status.success() {
+                        eprintln!(
+                            "Command ('{command:?}') returned with non zero exit code: {status}"
+                        );
+                    }
+                }
+            }
         }
+        cli::SubCommand::ShowHelp {} => wayland::main(config)?,
     }
 
     Ok(())
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/ansi/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/ansi/mod.rs
new file mode 100644
index 00000000..0517ecf2
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/ansi/mod.rs
@@ -0,0 +1,173 @@
+use std::mem;
+
+use vte::{Params, Parser, Perform};
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum Color {
+    Black,
+    Red,
+    Green,
+    Yellow,
+    Blue,
+    Purple,
+    Cyan,
+    White,
+}
+
+#[derive(Debug)]
+struct Cleaner {
+    current_color: Option<Color>,
+    styles: StyledString,
+    current: String,
+}
+
+#[derive(Debug)]
+struct StyledStringInner {
+    val: String,
+    color: Option<Color>,
+}
+
+pub(crate) struct StyledChar {
+    ch: char,
+    color: Option<Color>,
+}
+
+impl StyledChar {
+    pub(crate) fn as_char(&self) -> char {
+        self.ch
+    }
+
+    pub(crate) fn is_bold(&self) -> bool {
+        self.color.is_some()
+    }
+
+    pub(crate) fn color(&self) -> Option<Color> {
+        self.color
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct StyledString {
+    inner: Vec<StyledStringInner>,
+}
+
+impl StyledString {
+    fn push(&mut self, val: StyledStringInner) {
+        self.inner.push(val);
+    }
+
+    pub(crate) fn chars(&self) -> impl Iterator<Item = StyledChar> + use<'_> {
+        self.inner.iter().flat_map(|inner| {
+            inner.val.chars().map(|ch| StyledChar {
+                ch,
+                color: inner.color,
+            })
+        })
+    }
+}
+
+impl Cleaner {
+    fn reset_color(&mut self) {
+        self.styles.push(StyledStringInner {
+            val: mem::take(&mut self.current),
+            color: mem::take(&mut self.current_color),
+        });
+    }
+
+    fn set_color(&mut self, color: Color) {
+        self.current_color = Some(color);
+    }
+
+    fn add_char(&mut self, c: char) {
+        self.current.push(c);
+    }
+}
+
+impl Perform for Cleaner {
+    fn print(&mut self, c: char) {
+        self.add_char(c);
+    }
+
+    fn execute(&mut self, byte: u8) {
+        if byte == b'\n' {
+            self.reset_color();
+            self.add_char('\n');
+            self.reset_color();
+        } else {
+            eprintln!("Unknown [execute]: {byte:02x}");
+        }
+    }
+
+    fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
+        eprintln!(
+            "Unknown [hook] params={params:?}, intermediates={intermediates:?}, ignore={ignore:?}, char={c:?}"
+        );
+    }
+
+    fn put(&mut self, byte: u8) {
+        eprintln!("Unknonw [put] {byte:02x}");
+    }
+
+    fn unhook(&mut self) {
+        eprintln!("Unknown [unhook]");
+    }
+
+    fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
+        eprintln!("Unkown [osc_dispatch] params={params:?} bell_terminated={bell_terminated}");
+    }
+
+    fn csi_dispatch(&mut self, params: &Params, _: &[u8], _: bool, c: char) {
+        let params: Vec<u16> = params.iter().flatten().copied().collect();
+
+        if c != 'm' {
+            return;
+        }
+
+        // See: https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc87b2124
+        match params[..] {
+            [0] => self.reset_color(),
+            // [0, regular] if matches!(regular, 30..=37) => {}
+            [1, bold] if matches!(bold, 30..=37) => match bold {
+                30 => self.set_color(Color::Black),
+                31 => self.set_color(Color::Red),
+                32 => self.set_color(Color::Green),
+                36 => self.set_color(Color::Yellow),
+                34 => self.set_color(Color::Blue),
+                35 => self.set_color(Color::Purple),
+                33 => self.set_color(Color::Cyan),
+                37 => self.set_color(Color::White),
+                _ => unreachable!("Was filtered out"),
+            },
+            // [4, underline] if matches!(underline, 30..=37) => {}
+            // [background] if matches!(background, 40..=47) => {}
+            _ => todo!(),
+        }
+
+        // println!(
+        //     "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
+        //     params, intermediates, ignore, c
+        // );
+    }
+
+    fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
+        eprintln!(
+            "Unkown [esc_dispatch] intermediates={intermediates:?}, ignore={ignore:?}, byte={byte:02x}"
+        );
+    }
+}
+
+pub(crate) fn parse(input: &str) -> StyledString {
+    let mut statemachine = Parser::new();
+    let mut performer = Cleaner {
+        current_color: None,
+        styles: StyledString { inner: vec![] },
+        current: String::new(),
+    };
+
+    let buf: Vec<_> = input.bytes().collect();
+
+    statemachine.advance(&mut performer, &buf[..]);
+
+    assert!(performer.current.is_empty() && performer.current_color.is_none());
+    performer.styles
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/dispatches.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/dispatches.rs
new file mode 100644
index 00000000..c6e04fdf
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/dispatches.rs
@@ -0,0 +1,214 @@
+use std::num::NonZero;
+
+use keymaps::key_repr::Key;
+use wayland_client::{
+    globals::GlobalListContents,
+    protocol::{
+        wl_buffer::WlBuffer, wl_compositor::WlCompositor, wl_registry, wl_seat::WlSeat,
+        wl_shm::WlShm, wl_shm_pool::WlShmPool, wl_surface::WlSurface,
+    },
+    Connection, Dispatch, QueueHandle,
+};
+
+use wayland_protocols_wlr::layer_shell::v1::client::{
+    zwlr_layer_shell_v1::ZwlrLayerShellV1,
+    zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1},
+};
+
+use crate::wayland::{
+    ansi, render,
+    river::protocols::river_protocols::{
+        zriver_seat_status_v1::{self, ZriverSeatStatusV1},
+        zriver_status_manager_v1::ZriverStatusManagerV1,
+    },
+    AppData,
+};
+
+impl Dispatch<ZriverSeatStatusV1, ()> for AppData {
+    fn event(
+        state: &mut Self,
+        _: &ZriverSeatStatusV1,
+        event: <ZriverSeatStatusV1 as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+        if let zriver_seat_status_v1::Event::Mode { name } = event {
+            let new_text = {
+                if name == "normal" {
+                    // We are back at the normal mode.
+                    // There is no need to display the mappings anymore, exit.
+                    state.should_exit = true;
+                    return;
+                } else if let Ok(keys) = Key::parse_multiple(&name) {
+                    if let Some(val) = state.config.get(&keys) {
+                        ansi::parse(val.to_string().as_str())
+                    } else {
+                        // Mode name not know, do nothing.
+                        return;
+                    }
+                } else {
+                    // Mode name not valid, do nothing.
+                    return;
+                }
+            };
+
+            let px_height;
+            (state.pixel_data, (state.max_px_width, px_height)) =
+                render::text(&new_text).expect("Works?");
+
+            // We add the `5` here, so that our letters don't stop exactly at the border.
+            state
+                .window
+                .0
+                .set_size(state.max_px_width + 5, px_height + 5);
+            state.window.1.commit();
+
+            if state.configured {
+                state.draw();
+            }
+        }
+    }
+}
+
+impl Dispatch<ZwlrLayerSurfaceV1, ()> for AppData {
+    fn event(
+        state: &mut Self,
+        proxy: &ZwlrLayerSurfaceV1,
+        event: <ZwlrLayerSurfaceV1 as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+        match event {
+            zwlr_layer_surface_v1::Event::Configure {
+                serial,
+                width,
+                height,
+            } => {
+                state.buffer = None;
+
+                proxy.ack_configure(serial);
+
+                state.width = NonZero::new(width).map_or_else(|| state.width, NonZero::get);
+                state.height = NonZero::new(height).map_or_else(|| state.height, NonZero::get);
+
+                state.draw();
+
+                state.configured = true;
+            }
+            zwlr_layer_surface_v1::Event::Closed => {
+                state.should_exit = true;
+            }
+            _ => (),
+        }
+    }
+}
+
+impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for AppData {
+    fn event(
+        _: &mut AppData,
+        _: &wl_registry::WlRegistry,
+        _: wl_registry::Event,
+        _: &GlobalListContents,
+        _: &Connection,
+        _: &QueueHandle<AppData>,
+    ) {
+    }
+}
+
+impl Dispatch<WlShmPool, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlShmPool,
+        _: <WlShmPool as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<WlShm, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlShm,
+        _: <WlShm as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<WlSurface, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlSurface,
+        _: <WlSurface as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<WlCompositor, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlCompositor,
+        _: <WlCompositor as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<WlSeat, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlSeat,
+        _: <WlSeat as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<WlBuffer, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &WlBuffer,
+        _: <WlBuffer as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<ZriverStatusManagerV1, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &ZriverStatusManagerV1,
+        _: <ZriverStatusManagerV1 as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<ZwlrLayerShellV1, ()> for AppData {
+    fn event(
+        _: &mut Self,
+        _: &ZwlrLayerShellV1,
+        _: <ZwlrLayerShellV1 as wayland_client::Proxy>::Event,
+        (): &(),
+        _: &Connection,
+        _: &QueueHandle<Self>,
+    ) {
+    }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/mod.rs
new file mode 100644
index 00000000..44c010d5
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/mod.rs
@@ -0,0 +1,272 @@
+#![allow(
+    clippy::cast_sign_loss,
+    clippy::cast_possible_wrap,
+    clippy::cast_precision_loss,
+    clippy::cast_possible_truncation
+)]
+
+use anyhow::Result;
+use wayland_client::{
+    globals::registry_queue_init,
+    protocol::{
+        wl_compositor::WlCompositor,
+        wl_seat::WlSeat,
+        wl_shm::{self, WlShm},
+        wl_surface::WlSurface,
+    },
+    Connection,
+};
+use wayland_protocols_wlr::layer_shell::v1::client::{
+    zwlr_layer_shell_v1::{self, ZwlrLayerShellV1},
+    zwlr_layer_surface_v1::{self, ZwlrLayerSurfaceV1},
+};
+
+use crate::{
+    key_map::KeyMap,
+    wayland::{
+        ansi::Color,
+        river::protocols::river_protocols::zriver_status_manager_v1::ZriverStatusManagerV1,
+        shm::slot::{Buffer, SlotPool},
+    },
+};
+
+mod ansi;
+mod render;
+mod river;
+mod shm;
+
+mod dispatches;
+
+struct AppData {
+    pool: SlotPool,
+    window: (ZwlrLayerSurfaceV1, WlSurface),
+
+    configured: bool,
+    buffer: Option<Buffer>,
+
+    width: u32,
+    height: u32,
+
+    max_px_width: u32,
+    pixel_data: (Vec<f32>, Vec<Option<Color>>),
+
+    config: KeyMap,
+    should_exit: bool,
+}
+
+impl AppData {
+    #[allow(clippy::too_many_lines)]
+    fn draw(&mut self) {
+        let width = self.width;
+        let height = self.height;
+        let stride = self.width as i32 * 4;
+
+        let buffer = self.buffer.get_or_insert_with(|| {
+            self.pool
+                .create_buffer(
+                    width as i32,
+                    height as i32,
+                    stride,
+                    wl_shm::Format::Argb8888,
+                )
+                .expect("Works?")
+                .0
+        });
+
+        let canvas = if let Some(canvas) = self.pool.canvas(buffer) {
+            canvas
+        } else {
+            // This should be rare, but if the compositor has not released the previous
+            // buffer, we need double-buffering.
+            let (second_buffer, canvas) = self
+                .pool
+                .create_buffer(
+                    self.width as i32,
+                    self.height as i32,
+                    stride,
+                    wl_shm::Format::Argb8888,
+                )
+                .expect("create buffer");
+            *buffer = second_buffer;
+            canvas
+        };
+
+        // Draw to the window.
+        {
+            canvas
+                .chunks_exact_mut(stride as usize)
+                .enumerate()
+                .for_each(|(row_index, row)| {
+                    // let row_slice = row_slice(self.height, row_index as u32, 0.97);
+                    // let allowed_columns = (f64::from(self.width) * row_slice).ceil() as usize;
+
+                    row.chunks_exact_mut(4)
+                        .enumerate()
+                        .for_each(|(column_index, chunk)| {
+                            // const BACKGROUND_COLOR: u32 = 0xee58_5b70;
+                            const BACKGROUND_COLOR: u32 = 0xee00_0000;
+
+                            assert!(column_index as u32 <= self.width);
+
+                            // if column_index > allowed_columns
+                            //     || column_index < (self.width as usize - allowed_columns)
+                            // {
+                            //     let array: &mut [u8; 4] = chunk.try_into().unwrap();
+                            //     *array = 0u32.to_le_bytes();
+                            //     return;
+                            // }
+
+                            if column_index >= (self.max_px_width as usize) {
+                                let array: &mut [u8; 4] = chunk.try_into().unwrap();
+                                *array = BACKGROUND_COLOR.to_le_bytes();
+                            } else {
+                                assert!(column_index < self.max_px_width as usize);
+
+                                let position =
+                                    column_index + row_index * self.max_px_width as usize;
+
+                                if let Some(coverage) = &self.pixel_data.0.get(position) {
+                                    let a = (BACKGROUND_COLOR & (0xff << (6 * 4))) >> 24;
+
+                                    let (r, g, b) = if let Some(color) = self
+                                        .pixel_data
+                                        .1
+                                        .get(position)
+                                        .expect("If the pixel is set, the color will too")
+                                    {
+                                        let (r, g, b) = match color {
+                                            Color::Black => (0, 0, 0),
+                                            Color::Red => (0xff, 0, 0),
+                                            Color::Green => (0, 0xff, 0),
+                                            Color::Yellow => (0xff, 0xff, 0),
+                                            Color::Blue => (0, 0, 0xff),
+                                            Color::Purple => (0x80, 0, 0x80),
+                                            Color::Cyan => (0, 0xff, 0xff),
+                                            Color::White => (0xff, 0xff, 0xff),
+                                        };
+
+                                        let r = (r as f32 * **coverage).ceil() as u32;
+                                        let g = (g as f32 * **coverage).ceil() as u32;
+                                        let b = (b as f32 * **coverage).ceil() as u32;
+
+                                        (r, g, b)
+                                    } else {
+                                        let r = (255.0 * **coverage).ceil() as u32;
+                                        let g = (255.0 * **coverage).ceil() as u32;
+                                        let b = (255.0 * **coverage).ceil() as u32;
+
+                                        (r, g, b)
+                                    };
+
+                                    let color: u32 = (a << 24) + (r << 16) + (g << 8) + b;
+
+                                    let array: &mut [u8; 4] = chunk.try_into().unwrap();
+                                    *array = color.to_le_bytes();
+                                } else {
+                                    let array: &mut [u8; 4] = chunk.try_into().unwrap();
+                                    *array = BACKGROUND_COLOR.to_le_bytes();
+                                }
+                            }
+                        });
+                });
+        }
+
+        self.window
+            .1
+            .damage_buffer(0, 0, self.width as i32, self.height as i32);
+
+        buffer.attach_to(&self.window.1).expect("works");
+        self.window.1.commit();
+    }
+}
+
+/// # Errors
+/// If a protocol error arises.
+pub fn main(config: KeyMap) -> Result<()> {
+    let conn = Connection::connect_to_env()?;
+    let (globals, mut queue) = registry_queue_init::<AppData>(&conn)?;
+    let qh = queue.handle();
+
+    let seat: WlSeat = globals.bind(&qh, 9..=9, ())?;
+    let status_manager: ZriverStatusManagerV1 = globals.bind(&qh, 4..=4, ())?;
+    let _seat_status = status_manager.get_river_seat_status(&seat, &qh, ());
+
+    let compositor: WlCompositor = globals.bind(&qh, 6..=6, ())?;
+    let shm: WlShm = globals.bind(&qh, 1..=1, ())?;
+    // let xdg_wm: XdgWmBase = globals.bind(&qh, 5..=5, ())?;
+
+    let surface = compositor.create_surface(&qh, ());
+    let pool = SlotPool::new(1024 * 1024, &shm)?;
+
+    let zwlr_layer_shell: ZwlrLayerShellV1 = globals.bind(&qh, 4..=4, ())?;
+    let layer_surface = zwlr_layer_shell.get_layer_surface(
+        &surface,
+        None,
+        zwlr_layer_shell_v1::Layer::Overlay,
+        "river-mk-keymap which-key".to_owned(),
+        &qh,
+        (),
+    );
+
+    layer_surface.set_size(256, 256);
+    layer_surface
+        .set_anchor(zwlr_layer_surface_v1::Anchor::Left | zwlr_layer_surface_v1::Anchor::Top);
+
+    surface.commit();
+
+    let mut me = AppData {
+        config,
+        should_exit: false,
+
+        configured: false,
+        buffer: None,
+
+        width: 256,
+        height: 256,
+
+        max_px_width: 0,
+        pixel_data: (vec![], vec![]),
+
+        window: (layer_surface, surface),
+
+        pool,
+    };
+
+    loop {
+        queue.blocking_dispatch(&mut me)?;
+
+        if me.should_exit {
+            break;
+        }
+    }
+
+    Ok(())
+}
+
+// /// Calculate which amount of the current row (`i`) should be painted, if we want a corner
+// /// rounding of percent `p` and have an total of `n` rows.
+// fn row_slice(n_u32: u32, i_u32: u32, p: f64) -> f64 {
+//     fn within_tolerance(a: f64, b: f64) -> bool {
+//         const ALLOWED_ERROR: f64 = 0.000_000_1;
+//
+//         (a - b).abs() < ALLOWED_ERROR
+//     }
+//
+//     let i = f64::from(i_u32);
+//     let n = f64::from(n_u32);
+//
+//     let out = p + (1.0 - p) * (PI * i / n).sin();
+//
+//     assert!(out >= 0.0);
+//     assert!(out <= 1.0);
+//
+//     if i_u32 == 0 || i_u32 == n_u32 {
+//         assert!(within_tolerance(out, p));
+//     }
+//
+//     if i_u32 < n_u32 / 2 {
+//         assert!(within_tolerance(out, row_slice(n_u32, n_u32 - i_u32, p)));
+//     }
+//
+//     out
+// }
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs
new file mode 100644
index 00000000..7f0aaec9
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/layout.rs
@@ -0,0 +1,57 @@
+use ab_glyph::{point, Font, Glyph, Point, ScaleFont};
+
+use crate::wayland::ansi::{StyledChar, StyledString};
+
+/// Simple paragraph layout for glyphs into `target`.
+/// Starts at position `(0, ascent)`.
+///
+/// This is for testing and examples.
+pub(super) fn layout_paragraph<F, SF, BF, BSF>(
+    font: SF,
+    bold_font: BSF,
+    position: Point,
+    max_width: f32,
+    text: &StyledString,
+    target: &mut Vec<(Glyph, StyledChar)>,
+) where
+    F: Font,
+    SF: ScaleFont<F>,
+    BF: Font,
+    BSF: ScaleFont<BF>,
+{
+    let v_advance = font.height() + font.line_gap();
+    let mut caret = position + point(0.0, font.ascent());
+    let mut last_glyph: Option<Glyph> = None;
+
+    for c in text.chars() {
+        if c.as_char().is_control() {
+            if c.as_char() == '\n' {
+                caret = point(position.x, caret.y + v_advance);
+                last_glyph = None;
+            }
+            continue;
+        }
+
+        let mut glyph = if c.is_bold() {
+            bold_font.scaled_glyph(c.as_char())
+        } else {
+            font.scaled_glyph(c.as_char())
+        };
+
+        if let Some(previous) = last_glyph.take() {
+            caret.x += font.kern(previous.id, glyph.id);
+        }
+        glyph.position = caret;
+
+        last_glyph = Some(glyph.clone());
+        caret.x += font.h_advance(glyph.id);
+
+        if !c.as_char().is_whitespace() && caret.x > position.x + max_width {
+            caret = point(position.x, caret.y + v_advance);
+            glyph.position = caret;
+            last_glyph = None;
+        }
+
+        target.push((glyph, c));
+    }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
new file mode 100644
index 00000000..e92def3c
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/render/mod.rs
@@ -0,0 +1,129 @@
+use std::{fs::File, io::Read};
+
+use ab_glyph::{point, Font, FontVec, PxScale, ScaleFont};
+use anyhow::{Context, Result};
+use font_kit::{
+    family_name::FamilyName, handle::Handle, properties::Properties, source::SystemSource,
+};
+
+use crate::wayland::ansi::{Color, StyledString};
+
+mod layout;
+
+fn get_font(weight: f32) -> Result<impl Font> {
+    let handle = SystemSource::new()
+        .select_best_match(
+            &[FamilyName::Monospace],
+            Properties::new().weight(font_kit::properties::Weight(weight)),
+        )
+        .context("Failed to find a monospace font")?;
+
+    match handle {
+        Handle::Path { path, font_index } => {
+            let data = {
+                let mut buffer = vec![];
+
+                let mut file = File::open(&path)?;
+                file.read_to_end(&mut buffer)?;
+                buffer
+            };
+
+            FontVec::try_from_vec_and_index(data, font_index).with_context(|| {
+                format!(
+                    "Failed to load font at '{}' with index {}",
+                    path.display(),
+                    font_index
+                )
+            })
+        }
+        Handle::Memory { .. } => unimplemented!(),
+    }
+}
+
+pub(super) type ColorVec = (Vec<f32>, Vec<Option<Color>>);
+pub(super) fn text(input: &StyledString) -> Result<(ColorVec, (u32, u32))> {
+    let normal_font = get_font(400.0)?;
+    let bold_font = get_font(600.0)?;
+
+    let height: f32 = 15.0;
+    let px_height = height.ceil() as usize;
+
+    let scale = PxScale {
+        x: height,
+        y: height,
+    };
+
+    let scaled_font = normal_font.into_scaled(scale);
+    let bold_scaled_font = bold_font.into_scaled(scale);
+
+    let mut glyphs = Vec::new();
+    layout::layout_paragraph(
+        &scaled_font,
+        &bold_scaled_font,
+        point(0.0, 0.0),
+        9999.0,
+        input,
+        &mut glyphs,
+    );
+
+    let px_width = glyphs
+        .iter()
+        .fold(0.0, |acc, (g, c)| {
+            let next = g.position.x
+                + if c.is_bold() {
+                    bold_scaled_font.h_advance(g.id)
+                } else {
+                    scaled_font.h_advance(g.id)
+                };
+
+            if next > acc {
+                next
+            } else {
+                acc
+            }
+        })
+        .ceil() as usize;
+
+    // Rasterise to a f32 alpha vec
+    let mut pixel_data = vec![0.0; px_width * px_height];
+    let mut color_data = vec![None; px_width * px_height];
+    for (g, c) in glyphs {
+        let maybe_glyph = if c.is_bold() {
+            bold_scaled_font.outline_glyph(g)
+        } else {
+            scaled_font.outline_glyph(g)
+        };
+
+        if let Some(og) = maybe_glyph {
+            let bounds = og.px_bounds();
+            og.draw(|x, y, v| {
+                let x = x as f32 + bounds.min.x;
+                let y = y as f32 + bounds.min.y;
+                let next_idx = x as usize + y as usize * px_width;
+
+                assure_idx(&mut pixel_data, next_idx, 0.0);
+                assure_idx(&mut color_data, next_idx, None);
+
+                // save the coverage alpha
+                pixel_data[next_idx] += v;
+                color_data[next_idx] = c.color();
+            });
+        }
+    }
+
+    let len = pixel_data.len();
+    Ok((
+        (pixel_data, color_data),
+        (px_width as u32, (len / px_width) as u32),
+    ))
+}
+
+fn assure_idx<T: Copy + Clone>(pixel_data: &mut Vec<T>, next_idx: usize, fill: T) {
+    let last = pixel_data.len() - 1;
+
+    if next_idx > last {
+        let needed = next_idx - last;
+
+        pixel_data.extend(vec![fill; needed]);
+    }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/mod.rs
new file mode 100644
index 00000000..f17c7ac8
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/mod.rs
@@ -0,0 +1 @@
+pub(crate) mod protocols;
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/protocols.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/protocols.rs
new file mode 100644
index 00000000..e54b65e1
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/river/protocols.rs
@@ -0,0 +1,28 @@
+pub(crate) mod river_protocols {
+    use wayland_client;
+    // import objects from the core protocol if needed
+    use wayland_client::protocol::{wl_output, wl_seat};
+
+    // This module hosts a low-level representation of the protocol objects
+    // you will not need to interact with it yourself, but the code generated
+    // by the generate_client_code! macro will use it
+    // import the interfaces from the core protocol if needed
+
+    #[allow(non_upper_case_globals)]
+    pub(crate) mod __status {
+        use wayland_client::backend as wayland_backend;
+        use wayland_client::protocol::__interfaces::{
+            wl_output_interface, wl_seat_interface, WL_OUTPUT_INTERFACE, WL_SEAT_INTERFACE,
+        };
+        wayland_scanner::generate_interfaces!("./resources/river-status-unstable-v1.xml");
+    }
+
+    use self::__status::{
+        ZRIVER_OUTPUT_STATUS_V1_INTERFACE, ZRIVER_SEAT_STATUS_V1_INTERFACE,
+        ZRIVER_STATUS_MANAGER_V1_INTERFACE,
+    };
+
+    // This macro generates the actual types that represent the wayland objects of
+    // your custom protocol
+    wayland_scanner::generate_client_code!("./resources/river-status-unstable-v1.xml");
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/mod.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/mod.rs
new file mode 100644
index 00000000..65d3c590
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/mod.rs
@@ -0,0 +1,21 @@
+#![allow(dead_code)]
+
+pub(crate) mod multi;
+pub(crate) mod raw;
+pub(crate) mod slot;
+
+use std::io;
+
+use wayland_client::globals::GlobalError;
+
+/// An error that may occur when creating a pool.
+#[derive(Debug, thiserror::Error)]
+pub enum CreatePoolError {
+    /// The [`wl_shm`] global is not bound.
+    #[error(transparent)]
+    Global(#[from] GlobalError),
+
+    /// Error while allocating the shared memory.
+    #[error(transparent)]
+    Create(#[from] io::Error),
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/multi.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/multi.rs
new file mode 100644
index 00000000..0b1fdc1b
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/multi.rs
@@ -0,0 +1,437 @@
+//! A pool implementation which automatically manage buffers.
+//!
+//! This pool is built on the [`RawPool`].
+//!
+//! The [`MultiPool`] takes a key which is used to identify buffers and tries to return the buffer associated to the key
+//! if possible. If no buffer in the pool is associated to the key, it will create a new one.
+//!
+//! # Example
+//!
+//! ```rust
+//! use smithay_client_toolkit::reexports::client::{
+//!     QueueHandle,
+//!     protocol::wl_surface::WlSurface,
+//!     protocol::wl_shm::Format,
+//! };
+//! use smithay_client_toolkit::shm::multi::MultiPool;
+//!
+//! struct WlFoo {
+//!     // The surface we'll draw on and the index of buffer associated to it
+//!     surface: (WlSurface, usize),
+//!     pool: MultiPool<(WlSurface, usize)>
+//! }
+//!
+//! impl WlFoo {
+//!     fn draw(&mut self, qh: &QueueHandle<WlFoo>) {
+//!         let surface = &self.surface.0;
+//!         // We'll increment "i" until the pool can create a new buffer
+//!         // if there's no buffer associated with our surface and "i" or if
+//!         // a buffer with the obuffer associated with our surface and "i" is free for use.
+//!         //
+//!         // There's no limit to the amount of buffers we can allocate to our surface but since
+//!         // shm buffers are released fairly fast, it's unlikely we'll need more than double buffering.
+//!         for i in 0..2 {
+//!             self.surface.1 = i;
+//!             if let Ok((offset, buffer, slice)) = self.pool.create_buffer(
+//!                 100,
+//!                 100 * 4,
+//!                 100,
+//!                 &self.surface,
+//!                 Format::Argb8888,
+//!             ) {
+//!                 /*
+//!                     insert drawing code here
+//!                 */
+//!                 surface.attach(Some(buffer), 0, 0);
+//!                 surface.commit();
+//!                 // We exit the function after the draw.
+//!                 return;
+//!             }
+//!         }
+//!         /*
+//!             If there's no buffer available we can for example request a frame callback
+//!             and trigger a redraw when it fires.
+//!             (not shown in this example)
+//!         */
+//!     }
+//! }
+//!
+//! fn draw(slice: &mut [u8]) {
+//!     todo!()
+//! }
+//!
+//! ```
+//!
+
+use std::borrow::Borrow;
+use std::io;
+use std::os::unix::io::OwnedFd;
+
+use std::sync::{
+    atomic::{AtomicBool, Ordering},
+    Arc,
+};
+use wayland_client::backend::protocol::Message;
+use wayland_client::backend::{Backend, ObjectData, ObjectId};
+use wayland_client::{
+    protocol::{wl_buffer, wl_shm},
+    Proxy,
+};
+
+use crate::wayland::shm::CreatePoolError;
+
+use super::raw::RawPool;
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) enum PoolError {
+    #[error("buffer is currently used")]
+    InUse,
+    #[error("buffer is overlapping another")]
+    Overlap,
+    #[error("buffer could not be found")]
+    NotFound,
+}
+
+/// This pool manages buffers associated with keys.
+/// Only one buffer can be attributed to a given key.
+#[derive(Debug)]
+pub(crate) struct MultiPool<K> {
+    buffer_list: Vec<BufferSlot<K>>,
+    pub(crate) inner: RawPool,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) struct BufferSlot<K> {
+    free: Arc<AtomicBool>,
+    size: usize,
+    used: usize,
+    offset: usize,
+    buffer: Option<wl_buffer::WlBuffer>,
+    key: K,
+}
+
+impl<K> Drop for BufferSlot<K> {
+    fn drop(&mut self) {
+        self.destroy().ok();
+    }
+}
+
+impl<K> BufferSlot<K> {
+    pub(crate) fn destroy(&self) -> Result<(), PoolError> {
+        self.buffer
+            .as_ref()
+            .ok_or(PoolError::NotFound)
+            .and_then(|buffer| {
+                self.free
+                    .load(Ordering::Relaxed)
+                    .then(|| buffer.destroy())
+                    .ok_or(PoolError::InUse)
+            })
+    }
+}
+
+impl<K> MultiPool<K> {
+    pub(crate) fn new(shm: &wl_shm::WlShm) -> Result<Self, CreatePoolError> {
+        Ok(Self {
+            inner: RawPool::new(4096, shm)?,
+            buffer_list: Vec::new(),
+        })
+    }
+
+    /// Resizes the memory pool, notifying the server the pool has changed in size.
+    ///
+    /// The [`wl_shm`] protocol only allows the pool to be made bigger. If the new size is smaller than the
+    /// current size of the pool, this function will do nothing.
+    pub(crate) fn resize(&mut self, size: usize) -> io::Result<()> {
+        self.inner.resize(size)
+    }
+
+    /// Removes the buffer with the given key from the pool and rearranges the others.
+    pub(crate) fn remove<Q>(&mut self, key: &Q) -> Option<BufferSlot<K>>
+    where
+        Q: PartialEq,
+        K: Borrow<Q>,
+    {
+        self.buffer_list
+            .iter()
+            .enumerate()
+            .find(|(_, slot)| slot.key.borrow().eq(key))
+            .map(|(i, _)| i)
+            .map(|i| self.buffer_list.remove(i))
+    }
+
+    /// Insert a buffer into the pool.
+    ///
+    /// The parameters are:
+    ///
+    /// - `width`: the width of this buffer (in pixels)
+    /// - `height`: the height of this buffer (in pixels)
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one
+    /// - `key`: a borrowed form of the stored key type
+    /// - `format`: the encoding format of the pixels.
+    pub(crate) fn insert<Q>(
+        &mut self,
+        width: i32,
+        stride: i32,
+        height: i32,
+        key: &Q,
+        format: wl_shm::Format,
+    ) -> Result<usize, PoolError>
+    where
+        K: Borrow<Q>,
+        Q: PartialEq + ToOwned<Owned = K>,
+    {
+        let mut offset = 0;
+        let mut found_key = false;
+        let size = (stride * height) as usize;
+        let mut index = Err(PoolError::NotFound);
+
+        for (i, buf_slot) in self.buffer_list.iter_mut().enumerate() {
+            if buf_slot.key.borrow().eq(key) {
+                found_key = true;
+                if buf_slot.free.load(Ordering::Relaxed) {
+                    // Destroys the buffer if it's resized
+                    if size != buf_slot.used {
+                        if let Some(buffer) = buf_slot.buffer.take() {
+                            buffer.destroy();
+                        }
+                    }
+                    // Increases the size of the Buffer if it's too small and add 5% padding.
+                    // It is possible this buffer overlaps the following but the else if
+                    // statement prevents this buffer from being returned if that's the case.
+                    buf_slot.size = buf_slot.size.max(size + size / 20);
+                    index = Ok(i);
+                } else {
+                    index = Err(PoolError::InUse);
+                }
+            // If a buffer is resized, it is likely that the followings might overlap
+            } else if offset > buf_slot.offset {
+                // When the buffer is free, it's safe to shift it because we know the compositor won't try to read it.
+                if buf_slot.free.load(Ordering::Relaxed) {
+                    if offset != buf_slot.offset {
+                        if let Some(buffer) = buf_slot.buffer.take() {
+                            buffer.destroy();
+                        }
+                    }
+                    buf_slot.offset = offset;
+                } else {
+                    // If one of the overlapping buffers is busy, then no buffer can be returned because it could result in a data race.
+                    index = Err(PoolError::InUse);
+                }
+            } else if found_key {
+                break;
+            }
+            let size = (buf_slot.size + 63) & !63;
+            offset += size;
+        }
+
+        if !found_key {
+            if let Err(err) = index {
+                return self
+                    .dyn_resize(offset, width, stride, height, key.to_owned(), format)
+                    .map(|()| self.buffer_list.len() - 1)
+                    .ok_or(err);
+            }
+        }
+
+        index
+    }
+
+    /// Retreives the buffer associated with the given key.
+    ///
+    /// The parameters are:
+    ///
+    /// - `width`: the width of this buffer (in pixels)
+    /// - `height`: the height of this buffer (in pixels)
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one
+    /// - `key`: a borrowed form of the stored key type
+    /// - `format`: the encoding format of the pixels.
+    pub(crate) fn get<Q>(
+        &mut self,
+        width: i32,
+        stride: i32,
+        height: i32,
+        key: &Q,
+        format: wl_shm::Format,
+    ) -> Option<(usize, &wl_buffer::WlBuffer, &mut [u8])>
+    where
+        Q: PartialEq,
+        K: Borrow<Q>,
+    {
+        let len = self.inner.len();
+        let size = (stride * height) as usize;
+        let buf_slot = self
+            .buffer_list
+            .iter_mut()
+            .find(|buf_slot| buf_slot.key.borrow().eq(key))?;
+
+        if buf_slot.size >= size {
+            return None;
+        }
+
+        buf_slot.used = size;
+        let offset = buf_slot.offset;
+        if buf_slot.buffer.is_none() {
+            if offset + size > len {
+                self.inner.resize(offset + size + size / 20).ok()?;
+            }
+            let free = Arc::new(AtomicBool::new(true));
+            let data = BufferObjectData { free: free.clone() };
+            let buffer = self.inner.create_buffer_raw(
+                offset as i32,
+                width,
+                height,
+                stride,
+                format,
+                Arc::new(data),
+            );
+            buf_slot.free = free;
+            buf_slot.buffer = Some(buffer);
+        }
+        let buf = buf_slot.buffer.as_ref()?;
+        buf_slot.free.store(false, Ordering::Relaxed);
+        Some((offset, buf, &mut self.inner.mmap()[offset..][..size]))
+    }
+
+    /// Returns the buffer associated with the given key and its offset (usize) in the mempool.
+    ///
+    /// The parameters are:
+    ///
+    /// - `width`: the width of this buffer (in pixels)
+    /// - `height`: the height of this buffer (in pixels)
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one
+    /// - `key`: a borrowed form of the stored key type
+    /// - `format`: the encoding format of the pixels.
+    ///
+    /// The offset can be used to determine whether or not a buffer was moved in the mempool
+    /// and by consequence if it should be damaged partially or fully.
+    pub(crate) fn create_buffer<Q>(
+        &mut self,
+        width: i32,
+        stride: i32,
+        height: i32,
+        key: &Q,
+        format: wl_shm::Format,
+    ) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError>
+    where
+        K: Borrow<Q>,
+        Q: PartialEq + ToOwned<Owned = K>,
+    {
+        let index = self.insert(width, stride, height, key, format)?;
+        self.get_at(index, width, stride, height, format)
+    }
+
+    /// Retreives the buffer at the given index.
+    fn get_at(
+        &mut self,
+        index: usize,
+        width: i32,
+        stride: i32,
+        height: i32,
+        format: wl_shm::Format,
+    ) -> Result<(usize, &wl_buffer::WlBuffer, &mut [u8]), PoolError> {
+        let len = self.inner.len();
+        let size = (stride * height) as usize;
+        let buf_slot = self.buffer_list.get_mut(index).ok_or(PoolError::NotFound)?;
+
+        if size > buf_slot.size {
+            return Err(PoolError::Overlap);
+        }
+
+        buf_slot.used = size;
+        let offset = buf_slot.offset;
+        if buf_slot.buffer.is_none() {
+            if offset + size > len {
+                self.inner
+                    .resize(offset + size + size / 20)
+                    .map_err(|_| PoolError::Overlap)?;
+            }
+            let free = Arc::new(AtomicBool::new(true));
+            let data = BufferObjectData { free: free.clone() };
+            let buffer = self.inner.create_buffer_raw(
+                offset as i32,
+                width,
+                height,
+                stride,
+                format,
+                Arc::new(data),
+            );
+            buf_slot.free = free;
+            buf_slot.buffer = Some(buffer);
+        }
+        buf_slot.free.store(false, Ordering::Relaxed);
+        let buf = buf_slot.buffer.as_ref().unwrap();
+        Ok((offset, buf, &mut self.inner.mmap()[offset..][..size]))
+    }
+
+    /// Calcule the offet and size of a buffer based on its stride.
+    fn offset(mut offset: i32, stride: i32, height: i32) -> (usize, usize) {
+        // bytes per pixel
+        let size = stride * height;
+        // 5% padding.
+        offset += offset / 20;
+        offset = (offset + 63) & !63;
+        (offset as usize, size as usize)
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    /// Resizes the pool and appends a new buffer.
+    fn dyn_resize(
+        &mut self,
+        offset: usize,
+        width: i32,
+        stride: i32,
+        height: i32,
+        key: K,
+        format: wl_shm::Format,
+    ) -> Option<()> {
+        let (offset, size) = Self::offset(offset as i32, stride, height);
+        if self.inner.len() < offset + size {
+            self.resize(offset + size + size / 20).ok()?;
+        }
+        let free = Arc::new(AtomicBool::new(true));
+        let data = BufferObjectData { free: free.clone() };
+        let buffer = self.inner.create_buffer_raw(
+            offset as i32,
+            width,
+            height,
+            stride,
+            format,
+            Arc::new(data),
+        );
+        self.buffer_list.push(BufferSlot {
+            offset,
+            used: 0,
+            free,
+            buffer: Some(buffer),
+            size,
+            key,
+        });
+        Some(())
+    }
+}
+
+struct BufferObjectData {
+    free: Arc<AtomicBool>,
+}
+
+impl ObjectData for BufferObjectData {
+    fn event(
+        self: Arc<Self>,
+        _backend: &Backend,
+        msg: Message<ObjectId, OwnedFd>,
+    ) -> Option<Arc<dyn ObjectData>> {
+        debug_assert!(wayland_client::backend::protocol::same_interface(
+            msg.sender_id.interface(),
+            wl_buffer::WlBuffer::interface()
+        ));
+        debug_assert!(msg.opcode == 0);
+
+        // wl_buffer only has a single event: wl_buffer.release
+        self.free.store(true, Ordering::Relaxed);
+
+        None
+    }
+
+    fn destroyed(&self, _: ObjectId) {}
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/raw.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/raw.rs
new file mode 100644
index 00000000..a12afaa0
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/raw.rs
@@ -0,0 +1,290 @@
+//! A raw shared memory pool handler.
+//!
+//! This is intended as a safe building block for higher level shared memory pool abstractions and is not
+//! encouraged for most library users.
+
+use rustix::{
+    io::Errno,
+    shm::{Mode, OFlags},
+};
+use std::{
+    fs::File,
+    io,
+    ops::Deref,
+    os::unix::prelude::{AsFd, BorrowedFd, OwnedFd},
+    sync::Arc,
+    time::{SystemTime, UNIX_EPOCH},
+};
+
+use memmap2::MmapMut;
+use wayland_client::{
+    backend::ObjectData,
+    protocol::{wl_buffer, wl_shm, wl_shm_pool},
+    Dispatch, Proxy, QueueHandle, WEnum,
+};
+
+use super::CreatePoolError;
+
+/// A raw handler for file backed shared memory pools.
+///
+/// This type of pool will create the SHM memory pool and provide a way to resize the pool.
+///
+/// This pool does not release buffers. If you need this, use one of the higher level pools.
+#[derive(Debug)]
+pub struct RawPool {
+    pool: DestroyOnDropPool,
+    len: usize,
+    mem_file: File,
+    mmap: MmapMut,
+}
+
+impl RawPool {
+    pub fn new(len: usize, shm: &wl_shm::WlShm) -> Result<RawPool, CreatePoolError> {
+        let shm_fd = RawPool::create_shm_fd()?;
+        let mem_file = File::from(shm_fd);
+        mem_file.set_len(len as u64)?;
+
+        let pool = shm
+            .send_constructor(
+                wl_shm::Request::CreatePool {
+                    fd: mem_file.as_fd(),
+                    size: len as i32,
+                },
+                Arc::new(ShmPoolData),
+            )
+            .unwrap_or_else(|_| Proxy::inert(shm.backend().clone()));
+        let mmap = unsafe { MmapMut::map_mut(&mem_file)? };
+
+        Ok(RawPool {
+            pool: DestroyOnDropPool(pool),
+            len,
+            mem_file,
+            mmap,
+        })
+    }
+
+    /// Resizes the memory pool, notifying the server the pool has changed in size.
+    ///
+    /// The [`wl_shm`] protocol only allows the pool to be made bigger. If the new size is smaller than the
+    /// current size of the pool, this function will do nothing.
+    pub fn resize(&mut self, size: usize) -> io::Result<()> {
+        if size > self.len {
+            self.len = size;
+            self.mem_file.set_len(size as u64)?;
+            self.pool.resize(size as i32);
+            self.mmap = unsafe { MmapMut::map_mut(&self.mem_file) }?;
+        }
+
+        Ok(())
+    }
+
+    /// Returns a reference to the underlying shared memory file using the memmap2 crate.
+    pub fn mmap(&mut self) -> &mut MmapMut {
+        &mut self.mmap
+    }
+
+    /// Returns the size of the mempool
+    #[allow(clippy::len_without_is_empty)]
+    pub fn len(&self) -> usize {
+        self.len
+    }
+
+    /// Create a new buffer to this pool.
+    ///
+    /// ## Parameters
+    /// - `offset`: the offset (in bytes) from the beginning of the pool at which this buffer starts.
+    /// - `width` and `height`: the width and height of the buffer in pixels.
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one.
+    /// - `format`: the encoding format of the pixels.
+    ///
+    /// The encoding format of the pixels must be supported by the compositor or else a protocol error is
+    /// risen. You can ensure the format is supported by listening to [`Shm::formats`](crate::shm::Shm::formats).
+    ///
+    /// Note this function only creates the [`wl_buffer`] object, you will need to write to the pixels using the
+    /// [`io::Write`] implementation or [`RawPool::mmap`].
+    #[allow(clippy::too_many_arguments)]
+    pub fn create_buffer<D, U>(
+        &mut self,
+        offset: i32,
+        width: i32,
+        height: i32,
+        stride: i32,
+        format: wl_shm::Format,
+        udata: U,
+        qh: &QueueHandle<D>,
+    ) -> wl_buffer::WlBuffer
+    where
+        D: Dispatch<wl_buffer::WlBuffer, U> + 'static,
+        U: Send + Sync + 'static,
+    {
+        self.pool
+            .create_buffer(offset, width, height, stride, format, qh, udata)
+    }
+
+    /// Create a new buffer to this pool.
+    ///
+    /// This is identical to [`Self::create_buffer`], but allows using a custom [`ObjectData`]
+    /// implementation instead of relying on the [Dispatch] interface.
+    #[allow(clippy::too_many_arguments)]
+    pub fn create_buffer_raw(
+        &mut self,
+        offset: i32,
+        width: i32,
+        height: i32,
+        stride: i32,
+        format: wl_shm::Format,
+        data: Arc<dyn ObjectData + 'static>,
+    ) -> wl_buffer::WlBuffer {
+        self.pool
+            .send_constructor(
+                wl_shm_pool::Request::CreateBuffer {
+                    offset,
+                    width,
+                    height,
+                    stride,
+                    format: WEnum::Value(format),
+                },
+                data,
+            )
+            .unwrap_or_else(|_| Proxy::inert(self.pool.backend().clone()))
+    }
+
+    /// Returns the pool object used to communicate with the server.
+    pub fn pool(&self) -> &wl_shm_pool::WlShmPool {
+        &self.pool
+    }
+}
+
+impl AsFd for RawPool {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.mem_file.as_fd()
+    }
+}
+
+impl From<RawPool> for OwnedFd {
+    fn from(pool: RawPool) -> Self {
+        pool.mem_file.into()
+    }
+}
+
+impl io::Write for RawPool {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        io::Write::write(&mut self.mem_file, buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        io::Write::flush(&mut self.mem_file)
+    }
+}
+
+impl io::Seek for RawPool {
+    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
+        io::Seek::seek(&mut self.mem_file, pos)
+    }
+}
+
+impl RawPool {
+    fn create_shm_fd() -> io::Result<OwnedFd> {
+        #[cfg(target_os = "linux")]
+        {
+            match RawPool::create_memfd() {
+                Ok(fd) => return Ok(fd),
+
+                // Not supported, use fallback.
+                Err(Errno::NOSYS) => (),
+
+                Err(err) => return Err(Into::<io::Error>::into(err)),
+            }
+        }
+
+        let time = SystemTime::now();
+        let mut mem_file_handle = format!(
+            "/smithay-client-toolkit-{}",
+            time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
+        );
+
+        loop {
+            let flags = OFlags::CREATE | OFlags::EXCL | OFlags::RDWR;
+
+            let mode = Mode::RUSR | Mode::WUSR;
+
+            match rustix::shm::open(mem_file_handle.as_str(), flags, mode) {
+                Ok(fd) => match rustix::shm::unlink(mem_file_handle.as_str()) {
+                    Ok(()) => return Ok(fd),
+
+                    Err(errno) => {
+                        return Err(errno.into());
+                    }
+                },
+
+                Err(Errno::EXIST) => {
+                    // Change the handle if we happen to be duplicate.
+                    let time = SystemTime::now();
+
+                    mem_file_handle = format!(
+                        "/smithay-client-toolkit-{}",
+                        time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
+                    );
+                }
+
+                Err(Errno::INTR) => (),
+
+                Err(err) => return Err(err.into()),
+            }
+        }
+    }
+
+    #[cfg(target_os = "linux")]
+    fn create_memfd() -> rustix::io::Result<OwnedFd> {
+        use rustix::fs::{MemfdFlags, SealFlags};
+
+        loop {
+            let name = c"smithay-client-toolkit";
+            let flags = MemfdFlags::ALLOW_SEALING | MemfdFlags::CLOEXEC;
+
+            match rustix::fs::memfd_create(name, flags) {
+                Ok(fd) => {
+                    // We only need to seal for the purposes of optimization, ignore the errors.
+                    let _ = rustix::fs::fcntl_add_seals(&fd, SealFlags::SHRINK | SealFlags::SEAL);
+                    return Ok(fd);
+                }
+
+                Err(Errno::INTR) => (),
+
+                Err(err) => return Err(err),
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+struct DestroyOnDropPool(wl_shm_pool::WlShmPool);
+
+impl Deref for DestroyOnDropPool {
+    type Target = wl_shm_pool::WlShmPool;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl Drop for DestroyOnDropPool {
+    fn drop(&mut self) {
+        self.0.destroy();
+    }
+}
+
+#[derive(Debug)]
+struct ShmPoolData;
+
+impl ObjectData for ShmPoolData {
+    fn event(
+        self: Arc<Self>,
+        _: &wayland_client::backend::Backend,
+        _: wayland_client::backend::protocol::Message<wayland_client::backend::ObjectId, OwnedFd>,
+    ) -> Option<Arc<(dyn ObjectData + 'static)>> {
+        unreachable!("wl_shm_pool has no events")
+    }
+
+    fn destroyed(&self, _: wayland_client::backend::ObjectId) {}
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/slot.rs b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/slot.rs
new file mode 100644
index 00000000..ab52c5f6
--- /dev/null
+++ b/pkgs/by-name/ri/river-mk-keymap/src/wayland/shm/slot.rs
@@ -0,0 +1,596 @@
+//! A pool implementation based on buffer slots
+
+use std::io;
+use std::{
+    os::unix::io::{AsRawFd, OwnedFd},
+    sync::{
+        atomic::{AtomicU8, AtomicUsize, Ordering},
+        Arc, Mutex, Weak,
+    },
+};
+
+use wayland_client::backend::protocol::Message;
+use wayland_client::backend::{ObjectData, ObjectId};
+use wayland_client::{
+    protocol::{wl_buffer, wl_shm, wl_surface},
+    Proxy,
+};
+
+use crate::wayland::shm::raw::RawPool;
+use crate::wayland::shm::CreatePoolError;
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) enum CreateBufferError {
+    /// Slot creation error.
+    #[error(transparent)]
+    Io(#[from] io::Error),
+
+    /// Pool mismatch.
+    #[error("Incorrect pool for slot")]
+    PoolMismatch,
+
+    /// Slot size mismatch
+    #[error("Requested buffer size is too large for slot")]
+    SlotTooSmall,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) enum ActivateSlotError {
+    /// Buffer was already active
+    #[error("Buffer was already active")]
+    AlreadyActive,
+}
+
+#[derive(Debug)]
+pub(crate) struct SlotPool {
+    pub(crate) inner: RawPool,
+    free_list: Arc<Mutex<Vec<FreelistEntry>>>,
+}
+
+#[derive(Debug)]
+struct FreelistEntry {
+    offset: usize,
+    len: usize,
+}
+
+/// A chunk of memory allocated from a [`SlotPool`]
+///
+/// Retaining this object is only required if you wish to resize or change the buffer's format
+/// without changing the contents of the backing memory.
+#[derive(Debug)]
+pub(crate) struct Slot {
+    inner: Arc<SlotInner>,
+}
+
+#[derive(Debug)]
+struct SlotInner {
+    free_list: Weak<Mutex<Vec<FreelistEntry>>>,
+    offset: usize,
+    len: usize,
+    active_buffers: AtomicUsize,
+    /// Count of all "real" references to this slot.  This includes all Slot objects and any
+    /// [`BufferData`] object that is not in the DEAD state.  When this reaches zero, the memory for
+    /// this slot will return to the [`free_list`].  It is not possible for it to reach zero and have a
+    /// Slot or Buffer referring to it.
+    all_refs: AtomicUsize,
+}
+
+/// A wrapper around a [`wl_buffer::WlBuffer`] which has been allocated via a [`SlotPool`].
+///
+/// When this object is dropped, the buffer will be destroyed immediately if it is not active, or
+/// upon the server's release if it is.
+#[derive(Debug)]
+pub(crate) struct Buffer {
+    inner: wl_buffer::WlBuffer,
+    height: i32,
+    stride: i32,
+    slot: Slot,
+}
+
+/// [`ObjectData`] for the [`WlBuffer`]
+#[derive(Debug)]
+struct BufferData {
+    inner: Arc<SlotInner>,
+    state: AtomicU8,
+}
+
+// These constants define the value of BufferData::state, since AtomicEnum does not exist.
+impl BufferData {
+    /// Buffer is counted in [`active_buffers`] list; will return to INACTIVE on Release.
+    const ACTIVE: u8 = 0;
+
+    /// Buffer is not counted in [`active_buffers`] list, but also has not been destroyed.
+    const INACTIVE: u8 = 1;
+
+    /// Buffer is counted in [`active_buffers`] list; will move to DEAD on Release
+    const DESTROY_ON_RELEASE: u8 = 2;
+
+    /// Buffer has been destroyed
+    const DEAD: u8 = 3;
+
+    /// Value that is [`ORed`] on buffer release to transition to the next state
+    const RELEASE_SET: u8 = 1;
+
+    /// Value that is [`ORed`] on buffer destroy to transition to the next state
+    const DESTROY_SET: u8 = 2;
+
+    /// Call after successfully transitioning the state to DEAD
+    fn record_death(&self) {
+        drop(Slot {
+            inner: self.inner.clone(),
+        });
+    }
+}
+
+impl SlotPool {
+    pub(crate) fn new(len: usize, shm: &wl_shm::WlShm) -> Result<Self, CreatePoolError> {
+        let inner = RawPool::new(len, shm)?;
+        let free_list = Arc::new(Mutex::new(vec![FreelistEntry {
+            offset: 0,
+            len: inner.len(),
+        }]));
+        Ok(SlotPool { inner, free_list })
+    }
+
+    /// Create a new buffer in a new slot.
+    ///
+    /// This returns the buffer and the canvas.  The parameters are:
+    ///
+    /// - `width`: the width of this buffer (in pixels)
+    /// - `height`: the height of this buffer (in pixels)
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one
+    /// - `format`: the encoding format of the pixels. Using a format that was not
+    ///   advertised to the `wl_shm` global by the server is a protocol error and will
+    ///   terminate your connection.
+    ///
+    /// The [Slot] for this buffer will have exactly the size required for the data.  It can be
+    /// accessed via [`Buffer::slot`] to create additional buffers that point to the same data.  This
+    /// is required if you wish to change formats, buffer dimensions, or attach a canvas to
+    /// multiple surfaces.
+    ///
+    /// For more control over sizing, use [`Self::new_slot`] and [`Self::create_buffer_in`].
+    pub(crate) fn create_buffer(
+        &mut self,
+        width: i32,
+        height: i32,
+        stride: i32,
+        format: wl_shm::Format,
+    ) -> Result<(Buffer, &mut [u8]), CreateBufferError> {
+        let len = (height as usize) * (stride as usize);
+        let slot = self.new_slot(len)?;
+        let buffer = self.create_buffer_in(&slot, width, height, stride, format)?;
+        let canvas = self.raw_data_mut(&slot);
+        Ok((buffer, canvas))
+    }
+
+    /// Get the bytes corresponding to a given slot or buffer if drawing to the slot is permitted.
+    ///
+    /// Returns `None` if there are active buffers in the slot or if the slot does not correspond
+    /// to this pool.
+    pub(crate) fn canvas(&mut self, key: &impl CanvasKey) -> Option<&mut [u8]> {
+        key.canvas(self)
+    }
+
+    /// Returns the size, in bytes, of this pool.
+    #[allow(clippy::len_without_is_empty)]
+    pub(crate) fn len(&self) -> usize {
+        self.inner.len()
+    }
+
+    /// Resizes the memory pool, notifying the server the pool has changed in size.
+    ///
+    /// This is an optimization; the pool automatically resizes when you allocate new slots.
+    pub(crate) fn resize(&mut self, size: usize) -> io::Result<()> {
+        let old_len = self.inner.len();
+        self.inner.resize(size)?;
+        let new_len = self.inner.len();
+        if old_len == new_len {
+            return Ok(());
+        }
+        // add the new memory to the freelist
+        let mut free = self.free_list.lock().unwrap();
+        if let Some(FreelistEntry { offset, len }) = free.last_mut() {
+            if *offset + *len == old_len {
+                *len += new_len - old_len;
+                return Ok(());
+            }
+        }
+        free.push(FreelistEntry {
+            offset: old_len,
+            len: new_len - old_len,
+        });
+        Ok(())
+    }
+
+    fn alloc(&mut self, size: usize) -> io::Result<usize> {
+        let mut free = self.free_list.lock().unwrap();
+        for FreelistEntry { offset, len } in free.iter_mut() {
+            if *len >= size {
+                let rv = *offset;
+                *len -= size;
+                *offset += size;
+                return Ok(rv);
+            }
+        }
+        let mut rv = self.inner.len();
+        let mut pop_tail = false;
+        if let Some(FreelistEntry { offset, len }) = free.last() {
+            if offset + len == self.inner.len() {
+                rv -= len;
+                pop_tail = true;
+            }
+        }
+        // resize like Vec::reserve, always at least doubling
+        let target = std::cmp::max(rv + size, self.inner.len() * 2);
+        self.inner.resize(target)?;
+        // adjust the end of the freelist here
+        if pop_tail {
+            free.pop();
+        }
+        if target > rv + size {
+            free.push(FreelistEntry {
+                offset: rv + size,
+                len: target - rv - size,
+            });
+        }
+        Ok(rv)
+    }
+
+    fn free(free_list: &Mutex<Vec<FreelistEntry>>, mut offset: usize, mut len: usize) {
+        let mut free = free_list.lock().unwrap();
+        let mut nf = Vec::with_capacity(free.len() + 1);
+        for &FreelistEntry {
+            offset: ioff,
+            len: ilen,
+        } in free.iter()
+        {
+            if ioff + ilen == offset {
+                offset = ioff;
+                len += ilen;
+                continue;
+            }
+            if ioff == offset + len {
+                len += ilen;
+                continue;
+            }
+            if ioff > offset + len && len != 0 {
+                nf.push(FreelistEntry { offset, len });
+                len = 0;
+            }
+            if ilen != 0 {
+                nf.push(FreelistEntry {
+                    offset: ioff,
+                    len: ilen,
+                });
+            }
+        }
+        if len != 0 {
+            nf.push(FreelistEntry { offset, len });
+        }
+        *free = nf;
+    }
+
+    /// Create a new slot with the given size in bytes.
+    pub(crate) fn new_slot(&mut self, mut len: usize) -> io::Result<Slot> {
+        len = (len + 63) & !63;
+        let offset = self.alloc(len)?;
+
+        Ok(Slot {
+            inner: Arc::new(SlotInner {
+                free_list: Arc::downgrade(&self.free_list),
+                offset,
+                len,
+                active_buffers: AtomicUsize::new(0),
+                all_refs: AtomicUsize::new(1),
+            }),
+        })
+    }
+
+    /// Get the bytes corresponding to a given slot.
+    ///
+    /// Note: prefer using [`Self::canvas`], which will prevent drawing to a buffer that has not been
+    /// released by the server.
+    ///
+    /// Returns an empty buffer if the slot does not belong to this pool.
+    pub(crate) fn raw_data_mut(&mut self, slot: &Slot) -> &mut [u8] {
+        if slot.inner.free_list.as_ptr() == Arc::as_ptr(&self.free_list) {
+            &mut self.inner.mmap()[slot.inner.offset..][..slot.inner.len]
+        } else {
+            &mut []
+        }
+    }
+
+    /// Create a new buffer corresponding to a slot.
+    ///
+    /// The parameters are:
+    ///
+    /// - `width`: the width of this buffer (in pixels)
+    /// - `height`: the height of this buffer (in pixels)
+    /// - `stride`: distance (in bytes) between the beginning of a row and the next one
+    /// - `format`: the encoding format of the pixels. Using a format that was not
+    ///   advertised to the `wl_shm` global by the server is a protocol error and will
+    ///   terminate your connection
+    pub(crate) fn create_buffer_in(
+        &mut self,
+        slot: &Slot,
+        width: i32,
+        height: i32,
+        stride: i32,
+        format: wl_shm::Format,
+    ) -> Result<Buffer, CreateBufferError> {
+        let offset = slot.inner.offset as i32;
+        let len = (height as usize) * (stride as usize);
+        if len > slot.inner.len {
+            return Err(CreateBufferError::SlotTooSmall);
+        }
+
+        if slot.inner.free_list.as_ptr() != Arc::as_ptr(&self.free_list) {
+            return Err(CreateBufferError::PoolMismatch);
+        }
+
+        let slot = slot.clone();
+        // take a ref for the BufferData, which will be destroyed by BufferData::record_death
+        slot.inner.all_refs.fetch_add(1, Ordering::Relaxed);
+        let data = Arc::new(BufferData {
+            inner: slot.inner.clone(),
+            state: AtomicU8::new(BufferData::INACTIVE),
+        });
+        let buffer = self
+            .inner
+            .create_buffer_raw(offset, width, height, stride, format, data);
+        Ok(Buffer {
+            inner: buffer,
+            height,
+            stride,
+            slot,
+        })
+    }
+}
+
+impl Clone for Slot {
+    fn clone(&self) -> Self {
+        let inner = self.inner.clone();
+        inner.all_refs.fetch_add(1, Ordering::Relaxed);
+        Slot { inner }
+    }
+}
+
+impl Drop for Slot {
+    fn drop(&mut self) {
+        if self.inner.all_refs.fetch_sub(1, Ordering::Relaxed) == 1 {
+            if let Some(free_list) = self.inner.free_list.upgrade() {
+                SlotPool::free(&free_list, self.inner.offset, self.inner.len);
+            }
+        }
+    }
+}
+
+impl Drop for SlotInner {
+    fn drop(&mut self) {
+        debug_assert_eq!(*self.all_refs.get_mut(), 0);
+    }
+}
+
+/// A helper trait for [`SlotPool::canvas`].
+pub(crate) trait CanvasKey {
+    fn canvas<'pool>(&self, pool: &'pool mut SlotPool) -> Option<&'pool mut [u8]>;
+}
+
+impl Slot {
+    /// Return true if there are buffers referencing this slot whose contents are being accessed
+    /// by the server.
+    pub(crate) fn has_active_buffers(&self) -> bool {
+        self.inner.active_buffers.load(Ordering::Relaxed) != 0
+    }
+
+    /// Returns the size, in bytes, of this slot.
+    #[allow(clippy::len_without_is_empty)]
+    pub(crate) fn len(&self) -> usize {
+        self.inner.len
+    }
+
+    /// Get the bytes corresponding to a given slot if drawing to the slot is permitted.
+    ///
+    /// Returns `None` if there are active buffers in the slot or if the slot does not correspond
+    /// to this pool.
+    pub(crate) fn canvas<'pool>(&self, pool: &'pool mut SlotPool) -> Option<&'pool mut [u8]> {
+        if self.has_active_buffers() {
+            return None;
+        }
+        if self.inner.free_list.as_ptr() == Arc::as_ptr(&pool.free_list) {
+            Some(&mut pool.inner.mmap()[self.inner.offset..][..self.inner.len])
+        } else {
+            None
+        }
+    }
+}
+
+impl CanvasKey for Slot {
+    fn canvas<'pool>(&self, pool: &'pool mut SlotPool) -> Option<&'pool mut [u8]> {
+        self.canvas(pool)
+    }
+}
+
+impl Buffer {
+    /// Attach a buffer to a surface.
+    ///
+    /// This marks the slot as active until the server releases the buffer, which will happen
+    /// automatically assuming the surface is committed without attaching a different buffer.
+    ///
+    /// Note: if you need to ensure that [`canvas()`](Buffer::canvas) calls never return data that
+    /// could be attached to a surface in a multi-threaded client, make this call while you have
+    /// exclusive access to the corresponding [`SlotPool`].
+    pub(crate) fn attach_to(&self, surface: &wl_surface::WlSurface) -> Result<(), ActivateSlotError> {
+        self.activate()?;
+        surface.attach(Some(&self.inner), 0, 0);
+        Ok(())
+    }
+
+    /// Get the inner buffer.
+    pub(crate) fn wl_buffer(&self) -> &wl_buffer::WlBuffer {
+        &self.inner
+    }
+
+    pub(crate) fn height(&self) -> i32 {
+        self.height
+    }
+
+    pub(crate) fn stride(&self) -> i32 {
+        self.stride
+    }
+
+    fn data(&self) -> Option<&BufferData> {
+        self.inner.object_data()?.downcast_ref()
+    }
+
+    /// Get the bytes corresponding to this buffer if drawing is permitted.
+    ///
+    /// This may be smaller than the canvas associated with the slot.
+    pub(crate) fn canvas<'pool>(&self, pool: &'pool mut SlotPool) -> Option<&'pool mut [u8]> {
+        let len = (self.height as usize) * (self.stride as usize);
+        if self.slot.inner.active_buffers.load(Ordering::Relaxed) != 0 {
+            return None;
+        }
+        if self.slot.inner.free_list.as_ptr() == Arc::as_ptr(&pool.free_list) {
+            Some(&mut pool.inner.mmap()[self.slot.inner.offset..][..len])
+        } else {
+            None
+        }
+    }
+
+    /// Get the slot corresponding to this buffer.
+    pub(crate) fn slot(&self) -> Slot {
+        self.slot.clone()
+    }
+
+    /// Manually mark a buffer as active.
+    ///
+    /// An active buffer prevents drawing on its slot until a Release event is received or until
+    /// manually deactivated.
+    pub(crate) fn activate(&self) -> Result<(), ActivateSlotError> {
+        let data = self.data().expect("UserData type mismatch");
+
+        // This bitwise AND will transition INACTIVE -> ACTIVE, or do nothing if the buffer was
+        // already ACTIVE.  No other ordering is required, as the server will not send a Release
+        // until we send our attach after returning Ok.
+        match data
+            .state
+            .fetch_and(!BufferData::RELEASE_SET, Ordering::Relaxed)
+        {
+            BufferData::INACTIVE => {
+                data.inner.active_buffers.fetch_add(1, Ordering::Relaxed);
+                Ok(())
+            }
+            BufferData::ACTIVE => Err(ActivateSlotError::AlreadyActive),
+            _ => unreachable!("Invalid state in BufferData"),
+        }
+    }
+
+    /// Manually mark a buffer as inactive.
+    ///
+    /// This should be used when the buffer was manually marked as active or when a buffer was
+    /// attached to a surface but not committed.  Calling this function on a buffer that was
+    /// committed to a surface risks making the surface contents undefined.
+    pub(crate) fn deactivate(&self) -> Result<(), ActivateSlotError> {
+        let data = self.data().expect("UserData type mismatch");
+
+        // Same operation as the Release event, but we know the Buffer was not dropped.
+        match data
+            .state
+            .fetch_or(BufferData::RELEASE_SET, Ordering::Relaxed)
+        {
+            BufferData::ACTIVE => {
+                data.inner.active_buffers.fetch_sub(1, Ordering::Relaxed);
+                Ok(())
+            }
+            BufferData::INACTIVE => Err(ActivateSlotError::AlreadyActive),
+            _ => unreachable!("Invalid state in BufferData"),
+        }
+    }
+}
+
+impl CanvasKey for Buffer {
+    fn canvas<'pool>(&self, pool: &'pool mut SlotPool) -> Option<&'pool mut [u8]> {
+        self.canvas(pool)
+    }
+}
+
+impl Drop for Buffer {
+    fn drop(&mut self) {
+        if let Some(data) = self.data() {
+            match data
+                .state
+                .fetch_or(BufferData::DESTROY_SET, Ordering::Relaxed)
+            {
+                BufferData::ACTIVE => {
+                    // server is using the buffer, let ObjectData handle the destroy
+                }
+                BufferData::INACTIVE => {
+                    data.record_death();
+                    self.inner.destroy();
+                }
+                _ => unreachable!("Invalid state in BufferData"),
+            }
+        }
+    }
+}
+
+impl ObjectData for BufferData {
+    fn event(
+        self: Arc<Self>,
+        handle: &wayland_client::backend::Backend,
+        msg: Message<ObjectId, OwnedFd>,
+    ) -> Option<Arc<dyn ObjectData>> {
+        debug_assert!(wayland_client::backend::protocol::same_interface(
+            msg.sender_id.interface(),
+            wl_buffer::WlBuffer::interface()
+        ));
+        debug_assert!(msg.opcode == 0);
+
+        match self
+            .state
+            .fetch_or(BufferData::RELEASE_SET, Ordering::Relaxed)
+        {
+            BufferData::ACTIVE => {
+                self.inner.active_buffers.fetch_sub(1, Ordering::Relaxed);
+            }
+            BufferData::INACTIVE => {
+                // possible spurious release, or someone called deactivate incorrectly
+                eprintln!("Unexpected WlBuffer::Release on an inactive buffer");
+            }
+            BufferData::DESTROY_ON_RELEASE => {
+                self.record_death();
+                self.inner.active_buffers.fetch_sub(1, Ordering::Relaxed);
+
+                // The Destroy message is identical to Release message (no args, same ID), so just reply
+                handle
+                    .send_request(msg.map_fd(|x| x.as_raw_fd()), None, None)
+                    .expect("Unexpected invalid ID");
+            }
+            BufferData::DEAD => {
+                // no-op, this object is already unusable
+            }
+            _ => unreachable!("Invalid state in BufferData"),
+        }
+
+        None
+    }
+
+    fn destroyed(&self, _: ObjectId) {}
+}
+
+impl Drop for BufferData {
+    fn drop(&mut self) {
+        let state = *self.state.get_mut();
+        if state == BufferData::ACTIVE || state == BufferData::DESTROY_ON_RELEASE {
+            // Release the active-buffer count
+            self.inner.active_buffers.fetch_sub(1, Ordering::Relaxed);
+        }
+
+        if state != BufferData::DEAD {
+            // nobody has ever transitioned state to DEAD, so we are responsible for freeing the
+            // extra reference
+            self.record_death();
+        }
+    }
+}
diff --git a/pkgs/by-name/ri/river-mk-keymap/update.sh b/pkgs/by-name/ri/river-mk-keymap/update.sh
index 9268caf2..8e36e13e 100755
--- a/pkgs/by-name/ri/river-mk-keymap/update.sh
+++ b/pkgs/by-name/ri/river-mk-keymap/update.sh
@@ -1,3 +1,14 @@
 #!/bin/sh
 
-cargo update && cargo upgrade
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[ "$1" = "upgrade" ] && cargo upgrade
+cargo update
diff --git a/pkgs/by-name/ri/river-start/package.nix b/pkgs/by-name/ri/river-start/package.nix
deleted file mode 100644
index ab986708..00000000
--- a/pkgs/by-name/ri/river-start/package.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  writeShellApplication,
-  river,
-}:
-writeShellApplication {
-  name = "river-start";
-  text = builtins.readFile ./river-start.sh;
-  runtimeInputs = [
-    river
-  ];
-  meta = {
-    mainProgram = "river-start";
-  };
-}
diff --git a/pkgs/by-name/ri/river-start/river-start.sh b/pkgs/by-name/ri/river-start/river-start.sh
deleted file mode 100755
index 4587ff96..00000000
--- a/pkgs/by-name/ri/river-start/river-start.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /usr/bin/env sh
-
-# NOTE: Keep this in sync with the file from `base_init.sh` <2025-02-03>
-RIVER_LOG_FILE="$HOME/.local/share/river/log"
-
-[ -d "$(dirname "$RIVER_LOG_FILE")" ] || mkdir --parents "$(dirname "$RIVER_LOG_FILE")"
-
-exec river -log-level info >"$RIVER_LOG_FILE" 2>&1
-
-# vim: ft=sh
diff --git a/pkgs/by-name/sc/screenshot_persistent/package.nix b/pkgs/by-name/sc/screenshot_persistent/package.nix
index 12bb3968..34054b0f 100644
--- a/pkgs/by-name/sc/screenshot_persistent/package.nix
+++ b/pkgs/by-name/sc/screenshot_persistent/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   writeShellApplication,
   grim,
diff --git a/pkgs/by-name/sc/screenshot_persistent/screenshot_persistent.sh b/pkgs/by-name/sc/screenshot_persistent/screenshot_persistent.sh
index 2bea69e3..0eeb75c0 100755
--- a/pkgs/by-name/sc/screenshot_persistent/screenshot_persistent.sh
+++ b/pkgs/by-name/sc/screenshot_persistent/screenshot_persistent.sh
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 # shellcheck shell=bash
 
 tmp="$(mktemp)"
diff --git a/pkgs/by-name/sc/screenshot_temporary/package.nix b/pkgs/by-name/sc/screenshot_temporary/package.nix
index f3739b01..49bbeeb7 100644
--- a/pkgs/by-name/sc/screenshot_temporary/package.nix
+++ b/pkgs/by-name/sc/screenshot_temporary/package.nix
@@ -1,15 +1,24 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
+  writeShellApplication,
+  # Dependencies
   grim,
   slurp,
   wl-clipboard,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "screenshot_temporary";
-  src = ./screenshot_temporary.sh;
-  generateCompletions = false;
-  keepPath = false;
-  dependencies = [
+  text = builtins.readFile ./screenshot_temporary.sh;
+  inheritPath = false;
+  runtimeInputs = [
     grim
     slurp
     wl-clipboard
diff --git a/pkgs/by-name/sc/screenshot_temporary/screenshot_temporary.sh b/pkgs/by-name/sc/screenshot_temporary/screenshot_temporary.sh
index 8968ca79..db1a79b7 100755
--- a/pkgs/by-name/sc/screenshot_temporary/screenshot_temporary.sh
+++ b/pkgs/by-name/sc/screenshot_temporary/screenshot_temporary.sh
@@ -1,7 +1,14 @@
 #! /usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 grim -g "$(slurp)" | wl-copy
 
diff --git a/pkgs/by-name/sn/snap-sync-forked/package.nix b/pkgs/by-name/sn/snap-sync-forked/package.nix
deleted file mode 100644
index b3f40b24..00000000
--- a/pkgs/by-name/sn/snap-sync-forked/package.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-  sysLib,
-  bash,
-  btrfs-progs,
-  coreutils,
-  gawk,
-  gnugrep,
-  snapper,
-  util-linux,
-  # optional
-  libnotify,
-  openssh,
-  pv,
-  rsync,
-  sudo,
-}:
-sysLib.writeShellScript {
-  name = "snap-sync-forked";
-  src = ./snap-sync-forked.sh;
-  dependencies = [
-    bash
-    btrfs-progs
-    coreutils
-    gawk
-    gnugrep
-    snapper
-    util-linux
-
-    # optional:
-    libnotify
-    openssh
-    pv
-    rsync
-    sudo
-  ];
-}
diff --git a/pkgs/by-name/sn/snap-sync-forked/snap-sync-forked.sh b/pkgs/by-name/sn/snap-sync-forked/snap-sync-forked.sh
deleted file mode 100755
index 3d9c1ac9..00000000
--- a/pkgs/by-name/sn/snap-sync-forked/snap-sync-forked.sh
+++ /dev/null
@@ -1,534 +0,0 @@
-#!/usr/bin/env bash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-#
-# snap-sync
-# https://github.com/wesbarnett/snap-sync
-# Copyright (C) 2016-2021 Wes Barnett
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-
-# -------------------------------------------------------------------------
-
-# Takes snapshots of each snapper configuration. It then sends the snapshot to
-# a location on an external drive. After the initial transfer, it does
-# incremental snapshots on later calls. It's important not to delete the
-# snapshot created on your system since that will be used to determine the
-# difference for the next incremental snapshot.
-
-set -o errtrace
-
-version="0.7"
-name="snap-sync"
-
-printf "\nsnap-sync version %s, Copyright (C) 2016-2021 Wes Barnett\n" "$version"
-printf "snap-sync comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the license for more information. \n\n"
-
-# The following line is modified by the Makefile or
-# find_snapper_config script
-SNAPPER_CONFIG=/etc/sysconfig/snapper
-
-donotify=0
-if ! command -v notify-send &>/dev/null; then
-    donotify=1
-fi
-
-doprogress=0
-if ! command -v pv &>/dev/null; then
-    doprogress=1
-fi
-
-error() {
-    printf "==> ERROR: %s\n" "$@"
-    notify_error 'Error' 'Check journal for more information.'
-} >&2
-
-die() {
-    error "$@"
-    exit 1
-}
-
-traperror() {
-    printf "Exited due to error on line %s.\n" "$1"
-    printf "exit status: %s\n" "$2"
-    printf "command: %s\n" "$3"
-    printf "bash line: %s\n" "$4"
-    printf "function name: %s\n" "$5"
-    exit 1
-}
-
-trapkill() {
-    die "Exited due to user intervention."
-}
-
-trap 'traperror ${LINENO} $? "$BASH_COMMAND" $BASH_LINENO "${FUNCNAME[@]}"' ERR
-trap trapkill SIGTERM SIGINT
-
-usage() {
-    cat <<EOF
-$name $version
-Usage: $name [options]
-
-Options:
- -c, --config <config>    snapper configuration to backup
- -d, --description <desc> snapper description
- -h, --help               print this message
- -n, --noconfirm          do not ask for confirmation
- -k, --keepold            keep old incremental snapshots instead of deleting them
-                          after backup is performed
- -p, --port <port>        remote port; used with '--remote'.
- -q, --quiet              do not send notifications; instead print them.
- -r, --remote <address>   ip address of a remote machine to backup to
- --sudo                   use sudo on the remote machine
- -s, --subvolid <subvlid> subvolume id of the mounted BTRFS subvolume to back up to
- -u, --UUID <UUID>        UUID of the mounted BTRFS subvolume to back up to
-
-See 'man snap-sync' for more details.
-EOF
-}
-
-ssh=""
-sudo=0
-while [[ $# -gt 0 ]]; do
-    key="$1"
-    case $key in
-    -d | --description)
-        description="$2"
-        shift 2
-        ;;
-    -c | --config)
-        selected_configs="$2"
-        shift 2
-        ;;
-    -u | --UUID)
-        uuid_cmdline="$2"
-        shift 2
-        ;;
-    -s | --subvolid)
-        subvolid_cmdline="$2"
-        shift 2
-        ;;
-    -k | --keepold)
-        keep="yes"
-        shift
-        ;;
-    -n | --noconfirm)
-        noconfirm="yes"
-        shift
-        ;;
-    -h | --help)
-        usage
-        exit 1
-        ;;
-    -q | --quiet)
-        donotify=1
-        shift
-        ;;
-    -r | --remote)
-        remote=$2
-        shift 2
-        ;;
-    -p | --port)
-        port=$2
-        shift 2
-        ;;
-    --sudo)
-        sudo=1
-        shift
-        ;;
-    *)
-        die "Unknown option: '$key'. Run '$name -h' for valid options."
-        ;;
-    esac
-done
-
-notify() {
-    for u in $(users | tr ' ' '\n' | sort -u); do
-        sudo -u "$u" DISPLAY=:0 \
-            DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(sudo -u "$u" id -u)/bus" \
-            notify-send -a $name "$1" "$2" --icon="dialog-$3"
-    done
-}
-
-notify_info() {
-    if [[ $donotify -eq 0 ]]; then
-        notify "$1" "$2" "information"
-    else
-        printf '%s\n' "$1: $2"
-    fi
-}
-
-notify_error() {
-    if [[ $donotify -eq 0 ]]; then
-        notify "$1" "$2" "error"
-    else
-        printf '%s\n' "$1: $2"
-    fi
-}
-
-[[ $EUID -ne 0 ]] && die "Script must be run as root. See '$name -h' for a description of options"
-! [[ -f $SNAPPER_CONFIG ]] && die "$SNAPPER_CONFIG does not exist."
-
-description=${description:-"latest incremental backup"}
-uuid_cmdline=${uuid_cmdline:-"none"}
-subvolid_cmdline=${subvolid_cmdline:-"5"}
-noconfirm=${noconfirm:-"no"}
-
-if [[ -z $remote ]]; then
-    if ! command -v rsync &>/dev/null; then
-        die "--remote specified but rsync command not found"
-    fi
-fi
-
-if [[ $uuid_cmdline != "none" ]]; then
-    if [[ -z $remote ]]; then
-        notify_info "Backup started" "Starting backups to $uuid_cmdline subvolid=$subvolid_cmdline..."
-    else
-        notify_info "Backup started" "Starting backups to $uuid_cmdline subvolid $subvolid_cmdline at $remote..."
-    fi
-else
-    if [[ -z $remote ]]; then
-        notify_info "Backup started" "Starting backups. Use command line menu to select disk."
-    else
-        notify_info "Backup started" "Starting backups. Use command line menu to select disk on $remote."
-    fi
-fi
-
-if [[ -n $remote ]]; then
-    ssh="ssh $remote"
-    if [[ -n $port ]]; then
-        ssh="$ssh -p $port"
-    fi
-    if [[ $sudo -eq 1 ]]; then
-        ssh="$ssh sudo"
-    fi
-fi
-
-if [[ "$($ssh findmnt -n -v --target / -o FSTYPE)" == "btrfs" ]]; then
-    EXCLUDE_UUID=$($ssh findmnt -n -v -t btrfs --target / -o UUID)
-    TARGETS=$($ssh findmnt -n -v -t btrfs -o UUID,TARGET --list | grep -v "$EXCLUDE_UUID" | awk '{print $2}')
-    UUIDS=$($ssh findmnt -n -v -t btrfs -o UUID,TARGET --list | grep -v "$EXCLUDE_UUID" | awk '{print $1}')
-else
-    TARGETS=$($ssh findmnt -n -v -t btrfs -o TARGET --list)
-    UUIDS=$($ssh findmnt -n -v -t btrfs -o UUID --list)
-fi
-
-declare -a TARGETS_ARRAY
-declare -a UUIDS_ARRAY
-declare -a SUBVOLIDS_ARRAY
-
-i=0
-for x in $TARGETS; do
-    SUBVOLIDS_ARRAY[i]=$($ssh btrfs subvolume show "$x" | awk '/Subvolume ID:/ { print $3 }')
-    TARGETS_ARRAY[i]=$x
-    i=$((i + 1))
-done
-
-i=0
-disk=-1
-disk_count=0
-for x in $UUIDS; do
-    UUIDS_ARRAY[i]=$x
-    if [[ $x == "$uuid_cmdline" && ${SUBVOLIDS_ARRAY[$((i))]} == "$subvolid_cmdline" ]]; then
-        disk=$i
-        disk_count=$((disk_count + 1))
-    fi
-    i=$((i + 1))
-done
-
-if [[ ${#UUIDS_ARRAY[$@]} -eq 0 ]]; then
-    die "No external btrfs subvolumes found to backup to. Run '$name -h' for more options."
-fi
-
-if [[ $disk_count -gt 1 ]]; then
-    printf "Multiple mount points were found with UUID %s and subvolid %s.\n" "$uuid_cmdline" "$subvolid_cmdline"
-    disk="-1"
-fi
-
-if [[ $disk == -1 ]]; then
-    if [[ $disk_count == 0 && $uuid_cmdline != "none" ]]; then
-        error "A device with UUID $uuid_cmdline and subvolid $subvolid_cmdline was not found to be mounted, or it is not a BTRFS device."
-    fi
-    if [[ -z $ssh ]]; then
-        printf "Select a mounted BTRFS device on your local machine to backup to.\nFor more options, exit and run '%s -h'.\n" "$name"
-    else
-        printf "Select a mounted BTRFS device on %s to backup to.\nFor more options, exit and run '%s -h'.\n" "$remote" "$name"
-    fi
-    while [[ $disk -lt 0 || $disk -gt $i ]]; do
-        for x in "${!TARGETS_ARRAY[@]}"; do
-            printf "%4s) %s (uuid=%s, subvolid=%s)\n" "$((x + 1))" "${TARGETS_ARRAY[$x]}" "${UUIDS_ARRAY[$x]}" "${SUBVOLIDS_ARRAY[$x]}"
-        done
-        printf "%4s) Exit\n" "0"
-        read -e -r -p "Enter a number: " disk
-        if ! [[ $disk == ?(-)+([0-9]) ]] || [[ $disk -lt 0 || $disk -gt $i ]]; then
-            printf "\nNo disk selected. Select a disk to continue.\n"
-            disk=-1
-        fi
-    done
-    if [[ $disk == 0 ]]; then
-        exit 0
-    fi
-    disk=$((disk - 1))
-fi
-
-selected_subvolid="${SUBVOLIDS_ARRAY[$((disk))]}"
-selected_uuid="${UUIDS_ARRAY[$((disk))]}"
-selected_mnt="${TARGETS_ARRAY[$((disk))]}"
-printf "\nYou selected the disk with uuid=%s, subvolid=%s.\n" "$selected_uuid" "$selected_subvolid"
-if [[ -z $ssh ]]; then
-    printf "The disk is mounted at '%s'.\n" "$selected_mnt"
-else
-    printf "The disk is mounted at '%s:%s'.\n" "$remote" "$selected_mnt"
-fi
-
-# shellcheck source=/dev/null
-source "$SNAPPER_CONFIG"
-
-if [[ -z $selected_configs ]]; then
-    printf "\nInteractively cycling through all snapper configurations...\n"
-fi
-selected_configs=${selected_configs:-$SNAPPER_CONFIGS}
-
-declare -a BACKUPDIRS_ARRAY
-declare -a MYBACKUPDIR_ARRAY
-declare -a OLD_NUM_ARRAY
-declare -a OLD_SNAP_ARRAY
-declare -a NEW_NUM_ARRAY
-declare -a NEW_SNAP_ARRAY
-declare -a NEW_INFO_ARRAY
-declare -a BACKUPLOC_ARRAY
-declare -a CONT_BACKUP_ARRAY
-
-# Initial configuration of where backup directories are
-i=0
-for x in $selected_configs; do
-
-    if [[ "$(snapper -c "$x" list --disable-used-space -t single | awk '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {cnt++} END {print cnt}')" -gt 1 ]]; then
-        error "More than one snapper entry found with UUID $selected_uuid subvolid $selected_subvolid for configuration $x. Skipping configuration $x."
-        continue
-    fi
-
-    if [[ "$(snapper -c "$x" list --disable-used-space -t single | awk '/'$name' backup in progress/ {cnt++} END {print cnt}')" -gt 0 ]]; then
-        printf "\nNOTE: Previous failed %s backup snapshots found for '%s'.\n" "$name" "$x"
-        if [[ $noconfirm == "yes" ]]; then
-            printf "'noconfirm' option passed. Failed backups will not be deleted.\n"
-        else
-            read -e -r -p "Delete failed backup snapshot(s)? (These local snapshots from failed backups are not used.) [y/N]? " delete_failed
-            while [[ -n $delete_failed && $delete_failed != [Yy]"es" &&
-                $delete_failed != [Yy] && $delete_failed != [Nn]"o" &&
-                $delete_failed != [Nn] ]]; do
-                read -e -r -p "Delete failed backup snapshot(s)? (These local snapshots from failed backups are not used.) [y/N] " delete_failed
-                if [[ -n $delete_failed && $delete_failed != [Yy]"es" &&
-                    $delete_failed != [Yy] && $delete_failed != [Nn]"o" &&
-                    $delete_failed != [Nn] ]]; then
-                    printf "Select 'y' or 'N'.\n"
-                fi
-            done
-            if [[ $delete_failed == [Yy]"es" || $delete_failed == [Yy] ]]; then
-                # explicit split list of snapshots (on whitespace) into multiple arguments
-                # shellcheck disable=SC2046
-                snapper -c "$x" delete $(snapper -c "$x" list --disable-used-space | awk '/'$name' backup in progress/ {print $1}')
-            fi
-        fi
-    fi
-
-    SNAP_SYNC_EXCLUDE=no
-
-    if [[ -f "/etc/snapper/configs/$x" ]]; then
-        # shellcheck source=/dev/null
-        source "/etc/snapper/configs/$x"
-    else
-        die "Selected snapper configuration $x does not exist."
-    fi
-
-    if [[ $SNAP_SYNC_EXCLUDE == "yes" ]]; then
-        continue
-    fi
-
-    printf "\n"
-
-    old_num=$(snapper -c "$x" list --disable-used-space -t single | awk '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {print $1}')
-    old_snap=$SUBVOLUME/.snapshots/$old_num/snapshot
-
-    OLD_NUM_ARRAY[i]=$old_num
-    OLD_SNAP_ARRAY[i]=$old_snap
-
-    if [[ -z $old_num ]]; then
-        printf "No backups have been performed for '%s' on this disk.\n" "$x"
-        read -e -r -p "Enter name of subvolume to store backups, relative to $selected_mnt (to be created if not existing): " mybackupdir
-        printf "This will be the initial backup for snapper configuration '%s' to this disk. This could take awhile.\n" "$x"
-        BACKUPDIR="$selected_mnt/$mybackupdir"
-        $ssh test -d "$BACKUPDIR" || $ssh btrfs subvolume create "$BACKUPDIR"
-    else
-        mybackupdir=$(snapper -c "$x" list --disable-used-space -t single | awk -F"|" '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {print $5}' | awk -F "," '/backupdir/ {print $1}' | awk -F"=" '{print $2}')
-        BACKUPDIR="$selected_mnt/$mybackupdir"
-        $ssh test -d "$BACKUPDIR" || die "%s is not a directory on %s.\n" "$BACKUPDIR" "$selected_uuid"
-    fi
-    BACKUPDIRS_ARRAY[i]="$BACKUPDIR"
-    MYBACKUPDIR_ARRAY[i]="$mybackupdir"
-
-    printf "Creating new local snapshot for '%s' configuration...\n" "$x"
-    new_num=$(snapper -c "$x" create --print-number -d "$name backup in progress")
-    new_snap=$SUBVOLUME/.snapshots/$new_num/snapshot
-    new_info=$SUBVOLUME/.snapshots/$new_num/info.xml
-    sync
-    backup_location=$BACKUPDIR/$x/$new_num/
-    if [[ -z $ssh ]]; then
-        printf "Will backup %s to %s\n" "$new_snap" "$backup_location/snapshot"
-    else
-        printf "Will backup %s to %s\n" "$new_snap" "$remote":"$backup_location/snapshot"
-    fi
-
-    if ($ssh test -d "$backup_location/snapshot"); then
-        printf "WARNING: Backup directory '%s' already exists. This configuration will be skipped!\n" "$backup_location/snapshot"
-        printf "Move or delete destination directory and try backup again.\n"
-    fi
-
-    NEW_NUM_ARRAY[i]="$new_num"
-    NEW_SNAP_ARRAY[i]="$new_snap"
-    NEW_INFO_ARRAY[i]="$new_info"
-    BACKUPLOC_ARRAY[i]="$backup_location"
-
-    cont_backup="K"
-    CONT_BACKUP_ARRAY[i]="yes"
-    if [[ $noconfirm == "yes" ]]; then
-        cont_backup="yes"
-    else
-        while [[ -n $cont_backup && $cont_backup != [Yy]"es" &&
-            $cont_backup != [Yy] && $cont_backup != [Nn]"o" &&
-            $cont_backup != [Nn] ]]; do
-            read -e -r -p "Proceed with backup of '$x' configuration [Y/n]? " cont_backup
-            if [[ -n $cont_backup && $cont_backup != [Yy]"es" &&
-                $cont_backup != [Yy] && $cont_backup != [Nn]"o" &&
-                $cont_backup != [Nn] ]]; then
-                printf "Select 'Y' or 'n'.\n"
-            fi
-        done
-    fi
-
-    if [[ $cont_backup != [Yy]"es" && $cont_backup != [Yy] && -n $cont_backup ]]; then
-        CONT_BACKUP_ARRAY[i]="no"
-        printf "Not backing up '%s' configuration.\n" "$x"
-        snapper -c "$x" delete "$new_num"
-    fi
-
-    i=$((i + 1))
-
-done
-
-# Actual backing up
-printf "\nPerforming backups...\n"
-i=-1
-for x in $selected_configs; do
-
-    i=$((i + 1))
-
-    SNAP_SYNC_EXCLUDE=no
-
-    if [[ -f "/etc/snapper/configs/$x" ]]; then
-        # shellcheck source=/dev/null
-        source "/etc/snapper/configs/$x"
-    else
-        die "Selected snapper configuration $x does not exist."
-    fi
-
-    cont_backup=${CONT_BACKUP_ARRAY[$i]}
-    if [[ $cont_backup == "no" || $SNAP_SYNC_EXCLUDE == "yes" ]]; then
-        notify_info "Backup in progress" "NOTE: Skipping $x configuration."
-        continue
-    fi
-
-    notify_info "Backup in progress" "Backing up $x configuration."
-
-    printf "\n"
-
-    old_num="${OLD_NUM_ARRAY[$i]}"
-    old_snap="${OLD_SNAP_ARRAY[$i]}"
-    BACKUPDIR="${BACKUPDIRS_ARRAY[$i]}"
-    mybackupdir="${MYBACKUPDIR_ARRAY[$i]}"
-    new_num="${NEW_NUM_ARRAY[$i]}"
-    new_snap="${NEW_SNAP_ARRAY[$i]}"
-    new_info="${NEW_INFO_ARRAY[$i]}"
-    backup_location="${BACKUPLOC_ARRAY[$i]}"
-
-    if ($ssh test -d "$backup_location/snapshot"); then
-        printf "ERROR: Backup directory '%s' already exists. Skipping backup of this configuration!\n" "$backup_location/snapshot"
-        continue
-    fi
-
-    $ssh mkdir -p "$backup_location"
-
-    if [[ -z $old_num ]]; then
-        printf "Sending first snapshot for '%s' configuration...\n" "$x"
-        if [[ $doprogress -eq 0 ]]; then
-            btrfs send "$new_snap" | pv | $ssh btrfs receive "$backup_location" &>/dev/null
-        else
-            btrfs send "$new_snap" | $ssh btrfs receive "$backup_location" &>/dev/null
-        fi
-    else
-
-        printf "Sending incremental snapshot for '%s' configuration...\n" "$x"
-        # Sends the difference between the new snapshot and old snapshot to the
-        # backup location. Using the -c flag instead of -p tells it that there
-        # is an identical subvolume to the old snapshot at the receiving
-        # location where it can get its data. This helps speed up the transfer.
-
-        if [[ $doprogress -eq 0 ]]; then
-            btrfs send -c "$old_snap" "$new_snap" | pv | $ssh btrfs receive "$backup_location"
-        else
-            btrfs send -c "$old_snap" "$new_snap" | $ssh btrfs receive "$backup_location"
-        fi
-
-        if [[ $keep == "yes" ]]; then
-            printf "Modifying data for old local snapshot for '%s' configuration...\n" "$x"
-            snapper -v -c "$x" modify -d "old snap-sync snapshot (you may remove)" -u "backupdir=,subvolid=,uuid=" -c "number" "$old_num"
-        else
-            printf "Deleting old snapshot for %s...\n" "$x"
-            snapper -c "$x" delete "$old_num"
-        fi
-
-    fi
-
-    if [[ -z $remote ]]; then
-        cp "$new_info" "$backup_location"
-    else
-        if [[ -z $port ]]; then
-            rsync -avzq "$new_info" "$remote":"$backup_location"
-        else
-            rsync -avzqe "ssh -p $port" "$new_info" "$remote":"$backup_location"
-        fi
-    fi
-
-    # It's important not to change this userdata in the snapshots, since that's how
-    # we find the previous one.
-
-    userdata="backupdir=$mybackupdir, subvolid=$selected_subvolid, uuid=$selected_uuid"
-
-    # Tag new snapshot as the latest
-    printf "Tagging local snapshot as latest backup for '%s' configuration...\n" "$x"
-    snapper -v -c "$x" modify -d "$description" -u "$userdata" "$new_num"
-
-    printf "Backup complete for '%s' configuration.\n" "$x"
-
-done
-
-printf "\nDone!\n"
-
-if [[ $uuid_cmdline != "none" ]]; then
-    notify_info "Finished" "Backups to $uuid_cmdline complete!"
-else
-    notify_info "Finished" "Backups complete!"
-fi
diff --git a/pkgs/by-name/st/stamp/package.nix b/pkgs/by-name/st/stamp/package.nix
index 703f73e3..871d531c 100644
--- a/pkgs/by-name/st/stamp/package.nix
+++ b/pkgs/by-name/st/stamp/package.nix
@@ -1,20 +1,29 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
-  sysLib,
-  findutils,
+  writeShellApplication,
+  # Dependencies
+  coreutils,
   fd,
-  reuse,
   git,
+  reuse,
 }:
-sysLib.writeShellScript {
+writeShellApplication {
   name = "stamp";
-  src = ./stamp.sh;
-  generateCompletions = false;
-  keepPath = false;
+  text = builtins.readFile ./stamp.sh;
+  inheritPath = false;
 
-  dependencies = [
-    findutils
+  runtimeInputs = [
+    coreutils
     fd
-    reuse
     git
+    reuse
   ];
 }
diff --git a/pkgs/by-name/st/stamp/stamp.sh b/pkgs/by-name/st/stamp/stamp.sh
index 0aa6c281..5f17b2aa 100755
--- a/pkgs/by-name/st/stamp/stamp.sh
+++ b/pkgs/by-name/st/stamp/stamp.sh
@@ -1,7 +1,19 @@
 #!/usr/bin/env dash
 
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+die() {
+    echo "Error: $1"
+    exit 1
+}
 
 help() {
     cat <<EOF
diff --git a/pkgs/by-name/tr/tree-sitter-yts/package.nix b/pkgs/by-name/tr/tree-sitter-yts/package.nix
index 3a97530d..62ecf063 100644
--- a/pkgs/by-name/tr/tree-sitter-yts/package.nix
+++ b/pkgs/by-name/tr/tree-sitter-yts/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 # taken from nixpgks: pkgs/development/tools/parsing/tree-sitter/grammar.nix
 {
   yt,
diff --git a/pkgs/by-name/ts/tskm/.envrc b/pkgs/by-name/ts/tskm/.envrc
index d21a17fc..a84d550d 100644
--- a/pkgs/by-name/ts/tskm/.envrc
+++ b/pkgs/by-name/ts/tskm/.envrc
@@ -1,8 +1,18 @@
 #!/usr/bin/env sh
 
-SHELL_COMPLETION_DIR="$(pwd)/target/shell"
-export SHELL_COMPLETION_DIR
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 
 export TSKM_PROJECT_FILE=/home/soispha/repos/nix/config/modules/common/projects.json
 
 use flake
+
+PATH_add ./target/debug
+PATH_add ./target/release
diff --git a/pkgs/by-name/ts/tskm/.gitignore b/pkgs/by-name/ts/tskm/.gitignore
index 2d5df85d..f255eebd 100644
--- a/pkgs/by-name/ts/tskm/.gitignore
+++ b/pkgs/by-name/ts/tskm/.gitignore
@@ -1,2 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 /target
 .direnv
diff --git a/pkgs/by-name/ts/tskm/Cargo.lock b/pkgs/by-name/ts/tskm/Cargo.lock
index e4beb42a..a7395090 100644
--- a/pkgs/by-name/ts/tskm/Cargo.lock
+++ b/pkgs/by-name/ts/tskm/Cargo.lock
@@ -1,18 +1,27 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 version = 4
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 
 [[package]]
 name = "ahash"
-version = "0.8.11"
+version = "0.8.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -37,9 +46,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.18"
+version = "0.6.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -52,62 +61,68 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
 dependencies = [
  "anstyle",
- "once_cell",
- "windows-sys",
+ "once_cell_polyfill",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "arraydeque"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
 
 [[package]]
 name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[package]]
 name = "bitflags"
-version = "2.9.0"
+version = "2.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
 
 [[package]]
 name = "bumpalo"
-version = "3.17.0"
+version = "3.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
 
 [[package]]
 name = "byteorder"
@@ -117,24 +132,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "cc"
-version = "1.2.18"
+version = "1.2.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
+checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
 dependencies = [
  "shlex",
 ]
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "chrono"
-version = "0.4.40"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -147,9 +162,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -157,9 +172,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.35"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -169,18 +184,21 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.47"
+version = "4.5.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
+checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
 dependencies = [
  "clap",
+ "clap_lex",
+ "is_executable",
+ "shlex",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -190,15 +208,15 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
 
 [[package]]
 name = "core-foundation-sys"
@@ -233,7 +251,7 @@ dependencies = [
  "libc",
  "option-ext",
  "redox_users",
- "windows-sys",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -248,6 +266,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
 name = "fallible-iterator"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -261,15 +288,21 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
 
 [[package]]
 name = "flate2"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
 ]
 
 [[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
 name = "form_urlencoded"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -280,20 +313,20 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi 0.11.1+wasi-snapshot-preview1",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
 dependencies = [
  "cfg-if",
  "libc",
@@ -311,12 +344,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "hashbrown"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
 name = "hashlink"
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
+dependencies = [
+ "hashbrown 0.15.4",
 ]
 
 [[package]]
@@ -327,9 +378,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 
 [[package]]
 name = "iana-time-zone"
@@ -357,21 +408,22 @@ dependencies = [
 
 [[package]]
 name = "icu_collections"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
 dependencies = [
  "displaydoc",
+ "potential_utf",
  "yoke",
  "zerofrom",
  "zerovec",
 ]
 
 [[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
 dependencies = [
  "displaydoc",
  "litemap",
@@ -381,30 +433,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
-
-[[package]]
 name = "icu_normalizer"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
 dependencies = [
  "displaydoc",
  "icu_collections",
@@ -412,68 +444,55 @@ dependencies = [
  "icu_properties",
  "icu_provider",
  "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
  "zerovec",
 ]
 
 [[package]]
 name = "icu_normalizer_data"
-version = "1.5.1"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
 
 [[package]]
 name = "icu_properties"
-version = "1.5.1"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
 dependencies = [
  "displaydoc",
  "icu_collections",
- "icu_locid_transform",
+ "icu_locale_core",
  "icu_properties_data",
  "icu_provider",
- "tinystr",
+ "potential_utf",
+ "zerotrie",
  "zerovec",
 ]
 
 [[package]]
 name = "icu_properties_data"
-version = "1.5.1"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
 
 [[package]]
 name = "icu_provider"
-version = "1.5.0"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
 dependencies = [
  "displaydoc",
- "icu_locid",
- "icu_provider_macros",
+ "icu_locale_core",
  "stable_deref_trait",
  "tinystr",
  "writeable",
  "yoke",
  "zerofrom",
+ "zerotrie",
  "zerovec",
 ]
 
 [[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "idna"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -486,9 +505,9 @@ dependencies = [
 
 [[package]]
 name = "idna_adapter"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
 dependencies = [
  "icu_normalizer",
  "icu_properties",
@@ -502,7 +521,16 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
 dependencies = [
  "hermit-abi",
  "libc",
- "windows-sys",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "is_executable"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2"
+dependencies = [
+ "winapi",
 ]
 
 [[package]]
@@ -529,15 +557,15 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 
 [[package]]
 name = "libredox"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
 dependencies = [
  "bitflags",
  "libc",
@@ -555,9 +583,9 @@ dependencies = [
 
 [[package]]
 name = "litemap"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
 [[package]]
 name = "log"
@@ -566,25 +594,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
-name = "lz4_flex"
-version = "0.11.3"
+name = "md5"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
-dependencies = [
- "twox-hash",
-]
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
 ]
@@ -605,6 +630,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
 name = "option-ext"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -623,10 +654,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
 [[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -642,9 +682,9 @@ dependencies = [
 
 [[package]]
 name = "r-efi"
-version = "5.2.0"
+version = "5.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 
 [[package]]
 name = "redox_users"
@@ -652,7 +692,7 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
 dependencies = [
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
  "libredox",
  "thiserror",
 ]
@@ -666,16 +706,16 @@ dependencies = [
  "bitflags",
  "fallible-iterator",
  "fallible-streaming-iterator",
- "hashlink",
+ "hashlink 0.9.1",
  "libsqlite3-sys",
  "smallvec",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.20"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
 
 [[package]]
 name = "ryu"
@@ -732,9 +772,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "smallvec"
-version = "1.15.0"
+version = "1.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
 
 [[package]]
 name = "stable_deref_trait"
@@ -743,18 +783,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
 [[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
 name = "stderrlog"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b"
 dependencies = [
- "chrono",
  "is-terminal",
  "log",
  "termcolor",
@@ -788,9 +821,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.100"
+version = "2.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -799,9 +832,9 @@ dependencies = [
 
 [[package]]
 name = "synstructure"
-version = "0.13.1"
+version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -859,19 +892,18 @@ dependencies = [
 
 [[package]]
 name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
 dependencies = [
  "cfg-if",
- "once_cell",
 ]
 
 [[package]]
 name = "tinystr"
-version = "0.7.6"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
 dependencies = [
  "displaydoc",
  "zerovec",
@@ -886,23 +918,14 @@ dependencies = [
  "clap_complete",
  "dirs",
  "log",
- "lz4_flex",
+ "md5",
  "serde",
  "serde_json",
  "stderrlog",
  "taskchampion",
  "url",
  "walkdir",
-]
-
-[[package]]
-name = "twox-hash"
-version = "1.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
-dependencies = [
- "cfg-if",
- "static_assertions",
+ "yaml-rust2",
 ]
 
 [[package]]
@@ -924,12 +947,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
 name = "utf8_iter"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -943,12 +960,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.16.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
 dependencies = [
- "getrandom 0.3.2",
+ "getrandom 0.3.3",
+ "js-sys",
  "serde",
+ "wasm-bindgen",
 ]
 
 [[package]]
@@ -975,9 +994,9 @@ dependencies = [
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "wasi"
@@ -1047,19 +1066,41 @@ dependencies = [
 ]
 
 [[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
 name = "winapi-util"
 version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
 name = "windows-core"
-version = "0.61.0"
+version = "0.61.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
 dependencies = [
  "windows-implement",
  "windows-interface",
@@ -1092,24 +1133,24 @@ dependencies = [
 
 [[package]]
 name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
 [[package]]
 name = "windows-result"
-version = "0.3.2"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
 dependencies = [
  "windows-link",
 ]
 
 [[package]]
 name = "windows-strings"
-version = "0.4.0"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
 dependencies = [
  "windows-link",
 ]
@@ -1120,7 +1161,16 @@ version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.2",
 ]
 
 [[package]]
@@ -1129,14 +1179,30 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
 ]
 
 [[package]]
@@ -1146,48 +1212,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
 name = "wit-bindgen-rt"
 version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1197,22 +1311,27 @@ dependencies = [
 ]
 
 [[package]]
-name = "write16"
-version = "1.0.0"
+name = "writeable"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
 
 [[package]]
-name = "writeable"
-version = "0.5.5"
+name = "yaml-rust2"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7"
+dependencies = [
+ "arraydeque",
+ "encoding_rs",
+ "hashlink 0.10.0",
+]
 
 [[package]]
 name = "yoke"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
 dependencies = [
  "serde",
  "stable_deref_trait",
@@ -1222,9 +1341,9 @@ dependencies = [
 
 [[package]]
 name = "yoke-derive"
-version = "0.7.5"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1234,18 +1353,18 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.7.35"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1274,10 +1393,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
 name = "zerovec"
-version = "0.10.4"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
 dependencies = [
  "yoke",
  "zerofrom",
@@ -1286,9 +1416,9 @@ dependencies = [
 
 [[package]]
 name = "zerovec-derive"
-version = "0.10.3"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/pkgs/by-name/ts/tskm/Cargo.toml b/pkgs/by-name/ts/tskm/Cargo.toml
index 39feea0c..2655254b 100644
--- a/pkgs/by-name/ts/tskm/Cargo.toml
+++ b/pkgs/by-name/ts/tskm/Cargo.toml
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 [package]
 name = "tskm"
 version = "0.1.0"
@@ -6,17 +16,19 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.35", features = ["derive"] }
-dirs = "6.0.0"
-log = "0.4.27"
-lz4_flex = "0.11.3"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-stderrlog = "0.6.0"
+anyhow = { version = "1.0.98", default-features = false }
+clap = { version = "4.5.40", features = [ "derive", "std", "color", "help", "usage", "error-context", "suggestions", ], default-features = false }
+clap_complete = { version = "4.5.54", features = ["unstable-dynamic"] }
+dirs = { version = "6.0.0", default-features = false }
+log = { version = "0.4.27", default-features = false }
+serde = { version = "1.0.219", features = ["derive"], default-features = false }
+serde_json = { version = "1.0.140", default-features = false }
+stderrlog = { version = "0.6.0", default-features = false }
 taskchampion = { version = "2.0.3", default-features = false }
-url = { version = "2.5.4", features = ["serde"] }
-walkdir = "2.5.0"
+url = { version = "2.5.4", features = ["serde"], default-features = false }
+walkdir = { version = "2.5.0", default-features = false }
+md5 = { version = "0.7.0", default-features = false }
+yaml-rust2 = "0.10.3"
 
 [profile.release]
 lto = true
@@ -77,15 +89,3 @@ perf = { level = "warn", priority = -1 }
 pedantic = { level = "warn", priority = -1 }
 missing_panics_doc = "allow"
 missing_errors_doc = "allow"
-
-[build-dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.35", features = ["derive"] }
-clap_complete = "4.5.47"
-dirs = "6.0.0"
-log = "0.4.27"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-taskchampion = { version = "2.0.3", default-features = false }
-url = "2.5.4"
-walkdir = "2.5.0"
diff --git a/pkgs/by-name/ts/tskm/build.rs b/pkgs/by-name/ts/tskm/build.rs
deleted file mode 100644
index e3b60bb9..00000000
--- a/pkgs/by-name/ts/tskm/build.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use anyhow::{Context, Result};
-use clap::{CommandFactory, ValueEnum};
-use clap_complete::generate_to;
-use clap_complete::Shell;
-
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-
-use crate::cli::CliArgs;
-
-pub mod task {
-    include!("src/task/mod.rs");
-}
-pub mod state {
-    include!("src/state.rs");
-}
-
-pub mod interface {
-    pub mod input {
-        include!("src/interface/input/mod.rs");
-    }
-    pub mod project {
-        include!("src/interface/project/mod.rs");
-    }
-}
-
-pub mod cli {
-    include!("src/cli.rs");
-}
-
-fn main() -> Result<()> {
-    let outdir = match env::var_os("SHELL_COMPLETION_DIR") {
-        None => return Ok(()),
-        Some(outdir) => outdir,
-    };
-
-    if !PathBuf::from(&outdir).exists() {
-        fs::create_dir_all(&outdir)?;
-    }
-
-    let mut cmd = CliArgs::command();
-
-    for &shell in Shell::value_variants() {
-        let path = generate_to(shell, &mut cmd, "tskm", &outdir).with_context(|| {
-            format!("Failed to output shell completion for {shell} to {outdir:?}")
-        })?;
-        println!("cargo:warning=completion file for {shell} is generated at: {path:?}");
-    }
-
-    Ok(())
-}
diff --git a/pkgs/by-name/ts/tskm/flake.lock b/pkgs/by-name/ts/tskm/flake.lock
index eb3616c8..95f58ef9 100644
--- a/pkgs/by-name/ts/tskm/flake.lock
+++ b/pkgs/by-name/ts/tskm/flake.lock
@@ -2,11 +2,11 @@
   "nodes": {
     "nixpkgs": {
       "locked": {
-        "lastModified": 1744096231,
-        "narHash": "sha256-kUfx3FKU1Etnua3EaKvpeuXs7zoFiAcli1gBwkPvGSs=",
+        "lastModified": 1750994206,
+        "narHash": "sha256-3u6rEbIX9CN/5A5/mc3u0wIO1geZ0EhjvPBXmRDHqWM=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "b2b0718004cc9a5bca610326de0a82e6ea75920b",
+        "rev": "80d50fc87924c2a0d346372d242c27973cf8cdbf",
         "type": "github"
       },
       "original": {
diff --git a/pkgs/by-name/ts/tskm/flake.lock.license b/pkgs/by-name/ts/tskm/flake.lock.license
new file mode 100644
index 00000000..eae6a84c
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/flake.lock.license
@@ -0,0 +1,9 @@
+nixos-config - My current NixOS configuration
+
+Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+SPDX-License-Identifier: GPL-3.0-or-later
+
+This file is part of my nixos-config.
+
+You should have received a copy of the License along with this program.
+If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
diff --git a/pkgs/by-name/ts/tskm/flake.nix b/pkgs/by-name/ts/tskm/flake.nix
index 5a5f628b..583d4923 100644
--- a/pkgs/by-name/ts/tskm/flake.nix
+++ b/pkgs/by-name/ts/tskm/flake.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   description = "This is the core interface to the system-integrated task management";
 
diff --git a/pkgs/by-name/ts/tskm/package.nix b/pkgs/by-name/ts/tskm/package.nix
index 3d320772..ad10865f 100644
--- a/pkgs/by-name/ts/tskm/package.nix
+++ b/pkgs/by-name/ts/tskm/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   rustPlatform,
   installShellFiles,
@@ -7,7 +16,6 @@
   taskwarrior3,
   git,
   rofi,
-  firefox,
   sqlite,
 }:
 rustPlatform.buildRustPackage (finalAttrs: {
@@ -19,15 +27,10 @@ rustPlatform.buildRustPackage (finalAttrs: {
     lockFile = ./Cargo.lock;
   };
 
-  env = {
-    SHELL_COMPLETION_DIR = "./shell";
-  };
-
   buildInputs = [
     taskwarrior3
     git
     rofi
-    firefox
     sqlite
   ];
 
@@ -38,9 +41,9 @@ rustPlatform.buildRustPackage (finalAttrs: {
 
   postInstall = ''
     installShellCompletion --cmd tskm \
-      --bash ./shell/tskm.bash \
-      --fish ./shell/tskm.fish \
-      --zsh ./shell/_tskm
+      --bash <(COMPLETE=bash $out/bin/tskm) \
+      --fish <(COMPLETE=fish $out/bin/tskm) \
+      --zsh <(COMPLETE=zsh $out/bin/tskm)
 
     # NOTE: We cannot clear the path, because we need access to the $EDITOR. <2025-04-04>
     wrapProgram $out/bin/tskm \
diff --git a/pkgs/by-name/ts/tskm/src/browser/mod.rs b/pkgs/by-name/ts/tskm/src/browser/mod.rs
new file mode 100644
index 00000000..29abfcbd
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/src/browser/mod.rs
@@ -0,0 +1,172 @@
+use std::{
+    env,
+    io::Write,
+    os::unix::net::UnixStream,
+    path::PathBuf,
+    process::{self, ExitStatus},
+};
+
+use anyhow::{Context, Result};
+use log::{error, info};
+use serde_json::json;
+use url::Url;
+
+use crate::{state::State, task};
+
+#[allow(clippy::too_many_lines)]
+pub fn open_in_browser(
+    selected_project: &task::Project,
+    state: &mut State,
+    urls: Option<Vec<Url>>,
+) -> Result<()> {
+    let old_project: Option<task::Project> =
+        task::Project::get_current().context("Failed to get currently active project")?;
+    let old_task: Option<task::Task> =
+        task::Task::get_current(state).context("Failed to get currently active task")?;
+
+    selected_project.activate().with_context(|| {
+        format!(
+            "Failed to active project: '{}'",
+            selected_project.to_project_display()
+        )
+    })?;
+
+    let tracking_task = {
+        let all_tasks = selected_project.get_tasks(state).with_context(|| {
+            format!(
+                "Failed to get assoctiated tasks for project: '{}'",
+                selected_project.to_project_display()
+            )
+        })?;
+
+        let tracking_task = all_tasks.into_iter().find(|t| {
+            let maybe_desc = t.description(state);
+            if let Ok(desc) = maybe_desc {
+                desc == "tracking"
+            } else {
+                error!(
+                    "Getting task description returned error: {}",
+                    maybe_desc.expect_err("We already check for Ok")
+                );
+                false
+            }
+        });
+
+        if let Some(task) = tracking_task {
+            info!(
+                "Starting task {} -> tracking",
+                selected_project.to_project_display()
+            );
+            task.start(state)
+                .with_context(|| format!("Failed to start task {task}"))?;
+        }
+        tracking_task
+    };
+
+    let status = {
+        // #!/bin/sh
+        // # initial idea: Florian Bruhin (The-Compiler)
+        // # author: Thore Bödecker (foxxx0)
+        //
+        // _url="$1"
+        // _qb_version='1.0.4'
+        // _proto_version=1
+        // _ipc_socket="${XDG_RUNTIME_DIR}/qutebrowser/ipc-$(printf '%s' "$USER" | md5sum | cut -d' ' -f1)"
+        // _qute_bin="/usr/bin/qutebrowser"
+        //
+        // printf '{"args": ["%s"], "target_arg": null, "version": "%s", "protocol_version": %d, "cwd": "%s"}\n' \
+        //        "${_url}" \
+        //        "${_qb_version}" \
+        //        "${_proto_version}" \
+        //        "${PWD}" | socat -lf /dev/null - UNIX-CONNECT:"${_ipc_socket}" || "$_qute_bin" "$@" &
+
+        let ipc_socket_path = PathBuf::from(
+            env::var("XDG_RUNTIME_DIR").context("Failed to access XDG_RUNTIME_DIR var")?,
+        )
+        .join("qutebrowser")
+        .join(selected_project.to_project_display())
+        .join(format!("ipc-{:x}", {
+            let user_name = env::var("USER").context("Failed to get USER var")?;
+            let base_dir = env::var("XDG_DATA_HOME").context("Failed to get XDG_DATA_HOME")?;
+
+            md5::compute(
+                format!(
+                    "{user_name}-{}",
+                    PathBuf::from(base_dir)
+                        .join("qutebrowser")
+                        .join(selected_project.to_project_display())
+                        .display()
+                )
+                .as_bytes(),
+            )
+        }));
+
+        if ipc_socket_path.exists() {
+            let mut stream = UnixStream::connect(ipc_socket_path)?;
+
+            let real_url = if let Some(urls) = urls {
+                urls.into_iter().map(|url| url.to_string()).collect()
+            } else {
+                // Always add a new tab, so that qutebrowser is marked as “urgent”.
+                vec!["qute://start".to_owned()]
+            };
+
+            stream.write_all(
+                json! {
+                    {
+                        "args": real_url,
+                        "target_arg": null,
+                        "version": "1.0.4",
+                        "protocol_version": 1,
+                        "cwd": "/"
+                    }
+                }
+                .to_string()
+                .as_bytes(),
+            )?;
+            stream.write_all(b"\n")?;
+
+            ExitStatus::default()
+        } else {
+            let args = if let Some(urls) = urls {
+                urls.iter().map(|url| url.to_string()).collect()
+            } else {
+                vec![]
+            };
+
+            process::Command::new(format!(
+                "qutebrowser-{}",
+                selected_project.to_project_display()
+            ))
+            .args(args)
+            .status()
+            .context("Failed to start qutebrowser")?
+        }
+    };
+
+    if !status.success() {
+        error!("Qutebrowser run exited with error.");
+    }
+
+    if let Some(task) = tracking_task {
+        task.stop(state)
+            .with_context(|| format!("Failed to stop task {task}"))?;
+    }
+    if let Some(task) = old_task {
+        task.start(state)
+            .with_context(|| format!("Failed to start task {task}"))?;
+    }
+
+    if let Some(project) = old_project {
+        project.activate().with_context(|| {
+            format!(
+                "Failed to activate project {}",
+                project.to_project_display()
+            )
+        })?;
+    } else {
+        task::Project::clear().context("Failed to clear currently focused project")?;
+    }
+
+    Ok(())
+}
diff --git a/pkgs/by-name/ts/tskm/src/cli.rs b/pkgs/by-name/ts/tskm/src/cli.rs
index 1c72b3c2..23d9545f 100644
--- a/pkgs/by-name/ts/tskm/src/cli.rs
+++ b/pkgs/by-name/ts/tskm/src/cli.rs
@@ -1,13 +1,26 @@
-use std::path::PathBuf;
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{ffi::OsStr, path::PathBuf};
 
 use anyhow::{bail, Result};
-use clap::{ArgAction, Parser, Subcommand};
+use clap::{builder::StyledStr, ArgAction, Parser, Subcommand, ValueEnum};
+use clap_complete::{ArgValueCompleter, CompletionCandidate};
 use url::Url;
 
 use crate::{
-    interface::{input::Input, project::ProjectName},
-    state::State,
-    task,
+    interface::{
+        input::{Input, Tag},
+        project::ProjectName,
+    },
+    state, task,
 };
 
 #[derive(Parser, Debug)]
@@ -15,10 +28,12 @@ use crate::{
 /// This is the core interface to the system-integrated task management
 ///
 /// `tskm` effectively combines multiple applications together:
-/// - `taskwarrior` projects are raised connected to `firefox` profiles, making it possible to “open”
+/// - `taskwarrior` projects are connected to `qutebrowser` profiles, making it possible to “open”
 ///   a project.
+///
 /// - Every `taskwarrior` project has a determined `neorg` path, so that extra information for a
 ///   `project` can be stored in this `norg` file.
+///
 /// - `tskm` can track inputs for you. These are URLs with optional tags which you can that
 ///   “review” to open tasks based on them.
 pub struct CliArgs {
@@ -54,7 +69,7 @@ pub enum Command {
         command: NeorgCommand,
     },
 
-    /// Interface with the Firefox profile of each project.
+    /// Interface with the Qutebrowser profile of each project.
     Open {
         #[command(subcommand)]
         command: OpenCommand,
@@ -79,14 +94,14 @@ pub enum NeorgCommand {
     /// Open the `neorg` project associated with id of the task.
     Task {
         /// The working set id of the task
-        #[arg(value_parser = task_from_working_set_id)]
-        id: task::Task,
+        #[arg(value_name = "ID", value_parser = task_from_working_set_id, add = ArgValueCompleter::new(complete_task_id))]
+        task: task::Task,
     },
 }
 
 fn task_from_working_set_id(id: &str) -> Result<task::Task> {
     let id: usize = id.parse()?;
-    let mut state = State::new_ro()?;
+    let mut state = state::State::new_ro()?;
 
     let Some(task) = task::Task::from_working_set(id, &mut state)? else {
         bail!("Working set id '{id}' is not valid!")
@@ -96,58 +111,222 @@ fn task_from_working_set_id(id: &str) -> Result<task::Task> {
 
 #[derive(Subcommand, Debug)]
 pub enum OpenCommand {
-    /// Open each project's Firefox profile consecutively, that was opened since the last review.
+    /// Open each project's Qutebrowser profile consecutively, that was opened since the last review.
     ///
     /// This allows you to remove stale opened tabs and to commit open tabs to the `inputs`.
-    Review,
+    Review {
+        /// Review all projects, if they contain tabs
+        #[arg(short, long, default_value_t)]
+        non_empty: bool,
+    },
 
-    /// Opens Firefox with either the supplied project or the currently active project profile.
+    /// Opens Qutebrowser with either the supplied project or the currently active project profile.
     Project {
         /// The project to open.
-        #[arg(value_parser = task::Project::from_project_string)]
+        #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
         project: task::Project,
 
-        /// The URL to open.
-        url: Option<Url>,
+        /// The URLs to open.
+        urls: Option<Vec<Url>>,
     },
 
-    /// Open a selected project in it's Firefox profile.
+    /// Open a selected project in it's Qutebrowser profile.
     ///
     /// This will use rofi's dmenu mode to select one project from the list of all registered
     /// projects.
     Select {
-        /// The URL to open.
-        url: Option<Url>,
+        /// The URLs to open.
+        urls: Option<Vec<Url>>,
     },
 
     /// List all open tabs in the project.
     ListTabs {
-        /// The project to open.
-        #[arg(value_parser = task::Project::from_project_string)]
-        project: Option<task::Project>,
+        /// The projects to open.
+        #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
+        projects: Option<Vec<task::Project>>,
+
+        /// Only show the tabs, that are in this mode
+        #[arg(short, long, conflicts_with = "projects")]
+        mode: Option<ListMode>,
     },
 }
 
+#[derive(Clone, Copy, ValueEnum, Debug)]
+pub enum ListMode {
+    // The tab contains no tabs.
+    Empty,
+
+    // The tab contains tabs.
+    NonEmpty,
+}
+
 #[derive(Subcommand, Debug)]
 pub enum InputCommand {
     /// Add URLs as inputs to be categorized.
     Add { inputs: Vec<Input> },
     /// Remove URLs
-    Remove { inputs: Vec<Input> },
+    Remove {
+        #[arg(add = ArgValueCompleter::new(complete_input_url))]
+        inputs: Vec<Input>,
+    },
 
     /// Add all URLs in the file as inputs to be categorized.
     ///
     /// This expects each line to contain one URL.
-    File { file: PathBuf },
+    File {
+        /// The file to read from.
+        file: PathBuf,
+
+        /// Additional tags to apply to every read URL in the file.
+        #[arg(add = ArgValueCompleter::new(complete_tag))]
+        tags: Vec<Tag>,
+    },
 
     /// Like 'review', but for the inputs that have previously been added.
     /// It takes a project in which to open the URLs.
     Review {
         /// Opens all the URLs in this project.
-        #[arg(value_parser = task::Project::from_project_string)]
+        #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
         project: task::Project,
     },
 
     /// List all the previously added inputs.
-    List,
+    List {
+        /// Only list the inputs that have all the specified tags
+        #[arg(add = ArgValueCompleter::new(complete_tag))]
+        tags: Vec<Tag>,
+    },
+
+    /// Show all the available tags.
+    Tags {},
+}
+
+fn complete_task_id(current: &OsStr) -> Vec<CompletionCandidate> {
+    fn format_task(
+        task: task::Task,
+        current: &str,
+        state: &mut state::State,
+    ) -> Option<CompletionCandidate> {
+        let id = {
+            let Ok(base) = task.working_set_id(state) else {
+                return None;
+            };
+            base.to_string()
+        };
+
+        if !id.starts_with(current) {
+            return None;
+        }
+
+        let description = {
+            let Ok(base) = task.description(state) else {
+                return None;
+            };
+            StyledStr::from(base)
+        };
+
+        Some(CompletionCandidate::new(id).help(Some(description)))
+    }
+
+    let mut output = vec![];
+
+    let Some(current) = current.to_str() else {
+        return output;
+    };
+
+    let Ok(mut state) = state::State::new_ro() else {
+        return output;
+    };
+
+    let Ok(pending) = state.replica().pending_tasks() else {
+        return output;
+    };
+
+    let Ok(current_project) = task::Project::get_current() else {
+        return output;
+    };
+
+    if let Some(current_project) = current_project {
+        for t in pending {
+            let task = task::Task::from(&t);
+            if let Ok(project) = task.project(&mut state) {
+                if project == current_project {
+                    if let Some(out) = format_task(task, current, &mut state) {
+                        output.push(out);
+                    }
+                }
+            }
+        }
+    } else {
+        for t in pending {
+            let task = task::Task::from(&t);
+            if let Some(out) = format_task(task, current, &mut state) {
+                output.push(out);
+            }
+        }
+    }
+
+    output
+}
+fn complete_project(current: &OsStr) -> Vec<CompletionCandidate> {
+    let mut output = vec![];
+
+    let Some(current) = current.to_str() else {
+        return output;
+    };
+
+    let Ok(all) = task::Project::all() else {
+        return output;
+    };
+
+    for a in all {
+        if a.to_project_display().starts_with(current) {
+            output.push(CompletionCandidate::new(a.to_project_display()));
+        }
+    }
+
+    output
+}
+fn complete_input_url(current: &OsStr) -> Vec<CompletionCandidate> {
+    let mut output = vec![];
+
+    let Some(current) = current.to_str() else {
+        return output;
+    };
+
+    let Ok(all) = Input::all() else {
+        return output;
+    };
+
+    for a in all {
+        if a.to_string().starts_with(current) {
+            output.push(CompletionCandidate::new(a.to_string()));
+        }
+    }
+
+    output
+}
+fn complete_tag(current: &OsStr) -> Vec<CompletionCandidate> {
+    let mut output = vec![];
+
+    let Some(current) = current.to_str() else {
+        return output;
+    };
+
+    if !current.starts_with('+') {
+        output.push(CompletionCandidate::new(format!("+{current}")));
+    }
+
+    output
+}
+
+#[cfg(test)]
+mod test {
+    use clap::CommandFactory;
+
+    use super::CliArgs;
+    #[test]
+    fn verify_cli() {
+        CliArgs::command().debug_assert();
+    }
 }
diff --git a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
index 0ff0e56e..76eea6dc 100644
--- a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
@@ -1,23 +1,33 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{
-    fs, process,
+    collections::{HashMap, HashSet},
+    fs,
     str::FromStr,
-    thread::{self, sleep},
-    time::Duration,
 };
 
 use anyhow::{Context, Result};
-use log::{error, info};
+use log::info;
 
-use crate::cli::InputCommand;
+use crate::{browser::open_in_browser, cli::InputCommand, state::State};
 
-use super::Input;
+use super::{Input, Tag};
 
 /// # Errors
 /// When command handling fails.
 ///
 /// # Panics
 /// When internal assertions fail.
-pub fn handle(command: InputCommand) -> Result<()> {
+#[allow(clippy::too_many_lines)]
+pub fn handle(command: InputCommand, state: &mut State) -> Result<()> {
     match command {
         InputCommand::Add { inputs } => {
             for input in inputs {
@@ -33,47 +43,37 @@ pub fn handle(command: InputCommand) -> Result<()> {
                 })?;
             }
         }
-        InputCommand::File { file } => {
-            let file = fs::read_to_string(file)?;
-            for line in file.lines() {
-                let input = Input::from_str(line)?;
+        InputCommand::File { file, tags } => {
+            let file = fs::read_to_string(&file)
+                .with_context(|| format!("Failed to read input file '{}'", file.display()))?;
+
+            let mut tag_set = HashSet::with_capacity(tags.len());
+            for tag in tags {
+                tag_set.insert(tag);
+            }
+
+            for line in file.lines().map(str::trim) {
+                if line.is_empty() {
+                    continue;
+                }
+
+                let mut input = Input::from_str(line)?;
+                input.tags = input.tags.union(&tag_set).cloned().collect();
+
                 input.commit().with_context(|| {
                     format!("Failed to add input ('{input}') to the input storage.")
                 })?;
             }
         }
         InputCommand::Review { project } => {
-            let project = project.to_project_display();
-
-            let local_project = project.clone();
-            let handle = thread::spawn(move || {
-                // We assume that the project is not yet open.
-                let mut firefox = process::Command::new("firefox")
-                    .args(["-P", local_project.as_str(), "about:newtab"])
-                    .spawn()?;
-
-                Ok::<_, anyhow::Error>(firefox.wait()?)
-            });
-            // Give Firefox some time to start.
-            info!("Waiting on firefox to start");
-            sleep(Duration::from_secs(4));
-
-            let project_str = project.as_str();
             'outer: for all in Input::all()?.chunks(100) {
                 info!("Starting review for the first hundred URLs.");
 
-                for input in all {
-                    info!("-> '{input}'");
-                    let status = process::Command::new("firefox")
-                        .args(["-P", project_str, input.url().to_string().as_str()])
-                        .status()?;
-
-                    if status.success() {
-                        input.remove()?;
-                    } else {
-                        error!("Adding `{input}` to Firefox failed!");
-                    }
-                }
+                open_in_browser(
+                    &project,
+                    state,
+                    Some(all.iter().map(|f| f.url.clone()).collect()),
+                )?;
 
                 {
                     use std::io::{stdin, stdout, Write};
@@ -98,15 +98,51 @@ pub fn handle(command: InputCommand) -> Result<()> {
                     }
                 }
             }
-
-            info!("Waiting for firefox to stop");
-            handle.join().expect("Should be joinable")?;
         }
-        InputCommand::List => {
-            for url in Input::all()? {
+        InputCommand::List { tags } => {
+            let mut tag_set = HashSet::with_capacity(tags.len());
+            for tag in tags {
+                tag_set.insert(tag);
+            }
+
+            for url in Input::all()?
+                .iter()
+                .filter(|input| tag_set.is_subset(&input.tags))
+            {
                 println!("{url}");
             }
         }
+        InputCommand::Tags {} => {
+            let mut without_tags = 0;
+            let mut tag_set: HashMap<Tag, u64> = HashMap::new();
+
+            for input in Input::all()? {
+                if input.tags.is_empty() {
+                    without_tags += 1;
+                }
+
+                for tag in input.tags {
+                    if let Some(number) = tag_set.get_mut(&tag) {
+                        *number += 1;
+                    } else {
+                        tag_set.insert(tag, 1);
+                    }
+                }
+            }
+
+            let mut tags: Vec<(Tag, u64)> = tag_set.into_iter().collect();
+            tags.sort_by_key(|(_, number)| *number);
+            tags.reverse();
+
+            for (tag, number) in tags {
+                println!("{tag} {number}");
+            }
+
+            if without_tags != 0 {
+                println!();
+                println!("Witohut tags: {without_tags}");
+            }
+        }
     }
     Ok(())
 }
diff --git a/pkgs/by-name/ts/tskm/src/interface/input/mod.rs b/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
index 9ece7a3a..1d1d67f4 100644
--- a/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
@@ -1,7 +1,17 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{
-    collections::HashSet,
+    collections::{HashMap, HashSet},
     fmt::Display,
-    fs::{self, read_to_string, File},
+    fs,
     io::Write,
     path::PathBuf,
     process::Command,
@@ -16,38 +26,47 @@ pub mod handle;
 pub use handle::handle;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct NoWhitespaceString(String);
+pub struct Tag(String);
 
-impl NoWhitespaceString {
-    /// # Panics
-    /// If the input contains whitespace.
-    #[must_use]
-    pub fn new(input: String) -> Self {
-        if input.contains(' ') {
-            panic!("Your input '{input}' contains whitespace. I did not expect that.")
+impl Tag {
+    pub fn new(input: &str) -> Result<Self> {
+        Self::from_str(input)
+    }
+}
+
+impl FromStr for Tag {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+        if let Some(tag) = s.strip_prefix('+') {
+            if tag.contains(' ') {
+                bail!("Your tag '{s}' should not whitespace.")
+            }
+
+            Ok(Self(tag.to_owned()))
         } else {
-            Self(input)
+            bail!("Your tag '{s}' does not start with the required '+'");
         }
     }
 }
 
-impl Display for NoWhitespaceString {
+impl Display for Tag {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.0.fmt(f)
+        write!(f, "+{}", self.0)
     }
 }
 
-impl NoWhitespaceString {
+impl Tag {
     #[must_use]
     pub fn as_str(&self) -> &str {
         &self.0
     }
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Input {
     url: Url,
-    tags: HashSet<NoWhitespaceString>,
+    tags: HashSet<Tag>,
 }
 
 impl FromStr for Input {
@@ -61,13 +80,7 @@ impl FromStr for Input {
                 tags: {
                     tags.trim()
                         .split(' ')
-                        .map(|tag| {
-                            if let Some(tag) = tag.strip_prefix('+') {
-                                Ok(NoWhitespaceString::new(tag.to_owned()))
-                            } else {
-                                bail!("Your tag '{tag}' does not start with the required '+'");
-                            }
-                        })
+                        .map(Tag::new)
                         .collect::<Result<_, _>>()?
                 },
             })
@@ -91,13 +104,9 @@ impl Display for Input {
                 self.url,
                 self.tags
                     .iter()
-                    .fold(String::new(), |mut acc, tag| {
-                        acc.push('+');
-                        acc.push_str(tag.as_str());
-                        acc.push(' ');
-                        acc
-                    })
-                    .trim()
+                    .map(ToString::to_string)
+                    .collect::<Vec<_>>()
+                    .join(" ")
             )
         }
     }
@@ -113,7 +122,10 @@ impl Input {
     fn url_path(url: &Url) -> Result<PathBuf> {
         let base_path = Self::base_path();
 
-        let url_path = base_path.join(url.to_string());
+        let url_path = base_path
+            .join(url.scheme())
+            .join(url.host_str().unwrap_or("<No Host>"))
+            .join(url.path().trim_matches('/'));
         fs::create_dir_all(&url_path)
             .with_context(|| format!("Failed to open file: '{}'", url_path.display()))?;
 
@@ -132,17 +144,12 @@ impl Input {
     pub fn commit(&self) -> Result<()> {
         let url_path = Self::url_path(&self.url)?;
 
-        let url_content = {
-            if url_path.exists() {
-                read_to_string(&url_path)?
-            } else {
-                String::new()
-            }
-        };
-
-        let mut file = File::create(&url_path)
+        let mut file = fs::OpenOptions::new()
+            .create(true)
+            .append(true)
+            .open(&url_path)
             .with_context(|| format!("Failed to open file: '{}'", url_path.display()))?;
-        writeln!(file, "{url_content}{self}")?;
+        writeln!(file, "{self}")?;
 
         Self::git_commit(&format!("Add new url: '{self}'"))?;
 
@@ -173,28 +180,7 @@ impl Input {
         Ok(())
     }
 
-    /// Commit your changes
-    fn git_commit(message: &str) -> Result<()> {
-        let status = Command::new("git")
-            .args(["add", "."])
-            .current_dir(Self::base_path())
-            .status()?;
-        if !status.success() {
-            bail!("Git add . failed!");
-        }
-
-        let status = Command::new("git")
-            .args(["commit", "--message", message, "--no-gpg-sign"])
-            .current_dir(Self::base_path())
-            .status()?;
-        if !status.success() {
-            bail!("Git commit failed!");
-        }
-
-        Ok(())
-    }
-
-    /// Get all previously [`Self::commit`]ed inputs.
+    /// Get all previously [committed][`Self::commit`] inputs.
     ///
     /// # Errors
     /// When IO handling fails.
@@ -217,41 +203,58 @@ impl Input {
                 continue;
             }
 
-            let url_value_file = entry
-                .path()
-                .to_str()
-                .expect("All of these should be URLs and thus valid strings");
-            assert!(url_value_file.ends_with("/url_value"));
-
-            let url = {
-                let base = url_value_file
-                    .strip_prefix(&format!("{}/", Self::base_path().display()))
-                    .expect("This will exist");
-
-                let (proto, path) = base.split_once(':').expect("This will countain a :");
-
-                let path = path.strip_suffix("/url_value").expect("Will exist");
-
-                Url::from_str(&format!("{proto}:/{path}"))
-                    .expect("This was a URL, it should still be one")
-            };
-            let tags = {
-                let url_values = read_to_string(PathBuf::from(url_value_file))?;
-                url_values
-                    .lines()
-                    .map(|line| {
-                        let input = Self::from_str(line)?;
-                        Ok::<_, anyhow::Error>(input.tags)
-                    })
-                    .collect::<Result<Vec<HashSet<NoWhitespaceString>>, _>>()?
-                    .into_iter()
-                    .flatten()
-                    .collect()
-            };
-
-            output.push(Self { url, tags });
+            let url_value_file = entry.path();
+            assert!(url_value_file.ends_with("url_value"));
+
+            let url_values = fs::read_to_string(PathBuf::from(url_value_file))?;
+
+            let mut inputs: HashMap<Url, Self> = HashMap::new();
+            for input in url_values
+                .lines()
+                .map(Self::from_str)
+                .collect::<Result<Vec<Self>, _>>()?
+            {
+                if let Some(found) = inputs.get_mut(&input.url) {
+                    found.tags = found.tags.union(&input.tags).cloned().collect();
+                } else {
+                    assert_eq!(inputs.insert(input.url.clone(), input), None);
+                }
+            }
+
+            output.extend(inputs.drain().map(|(_, value)| value));
         }
 
         Ok(output)
     }
+
+    /// Commit your changes
+    fn git_commit(message: &str) -> Result<()> {
+        if !Self::base_path().join(".git").exists() {
+            let status = Command::new("git")
+                .args(["init"])
+                .current_dir(Self::base_path())
+                .status()?;
+            if !status.success() {
+                bail!("Git init failed!");
+            }
+        }
+
+        let status = Command::new("git")
+            .args(["add", "."])
+            .current_dir(Self::base_path())
+            .status()?;
+        if !status.success() {
+            bail!("Git add . failed!");
+        }
+
+        let status = Command::new("git")
+            .args(["commit", "--message", message, "--no-gpg-sign"])
+            .current_dir(Self::base_path())
+            .status()?;
+        if !status.success() {
+            bail!("Git commit failed!");
+        }
+
+        Ok(())
+    }
 }
diff --git a/pkgs/by-name/ts/tskm/src/interface/mod.rs b/pkgs/by-name/ts/tskm/src/interface/mod.rs
index 1a0d934c..513ca317 100644
--- a/pkgs/by-name/ts/tskm/src/interface/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 pub mod input;
 pub mod neorg;
 pub mod open;
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
index d904b12e..ea3a89ae 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{
     env,
     fs::{self, read_to_string, File, OpenOptions},
@@ -11,8 +21,8 @@ use crate::{cli::NeorgCommand, state::State};
 
 pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
     match command {
-        NeorgCommand::Task { id } => {
-            let project = id.project(state)?;
+        NeorgCommand::Task { task } => {
+            let project = task.project(state)?;
             let base = dirs::data_local_dir()
                 .expect("This should exists")
                 .join("tskm/notes");
@@ -30,15 +40,17 @@ pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
                     String::new()
                 };
 
-                if !contents.contains(format!("% {}", id.uuid()).as_str()) {
+                if !contents.contains(format!("% {}", task.uuid()).as_str()) {
                     let mut options = OpenOptions::new();
                     options.append(true).create(false);
 
                     let mut file = options.open(&path)?;
-                    file.write_all(format!("* TITLE (% {})", id.uuid()).as_bytes())
-                        .with_context(|| {
-                            format!("Failed to write task uuid to file: '{}'", path.display())
-                        })?;
+                    file.write_all(
+                        format!("* {} (% {})", task.description(state)?, task.uuid()).as_bytes(),
+                    )
+                    .with_context(|| {
+                        format!("Failed to write task uuid to file: '{}'", path.display())
+                    })?;
                     file.flush()
                         .with_context(|| format!("Failed to flush file: '{}'", path.display()))?;
                 }
@@ -49,7 +61,7 @@ pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
                 .args([
                     path.to_str().expect("Should be a utf-8 str"),
                     "-c",
-                    format!("/% {}", id.uuid()).as_str(),
+                    format!("/% {}", task.uuid()).as_str(),
                 ])
                 .status()?;
             if !status.success() {
@@ -80,7 +92,7 @@ pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
             }
 
             {
-                id.mark_neorg_data(state)?;
+                task.mark_neorg_data(state)?;
             }
         }
     }
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
index 51d58ab3..6bed1e39 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
@@ -1,8 +1,18 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::path::PathBuf;
 
 use anyhow::Result;
 
-use crate::task::{run_task, Project};
+use crate::task::{Project, run_task};
 
 pub mod handle;
 pub use handle::handle;
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
index 4d7341b2..0cf60b41 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -1,44 +1,73 @@
-use std::{
-    fs,
-    net::{IpAddr, Ipv4Addr},
-    path::PathBuf,
-    process,
-};
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::str::FromStr;
 
 use anyhow::{bail, Context, Result};
-use log::{error, info, warn};
+use log::{error, info};
 use url::Url;
 
-use crate::{cli::OpenCommand, rofi, state::State, task};
+use crate::{browser::open_in_browser, cli::OpenCommand, rofi, state::State, task};
 
+fn is_empty(project: &task::Project) -> Result<bool> {
+    let tabs = get_tabs(project)?;
+
+    if tabs.is_empty() {
+        Ok(true)
+    } else if tabs.len() > 1 {
+        Ok(false)
+    } else {
+        let url = &tabs[0].1;
+
+        Ok(url == &Url::from_str("qute://start/").expect("Hardcoded"))
+    }
+}
+
+#[allow(clippy::too_many_lines)]
 pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
     match command {
-        OpenCommand::Review => {
+        OpenCommand::Review { non_empty } => {
             for project in task::Project::all().context("Failed to get all project files")? {
-                if project.is_touched() {
-                    info!("Reviewing project: '{}'", project.to_project_display());
+                let is_empty = is_empty(project)?;
+
+                if project.is_touched() || (non_empty && !is_empty) {
+                    info!(
+                        "Reviewing project: '{}' ({})",
+                        project.to_project_display(),
+                        if is_empty { "is empty" } else { "is not empty" }
+                    );
                     open_in_browser(project, state, None).with_context(|| {
                         format!(
-                            "Failed to open project ('{}') in Firefox",
-                            project.to_project_display()
-                        )
-                    })?;
-                    project.untouch().with_context(|| {
-                        format!(
-                            "Failed to untouch project ('{}')",
+                            "Failed to open project ('{}') in qutebrowser",
                             project.to_project_display()
                         )
                     })?;
+
+                    if project.is_touched() {
+                        project.untouch().with_context(|| {
+                            format!(
+                                "Failed to untouch project ('{}')",
+                                project.to_project_display()
+                            )
+                        })?;
+                    }
                 }
             }
         }
-        OpenCommand::Project { project, url } => {
+        OpenCommand::Project { project, urls } => {
             project.touch().context("Failed to touch project")?;
-            open_in_browser(&project, state, url).with_context(|| {
+            open_in_browser(&project, state, urls).with_context(|| {
                 format!("Failed to open project: {}", project.to_project_display())
             })?;
         }
-        OpenCommand::Select { url } => {
+        OpenCommand::Select { urls } => {
             let selected_project: task::Project = task::Project::from_project_string(
                 &rofi::select(
                     task::Project::all()
@@ -56,177 +85,106 @@ pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
                 .touch()
                 .context("Failed to touch project")?;
 
-            open_in_browser(&selected_project, state, url).context("Failed to open project")?;
+            open_in_browser(&selected_project, state, urls).context("Failed to open project")?;
         }
-        OpenCommand::ListTabs { project } => {
-            let project = if let Some(p) = project {
-                p
-            } else if let Some(p) =
-                task::Project::get_current().context("Failed to get currently focused project")?
-            {
-                p
-            } else {
-                bail!("You need to either supply a project or have a project active!");
+        OpenCommand::ListTabs { projects, mode } => {
+            let projects = {
+                if let Some(p) = projects {
+                    p
+                } else if mode.is_some() {
+                    task::Project::all()
+                        .context("Failed to get all projects")?
+                        .to_owned()
+                } else if let Some(p) = task::Project::get_current()
+                    .context("Failed to get currently focused project")?
+                {
+                    vec![p]
+                } else {
+                    bail!("You need to either select projects or pass --mode");
+                }
             };
 
-            let session_store = project.get_sessionstore().with_context(|| {
-                format!(
-                    "Failed to get session store for project: '{}'",
-                    project.to_project_display()
-                )
-            })?;
-
-            let selected = session_store
-                .windows
-                .iter()
-                .map(|w| w.selected)
-                .collect::<Vec<_>>();
-
-            let tabs = session_store
-                .windows
-                .iter()
-                .flat_map(|window| window.tabs.iter())
-                .map(|tab| tab.entries.get(tab.index - 1).expect("This should be Some"))
-                .collect::<Vec<_>>();
-
-            for (index, entry) in tabs.iter().enumerate() {
-                let index = index + 1;
-                let is_selected = {
-                    if selected.contains(&index) {
-                        "🔻 "
-                    } else {
-                        "   "
+            for project in &projects {
+                if let Some(mode) = mode {
+                    match mode {
+                        crate::cli::ListMode::Empty => {
+                            if !is_empty(project)? {
+                                continue;
+                            }
+
+                            // We do not need to print, tabs they are always empty.
+                            if projects.len() > 1 {
+                                println!("/* {} */", project.to_project_display());
+                            }
+                            continue;
+                        }
+                        crate::cli::ListMode::NonEmpty => {
+                            if is_empty(project)? {
+                                continue;
+                            }
+                        }
                     }
-                };
-                println!("{}{}", is_selected, entry.url);
-            }
-        }
-    }
-    Ok(())
-}
-
-fn open_in_browser(
-    selected_project: &task::Project,
-    state: &mut State,
-    url: Option<Url>,
-) -> Result<()> {
-    let old_project: Option<task::Project> =
-        task::Project::get_current().context("Failed to get currently active project")?;
-    let old_task: Option<task::Task> =
-        task::Task::get_current(state).context("Failed to get currently active task")?;
-
-    selected_project.activate().with_context(|| {
-        format!(
-            "Failed to active project: '{}'",
-            selected_project.to_project_display()
-        )
-    })?;
-
-    let tracking_task = {
-        let all_tasks = selected_project.get_tasks(state).with_context(|| {
-            format!(
-                "Failed to get assoctiated tasks for project: '{}'",
-                selected_project.to_project_display()
-            )
-        })?;
-
-        let tracking_task = all_tasks.into_iter().find(|t| {
-            let maybe_desc = t.description(state);
-            if let Ok(desc) = maybe_desc {
-                desc == "tracking"
-            } else {
-                error!(
-                    "Getting task description returned error: {}",
-                    maybe_desc.expect_err("We already check for Ok")
-                );
-                false
-            }
-        });
-
-        if let Some(task) = tracking_task {
-            info!(
-                "Starting task {} -> tracking",
-                selected_project.to_project_display()
-            );
-            task.start(state)
-                .with_context(|| format!("Failed to start task {task}"))?;
-        }
-        tracking_task
-    };
-
-    let status = {
-        let mut args = vec!["-P".to_owned(), selected_project.to_project_display()];
-        if let Some(url) = url {
-            args.push(url.to_string());
-        } else {
-            let lock_file = dirs::home_dir()
-                .expect("Exists")
-                .join(".mozilla/firefox")
-                .join(selected_project.to_project_display())
-                .join("lock");
-
-            if lock_file.exists() {
-                let (ip, pid): (IpAddr, u32) = {
-                    let link = fs::read_link(&lock_file).with_context(|| {
-                        format!("Failed to readlink lock at '{}'", lock_file.display())
-                    })?;
+                }
 
-                    let (ip, pid) = link
-                        .to_str()
-                        .expect("Should work")
-                        .split_once(':')
-                        .expect("The split works");
+                if projects.len() > 1 {
+                    println!("/* {} */", project.to_project_display());
+                }
 
-                    (
-                        ip.parse().expect("Should be a valid ip address"),
-                        pid.parse().expect("Should be a valid pid"),
-                    )
+                let tabs = match get_tabs(project) {
+                    Ok(ok) => ok,
+                    Err(err) => {
+                        if projects.len() > 1 {
+                            error!(
+                                "While trying to get the sessionstore for {}: {:?}",
+                                project.to_project_display(),
+                                err
+                            );
+                            continue;
+                        }
+
+                        return Err(err).with_context(|| {
+                            format!(
+                                "While trying to get the sessionstore for {}",
+                                project.to_project_display()
+                            )
+                        });
+                    }
                 };
 
-                if ip != Ipv4Addr::new(127, 0, 0, 2) {
-                    warn!("Your ip is weird..");
-                }
-
-                if PathBuf::from("/proc").join(pid.to_string()).exists() {
-                    // Another Firefox instance has already been started for this project
-                    // Add a buffer URL to force Firefox to open it in the already open instance
-                    args.push("about:newtab".to_owned());
-                } else {
-                    // This project does not yet have another Firefox instance
-                    // We do not need to add anything to the arguments, Firefox will open a new
-                    // instance.
+                for (active, url) in tabs {
+                    let is_selected = {
+                        if active {
+                            "🔻 "
+                        } else {
+                            "   "
+                        }
+                    };
+                    println!("{is_selected}{url}");
                 }
-            } else {
-                // There is no lock file and thus no instance already open.
             }
-        };
-
-        process::Command::new("firefox")
-            .args(args)
-            .status()
-            .context("Failed to start firefox")?
-    };
-
-    if !status.success() {
-        error!("Firefox run exited with error.");
+        }
     }
 
-    if let Some(task) = tracking_task {
-        task.stop(state)
-            .with_context(|| format!("Failed to stop task {task}"))?;
-    }
-    if let Some(task) = old_task {
-        task.start(state)
-            .with_context(|| format!("Failed to start task {task}"))?;
-    }
+    Ok(())
+}
 
-    if let Some(project) = old_project {
-        project.activate().with_context(|| {
-            format!("Failed to active project {}", project.to_project_display())
-        })?;
-    } else {
-        task::Project::clear().context("Failed to clear currently focused project")?;
-    }
+fn get_tabs(project: &task::Project) -> Result<Vec<(bool, Url)>> {
+    let session_store = project.get_sessionstore()?;
 
-    Ok(())
+    let tabs = session_store
+        .windows
+        .iter()
+        .flat_map(|window| window.tabs.iter())
+        .filter_map(|tab| {
+            tab.history
+                .iter()
+                .find(|hist| hist.active)
+                .map(|hist| (tab.active, hist))
+        })
+        .collect::<Vec<_>>();
+
+    Ok(tabs
+        .into_iter()
+        .map(|(active, hist)| (active, hist.url.clone()))
+        .collect())
 }
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
index 2dc75957..e302c7d1 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
@@ -1,10 +1,19 @@
-use std::{collections::HashMap, fs::File, io};
-
-use anyhow::{Context, Result};
-use lz4_flex::decompress_size_prepended;
-use serde::Deserialize;
-use serde_json::Value;
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::{fs::File, io::Read, str::FromStr};
+
+use anyhow::{anyhow, Context, Result};
+use taskchampion::chrono::NaiveDateTime;
 use url::Url;
+use yaml_rust2::Yaml;
 
 use crate::task::Project;
 
@@ -13,94 +22,149 @@ pub use handle::handle;
 
 impl Project {
     pub(super) fn get_sessionstore(&self) -> Result<SessionStore> {
-        let path = dirs::home_dir()
-            .expect("Will exist")
-            .join(".mozilla/firefox")
+        let path = dirs::data_local_dir()
+            .context("Failed to get data dir")?
+            .join("qutebrowser")
             .join(self.to_project_display())
-            .join("sessionstore-backups/recovery.jsonlz4");
-        let file = decompress_mozlz4(
-            File::open(&path)
-                .with_context(|| format!("Failed to open path '{}'", path.display()))?,
-        )
-        .with_context(|| format!("Failed to decompress file as mozlzh '{}'", path.display()))?;
-
-        let contents: SessionStore = serde_json::from_str(&file).with_context(|| {
-            format!(
-                "Failed to deserialize file ('{}') as session store.",
-                path.display()
-            )
-        })?;
-        Ok(contents)
-    }
-}
+            .join("data/sessions/default.yml");
 
-fn decompress_mozlz4<P: io::Read>(mut file: P) -> Result<String> {
-    const MOZLZ4_MAGIC_NUMBER: &[u8] = b"mozLz40\0";
+        let mut file = File::open(&path)
+            .with_context(|| format!("Failed to open path '{}'", path.display()))?;
 
-    let mut buf = [0u8; 8];
-    file.read_exact(&mut buf)
-        .context("Failed to read the mozlz40 header.")?;
+        let mut yaml_str = String::new();
+        file.read_to_string(&mut yaml_str)
+            .context("Failed to read _autosave.yml path")?;
+        let yaml = yaml_rust2::YamlLoader::load_from_str(&yaml_str)?;
 
-    assert_eq!(buf, MOZLZ4_MAGIC_NUMBER);
+        let store = qute_store_from_yaml(&yaml).context("Failed to read yaml store")?;
 
-    let mut buf = vec![];
-    file.read_to_end(&mut buf).context("Failed to read file")?;
-
-    let uncompressed = decompress_size_prepended(&buf).context("Failed to decompress file")?;
+        Ok(store)
+    }
+}
 
-    Ok(String::from_utf8(uncompressed).expect("This should be valid json and thus utf8"))
+fn qute_store_from_yaml(yaml: &[Yaml]) -> Result<SessionStore> {
+    assert_eq!(yaml.len(), 1);
+    let doc = &yaml[0];
+
+    let hash = doc.as_hash().context("Invalid yaml")?;
+    let windows = hash
+        .get(&Yaml::String("windows".to_owned()))
+        .ok_or(anyhow!("Missing windows"))?
+        .as_vec()
+        .ok_or(anyhow!("Windows not vector"))?;
+
+    Ok(SessionStore {
+        windows: windows
+            .iter()
+            .map(|window| {
+                let hash = window.as_hash().ok_or(anyhow!("Windows not hashmap"))?;
+
+                Ok::<_, anyhow::Error>(Window {
+                    geometry: hash
+                        .get(&Yaml::String("geometry".to_owned()))
+                        .ok_or(anyhow!("Missing window geometry"))?
+                        .as_str()
+                        .ok_or(anyhow!("geometry not string"))?
+                        .to_owned(),
+                    tabs: hash
+                        .get(&Yaml::String("tabs".to_owned()))
+                        .ok_or(anyhow!("Missing window tabs"))?
+                        .as_vec()
+                        .ok_or(anyhow!("Tabs not vec"))?
+                        .iter()
+                        .map(|tab| {
+                            let hash = tab.as_hash().ok_or(anyhow!("Tab not hashmap"))?;
+
+                            Ok::<_, anyhow::Error>(Tab {
+                                history: hash
+                                    .get(&Yaml::String("history".to_owned()))
+                                    .ok_or(anyhow!("Missing tab history"))?
+                                    .as_vec()
+                                    .ok_or(anyhow!("tab history not vec"))?
+                                    .iter()
+                                    .map(|history| {
+                                        let hash = history
+                                            .as_hash()
+                                            .ok_or(anyhow!("Tab history not hashmap"))?;
+
+                                        Ok::<_, anyhow::Error>(TabHistory {
+                                            active: hash
+                                                .get(&Yaml::String("active".to_owned()))
+                                                .unwrap_or(&Yaml::Boolean(false))
+                                                .as_bool()
+                                                .ok_or(anyhow!("tab history active not bool"))?,
+                                            last_visited: NaiveDateTime::from_str(
+                                                hash.get(&Yaml::String("last_visited".to_owned()))
+                                                    .ok_or(anyhow!(
+                                                        "Missing tab history last_visited"
+                                                    ))?
+                                                    .as_str()
+                                                    .ok_or(anyhow!(
+                                                        "tab history last_visited not string"
+                                                    ))?,
+                                            )
+                                            .context("Failed to parse last_visited")?,
+                                            pinned: hash
+                                                .get(&Yaml::String("pinned".to_owned()))
+                                                .ok_or(anyhow!("Missing tab history pinned"))?
+                                                .as_bool()
+                                                .ok_or(anyhow!("tab history pinned not bool"))?,
+                                            title: hash
+                                                .get(&Yaml::String("title".to_owned()))
+                                                .ok_or(anyhow!("Missing tab history title"))?
+                                                .as_str()
+                                                .ok_or(anyhow!("tab history title not string"))?
+                                                .to_owned(),
+                                            url: Url::parse(
+                                                hash.get(&Yaml::String("url".to_owned()))
+                                                    .ok_or(anyhow!("Missing tab history url"))?
+                                                    .as_str()
+                                                    .ok_or(anyhow!("tab history url not string"))?,
+                                            )
+                                            .context("Failed to parse url")?,
+                                            zoom: hash
+                                                .get(&Yaml::String("zoom".to_owned()))
+                                                .unwrap_or(&Yaml::Real("1.0".to_owned()))
+                                                .as_f64()
+                                                .ok_or(anyhow!("tab history zoom not 64"))?,
+                                        })
+                                    })
+                                    .collect::<Result<Vec<_>, _>>()?,
+                                active: hash
+                                    .get(&Yaml::String("active".to_owned()))
+                                    .unwrap_or(&Yaml::Boolean(false))
+                                    .as_bool()
+                                    .ok_or(anyhow!("active not bool"))?,
+                            })
+                        })
+                        .collect::<Result<Vec<_>, _>>()?,
+                })
+            })
+            .collect::<Result<Vec<_>, _>>()?,
+    })
 }
 
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
 pub struct SessionStore {
     pub windows: Vec<Window>,
 }
-
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
 pub struct Window {
+    pub geometry: String,
     pub tabs: Vec<Tab>,
-    pub selected: usize,
 }
-
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
 pub struct Tab {
-    pub entries: Vec<TabEntry>,
-    #[serde(rename = "lastAccessed")]
-    pub last_accessed: u64,
-    pub hidden: bool,
-    #[serde(rename = "searchMode")]
-    pub search_mode: Option<Value>,
-    #[serde(rename = "userContextId")]
-    pub user_context_id: u32,
-    pub attributes: TabAttributes,
-    #[serde(rename = "extData")]
-    pub ext_data: Option<HashMap<String, Value>>,
-    pub index: usize,
-    #[serde(rename = "requestedIndex")]
-    pub requested_index: Option<u32>,
-    pub image: Option<Url>,
+    pub history: Vec<TabHistory>,
+    pub active: bool,
 }
-
-#[derive(Deserialize, Debug)]
-pub struct TabEntry {
-    pub url: Url,
+#[derive(Debug)]
+pub struct TabHistory {
+    pub active: bool,
+    pub last_visited: NaiveDateTime,
+    pub pinned: bool,
+    // pub scroll-pos:
     pub title: String,
-    #[serde(rename = "cacheKey")]
-    pub cache_key: u32,
-    #[serde(rename = "ID")]
-    pub id: u32,
-    #[serde(rename = "docshellUUID")]
-    pub docshell_uuid: Value,
-    #[serde(rename = "resultPrincipalURI")]
-    pub result_principal_uri: Option<Url>,
-    #[serde(rename = "hasUserInteraction")]
-    pub has_user_interaction: bool,
-    #[serde(rename = "triggeringPrincipal_base64")]
-    pub triggering_principal_base64: Value,
-    #[serde(rename = "docIdentifier")]
-    pub doc_identifier: u32,
-    pub persist: bool,
+    pub url: Url,
+    pub zoom: f64,
 }
-
-#[derive(Deserialize, Debug, Clone, Copy)]
-pub struct TabAttributes {}
diff --git a/pkgs/by-name/ts/tskm/src/interface/project/handle.rs b/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
index 2b01f5d1..6d44b340 100644
--- a/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
@@ -1,6 +1,16 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{env, fs::File, io::Write};
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result, anyhow};
 use log::trace;
 
 use crate::{cli::ProjectCommand, task};
@@ -60,10 +70,12 @@ pub fn handle(command: ProjectCommand) -> Result<()> {
 
                     let new_definition = ProjectDefinition::default();
 
-                    assert!(definition
-                        .subprojects
-                        .insert(segment.clone(), new_definition)
-                        .is_none());
+                    assert!(
+                        definition
+                            .subprojects
+                            .insert(segment.clone(), new_definition)
+                            .is_none()
+                    );
 
                     definition = definition
                         .subprojects
diff --git a/pkgs/by-name/ts/tskm/src/interface/project/mod.rs b/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
index 62069746..8a7fa1b0 100644
--- a/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::collections::HashMap;
 
 use anyhow::Result;
diff --git a/pkgs/by-name/ts/tskm/src/main.rs b/pkgs/by-name/ts/tskm/src/main.rs
index f4416c6d..e6113111 100644
--- a/pkgs/by-name/ts/tskm/src/main.rs
+++ b/pkgs/by-name/ts/tskm/src/main.rs
@@ -1,47 +1,32 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use anyhow::Result;
-use clap::Parser;
-use state::State;
+use clap::{CommandFactory, Parser};
 
-use crate::interface::{input, neorg, open, project};
+use crate::{
+    cli::{CliArgs, Command},
+    interface::{input, neorg, open, project},
+    state::State,
+};
 
+pub mod browser;
 pub mod cli;
 pub mod interface;
 pub mod rofi;
 pub mod state;
 pub mod task;
 
-use crate::cli::{CliArgs, Command};
-
 fn main() -> Result<(), anyhow::Error> {
-    // TODO: Support these completions for the respective types <2025-04-04>
-    //
-    // ID_GENERATION_FUNCTION
-    // ```sh
-    //  context="$(task _get rc.context)"
-    //  if [ "$context" ]; then
-    //      filter="project:$context"
-    //  else
-    //      filter="0-10000"
-    //  fi
-    //  tasks="$(task "$filter" _ids)"
-    //
-    //  if [ "$tasks" ]; then
-    //      echo "$tasks" | xargs task _zshids | awk -F: -v q="'" '{gsub(/'\''/, q "\\" q q ); print $1 ":" q $2 q}'
-    //  fi
-    // ```
-    //
-    // ARGUMENTS:
-    //     ID | *([0-9]) := [[%ID_GENERATION_FUNCTION]]
-    //                             The function displays all possible IDs of the eligible tasks.
-    //
-    //     WS := %ALL_WORKSPACES
-    //                             All possible workspaces.
-    //
-    //     P := %ALL_PROJECTS_PIPE
-    //                             The possible project.
-    //
-    //     F := [[fd . --max-depth 3]]
-    //                             A URL-Input file to use as source.
+    clap_complete::CompleteEnv::with_factory(CliArgs::command).complete();
+
     let args = CliArgs::parse();
 
     stderrlog::new()
@@ -50,14 +35,13 @@ fn main() -> Result<(), anyhow::Error> {
         .show_module_names(true)
         .color(stderrlog::ColorChoice::Auto)
         .verbosity(usize::from(args.verbosity))
-        .timestamp(stderrlog::Timestamp::Off)
         .init()
         .expect("Let's just hope that this does not panic");
 
     let mut state = State::new_rw()?;
 
     match args.command {
-        Command::Inputs { command } => input::handle(command)?,
+        Command::Inputs { command } => input::handle(command, &mut state)?,
         Command::Neorg { command } => neorg::handle(command, &mut state)?,
         Command::Open { command } => open::handle(command, &mut state)?,
         Command::Projects { command } => project::handle(command)?,
diff --git a/pkgs/by-name/ts/tskm/src/rofi/mod.rs b/pkgs/by-name/ts/tskm/src/rofi/mod.rs
index a0591b7f..37c2eafa 100644
--- a/pkgs/by-name/ts/tskm/src/rofi/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/rofi/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{
     io::Write,
     process::{Command, Stdio},
diff --git a/pkgs/by-name/ts/tskm/src/state.rs b/pkgs/by-name/ts/tskm/src/state.rs
index 175a7f03..ae71764e 100644
--- a/pkgs/by-name/ts/tskm/src/state.rs
+++ b/pkgs/by-name/ts/tskm/src/state.rs
@@ -1,7 +1,17 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::path::PathBuf;
 
 use anyhow::Result;
-use taskchampion::{storage::AccessMode, Replica, StorageConfig};
+use taskchampion::{Replica, StorageConfig, storage::AccessMode};
 
 pub struct State {
     replica: Replica,
diff --git a/pkgs/by-name/ts/tskm/src/task/mod.rs b/pkgs/by-name/ts/tskm/src/task/mod.rs
index 03a12faa..9c671273 100644
--- a/pkgs/by-name/ts/tskm/src/task/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/task/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 use std::{
     fmt::Display,
     fs::{self, read_to_string, File},
@@ -66,6 +76,13 @@ impl Task {
     pub fn uuid(&self) -> &taskchampion::Uuid {
         &self.uuid
     }
+    pub fn working_set_id(&self, state: &mut State) -> Result<usize> {
+        Ok(state
+            .replica()
+            .working_set()?
+            .by_uuid(self.uuid)
+            .expect("The task should be in the working set"))
+    }
 
     fn as_task(&self, state: &mut State) -> Result<taskchampion::Task> {
         Ok(state
@@ -121,7 +138,7 @@ impl Task {
                 .expect("Every task should have a project")
                 .to_owned()
         };
-        let project = Project::from_project_string(output.as_str())
+        let project = Project::from_project_string(output.as_str().trim())
             .expect("This comes from tw, it should be valid");
         Ok(project)
     }
@@ -338,5 +355,13 @@ pub(crate) fn run_task(args: &[&str]) -> Result<String> {
     trace!("Output (stdout): '{}'", stdout.trim());
     trace!("Output (stderr): '{}'", stderr.trim());
 
+    if !output.status.success() {
+        bail!(
+            "Command `task {}` failed with status: {}",
+            args.join(" "),
+            output.status
+        );
+    }
+
     Ok(stdout.trim().to_owned())
 }
diff --git a/pkgs/by-name/ts/tskm/update.sh b/pkgs/by-name/ts/tskm/update.sh
index 9268caf2..8e36e13e 100755
--- a/pkgs/by-name/ts/tskm/update.sh
+++ b/pkgs/by-name/ts/tskm/update.sh
@@ -1,3 +1,14 @@
 #!/bin/sh
 
-cargo update && cargo upgrade
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[ "$1" = "upgrade" ] && cargo upgrade
+cargo update
diff --git a/pkgs/by-name/up/update-sys/package.nix b/pkgs/by-name/up/update-sys/package.nix
deleted file mode 100644
index 8777f82d..00000000
--- a/pkgs/by-name/up/update-sys/package.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  sysLib,
-  git,
-  nixos-rebuild,
-  sudo,
-  openssh,
-  coreutils,
-  mktemp,
-  gnugrep,
-  gnused,
-  systemd,
-}:
-sysLib.writeShellScript {
-  name = "update-sys";
-  src = ./update-sys.sh;
-  generateCompletions = true;
-  keepPath = false;
-  dependencies = [
-    git
-    nixos-rebuild
-    sudo
-    openssh
-    coreutils
-    mktemp
-    gnugrep
-    gnused
-    systemd
-  ];
-}
diff --git a/pkgs/by-name/up/update-sys/update-sys.sh b/pkgs/by-name/up/update-sys/update-sys.sh
deleted file mode 100755
index d28247f6..00000000
--- a/pkgs/by-name/up/update-sys/update-sys.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env dash
-
-# shellcheck source=/dev/null
-SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH
-
-help() {
-    cat <<EOF
-This is a NixOS System flake update manager.
-
-USAGE:
-    $NAME [--branch <branchname>] [--help]
-
-OPTIONS:
-    --branch | -b  BRANCHNAME
-                                select a branch to update from.
-
-    --mode   | -m  MODE
-                                select a mode to update with
-
-    --help   | -h
-                                output this help.
-ARGUMENTS:
-    BRANCHNAME := [[ git branch --list --format '%(refname:short)' ]]
-                                The name of the branch to deploy the config from
-
-    MODE := switch|boot|test|build|dry-build|dry-activate|edit|repl|build-vm|build-vm-with-bootloader
-                                See the 'nixos-rebuild' manpage for more information about these modes.
-EOF
-    exit "$1"
-}
-default_branch=$(mktmp)
-BRANCH=""
-
-while [ "$#" -gt 0 ]; do
-    case "$1" in
-    "--help" | "-h")
-        help 0
-        ;;
-    "--branch" | "-b")
-        if [ -n "$2" ]; then
-            BRANCH="$2"
-        else
-            error "$1 requires an argument"
-            help 1
-        fi
-        shift 2
-        ;;
-    "--mode" | "-m")
-        if [ -n "$2" ]; then
-            MODE="$2"
-        else
-            error "$1 requires an argument"
-            help 1
-        fi
-        shift 2
-        ;;
-    *)
-        error "the option $1 does not exist!"
-        help 1
-        ;;
-    esac
-done
-
-cd /etc/nixos || die "No /etc/nixos"
-msg "Starting system update..."
-git remote update origin --prune >/dev/null 2>&1
-if ! [ "$BRANCH" = "" ]; then
-    git switch "$BRANCH" >/dev/null 2>&1 && msg2 "Switched to branch '$BRANCH'"
-fi
-msg2 "Updating git repository..."
-git pull --rebase
-
-git remote show origin | grep 'HEAD' | cut -d':' -f2 | sed -e 's/^ *//g' -e 's/ *$//g' >"$default_branch" &
-
-msg2 "Updating system..."
-if [ -n "$MODE" ]; then
-    nixos-rebuild "$MODE"
-else
-    nixos-rebuild switch
-fi
-
-git switch "$(cat "$default_branch")" >/dev/null 2>&1 && msg2 "Switched to branch '$(cat "$default_branch")'"
-msg "Finished Update!"
-
-# vim: ft=sh
diff --git a/pkgs/by-name/ya/yambar-modules/.envrc b/pkgs/by-name/ya/yambar-modules/.envrc
deleted file mode 100644
index 2f9f1a81..00000000
--- a/pkgs/by-name/ya/yambar-modules/.envrc
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env sh
-use flake
diff --git a/pkgs/by-name/ya/yambar-modules/.gitignore b/pkgs/by-name/ya/yambar-modules/.gitignore
deleted file mode 100644
index ea8c4bf7..00000000
--- a/pkgs/by-name/ya/yambar-modules/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/target
diff --git a/pkgs/by-name/ya/yambar-modules/Cargo.lock b/pkgs/by-name/ya/yambar-modules/Cargo.lock
deleted file mode 100644
index 6b689d29..00000000
--- a/pkgs/by-name/ya/yambar-modules/Cargo.lock
+++ /dev/null
@@ -1,131 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
-
-[[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-
-[[package]]
-name = "libc"
-version = "0.2.167"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
-
-[[package]]
-name = "ntapi"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.20.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
-
-[[package]]
-name = "rayon"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "sysinfo"
-version = "0.28.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b"
-dependencies = [
- "cfg-if",
- "core-foundation-sys",
- "libc",
- "ntapi",
- "once_cell",
- "rayon",
- "winapi",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "yambar-modules"
-version = "0.1.0"
-dependencies = [
- "sysinfo",
-]
diff --git a/pkgs/by-name/ya/yambar-modules/Cargo.toml b/pkgs/by-name/ya/yambar-modules/Cargo.toml
deleted file mode 100644
index 8e3995fe..00000000
--- a/pkgs/by-name/ya/yambar-modules/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "yambar-modules"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-sysinfo = "0.28.4"
diff --git a/pkgs/by-name/ya/yambar-modules/flake.lock b/pkgs/by-name/ya/yambar-modules/flake.lock
deleted file mode 100644
index 8043448e..00000000
--- a/pkgs/by-name/ya/yambar-modules/flake.lock
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  "nodes": {
-    "flake-utils": {
-      "inputs": {
-        "systems": "systems"
-      },
-      "locked": {
-        "lastModified": 1731533236,
-        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "nixpkgs": {
-      "locked": {
-        "lastModified": 1732617236,
-        "narHash": "sha256-PYkz6U0bSEaEB1al7O1XsqVNeSNS+s3NVclJw7YC43w=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "af51545ec9a44eadf3fe3547610a5cdd882bc34e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixpkgs-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "root": {
-      "inputs": {
-        "flake-utils": "flake-utils",
-        "nixpkgs": "nixpkgs"
-      }
-    },
-    "systems": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    }
-  },
-  "root": "root",
-  "version": 7
-}
diff --git a/pkgs/by-name/ya/yambar-modules/flake.nix b/pkgs/by-name/ya/yambar-modules/flake.nix
deleted file mode 100644
index e3d0cd49..00000000
--- a/pkgs/by-name/ya/yambar-modules/flake.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  description = "Extension modules for yambar(1)";
-
-  inputs = {
-    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
-    flake-utils.url = "github:numtide/flake-utils";
-  };
-
-  outputs = {
-    nixpkgs,
-    flake-utils,
-    ...
-  }:
-    flake-utils.lib.eachDefaultSystem (system: let
-      pkgs = nixpkgs.legacyPackages."${system}";
-    in {
-      devShells.default = pkgs.mkShell {
-        packages = with pkgs; [
-          # rust stuff
-          cargo
-          clippy
-          rustc
-          rustfmt
-
-          cargo-edit
-          cargo-expand
-        ];
-      };
-    });
-}
-# vim: ts=2
-
diff --git a/pkgs/by-name/ya/yambar-modules/package.nix b/pkgs/by-name/ya/yambar-modules/package.nix
deleted file mode 100644
index 79281429..00000000
--- a/pkgs/by-name/ya/yambar-modules/package.nix
+++ /dev/null
@@ -1,14 +0,0 @@
-{rustPlatform}:
-rustPlatform.buildRustPackage {
-  pname = "yambar-modules";
-  version = "0.1.0";
-
-  src = ./.;
-  cargoLock = {
-    lockFile = ./Cargo.lock;
-  };
-
-  meta = {
-    mainProgram = "yambar-modules";
-  };
-}
diff --git a/pkgs/by-name/ya/yambar-modules/src/cpu.rs b/pkgs/by-name/ya/yambar-modules/src/cpu.rs
deleted file mode 100644
index 5a6dd084..00000000
--- a/pkgs/by-name/ya/yambar-modules/src/cpu.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use std::{thread, time::Duration};
-
-use sysinfo::{CpuExt, System, SystemExt};
-
-pub fn cpu() {
-    let mut sys = System::new();
-
-    loop {
-        sys.refresh_cpu();
-        let cpu_usage: f32 = sys.cpus().iter().map(|cpu| cpu.cpu_usage()).sum();
-        println!(
-            "cpu|range:0-100|{:.0}",
-            cpu_usage / sys.cpus().iter().count() as f32
-        );
-        println!();
-
-        // Sleeping to give the system time to run for long
-        // enough to have useful information.
-        thread::sleep(Duration::from_secs(3));
-    }
-}
diff --git a/pkgs/by-name/ya/yambar-modules/src/main.rs b/pkgs/by-name/ya/yambar-modules/src/main.rs
deleted file mode 100644
index 315c3be7..00000000
--- a/pkgs/by-name/ya/yambar-modules/src/main.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use std::{env::args, process};
-
-mod cpu;
-mod memory;
-
-fn main() {
-    let args: Vec<String> = args().collect();
-
-    if args.len() != 2 {
-        eprintln!("Usage: yambar-modules cpu|memory");
-        process::exit(1);
-    }
-
-    match args[1].as_str() {
-        "cpu" => {
-            cpu::cpu();
-        }
-        "memory" => {
-            memory::memory();
-        }
-        other => {
-            eprintln!("'{other}' is not a valid command. Only 'cpu' or 'memory'.");
-            process::exit(1);
-        }
-    }
-}
diff --git a/pkgs/by-name/ya/yambar-modules/src/memory.rs b/pkgs/by-name/ya/yambar-modules/src/memory.rs
deleted file mode 100644
index 6da714cc..00000000
--- a/pkgs/by-name/ya/yambar-modules/src/memory.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use std::{thread, time::Duration};
-
-use sysinfo::{System, SystemExt};
-
-pub fn memory() {
-    let mut sys = System::new();
-
-    loop {
-        sys.refresh_memory();
-
-        let memory_percentage: f64 =
-            100 as f64 * (sys.used_memory() as f64 / sys.total_memory() as f64);
-
-        println!("memperc|string|{:.0}", memory_percentage);
-        if sys.total_swap() > 0 {
-            let swap_percentage: f64 =
-                100 as f64 * (sys.used_swap() as f64 / sys.total_swap() as f64);
-            println!("swapperc|string|{:.0}", swap_percentage);
-            println!("swapstate|bool|true");
-        } else {
-            println!("swapstate|bool|false");
-        }
-        println!("");
-
-        // Sleeping to give the system time to run for long
-        // enough to have useful information.
-        thread::sleep(Duration::from_secs(3));
-    }
-}
diff --git a/pkgs/by-name/yt/yt/package.nix b/pkgs/by-name/yt/yt/package.nix
index 3c780374..63a37a47 100644
--- a/pkgs/by-name/yt/yt/package.nix
+++ b/pkgs/by-name/yt/yt/package.nix
@@ -1,39 +1,71 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
+  lib,
+  rustPlatform,
+  installShellFiles,
   fetchgit,
-  ffmpeg,
   gitUpdater,
-  glibc,
-  lib,
-  llvmPackages_latest,
-  makeWrapper,
+  # buildInputs
   mpv-unwrapped,
+  python3Packages,
   python3,
-  rustPlatform,
+  ffmpeg,
+  openssl,
+  libffi,
+  zlib,
+  curl,
+  # NativeBuildInputs
+  makeWrapper,
+  llvmPackages_latest,
+  glibc,
+  mold-wrapped,
   sqlite,
   fd,
+  pkg-config,
+  SDL2,
 }: let
-  version = "1.5.0";
-
-  src = fetchgit {
-    url = "https://git.foss-syndicate.org/bpeetz/clients/yt";
-    rev = "v${version}";
-    hash = "sha256-P/mpF2KPjoC7XZ6juJubeGEHhL2ajdOeiuIEb5sYrS0=";
-  };
-
-  buildInputs = [
-    (python3.withPackages (ps: [ps.yt-dlp]))
-    mpv-unwrapped.dev
-    ffmpeg
-  ];
+  version = "1.7.1";
 in
-  rustPlatform.buildRustPackage {
-    inherit version src buildInputs;
+  rustPlatform.buildRustPackage (finalAttrs: {
+    inherit version;
     pname = "yt";
 
+    src = fetchgit {
+      url = "https://git.foss-syndicate.org/bpeetz/clients/yt";
+      tag = "v${version}";
+      hash = "sha256-FKEMbxJI7OOXakY/JMyoz1rVU57xPuJ49m/JsB2b5Cc=";
+    };
+
+    buildInputs = [
+      python3Packages.yt-dlp
+      mpv-unwrapped.dev
+      ffmpeg
+      openssl
+      libffi
+      zlib
+      curl.dev
+    ];
+
     nativeBuildInputs = [
+      installShellFiles
       makeWrapper
       sqlite
       fd
+      pkg-config
+      mold-wrapped
+    ];
+
+    checkInputs = [
+      # Needed for the tests in `libmpv2`
+      SDL2
     ];
 
     passthru.updateScript = gitUpdater {rev-prefix = "v";};
@@ -43,27 +75,56 @@ in
         lib.versions.major
         llvmPackages_latest.clang-unwrapped.version;
     in {
+      # Needed for the compile time sqlite checks.
+      DATABASE_URL = "sqlite://database.sqlx";
+
+      # Required by yt_dlp
       FFMPEG_LOCATION = "${lib.getExe ffmpeg}";
-      PYO3_PYTHON = lib.getExe (python3.withPackages (ps: [ps.yt-dlp]));
 
+      # Needed for the libmpv2.
       C_INCLUDE_PATH = "${glibc.dev}/include";
-      DATABASE_URL = "sqlite://target/database.sqlx";
       LIBCLANG_INCLUDE_PATH = "${llvmPackages_latest.clang-unwrapped.lib}/lib/clang/${clang_version}/include";
       LIBCLANG_PATH = "${llvmPackages_latest.clang-unwrapped.lib}/lib/libclang.so";
     };
 
-    # Some tests depend on network access, others require a special library.
-    doCheck = false;
+    doCheck = true;
 
     prePatch = ''
+      # Generate the sqlite db, so that we can run the comp-time sqlite checks.
       bash ./scripts/mkdb.sh
     '';
 
-    useFetchCargoVendor = true;
-    cargoHash = "sha256-0XTbC+mFsczUFXqAtiQ+BIsCfKilerhlzE41pzVjLVs=";
+    cargoHash = "sha256-E5kMlXIoagidovGonTqbz9SMmvgKitJ9+zZW9U8WK7s=";
+
+    postInstall = let
+      collectDeps = pkg: let
+        next = pkg.propagatedBuildInputs or [];
+      in
+        [pkg]
+        ++ next
+        ++ (lib.flatten (builtins.map collectDeps next));
+
+      loadPythonDep = der: "${der}/lib/python${lib.versions.majorMinor python3.version}/site-packages";
+
+      pythonPath = builtins.concatStringsSep ":" (lib.lists.unique (
+        builtins.map loadPythonDep (
+          (collectDeps python3Packages.yt-dlp)
+          ++ [
+            # HACK(@bpeetz): These packages are not picked up in the traversal up top. <2025-06-16>
+            python3Packages.chardet
+          ]
+        )
+      ));
+    in ''
+      installShellCompletion --cmd yt \
+        --bash <(COMPLETE=bash $out/bin/yt) \
+        --fish <(COMPLETE=fish $out/bin/yt) \
+        --zsh <(COMPLETE=zsh $out/bin/yt)
 
-    postInstall = ''
+      # NOTE: We cannot clear the path, because we need access to the $EDITOR. <2025-04-04>
       wrapProgram $out/bin/yt \
-        --prefix PATH : ${lib.makeBinPath buildInputs}
+        --prefix PATH : ${lib.makeBinPath finalAttrs.buildInputs} \
+        --set YTDLP_NO_PLUGINS 1 \
+        --set PYTHONPATH ${pythonPath}
     '';
-  }
+  })
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 41bb654f..9a3ac842 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -1,23 +1,21 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
 {
   pkgs,
-  sysLib,
-  nixLib,
+  libraries,
 }: let
   inherit (pkgs) lib;
 
-  maybeMergeMessage = "the ./pkgs/by-name set";
-  mMM = maybeMergeMessage;
-  callPackage =
-    lib.callPackageWith
-    (nixLib.warnMerge
-      (nixLib.warnMerge
-        pkgs
-        myPkgs
-        mMM)
-      {inherit sysLib;}
-      mMM);
+  callPackage = lib.callPackageWith (libraries.extra.warnMerge pkgs myPkgs "the ./pkgs/by-name set");
 
-  myPkgs = nixLib.mkByName {
+  myPkgs = libraries.extra.mkByName {
     baseDirectory = ./by-name;
     fileName = "package.nix";
     finalizeFunction = name: value: callPackage value {};
diff --git a/pkgs/update_pkgs.sh b/pkgs/update_pkgs.sh
index 3e33b720..d046ee76 100755
--- a/pkgs/update_pkgs.sh
+++ b/pkgs/update_pkgs.sh
@@ -1,5 +1,15 @@
 #!/usr/bin/env sh
 
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
 die() {
     printf "\033[31;1mError: \033[0m%s\n" "$1"
     exit 1