about summary refs log tree commit diff
path: root/users/tazjin
diff options
context:
space:
mode:
Diffstat (limited to 'users/tazjin')
-rw-r--r--users/tazjin/chase-geese/default.nix2
-rw-r--r--users/tazjin/cursed/default.nix9
-rw-r--r--users/tazjin/cursed/responder.nix76
-rw-r--r--users/tazjin/dotfiles/.skip-subtree1
-rw-r--r--users/tazjin/dotfiles/default.nix22
-rw-r--r--users/tazjin/dotfiles/dunstrc65
-rw-r--r--users/tazjin/dotfiles/fuzzel.ini17
-rw-r--r--users/tazjin/dotfiles/niri.config.kdl133
-rw-r--r--users/tazjin/dotfiles/waybar/config.nix64
-rw-r--r--users/tazjin/dotfiles/waybar/style.css254
-rw-r--r--users/tazjin/eaglemode/default.nix16
-rw-r--r--users/tazjin/emacs/config/init.el18
-rw-r--r--users/tazjin/emacs/config/settings.el3
-rw-r--r--users/tazjin/emacs/default.nix6
-rw-r--r--users/tazjin/german-string/.gitignore1
-rw-r--r--users/tazjin/german-string/Cargo.lock399
-rw-r--r--users/tazjin/german-string/Cargo.toml7
-rw-r--r--users/tazjin/german-string/default.nix5
-rw-r--r--users/tazjin/german-string/src/lib.rs435
-rw-r--r--users/tazjin/home/arbat.nix11
-rw-r--r--users/tazjin/home/shared.nix55
-rw-r--r--users/tazjin/home/zamalek.nix2
-rw-r--r--users/tazjin/keys/default.nix1
-rw-r--r--users/tazjin/niri-reap/.gitignore1
-rw-r--r--users/tazjin/niri-reap/Cargo.lock104
-rw-r--r--users/tazjin/niri-reap/Cargo.toml7
-rw-r--r--users/tazjin/niri-reap/default.nix13
-rw-r--r--users/tazjin/niri-reap/src/main.rs76
-rw-r--r--users/tazjin/nixos/arbat/default.nix72
-rw-r--r--users/tazjin/nixos/default.nix5
-rw-r--r--users/tazjin/nixos/frog/default.nix5
-rw-r--r--users/tazjin/nixos/khamovnik/default.nix15
-rw-r--r--users/tazjin/nixos/koptevo/default.nix137
-rw-r--r--users/tazjin/nixos/modules/desktop.nix74
-rw-r--r--users/tazjin/nixos/modules/fonts.nix6
-rw-r--r--users/tazjin/nixos/modules/geesefs.nix2
-rw-r--r--users/tazjin/nixos/modules/home-config.nix6
-rw-r--r--users/tazjin/nixos/modules/homepage.nix59
-rw-r--r--users/tazjin/nixos/modules/physical.nix16
-rw-r--r--users/tazjin/nixos/tverskoy/default.nix4
-rw-r--r--users/tazjin/nixos/zamalek/default.nix11
-rw-r--r--users/tazjin/secrets/lego-yandex.agebin0 -> 3886 bytes
-rw-r--r--users/tazjin/secrets/secrets.nix1
-rw-r--r--users/tazjin/wallpapers/alphasoft.webpbin0 -> 176360 bytes
-rw-r--r--users/tazjin/wallpapers/svema_02_big.webpbin0 -> 437844 bytes
-rw-r--r--users/tazjin/wallpapers/svema_07_big.webpbin0 -> 1023978 bytes
-rw-r--r--users/tazjin/wallpapers/svema_09_big.webpbin0 -> 833118 bytes
-rw-r--r--users/tazjin/wallpapers/svema_14_big.webpbin0 -> 861654 bytes
48 files changed, 2083 insertions, 133 deletions
diff --git a/users/tazjin/chase-geese/default.nix b/users/tazjin/chase-geese/default.nix
index 3549f758687d..595ca92896a5 100644
--- a/users/tazjin/chase-geese/default.nix
+++ b/users/tazjin/chase-geese/default.nix
@@ -9,5 +9,5 @@ pkgs.writeShellScriptBin "chase-geese" ''
 
   echo "Mounting the cloud ..."
   mkdir -p ~/cloud
-  ${depot.third_party.geesefs}/bin/geesefs tazjins-files ~/cloud
+  ${pkgs.geesefs}/bin/geesefs tazjins-files ~/cloud
 ''
diff --git a/users/tazjin/cursed/default.nix b/users/tazjin/cursed/default.nix
new file mode 100644
index 000000000000..336a746db79d
--- /dev/null
+++ b/users/tazjin/cursed/default.nix
@@ -0,0 +1,9 @@
+{ depot, ... }:
+
+let
+  inherit (depot.web) bubblegum;
+in
+(bubblegum.writeCGI
+  {
+    name = "cursed";
+  } ./responder.nix) // { meta.ci.skip = true; }
diff --git a/users/tazjin/cursed/responder.nix b/users/tazjin/cursed/responder.nix
new file mode 100644
index 000000000000..9aa6a2d55807
--- /dev/null
+++ b/users/tazjin/cursed/responder.nix
@@ -0,0 +1,76 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix.html)
+    __findFile
+    esc
+    withDoctype
+    ;
+
+  # CGI envvars: https://www.instanet.com/cgi/env.html
+  method = builtins.getEnv "REQUEST_METHOD";
+  path = builtins.getEnv "PATH_INFO";
+
+  rawQuery = builtins.getEnv "QUERY_STRING";
+  query = with builtins; let
+    pairs = (filter (s: isString s && s != "") (split "&" rawQuery));
+    tuples = filter (l: length l > 0) (map (p: filter (s: isString s) (split "=" p)) pairs);
+    mkAttr = t: {
+      name = elemAt t 0;
+      value = elemAt t 1;
+    };
+  in
+  listToAttrs (map mkAttr tuples);
+
+  default = let {
+  hasQuery = if builtins.length (builtins.attrNames query) > 0 then "?" else "";
+  body = (withDoctype (<html> { lang = "en"; } [
+    (<head> { } [
+      (<title> { } "some cursed nix")
+    ])
+    (<body> { } [
+      (<p> { } "hello volgasprint")
+      (<p> { } [ method " " path hasQuery rawQuery ])
+      (<p> { } (builtins.toJSON query))
+    ])
+  ]));
+  };
+
+  greeter = withDoctype (<html> { lang = "en"; } [
+    (<head> { } [
+      (<title> { } "hello there")
+    ])
+    (<body> { } [
+      (<p> { } "hello ${query.name or "unknown"}")
+    ])
+  ]);
+
+  weather = let {
+  town = query.town or "Kazan";
+  w = builtins.fetchurl "https://wttr.in/${town}?";
+  rendered = with depot.third_party.nixpkgs; runCommand "weather-${town}" { } ''
+    cat ${w} | ${ansi2html}/bin/ansi2html > $out
+  '';
+
+  body = builtins.readFile "${rendered}";
+  };
+
+  routes = {
+    "/other" = (withDoctype (<html> { lang = "en"; } [
+      (<head> { } [
+        (<title> { } "other endpoint")
+      ])
+      (<body> { } [
+        (<p> { } "this is another route")
+      ])
+    ]));
+    "/greeter" = greeter;
+    "/weather" = weather;
+  }."${path}" or default;
+
+in
+depot.web.bubblegum.respond "OK"
+{
+  "Content-Type" = "text/html";
+}
+  routes
diff --git a/users/tazjin/dotfiles/.skip-subtree b/users/tazjin/dotfiles/.skip-subtree
new file mode 100644
index 000000000000..954981f436ee
--- /dev/null
+++ b/users/tazjin/dotfiles/.skip-subtree
@@ -0,0 +1 @@
+Stuff below here is managed manually, without readTree.
diff --git a/users/tazjin/dotfiles/default.nix b/users/tazjin/dotfiles/default.nix
index 9b783a9c857c..6291a10303e7 100644
--- a/users/tazjin/dotfiles/default.nix
+++ b/users/tazjin/dotfiles/default.nix
@@ -1,3 +1,23 @@
-_: {
+{ depot, pkgs, ... }@args:
+
+rec {
   dunstrc = ./dunstrc;
+  niri = ./niri.config.kdl;
+  fuzzel = ./fuzzel.ini;
+  waybar = {
+    config = import ./waybar/config.nix args;
+    style = pkgs.runCommandNoCC "waybar-style.css"
+      {
+        CHICAGO95 = depot.third_party.chicago95;
+      } ''
+      cat ${./waybar/style.css} | ${pkgs.envsubst}/bin/envsubst > $out
+    '';
+  };
+
+  # Helper derivation for iterating on waybar config.
+  waybarTest = pkgs.runCommandNoCC "waybar-conf" { } ''
+    mkdir -p $out
+    cat ${pkgs.writeText "waybar-conf.json" (builtins.toJSON(builtins.attrValues waybar.config))} > $out/config
+    cp ${waybar.style} $out/style.css
+  '';
 }
diff --git a/users/tazjin/dotfiles/dunstrc b/users/tazjin/dotfiles/dunstrc
index 2aa1141b6ec2..d984ff94ecaa 100644
--- a/users/tazjin/dotfiles/dunstrc
+++ b/users/tazjin/dotfiles/dunstrc
@@ -1,54 +1,15 @@
 [global]
-font = Iosevka Term 11
-origin = top-left
-markup = yes
-plain_text = no
-format = "<b>%s</b>\n%b"
-sort = no
-indicate_hidden = yes
-alignment = center
-bounce_freq = 0
-show_age_threshold = -1
-word_wrap = yes
-ignore_newline = no
-stack_duplicates = yes
-hide_duplicate_count = yes
-geometry = "300x50-15+49"
-shrink = no
-transparency = 5
-idle_threshold = 0
-monitor = 0
+origin = bottom-right
+offset = 5x5 # takes into account menu bar!
+corner_radius = 5
+frame_width = 1
+frame_color = "#000000"
+foreground = "#000000"
+background = "#ffffe1"
+font = Arial 12
 follow = keyboard
-sticky_history = yes
-history_length = 15
-show_indicators = no
-line_height = 3
-separator_height = 2
-padding = 6
-horizontal_padding = 6
-separator_color = frame
-startup_notification = false
-dmenu = /usr/bin/dmenu -p dunst:
-browser = /usr/bin/firefox -new-tab
-icon_position = off
-max_icon_size = 80
-frame_width = 3
-frame_color = "#8EC07C"
-
-[urgency_low]
-frame_color = "#3B7C87"
-foreground = "#3B7C87"
-background = "#191311"
-timeout = 4
-
-[urgency_normal]
-frame_color = "#5B8234"
-foreground = "#5B8234"
-background = "#191311"
-timeout = 6
-
-[urgency_critical]
-frame_color = "#B7472A"
-foreground = "#B7472A"
-background = "#191311"
-timeout = 8
+vertical_alignment = top
+format = "<b>%s</b>\n<i>from %a</i>\n\n%b"
+icon_theme = "Chicago95-tux"
+enable_recursive_icon_lookup = true
+icon_position = left
diff --git a/users/tazjin/dotfiles/fuzzel.ini b/users/tazjin/dotfiles/fuzzel.ini
new file mode 100644
index 000000000000..92d46c0b6d88
--- /dev/null
+++ b/users/tazjin/dotfiles/fuzzel.ini
@@ -0,0 +1,17 @@
+[main]
+font=Arial
+
+[colors]
+background=c0c0c0ff
+text=000000ff
+input=000000ff
+prompt=000000ff
+match=808080ff
+selection=000080ff
+selection-match=ffffffff
+selection-text=ffffffff
+border=080808ff
+
+[border]
+width=2
+radius=5
diff --git a/users/tazjin/dotfiles/niri.config.kdl b/users/tazjin/dotfiles/niri.config.kdl
new file mode 100644
index 000000000000..a5926d5a8a8d
--- /dev/null
+++ b/users/tazjin/dotfiles/niri.config.kdl
@@ -0,0 +1,133 @@
+// https://github.com/YaLTeR/niri/wiki/Configuration:-Overview
+
+input {
+    keyboard {
+        xkb {
+            layout "us,ru"
+            variant "hyper"
+            options "grp:win_space_toggle,compose:ralt,caps:hyper"
+        }
+    }
+
+    touchpad {
+        tap
+    }
+}
+
+layout {
+    gaps 12
+    center-focused-column "never"
+
+    preset-column-widths {
+        proportion 0.33333
+        proportion 0.5
+        proportion 0.66667
+    }
+
+    default-column-width {}
+
+    focus-ring {
+        off
+    }
+
+    border {
+        off
+    }
+}
+
+spawn-at-startup "xwayland-satellite"
+spawn-at-startup "xrandr --output eDP-1 --primary"
+spawn-at-startup "wpaperd" "-d"
+spawn-at-startup "systemctl --user start xss-lock"
+
+environment {
+  QT_QPA_PLATFORM "wayland"
+  DISPLAY ":0"
+  EDITOR "emacsclient"
+}
+
+hotkey-overlay {
+  skip-at-startup
+}
+
+screenshot-path "~/screenshots/screenshot-%Y-%m-%d_%H-%M-%S.png"
+
+animations {
+    slowdown 0.3
+}
+
+binds {
+    Mod+Shift+Slash { show-hotkey-overlay; }
+
+    Mod+T { spawn "emacsclient" "--no-wait" "--create-frame" "--eval" "(vterm)"; }
+    Mod+Shift+T { spawn "alacritty"; } // fallback terminal
+    Mod+D { spawn "xfce4-appfinder" "--disable-server"; }
+    Super+Alt+L { spawn "swaylock" "-fFkl" "-c" "#008080"; }
+    Super+B { spawn "emacsclient" "-e" "(niri-go-anywhere-external)"; }
+
+    // Volume control
+    XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; }
+    XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; }
+    XF86AudioMute        allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
+    XF86AudioMicMute     allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
+
+    // Brightness control
+    XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "5"; }
+    Shift+XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "1"; }
+    XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "5"; }
+    Shift+XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "1"; }
+
+    Mod+Q { close-window; }
+
+    Mod+Left      { focus-column-or-monitor-left; }
+    Mod+Right     { focus-column-or-monitor-right; }
+    Mod+Down      { focus-column-or-monitor-right; }
+    Mod+Up        { focus-column-or-monitor-left; }
+    Mod+J         { focus-column-or-monitor-left; }
+    Mod+K         { focus-column-or-monitor-right; }
+    Mod+L         { focus-window-up; }
+    Mod+Semicolon { focus-window-down; }
+
+    Mod+Ctrl+Left  { move-column-left-or-to-monitor-left; }
+    Mod+Ctrl+Right { move-column-right-or-to-monitor-right; }
+    Mod+Ctrl+J     { move-column-left-or-to-monitor-left; }
+    Mod+Ctrl+K     { move-column-right-or-to-monitor-right; }
+
+    Mod+Home { focus-column-first; }
+    Mod+End  { focus-column-last; }
+
+    Mod+Ctrl+Home { move-column-to-first; }
+    Mod+Ctrl+End  { move-column-to-last; }
+
+    // Scroll (or move windows) between columns when holding the modifier down.
+    Mod+WheelScrollDown      cooldown-ms=150 { focus-column-or-monitor-right; }
+    Mod+WheelScrollUp        cooldown-ms=150 { focus-column-or-monitor-left; }
+    Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-right-or-to-monitor-right; }
+    Mod+Ctrl+WheelScrollUp   cooldown-ms=150 { move-column-left-or-to-monitor-left; }
+
+    Mod+Comma  { consume-window-into-column; }
+    Mod+Period { expel-window-from-column; }
+
+    // There are also commands that consume or expel a single window to the side.
+    // Mod+BracketLeft  { consume-or-expel-window-left; }
+    // Mod+BracketRight { consume-or-expel-window-right; }
+
+    Mod+R { switch-preset-column-width; }
+    Mod+Shift+R { reset-window-height; }
+    Mod+F { maximize-column; }
+    Mod+Shift+F { fullscreen-window; }
+    Mod+C { center-column; }
+
+    Mod+Minus { set-column-width "-10%"; }
+    Mod+Equal { set-column-width "+10%"; }
+
+    // Finer height adjustments when in column with other windows.
+    Mod+Shift+Minus { set-window-height "-2%"; }
+    Mod+Shift+Equal { set-window-height "+2%"; }
+
+    Print { screenshot; }
+    Ctrl+Print { screenshot-screen; }
+    Alt+Print { screenshot-window; }
+
+    Mod+Shift+E { quit; }
+}
diff --git a/users/tazjin/dotfiles/waybar/config.nix b/users/tazjin/dotfiles/waybar/config.nix
new file mode 100644
index 000000000000..ba5281f02e07
--- /dev/null
+++ b/users/tazjin/dotfiles/waybar/config.nix
@@ -0,0 +1,64 @@
+{ depot, pkgs, ... }:
+
+let
+  launcher = "${pkgs.xfce4-appfinder}/bin/xfce4-appfinder --disable-server";
+in
+{
+  mainBar = {
+    layer = "top";
+    position = "bottom";
+    modules-left = [ "custom/start" "wlr/taskbar" ];
+
+    "custom/start" = {
+      format = " Start";
+      on-click = "xfce4-appfinder --disable-server";
+    };
+
+    modules-right = [ "tray" "backlight" "battery" "pulseaudio" "clock" ];
+
+    pulseaudio = {
+      on-click = "pavucontrol";
+      format = " "; #styling only
+      states = {
+        low = 1;
+        medium = 40;
+        high = 75;
+      };
+    };
+
+    battery = {
+      format = " "; # styling only
+      interval = 10;
+      states = {
+        full = 100;
+        good = 85;
+        medium = 60;
+        low = 40;
+        warning = 20;
+        critical = 10;
+      };
+    };
+
+    backlight = {
+      format = "{percent}%"; # styling only
+      on-scroll-up = "light -A 1";
+      on-scroll-down = "light -U 1";
+    };
+
+    clock.format-alt = "{:%a, %d. %b  %H:%M}";
+
+    tray = {
+      icon-size = 20;
+      spacing = 10;
+    };
+
+    "wlr/taskbar" = {
+      format = "{icon} {title}";
+      on-click = "activate";
+      rewrite = {
+        # Truncate any format over 16 characters.
+        "^(.{16}).+$" = "$1…";
+      };
+    };
+  };
+}
diff --git a/users/tazjin/dotfiles/waybar/style.css b/users/tazjin/dotfiles/waybar/style.css
new file mode 100644
index 000000000000..52f5759cc796
--- /dev/null
+++ b/users/tazjin/dotfiles/waybar/style.css
@@ -0,0 +1,254 @@
+* {
+    /* `otf-font-awesome` is required to be installed for icons */
+    font-family: FontAwesome, MS Sans Serif;
+    font-size: 14px;
+}
+
+window#waybar {
+    background-color: #c0c0c0;
+    border-top: 0.1875em solid #dfdfdf;
+    color: #000000;
+    transition-property: background-color;
+    transition-duration: .5s;
+}
+
+window#waybar.hidden {
+    opacity: 0.2;
+}
+
+window#waybar.termite {
+    background-color: #3F3F3F;
+}
+
+window#waybar.chromium {
+    background-color: #000000;
+    border: none;
+}
+
+button {
+    /* Use box-shadow instead of border so the text isn't offset */
+    box-shadow: inset 0 -0.1875em transparent;
+    /* Avoid rounded borders under each button name */
+    border: none;
+    border-radius: 0;
+}
+
+/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
+button:hover {
+    background: inherit;
+    box-shadow: inset 0 -0.1875em #ffffff;
+}
+
+#mode {
+    background-color: #64727D;
+    box-shadow: inset 0 -0.1875em #ffffff;
+}
+
+#clock,
+#battery,
+#cpu,
+#memory,
+#disk,
+#temperature,
+#backlight,
+#network,
+#pulseaudio,
+#wireplumber,
+#custom-media,
+#tray,
+#mode,
+#idle_inhibitor,
+#scratchpad,
+#power-profiles-daemon,
+#mpd {
+    padding: 0 0.3125em;
+    padding-top: 0em;
+    padding-bottom: 0em;
+    /* color: #ffffff; */
+}
+
+#window,
+#workspaces {
+    margin: 0 0.25em;
+}
+
+/* faithful-ish recreation of the old Windows start button ... */
+#custom-start {
+    /* general positioning to keep the spacing approximately correct */
+    color: @button_text_color;
+    font-weight: bold;
+    margin: 0.2em;
+    margin-top: 0.35em;
+    padding: 0.2em;
+    padding-left: 1.25em;
+
+    /* raised button look, as per the Chicago95 GTK button style */
+    border: 0.1em solid;
+    border-radius: 0em;
+    color: @button_text_color;
+    outline-color: @outline_color;
+    border-top-color: @border_bright;
+    border-right-color: @border_dark;
+    border-left-color: @border_bright;
+    border-bottom-color: @border_dark;
+    background-color: @button_bg_color;
+    box-shadow: inset -0.1em -0.1em @border_shade, inset 0.1em 0.1em @border_light;
+
+    /* the actual image! */
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/categories/scalable/xfdesktop-menu.svg");
+    background-position: 0.15em center;
+    background-repeat: no-repeat;
+    background-size: 1.4em;
+}
+
+.modules-right {
+    margin: 0.2em;
+    margin-top: 0.35em;
+}
+
+#clock {
+    border-top: 0.1em solid gray;
+    border-left: 0.1em solid gray;
+    border-right: 0.1em solid white;
+    border-bottom: 0.1em solid white;
+}
+
+/* base setup for classes that have a Chicago95 icon as the display */
+#battery, #pulseaudio, #backlight {
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: 24px;
+    min-width: 24px;
+    color: transparent; /* because the tooltips are still desirable */
+}
+
+#backlight {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/xfpm-brightness-lcd.png");
+    font-size: 0px;
+}
+
+/* battery levels matching Chicago95 icons */
+
+#battery.charging.critical {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-000-charging.png");
+}
+
+#battery.charging.warning {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-020-charging.png");
+}
+
+#battery.charging.low {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-040-charging.png");
+}
+
+#battery.charging.medium {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-060-charging.png");
+}
+
+#battery.charging.good {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-080-charging.png");
+}
+
+#battery.charging.full {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-100-charging.png");
+}
+
+#battery.critical {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-000.png");
+}
+
+#battery.warning {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-020.png");
+}
+
+#battery.low {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-040.png");
+}
+
+#battery.medium {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-060.png");
+}
+
+#battery.good {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-080.png");
+}
+
+#battery.full {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/48/battery-100.png");
+}
+
+/* volume levels matching Chicago95 icons */
+
+#pulseaudio.muted {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-muted.png");
+}
+
+#pulseaudio.low {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-low.png");
+}
+
+#pulseaudio.medium {
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-medium.png");
+}
+
+#pulseaudio { /* default, if no lower volume state is set */
+    background-image: url("${CHICAGO95}/share/icons/Chicago95/status/32/audio-volume-high.png");
+}
+
+@keyframes blink {
+    to {
+        background-color: #ffffff;
+        color: #000000;
+    }
+}
+
+label:focus {
+    background-color: #000000;
+}
+
+#tray > .passive {
+    -gtk-icon-effect: dim;
+}
+
+#tray > .needs-attention {
+    -gtk-icon-effect: highlight;
+    background-color: #e35f5f;
+}
+
+#idle_inhibitor {
+    background-color: #2d3436;
+}
+
+#idle_inhibitor.activated {
+    background-color: #ecf0f1;
+    color: #2d3436;
+}
+
+#taskbar {
+    color: @button_text_color;
+    margin: 0.2em;
+    margin-top: 0.35em;
+}
+
+#taskbar button {
+    padding: 0.2em;
+    margin-right: 0.3em;
+    border: 0.1em solid;
+    border-radius: 0em;
+    color: @button_text_color;
+    outline-color: @outline_color;
+    border-top-color: @border_bright;
+    border-right-color: @border_dark;
+    border-left-color: @border_bright;
+    border-bottom-color: @border_dark;
+    background-color: @button_bg_color;
+    box-shadow: inset -0.1em -0.1em @border_shade, inset 0.1em 0.1em @border_light;
+}
+
+#taskbar button.active {
+    border-top-color: @border_dark;
+    border-right-color: @border_bright;
+    border-left-color: @border_dark;
+    border-bottom-color: @border_bright;
+    box-shadow: inset 1px 1px @border_shade;
+}
diff --git a/users/tazjin/eaglemode/default.nix b/users/tazjin/eaglemode/default.nix
new file mode 100644
index 000000000000..9f59695a40b8
--- /dev/null
+++ b/users/tazjin/eaglemode/default.nix
@@ -0,0 +1,16 @@
+# Derivation for my fully configured Eagle Mode.
+{ depot, ... }:
+
+with depot.tools.eaglemode;
+
+withConfig {
+  config = etcDir {
+    extraPaths = [
+      commands.emacsclient
+      plugins.example
+      plugins.yatracker
+      plugins.qoi
+      plugins.avif
+    ];
+  };
+}
diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el
index ced3bf2ff83b..8cf1979bd64f 100644
--- a/users/tazjin/emacs/config/init.el
+++ b/users/tazjin/emacs/config/init.el
@@ -72,7 +72,8 @@
 (use-package rainbow-mode)
 (use-package s)
 (use-package string-edit-at-point)
-(use-package term-switcher)
+(use-package term-switcher
+  :bind (:map global-map ("<f5>" . #'ts/switch-to-terminal)))
 
 (use-package undo-tree
   :config (global-undo-tree-mode)
@@ -126,12 +127,9 @@
 
 (use-package f)
 
-(use-package go-mode
-  :bind (:map go-mode-map ("C-c C-r" . recompile))
-  :hook ((go-mode . (lambda ()
-                      (setq tab-width 2)
-                      (setq-local compile-command
-                                  (concat "go build " buffer-file-name))))))
+(use-package go-ts-mode
+  :custom
+  (go-ts-mode-indent-offset 4))
 
 (use-package haskell-mode)
 
@@ -152,6 +150,7 @@
   (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)))
 
 (use-package markdown-toc)
+(use-package niri)
 
 (use-package nix-mode
   :hook ((nix-mode . (lambda ()
@@ -169,7 +168,7 @@
   (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/"))
 
 (use-package telega
-  :bind (:map global-map ("s-c" . (lambda (p) (interactive "P")
+  :bind (:map global-map ("C-x c" . (lambda (p) (interactive "P")
                                     (if p (call-interactively #'telega-chat-with)
                                       (telega))))
          :map telega-chat-button-map ("a" . ignore))
@@ -241,8 +240,7 @@
 
 ;; Load all other Emacs configuration. These configurations are
 ;; added to `load-path' by Nix.
-(mapc 'require '(desktop
-                 mail-setup
+(mapc 'require '(mail-setup
                  look-and-feel
                  functions
                  settings
diff --git a/users/tazjin/emacs/config/settings.el b/users/tazjin/emacs/config/settings.el
index 6c66ca608d6e..afe181b70bdf 100644
--- a/users/tazjin/emacs/config/settings.el
+++ b/users/tazjin/emacs/config/settings.el
@@ -19,6 +19,9 @@
       ediff-split-window-function 'split-window-horizontally
       initial-major-mode 'emacs-lisp-mode)
 
+(setq-default tab-width 4)
+(setq-default fill-column 80)
+
 (add-to-list 'safe-local-variable-values '(lexical-binding . t))
 (add-to-list 'safe-local-variable-values '(whitespace-line-column . 80))
 
diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix
index 46843432f189..80a922043277 100644
--- a/users/tazjin/emacs/default.nix
+++ b/users/tazjin/emacs/default.nix
@@ -3,14 +3,14 @@
 { depot, lib, pkgs, ... }:
 
 pkgs.makeOverridable
-  ({ emacs ? pkgs.emacs29 }:
+  ({ emacs ? pkgs.emacs29-pgtk }:
   let
     emacsPackages = (pkgs.emacsPackagesFor emacs);
     emacsWithPackages = emacsPackages.emacsWithPackages;
 
     # If switching telega versions, use this variable because it will
     # keep the version check, binary path and so on in sync.
-    currentTelega = epkgs: epkgs.melpaPackages.telega;
+    currentTelega = epkgs: epkgs.telega;
 
     # $PATH for binaries that need to be available to Emacs
     emacsBinPath = lib.makeBinPath [
@@ -41,6 +41,7 @@ pkgs.makeOverridable
       tree-sitter-rust
       tree-sitter-sql
       tree-sitter-toml
+      tree-sitter-typescript
       tree-sitter-yaml
     ]);
 
@@ -112,6 +113,7 @@ pkgs.makeOverridable
 
       # Custom depot packages (either ours, or overridden ones)
       tvlPackages.dottime
+      tvlPackages.niri
       tvlPackages.nix-util
       tvlPackages.passively
       tvlPackages.rcirc
diff --git a/users/tazjin/german-string/.gitignore b/users/tazjin/german-string/.gitignore
new file mode 100644
index 000000000000..2f7896d1d136
--- /dev/null
+++ b/users/tazjin/german-string/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/german-string/Cargo.lock b/users/tazjin/german-string/Cargo.lock
new file mode 100644
index 000000000000..ffd73ea32472
--- /dev/null
+++ b/users/tazjin/german-string/Cargo.lock
@@ -0,0 +1,399 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "german-string"
+version = "0.1.0"
+dependencies = [
+ "proptest",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[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.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[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"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/users/tazjin/german-string/Cargo.toml b/users/tazjin/german-string/Cargo.toml
new file mode 100644
index 000000000000..8eec963f071a
--- /dev/null
+++ b/users/tazjin/german-string/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "german-string"
+version = "0.1.0"
+edition = "2021"
+
+[dev-dependencies]
+proptest = "1.5.0"
diff --git a/users/tazjin/german-string/default.nix b/users/tazjin/german-string/default.nix
new file mode 100644
index 000000000000..c6cbc8c3c2d0
--- /dev/null
+++ b/users/tazjin/german-string/default.nix
@@ -0,0 +1,5 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+}
diff --git a/users/tazjin/german-string/src/lib.rs b/users/tazjin/german-string/src/lib.rs
new file mode 100644
index 000000000000..328eca309f38
--- /dev/null
+++ b/users/tazjin/german-string/src/lib.rs
@@ -0,0 +1,435 @@
+use std::alloc::Layout;
+use std::cmp::Ordering;
+use std::fmt::{Debug, Formatter};
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct GSSmall {
+    len: u32,
+    data: [u8; 12],
+}
+
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+struct StorageClassPtr(usize);
+
+impl StorageClassPtr {
+    fn transient(ptr: *const u8) -> Self {
+        debug_assert!(
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
+        );
+        Self(ptr as usize)
+    }
+
+    fn persistent(ptr: *const u8) -> Self {
+        debug_assert!(
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
+        );
+        Self((ptr as usize) | 0b1)
+    }
+
+    fn as_ptr(&self) -> *const u8 {
+        (self.0 & !0b1) as *const u8
+    }
+
+    unsafe fn as_mut_ptr(&self) -> *mut u8 {
+        (self.0 & !0b1) as *mut u8
+    }
+
+    fn is_transient(&self) -> bool {
+        (self.0 & 0b1) == 0
+    }
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct GSLarge {
+    len: u32,
+    prefix: [u8; 4],
+    data: StorageClassPtr,
+}
+
+const _ASSERT_VARIANTS_SIZE: () = assert!(
+    std::mem::size_of::<GSSmall>() == std::mem::size_of::<GSLarge>(),
+    "German String variants must have the same size"
+);
+
+union GSRepr {
+    small: GSSmall,
+    large: GSLarge,
+}
+
+#[repr(transparent)]
+pub struct GermanString(GSRepr);
+
+const _ASSERT_GSTRING_SIZE: () = assert!(
+    std::mem::size_of::<GermanString>() == 16,
+    "German String should be 16 bytes in size",
+);
+
+impl GermanString {
+    /// Creates a new transient German String from the given slice, copying the
+    /// data in the process.
+    pub fn transient(bytes: &[u8]) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+            s.data[..bytes.len()].copy_from_slice(bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let layout = Layout::array::<u8>(bytes.len()).unwrap();
+            let mut large = GSLarge {
+                len: bytes.len() as u32,
+                prefix: [0u8; 4],
+                data: unsafe {
+                    let ptr = std::alloc::alloc(layout);
+                    if ptr.is_null() {
+                        std::alloc::handle_alloc_error(layout);
+                    }
+                    std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
+                    StorageClassPtr::transient(ptr)
+                },
+            };
+
+            large.prefix.copy_from_slice(&bytes[..4]);
+
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a new transient German String from the given owned bytes. Short
+    /// strings will be copied into the string representation, long strings will
+    /// be moved out of the given vector without additional allocations.
+    pub fn transient_from_owned(bytes: Vec<u8>) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let md = std::mem::ManuallyDrop::new(bytes);
+            let mut large = GSLarge {
+                len: md.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::transient(md.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&md[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent(bytes: &'static [u8]) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let mut large = GSLarge {
+                len: bytes.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::persistent(bytes.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&bytes[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String by leaking the provided data.
+    pub fn persistent_leak(bytes: Vec<u8>) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let md = std::mem::ManuallyDrop::new(bytes);
+            let mut large = GSLarge {
+                len: md.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::persistent(md.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&md[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent_from_str(s: &'static str) -> GermanString {
+        GermanString::persistent(s.as_bytes())
+    }
+
+    pub fn len(&self) -> usize {
+        // SAFETY: The length field is located in the same location for both
+        // variants, reading it from either is safe.
+        unsafe { self.0.small.len as usize }
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        if self.len() > 12 {
+            unsafe { std::slice::from_raw_parts(self.0.large.data.as_ptr(), self.len()) }
+        } else {
+            unsafe { &self.0.small.data.as_ref()[..self.len()] }
+        }
+    }
+
+    pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
+        std::str::from_utf8(self.as_bytes())
+    }
+}
+
+impl Drop for GermanString {
+    fn drop(&mut self) {
+        unsafe {
+            if self.len() > 12 && self.0.large.data.is_transient() {
+                let layout = Layout::array::<u8>(self.len()).unwrap();
+                std::alloc::dealloc(self.0.large.data.as_mut_ptr(), layout);
+            }
+        }
+    }
+}
+
+impl PartialEq for GermanString {
+    fn eq(&self, other: &GermanString) -> bool {
+        if self.len() != other.len() {
+            return false;
+        }
+
+        unsafe {
+            if self.len() <= 12 {
+                return self.0.small.data[..self.len()] == other.0.small.data[..other.len()];
+            }
+            return self.0.large.data.as_ptr() == other.0.large.data.as_ptr()
+                || (self.0.large.prefix == other.0.large.prefix
+                    && self.as_bytes() == other.as_bytes());
+        }
+    }
+}
+
+impl Eq for GermanString {}
+
+impl Ord for GermanString {
+    fn cmp(&self, other: &GermanString) -> Ordering {
+        match (self.len().cmp(&12), other.len().cmp(&12)) {
+            // two small strings
+            (Ordering::Less | Ordering::Equal, Ordering::Less | Ordering::Equal) => unsafe {
+                self.0.small.data[..self.len()].cmp(&other.0.small.data[..other.len()])
+            },
+            // two large strings
+            (Ordering::Greater, Ordering::Greater) => unsafe {
+                match self.0.large.prefix.cmp(&other.0.large.prefix) {
+                    Ordering::Equal => self.as_bytes().cmp(other.as_bytes()),
+                    ordering => ordering,
+                }
+            },
+
+            // LHS large, RHS small
+            (Ordering::Greater, _) => {
+                let prefix_ordering =
+                    unsafe { self.0.large.prefix.as_slice().cmp(&other.0.small.data[..4]) };
+
+                if prefix_ordering != Ordering::Equal {
+                    return prefix_ordering;
+                }
+
+                self.as_bytes().cmp(other.as_bytes())
+            }
+
+            // LHS small, RHS large
+            (_, Ordering::Greater) => {
+                let prefix_ordering =
+                    unsafe { self.0.small.data[..4].cmp(other.0.large.prefix.as_slice()) };
+
+                if prefix_ordering != Ordering::Equal {
+                    return prefix_ordering;
+                }
+
+                self.as_bytes().cmp(other.as_bytes())
+            }
+        }
+    }
+}
+
+impl PartialOrd for GermanString {
+    fn partial_cmp(&self, other: &GermanString) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Debug for GermanString {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        String::from_utf8_lossy(self.as_bytes()).fmt(f)
+    }
+}
+
+impl Clone for GermanString {
+    fn clone(&self) -> Self {
+        unsafe {
+            if self.len() <= 12 {
+                return GermanString(GSRepr {
+                    small: self.0.small.clone(),
+                });
+            }
+
+            if self.0.large.data.is_transient() {
+                return GermanString::transient(self.as_bytes());
+            }
+
+            return GermanString(GSRepr {
+                large: self.0.large.clone(),
+            });
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    impl Arbitrary for GermanString {
+        type Parameters = <String as Arbitrary>::Parameters;
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+            any_with::<String>(args)
+                .prop_map(|s| GermanString::transient(s.as_bytes()))
+                .boxed()
+        }
+    }
+
+    #[test]
+    fn test_empty_string() {
+        let empty = GermanString::transient(b"");
+
+        assert_eq!(empty.len(), 0, "empty string should be empty");
+        assert_eq!(empty.as_bytes(), b"", "empty string should contain nothing");
+        assert_eq!(
+            empty.as_str().expect("empty string is valid UTF-8"),
+            "",
+            "empty string should contain empty string"
+        );
+    }
+
+    #[test]
+    fn test_short_string() {
+        let short = GermanString::transient(b"meow");
+
+        assert_eq!(short.len(), 4, "'meow' is four characters");
+        assert_eq!(
+            short.as_bytes(),
+            b"meow",
+            "short string returns correct bytes"
+        );
+        assert_eq!(
+            short.as_str().expect("'meow' is valid UTF-8"),
+            "meow",
+            "short string returns correct string"
+        );
+    }
+
+    #[test]
+    fn test_long_string() {
+        let input: &str = "This code was written at https://signal.live";
+        let long = GermanString::transient(input.as_bytes());
+
+        assert_eq!(long.len(), 44, "long string has correct length");
+        assert_eq!(
+            long.as_bytes(),
+            input.as_bytes(),
+            "long string returns correct bytes"
+        );
+
+        assert_eq!(
+            long.as_str().expect("input is valid UTF-8"),
+            input,
+            "long string returns correct string"
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn test_roundtrip_vec(input: Vec<u8>) {
+            let gs = GermanString::transient_from_owned(input.clone());
+            assert_eq!(input.len(), gs.len(), "length should match");
+
+            let out = gs.as_bytes().to_owned();
+            assert_eq!(input, out, "roundtrip should yield same bytes");
+        }
+
+        #[test]
+        fn test_roundtrip_string(input: String) {
+            let gs = GermanString::transient_from_owned(input.clone().into_bytes());
+            assert_eq!(input.len(), gs.len(), "length should match");
+
+            let out = String::from_utf8(gs.as_bytes().to_owned())
+              .expect("string should be valid after roundtrip");
+
+            assert_eq!(input, out, "roundtrip should yield same string");
+        }
+
+        // Test [`Eq`] implementation.
+        #[test]
+        fn test_eq(lhs: Vec<u8>, rhs: Vec<u8>) {
+            let lhs_gs = GermanString::transient(lhs.as_slice());
+            let rhs_gs = GermanString::transient(rhs.as_slice());
+
+            assert_eq!(
+                (lhs == rhs),
+                (lhs_gs == rhs_gs),
+                "Eq should match between std::String and GermanString ({:?} == {:?})",
+                lhs, rhs,
+            );
+        }
+
+        #[test]
+        fn test_reflexivity(x: GermanString) {
+            prop_assert!(x == x);
+        }
+
+        #[test]
+        fn test_symmetry(x: GermanString, y: GermanString) {
+            prop_assert_eq!(x == y, y == x);
+        }
+
+        #[test]
+        fn test_transitivity(x: GermanString, y: GermanString, z: GermanString) {
+            if x == y && y == z {
+                assert!(x == z);
+            }
+        }
+    }
+}
diff --git a/users/tazjin/home/arbat.nix b/users/tazjin/home/arbat.nix
new file mode 100644
index 000000000000..83daf2012ca0
--- /dev/null
+++ b/users/tazjin/home/arbat.nix
@@ -0,0 +1,11 @@
+# Home manage configuration for arbat.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+    depot.users.tazjin.home.persistence
+  ];
+}
diff --git a/users/tazjin/home/shared.nix b/users/tazjin/home/shared.nix
index 38d8add4ac2f..51ff1ac94af5 100644
--- a/users/tazjin/home/shared.nix
+++ b/users/tazjin/home/shared.nix
@@ -5,6 +5,8 @@
 
 
 let
+  inherit (depot.third_party) chicago95;
+
   # URL handler to open `tg://` URLs in telega.el
   telega-launcher = pkgs.writeShellScriptBin "telega-launcher" ''
     echo "Opening ''${1} in telega.el ..."
@@ -38,12 +40,6 @@ in
     '';
   };
 
-  services.screen-locker = {
-    enable = true;
-    inactiveInterval = 10; # minutes
-    lockCmd = "${depot.users.tazjin.screenLock}/bin/tazjin-screen-lock";
-  };
-
   home.packages = [ telega-launcher ];
 
   xdg.desktopEntries.telega-launcher = {
@@ -65,13 +61,40 @@ in
     };
   };
 
-  services.picom = {
+  # put Niri (& related tools) configuration in place
+  xdg.configFile."niri/config.kdl".source = depot.users.tazjin.dotfiles.niri;
+  xdg.configFile."fuzzel/fuzzel.ini".source = depot.users.tazjin.dotfiles.fuzzel;
+
+  programs.wpaperd = {
+    enable = true;
+    settings = {
+      default = {
+        duration = "1d";
+        mode = "center";
+        sorting = "random";
+      };
+
+      any.path = ../wallpapers;
+    };
+  };
+
+  programs.waybar = {
     enable = true;
-    vSync = true;
-    backend = "glx";
+    settings = depot.users.tazjin.dotfiles.waybar.config;
+    style = depot.users.tazjin.dotfiles.waybar.style;
+    systemd.enable = true;
   };
+  systemd.user.services.waybar.Unit.After = lib.mkForce [ "niri.service" ];
 
-  services.syncthing.enable = true;
+
+  services.swayidle = let cmd = "${pkgs.swaylock}/bin/swaylock -fFkl -c 008080"; in {
+    enable = true;
+    events = [
+      { event = "before-sleep"; command = cmd; }
+      { event = "lock"; command = cmd; }
+    ];
+  };
+  systemd.user.services.swayidle.Unit.After = lib.mkForce [ "niri.service" ];
 
   # Enable the dunst notification daemon, but force the
   # configuration file separately instead of going via the strange
@@ -84,6 +107,18 @@ in
     '';
   };
 
+  gtk = {
+    enable = true;
+    theme.name = "Chicago95";
+    theme.package = chicago95;
+
+    iconTheme.name = "Chicago95-tux";
+    iconTheme.package = chicago95;
+
+    cursorTheme.name = lib.mkDefault "Chicago95_Animated_Hourglass_Cursors";
+    cursorTheme.package = chicago95;
+  };
+
   systemd.user.startServices = true;
 
   # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
diff --git a/users/tazjin/home/zamalek.nix b/users/tazjin/home/zamalek.nix
index d24de945bb28..98da5e6b233e 100644
--- a/users/tazjin/home/zamalek.nix
+++ b/users/tazjin/home/zamalek.nix
@@ -8,4 +8,6 @@
     depot.users.tazjin.home.shared
     depot.users.tazjin.home.persistence
   ];
+
+  gtk.cursorTheme.name = lib.mkForce "Chicago95_Animated_Hourglass_Cursors_HiDPI";
 }
diff --git a/users/tazjin/keys/default.nix b/users/tazjin/keys/default.nix
index 16b232b09435..300cd49e89de 100644
--- a/users/tazjin/keys/default.nix
+++ b/users/tazjin/keys/default.nix
@@ -9,4 +9,5 @@ in withAll {
   zamalek_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBRXeb8EuecLHP0bW4zuebXp4KRnXgJTZfeVWXQ1n1R tazjin@zamalek";
   khamovnik_yk = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPgOyR4rRM8IaVGgN2ZxGlKtd7GLYbxdRTRa3u9EhRNSkHAvRTN9sgw7mm0iPLnHChPy10anKV43vTaIm906Gm8=";
   khamovnik_agenix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG4YSl5+DHQR3rOoBJLQfQ840U0CrYkByMKdzu/LDxoT tazjin@khamovnik";
+  arbat = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ1Eai0p7eF7XML5wokqF4GlVZM+YXEORfs/GPGwEky7 tazjin@arbat";
 }
diff --git a/users/tazjin/niri-reap/.gitignore b/users/tazjin/niri-reap/.gitignore
new file mode 100644
index 000000000000..2f7896d1d136
--- /dev/null
+++ b/users/tazjin/niri-reap/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/niri-reap/Cargo.lock b/users/tazjin/niri-reap/Cargo.lock
new file mode 100644
index 000000000000..e7916c5b3acd
--- /dev/null
+++ b/users/tazjin/niri-reap/Cargo.lock
@@ -0,0 +1,104 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "niri-ipc"
+version = "0.1.9"
+source = "git+https://github.com/YaLTeR/niri.git#6a48728ffb1e638839b07f9ab2f06b2adb41dc61"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "niri-reap"
+version = "0.1.0"
+dependencies = [
+ "niri-ipc",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.128"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
diff --git a/users/tazjin/niri-reap/Cargo.toml b/users/tazjin/niri-reap/Cargo.toml
new file mode 100644
index 000000000000..5f6677196333
--- /dev/null
+++ b/users/tazjin/niri-reap/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "niri-reap"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+niri-ipc = { git = "https://github.com/YaLTeR/niri.git", version = "0.1.9" }
diff --git a/users/tazjin/niri-reap/default.nix b/users/tazjin/niri-reap/default.nix
new file mode 100644
index 000000000000..80c82f475a2f
--- /dev/null
+++ b/users/tazjin/niri-reap/default.nix
@@ -0,0 +1,13 @@
+{ depot, pkgs, ... }:
+
+pkgs.rustPlatform.buildRustPackage {
+  name = "niri-reap";
+  src = depot.third_party.gitignoreSource ./.;
+
+  cargoLock = {
+    lockFile = ./Cargo.lock;
+    outputHashes = {
+      "niri-ipc-0.1.9" = "sha256:1s294bw62mmckq9xyfzgw4p2nvkzday4k276j60m668prhlfp071";
+    };
+  };
+}
diff --git a/users/tazjin/niri-reap/src/main.rs b/users/tazjin/niri-reap/src/main.rs
new file mode 100644
index 000000000000..d89b18fc57cf
--- /dev/null
+++ b/users/tazjin/niri-reap/src/main.rs
@@ -0,0 +1,76 @@
+use niri_ipc::socket::Socket;
+use niri_ipc::{Action, Reply, Request, Response, Window, Workspace};
+
+fn sock() -> Socket {
+    Socket::connect().expect("could not connect to Niri socket")
+}
+
+fn list_workspaces() -> Vec<Workspace> {
+    let (reply, _) = sock()
+        .send(Request::Workspaces)
+        .expect("failed to send workspace request");
+
+    match reply {
+        Reply::Err(err) => panic!("failed to list workspaces: {}", err),
+        Reply::Ok(Response::Workspaces(w)) => w,
+        Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
+    }
+}
+
+fn list_windows() -> Vec<Window> {
+    let (reply, _) = sock()
+        .send(Request::Windows)
+        .expect("failed to send window request");
+
+    match reply {
+        Reply::Err(err) => panic!("failed to list windows: {}", err),
+        Reply::Ok(Response::Windows(w)) => w,
+        Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
+    }
+}
+
+fn reap_window(window: u64, workspace: u64) {
+    let (reply, _) = sock()
+        .send(Request::Action(Action::MoveWindowToWorkspace {
+            window_id: Some(window),
+            reference: niri_ipc::WorkspaceReferenceArg::Id(workspace),
+        }))
+        .expect("failed to send window move request");
+
+    reply.expect("failed to move window to workspace");
+}
+
+fn main() {
+    let workspaces = list_workspaces();
+
+    let active_workspace = workspaces
+        .iter()
+        .filter(|w| w.is_focused)
+        .next()
+        .expect("expected an active workspace");
+
+    let orphan_workspaces = workspaces
+        .iter()
+        .filter(|w| w.output == active_workspace.output)
+        // Only select workspaces that are further down, to avoid issues with
+        // indices changing during the operation.
+        .filter(|w| w.idx > active_workspace.idx)
+        .map(|w| w.id)
+        .collect::<Vec<_>>();
+
+    if orphan_workspaces.is_empty() {
+        return;
+    }
+
+    let reapable = list_windows()
+        .into_iter()
+        .filter(|w| match w.workspace_id {
+            Some(id) => orphan_workspaces.contains(&id),
+            None => true,
+        })
+        .collect::<Vec<_>>();
+
+    for window in reapable.iter().rev() {
+        reap_window(window.id, active_workspace.id);
+    }
+}
diff --git a/users/tazjin/nixos/arbat/default.nix b/users/tazjin/nixos/arbat/default.nix
new file mode 100644
index 000000000000..c87aa445c29c
--- /dev/null
+++ b/users/tazjin/nixos/arbat/default.nix
@@ -0,0 +1,72 @@
+# arbat is my Unchartevice 6640MA, with a Zhaoxin CPU.
+{ depot, lib, pkgs, ... }:
+
+config:
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+
+  zdevice = device: {
+    inherit device;
+    fsType = "zfs";
+  };
+in
+{
+  imports = [
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "persistence.nix")
+    (usermod "physical.nix")
+    (pkgs.home-manager.src + "/nixos")
+  ];
+
+  tvl.cache.enable = true;
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    supportedFilesystems = [ "zfs" ];
+    zfs.devNodes = "/dev/";
+    # TODO: double-check this list
+    initrd.availableKernelModules = [ "ahci" "uhci_hcd" "ehci_pci" "xhci_pci" "usb_storage" "sd_mod" "rtsx_usb_sdmmc" ];
+    kernelModules = [ "kvm-intel" ]; # interesting
+  };
+
+  networking = {
+    hostName = "arbat";
+    hostId = "864f050b";
+    networkmanager.enable = true;
+  };
+
+  fileSystems = {
+    "/" = zdevice "zpool/ephemeral/root";
+    "/home" = zdevice "zpool/ephemeral/home";
+    "/persist" = zdevice "zpool/persistent/data" // { neededForBoot = true; };
+    "/nix" = zdevice "zpool/persistent/nix";
+    "/depot" = zdevice "zpool/persistent/depot";
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/B3B5-92F7";
+      fsType = "vfat";
+    };
+  };
+
+  hardware = {
+    enableRedistributableFirmware = true;
+    graphics.enable = true;
+    bluetooth.enable = true;
+  };
+
+  # TODO(tazjin): decide on this
+  services.libinput = {
+    enable = true;
+    # libinput thinks the touchpad is a mouse
+    mouse.naturalScrolling = false;
+    mouse.disableWhileTyping = true;
+  };
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  system.stateVersion = "24.11";
+}
diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix
index 8f82c39ea11f..6bca09d8f129 100644
--- a/users/tazjin/nixos/default.nix
+++ b/users/tazjin/nixos/default.nix
@@ -2,11 +2,14 @@
 
 let systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
 in depot.nix.readTree.drvTargets {
+  arbatSystem = systemFor depot.users.tazjin.nixos.arbat;
   camdenSystem = systemFor depot.users.tazjin.nixos.camden;
-  frogSystem = systemFor depot.users.tazjin.nixos.frog;
   tverskoySystem = systemFor depot.users.tazjin.nixos.tverskoy;
   zamalekSystem = systemFor depot.users.tazjin.nixos.zamalek;
   koptevoRaw = depot.ops.nixos.nixosFor depot.users.tazjin.nixos.koptevo;
   koptevoSystem = systemFor depot.users.tazjin.nixos.koptevo;
   khamovnikSystem = systemFor depot.users.tazjin.nixos.khamovnik;
+
+  # no need to build this while the machine is in storage
+  # frogSystem = systemFor depot.users.tazjin.nixos.frog;
 }
diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix
index dfb6b46d5aa1..ce82d749f408 100644
--- a/users/tazjin/nixos/frog/default.nix
+++ b/users/tazjin/nixos/frog/default.nix
@@ -41,10 +41,9 @@ lib.fix (self: {
   hardware = {
     cpu.amd.updateMicrocode = true;
     enableRedistributableFirmware = true;
-    opengl = {
+    graphics = {
       enable = true;
-      driSupport = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
     };
 
     pulseaudio = {
diff --git a/users/tazjin/nixos/khamovnik/default.nix b/users/tazjin/nixos/khamovnik/default.nix
index 8ea925c90dd0..dcb19be2ae9f 100644
--- a/users/tazjin/nixos/khamovnik/default.nix
+++ b/users/tazjin/nixos/khamovnik/default.nix
@@ -43,6 +43,8 @@ in
       "rtsx_pci_sdmmc"
     ];
     kernelModules = [ "kvm-intel" ];
+
+    tmp.cleanOnBoot = true;
   };
 
   fileSystems = {
@@ -64,12 +66,13 @@ in
   tvl.cache.enable = true;
 
   networking.hostName = "khamovnik";
+  networking.networkmanager.enable = true;
 
   nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
   powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
   hardware.cpu.intel.updateMicrocode = true;
   hardware.enableRedistributableFirmware = true;
-  hardware.opengl.extraPackages = with pkgs; [
+  hardware.graphics.extraPackages = with pkgs; [
     intel-compute-runtime
     intel-media-driver
     intel-vaapi-driver
@@ -104,7 +107,6 @@ in
   };
 
   # Enable sound with pipewire.
-  sound.enable = true;
   hardware.pulseaudio.enable = false;
   security.rtkit.enable = true;
   services.pipewire = {
@@ -117,6 +119,13 @@ in
   # Try to work around Intel CPU throttling bugs
   services.throttled.enable = true;
 
+  # Try to get suspend to work more reliably
+  services.logind = {
+    lidSwitch = "suspend";
+    lidSwitchDocked = "suspend";
+    lidSwitchExternalPower = "suspend";
+  };
+
   virtualisation.docker.enable = true;
 
   hardware.bluetooth.enable = true;
@@ -129,5 +138,7 @@ in
     protobuf
   ];
 
+  programs.adb.enable = true;
+
   system.stateVersion = "23.05"; # Did you read the comment?
 }
diff --git a/users/tazjin/nixos/koptevo/default.nix b/users/tazjin/nixos/koptevo/default.nix
index ea8dfd4bd809..fd88aefec6be 100644
--- a/users/tazjin/nixos/koptevo/default.nix
+++ b/users/tazjin/nixos/koptevo/default.nix
@@ -11,12 +11,12 @@ in
   imports = [
     (mod "quassel.nix")
     (mod "www/base.nix")
-    (mod "www/tazj.in.nix")
     (usermod "airsonic.nix")
     (usermod "geesefs.nix")
+    (usermod "homepage.nix")
+    (usermod "miniflux.nix")
     (usermod "predlozhnik.nix")
     (usermod "tgsa.nix")
-    (usermod "miniflux.nix")
     (depot.third_party.agenix.src + "/modules/age.nix")
   ];
 
@@ -62,7 +62,7 @@ in
     domain = "tazj.in";
     useDHCP = true;
     firewall.enable = true;
-    firewall.allowedTCPPorts = [ 22 80 443 ];
+    firewall.allowedTCPPorts = [ 22 80 443 8776 9443 ];
 
     wireless.enable = true;
     wireless.networks."How do I computer fast?" = {
@@ -72,8 +72,22 @@ in
 
   time.timeZone = "UTC";
 
-  security.acme.acceptTerms = true;
-  security.acme.defaults.email = lib.mkForce "acme@tazj.in";
+  security.acme = {
+    acceptTerms = true;
+    defaults.email = lib.mkForce "acme@tazj.in";
+
+    # wildcard cert for usage with Yggdrasil services
+    certs."y.tazj.in" = {
+      dnsProvider = "yandexcloud";
+      credentialFiles.YANDEX_CLOUD_IAM_TOKEN_FILE = "/run/agenix/lego-yandex";
+      extraDomainNames = [ "*.y.tazj.in" ];
+
+      # folder tvl/tazjin-private/default
+      environmentFile = builtins.toFile "lego-yandex-env" ''
+        YANDEX_CLOUD_FOLDER_ID=b1gq41rsbggeum4qafnh
+      '';
+    };
+  };
 
   programs.fish.enable = true;
 
@@ -84,11 +98,14 @@ in
     openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
   };
 
+  users.users.nginx.extraGroups = [ "acme" ];
+
   age.secrets =
     let
       secretFile = name: depot.users.tazjin.secrets."${name}.age";
     in
     {
+      lego-yandex.file = secretFile "lego-yandex";
       tgsa-yandex.file = secretFile "tgsa-yandex";
     };
 
@@ -101,6 +118,7 @@ in
     acmeHost = "koptevo.tazj.in";
     bindAddresses = [
       "0.0.0.0"
+      "::"
     ];
   };
 
@@ -169,16 +187,119 @@ in
   # List packages installed in system profile. To search, run:
   # $ nix search wget
   environment.systemPackages = with pkgs; [
+    bat
     curl
+    emacs-nox
     htop
     jq
-    nmap
-    bat
-    emacs-nox
     nano
+    nmap
+    radicle-node
     wget
   ];
 
+  # configure Yggdrasil network
+  services.yggdrasil = {
+    enable = true;
+    persistentKeys = true;
+    openMulticastPort = true;
+
+    settings = {
+      Listen = [ "tls://[::]:9443" ]; # yggd
+      IfName = "ygg0";
+      Peers = [
+        "quic://ygg-msk-1.averyan.ru:8364"
+        "tls://ekb.itrus.su:7992"
+        "tls://s-mow-1.sergeysedoy97.ru:65534"
+      ];
+
+      MulticastInterfaces = [{
+        Regex = "enp.*";
+        Beacon = true;
+        Listen = true;
+        Port = 0;
+      }];
+
+      AllowedPublicKeys = [
+        "573fd89392e2741ead4edd85034c91c88f1e560d991bbdbf1fccb6233db4d325" # khamovnik
+        "a56300c3af1ad54840f4b38b9438e3c108a0aa0fd72793dc7d6bd57325c6d691" # zamalek
+        "152b658f8a3e0cd6d1486c3cb984795ec7c9a02274c9f096bd2045cabf8bfa92" # A9
+        "550f4920592d2831d013fd1c83ba9ad174ec352273260fd5d7c2627dbe60d097" # matepad
+      ];
+    };
+  };
+
+  # TODO(tazjin): move this to a module for radicle stuff
+  services.radicle = {
+    enable = true;
+    publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILHs6jSvMdtu9oJCt48etEs8ExjfGY5PmWQsRzFleogS";
+    privateKeyFile = "/etc/secrets/radicle"; # TODO: to manage, or not to manage ...
+
+    settings = {
+      web.pinned.repositories = [
+        "rad:z3r5zMi9U3az3i4cPKxMcA3K7xx9L" # depot
+        "rad:z2mdnBK1tX6pibdBfRct3ThCgheHu" # tvix-go
+      ];
+
+      node = {
+        alias = "rad.tazj.in";
+        seedingPolicy.default = "block";
+      };
+    };
+
+    node = {
+      openFirewall = true;
+      listenAddress = "[::]";
+    };
+
+    httpd = {
+      enable = true;
+      listenAddress = "127.0.0.1";
+      listenPort = 7235; # radl
+    };
+  };
+
+  services.nginx.virtualHosts."rad.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    locations."/".proxyPass = "http://127.0.0.1:7235";
+  };
+
+  services.nginx.virtualHosts."rad.y.tazj.in" = {
+    enableSSL = true;
+    useACMEHost = "y.tazj.in";
+    locations = config.services.nginx.virtualHosts."rad.tazj.in".locations;
+  };
+
+  services.nginx.virtualHosts."src.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    root = depot.third_party.radicle-explorer.withPreferredSeeds [{
+      hostname = "rad.tazj.in";
+      port = 443;
+      scheme = "https";
+    }];
+
+    locations."/" = {
+      index = "index.html";
+      extraConfig = ''
+        try_files $uri $uri/ /index.html;
+      '';
+    };
+  };
+
+  services.nginx.virtualHosts."src.y.tazj.in" = {
+    enableSSL = true;
+    useACMEHost = "y.tazj.in";
+    root = depot.third_party.radicle-explorer.withPreferredSeeds [{
+      hostname = "rad.y.tazj.in";
+      port = 443;
+      scheme = "https";
+    }];
+
+    locations = config.services.nginx.virtualHosts."src.tazj.in".locations;
+  };
+
   programs.mtr.enable = true;
   programs.mosh.enable = true;
   zramSwap.enable = true;
diff --git a/users/tazjin/nixos/modules/desktop.nix b/users/tazjin/nixos/modules/desktop.nix
index 12a42b8faa4b..afd41f0bddf1 100644
--- a/users/tazjin/nixos/modules/desktop.nix
+++ b/users/tazjin/nixos/modules/desktop.nix
@@ -10,43 +10,65 @@
       pulse.enable = true;
     };
 
-    redshift.enable = true;
     blueman.enable = true;
+    libinput.enable = true;
 
     xserver = {
-      enable = true;
-      xkb.layout = "us";
-      xkb.options = "caps:super";
-
-      libinput.enable = true;
-
-      displayManager = {
-        # Give EXWM permission to control the session.
-        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
-        lightdm.enable = true;
-        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
-      };
-
-      windowManager.session = lib.singleton {
-        name = "exwm";
-        start = "${config.tazjin.emacs}/bin/tazjins-emacs --internal-border=0 --border-width=0";
+      enable = true; # wayland doesn't work otherwise ...?!
+      displayManager.gdm = {
+        enable = true;
+        wayland = true;
       };
     };
   };
 
-  # Set variables to enable EXWM-XIM and other Emacs features.
-  environment.sessionVariables = {
-    XMODIFIERS = "@im=exwm-xim";
-    GTK_IM_MODULE = "xim";
-    QT_IM_MODULE = "xim";
-    CLUTTER_IM_MODULE = "xim";
-    EDITOR = "emacsclient";
-    _JAVA_AWT_WM_NONREPARENTING = "1";
-  };
+  services.displayManager.sessionPackages = [ pkgs.niri ];
+
+  programs.xwayland.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    # core packages
+    niri
+    xwayland-satellite
+    swaylock
+
+    # support tooling
+    alacritty
+    fuzzel
+    qt5.qtwayland
+    swayidle
+    waybar
+    wdisplays
+    wl-clipboard
+    wl-mirror
+    xfce.xfce4-appfinder
+    depot.users.tazjin.niri-reap
+  ];
 
   # Do not restart the display manager automatically
   systemd.services.display-manager.restartIfChanged = lib.mkForce false;
 
+  # pipewire MUST start before niri, otherwise screen sharing doesn't work
+  systemd.user.services.pipewire.wantedBy = [ "niri.service" ];
+  systemd.user.services.pipewire.before = [ "niri.service" ];
+
+  # enable "desktop portals", which are important somehow
+  xdg.portal = {
+    enable = true;
+    extraPortals = with pkgs; [
+      xdg-desktop-portal-gtk
+      xdg-desktop-portal-gnome
+    ];
+    config.common.default = "*";
+  };
+
+  # swaylock needs an empty PAM configuration, otherwise it locks the user out
+  security.pam.services.swaylock = { };
+
+  # enable theming support for Qt that is compatible with Chicago95 theme
+  qt.enable = true;
+  qt.platformTheme = "qt5ct";
+
   # If something needs more than 10s to stop it should probably be
   # killed.
   systemd.extraConfig = ''
diff --git a/users/tazjin/nixos/modules/fonts.nix b/users/tazjin/nixos/modules/fonts.nix
index ee1b84e581f1..36b4cbe969ce 100644
--- a/users/tazjin/nixos/modules/fonts.nix
+++ b/users/tazjin/nixos/modules/fonts.nix
@@ -1,15 +1,17 @@
 # Attempt at configuring reasonable font-rendering.
 
-{ pkgs, ... }:
+{ depot, pkgs, ... }:
 
 {
   fonts = {
     packages = with pkgs; [
       corefonts
       dejavu_fonts
+      font-awesome
       jetbrains-mono
       noto-fonts-cjk
-      noto-fonts-emoji
+      noto-fonts-color-emoji
+      noto-fonts-monochrome-emoji
     ];
 
     fontconfig = {
diff --git a/users/tazjin/nixos/modules/geesefs.nix b/users/tazjin/nixos/modules/geesefs.nix
index c45ee528f6a2..60ee821e2fe2 100644
--- a/users/tazjin/nixos/modules/geesefs.nix
+++ b/users/tazjin/nixos/modules/geesefs.nix
@@ -28,7 +28,7 @@
 
       mkdir -p $STATE_DIRECTORY/tazjins-files $STATE_DIRECTORY/cache
 
-      ${depot.third_party.geesefs}/bin/geesefs \
+      ${pkgs.geesefs}/bin/geesefs \
         -f -o allow_other \
         --cache $STATE_DIRECTORY/cache \
         --shared-config $CREDENTIALS_DIRECTORY/geesefs-tazjins-files \
diff --git a/users/tazjin/nixos/modules/home-config.nix b/users/tazjin/nixos/modules/home-config.nix
index bda8f7a44014..9aa1cab46d66 100644
--- a/users/tazjin/nixos/modules/home-config.nix
+++ b/users/tazjin/nixos/modules/home-config.nix
@@ -6,7 +6,7 @@
   users.users.tazjin = {
     isNormalUser = true;
     createHome = true;
-    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
+    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" "yggdrasil" ];
     uid = 1000;
     shell = pkgs.fish;
     initialHashedPassword = "$2b$05$1eBPdoIgan/C/L8JFqIHBuVscQyTKw1L/4VBlzlLvLBEf6CXS3EW6";
@@ -14,6 +14,8 @@
 
   nix.settings.trusted-users = [ "tazjin" ];
 
+  home-manager.backupFileExtension = "backup";
   home-manager.useGlobalPkgs = true;
-  home-manager.users.tazjin = depot.users.tazjin.home."${config.networking.hostName}";
+  home-manager.users.tazjin = with depot.users.tazjin;
+    home."${config.networking.hostName}" or home.shared;
 }
diff --git a/users/tazjin/nixos/modules/homepage.nix b/users/tazjin/nixos/modules/homepage.nix
new file mode 100644
index 000000000000..65191d6e7087
--- /dev/null
+++ b/users/tazjin/nixos/modules/homepage.nix
@@ -0,0 +1,59 @@
+# serve tazjin's website & blog
+{ depot, config, lib, pkgs, ... }:
+
+let
+  extraConfig = ''
+    location = /en/rss.xml {
+      return 301 https://tazj.in/feed.atom;
+    }
+
+    ${depot.users.tazjin.blog.oldRedirects}
+    location /blog/ {
+      alias ${depot.users.tazjin.blog.rendered}/;
+
+      if ($request_uri ~ ^/(.*)\.html$) {
+        return 302 /$1;
+      }
+
+      try_files $uri $uri.html $uri/ =404;
+    }
+
+    location = /predlozhnik {
+      return 302 https://predlozhnik.ru;
+    }
+
+    # redirect for easier entry on a TV
+    location = /tv {
+      return 302 https://tazj.in/blobs/play.html;
+    }
+
+    # Temporary place for serving static files.
+    location /blobs/ {
+      alias /var/lib/tazjins-blobs/;
+    }
+  '';
+in
+{
+  config = {
+    services.nginx.virtualHosts."tazj.in" = {
+      enableACME = true;
+      forceSSL = true;
+      root = depot.users.tazjin.homepage;
+      serverAliases = [ "www.tazj.in" ];
+      inherit extraConfig;
+    };
+
+    services.nginx.virtualHosts."y.tazj.in" = {
+      enableSSL = true;
+      useACMEHost = "y.tazj.in";
+      root = depot.users.tazjin.homepage;
+      inherit extraConfig;
+    };
+
+    services.nginx.virtualHosts."git.tazj.in" = {
+      enableACME = true;
+      forceSSL = true;
+      extraConfig = "return 301 https://code.tvl.fyi$request_uri;";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/physical.nix b/users/tazjin/nixos/modules/physical.nix
index d469da7e5ae0..92b22112d4f4 100644
--- a/users/tazjin/nixos/modules/physical.nix
+++ b/users/tazjin/nixos/modules/physical.nix
@@ -20,11 +20,12 @@ in
     environment.systemPackages =
       # programs from the depot
       (with depot; [
-        users.tazjin.screenLock
-        users.tazjin.chase-geese
         config.tazjin.emacs
         third_party.agenix.cli
         tools.when
+        users.tazjin.chase-geese
+        users.tazjin.eaglemode
+        users.tazjin.screenLock
       ]) ++
 
       # programs from nixpkgs
@@ -45,6 +46,9 @@ in
         gdb
         git
         gnupg
+        go
+        gopls
+        gotools
         gtk3 # for gtk-launch
         htop
         hyperfine
@@ -71,6 +75,7 @@ in
         pulseaudio # for pactl
         pwgen
         quasselClient
+        radicle-node
         rink
         ripgrep
         rustup
@@ -97,6 +102,13 @@ in
     # run manually patchelfed binaries
     environment.stub-ld.enable = false;
 
+    # Enable yggdrasil network.
+    services.yggdrasil = {
+      enable = true;
+      persistentKeys = true;
+      settings.IfName = "ygg0";
+    };
+
     programs = {
       fish.enable = true;
       mosh.enable = true;
diff --git a/users/tazjin/nixos/tverskoy/default.nix b/users/tazjin/nixos/tverskoy/default.nix
index 733929219a3a..c074fd9c5d70 100644
--- a/users/tazjin/nixos/tverskoy/default.nix
+++ b/users/tazjin/nixos/tverskoy/default.nix
@@ -93,9 +93,9 @@ lib.fix (self: {
     enableRedistributableFirmware = true;
     bluetooth.enable = true;
 
-    opengl = {
+    graphics = {
       enable = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
 
       extraPackages = with pkgs; [
         vaapiVdpau
diff --git a/users/tazjin/nixos/zamalek/default.nix b/users/tazjin/nixos/zamalek/default.nix
index a340e8a3e897..ebaf2d2cfa6d 100644
--- a/users/tazjin/nixos/zamalek/default.nix
+++ b/users/tazjin/nixos/zamalek/default.nix
@@ -61,10 +61,6 @@ in
     hostId = "ee399356";
     networkmanager.enable = true;
 
-    extraHosts = ''
-      10.101.240.1 wifi.silja.fi
-    '';
-
     nameservers = [
       "8.8.8.8"
       "8.8.4.4"
@@ -75,14 +71,13 @@ in
     cpu.intel.updateMicrocode = true;
     bluetooth.enable = true;
     enableRedistributableFirmware = true;
-    opengl.enable = true;
+    graphics.enable = true;
   };
 
-  services.xserver.libinput.touchpad.clickMethod = "clickfinger";
-  services.xserver.libinput.touchpad.tapping = false;
+  services.libinput.touchpad.clickMethod = "clickfinger";
+  services.libinput.touchpad.tapping = false;
   services.avahi.enable = true;
   services.tailscale.enable = true;
-  powerManagement.powertop.enable = true;
 
   system.stateVersion = "21.11";
 }
diff --git a/users/tazjin/secrets/lego-yandex.age b/users/tazjin/secrets/lego-yandex.age
new file mode 100644
index 000000000000..10524a9577c2
--- /dev/null
+++ b/users/tazjin/secrets/lego-yandex.age
Binary files differdiff --git a/users/tazjin/secrets/secrets.nix b/users/tazjin/secrets/secrets.nix
index 12f12f721c6c..a29bd30b7766 100644
--- a/users/tazjin/secrets/secrets.nix
+++ b/users/tazjin/secrets/secrets.nix
@@ -13,4 +13,5 @@ in
   "geesefs-tazjins-files.age".publicKeys = allKeys;
   "miniflux.age".publicKeys = allKeys;
   "tgsa-yandex.age".publicKeys = allKeys;
+  "lego-yandex.age".publicKeys = allKeys;
 }
diff --git a/users/tazjin/wallpapers/alphasoft.webp b/users/tazjin/wallpapers/alphasoft.webp
new file mode 100644
index 000000000000..10c404eff0ab
--- /dev/null
+++ b/users/tazjin/wallpapers/alphasoft.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_02_big.webp b/users/tazjin/wallpapers/svema_02_big.webp
new file mode 100644
index 000000000000..5b7f18715c9d
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_02_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_07_big.webp b/users/tazjin/wallpapers/svema_07_big.webp
new file mode 100644
index 000000000000..0706543473e5
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_07_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_09_big.webp b/users/tazjin/wallpapers/svema_09_big.webp
new file mode 100644
index 000000000000..4983efef29b8
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_09_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_14_big.webp b/users/tazjin/wallpapers/svema_14_big.webp
new file mode 100644
index 000000000000..c74542807c29
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_14_big.webp
Binary files differ