Flake for my desktop, laptop, and my homelab.
0

Configure Feed

Select the types of activity you want to include in your feed.

hyprland fix and proton wireguard stuff

Adam0 (Jun 13, 2026, 10:28 PM +0200) 0873d84c 8bf0116e

+126 -10
+1 -1
modules/desktop/hyprland/gestures.nix
··· 17 17 ; 18 18 19 19 # keep-sorted start 20 - noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc call"; 20 + noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc --path ${config.programs.noctalia-shell.package}/share/noctalia-shell call"; 21 21 overzicht = getExe config.programs.overzicht.package; 22 22 # keep-sorted end 23 23
+1 -1
modules/desktop/hyprland/keybinds/brightness.nix
··· 18 18 19 19 cfg = config.hyprland.brightness; 20 20 21 - noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc call"; 21 + noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc --path ${config.programs.noctalia-shell.package}/share/noctalia-shell call"; 22 22 in { 23 23 options.hyprland.brightness.enable = mkEnableOption "function-row brightness keybindings"; 24 24
+1 -1
modules/desktop/hyprland/keybinds/media.nix
··· 9 9 inherit (lib) getExe'; 10 10 inherit (lib.self) mkHylixBindGroup; 11 11 12 - noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc call"; 12 + noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc --path ${config.programs.noctalia-shell.package}/share/noctalia-shell call"; 13 13 in { 14 14 config.programs.hylix.bindGroups = [ 15 15 (mkHylixBindGroup "Media" [
+1 -1
modules/desktop/hyprland/keybinds/shell.nix
··· 9 9 inherit (lib) getExe'; 10 10 inherit (lib.self) mkHylixBindGroup; 11 11 12 - noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc call"; 12 + noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc --path ${config.programs.noctalia-shell.package}/share/noctalia-shell call"; 13 13 in { 14 14 config.programs.hylix.bindGroups = [ 15 15 (mkHylixBindGroup "Shell" [
+1 -1
modules/desktop/hyprland/keybinds/system.nix
··· 16 16 inherit (lib.self) mkHylixBindGroup; 17 17 18 18 # keep-sorted start 19 - noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc call"; 19 + noctalia = "${getExe' config.programs.noctalia-shell.package "noctalia-shell"} ipc --path ${config.programs.noctalia-shell.package}/share/noctalia-shell call"; 20 20 overzicht = "${getExe config.programs.overzicht.package} ipc call"; 21 21 # keep-sorted end 22 22 in {
+1
modules/pkgs/default.nix
··· 15 15 os-age 16 16 performant-mode 17 17 pptx2md-adapter 18 + proton-port-forward 18 19 systemd-status-preview 19 20 telescope-all-recent-nvim 20 21 text-preview
+82
modules/pkgs/proton-port-forward.nix
··· 1 + { 2 + perSystem = {pkgs, ...}: let 3 + inherit (pkgs) writeShellApplication; 4 + in { 5 + packages.proton-port-forward = writeShellApplication { 6 + name = "proton-port-forward"; 7 + runtimeInputs = with pkgs; [ 8 + # keep-sorted start 9 + coreutils 10 + curl 11 + gnused 12 + libnatpmp 13 + nftables 14 + # keep-sorted end 15 + ]; 16 + text = '' 17 + set -eu 18 + 19 + state_dir=/run/proton-wireguard 20 + port_file="$state_dir/forwarded_port" 21 + nft_table=proton-wireguard-port-forward 22 + 23 + mkdir -p "$state_dir" 24 + 25 + cleanup() { 26 + nft delete table ip "$nft_table" 2>/dev/null || true 27 + } 28 + 29 + trap cleanup EXIT INT TERM 30 + 31 + update_qbittorrent() { 32 + port="$1" 33 + proxy_path="$(tr -d '\r\n' < "$CREDENTIALS_DIRECTORY/qui_qbittorrent_proxy_path")" 34 + 35 + case "$proxy_path" in 36 + /*) ;; 37 + *) proxy_path="/$proxy_path" ;; 38 + esac 39 + 40 + curl \ 41 + --fail \ 42 + --silent \ 43 + --show-error \ 44 + --data-urlencode "json={\"listen_port\":$port,\"upnp\":false}" \ 45 + "$QUI_URL$proxy_path/api/v2/app/setPreferences" >/dev/null 46 + } 47 + 48 + while true; do 49 + udp_output=$(natpmpc -a 1 0 udp 60 -g "$PROTON_GATEWAY") 50 + tcp_output=$(natpmpc -a 1 0 tcp 60 -g "$PROTON_GATEWAY") 51 + 52 + udp_port=$(printf '%s\n' "$udp_output" | sed -n 's/.*Mapped public port \([0-9][0-9]*\).*/\1/p;T;q') 53 + tcp_port=$(printf '%s\n' "$tcp_output" | sed -n 's/.*Mapped public port \([0-9][0-9]*\).*/\1/p;T;q') 54 + 55 + if [ -z "$udp_port" ] || [ -z "$tcp_port" ] || [ "$udp_port" != "$tcp_port" ]; then 56 + printf 'failed to get matching Proton forwarded ports\nUDP output:\n%s\nTCP output:\n%s\n' "$udp_output" "$tcp_output" >&2 57 + exit 1 58 + fi 59 + 60 + printf '%s\n' "$udp_port" > "$port_file" 61 + 62 + if ! update_qbittorrent "$udp_port"; then 63 + printf 'failed to update qBittorrent listening port to %s\n' "$udp_port" >&2 64 + fi 65 + 66 + nft delete table ip "$nft_table" 2>/dev/null || true 67 + nft -f - <<EOF 68 + table ip $nft_table { 69 + chain prerouting { 70 + type nat hook prerouting priority dstnat; policy accept; 71 + iifname "$WIREGUARD_INTERFACE" tcp dport $udp_port dnat to $QBITTORRENT_IPV4:$udp_port 72 + iifname "$WIREGUARD_INTERFACE" udp dport $udp_port dnat to $QBITTORRENT_IPV4:$udp_port 73 + } 74 + } 75 + EOF 76 + 77 + sleep 45 78 + done 79 + ''; 80 + }; 81 + }; 82 + }
+32
modules/services/proton-wireguard.nix
··· 10 10 inherit (lib) concatMapStringsSep; 11 11 12 12 interface = "proton0"; 13 + protonGateway = "10.2.0.1"; 14 + qbittorrentIPv4 = "10.89.50.11"; 15 + quiIPv4 = "10.89.51.12"; 16 + quiUrl = "http://${quiIPv4}:7476"; 13 17 routingPriority = 1000; 14 18 routingTable = "51820"; 15 19 containerIPv4Subnets = ["10.89.50.0/24"]; ··· 40 44 "wireguard/proton/endpoint" = {}; 41 45 "wireguard/proton/private_key" = {}; 42 46 "wireguard/proton/public_key" = {}; 47 + qbittorrent_proxy_path = {}; 43 48 # keep-sorted end 44 49 }; 45 50 ··· 67 72 after = ["sops-install-secrets.service"]; 68 73 wants = ["sops-install-secrets.service"]; 69 74 # keep-sorted end 75 + }; 76 + 77 + systemd.services.proton-port-forward = { 78 + after = [ 79 + "nftables.service" 80 + "sops-install-secrets.service" 81 + "wg-quick-${interface}.service" 82 + ]; 83 + description = "Maintain Proton VPN port forwarding"; 84 + environment = { 85 + PROTON_GATEWAY = protonGateway; 86 + QBITTORRENT_IPV4 = qbittorrentIPv4; 87 + QUI_URL = quiUrl; 88 + WIREGUARD_INTERFACE = interface; 89 + }; 90 + wantedBy = ["multi-user.target"]; 91 + wants = [ 92 + "sops-install-secrets.service" 93 + "wg-quick-${interface}.service" 94 + ]; 95 + 96 + serviceConfig = { 97 + ExecStart = "${pkgs.proton-port-forward}/bin/proton-port-forward"; 98 + LoadCredential = ["qui_qbittorrent_proxy_path:${config.sops.secrets.qbittorrent_proxy_path.path}"]; 99 + Restart = "always"; 100 + RestartSec = "5s"; 101 + }; 70 102 }; 71 103 72 104 networking = {
+6 -5
secrets.yaml
··· 63 63 admin_password: ENC[AES256_GCM,data:egySL8SuW3W+jWW0Rd2a3MvUkYrl6MmT3SfonnPOTNg6B+W90yzrKoqyilEh8MMVIDn5qhRPdtSIp8MGwi9wZQ==,iv:d4AL+Lc8kxk+9zKHAyPIRFigkCZeVk0WtoYbrGB7j+Q=,tag:aEFZKufG0ZXHxvmxJHeFVQ==,type:str] 64 64 wireguard: 65 65 proton: 66 - private_key: ENC[AES256_GCM,data:N5pZv8yNIJugZuFg2/xtHBqof+aa+GBdoVLAKtaKLwVbH05dBrx/Ed3RhHc=,iv:5ojYtEyeLbCBzdp+R4P60s0tRrZNS/PJ7ujUNkzU+Lk=,tag:IgacOi9oakwOH4LuuI2oXQ==,type:str] 66 + private_key: ENC[AES256_GCM,data:mu/ATCGbvaJ7HyE7vT3TH7zAcP0pKMFhAGI1PWqBPZWpSSywmJFK2qxPtPw=,iv:e8m7KE/VyLlQoNRWAFZvuiFdlUszrTbEBOK3J6+MsYk=,tag:dsLF/O+Kfv1T7QtsrlYWIQ==,type:str] 67 67 address: ENC[AES256_GCM,data:QirdDhl1RAuyl6s=,iv:TG6v0Kqx9y90w09Rnse3XIyDsmGnqsrLtWR19Cz2F6g=,tag:AtXkSTzo3DJ9eWngEOtzxA==,type:str] 68 68 dns: ENC[AES256_GCM,data:rcXede6T+n4=,iv:meUFADObHb774NWFBWvwezP1ue6ajmrVl5C+whaJtpI=,tag:nnoW+Dwh2qboyONLMyNxYg==,type:str] 69 - public_key: ENC[AES256_GCM,data:LRUGaeDP1wjnQkl5f/tynkjFtVaZzacesCYdYeE/nemZnOcCsZlw0hOd3eU=,iv:IqHbyj8mPbUNep2rpYw2eiKYfsNcN2unOucWEyD+IqI=,tag:YcDN494yEXDylnaQ1YOs7Q==,type:str] 69 + public_key: ENC[AES256_GCM,data:NgGtMiobS32NvEHc9c1Ngk75H9HbEvPKkxMxwdSTIlyAL5HGEfSV+n02hdw=,iv:1Ci6jBjcfYZZ86qtrPtx4f4W1ynzTkeF7M7g7HyyRY8=,tag:0OghdJoTf0gx/0HjArTIGg==,type:str] 70 70 allowed_ips: ENC[AES256_GCM,data:9kOkZno3oiOKoykVoDBB,iv:IpBDkg8HbB1pzpn0mpuXkIgrZz8Sf30BGvyIRSEaqW8=,tag:wIytxSUSNHUFrmVeGGJQtQ==,type:str] 71 - endpoint: ENC[AES256_GCM,data:dFMJ96NMcBMPjYIsQ5sNSBS2VEg=,iv:ZEX9mJzqjZJWiz6L7+bMsLaZvFkr5jCN4CK7RicwLRA=,tag:22SSWQkEMEJ2zoQzwLPUQQ==,type:str] 71 + endpoint: ENC[AES256_GCM,data:dyF/WM2Xlz05HnJgkYSNKWhS6Q==,iv:Qv/Q3ef+eKPQ4+Gf9BwrCp7cj6mQiyCAnk1iMXHPbrs=,tag:0CjXKCxR0vY7wV5uFPEZqQ==,type:str] 72 72 euclid: 73 73 private_key: ENC[AES256_GCM,data:dGh0qAiYgloX6EoV0Q72UP/faOpeoPqXUaxkN8lKq6KQbxycRqBHapPo+5s=,iv:jb/sliGbNK2r7yWoaL7u6RTUnbMTH6+wFJgq/LCxucE=,tag:mdfz5/aRkozRB8lRRPtd2Q==,type:str] 74 74 gotify: 75 75 client_id: ENC[AES256_GCM,data:sWHLVejwqjtjFhjAyT0dK+s4bdyXUUC8jh+u4uWu+kwgU5tDS21qUw==,iv:atw/QxQYZ38j30IdA4Osrba1wDb5YVomrPojoWd/82g=,tag:7d600/i1rzp4y7WO6M/YRw==,type:str] 76 76 client_secret: ENC[AES256_GCM,data:6PhFHPRcrUD1tPySDiB4tAtTng4/lYfNZ7XGYvIxxkbDobH9q3pjtuvXkz4MvGyjtC+M4sxc8qH1or7AYFtb/u71aptIwt+u821jz9XiFrQJiM7RC9yTZt0RQLtpPkTejRg4jfBXhDt+xOVkOZG7Z7cMrsMIS6eYDUJhpOXUO/c=,iv:6IseU28YQFyTmTh3dEUeK2pfvVq5CFIpXXYDkuY4HVY=,tag:NRZIDm07RhgDsEXT8IGl4Q==,type:str] 77 77 desktop_token: ENC[AES256_GCM,data:LsEQ+gXlyXpl7BF6d+S1okv9t4OlMCk=,iv:Gq+Z5OiQR4cKy7+hM5JswAVtol+WX7nb9a7pNgmW5ZA=,tag:wz64fqoNyKmpGnIrjHt1mA==,type:str] 78 + qbittorrent_proxy_path: ENC[AES256_GCM,data:FGSBt2MQZ8Flq0y1VG0OYlrlKzweSaaiB+xKSWFObJXX2q/oNODo/7dPL8r29Fb79GKV7n5hXpQDLzSYOwd0A0EPijj/SAk=,iv:yJqKvC2L2O0eOTKvO/cAX1ldEx8z8GTvG6cbjdiXN+Q=,tag:cpFgkJoVXgfiTrLVS/KTdA==,type:str] 78 79 sops: 79 80 age: 80 81 - enc: | ··· 113 114 w2ToACYVigKMSlqPNG9bKcK75XI/iAONG48DQm1Nse3yB9/q3jwd+Q== 114 115 -----END AGE ENCRYPTED FILE----- 115 116 recipient: age1yl52c6aemw9anmfuqayamvsnkvlu6fmy0kk3gzvrraexxd9pr90qvs8pe5 116 - lastmodified: "2026-06-13T13:28:32Z" 117 - mac: ENC[AES256_GCM,data:bGA/A2FYF8p1YqDTByBX/L4W3eoGRqhTVy55ia5IT4/PV3hwz1vjc1nwNJJ47INpcfp/E8fXWkY1SXg45zL3cNmsUuc82n5QsTBOI2k5e+okiRn1OPpzLbyAa7H2v5uS35lm2Y1JTZcHbsiIGPIs6zLyppP3d30N3OG0Mm4fsHc=,iv:wJ987VunBxZtqrwbypS4qjbGLN+7xu0ecp51p7jYyns=,tag:tkpy9O5kVPQCVgX01MX0Tg==,type:str] 117 + lastmodified: "2026-06-13T20:19:37Z" 118 + mac: ENC[AES256_GCM,data:H+QA1jT/roe0D10P30dpgyYCD0XgJgBvYsvwieh6svxI1xEoFu0ckWxLMSmjiESCP2uFkmyeKNRV0exiiwTAG7NDLsqV5i3z/swMwvbGY38ZdIDnOE0Gs3M2TdcOOldmNmxCfhrwuLCGW69DNU7HFrTcqpAsL9lp3LeabpVQlCs=,iv:6vushk/VM+9EIijROmhz4nWZ/At6EGejEnbSJsNBcuk=,tag:GMlo349urMUJpowxpoNZfA==,type:str] 118 119 pgp: 119 120 - created_at: "2026-06-02T01:50:38Z" 120 121 enc: |-