aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorspl3g <notspl3g@duck.com>2026-03-18 18:01:41 +0300
committerspl3g <notspl3g@duck.com>2026-03-18 18:01:59 +0300
commit03648b3d9f177227df40129bed22558f6924b91c (patch)
tree8a22eda142beeafd9002a8d5901ba9428a77ad52 /modules
parentdc19a2b583b3ab50d8e36ff0a90ca633495f675f (diff)
so.. v2 i guess
Diffstat (limited to 'modules')
-rwxr-xr-xmodules/.direnv/bin/nix-direnv-reload19
-rw-r--r--modules/deploy.nix49
-rw-r--r--modules/flake-parts.nix16
-rw-r--r--modules/home-manager/default.nix7
-rw-r--r--modules/homeModules/alacritty.nix29
-rw-r--r--modules/homeModules/attachments/basecat.asepritebin0 -> 730 bytes
-rw-r--r--modules/homeModules/attachments/basecat.pngbin0 -> 11503 bytes
-rw-r--r--modules/homeModules/attachments/cat.pngbin0 -> 15467 bytes
-rwxr-xr-xmodules/homeModules/attachments/hypr-scripts/bitwarden-float.sh21
-rwxr-xr-xmodules/homeModules/attachments/hypr-scripts/hshot.sh45
-rwxr-xr-xmodules/homeModules/attachments/hypr-scripts/switch-sink.py49
-rwxr-xr-xmodules/homeModules/attachments/hypr-scripts/toggle-tg.sh14
-rwxr-xr-xmodules/homeModules/attachments/hypr-scripts/toggle-vpn.sh93
-rw-r--r--modules/homeModules/attachments/rofi-theme.rasi79
-rw-r--r--modules/homeModules/attachments/waybar-style.css30
-rw-r--r--modules/homeModules/bspwm.nix68
-rw-r--r--modules/homeModules/dunst.nix25
-rw-r--r--modules/homeModules/emacs/aliases4
-rw-r--r--modules/homeModules/emacs/default.nix49
-rw-r--r--modules/homeModules/emacs/early-init.el171
-rw-r--r--modules/homeModules/emacs/elpaca.el46
-rw-r--r--modules/homeModules/emacs/init.el848
-rw-r--r--modules/homeModules/emacs/templates.eld18
-rw-r--r--modules/homeModules/exwm.nix71
-rw-r--r--modules/homeModules/firefox.nix111
-rw-r--r--modules/homeModules/fish.nix42
-rw-r--r--modules/homeModules/fuzzel.nix33
-rw-r--r--modules/homeModules/hyprland.nix236
-rw-r--r--modules/homeModules/mako.nix24
-rw-r--r--modules/homeModules/niri.nix264
-rw-r--r--modules/homeModules/nvim.nix12
-rw-r--r--modules/homeModules/picom.nix57
-rw-r--r--modules/homeModules/rofi.nix50
-rw-r--r--modules/homeModules/stylix.nix80
-rw-r--r--modules/homeModules/sxhkd.nix45
-rw-r--r--modules/homeModules/tmux.nix50
-rw-r--r--modules/homeModules/waybar.nix131
-rw-r--r--modules/homeModules/zen-browser.nix151
-rw-r--r--modules/hosts/ltrr-block/age.nix10
-rw-r--r--modules/hosts/ltrr-block/configuration.nix467
-rw-r--r--modules/hosts/ltrr-block/disk-config.nix39
-rw-r--r--modules/hosts/ltrr-block/hardware-configuration.nix32
-rw-r--r--modules/hosts/ltrr-block/secrets/cwa.env.agebin0 -> 821 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/explo.env.agebin0 -> 3721 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/navidrome.env.agebin0 -> 351 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/1c8822a2f185737c765ae9a5ce0d3879-soularr-config.agebin0 -> 2790 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/550a141b44c76106807b541c1987996b-wg-priv-key.age8
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/9288d02fd4269798567444d076247538-explo-env.agebin0 -> 3760 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/939083f61f3167ef5aff27bdba700e8e-xray-config.agebin0 -> 1750 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/d5f4d0c5c7b3217d008be68e8ad757e8-navidrome-env.age7
-rw-r--r--modules/hosts/ltrr-block/secrets/rekeyed/e9669da1b38fb37ba09edf8fdeafc4de-slskd-env.agebin0 -> 507 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/slskd.env.agebin0 -> 491 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/soularr.conf.agebin0 -> 2711 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/transmission-settings.json.age8
-rw-r--r--modules/hosts/ltrr-block/secrets/wg-priv.key.agebin0 -> 294 bytes
-rw-r--r--modules/hosts/ltrr-block/secrets/xray.json.agebin0 -> 1752 bytes
-rw-r--r--modules/hosts/ltrr-cloud/age.nix10
-rw-r--r--modules/hosts/ltrr-cloud/configuration.nix345
-rw-r--r--modules/hosts/ltrr-cloud/disk-config.nix56
-rw-r--r--modules/hosts/ltrr-cloud/secrets/authelia-jwt.key.agebin0 -> 473 bytes
-rw-r--r--modules/hosts/ltrr-cloud/secrets/authelia-storage.key.agebin0 -> 464 bytes
-rw-r--r--modules/hosts/ltrr-cloud/secrets/rekeyed/97c2df6cc789b9e8ced5811bfa43d3f8-authelia-jwt.age8
-rw-r--r--modules/hosts/ltrr-cloud/secrets/rekeyed/98c1b723eb9ef4334c5a90c456a33743-wg-priv-key.age7
-rw-r--r--modules/hosts/ltrr-cloud/secrets/rekeyed/ab2826e18d1b8ee845f01ac87f5dd6ea-authelia-storage.age8
-rw-r--r--modules/hosts/ltrr-cloud/secrets/wg-priv.key.agebin0 -> 392 bytes
-rw-r--r--modules/hosts/ltrr-mask/configuration.nix125
-rw-r--r--modules/hosts/ltrr-mask/disk-config.nix56
-rw-r--r--modules/nixos/default.nix7
-rw-r--r--modules/nixosModules/booklore.nix176
-rw-r--r--modules/nixosModules/directories.nix90
-rw-r--r--modules/nixosModules/gonic.nix114
-rw-r--r--modules/nixosModules/nfs.nix118
-rw-r--r--modules/nixosModules/nginxProxy.nix217
-rw-r--r--modules/nixosModules/watcharr.nix74
-rw-r--r--modules/overlays.nix30
-rw-r--r--modules/pkgs/explo/default.nix28
-rw-r--r--modules/shell.nix14
77 files changed, 5077 insertions, 14 deletions
diff --git a/modules/.direnv/bin/nix-direnv-reload b/modules/.direnv/bin/nix-direnv-reload
new file mode 100755
index 0000000..a5a6d73
--- /dev/null
+++ b/modules/.direnv/bin/nix-direnv-reload
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+set -e
+if [[ ! -d "/home/jerpo/nixfiles-v2/modules" ]]; then
+ echo "Cannot find source directory; Did you move it?"
+ echo "(Looking for "/home/jerpo/nixfiles-v2/modules")"
+ echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
+ exit 1
+fi
+
+# rebuild the cache forcefully
+_nix_direnv_force_reload=1 direnv exec "/home/jerpo/nixfiles-v2/modules" true
+
+# Update the mtime for .envrc.
+# This will cause direnv to reload again - but without re-building.
+touch "/home/jerpo/nixfiles-v2/modules/.envrc"
+
+# Also update the timestamp of whatever profile_rc we have.
+# This makes sure that we know we are up to date.
+touch -r "/home/jerpo/nixfiles-v2/modules/.envrc" "/home/jerpo/nixfiles-v2/modules/.direnv"/*.rc
diff --git a/modules/deploy.nix b/modules/deploy.nix
new file mode 100644
index 0000000..14e3315
--- /dev/null
+++ b/modules/deploy.nix
@@ -0,0 +1,49 @@
+{
+ inputs,
+ self,
+ withSystem,
+ config,
+ ...
+}: {
+ perSystem = {
+ pkgs,
+ self',
+ system,
+ ...
+ }: let
+ deployPkgs = import inputs.nixpkgs {
+ inherit system;
+ overlays = [
+ inputs.deploy-rs.overlays.default
+ (self: super: {
+ deploy-rs = {
+ inherit (pkgs) deploy-rs;
+ lib = super.deploy-rs.lib;
+ };
+ })
+ ];
+ };
+ in {
+ _module.args = {
+ inherit deployPkgs;
+ };
+ };
+ flake.deploy.nodes = {
+ ltrr-block = {
+ hostname = "ltrr-block";
+ profiles.system = {
+ user = "root";
+ path = withSystem "x86_64-linux" ({deployPkgs, ...}: deployPkgs.deploy-rs.lib.activate.nixos self.nixosConfigurations.ltrr-block);
+ };
+ sshUser = "root";
+ };
+ ltrr-cloud = {
+ hostname = "kcu.su";
+ profiles.system = {
+ user = "root";
+ path = withSystem "x86_64-linux" ({deployPkgs, ...}: deployPkgs.deploy-rs.lib.activate.nixos self.nixosConfigurations.ltrr-cloud);
+ };
+ sshUser = "root";
+ };
+ };
+}
diff --git a/modules/flake-parts.nix b/modules/flake-parts.nix
new file mode 100644
index 0000000..2a7df32
--- /dev/null
+++ b/modules/flake-parts.nix
@@ -0,0 +1,16 @@
+{inputs, ...}: {
+ imports = [
+ inputs.home-manager.flakeModules.home-manager
+ inputs.disko.flakeModules.default
+ inputs.agenix-rekey.flakeModule
+ ];
+
+ config = {
+ systems = [
+ "aarch64-darwin"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "x86_64-linux"
+ ];
+ };
+}
diff --git a/modules/home-manager/default.nix b/modules/home-manager/default.nix
deleted file mode 100644
index 753ad58..0000000
--- a/modules/home-manager/default.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-# Add your reusable home-manager modules to this directory, on their own file (https://nixos.wiki/wiki/Module).
-# These should be stuff you would like to share with others, not your personal configurations.
-{ inputs, ... }:
-{
- # List your module files here
- # my-module = import ./my-module.nix;
-}
diff --git a/modules/homeModules/alacritty.nix b/modules/homeModules/alacritty.nix
new file mode 100644
index 0000000..3fcce6c
--- /dev/null
+++ b/modules/homeModules/alacritty.nix
@@ -0,0 +1,29 @@
+{inputs, ...}: {
+ flake.homeModules.alacritty = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ alacritty.enable = lib.mkEnableOption "enable alacritty";
+ };
+
+ config = lib.mkIf config.alacritty.enable {
+ programs.alacritty = {
+ enable = true;
+ settings = {
+ cursor = {
+ style = "Beam";
+ thickness = 0.25;
+ };
+ window = {
+ padding = {
+ x = 10;
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/attachments/basecat.aseprite b/modules/homeModules/attachments/basecat.aseprite
new file mode 100644
index 0000000..16caffd
--- /dev/null
+++ b/modules/homeModules/attachments/basecat.aseprite
Binary files differ
diff --git a/modules/homeModules/attachments/basecat.png b/modules/homeModules/attachments/basecat.png
new file mode 100644
index 0000000..d202c64
--- /dev/null
+++ b/modules/homeModules/attachments/basecat.png
Binary files differ
diff --git a/modules/homeModules/attachments/cat.png b/modules/homeModules/attachments/cat.png
new file mode 100644
index 0000000..5657a78
--- /dev/null
+++ b/modules/homeModules/attachments/cat.png
Binary files differ
diff --git a/modules/homeModules/attachments/hypr-scripts/bitwarden-float.sh b/modules/homeModules/attachments/hypr-scripts/bitwarden-float.sh
new file mode 100755
index 0000000..7edd5f2
--- /dev/null
+++ b/modules/homeModules/attachments/hypr-scripts/bitwarden-float.sh
@@ -0,0 +1,21 @@
+windowtitlev2() {
+ IFS=',' read -r -a args <<< "$1"
+ args[0]="${args[0]#*>>}"
+
+ if [[ ${args[1]} =~ "Extension: (Bitwarden Password Manager)" ]]; then
+ hyprctl --batch "\
+ dispatch setfloating address:0x${args[0]}; \
+ dispatch resizewindowpixel exact 20% 50%, address:0x${args[0]}; \
+ dispatch centerwindow; \
+ "
+ fi
+}
+
+handle() {
+ case $1 in
+ windowtitlev2\>*) windowtitlev2 "$1" ;;
+ esac
+}
+
+socat -U - UNIX-CONNECT:"/$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" \
+ | while read -r line; do handle "$line"; done
diff --git a/modules/homeModules/attachments/hypr-scripts/hshot.sh b/modules/homeModules/attachments/hypr-scripts/hshot.sh
new file mode 100755
index 0000000..0d02b9c
--- /dev/null
+++ b/modules/homeModules/attachments/hypr-scripts/hshot.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+declare -a cmd
+
+usage() {
+ echo -e "-m | monitor\n-s | slurp\n-w | active window\n-c | add copy"
+}
+
+monitor() {
+ cmd=("grim -o \"\$(hyprctl -j monitors | jq -r '.[] | select(.focused) | .name')\"")
+}
+
+slurp() {
+ cmd=("grim -g \"\$(slurp)\"")
+}
+
+window() {
+ cmd=("grim -g \"\$(hyprctl activewindow -j | jq -j '\"\(.at | .[0]),\(.at | .[1]) \(.size | .[0])x\(.size | .[1])\"')\"")
+}
+
+copy() {
+ if [[ -n ${cmd[0]} ]]; then
+ cmd+=("- | wl-copy")
+ else
+ usage
+ fi
+}
+
+while getopts ":mswc" opt; do
+ case ${opt} in
+ m) monitor;;
+ s) slurp;;
+ w) window;;
+ c) copy;;
+ *) usage
+ exit 1;;
+ esac
+done
+
+if [[ -z $1 ]]; then
+ usage
+ exit 1
+fi
+
+bash -c "${cmd[*]}"
diff --git a/modules/homeModules/attachments/hypr-scripts/switch-sink.py b/modules/homeModules/attachments/hypr-scripts/switch-sink.py
new file mode 100755
index 0000000..aa7eec1
--- /dev/null
+++ b/modules/homeModules/attachments/hypr-scripts/switch-sink.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+import subprocess
+
+status = subprocess.run(["wpctl", "status"], stdout=subprocess.PIPE).stdout.decode('utf-8')
+status_lines = status.split('\n')
+sink_line = 0
+sinks = []
+sink_names = []
+
+# Get sink line
+for i in range(len(status_lines) - 1):
+ if 'Sinks:' in status_lines[i]:
+ sink_line = i + 1
+ break
+
+# Find unused sinks
+for line in status_lines[sink_line:]:
+ if line == ' │ ':
+ break
+ line = line.split('.')
+ nums = line[0]
+ name = line[1]
+ end_name = line[1].index('[')
+ sink = nums[2:].strip()
+ if nums[4] == '*':
+ continue
+
+ sinks.append(sink)
+ sink_names.append(name[:end_name].strip())
+
+if (len(sinks) == 0):
+ subprocess.run(["notify-send", "Pipewire sink", "No sinks to switch to"])
+ exit(0)
+
+selected_sink = subprocess.run(["rofi", "-dmenu"], input="\n".join(sink_names), capture_output=True, text=True).stdout[:-1]
+
+if len(selected_sink) == 0:
+ exit(0)
+
+try:
+ next_sink = sink_names.index(selected_sink)
+except Exception:
+ subprocess.run(["notify-send", "Pipewire sink", "Sink not found"])
+ exit(1)
+
+subprocess.run(["wpctl", "set-default", sinks[next_sink]])
+
+subprocess.run(["notify-send", "Pipewire sink", "Selected " + selected_sink])
diff --git a/modules/homeModules/attachments/hypr-scripts/toggle-tg.sh b/modules/homeModules/attachments/hypr-scripts/toggle-tg.sh
new file mode 100755
index 0000000..44ed52d
--- /dev/null
+++ b/modules/homeModules/attachments/hypr-scripts/toggle-tg.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+TG_CLASS=org.telegram.desktop
+
+tg_workspace=$(hyprctl clients -j | jq -e ".[] | select((.class | contains(\"${TG_CLASS}\"))) | .workspace.id")
+if [[ -z $tg_workspace ]]; then
+ telegram-desktop
+elif [[ $tg_workspace -eq $(hyprctl activeworkspace -j | jq -e '.id') ]]; then
+ hyprctl dispatch pin class:$TG_CLASS
+ hyprctl dispatch movetoworkspacesilent special:magic,class:$TG_CLASS
+else
+ hyprctl dispatch movetoworkspacesilent +0,class:$TG_CLASS
+ hyprctl dispatch pin class:$TG_CLASS
+ hyprctl dispatch focuswindow
+fi
diff --git a/modules/homeModules/attachments/hypr-scripts/toggle-vpn.sh b/modules/homeModules/attachments/hypr-scripts/toggle-vpn.sh
new file mode 100755
index 0000000..8775f39
--- /dev/null
+++ b/modules/homeModules/attachments/hypr-scripts/toggle-vpn.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+ROFI_CMD="rofi -dmenu -theme-str listview{enabled:false;} -p"
+LOCAL_STORAGE=~/.local/share/toggle
+TMP_PATH=/tmp/vpn-status
+V2RAYA_URL="http://localhost:2017"
+
+DBUS_INTERFACE="com.vpn_status"
+DBUS_MEMBER="StatusChanged"
+
+set_token() {
+ login=$(echo "" | $ROFI_CMD "Enter login > ")
+ password=$(echo "" | $ROFI_CMD "Enter password > " -theme-str 'entry {enabled: false;}')
+ response=$(curl -s -X POST \
+ "${V2RAYA_URL}/api/login" \
+ -d "{\"username\": \"${login}\", \"password\": \"${password}\"}")
+
+ code=$(echo $response | jq -r ".code")
+ echo "${response}" | jq ".data.token" -r > "${LOCAL_STORAGE}/token"
+}
+
+get_status() {
+ token=$1
+ response=$(curl -s -X GET \
+ "${V2RAYA_URL}/api/touch" \
+ -H "Authorization: ${token}")
+ echo $response | jq ".data.running" -r
+}
+
+toggle() {
+ token=$1
+ method=$2
+ response=$(curl -s -X ${method} \
+ "${V2RAYA_URL}/api/v2ray" \
+ -H "Authorization: ${token}")
+ code=$(echo $response | jq ".code" -r)
+ echo $response | jq ".data.running" -r
+}
+
+check_status() {
+ case $(cat $TMP_PATH) in
+ true)
+ output='{"text": "󰠥"}'
+ ;;
+ *)
+ output='{"text": ""}'
+ ;;
+ esac
+ echo $output | jq --unbuffered --compact-output
+}
+
+
+if [[ ! -d "${LOCAL_STORAGE}" ]]; then
+ mkdir "${LOCAL_STORAGE}"
+fi
+
+if [[ ! -e "${LOCAL_STORAGE}/token" ]]; then
+ touch "${LOCAL_STORAGE}/token"
+fi
+
+TOKEN=$(cat "${LOCAL_STORAGE}/token")
+if [[ -z "${TOKEN}" ]]; then
+ set_token
+ TOKEN=$(cat "${LOCAL_STORAGE}/token")
+fi
+
+STATUS=$(get_status $TOKEN)
+
+if [[ $1 == "waybar" ]]; then
+ echo $STATUS > $TMP_PATH
+ check_status
+
+ dbus-monitor --profile "interface='${DBUS_INTERFACE}',member='${DBUS_MEMBER}'" |
+ while read -r line; do
+ check_status
+ done
+else
+ if [[ $STATUS == "true" ]]; then
+ NEW_STATUS=$(toggle $TOKEN DELETE)
+ else
+ NEW_STATUS=$(toggle $TOKEN POST)
+ fi
+
+ if [[ $NEW_STATUS == "null" ]]; then
+ set_token
+ exit 0
+ fi
+
+ echo $NEW_STATUS > $TMP_PATH
+ dbus-send --type=signal / "${DBUS_INTERFACE}.${DBUS_MEMBER}"
+
+ notify-send v2rayA "running: ${NEW_STATUS}"
+fi
diff --git a/modules/homeModules/attachments/rofi-theme.rasi b/modules/homeModules/attachments/rofi-theme.rasi
new file mode 100644
index 0000000..bba4f6c
--- /dev/null
+++ b/modules/homeModules/attachments/rofi-theme.rasi
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * SNITCHED FROM:
+ * User : LR-Tech
+ * Theme Repo : https://github.com/lr-tech/rofi-themes-collection
+ *******************************************************************************/
+
+window {
+ location: center;
+ width: 480;
+ y-offset: -160;
+ border-radius: 7px;
+
+ background-color: @bg0;
+}
+
+inputbar {
+ spacing: 8px;
+ padding: 8px;
+
+ background-color: @bg1;
+}
+
+prompt, entry, element-icon, element-text {
+ vertical-align: 0.5;
+}
+
+prompt {
+ text-color: @accent-color;
+}
+
+textbox {
+ padding: 8px;
+ background-color: @bg1;
+}
+
+listview {
+ padding: 4px 0;
+ lines: 8;
+ columns: 1;
+
+ fixed-height: false;
+}
+
+element {
+ padding: 8px;
+ spacing: 8px;
+}
+
+element normal normal {
+ text-color: @fg0;
+}
+
+element normal urgent {
+ text-color: @urgent-color;
+}
+
+element normal active {
+ text-color: @accent-color;
+}
+
+element selected {
+ text-color: @bg0;
+}
+
+element selected normal, element selected active {
+ background-color: @accent-color;
+}
+
+element selected urgent {
+ background-color: @urgent-color;
+}
+
+element-icon {
+ size: 0.8em;
+}
+
+element-text {
+ text-color: inherit;
+}
diff --git a/modules/homeModules/attachments/waybar-style.css b/modules/homeModules/attachments/waybar-style.css
new file mode 100644
index 0000000..214c779
--- /dev/null
+++ b/modules/homeModules/attachments/waybar-style.css
@@ -0,0 +1,30 @@
+* {
+ font-family: Material Design Icons, Rubik Medium;
+ font-size: 14px;
+ color: @base05;
+}
+
+window#waybar {
+ background: @base01;
+ border-bottom: 3px solid @base02;
+}
+
+#battery {
+ margin-right: 6px;
+}
+
+#workspaces button label {
+ font-size: 15px;
+ color: @base05;
+ transition: all 100ms ease-out;
+}
+
+#workspaces button.active label {
+ font-weight: bolder;
+ color: @base0A;
+ transition: all 100ms ease-out;
+}
+
+#battery {
+ color: @base0A;
+}
diff --git a/modules/homeModules/bspwm.nix b/modules/homeModules/bspwm.nix
new file mode 100644
index 0000000..33b077b
--- /dev/null
+++ b/modules/homeModules/bspwm.nix
@@ -0,0 +1,68 @@
+{inputs, ...}: {
+ flake.homeModules.bspwm = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ imports = [
+ ./picom.nix
+ ./dunst.nix
+ ./sxhkd.nix
+ ];
+
+ options.customs = {
+ bspwm.enable = lib.mkEnableOption "enable bspwm";
+ };
+
+ config = lib.mkIf config.customs.bspwm.enable {
+ picom.enable = true;
+ dunst.enable = true;
+ sxhkd.enable = true;
+
+ rofi = {
+ enable = true;
+ package = pkgs.rofi;
+ };
+
+ xsession.windowManager.bspwm = {
+ enable = true;
+
+ monitors = let
+ workspaces = [
+ "α"
+ "β"
+ "γ"
+ "δ"
+ "ε"
+ ];
+ in {
+ "^1" = workspaces;
+ # "^2" = workspaces;
+ };
+
+ settings = {
+ # focused_border_color = "#908caa";
+ # normal_border_color = "#363a4f";
+ # presel_feedback_color = "#752f20";
+ border_width = 3;
+ window_gap = 12;
+ focus_follows_pointer = true;
+ split_ratio = 0.5;
+ };
+
+ startupPrograms = [
+ "sxhkd"
+ "picom -b"
+ "emacs --daemon"
+ "feh --bg-fill ${config.wallpaper}"
+ ];
+ };
+ home.packages = with pkgs; [
+ feh
+ betterlockscreen
+ xfce.xfce4-screenshooter
+ ];
+ };
+ };
+}
diff --git a/modules/homeModules/dunst.nix b/modules/homeModules/dunst.nix
new file mode 100644
index 0000000..c95f1e3
--- /dev/null
+++ b/modules/homeModules/dunst.nix
@@ -0,0 +1,25 @@
+{inputs, ...}: {
+ flake.homeModules.dunst = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ dunst.enable = lib.mkEnableOption "enable dunst";
+ };
+ config = lib.mkIf config.customs.dunst.enable {
+ home.packages = [pkgs.libnotify];
+ services.dunst = {
+ enable = true;
+ settings = {
+ global = {
+ origin = "bottom-right";
+ notification_limit = 5;
+ progress_bar = true;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/emacs/aliases b/modules/homeModules/emacs/aliases
new file mode 100644
index 0000000..c6d56c9
--- /dev/null
+++ b/modules/homeModules/emacs/aliases
@@ -0,0 +1,4 @@
+alias nr sudo nixos-rebuild switch --flake /home/jerpo/nixfiles#ltrr-mini
+alias hr home-manager switch --flake /home/jerpo/nixfiles
+alias ff find-file $1
+alias ntemplate nix flake init --template github:the-nix-way/dev-templates#$1
diff --git a/modules/homeModules/emacs/default.nix b/modules/homeModules/emacs/default.nix
new file mode 100644
index 0000000..b57a25a
--- /dev/null
+++ b/modules/homeModules/emacs/default.nix
@@ -0,0 +1,49 @@
+{inputs, ...}: {
+ flake.homeModules.emacs = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: let
+ mkMutableSymlink = config.lib.meta.mkMutableSymlink;
+ in {
+ options.customs = {
+ emacs.enable = lib.mkEnableOption "enable emacs";
+ emacs.package = lib.mkPackageOption pkgs "emacs package" {default = ["emacs30-pgtk"];};
+ };
+ config = lib.mkIf config.emacs.enable {
+ home.sessionVariables.EDITOR = "emacsclient -a emacs";
+ home.packages = with pkgs;
+ with python311Packages; [
+ # required dependencies
+ ripgrep
+ fd
+ tree-sitter
+ emacs-all-the-icons-fonts
+ libappindicator
+ poppler-utils
+ nixd
+ alejandra
+ sqlite
+ ];
+
+ programs.emacs = {
+ enable = true;
+ package = config.emacs.package;
+ extraPackages = epkgs:
+ with epkgs; [
+ treesit-grammars.with-all-grammars
+ mu4e
+ ];
+ };
+
+ xdg.configFile = {
+ "emacs/early-init.el".source = mkMutableSymlink ./early-init.el;
+ "emacs/init.el".source = mkMutableSymlink ./init.el;
+ "emacs/elpaca.el".source = mkMutableSymlink ./elpaca.el;
+ "emacs/etc/tempel/templates.eld".source = mkMutableSymlink ./templates.eld;
+ "emacs/etc/eshell/aliases".source = mkMutableSymlink ./aliases;
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/emacs/early-init.el b/modules/homeModules/emacs/early-init.el
new file mode 100644
index 0000000..c6334f5
--- /dev/null
+++ b/modules/homeModules/emacs/early-init.el
@@ -0,0 +1,171 @@
+;;;; Optimisations from https://github.com/jamescherti/minimal-emacs.d
+
+;;; Garbage collection
+(setq gc-cons-threshold most-positive-fixnum)
+
+(add-hook 'emacs-startup-hook
+ (lambda ()
+ (setq gc-cons-threshold (* 16 1024 1024))))
+
+;;; Performance
+
+;; Prefer loading newer compiled files
+(setq load-prefer-newer t)
+
+;; Increase how much is read from processes in a single chunk (default is 4kb).
+(setq read-process-output-max (* 1024 1024)) ; 512kb
+
+;; Disable warnings from the legacy advice API. They aren't useful.
+(setq ad-redefinition-action 'accept)
+
+(setq warning-suppress-types '((lexical-binding)))
+
+;; Don't ping things that look like domain names.
+(setq ffap-machine-p-known 'reject)
+
+;; By default, Emacs "updates" its ui more often than it needs to
+(setq idle-update-delay 1.0)
+
+;; Font compacting can be very resource-intensive, especially when rendering
+;; icon fonts on Windows. This will increase memory usage.
+(setq inhibit-compacting-font-caches t)
+
+(unless (daemonp)
+ (unless noninteractive
+ (progn
+ ;; Disable mode-line-format during init
+ (defun minimal-emacs--reset-inhibited-vars-h ()
+ (setq-default inhibit-redisplay nil
+ ;; Inhibiting `message' only prevents redraws and
+ inhibit-message nil)
+ (redraw-frame))
+
+ (defvar minimal-emacs--default-mode-line-format mode-line-format
+ "Default value of `mode-line-format'.")
+ (setq-default mode-line-format nil)
+
+ (defun minimal-emacs--startup-load-user-init-file (fn &rest args)
+ "Advice for startup--load-user-init-file to reset mode-line-format."
+ (let (init)
+ (unwind-protect
+ (progn
+ (apply fn args) ; Start up as normal
+ (setq init t))
+ (unless init
+ ;; If we don't undo inhibit-{message, redisplay} and there's an
+ ;; error, we'll see nothing but a blank Emacs frame.
+ (minimal-emacs--reset-inhibited-vars-h))
+ (unless (default-toplevel-value 'mode-line-format)
+ (setq-default mode-line-format
+ minimal-emacs--default-mode-line-format)))))
+
+ (advice-add 'startup--load-user-init-file :around
+ #'minimal-emacs--startup-load-user-init-file))
+
+ ;; Without this, Emacs will try to resize itself to a specific column size
+ (setq frame-inhibit-implied-resize t)
+
+ ;; A second, case-insensitive pass over `auto-mode-alist' is time wasted.
+ ;; No second pass of case-insensitive search over auto-mode-alist.
+ (setq auto-mode-case-fold nil)
+
+ ;; Reduce *Message* noise at startup. An empty scratch buffer (or the
+ ;; dashboard) is more than enough, and faster to display.
+ (setq inhibit-startup-screen t
+ inhibit-startup-echo-area-message user-login-name)
+ (setq initial-buffer-choice nil
+ inhibit-startup-buffer-menu t
+ inhibit-x-resources t)
+
+ ;; Disable bidirectional text scanning for a modest performance boost.
+ (setq-default bidi-display-reordering 'left-to-right
+ bidi-paragraph-direction 'left-to-right)
+
+ ;; Give up some bidirectional functionality for slightly faster re-display.
+ (setq bidi-inhibit-bpa t)
+
+ ;; Remove "For information about GNU Emacs..." message at startup
+ (advice-add #'display-startup-echo-area-message :override #'ignore)
+
+ ;; Suppress the vanilla startup screen completely. We've disabled it with
+ ;; `inhibit-startup-screen', but it would still initialize anyway.
+ (advice-add #'display-startup-screen :override #'ignore)
+
+ ;; Shave seconds off startup time by starting the scratch buffer in
+ ;; `fundamental-mode'
+ (setq initial-major-mode 'fundamental-mode
+ initial-scratch-message nil)))
+
+;;; Native compilation and Byte compilation
+
+(if (and (featurep 'native-compile)
+ (fboundp 'native-comp-available-p)
+ (native-comp-available-p))
+ ;; Activate `native-compile'
+ (setq native-comp-jit-compilation t
+ package-native-compile t)
+ ;; Deactivate the `native-compile' feature if it is not available
+ (setq features (delq 'native-compile features)))
+
+;; Suppress compiler warnings and don't inundate users with their popups.
+(setq native-comp-async-report-warnings-errors 'silent)
+
+;;; UI elements
+
+;; Disable startup screens and messages
+(setq inhibit-splash-screen t)
+
+
+(push '(menu-bar-lines . 0) default-frame-alist)
+(unless (memq window-system '(mac ns))
+ (setq menu-bar-mode nil))
+
+(unless (daemonp)
+ (unless noninteractive
+ (when (fboundp 'tool-bar-setup)
+ ;; Temporarily override the tool-bar-setup function to prevent it from
+ ;; running during the initial stages of startup
+ (advice-add #'tool-bar-setup :override #'ignore)
+ (define-advice startup--load-user-init-file
+ (:before (&rest _) minimal-emacs-setup-toolbar)
+ (advice-remove #'tool-bar-setup #'ignore)
+ (tool-bar-setup)))))
+
+(push '(tool-bar-lines . 0) default-frame-alist)
+(setq tool-bar-mode nil)
+
+(push '(vertical-scroll-bars) default-frame-alist)
+(push '(horizontal-scroll-bars) default-frame-alist)
+(setq scroll-bar-mode nil)
+(when (fboundp 'horizontal-scroll-bar-mode)
+ (horizontal-scroll-bar-mode -1))
+
+(when (bound-and-true-p tooltip-mode)
+ (tooltip-mode -1))
+
+;; Disable GUIs because they are inconsistent across systems, desktop
+;; environments, and themes, and they don't match the look of Emacs.
+(setq use-file-dialog nil)
+(setq use-dialog-box nil)
+
+;; Allow for shorter responses: "y" for yes and "n" for no.
+(if (boundp 'use-short-answers)
+ (setq use-short-answers t)
+ (advice-add #'yes-or-no-p :override #'y-or-n-p))
+(defalias #'view-hello-file #'ignore) ; Never show the hello file
+
+;;; And that's mine
+
+(setq package-enable-at-startup nil)
+(setq-default pgtk-wait-for-event-timeout 0)
+
+(let ((mono-spaced-font "FiraCode Nerd Font")
+ (proportionately-spaced-font "Inconsonata"))
+ (set-face-attribute 'default nil :family mono-spaced-font :height 110 :weight 'medium)
+ (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
+ (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0)
+ (set-face-attribute 'italic nil :underline nil))
+
+
+(provide 'early-init)
+;;; early-init.el ends here
diff --git a/modules/homeModules/emacs/elpaca.el b/modules/homeModules/emacs/elpaca.el
new file mode 100644
index 0000000..4e1230f
--- /dev/null
+++ b/modules/homeModules/emacs/elpaca.el
@@ -0,0 +1,46 @@
+(defvar elpaca-installer-version 0.11)
+(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
+(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
+(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
+(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
+ :ref nil :depth 1 :inherit ignore
+ :files (:defaults "elpaca-test.el" (:exclude "extensions"))
+ :build (:not elpaca--activate-package)))
+(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
+ (build (expand-file-name "elpaca/" elpaca-builds-directory))
+ (order (cdr elpaca-order))
+ (default-directory repo))
+ (add-to-list 'load-path (if (file-exists-p build) build repo))
+ (unless (file-exists-p repo)
+ (make-directory repo t)
+ (when (<= emacs-major-version 28) (require 'subr-x))
+ (condition-case-unless-debug err
+ (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
+ ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
+ ,@(when-let* ((depth (plist-get order :depth)))
+ (list (format "--depth=%d" depth) "--no-single-branch"))
+ ,(plist-get order :repo) ,repo))))
+ ((zerop (call-process "git" nil buffer t "checkout"
+ (or (plist-get order :ref) "--"))))
+ (emacs (concat invocation-directory invocation-name))
+ ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
+ "--eval" "(byte-recompile-directory \".\" 0 'force)")))
+ ((require 'elpaca))
+ ((elpaca-generate-autoloads "elpaca" repo)))
+ (progn (message "%s" (buffer-string)) (kill-buffer buffer))
+ (error "%s" (with-current-buffer buffer (buffer-string))))
+ ((error) (warn "%s" err) (delete-directory repo 'recursive))))
+ (unless (require 'elpaca-autoloads nil t)
+ (require 'elpaca)
+ (elpaca-generate-autoloads "elpaca" repo)
+ (let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
+(add-hook 'after-init-hook #'elpaca-process-queues)
+(elpaca `(,@elpaca-order))
+
+(elpaca elpaca-use-package
+ ;; Enable :elpaca use-package keyword.
+ (elpaca-use-package-mode)
+ (setq use-package-always-ensure t))
+
+(provide 'elpaca)
+;;; elpaca.el ends here
diff --git a/modules/homeModules/emacs/init.el b/modules/homeModules/emacs/init.el
new file mode 100644
index 0000000..9a3053c
--- /dev/null
+++ b/modules/homeModules/emacs/init.el
@@ -0,0 +1,848 @@
+(setopt custom-file (locate-user-emacs-file "custom.el"))
+(load custom-file :no-error-if-file-is-missing)
+
+
+(load (expand-file-name "elpaca.el" user-emacs-directory))
+
+
+;;; Basic behaviour
+
+
+(setopt uniquify-buffer-name-style 'forward)
+
+
+(setopt enable-recursive-minibuffers t)
+
+
+;; Auto save
+(setopt auto-save-default t)
+(setopt auto-save-include-big-deletions t)
+(setopt kill-buffer-delete-auto-save-files t)
+
+
+;; Auto revert
+(setopt revert-without-query (list ".") ; Do not prompt
+ auto-revert-stop-on-user-input nil
+ auto-revert-verbose t)
+
+
+;; Revert other buffers (e.g, Dired)
+(setopt global-auto-revert-non-file-buffers t)
+(add-hook 'elpaca-after-init-hook #'global-auto-revert-mode)
+
+
+;; Save place in buffer
+(setopt save-place-limit 600)
+(add-hook 'elpaca-after-init-hook #'save-place-mode)
+
+
+(defun prot/keyboard-quit-dwim ()
+ "Do-What-I-Mean behaviour for a general `keyboard-quit'.
+
+The generic `keyboard-quit' does not do the expected thing when
+the minibuffer is open. Whereas we want it to close the
+minibuffer, even without explicitly focusing it.
+
+The DWIM behaviour of this command is as follows:
+
+- When the region is active, disable it.
+- When a minibuffer is open, but not focused, close the minibuffer.
+- When the Completions buffer is selected, close it.
+- In every other case use the regular `keyboard-quit'."
+ (interactive)
+ (cond
+ ((region-active-p)
+ (keyboard-quit))
+ ((derived-mode-p 'completion-list-mode)
+ (delete-completion-window))
+ ((> (minibuffer-depth) 0)
+ (abort-recursive-edit))
+ (t
+ (keyboard-quit))))
+
+(keymap-global-set "C-g" #'prot/keyboard-quit-dwim)
+
+
+(use-package no-littering
+ :config
+ (no-littering-theme-backups))
+
+
+(use-package which-key
+ :ensure nil
+ :init
+ (which-key-mode))
+
+
+(use-package vundo
+ :commands (vundo)
+ :hook (vundo-mode . (lambda () (visual-line-mode -1) (setopt truncate-lines t))))
+
+
+(use-package winner
+ :ensure nil
+ :bind ("C-0" . winner-undo)
+ :custom
+ (winner-dont-bind-my-keys t)
+ :hook (elpaca-after-init . winner-mode))
+
+
+(use-package repeat
+ :ensure nil
+ :hook (elpaca-after-init . repeat-mode))
+
+
+(use-package avy
+ :bind (("M-j" . avy-goto-char-timer)
+ ("C-c j" . avy-goto-line)))
+
+
+(use-package helpful
+ :bind (("C-h f" . helpful-callable)
+ ("C-h v" . helpful-variable)
+ ("C-h k" . helpful-key)
+ ("C-h x" . helpful-command)
+ ("C-c C-d" . helpful-at-point)
+ ("C-h F" . helpful-function)))
+
+
+(setopt ediff-window-setup-function #'ediff-setup-windows-plain
+ ediff-split-window-function #'split-window-horizontally)
+
+
+(electric-pair-mode t)
+
+
+(global-set-key [remap list-buffers] 'ibuffer)
+
+;;; Tweak the looks of Emacs
+(setq-default tab-width 4)
+
+(global-word-wrap-whitespace-mode t)
+(global-visual-line-mode t)
+(global-visual-wrap-prefix-mode t)
+
+;; Mode Line
+(defun mood-line-segment-input-method ()
+ "Return the selected input method if it differs from the default one"
+ (if current-input-method
+ (propertize (format-mode-line
+ (format "[%s]" current-input-method-title))
+ 'face 'mood-line-unimportant)))
+
+(use-package mood-line
+ :config
+ (mood-line-mode)
+ :custom
+ (mood-line-segment-modal-meow-state-alist
+ '((normal "N" . mood-line-meow-normal)
+ (insert "I" . mood-line-meow-insert)
+ (keypad "K" . mood-line-meow-keypad)
+ (beacon "B" . mood-line-meow-beacon)
+ (motion "M" . mood-line-meow-motion)))
+ (mood-line-glyph-alist mood-line-glyphs-fira-code)
+
+ (mood-line-format '((" " (mood-line-segment-modal) " "
+ (or (mood-line-segment-buffer-status) " ") " "
+ (mood-line-segment-buffer-name) " " (mood-line-segment-anzu) " "
+ (mood-line-segment-multiple-cursors) " "
+ (mood-line-segment-cursor-position) " " (mood-line-segment-scroll)
+ "")
+ ((mood-line-segment-vc) " "
+ (mood-line-segment-major-mode) " "
+ (mood-line-segment-input-method) " "
+ (mood-line-segment-misc-info) " " (mood-line-segment-checker) " "
+ (mood-line-segment-process) " " " ")))
+ :custom-face
+ (mood-line-meow-beacon ((t (:inherit 'font-lock-function-name-face :weight bold))))
+ (mood-line-meow-insert ((t (:inherit 'font-lock-string-face :weight bold))))
+ (mood-line-meow-keypad ((t (:inherit 'font-lock-keyword-face :weight bold))))
+ (mood-line-meow-motion ((t (:inherit 'font-lock-constant-face :weight bold))))
+ (mood-line-meow-normal ((t (:inherit 'font-lock-variable-use-face :weight bold)))))
+
+
+(defvar after-load-theme-hook nil
+ "Hook run after a color theme is loaded using `load-theme'.")
+
+(defun spl3g/after-load-theme-advice (&rest r)
+ "Run `after-load-theme-hook' and ignore R."
+ (run-hooks 'after-load-theme-hook))
+(advice-add 'load-theme :after #'spl3g/after-load-theme-advice)
+
+(defun widen-mode-line ()
+ "Widen the mode-line."
+ (interactive)
+ (set-face-attribute 'mode-line nil
+ :inherit 'mode-line
+ :box '(:line-width 8 :style flat-button))
+ (set-face-attribute 'mode-line-inactive nil
+ :inherit 'mode-line-inactive
+ :box '(:line-width 8 :style flat-button)))
+
+(add-hook 'after-load-theme-hook 'widen-mode-line)
+
+
+(use-package rainbow-delimiters
+ :hook (prog-mode . rainbow-delimiters-mode))
+
+
+;; Scrolling
+(setopt scroll-conservatively 101)
+(setopt scroll-margin 5)
+(setopt mouse-wheel-progressive-speed nil)
+(setopt fast-but-imprecise-scrolling t)
+(setopt scroll-error-top-bottom t)
+(setopt scroll-preserve-screen-position t)
+
+
+;; Annoyances
+(blink-cursor-mode -1)
+(setopt visible-bell nil)
+(setopt ring-bell-function #'ignore)
+(setopt delete-pair-blink-delay 0.03)
+(setopt blink-matching-paren nil)
+
+(add-hook 'prog-mode-hook 'display-line-numbers-mode)
+(setopt display-line-numbers-width 3)
+
+(kill-ring-deindent-mode)
+
+;; Overwrite the default function with a patched one
+(defun kill-ring-deindent-buffer-substring-function (beg end delete)
+ "Save the text within BEG and END to kill-ring, decreasing indentation.
+Delete the saved text if DELETE is non-nil.
+
+In the saved copy of the text, remove some of the indentation, such
+that the buffer position at BEG will be at column zero when the text
+is yanked."
+ (let ((a beg)
+ (b end))
+ (setq beg (min a b)
+ end (max a b)))
+ (let ((indentation (save-excursion (goto-char beg)
+ (current-column)))
+ (i-t-m indent-tabs-mode)
+ (text (if delete
+ (delete-and-extract-region beg end)
+ (buffer-substring beg end))))
+ (with-temp-buffer
+ ;; Indent/deindent the same as the major mode in the original
+ ;; buffer.
+ (setq indent-tabs-mode i-t-m)
+ (insert text)
+ (indent-rigidly (point-min) (point-max)
+ (- indentation))
+ (buffer-string))))
+
+
+;;; TRAMP
+(setq remote-file-name-inhibit-locks t
+ tramp-use-scp-direct-remote-copying t
+ remote-file-name-inhibit-auto-save-visited t)
+
+(setq tramp-copy-size-limit (* 1024 1024) ;; 1MB
+ tramp-verbose 2)
+
+
+;;; Configure the minibuffer and completions
+(use-package vertico
+ :ensure t
+ :hook (elpaca-after-init . vertico-mode)
+ :bind (:map vertico-map
+ ("RET" . vertico-directory-enter)
+ ("DEL" . vertico-directory-delete-char)
+ ("M-DEL" . vertico-directory-delete-word)))
+
+
+(use-package marginalia
+ :ensure t
+ :hook (elpaca-after-init . marginalia-mode))
+
+
+(use-package orderless
+ :custom
+ (completion-styles '(orderless basic))
+ (completion-category-overrides '((file (styles basic partial-completion)))))
+
+
+(use-package savehist
+ :ensure nil ; it is built-in
+ :hook (elpaca-after-init . savehist-mode)
+ :custom
+ (savehist-file "~/.config/emacs/var/savehist.el")
+ (history-length 1000)
+ (history-delete-duplicates t)
+ (savehist-additional-variables '(kill-ring search-ring)))
+
+
+(defun cape--dabbrev-project ()
+ (let* ((project (project-current))
+ (buffers (when project
+ (project-buffers project))))
+ (if project
+ (butlast buffers (- (length buffers) 4))
+ (cape--buffers-major-mode))))
+
+(use-package cape
+ :after corfu
+ :custom
+ (dabbrev-ignored-buffer-modes '(archive-mode image-mode eshell-mode))
+ (cape-dabbrev-check-other-buffers #'cape--dabbrev-project)
+ :config
+ (add-hook 'completion-at-point-functions #'cape-dabbrev)
+ (add-hook 'completion-at-point-functions #'cape-file)
+ (add-hook 'completion-at-point-functions #'cape-elisp-block))
+
+
+(use-package corfu
+ :hook (elpaca-after-init . global-corfu-mode)
+ :bind (:map corfu-map
+ ("M-j" . corfu-next)
+ ("M-k" . corfu-previous)
+ ([remap previous-line] . nil)
+ ([remap next-line] . nil))
+ :custom
+ (corfu-preselect 'prompt)
+ (corfu-auto nil)
+ (corfu-popupinfo-delay '(1.25 . 0.5))
+ (corfu-auto-delay 0)
+ (corfu-auto-prefix 2)
+ (corfu-count 16)
+ (corfu-max-width 120)
+ (corfu-min-width 20)
+ (corfu-scroll-margin 4)
+ (corfu-on-exact-match nil)
+ (tab-always-indent 'complete)
+ (corfu-cycle t)
+ :config
+ (corfu-popupinfo-mode 1)
+ (corfu-history-mode 1))
+
+
+(use-package completion-preview
+ :ensure nil
+ :hook (elpaca-after-init . global-completion-preview-mode)
+ :bind (:map completion-preview-active-mode-map
+ ("TAB" . (lambda ()
+ (interactive)
+ (completion-preview-complete)
+ (completion-at-point)))
+
+ ("M-i" . (lambda ()
+ (interactive)
+ (completion-preview-insert)))
+ ("M-n" . completion-preview-next-candidate)
+ ("M-p" . completion-preview-prev-candidate))
+ :custom
+ (completion-preview-minimum-symbol-length 2))
+
+
+
+;;; Movement
+
+
+(use-package embark
+ :ensure t
+
+ :bind
+ (("C-." . embark-act)
+ ("C-;" . embark-dwim)
+ ("C-h B" . embark-bindings))
+ :config
+ (defun embark-which-key-indicator ()
+ "An embark indicator that displays keymaps using which-key.
+The which-key help message will show the type and value of the
+current target followed by an ellipsis if there are further
+targets."
+ (lambda (&optional keymap targets prefix)
+ (if (null keymap)
+ (which-key--hide-popup-ignore-command)
+ (which-key--show-keymap
+ (if (eq (plist-get (car targets) :type) 'embark-become)
+ "Become"
+ (format "Act on %s '%s'%s"
+ (plist-get (car targets) :type)
+ (embark--truncate-target (plist-get (car targets) :target))
+ (if (cdr targets) "…" "")))
+ (if prefix
+ (pcase (lookup-key keymap prefix 'accept-default)
+ ((and (pred keymapp) km) km)
+ (_ (key-binding prefix 'accept-default)))
+ keymap)
+ nil nil t (lambda (binding)
+ (not (string-suffix-p "-argument" (cdr binding))))))))
+
+ (setq embark-indicators
+ '(embark-which-key-indicator
+ embark-highlight-indicator
+ embark-isearch-highlight-indicator))
+
+ (defun embark-hide-which-key-indicator (fn &rest args)
+ "Hide the which-key indicator immediately when using the completing-read prompter."
+ (which-key--hide-popup-ignore-command)
+ (let ((embark-indicators
+ (remq #'embark-which-key-indicator embark-indicators)))
+ (apply fn args)))
+
+ (advice-add #'embark-completing-read-prompter
+ :around #'embark-hide-which-key-indicator))
+
+
+(use-package multiple-cursors
+ :config
+ (global-set-key (kbd "C->") 'mc/mark-next-like-this)
+ (global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
+ (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this))
+
+
+
+;;; The file manager (Dired)
+
+
+(use-package dired
+ :ensure nil
+ :commands (dired)
+ :hook
+ ((dired-mode . dired-hide-details-mode)
+ (dired-mode . hl-line-mode))
+ :custom
+ (dired-recursive-copies 'always)
+ (dired-recursive-deletes 'always)
+ (delete-by-moving-to-trash t)
+ (dired-dwim-target t)
+ (dired-listing-switches "-hal --group-directories-first"))
+
+
+(use-package dired-subtree
+ :ensure t
+ :after dired
+ :bind
+ ( :map dired-mode-map
+ ("<tab>" . dired-subtree-toggle)
+ ("TAB" . dired-subtree-toggle)
+ ("<backtab>" . dired-subtree-remove)
+ ("S-TAB" . dired-subtree-remove))
+ :custom
+ (dired-subtree-use-backgrounds nil))
+
+
+(use-package trashed
+ :ensure t
+ :commands (trashed)
+ :custom
+ (trashed-action-confirmer 'y-or-n-p)
+ (trashed-use-header-line t)
+ (trashed-sort-key '("Date deleted" . t))
+ (trashed-date-format "%Y-%m-%d %H:%M:%S"))
+
+
+
+;;; Eshell
+
+
+(use-package eshell
+ :ensure nil
+ :hook
+ (eshell-exec . (lambda (p) (buffer-disable-undo)))
+ (eshell-kill . (lambda (p s) (buffer-enable-undo)))
+ (eshell-pre-command . eshell-append-history)
+
+ :bind (("C-c o t" . spl3g/eshell-dwim)
+ ("C-c o s" . spl3g/select-eshell)
+ ("C-c o r" . spl3g/rename-current-eshell))
+
+ :custom
+ (eshell-history-size 500)
+ (eshell-history-append t)
+ (eshell-save-history-on-exit nil)
+
+ :config
+ ;; (add-to-list 'eshell-modules-list 'eshell-tramp)
+
+ ;; Save history on every command
+ (defun eshell-append-history ()
+ "Call `eshell-write-history' with the `append' parameter set to `t'."
+ (when eshell-history-ring
+ (let ((newest-cmd-ring (make-ring 1)))
+ (ring-insert newest-cmd-ring (car (ring-elements eshell-history-ring)))
+ (let ((eshell-history-ring newest-cmd-ring))
+ (eshell-write-history eshell-history-file-name t)))))
+
+
+ (defun spl3g/eshell-dwim ()
+ (interactive)
+ (defvar current-prefix-arg)
+ (let* ((project (project-current))
+ (eshell-func (if project
+ 'project-eshell
+ 'eshell))
+ (buffer-name (if project
+ (format "*%s-eshell*" (project-name project))
+ "*eshell*"))
+ (current-prefix-arg t))
+ (if (not (get-buffer buffer-name))
+ (let ((buf (funcall eshell-func)))
+ (switch-to-buffer (other-buffer buf))
+ (switch-to-buffer-other-window buf))
+ (switch-to-buffer-other-window buffer-name))))
+
+ (defun spl3g/select-eshell ()
+ (interactive)
+ (let* ((eshell-buffers (seq-filter (lambda (buffer)
+ (eq (with-current-buffer buffer major-mode)
+ 'eshell-mode))
+ (buffer-list)))
+
+ (eshell-windows (remove nil (mapcar (lambda (buffer)
+ (let* ((window (get-buffer-window buffer))
+ (name (buffer-name buffer)))
+ (when window
+ (cons name window))))
+ eshell-buffers)))
+
+ (eshell-names (seq-filter (lambda (buffer) (not (eq buffer (buffer-name (current-buffer)))))
+ (mapcar (lambda (buffer) (buffer-name buffer)) eshell-buffers)))
+
+ (selected-buffer (if (length> eshell-buffers 1)
+ (completing-read "Select eshell buffer: " eshell-names)
+ (car eshell-buffers)))
+ (selected-window (if (length> eshell-windows 1)
+ (cdr (assoc (completing-read "Select window to place the buffer in: " eshell-windows) eshell-windows))
+ (cdar eshell-windows))))
+ (if selected-window
+ (progn
+ (select-window selected-window)
+ (switch-to-buffer selected-buffer))
+ (switch-to-buffer-other-window selected-buffer))))
+
+ (defun spl3g/rename-current-eshell ()
+ "Add NAME in <> to the current project eshell buffer"
+ (interactive)
+ (let* ((eshell-buffers (seq-filter (lambda (buffer)
+ (eq (with-current-buffer buffer major-mode)
+ 'eshell-mode))
+ (buffer-list)))
+ (eshell-names (mapcar (lambda (buffer) (buffer-name buffer)) eshell-buffers))
+ (eshell-windows (remove nil (mapcar (lambda (buffer)
+ (let* ((window (get-buffer-window buffer))
+ (name (buffer-name buffer)))
+ (when window
+ (cons name window))))
+ eshell-buffers)))
+ (selected-window (if (and (sequencep eshell-windows) (length> eshell-windows 1))
+ (assoc (completing-read "Select window to place the buffer in: " eshell-windows) eshell-windows)
+ (car eshell-windows)))
+ (additional-name (when selected-window
+ (read-string "Additional name: ")))
+ (buffer-name (when selected-window
+ (car (split-string (car selected-window) "<"))))
+ (formatted-name (if (length> additional-name 0)
+ (format "%s<%s>" buffer-name additional-name)
+ buffer-name))
+ )
+ (if selected-window
+ (with-current-buffer (car selected-window)
+ (rename-buffer formatted-name))
+ (message "No eshell buffers")))))
+
+
+(use-package eat
+ :hook
+ (eshell-load . eat-eshell-mode)
+ (eshell-load . eat-eshell-visual-command-mode)
+ :custom
+ (eat-enable-auto-line-mode t)
+ :custom-face
+ (ansi-color-bright-blue ((t (:inherit 'ansi-color-blue))))
+ (ansi-color-bright-red ((t (:inherit 'ansi-color-red))))
+ (ansi-color-bright-red ((t (:inherit 'ansi-color-red))))
+ (ansi-color-bright-cyan ((t (:inherit 'ansi-color-cyan))))
+ (ansi-color-bright-black ((t (:inherit 'ansi-color-black))))
+ (ansi-color-bright-green ((t (:inherit 'ansi-color-green))))
+ (ansi-color-bright-white ((t (:inherit 'ansi-color-white))))
+ (ansi-color-bright-yellow ((t (:inherit 'ansi-color-yellow))))
+ (ansi-color-bright-magenta ((t (:inherit 'ansi-color-magenta)))))
+
+
+(use-package eshell-syntax-highlighting
+ :hook (eshell-mode . eshell-syntax-highlighting-mode))
+
+
+
+;;; Programming things
+
+
+(use-package tempel
+ :ensure t
+ ;; By default, tempel looks at the file "templates" in
+ ;; user-emacs-directory, but you can customize that with the
+ ;; tempel-path variable:
+ ;; :custom
+ ;; (tempel-path (concat user-emacs-directory "custom_template_file"))
+ :bind (("M-*" . tempel-insert)
+ ("M-+" . tempel-complete)
+ :map tempel-map
+ ("C-c RET" . tempel-done)
+ ("C-<down>" . tempel-next)
+ ("C-<up>" . tempel-previous)
+ ("M-<down>" . tempel-next)
+ ("M-<up>" . tempel-previous))
+ :custom
+ (tempel-trigger-prefix "<")
+ :init
+ ;; Make a function that adds the tempel expansion function to the
+ ;; list of completion-at-point-functions (capf).
+ (defun tempel-setup-capf ()
+ (add-hook 'completion-at-point-functions #'tempel-expand -1 'local))
+ ;; Put tempel-expand on the list whenever you start programming or
+ ;; writing prose.
+ (add-hook 'prog-mode-hook 'tempel-setup-capf)
+ (add-hook 'text-mode-hook 'tempel-setup-capf))
+
+(use-package tempel-collection
+ :ensure t
+ :after tempel)
+
+(use-package lsp-snippet-tempel
+ :ensure (:host github :repo "tpeacock19/lsp-snippet")
+ :config
+ (lsp-snippet-tempel-eglot-init))
+
+
+(use-package apheleia
+ :hook (prog-mode-hook . apheleia-mode)
+ :config
+ (push '(alejandra . ("alejandra")) apheleia-formatters)
+ (setf (alist-get 'nix-mode apheleia-mode-alist) '(alejandra)))
+
+
+(use-package envrc
+ :mode ("\\.envrc\\.?[[:alpha:]]*\\'" . envrc-file-mode)
+ :hook (elpaca-after-init . envrc-global-mode)
+ :bind-keymap
+ ("C-c e" . envrc-command-map))
+
+
+(use-package scratch
+ :commands scratch)
+
+
+(use-package transient)
+
+(use-package magit
+ :after transient
+ :bind (("C-c o g" . magit)))
+
+
+(use-package treesit-auto
+ :hook (elpaca-after-init . global-treesit-auto-mode)
+ :custom
+ (treesit-auto-install 'prompt)
+ :config
+ (treesit-auto-add-to-auto-mode-alist 'all)
+ (delete 'html treesit-auto-langs))
+
+
+(keymap-global-set "C-c c c" 'compile)
+(keymap-global-set "C-c c r" 'recompile)
+
+
+(defun colorize-compilation-buffer ()
+ (ansi-color-apply-on-region compilation-filter-start (point)))
+(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
+
+
+
+;; LSP shit
+(use-package eglot
+ :ensure nil
+ :bind (:map eglot-mode-map
+ ("C-c s a" . eglot-code-actions)
+ ("C-c s r" . eglot-rename)
+ ("C-c s h" . eldoc)
+ ("C-c s f" . eglot-format)
+ ("C-c s F" . eglot-format-buffer)
+ ("C-c s d" . xref-find-definitions-at-mouse)
+ ("C-c s R" . eglot-reconnect))
+ :custom
+ (completion-category-overrides '((eglot (styles prescient))
+ (eglot-capf (styles prescient))))
+ :config
+ (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
+
+ (setq-default
+ eglot-workspace-configuration
+ `(:nixd ( :nixpkgs (:expr "import <nixpkgs> { }")
+ :formatting (:command ["nixpkgs-fmt"])
+ :options ( :nixos (:expr "(builtins.getFlake \"/home/jerpo/nixfiles\").nixosConfigurations.ltrr-mini.options")
+ :home-manager (:expr "(builtins.getFlake \"/home/jerpo/nixfiles\").homeConfigurations.\"jerpo@ltrr-mini\".options"))))))
+
+
+(use-package dumb-jump
+ :commands (dumb-jump-xref-activate)
+ :custom
+ (dumb-jump-force-searcher 'rg)
+ :init
+ (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
+
+
+(use-package dape)
+
+
+
+;;; Languages
+(use-package web-mode
+ :mode
+ ("\\.html\\'"
+ "\\.phtml\\'"
+ "\\.tpl\\.php\\'"
+ "\\.[agj]sp\\'"
+ "\\.as[cp]x\\'"
+ "\\.erb\\'"
+ "\\.mustache\\'"
+ "\\.djhtml\\'")
+ :hook
+ (eb-mode . (lambda () (electric-pair-local-mode -1)))
+ :custom
+ (web-mode-markup-indent-offset tab-width)
+ (web-mode-css-indent-offset tab-width)
+ (web-mode-code-indent-offset tab-width)
+ (web-mode-script-padding tab-width)
+ (web-mode-style-padding tab-width)
+ :init
+
+ (define-derived-mode vue-mode web-mode "Vue")
+ (add-to-list 'auto-mode-alist '("\\.vue\\'" . vue-mode))
+ (add-hook 'vue-mode-hook
+ (lambda ()
+ (with-eval-after-load 'eglot
+ (defun vue-eglot-init-options ()
+ "Set SDK path and default options."
+ (let ((tsdk-path (expand-file-name
+ "lib/node_modules/typescript/lib/"
+ (inheritenv (shell-command-to-string
+ (string-join '("nix-store --query --references $(which vue-language-server)"
+ "xargs -n1 nix-store -q --referrers"
+ "grep typescript"
+ "head -n1"
+ "tr -d '\n'")
+ " | "))))))
+ `( :typescript (:tsdk ,tsdk-path)
+ :hybridMode :json-false)))
+ (setf (alist-get 'vue-mode eglot-server-programs) ;; nix-env -iA nixpkgs.nodePackages.volar
+ `("vue-language-server" "--stdio" :initializationOptions ,(vue-eglot-init-options))))))
+
+ (define-derived-mode svelte-mode web-mode "Svelte")
+ (add-to-list 'auto-mode-alist '("\\.svelte\\'" . svelte-mode))
+ (with-eval-after-load 'eglot
+ (add-to-list 'eglot-server-programs `(svelte-mode "svelteserver" "--stdio"))))
+
+(use-package emmet-mode
+ :hook (web-mode . emmet-mode))
+
+
+(use-package nix-mode
+ :mode ("\\.nix\\'" "\\.nix.in\\'"))
+
+
+(use-package typescript-ts-mode
+ :ensure nil
+ :mode ("\\.ts\\'")
+ :hook (typescript-ts-mode . (lambda () (setq-local forward-sexp-function nil)))
+ :custom
+ (typescript-ts-mode-indent-offset tab-width))
+
+
+(use-package go-ts-mode
+ :ensure nil
+ :custom
+ (go-ts-mode-indent-offset tab-width))
+
+(use-package c-ts-mode
+ :ensure nil
+ :hook (c-ts-mode . (lambda () (apheleia-mode -1))))
+
+(use-package c-mode
+ :ensure nil
+ :hook (c-mode . (lambda () (apheleia-mode -1)))
+ :mode ("\\.c\\'"))
+
+
+(use-package markdown-mode
+ :mode ("\\.md\\'"))
+
+
+(use-package conf-unix-mode
+ :ensure nil
+ :mode ("\\.env\\.?[[:alpha:]]*\\'"))
+
+
+(use-package sql-indent
+ :hook (sql-mode . sqlind-minor-mode))
+
+(setopt sql-connection-alist
+ '(("postgres-sirius"
+ (sql-product 'postgres)
+ (sql-user "college")
+ (sql-server "127.0.0.1")
+ (sql-database "coll")
+ (sql-port 5432))))
+
+(setopt sql-sqlite-program "sqlite3")
+
+
+(use-package elm-mode
+ :mode "\\.elm\\'")
+
+
+(use-package zig-ts-mode
+ :mode "\\.zig\\'")
+
+
+(use-package verb
+ :after org
+ :config (define-key org-mode-map (kbd "C-c C-r") verb-command-map))
+
+
+
+;; Notetaking
+
+
+(use-package org-mode
+ :ensure nil
+ :hook (org-mode . variable-pitch-mode)
+ :custom
+ (org-startup-indented t)
+ :mode "\\.org\\'"
+ :config
+ (set-face-attribute 'org-table nil :inherit 'fixed-pitch))
+
+
+(use-package writeroom-mode
+ :custom
+ (writeroom-global-effects nil)
+ (writeroom-maximize-window nil))
+
+
+(use-package denote
+ :commands (denote denote-create-note denote-journal-extras-new-entry))
+
+
+
+;; Other
+
+
+(use-package kubel
+ :commands (kubel)
+ :hook (kubel-mode . hl-line-mode)
+ :bind ((:map kubel-mode-map
+ ("N" . kubel-set-namespace)
+ ("P" . kubel-port-forward-pod)
+ ("n" . #'next-line)
+ ("p" . #'previous-line)))
+ :custom-face
+ (kubel-status-completed ((t (:inherit 'font-lock-keyword-face :weight bold))))
+ (kubel-status-terminating ((t (:inherit 'font-lock-variable-use-face :weight bold)))))
+
+(provide 'init)
+;;; init.el ends here.
diff --git a/modules/homeModules/emacs/templates.eld b/modules/homeModules/emacs/templates.eld
new file mode 100644
index 0000000..138888f
--- /dev/null
+++ b/modules/homeModules/emacs/templates.eld
@@ -0,0 +1,18 @@
+nix-mode
+
+(opt "{ pkgs, config, lib, ... }:"
+ n
+ n "{"
+ n> "options = {"
+ n> (p "option name" name) ".enable = lib.mkEnableOption \"enable " (s name) "\";"
+ n " };"
+ n> "config = lib.mkIf config." (s name) ".enable {"
+ n> q
+ n " };"
+ n "}")
+
+python-ts-mode
+
+(view "class " (s name) "ViewSet(viewsets.ModelViewSet):"
+ n> "queryset = " (s name) ".objects.all()"
+ n> "serializer_class = " (s name) "Serializer")
diff --git a/modules/homeModules/exwm.nix b/modules/homeModules/exwm.nix
new file mode 100644
index 0000000..ccc7dbf
--- /dev/null
+++ b/modules/homeModules/exwm.nix
@@ -0,0 +1,71 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.homeModules.exwm = {
+ lib,
+ pkgs,
+ config,
+ ...
+ }: {
+ modules = [
+ self.homeModules.picom
+ self.homeModules.dunst
+ ];
+
+ options.customs = {
+ exwm.enable = lib.mkEnableOption " enable exwm";
+ };
+
+ config = lib.mkIf config.exwm.enable {
+ customs.picom.enable = true;
+ customs.dunst.enable = true;
+
+ programs.emacs = {
+ extraPackages = epkgs: with epkgs; [exwm];
+ extraConfig = ''
+ (setq exwm--my-scripts "${./attachments/hypr-scripts}")
+ '';
+ };
+ services = {
+ # Screenshotting.
+ flameshot.enable = true;
+
+ # Screen locking.
+ screen-locker = {
+ enable = true;
+ lockCmd = "${pkgs.i3lock-fancy}/bin/i3lock-fancy -p -t ''";
+ inactiveInterval = 20;
+ };
+ };
+ home.file.xinitrc = {
+ text = ''
+ # Disable access control for the current user.
+ xhost +SI:localuser:$USER
+
+ # Make Java applications aware this is a non-reparenting window manager.
+ export _JAVA_AWT_WM_NONREPARENTING=1
+
+ # Set default cursor.
+ xsetroot -cursor_name left_ptr
+
+ picom -b
+
+ # Finally start Emacs
+ ${pkgs.dbus.dbus-launch} --exit-with-session emacs -mm --fullscreen --internal-border=0 --border-width=0
+ '';
+ target = ".xinitrc";
+ };
+ home.packages = with pkgs; [
+ boomer
+ arandr
+ feh
+ gtk3
+ i3lock-fancy
+ xclip
+ xorg.xev
+ ];
+ };
+ };
+}
diff --git a/modules/homeModules/firefox.nix b/modules/homeModules/firefox.nix
new file mode 100644
index 0000000..d92b50d
--- /dev/null
+++ b/modules/homeModules/firefox.nix
@@ -0,0 +1,111 @@
+{inputs, ...}: {
+ flake.homeModules.firefox = {
+ pkgs,
+ config,
+ lib,
+ inputs,
+ ...
+ }: {
+ options.customs = {
+ firefox.enable = lib.mkEnableOption "enable firefox";
+ };
+ config = lib.mkIf config.firefox.enable {
+ programs.firefox = {
+ enable = true;
+ package = pkgs.firefox;
+ profiles.ShyFox = {
+ isDefault = true;
+ extensions = {
+ packages = with pkgs.nur.repos.rycee.firefox-addons; [
+ bitwarden
+ ublock-origin
+ sponsorblock
+ return-youtube-dislikes
+ firefox-color
+ tampermonkey
+ duckduckgo-privacy-essentials
+ mal-sync
+ sidebery
+ ];
+
+ force = true;
+ # settings = {
+ # "{3c078156-979c-498b-8990-85f7987dd929}".settings =
+ # builtins.fromJSON (builtins.readFile "${inputs.shimmer.outPath}/sidebery.json");
+ # };
+ };
+
+ preConfig = builtins.readFile "${inputs.betterfox.outPath}/user.js";
+ userChrome = builtins.readFile "${inputs.shimmer.outPath}/userChrome.css";
+ userContent = builtins.readFile "${inputs.shimmer.outPath}/userContent.css";
+
+ settings = {
+ "shimmer.remove-winctr-buttons" = true;
+ "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
+ "svg.context-properties.content.enabled" = true;
+ "browser.search.suggest.enabled" = true;
+ "captivedetect.canonicalURL" = "http://detectportal.firefox.com/canonical.html";
+ "network.captive-portal-service.enabled" = true;
+ "network.connectivity-service.enabled" = true;
+ "extensions.autoDisableScopes" = 0;
+ };
+ search = {
+ force = true;
+ engines = {
+ "Brave" = {
+ urls = [
+ {template = "https://search.brave.com/search?q={searchTerms}";}
+ {
+ type = "application/x-suggestions+json";
+ template = "https://search.brave.com/api/suggest?q={searchTerms}";
+ }
+ ];
+
+ icon = "https://cdn.search.brave.com/serp/v2/_app/immutable/assets/safari-pinned-tab.539899c7.svg";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!br"];
+ };
+ "NixOS Packages" = {
+ urls = [{template = "https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!ns"];
+ };
+ "NixOS Options" = {
+ urls = [{template = "https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!no"];
+ };
+ "HomeManager" = {
+ urls = [{template = "https://home-manager-options.extranix.com/?query={searchTerms}&release=master";}];
+ icon = "https://github.com/mipmip/home-manager-option-search/blob/main/images/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!hs"];
+ };
+ "NixWiki" = {
+ urls = [{template = "https://wiki.nixos.org/w/index.php?search={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!nw"];
+ };
+ "Kinopoisk" = {
+ urls = [{template = "https://www.kinopoisk.ru/index.php?kp_query={searchTerms}";}];
+ icon = "https://www.kinopoisk.ru/favicon.ico";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!ks"];
+ };
+ "MDN Docs" = {
+ urls = [{template = "https://developer.mozilla.org/en-US/search?q={searchTerms}";}];
+ icon = "https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!md"];
+ };
+ };
+ default = "Brave";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/fish.nix b/modules/homeModules/fish.nix
new file mode 100644
index 0000000..1876da5
--- /dev/null
+++ b/modules/homeModules/fish.nix
@@ -0,0 +1,42 @@
+{inputs, ...}: {
+ flake.homeModules.fish = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ fish.enable = lib.mkEnableOption "enable fish";
+ };
+ config = lib.mkIf config.customs.fish.enable {
+ programs.direnv = {
+ enable = true;
+ nix-direnv.enable = true;
+ };
+
+ programs.nix-index.enableFishIntegration = true;
+
+ programs.fish = {
+ enable = true;
+ plugins = [
+ {
+ name = "pure";
+ src = pkgs.fishPlugins.pure.src;
+ }
+ {
+ name = "autopair";
+ src = pkgs.fishPlugins.autopair.src;
+ }
+ ];
+ interactiveShellInit = ''
+ set fish_greeting
+ pokemon-colorscripts -r --no-title
+ '';
+
+ shellAliases = {
+ ls = "ls --hyperlink=auto --color=auto";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/fuzzel.nix b/modules/homeModules/fuzzel.nix
new file mode 100644
index 0000000..b8af295
--- /dev/null
+++ b/modules/homeModules/fuzzel.nix
@@ -0,0 +1,33 @@
+{inputs, ...}: {
+ flake.homeModules.fuzzel = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ fuzzel.enable = lib.mkEnableOption "enable fuzzel";
+ };
+
+ config = lib.mkIf config.customs.fuzzel.enable {
+ programs.fuzzel = {
+ enable = true;
+ settings = {
+ colors = {
+ background = "1f1d2eff";
+ text = "6e6a86ff";
+ selection = "908caaff";
+ selection-text = "1f1d2eff";
+ };
+ main = {
+ lines = 9;
+ terminal = "alacritty -e";
+ vertical-pad = 0;
+ horizontal-pad = 0;
+ };
+ border.width = 0;
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/hyprland.nix b/modules/homeModules/hyprland.nix
new file mode 100644
index 0000000..e6455e3
--- /dev/null
+++ b/modules/homeModules/hyprland.nix
@@ -0,0 +1,236 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.homeModules.hyprland = {
+ pkgs,
+ lib,
+ config,
+ inputs,
+ ...
+ }: {
+ modules = [
+ self.homeModules.waybar
+ self.homeModules.rofi
+ self.homeModules.mako
+ ];
+
+ options.customs = {
+ hyprland.enable = lib.mkEnableOption "enable hyprland";
+ };
+
+ config = lib.mkIf config.customs.hyprland.enable {
+ waybar.enable = true;
+ rofi.enable = true;
+ mako.enable = true;
+
+ home.packages = with pkgs; [
+ swww
+ brightnessctl
+ grimblast
+ cliphist
+ polkit_gnome
+ wl-clipboard
+ libnotify
+ socat
+ ];
+
+ home.sessionVariables.XDG_CURRENT_DESKTOP = "Hyprland";
+
+ wayland.windowManager.hyprland = {
+ enable = true;
+ settings = {
+ "$scripts" = "${./attachments/hypr-scripts}";
+ "$mainMod" = "SUPER";
+ "$terminal" = "alacritty";
+
+ exec-once = [
+ "emacs --daemon"
+ "swww-daemon"
+ "swww img ${config.wallpaper}"
+ "$scripts/bitwarden-float.sh"
+ ];
+
+ monitor = [
+ "eDP-1,preferred,auto,1.6"
+ ",preferred,auto,1,mirror,eDP-1"
+ ];
+
+ input = {
+ kb_layout = "us,ru";
+ kb_options = "grp:win_space_toggle";
+ touchpad = {
+ natural_scroll = "yes";
+ disable_while_typing = "no";
+ };
+ sensitivity = 0.1;
+ };
+
+ general = {
+ gaps_in = 5;
+ gaps_out = 15;
+ border_size = 3;
+ layout = "dwindle";
+ allow_tearing = false;
+ };
+
+ misc = {
+ vfr = true;
+ };
+
+ decoration = {
+ rounding = 7;
+ blur = {
+ enabled = true;
+ size = 3;
+ passes = 1;
+ };
+ };
+
+ xwayland.force_zero_scaling = true;
+ env = [
+ "GDK_SCALE,2"
+ "XCURSOR_SIZE,32"
+ ];
+
+ animations = {
+ enabled = 1;
+ # bezier = "overshot,0.13,0.99,0.29,1.1,";
+ animation = [
+ "fade,1,4,default"
+ "workspaces,1,4,default,fade"
+ "windows,1,4,default,popin 95%"
+ "windowsMove,0"
+ ];
+ };
+
+ dwindle = {
+ pseudotile = "yes";
+ preserve_split = "yes";
+ };
+
+ gesture = [
+ "3, horizontal, workspace"
+ ];
+ misc.force_default_wallpaper = 1;
+
+ windowrule = [
+ "bordersize 0, floating:0, onworkspace:w[tv1]"
+ "rounding 0, floating:0, onworkspace:w[tv1]"
+ "bordersize 0, floating:0, onworkspace:f[1]"
+ "rounding 0, floating:0, onworkspace:f[1]"
+ ];
+
+ workspace = [
+ "w[tv1], gapsout:0, gapsin:0"
+ "f[1], gapsout:0, gapsin:0"
+ ];
+
+ windowrulev2 = [
+ "float, class:^(org.telegram.desktop)$"
+ "pin, class:^(org.telegram.desktop)$"
+ "size 30% 845, class:^(org.telegram.desktop)$"
+ "move 100%-w-25 73, class:^(org.telegram.desktop)$"
+ # firefox
+ "float, title:(Sharing Indicator)"
+ "noborder, title:(Sharing Indicator)"
+ "rounding 0, title:(Sharing Indicator)"
+ "float, title:(Picture-in-Picture)"
+ "pin, title:(Picture-in-Picture)"
+ "move 100%-w-21 100%-w-21, title:^(Picture-in-Picture)$"
+ "noinitialfocus, title:^(Picture-in-Picture)$"
+ "float, title:^(Save File)$"
+ "pin, title:^(Save File)$"
+ # dragon-drop
+ "pin, class:^(dragon-drop)$"
+ # torrent
+ "float, title:^(Torrent Options)$"
+ "pin, title:^(Torrent Options)$"
+ # xwaylandvideobridge
+ "opacity 0.0 override 0.0 override,class:^(xwaylandvideobridge)$"
+ "noanim,class:^(xwaylandvideobridge)$"
+ "noinitialfocus,class:^(xwaylandvideobridge)$"
+ "maxsize 1 1,class:^(xwaylandvideobridge)$"
+ "noblur,class:^(xwaylandvideobridge)$"
+ ];
+
+ bind =
+ [
+ "$mainMod, V, togglefloating, "
+ "$mainMod, P, pseudo,"
+ "$mainMod, I, togglesplit,"
+ "$mainMod, F, fullscreen, 0"
+ "$mainMod, M, fullscreen, 1"
+ "$mainMod SHIFT, Q, killactive, "
+ "$mainMod SHIFT, E, exit,"
+
+ # Apps
+ "$mainMod, D, exec, pkill rofi || rofi -show-icons -show drun"
+ "$mainMod, Q, exec, $terminal"
+ "$mainMod, B, exec, zen-beta"
+ "$mainMod, T, exec, Telegram"
+ "$mainMod, E, exec, emacsclient -c -a emacs"
+ "$mainMod CONTROL, E, exec, emacs"
+ "$mainMod, T, exec, $scripts/toggle-tg.sh"
+ "$mainMod SHIFT, Esc, exec, swww img ${config.wallpaper}"
+ ",XF86Favorites, exec, bash $scripts/toggle-vpn.sh"
+
+ # Screenshooting
+ ", Print, exec, grimblast save screen"
+ "ALT, Print, exec, grimblast save active"
+ "SHIFT, Print, exec, grimblast save area"
+ "CONTROL, Print, exec, grimblast copy screen"
+ "ALT_CONTROL, Print, exec, grimblast copy active"
+ "CONTROL_SHIFT, Print, exec, grimblast copy area "
+
+ # Windows
+ "$mainMod, J, movefocus, d"
+ "$mainMod, K, movefocus, u"
+ "$mainMod, H, movefocus, l"
+ "$mainMod, L, movefocus, r"
+ "SUPER_SHIFT,J,movewindow,d"
+ "SUPER_SHIFT,K,movewindow,u"
+ "SUPER_SHIFT,H,movewindow,l"
+ "SUPER_SHIFT,L,movewindow,r"
+ "$mainMod, mouse_down, workspace, e+1"
+ "$mainMod, mouse_up, workspace, e-1"
+ ]
+ ++ (
+ # workspaces
+ # binds $mod + [shift +] {1..10} to [move to] workspace {1..10}
+ builtins.concatLists (builtins.genList (
+ x: let
+ ws = let
+ c = (x + 1) / 10;
+ in
+ builtins.toString (x + 1 - (c * 10));
+ in [
+ "$mainMod, ${ws}, workspace, ${toString (x + 1)}"
+ "$mainMod SHIFT, ${ws}, movetoworkspacesilent, ${toString (x + 1)}"
+ ]
+ )
+ 10)
+ );
+ binde = [
+ # Volume
+ ",0x1008FF11,exec,wpctl set-volume @DEFAULT_SINK@ 5%-"
+ ",0x1008FF13,exec,wpctl set-volume @DEFAULT_SINK@ 5%+"
+ ",0x1008FF12,exec,wpctl set-mute @DEFAULT_SINK@ toggle"
+ ",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle"
+ ",XF86TouchpadToggle, exec, python3 $scripts/switch-sink.py"
+
+ # Brightness
+ ",XF86MonBrightnessUp,exec,brightnessctl s +5%"
+ ",XF86MonBrightnessDown,exec,brightnessctl s 5%-"
+ ];
+
+ bindm = [
+ "$mainMod, mouse:272, movewindow"
+ "$mainMod, mouse:273, resizewindow"
+ ];
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/mako.nix b/modules/homeModules/mako.nix
new file mode 100644
index 0000000..d9f9ae2
--- /dev/null
+++ b/modules/homeModules/mako.nix
@@ -0,0 +1,24 @@
+{inputs, ...}: {
+ flake.homeModules.mako = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ mako.enable = lib.mkEnableOption "enable mako";
+ };
+
+ config = lib.mkIf config.customs.mako.enable {
+ services.mako = {
+ enable = true;
+ settings = {
+ anchor = "bottom-right";
+ default-timeout = 5000;
+ border-size = 3;
+ border-radius = 7;
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/niri.nix b/modules/homeModules/niri.nix
new file mode 100644
index 0000000..4317da7
--- /dev/null
+++ b/modules/homeModules/niri.nix
@@ -0,0 +1,264 @@
+{inputs, ...}: {
+ flake.homeModules.niri = {
+ pkgs,
+ config,
+ lib,
+ inputs,
+ ...
+ }:
+ with lib; let
+ cfg = config.niri.customs;
+ in {
+ imports = [
+ ./waybar.nix
+ ./rofi.nix
+ ./mako.nix
+ inputs.niri.homeModules.niri
+ inputs.niri.homeModules.stylix
+ ];
+
+ options.customs = {
+ niri = {
+ enable = mkEnableOption "enable niri config";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ waybar = {
+ enable = true;
+ windowManager = "niri";
+ workspaceIcons = {
+ # "1" = "α";
+ # "2" = "β";
+ # "3" = "γ";
+ # "4" = "δ";
+ # "5" = "ε";
+ # urgent = "λ";
+ # default = "ω";
+ "internet" = "";
+ "discord" = "";
+ "chat" = "<b></b>";
+
+ "active" = "";
+ "default" = "";
+ };
+ };
+ rofi.enable = true;
+ mako.enable = true;
+
+ home.packages = with pkgs; [
+ pkgs.xwayland-satellite
+ swww
+ brightnessctl
+ grimblast
+ polkit_gnome
+ wl-clipboard
+ libnotify
+ wl-mirror
+ playerctl
+ ];
+
+ stylix.targets.niri.enable = true;
+ programs.niri = {
+ enable = true;
+
+ settings = {
+ input = {
+ keyboard.xkb = {
+ layout = "us,ru";
+ options = "grp:win_space_toggle,compose:ralt,ctrl:nocaps";
+ };
+ touchpad = {
+ tap = true;
+ dwt = false;
+ dwtp = true;
+ natural-scroll = true;
+ };
+ warp-mouse-to-focus.enable = true;
+ focus-follows-mouse = {
+ enable = true;
+ max-scroll-amount = "25%";
+ };
+ };
+
+ cursor = {
+ theme = "Bibata-Modern-Ice";
+ size = 24;
+ };
+
+ layout = {
+ gaps = 16;
+ center-focused-column = "never";
+ preset-column-widths = [
+ {proportion = 0.33333;}
+ {proportion = 0.5;}
+ {proportion = 0.66667;}
+ ];
+ border = {
+ enable = true;
+ };
+ focus-ring.enable = false;
+ };
+
+ animations = {
+ workspace-switch.enable = false;
+ };
+
+ # workspaces = {
+ # internet = {};
+ # code = {};
+ # };
+
+ window-rules = [
+ {
+ matches = [
+ {
+ app-id = "steam";
+ title = ''r#"^notificationtoasts_\d+_desktop$"#'';
+ }
+ ];
+ default-floating-position = {
+ x = 20;
+ y = 10;
+ relative-to = "bottom-right";
+ };
+ }
+ ];
+
+ spawn-at-startup = [
+ {argv = ["swww-daemon"];}
+ {argv = ["mako"];}
+ {argv = ["swww img ${config.wallpaper}"];}
+ ];
+
+ prefer-no-csd = true;
+
+ binds = with config.lib.niri.actions; let
+ scripts = "${./attachments/hypr-scripts}";
+ in {
+ "Mod+Q".action.spawn = "alacritty";
+ "Mod+D".action.spawn = ["sh" "-c" "pkill rofi || rofi -show-icons -show drun"];
+ "Mod+B".action.spawn = "zen-beta";
+ "Mod+E".action.spawn = ["emacsclient" "-c" "-a" "emacs"];
+ "Mod+T".action.spawn = "Telegram";
+
+ "XF86AudioRaiseVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05+"];
+ "XF86AudioLowerVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05-"];
+ "XF86AudioMute".action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"];
+ "XF86AudioMicMute".action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"];
+
+ "XF86MonBrightnessUp".action.spawn = ["brightnessctl" "s" "+5%"];
+ "XF86MonBrightnessDown".action.spawn = ["brightnessctl" "s" "5%-"];
+
+ "XF86Favorites".action.spawn = "${scripts}/toggle-vpn.sh";
+ "XF86TouchpadToggle".action.spawn = "${scripts}/switch-sink.py";
+
+ "XF86AudioPlay".action.spawn = ["playerctl" "play-pause"];
+
+ "Mod+Shift+Slash".action = show-hotkey-overlay;
+
+ "Mod+Alt+L".action.spawn = "swaylock";
+ "Mod+Shift+E".action = quit;
+ "Mod+Shift+P".action = power-off-monitors;
+ "Mod+Shift+Q".action = close-window;
+
+ "Mod+H".action = focus-column-left;
+ "Mod+J".action = focus-window-down;
+ "Mod+K".action = focus-window-up;
+ "Mod+L".action = focus-column-right;
+
+ "Mod+Shift+H".action = move-column-left;
+ "Mod+Shift+J".action = move-window-down;
+ "Mod+Shift+K".action = move-window-up;
+ "Mod+Shift+L".action = move-column-right;
+
+ "Mod+Home".action = focus-column-first;
+ "Mod+End".action = focus-column-last;
+
+ "Mod+Ctrl+Home".action = move-column-to-first;
+ "Mod+Ctrl+End".action = move-column-to-last;
+
+ "Mod+Ctrl+H".action = focus-monitor-left;
+ "Mod+Ctrl+J".action = focus-monitor-down;
+ "Mod+Ctrl+K".action = focus-monitor-up;
+ "Mod+Ctrl+L".action = focus-monitor-right;
+
+ "Mod+Shift+Ctrl+H".action = move-column-to-monitor-left;
+ "Mod+Shift+Ctrl+J".action = move-column-to-monitor-down;
+ "Mod+Shift+Ctrl+K".action = move-column-to-monitor-up;
+ "Mod+Shift+Ctrl+L".action = move-column-to-monitor-right;
+ "Mod+P" = {
+ repeat = false;
+ action.spawn-sh = "wl-mirror $(niri msg --json focused-output | jq -r .name)";
+ };
+
+ "Mod+U".action = focus-workspace-down;
+ "Mod+I".action = focus-workspace-up;
+
+ "Mod+Ctrl+U".action = move-column-to-workspace-down;
+ "Mod+Ctrl+I".action = move-column-to-workspace-up;
+
+ "Mod+Shift+U".action = move-workspace-down;
+ "Mod+Shift+I".action = move-workspace-down;
+
+ "Mod+WheelScrollRight".action = focus-column-right;
+ "Mod+WheelScrollLeft".action = focus-column-left;
+
+ "Mod+Ctrl+WheelScrollRight".action = move-column-right;
+ "Mod+Ctrl+WheelScrollLeft".action = move-column-left;
+
+ "Mod+Shift+WheelScrollDown".action = focus-column-right;
+ "Mod+Shift+WheelScrollUp".action = focus-column-left;
+
+ "Mod+Ctrl+Shift+WheelScrollDown".action = move-column-right;
+ "Mod+Ctrl+Shift+WheelScrollUp".action = move-column-left;
+
+ "Mod+1".action.focus-workspace = 1;
+ "Mod+2".action.focus-workspace = 2;
+ "Mod+3".action.focus-workspace = 3;
+ "Mod+4".action.focus-workspace = 4;
+ "Mod+5".action.focus-workspace = 5;
+ "Mod+6".action.focus-workspace = 6;
+ "Mod+7".action.focus-workspace = 7;
+ "Mod+8".action.focus-workspace = 8;
+ "Mod+9".action.focus-workspace = 9;
+
+ "Mod+Shift+1".action.move-column-to-workspace = 1;
+ "Mod+Shift+2".action.move-column-to-workspace = 2;
+ "Mod+Shift+3".action.move-column-to-workspace = 3;
+ "Mod+Shift+4".action.move-column-to-workspace = 4;
+ "Mod+Shift+5".action.move-column-to-workspace = 5;
+ "Mod+Shift+6".action.move-column-to-workspace = 6;
+ "Mod+Shift+7".action.move-column-to-workspace = 7;
+ "Mod+Shift+8".action.move-column-to-workspace = 8;
+ "Mod+Shift+9".action.move-column-to-workspace = 9;
+
+ "Mod+Comma".action = consume-window-into-column;
+ "Mod+Period".action = expel-window-from-column;
+ "Mod+BracketLeft".action = consume-or-expel-window-left;
+ "Mod+BracketRight".action = consume-or-expel-window-right;
+
+ "Mod+M".action = maximize-column;
+ "Mod+F".action = fullscreen-window;
+ "Mod+C".action = center-column;
+
+ "Mod+R".action = switch-preset-column-width;
+ "Mod+Minus".action.set-column-width = "-10%";
+ "Mod+Equal".action.set-column-width = "+10%";
+ "Mod+Shift+Minus".action.set-window-height = "-10%";
+ "Mod+Shift+Equal".action.set-window-height = "+10%";
+
+ "Mod+V".action = toggle-window-floating;
+ "Mod+Ctrl+V".action = switch-focus-between-floating-and-tiling;
+
+ "Print".action.screenshot.show-pointer = true;
+ "Shift+Print".action.screenshot-screen.write-to-disk = true;
+ "Ctrl+Shift+Print".action.screenshot-screen.write-to-disk = false;
+ "Alt+Print".action.screenshot-window.write-to-disk = true;
+ "Ctrl+Alt+Print".action.screenshot-window.write-to-disk = false;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/nvim.nix b/modules/homeModules/nvim.nix
new file mode 100644
index 0000000..cd93f2d
--- /dev/null
+++ b/modules/homeModules/nvim.nix
@@ -0,0 +1,12 @@
+{inputs, ...}: {
+ flake.homeModules.nvim = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ nvim.enable = lib.mkEnableOption "enable nvim";
+ };
+ };
+}
diff --git a/modules/homeModules/picom.nix b/modules/homeModules/picom.nix
new file mode 100644
index 0000000..a7835eb
--- /dev/null
+++ b/modules/homeModules/picom.nix
@@ -0,0 +1,57 @@
+{inputs, ...}: {
+ flake.homeModules.picom = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ picom.enable = lib.mkEnableOption "enable picom";
+ };
+
+ config = lib.mkIf config.customs.picom.enable {
+ services.picom = {
+ enable = true;
+ settings = {
+ fading = true;
+ fade-delta = 3;
+ blur-background = false;
+ corner-radius = 8;
+ # blur = {
+ # method = "dual_kawase";
+ # size = 12;
+ # deviation = false;
+ # strength = 2;
+ # kern = "3x3box";
+ # };
+ backend = "glx";
+ vsync = true;
+ mark = {
+ wmwin-focused = true;
+ overdir-focused = true;
+ };
+ detect = {
+ rounded-corners = true;
+ client-opacity = true;
+ transient = true;
+ };
+ use-ewmh-active-win = true;
+ glx-no-stencil = true;
+ use-damage = true;
+ };
+ wintypes = {
+ tooltip = {
+ fade = true;
+ shadow = true;
+ full-shadow = false;
+ blur = false;
+ focus = true;
+ };
+ dock = {
+ shadow = false;
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/rofi.nix b/modules/homeModules/rofi.nix
new file mode 100644
index 0000000..735dc77
--- /dev/null
+++ b/modules/homeModules/rofi.nix
@@ -0,0 +1,50 @@
+{inputs, ...}: {
+ flake.homeModules.rofi = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ rofi.enable = lib.mkEnableOption "enable rofi";
+ rofi.package = lib.mkPackageOption pkgs "rofi package" {default = ["rofi"];};
+ };
+
+ config = lib.mkIf config.customs.rofi.enable {
+ programs.rofi = {
+ enable = true;
+ package = config.customs.rofi.package;
+ theme = with config.lib.stylix.colors.withHashtag;
+ builtins.toFile "theme.rasi" ''
+ * {
+ font: "FiraCode Nerd Font Medium 12";
+
+ bg0: ${base01};
+ bg1: ${base02};
+ fg0: ${base04};
+
+ accent-color: ${base03};
+ urgent-color: #ffffff;
+
+ background-color: transparent;
+ text-color: @fg0;
+
+ margin: 0;
+ padding: 0;
+ spacing: 0;
+ }
+
+ ${builtins.readFile ./attachments/rofi-theme.rasi}'';
+ cycle = true;
+ plugins = with pkgs; [
+ rofi-emoji
+ rofi-calc
+ ];
+ extraConfig = {
+ kb-row-up = "Up,Ctrl+p";
+ kb-row-down = "Down,Ctrl+n";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/stylix.nix b/modules/homeModules/stylix.nix
new file mode 100644
index 0000000..1a28fe9
--- /dev/null
+++ b/modules/homeModules/stylix.nix
@@ -0,0 +1,80 @@
+{inputs, ...}: {
+ flake.homeModules.stylixConfig = {
+ pkgs,
+ config,
+ lib,
+ inputs,
+ ...
+ }: {
+ imports = [
+ inputs.stylix.homeModules.stylix
+ ];
+
+ options.customs = {
+ stylixConfig = {
+ enable = lib.mkEnableOption "enable stylix";
+ theme = lib.mkOption {type = lib.types.str;};
+ };
+ wallpaper = lib.mkOption {type = with lib.types; oneOf [str path package];};
+ };
+ config = lib.mkIf config.customs.stylixConfig.enable {
+ wallpaper = with config.lib.stylix.colors.withHashtag;
+ pkgs.runCommand "cat.png" {} ''
+ pastel=${pkgs.pastel}/bin/pastel
+ SHADOWS=$($pastel darken 0.1 '${base05}' | $pastel format hex)
+ TAIL=$($pastel lighten 0.1 '${base02}' | $pastel format hex)
+ HIGHLIGHTS=$($pastel lighten 0.1 '${base05}' | $pastel format hex)
+
+ ${pkgs.imagemagick}/bin/convert ${./attachments/basecat.png} \
+ -fill '${base00}' -opaque black \
+ -fill '${base05}' -opaque white \
+ -fill '${base08}' -opaque blue \
+ -fill $SHADOWS -opaque gray \
+ -fill '${base02}' -opaque orange \
+ -fill $TAIL -opaque green \
+ -fill $HIGHLIGHTS -opaque brown \
+ $out'';
+ stylix = {
+ enable = true;
+ targets = {
+ rofi.enable = false;
+ waybar.enable = false;
+ firefox.profileNames = ["ShyFox"];
+ };
+
+ polarity = "dark";
+
+ base16Scheme = "${pkgs.base16-schemes}/share/themes/${config.customs.stylixConfig.theme}.yaml";
+ image = config.wallpaper;
+ cursor = {
+ package = pkgs.bibata-cursors;
+ name = "Bibata-Modern-Ice";
+ size = 24;
+ };
+
+ fonts = {
+ sizes.terminal = 11;
+ serif = {
+ package = pkgs.noto-fonts;
+ name = "Noto Serif";
+ };
+
+ sansSerif = {
+ package = pkgs.rubik;
+ name = "Rubik";
+ };
+
+ monospace = {
+ package = pkgs.nerd-fonts.fira-code;
+ name = "FiraCode Nerd Font";
+ };
+
+ emoji = {
+ package = pkgs.noto-fonts-color-emoji;
+ name = "Noto Color Emoji";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/sxhkd.nix b/modules/homeModules/sxhkd.nix
new file mode 100644
index 0000000..2172599
--- /dev/null
+++ b/modules/homeModules/sxhkd.nix
@@ -0,0 +1,45 @@
+{inputs, ...}: {
+ flake.homeModules.sxhkd = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }: {
+ options.customs = {
+ sxhkd.enable = lib.mkEnableOption "enable sxhkd";
+ };
+
+ config = lib.mkIf config.customs.sxhkd.enable {
+ services.sxhkd = {
+ enable = true;
+ keybindings = {
+ "{_,shift} + Print" = "xfce4-screenshooter -{r,f}";
+ "{_,shift} + control + Print" = "xfce4-screenshooter -{r,f} --save /dev/stdout | xclip -i -selection clipboard -t image/png";
+ "super + apostrophe" = "betterlockscreen -l"; # Lockscreen
+ "super + grave" = "polybar -r"; # Restart polybar
+ "super + q" = "alacritty"; # Open terminal
+ "super + d" = "rofi -show-icons -show drun"; # Open app chooser
+ "super + shift + d" = "CM_LAUNCHER=rofi clipmenu";
+ "super + b" = "zen-beta"; # Open browser
+ "super + e" = "emacsclient -c -a 'emacs'"; # Open emacs
+ "super + Escape" = "pkill -USR1 -x sxhkd"; # Restart sxhkd
+ "super + shift + {e,r}" = "bspc {quit,wm -r}"; # Quit/restart bspwm
+ "super + {control,shift} + q" = "bspc node -{k,c}"; # Close/kill window
+ "super + m" = "bspc desktop -l next"; # Maximise window
+ "super + {t,shift + t,v,f}" = "bspc node -t {tiled,pseudo_tiled,floating,fullscreen}"; # Set window state
+ "super + {_,shift + }{h,j,k,l}" = "bspc node -{f,s} {west,south,north,east}"; # Focus window in the given direction
+ "super + {Left,Down,Up,Right}" = "bspc node -v {-20 0,0 20,0 -20,20 0}"; # Move a floating window
+ "super + s : {h,j,k,l}" = '' STEP=20; SELECTION={1,2,3,4};\
+ bspc node -z $(echo "left -$STEP 0,bottom 0 $STEP,top 0 -$STEP,right $STEP 0" | cut -d',' -f$SELECTION) ||\
+ bspc node -z $(echo "right -$STEP 0,top 0 $STEP,bottom 0 -$STEP,left $STEP 0" | cut -d',' -f$SELECTION)''; # Better window resize
+ "super + bracket{left,right}" = "bspc desktop -f {prev,next}.local"; # Focus next/previos desktop
+ "super + {_,shift + }{1-9,0}" = "bspc {desktop -f,node -d} $(bspc query -D -m focused | awk 'NR=={1-9,0}')"; # Focus/send window to the given desktop on the focused monitor
+ "super + o" = "bspc node -m last -f"; # Send window to the last used monitor
+ "super + ctrl + {1-9}" = "bspc node -o 0.{1-9}"; # Preselect the window ratio
+ "super + ctrl + space" = "bspc node -p cancel"; # Cansel the preselected ratio
+ "super + n" = "fish ~/.nixfiles/home-manager/home/services/polybar/hide.fish";
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/tmux.nix b/modules/homeModules/tmux.nix
new file mode 100644
index 0000000..97f85dc
--- /dev/null
+++ b/modules/homeModules/tmux.nix
@@ -0,0 +1,50 @@
+{inputs, ...}: {
+ flake.homeModules.tmux = {
+ pkgs,
+ lib,
+ config,
+ ...
+ }: {
+ options.customs = {
+ tmux.enable = lib.mkEnableOption "enable tmux config";
+ };
+
+ config = lib.mkIf config.customs.tmux.enable {
+ stylix.targets.tmux.enable = true;
+ home.packages = with pkgs; [
+ fzf
+ ];
+ programs.tmux = {
+ enable = true;
+ prefix = "C-x";
+ baseIndex = 1;
+ historyLimit = 10000;
+ extraConfig = ''
+ set -g mode-keys vi
+ set -g default-terminal "''${TERM}"
+ set -sg terminal-overrides ",*:RGB"
+
+ set -g pane-border-lines simple
+
+ set -g escape-time 0
+ set -g renumber-windows on
+
+ set -g status-style bg=default,fg=black,bright
+ set -g status-left ""
+ set -g window-status-format " #W "
+ set -g window-status-current-format " #W "
+
+ set -g window-status-bell-style "bg=red,nobold"
+ set -g window-status-current-style \
+ "#{?window_zoomed_flag,bg=yellow,bg=green,nobold}"
+
+ bind j next-window
+ bind k previous-window
+ '';
+ plugins = with pkgs.tmuxPlugins; [
+ tmux-fzf
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/waybar.nix b/modules/homeModules/waybar.nix
new file mode 100644
index 0000000..b1f6dce
--- /dev/null
+++ b/modules/homeModules/waybar.nix
@@ -0,0 +1,131 @@
+{inputs, ...}: {
+ flake.homeModules.waybar = {
+ pkgs,
+ lib,
+ config,
+ ...
+ }:
+ with lib; let
+ cfg = config.customs.waybar;
+ in {
+ options.customs = {
+ waybar = {
+ enable = mkEnableOption "enable waybar";
+ windowManager = mkOption {
+ description = "WM string to use with /workspaces and /language";
+ default = "hyprland";
+ type = types.str;
+ };
+ workspaceIcons = mkOption {
+ default = {
+ "1" = "α";
+ "2" = "β";
+ "3" = "γ";
+ "4" = "δ";
+ "5" = "ε";
+ urgent = "λ";
+ default = "ω";
+ };
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ programs.waybar = {
+ enable = true;
+ systemd.enable = true;
+ style = with config.lib.stylix.colors.withHashtag;
+ ''
+ @define-color base00 ${base00}; @define-color base01 ${base01}; @define-color base02 ${base02}; @define-color base03 ${base03};
+ @define-color base04 ${base04}; @define-color base05 ${base05}; @define-color base06 ${base06}; @define-color base07 ${base07};
+
+ @define-color base08 ${base08}; @define-color base09 ${base09}; @define-color base0A ${base0A}; @define-color base0B ${base0B};
+ @define-color base0C ${base0C}; @define-color base0D ${base0D}; @define-color base0E ${base0E}; @define-color base0F ${base0F};
+ ''
+ + builtins.readFile ./attachments/waybar-style.css;
+ settings = {
+ bar = {
+ layer = "top";
+ height = 30;
+ spacing = 8;
+ margin-down = 5;
+ modules-left = ["${cfg.windowManager}/workspaces"];
+ modules-center = ["clock"];
+ modules-right = ["network" "custom/vpn" "memory" "temperature" "backlight" "pulseaudio" "${cfg.windowManager}/language" "tray" "battery"];
+ "${cfg.windowManager}/workspaces" = {
+ format = "{icon}";
+ "format-icons" = cfg.workspaceIcons;
+ };
+ "${cfg.windowManager}/language" = {
+ format = "{} <span font-family='Material Design Icons' rise='-1000' size='medium'>󰌌</span>";
+ format-ru = "ru";
+ format-en = "en";
+ };
+ "tray" = {
+ spacing = 10;
+ };
+ "clock" = {
+ format = "{:%H:%M 󰅐}";
+ tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
+ format-alt = "{:%d %h %Y 󰃮}";
+ };
+ "memory" = {
+ format = "{}% ";
+ };
+ "temperature" = {
+ format = "{temperatureC}°C󰔏";
+ };
+ "backlight" = {
+ format = "{percent}% {icon}";
+ format-icons = ["󰃞" "󰃟" "󰃠"];
+ };
+ "battery" = {
+ "states" = {
+ good = 95;
+ warning = 30;
+ critical = 15;
+ };
+ format = "{capacity}% {icon}";
+ format-charging = "{capacity}% 󰂄";
+ format-plugged = "{capacity}% ";
+ format-alt = "{icon}";
+ format-icons = ["󱃍" "󰁼" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"];
+ };
+ "network" = {
+ interface = "wlp*";
+ format-wifi = "{essid} ({signalStrength}%) 󰤨";
+ format-ethernet = "{ipaddr}/{cidr} 󰈀";
+ tooltip-format = "{ifname} via {gwaddr} 󰩟";
+ format-linked = "{ifname} (No IP) 󰩟";
+ format-disconnected = "󰤫";
+ };
+ "custom/vpn" = {
+ format = "{text}";
+ exec = "${./attachments/hypr-scripts/toggle-vpn.sh} waybar";
+ return-type = "json";
+ };
+ "pulseaudio" = {
+ format = "{volume}% {icon} {format_source}";
+ format-bluetooth = "{volume}% <span font-family='Material Design Icons' rise='-2000' font-size='x-large'>󰥰</span> {format_source}";
+ format-bluetooth-muted = "󰟎 {format_source}";
+ format-muted = "󰝟 {format_source}";
+ format-source = "{volume}%󰍬";
+ format-source-muted = "󰍭";
+
+ "format-icons" = {
+ headphone = "󰋋";
+ hands-free = "";
+ headset = "";
+ phone = "";
+ portable = "";
+ car = "";
+ muted-icon = "󰝟";
+ default = ["󰕿" "󰖀" "󰕾"];
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/homeModules/zen-browser.nix b/modules/homeModules/zen-browser.nix
new file mode 100644
index 0000000..ae073ab
--- /dev/null
+++ b/modules/homeModules/zen-browser.nix
@@ -0,0 +1,151 @@
+{inputs, ...}: {
+ flake.homeModules.zen-browser = {
+ pkgs,
+ config,
+ lib,
+ inputs,
+ ...
+ }: {
+ imports = [
+ inputs.zen-browser.homeModules.beta
+ ];
+
+ options.customs = {
+ zen-browser.enable = lib.mkEnableOption "enable zen-browser";
+ };
+
+ config = lib.mkIf config.customs.zen-browser.enable {
+ stylix.targets.zen-browser.profileNames = ["ZZZ"];
+ xdg.mimeApps = {
+ enable = true;
+ associations.added = {
+ "x-scheme-handler/http" = "zen-beta.desktop";
+ "x-scheme-handler/https" = "zen-beta.desktop";
+ "x-scheme-handler/chrome" = "zen-beta.desktop";
+ "text/html" = "zen-beta.desktop";
+ "application/x-extension-htm" = "zen-beta.desktop";
+ "application/x-extension-html" = "zen-beta.desktop";
+ "application/x-extension-shtml" = "zen-beta.desktop";
+ "application/xhtml+xml" = "zen-beta.desktop";
+ "application/x-extension-xhtml" = "zen-beta.desktop";
+ "application/x-extension-xht" = "zen-beta.desktop";
+ };
+
+ defaultApplications = {
+ "x-scheme-handler/http" = "zen-beta.desktop";
+ "x-scheme-handler/https" = "zen-beta.desktop";
+ "x-scheme-handler/chrome" = "zen-beta.desktop";
+ "text/html" = "zen-beta.desktop";
+ "application/x-extension-htm" = "zen-beta.desktop";
+ "application/x-extension-html" = "zen-beta.desktop";
+ "application/x-extension-shtml" = "zen-beta.desktop";
+ "application/xhtml+xml" = "zen-beta.desktop";
+ "application/x-extension-xhtml" = "zen-beta.desktop";
+ "application/x-extension-xht" = "zen-beta.desktop";
+ "application/pdf" = "zen-beta.desktop";
+ };
+ };
+
+ programs.zen-browser = {
+ enable = true;
+ policies = {
+ AutofillAddressEnabled = false;
+ AutofillCreditCardEnabled = false;
+ DisableAppUpdate = true;
+ DisableFeedbackCommands = true;
+ DisableFirefoxStudies = true;
+ DisableTelemetry = true;
+ DontCheckDefaultBrowser = true;
+ NoDefaultBookmarks = true;
+ OfferToSaveLogins = false;
+ Certificates = {
+ ImportEnterpriseRoots = true;
+ };
+ };
+
+ profiles.ZZZ = {
+ isDefault = true;
+ extensions = {
+ packages = with pkgs.nur.repos.rycee.firefox-addons; [
+ bitwarden
+ ublock-origin
+ sponsorblock
+ return-youtube-dislikes
+ firefox-color
+ tampermonkey
+ duckduckgo-privacy-essentials
+ mal-sync
+ sidebery
+ ];
+
+ force = true;
+ };
+
+ settings = {
+ "svg.context-properties.content.enabled" = true;
+ "browser.search.suggest.enabled" = true;
+ "extensions.autoDisableScopes" = 0;
+ "zen.view.grey-out-inactive-windows" = false;
+ "zen.view.experimental-no-window-controls" = true;
+ };
+
+ search = {
+ force = true;
+ engines = {
+ "Brave" = {
+ urls = [
+ {template = "https://search.brave.com/search?q={searchTerms}";}
+ {
+ type = "application/x-suggestions+json";
+ template = "https://search.brave.com/api/suggest?q={searchTerms}";
+ }
+ ];
+
+ icon = "https://cdn.search.brave.com/serp/v2/_app/immutable/assets/safari-pinned-tab.539899c7.svg";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!br"];
+ };
+ "NixOS Packages" = {
+ urls = [{template = "https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!ns"];
+ };
+ "NixOS Options" = {
+ urls = [{template = "https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!no"];
+ };
+ "HomeManager" = {
+ urls = [{template = "https://home-manager-options.extranix.com/?query={searchTerms}&release=master";}];
+ icon = "https://github.com/mipmip/home-manager-option-search/blob/main/images/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!hs"];
+ };
+ "NixWiki" = {
+ urls = [{template = "https://wiki.nixos.org/w/index.php?search={searchTerms}";}];
+ icon = "https://nixos.org/favicon.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!nw"];
+ };
+ "Kinopoisk" = {
+ urls = [{template = "https://www.kinopoisk.ru/index.php?kp_query={searchTerms}";}];
+ icon = "https://www.kinopoisk.ru/favicon.ico";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!ks"];
+ };
+ "MDN Docs" = {
+ urls = [{template = "https://developer.mozilla.org/en-US/search?q={searchTerms}";}];
+ icon = "https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png";
+ updateInterval = 24 * 60 * 60 * 1000;
+ definedAliases = ["!md"];
+ };
+ };
+ default = "Brave";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/hosts/ltrr-block/age.nix b/modules/hosts/ltrr-block/age.nix
new file mode 100644
index 0000000..e22263b
--- /dev/null
+++ b/modules/hosts/ltrr-block/age.nix
@@ -0,0 +1,10 @@
+{inputs, ...}: {
+ flake.nixosModules.ltrr-block = {
+ age.rekey = {
+ hostPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINe2bfzslwh9DwNXopmaiRKVNQMIQNuMlP/jJCDrwSbc";
+ masterIdentities = ["/home/jerpo/.config/age/keys.txt"];
+ storageMode = "local";
+ localStorageDir = ./. + "/secrets/rekeyed";
+ };
+ };
+}
diff --git a/modules/hosts/ltrr-block/configuration.nix b/modules/hosts/ltrr-block/configuration.nix
new file mode 100644
index 0000000..5ee4711
--- /dev/null
+++ b/modules/hosts/ltrr-block/configuration.nix
@@ -0,0 +1,467 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.nixosConfigurations.ltrr-block = inputs.nixpkgs-stable.lib.nixosSystem {
+ modules = [
+ self.nixosModules.ltrr-block
+ ];
+ };
+
+ flake.nixosModules.ltrr-block = {
+ modulesPath,
+ pkgs,
+ config,
+ ...
+ }: let
+ domain = "kcu.su";
+ in {
+ imports = [
+ (modulesPath + "/installer/scan/not-detected.nix")
+ (modulesPath + "/profiles/qemu-guest.nix")
+
+ self.nixosModules.nginxProxy
+ self.nixosModules.directories
+ self.nixosModules.booklore
+ self.nixosModules.watcharr
+
+ inputs.disko.nixosModules.disko
+ self.diskoConfigurations.ltrr-block
+
+ inputs.agenix.nixosModules.default
+ inputs.agenix-rekey.nixosModules.default
+ ];
+
+ nixpkgs = {
+ config.allowUnfree = true;
+ overlays = [
+ self.overlays.unstable-packages
+ ];
+ };
+
+ boot.loader.grub = {
+ efiSupport = true;
+ efiInstallAsRemovable = true;
+ };
+
+ services.openssh = {
+ enable = true;
+ settings.PasswordAuthentication = false;
+ };
+
+ environment.systemPackages = with pkgs; [
+ curl
+ gitMinimal
+ ];
+
+ users.users = {
+ root = {
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJ8UW1BXDGDmlaiARO3a9boTG8wknUyITMz0Z0OJpHx spleefer6@yandex.ru"
+ ];
+ };
+ };
+
+ users.users.mc = {
+ isNormalUser = true;
+ packages = with pkgs; [
+ steamcmd
+ tmux
+ steam-run
+ ];
+ };
+
+ users.users.files = {
+ isNormalUser = true;
+ group = "files";
+ uid = 1000;
+ linger = true;
+ extraGroups = [
+ "music"
+ "images"
+ ];
+ };
+
+ users.groups.music = {
+ gid = 991;
+ };
+
+ createPaths."/srv/files" = {
+ owner = "files";
+ permissions = "0770";
+ group = "files";
+ subPaths = {
+ "music".group = "music";
+ "images".group = "images";
+ };
+ };
+
+ networking.hostName = "ltrr-block";
+ networking.firewall = {
+ allowedTCPPorts = [80 5030 2049 25565];
+ allowedUDPPorts = [51820 16261 16262];
+ };
+
+ security.acme = {
+ acceptTerms = true;
+ defaults.email = "notspl3g+acme@duck.com";
+ };
+
+ nginxProxy = {
+ enable = true;
+ domain = domain;
+
+ recommendedProxySettings = false;
+ subdomains = {
+ "slskd" = {
+ proxyPass = "http://127.0.0.1:${toString config.services.slskd.settings.web.port}";
+ proxyWebsockets = true;
+ };
+ "immich" = {
+ proxyPass = "http://localhost:${toString config.services.immich.port}";
+ proxyWebsockets = true;
+ recommendedProxySettings = true;
+ extraConfig = ''
+ client_max_body_size 50000M;
+ proxy_read_timeout 600s;
+ proxy_send_timeout 600s;
+ send_timeout 600s;
+ '';
+ };
+
+ "music".proxyPass = "http://127.0.0.1:5692";
+ "navidrome".proxyPass = "http://127.0.0.1:4533";
+ "files".proxyPass = "http://127.0.0.1:${toString config.services.filebrowser.settings.port}";
+ "track".proxyPass = "http://127.0.0.1:7093";
+ "tube".proxyPass = "http://127.0.0.1:5410";
+ "torrents".proxyPass = "http://127.0.0.1:7317";
+ "jellyfin".proxyPass = "http://127.0.0.1:8096";
+ "lidarr" = {
+ proxyPass = "http://127.0.0.1:8686";
+ proxyWebsockets = true;
+ };
+
+ "prowlarr".proxyPass = "http://127.0.0.1:9696";
+ "shelfmark".proxyPass = "http://127.0.0.1:8084";
+ };
+ };
+
+ age.secrets.wg-priv-key = {
+ rekeyFile = ./secrets/wg-priv.key.age;
+ };
+
+ networking.wg-quick = {
+ interfaces.wg0 = {
+ address = ["10.1.1.2/32"];
+ listenPort = 51820;
+
+ privateKeyFile = config.age.secrets.wg-priv-key.path;
+
+ peers = [
+ {
+ endpoint = "${domain}:51820";
+ publicKey = "1RwEOL8br97Mujhz3fkfYKcxUFNHYAmt5JbWTbR3ihE=";
+ allowedIPs = ["10.1.1.1/32"];
+ persistentKeepalive = 25;
+ }
+ ];
+ };
+ };
+
+ services.tailscale.enable = true;
+
+ users.users.filebrowser.extraGroups = ["music" "images"];
+ systemd.services.filebrowser.serviceConfig.SupplementaryGroups = ["music" "images"];
+ services.filebrowser = {
+ enable = true;
+ group = "files";
+ settings = {
+ root = "/srv/files";
+ port = 9337;
+ };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d /srv/files/slskd 0770 slskd music"
+ "d /opt/traggo/data"
+ "d /var/lib/traggo"
+ ];
+ users.users.slskd.extraGroups = ["files"];
+ age.secrets.slskd-env = {
+ rekeyFile = ./secrets/slskd.env.age;
+ };
+ systemd.services.slskd.serviceConfig.UMask = 0002;
+ services.slskd = {
+ enable = true;
+ environmentFile = config.age.secrets.slskd-env.path;
+ group = "music";
+ settings = {
+ shares = {
+ directories = ["/srv/files/music"];
+ filters = ["explo"];
+ };
+ directories.downloads = "/srv/files/slskd";
+ permissions.file.mode = 770;
+ web.authentication.api_keys = {
+ arr = {
+ key = "mahChufaeweipemeeheexaoDahchohZi";
+ cidr = "127.0.0.1/32";
+ };
+ };
+ };
+ openFirewall = true;
+ domain = null;
+ };
+
+ users.users.jellyfin.extraGroups = ["files"];
+ services.jellyfin = {
+ enable = true;
+ };
+
+ createPaths."/srv/files/torrents" = {
+ owner = "files";
+ group = "files";
+ permissions = "0770";
+ subPaths = {
+ "incomplete" = {};
+ "completed" = {};
+ };
+ };
+
+ users.users.lidarr.extraGroups = ["files"];
+ services.lidarr = {
+ enable = true;
+ group = "music";
+ };
+
+ services.prowlarr = {
+ enable = true;
+ };
+
+ services.qbittorrent = {
+ enable = true;
+ user = "files";
+ group = "files";
+ webuiPort = 7317;
+ serverConfig = {
+ Preferences = {
+ WebUI = {
+ Enabled = "true";
+ Username = "spl3g";
+ Password_PBKDF2 = "@ByteArray(drq4VxxcJLLK0Bma9mxUeg==:47J+BVdVlmpJt0Hb9LqiAj6rTK3ZlFPvy00PdGPtmeXO7SslNg/4Uej7Vmwn3+oFPuE1q/9tm1z1UogfopREUQ==)";
+ };
+ Downloads = {
+ SavePath = "/srv/files/torrents/complete/";
+ TempPathEnabled = "true";
+ TempPath = "/srv/files/torrents/incomplete/";
+ };
+ };
+ };
+ };
+
+ createPaths."/var/lib/soularr" = {
+ owner = "slskd";
+ group = "slskd";
+ permissions = "0770";
+ };
+ age.secrets.soularr-config = {
+ rekeyFile = ./secrets/soularr.conf.age;
+ owner = "slskd";
+ group = "music";
+ };
+ virtualisation.oci-containers.containers.soularr = {
+ image = "mrusse08/soularr:latest";
+ environment = {
+ TZ = "Asia/Yekaterinburg";
+ SCRIPT_INTERVAL = "300";
+ };
+ # slskd:music
+ user = "991:991";
+ volumes = [
+ "/srv/files/slskd:/downloads"
+ "/var/lib/soularr:/data"
+ "${config.age.secrets.soularr-config.path}:/data/config.ini"
+ ];
+ networks = [
+ "host"
+ ];
+ };
+
+ createPaths."/srv/files/explo" = {
+ owner = "files";
+ group = "music";
+ permissions = "0770";
+ };
+ age.secrets.explo-env = {
+ rekeyFile = ./secrets/explo.env.age;
+ owner = "files";
+ group = "music";
+ };
+ # TODO: write a module for explo
+ virtualisation.oci-containers.containers.explo = {
+ image = "ghcr.io/lumepart/explo:latest";
+ volumes = [
+ "${config.age.secrets.explo-env.path}:/opt/explo/.env"
+ "/srv/files/explo:/data/"
+ "/srv/files/slskd:/slskd/"
+ ];
+ environment = {
+ EXECUTE_ON_START = "true";
+ };
+ networks = [
+ "host"
+ ];
+ # files:music
+ extraOptions = [
+ "--uidmap=0:1000:1"
+ "--gidmap=0:991:1"
+ "--uidmap=1:100000:65535"
+ "--gidmap=1:100000:65535"
+ ];
+ };
+
+ age.secrets.navidrome-env = {
+ rekeyFile = ./secrets/navidrome.env.age;
+ };
+ users.users.navidrome.extraGroups = ["files" "music"];
+ systemd.services.navidrome.serviceConfig.BindReadOnlyPaths = ["/srv/files/explo"];
+ services.navidrome = {
+ enable = true;
+ package = pkgs.unstable.navidrome;
+ settings = {
+ BaseUrl = "https://navidrome.${domain}";
+ MusicFolder = "/srv/files/music";
+ PlaylistsPath = "playlists";
+ Scanner.PurgeMissing = "always";
+ EnableSharing = true;
+ };
+ environmentFile = config.age.secrets.navidrome-env.path;
+ };
+
+ age.secrets.xray-config = {
+ rekeyFile = ./secrets/xray.json.age;
+ };
+ services.xray = {
+ enable = true;
+ settingsFile = config.age.secrets.xray-config.path;
+ };
+
+ services.invidious = {
+ enable = true;
+ address = "127.0.0.1";
+ port = 5410;
+ domain = "tube.${domain}";
+ settings = {
+ http_proxy = {
+ host = "127.0.0.1";
+ port = 10801;
+ user = "";
+ password = "";
+ };
+ };
+ };
+
+ virtualisation.oci-containers.backend = "podman";
+ virtualisation.oci-containers.containers.aonsoku = {
+ image = "ghcr.io/victoralvesf/aonsoku:latest";
+ ports = [
+ "127.0.0.1:5692:8080"
+ ];
+ environment = {
+ SERVER_URL = "https://navidrome.${domain}";
+ HIDE_SERVER = "true";
+ };
+ };
+
+ users.groups.books = {
+ gid = 1001;
+ };
+ createPaths."/srv/files/books" = {
+ owner = "files";
+ group = "books";
+ permissions = "0770";
+ subPaths = {
+ "/library" = {};
+ "/bookdrop" = {};
+ };
+ };
+
+ createPaths."/var/lib/cwa" = {
+ owner = "files";
+ group = "books";
+ permissions = "0750";
+ };
+
+ # age.secrets.cwa-env = {
+ # rekeyFile = ./secrets/cwa.env.age;
+ # };
+ # virtualisation.oci-containers.containers.cwa = {
+ # image = "ghcr.io/crocodilestick/calibre-web-automated:latest";
+ # environmentFiles = [
+ # config.age.secrets.cwa-env.path
+ # ];
+ # environment = {
+ # TZ = "Europe/Moscow";
+ # PUID = "1000";
+ # PGID = "1001";
+ # };
+ # ports = [
+ # "127.0.0.1:8083:8083"
+ # ];
+ # volumes = [
+ # "/srv/files/books:/calibre-library"
+ # "/srv/files/books/injest:/cwa-book-ingest"
+ # "/var/lib/cwa:/config"
+ # ];
+ # };
+
+ createPaths."/var/lib/shelfmark" = {
+ owner = "files";
+ group = "books";
+ permissions = "0750";
+ };
+
+ virtualisation.oci-containers.containers.shelfmark = {
+ image = "ghcr.io/calibrain/shelfmark:latest";
+ environment = {
+ PUID = "1000";
+ PGID = "1001";
+ };
+
+ ports = [
+ "127.0.0.1:8084:8084"
+ ];
+
+ volumes = [
+ "/srv/files/books/injest:/books"
+ "/var/lib/shelfmark:/config"
+ ];
+ networks = [
+ "host"
+ ];
+ };
+ services.booklore = {
+ enable = true;
+ subdomain = "books";
+ uid = "1000";
+ gid = "1001";
+ settings = {
+ timezone = "Europe/Yekaterinburg";
+ booksDir = "/srv/files/books/library";
+ bookdropDir = "/srv/files/books/injest";
+ };
+ };
+
+ services.watcharr = {
+ enable = true;
+ subdomain = "watched";
+ };
+
+ services.immich = {
+ enable = true;
+ };
+
+ system.stateVersion = "24.05";
+ };
+}
diff --git a/modules/hosts/ltrr-block/disk-config.nix b/modules/hosts/ltrr-block/disk-config.nix
new file mode 100644
index 0000000..52c5070
--- /dev/null
+++ b/modules/hosts/ltrr-block/disk-config.nix
@@ -0,0 +1,39 @@
+{
+ flake.diskoConfigurations.ltrr-block = {
+ disko.devices = {
+ disk = {
+ main = {
+ device = "/dev/sdb";
+ type = "disk";
+ content = {
+ type = "gpt";
+ partitions = {
+ boot = {
+ size = "1M";
+ type = "EF02";
+ };
+ ESP = {
+ size = "1G";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ mountOptions = ["umask=0077"];
+ };
+ };
+ root = {
+ size = "100%";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/hosts/ltrr-block/hardware-configuration.nix b/modules/hosts/ltrr-block/hardware-configuration.nix
new file mode 100644
index 0000000..ed72c61
--- /dev/null
+++ b/modules/hosts/ltrr-block/hardware-configuration.nix
@@ -0,0 +1,32 @@
+# Do not modify this file! It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations. Please make changes
+# to /etc/nixos/configuration.nix instead.
+{
+ flake.nixosModules.ltrr-block = {
+ config,
+ lib,
+ pkgs,
+ modulesPath,
+ ...
+ }: {
+ imports = [
+ (modulesPath + "/installer/scan/not-detected.nix")
+ ];
+
+ boot.initrd.availableKernelModules = ["ehci_pci" "ata_piix" "xhci_pci" "usbhid" "usb_storage" "sd_mod"];
+ boot.initrd.kernelModules = [];
+ boot.kernelModules = [];
+ boot.extraModulePackages = [];
+
+ # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+ # (the default) this is the recommended approach. When using systemd-networkd it's
+ # still possible to use this option, but it's recommended to use it in conjunction
+ # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+ networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.eno1.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp0s29u1u3.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+ };
+}
diff --git a/modules/hosts/ltrr-block/secrets/cwa.env.age b/modules/hosts/ltrr-block/secrets/cwa.env.age
new file mode 100644
index 0000000..25e9486
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/cwa.env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/explo.env.age b/modules/hosts/ltrr-block/secrets/explo.env.age
new file mode 100644
index 0000000..1c0f605
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/explo.env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/navidrome.env.age b/modules/hosts/ltrr-block/secrets/navidrome.env.age
new file mode 100644
index 0000000..5a6b99d
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/navidrome.env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/1c8822a2f185737c765ae9a5ce0d3879-soularr-config.age b/modules/hosts/ltrr-block/secrets/rekeyed/1c8822a2f185737c765ae9a5ce0d3879-soularr-config.age
new file mode 100644
index 0000000..608846d
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/1c8822a2f185737c765ae9a5ce0d3879-soularr-config.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/550a141b44c76106807b541c1987996b-wg-priv-key.age b/modules/hosts/ltrr-block/secrets/rekeyed/550a141b44c76106807b541c1987996b-wg-priv-key.age
new file mode 100644
index 0000000..52a14bd
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/550a141b44c76106807b541c1987996b-wg-priv-key.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 ptoveQ ulI09UBOLtapH3caDEteDd52zftMg/7xb9XT2N643A4
+JxzMCCqoLhPIuF7uas7xs1FhX3oqwm11a4r7mV56aiw
+-> pXy7_[-grease H=# dQnNDe%G ] o
+bT0nTM57IpqunS1CNyc/DXpM3f5L3n1887I++YtmrFZQEiU5liKv8Kr+aUD6lK7N
+S44U1Z0Y3VmTeWbN1iV0f5KMk2ZIFXtdjf1kw5JGfFWWVoGXJAxQuQ
+--- 522Kc0bJO1nPQ7bKfYj2+dNqArZpWbwGHRQCLnjbspI
+vν1PʾPMqkI6!vj/)/OZhGxnUš9cL \ No newline at end of file
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/9288d02fd4269798567444d076247538-explo-env.age b/modules/hosts/ltrr-block/secrets/rekeyed/9288d02fd4269798567444d076247538-explo-env.age
new file mode 100644
index 0000000..c11aa4a
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/9288d02fd4269798567444d076247538-explo-env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/939083f61f3167ef5aff27bdba700e8e-xray-config.age b/modules/hosts/ltrr-block/secrets/rekeyed/939083f61f3167ef5aff27bdba700e8e-xray-config.age
new file mode 100644
index 0000000..7cba65e
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/939083f61f3167ef5aff27bdba700e8e-xray-config.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/d5f4d0c5c7b3217d008be68e8ad757e8-navidrome-env.age b/modules/hosts/ltrr-block/secrets/rekeyed/d5f4d0c5c7b3217d008be68e8ad757e8-navidrome-env.age
new file mode 100644
index 0000000..954c2bb
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/d5f4d0c5c7b3217d008be68e8ad757e8-navidrome-env.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 ptoveQ OduiFfXpTXdPiFhmwROVjOSFSPSE8ViVFKpDKbkU4GA
+SOtXYB7vqxYAdiZ81YkQJPU+EBljy8q/5xfUwkQE9FA
+-> GPDe-grease
+tK3q1L/lU2oMO9AzA2cfa5OE36z3x2YCGAu5SVyajQ
+--- Y//hhULEy1uzl/p289AiDRGKFuR4GufSVa48rrgD8Ck
+&o$)ʓ\StkಚJ+J=}a8~ҪCY*O@%:<x !V8eOȁnOwmMԊߔ`m-V,8y TDFjM 2߃U \ No newline at end of file
diff --git a/modules/hosts/ltrr-block/secrets/rekeyed/e9669da1b38fb37ba09edf8fdeafc4de-slskd-env.age b/modules/hosts/ltrr-block/secrets/rekeyed/e9669da1b38fb37ba09edf8fdeafc4de-slskd-env.age
new file mode 100644
index 0000000..37adc46
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/rekeyed/e9669da1b38fb37ba09edf8fdeafc4de-slskd-env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/slskd.env.age b/modules/hosts/ltrr-block/secrets/slskd.env.age
new file mode 100644
index 0000000..1744660
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/slskd.env.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/soularr.conf.age b/modules/hosts/ltrr-block/secrets/soularr.conf.age
new file mode 100644
index 0000000..d02e1c7
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/soularr.conf.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/transmission-settings.json.age b/modules/hosts/ltrr-block/secrets/transmission-settings.json.age
new file mode 100644
index 0000000..f9811c4
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/transmission-settings.json.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> X25519 OUnJLswhrEjV/CylaDqKGX/VqhxhepjCt458Gk0wBgE
+AN2//RRPB5aF+UMSF7ezSXza3t1dr6AGuTGhVzkDitM
+-> 2f#v;H-grease dJa
+ErP6N46d+W72CKSHEbgxzWSiCjN7VM0unjwAE/LWLXt+GNUGEN3I+DaPTWf46v05
+EA
+--- URehB3fPuN8jXQoFfe2YZTyT6gZVs44slp/F7pXBYXM
+=T̈` AMwƗ{ #荂w֬5Ԫ@A܇91{,k{B͆HfSn^;Lew`W41K>hfY \ No newline at end of file
diff --git a/modules/hosts/ltrr-block/secrets/wg-priv.key.age b/modules/hosts/ltrr-block/secrets/wg-priv.key.age
new file mode 100644
index 0000000..7ea39e9
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/wg-priv.key.age
Binary files differ
diff --git a/modules/hosts/ltrr-block/secrets/xray.json.age b/modules/hosts/ltrr-block/secrets/xray.json.age
new file mode 100644
index 0000000..f50d67b
--- /dev/null
+++ b/modules/hosts/ltrr-block/secrets/xray.json.age
Binary files differ
diff --git a/modules/hosts/ltrr-cloud/age.nix b/modules/hosts/ltrr-cloud/age.nix
new file mode 100644
index 0000000..80232d7
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/age.nix
@@ -0,0 +1,10 @@
+{inputs, ...}: {
+ flake.nixosModules.ltrr-cloud = {
+ age.rekey = {
+ hostPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGkmaIw2J4H6lWDVnopnUKQuQMJuQf5VMoC1/YwCuhAb";
+ masterIdentities = ["/home/jerpo/.config/age/keys.txt"];
+ storageMode = "local";
+ localStorageDir = ./. + "/secrets/rekeyed";
+ };
+ };
+}
diff --git a/modules/hosts/ltrr-cloud/configuration.nix b/modules/hosts/ltrr-cloud/configuration.nix
new file mode 100644
index 0000000..34a74e0
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/configuration.nix
@@ -0,0 +1,345 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.nixosConfigurations.ltrr-cloud = inputs.nixpkgs-stable.lib.nixosSystem {
+ modules = [
+ self.nixosModules.ltrr-cloud
+ ];
+ };
+
+ flake.nixosModules.ltrr-cloud = {
+ modulesPath,
+ config,
+ lib,
+ pkgs,
+ ...
+ }: let
+ domain = "kcu.su";
+ in {
+ imports = [
+ (modulesPath + "/installer/scan/not-detected.nix")
+ (modulesPath + "/profiles/qemu-guest.nix")
+ "${inputs.nixpkgs}/nixos/modules/services/networking/headscale.nix"
+
+ self.nixosModules.nginxProxy
+ self.nixosModules.directories
+
+ inputs.disko.nixosModules.disko
+ self.diskoConfigurations.ltrr-cloud
+
+ inputs.agenix.nixosModules.default
+ inputs.agenix-rekey.nixosModules.default
+ ];
+ nixpkgs.hostPlatform = "x86_64-linux";
+
+ disabledModules = ["services/networking/headscale.nix"];
+
+ nixpkgs.overlays = [
+ self.overlays.unstable-packages
+ ];
+
+ boot.loader.grub = {
+ efiSupport = true;
+ efiInstallAsRemovable = true;
+ };
+
+ networking = {
+ interfaces.ens3 = {
+ ipv4.addresses = [
+ {
+ address = "194.156.117.206";
+ prefixLength = 32;
+ }
+ ];
+ };
+ defaultGateway = {
+ address = "100.100.1.1";
+ interface = "ens3";
+ };
+ };
+
+ networking.nameservers = ["8.8.8.8" "1.1.1.1"];
+
+ networking.useDHCP = lib.mkDefault false;
+
+ networking.hostName = "ltrr-cloud";
+
+ services.openssh = {
+ enable = true;
+ };
+
+ environment.systemPackages = map lib.lowPrio [
+ pkgs.curl
+ pkgs.gitMinimal
+ ];
+
+ users.users = {
+ root = {
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJ8UW1BXDGDmlaiARO3a9boTG8wknUyITMz0Z0OJpHx spleefer6@yandex.ru"
+ ];
+ };
+ };
+
+ swapDevices = [
+ {
+ device = "/var/lib/swapfile";
+ size = 2 * 1024;
+ }
+ ];
+
+ networking.nat = {
+ enable = true;
+ externalInterface = "ens3";
+ internalInterfaces = ["wg0"];
+ };
+
+ age.secrets.wg-priv-key = {
+ rekeyFile = ./secrets/wg-priv.key.age;
+ };
+ networking.wg-quick = {
+ interfaces.wg0 = {
+ address = ["10.1.1.1/32"];
+ listenPort = 51820;
+
+ privateKeyFile = config.age.secrets.wg-priv-key.path;
+
+ preUp = ''
+ sysctl -w net.ipv4.ip_forward=1
+ # 16261
+ iptables -t nat -I PREROUTING 1 -i ens3 -p udp --dport 16261 -j DNAT --to-destination 10.1.1.2:16261
+ iptables -A FORWARD -p udp -d 10.1.1.2 --dport 16261 -j ACCEPT
+ iptables -t nat -A POSTROUTING -o wg0 -p udp --dport 16261 -d 10.1.1.2 -j MASQUERADE
+
+ # 16262
+ iptables -t nat -I PREROUTING 1 -i ens3 -p udp --dport 16262 -j DNAT --to-destination 10.1.1.2:16262
+ iptables -A FORWARD -p udp -d 10.1.1.2 --dport 16262 -j ACCEPT
+ iptables -t nat -A POSTROUTING -o wg0 -p udp --dport 16262 -d 10.1.1.2 -j MASQUERADE
+ '';
+ postDown = ''
+ # 16261
+ iptables -t nat -D PREROUTING -i ens3 -p udp --dport 16261 -j DNAT --to-destination 10.1.1.2:16261
+ iptables -D FORWARD -p udp -d 10.1.1.2 --dport 16261 -j ACCEPT
+ iptables -t nat -D POSTROUTING -o wg0 -p udp --dport 16261 -d 10.1.1.2 -j MASQUERADE
+
+ # 16262
+ iptables -t nat -D PREROUTING -i ens3 -p udp --dport 16262 -j DNAT --to-destination 10.1.1.2:16262
+ iptables -D FORWARD -p udp -d 10.1.1.2 --dport 16262 -j ACCEPT
+ iptables -t nat -D POSTROUTING -o wg0 -p udp --dport 16262 -d 10.1.1.2 -j MASQUERADE
+ '';
+
+ peers = [
+ {
+ allowedIPs = ["10.1.1.2/32"];
+ publicKey = "kzXzxJu1AdcRI5UwtGOrN6WuTZYqJo++PYRrXdOu/lY=";
+ persistentKeepalive = 25;
+ }
+ ];
+ };
+ };
+
+ networking.firewall.allowedTCPPorts = [80 443 25565];
+ networking.firewall.allowedUDPPorts = [51820 16261 16262];
+
+ services.fail2ban = {
+ enable = true;
+ ignoreIP = [
+ "10.0.0.0/8"
+ ];
+ bantime = "24h";
+ bantime-increment = {
+ enable = true;
+ multipliers = "1 2 4 8 16 32 64";
+ maxtime = "168h";
+ overalljails = true;
+ };
+
+ jails = {
+ nginx-http-auth.settings = {
+ enabled = true;
+ port = "http,https";
+ logpath = "/var/log/nginx/*.log";
+ backend = "auto";
+ };
+ nginx-botsearch.settings = {
+ enabled = true;
+ port = "http,https";
+ logpath = "/var/log/nginx/*.log";
+ backend = "auto";
+ };
+ nginx-bad-request.settings = {
+ enabled = true;
+ port = "http,https";
+ logpath = "/var/log/nginx/*.log";
+ backend = "auto";
+ };
+ };
+ };
+
+ security.acme = {
+ acceptTerms = true;
+ defaults.email = "notspl3g+acme@duck.com";
+ };
+
+ nginxProxy = {
+ enable = true;
+ acme.enable = true;
+
+ inherit domain;
+ subdomains = {
+ "headscale" = {
+ proxyPass = "http://127.0.0.1:8768";
+ proxyWebsockets = true;
+ recommendedProxySettings = true;
+ };
+ "uptime" = {
+ proxyPass = "http://127.0.0.1:8762";
+ proxyWebsockets = true;
+ recommendedProxySettings = true;
+ };
+ "monitor" = {
+ proxyPass = "http://127.0.0.1:8090";
+ };
+ "auth".proxyPass = "http://127.0.0.1:9091";
+ };
+
+ extraVirtualHosts = {
+ "kcu.su" = {
+ forceSSL = true;
+ enableACME = true;
+ locations."/apple" = {
+ root = "/var/www";
+ };
+ locations."/" = {
+ return = 444;
+ };
+ };
+
+ "_".locations = {
+ "/" = {
+ return = 444;
+ };
+ };
+ };
+
+ home = let
+ homeConfig = self.nixosConfigurations.ltrr-block.config;
+ in {
+ subdomains = homeConfig.nginxProxy.subdomains;
+ url = "http://10.1.1.2";
+ };
+ };
+
+ age.secrets.authelia-jwt = {
+ rekeyFile = ./secrets/authelia-jwt.key.age;
+ owner = "authelia-kcu";
+ group = "authelia-kcu";
+ };
+ age.secrets.authelia-storage = {
+ rekeyFile = ./secrets/authelia-storage.key.age;
+ owner = "authelia-kcu";
+ group = "authelia-kcu";
+ };
+ services.authelia.instances.kcu = {
+ enable = true;
+ secrets = {
+ jwtSecretFile = config.age.secrets.authelia-jwt.path;
+ storageEncryptionKeyFile = config.age.secrets.authelia-storage.path;
+ };
+ settings = {
+ authentication_backend = {
+ file = {
+ path = "/var/lib/authelia-kcu/users_database.yml";
+ };
+ };
+
+ storage = {
+ local = {};
+ };
+
+ access_control = {
+ default_policy = "deny";
+ rules = [
+ {
+ domain = ["auth.${domain}"];
+ policy = "bypass";
+ }
+ {
+ domain = ["*.${domain}"];
+ policy = "one_factor";
+ }
+ ];
+ };
+
+ session = {
+ name = "authelia_session";
+ expiration = "12h";
+ inactivity = "45m";
+ cookies = [
+ {
+ inherit domain;
+ authelia_url = "https://auth.kcu.su";
+ }
+ ];
+ };
+
+ storage = {
+ local = {
+ path = "/var/lib/authelia-kcu/db.sqlite3";
+ };
+ };
+
+ notifier = {
+ disable_startup_check = false;
+ filesystem = {
+ filename = "/var/lib/authelia-kcu/notification.txt";
+ };
+ };
+ };
+ };
+
+ services.headscale = {
+ enable = true;
+ package = pkgs.headscale;
+ port = 8768;
+ settings = {
+ server_url = "https://headscale.${domain}:443";
+ dns = {
+ base_domain = "ts.net";
+ nameservers.global = ["8.8.8.8"];
+ magicdns = true;
+ };
+ };
+ };
+
+ createPaths = {
+ "/var/lib/uptime-kuma" = {
+ owner = "root";
+ group = "root";
+ };
+ };
+ virtualisation.oci-containers.backend = "podman";
+ virtualisation.oci-containers.containers = {
+ "uptime-kuma" = {
+ image = "louislam/uptime-kuma:2";
+ volumes = [
+ "/var/lib/uptime-kuma:/app/data"
+ ];
+ ports = [
+ "127.0.0.1:8762:3001"
+ ];
+ capabilities = {
+ NET_RAW = true;
+ };
+ };
+ };
+
+ services.beszel.hub = {
+ enable = true;
+ };
+
+ system.stateVersion = "24.05";
+ };
+}
diff --git a/modules/hosts/ltrr-cloud/disk-config.nix b/modules/hosts/ltrr-cloud/disk-config.nix
new file mode 100644
index 0000000..f7abadd
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/disk-config.nix
@@ -0,0 +1,56 @@
+{
+ flake.diskoConfigurations.ltrr-cloud = {
+ disko.devices = {
+ disk.disk1 = {
+ device = "/dev/vda";
+ type = "disk";
+ content = {
+ type = "gpt";
+ partitions = {
+ boot = {
+ name = "boot";
+ size = "1M";
+ type = "EF02";
+ };
+ esp = {
+ name = "ESP";
+ size = "500M";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ };
+ };
+ root = {
+ name = "root";
+ size = "100%";
+ content = {
+ type = "lvm_pv";
+ vg = "pool";
+ };
+ };
+ };
+ };
+ };
+ lvm_vg = {
+ pool = {
+ type = "lvm_vg";
+ lvs = {
+ root = {
+ size = "100%FREE";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ mountOptions = [
+ "defaults"
+ ];
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/hosts/ltrr-cloud/secrets/authelia-jwt.key.age b/modules/hosts/ltrr-cloud/secrets/authelia-jwt.key.age
new file mode 100644
index 0000000..ecad260
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/authelia-jwt.key.age
Binary files differ
diff --git a/modules/hosts/ltrr-cloud/secrets/authelia-storage.key.age b/modules/hosts/ltrr-cloud/secrets/authelia-storage.key.age
new file mode 100644
index 0000000..22e9eb5
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/authelia-storage.key.age
Binary files differ
diff --git a/modules/hosts/ltrr-cloud/secrets/rekeyed/97c2df6cc789b9e8ced5811bfa43d3f8-authelia-jwt.age b/modules/hosts/ltrr-cloud/secrets/rekeyed/97c2df6cc789b9e8ced5811bfa43d3f8-authelia-jwt.age
new file mode 100644
index 0000000..3fdbf7c
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/rekeyed/97c2df6cc789b9e8ced5811bfa43d3f8-authelia-jwt.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 +W/xBg sJTHHtkYrmAQbKR8ZOdR9K/reO0qeKTL4gsx34pysiY
+iTmPnK/LRYZIgEuq/xtLiQyDMML6hFPO262OdIq8UD8
+-> orB|s|Kd-grease K7 l7hC&yL A^ 6,
+MoHcwp57sM2RWYOYAk8LihPGhPSrmZkUpd3X4eJODfsqfEuJys9ZhKVyr4l4FwzN
+NsQJcBuH1//fXe/P
+--- gku/aoba9gRLFf1wD6rrwogur0EYGovRAybhyr3CLco
+ĩ,y yLۘXP1d(5Xvst6D}S 8ҹ03.XDLQsUEgK4<w [u3BxN'9}z $wF Ŧ^z!lg ńØ $qjP \ No newline at end of file
diff --git a/modules/hosts/ltrr-cloud/secrets/rekeyed/98c1b723eb9ef4334c5a90c456a33743-wg-priv-key.age b/modules/hosts/ltrr-cloud/secrets/rekeyed/98c1b723eb9ef4334c5a90c456a33743-wg-priv-key.age
new file mode 100644
index 0000000..5605e81
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/rekeyed/98c1b723eb9ef4334c5a90c456a33743-wg-priv-key.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 +W/xBg HvzCOTiwRSUSNLxgsyBbhWOU7JdVeA0OLMTnHTAXlgE
+v4G1Z3E2H0zzbamMppALx25HeS5rSfM+bJmrbUM+jgs
+-> 6hkBDp}-grease ^w s#u_m8> 1?':qM
+IAyx+VQq7VnSNf2SfqusH0eXFffH
+--- L9C8w8DV9hstoTBd39/zve9OJt4v/vFpIDbRcxUWIeI
+W:l{2u%:Vy/> .H u+AtLȲN ޼뺃 \ No newline at end of file
diff --git a/modules/hosts/ltrr-cloud/secrets/rekeyed/ab2826e18d1b8ee845f01ac87f5dd6ea-authelia-storage.age b/modules/hosts/ltrr-cloud/secrets/rekeyed/ab2826e18d1b8ee845f01ac87f5dd6ea-authelia-storage.age
new file mode 100644
index 0000000..ff31a34
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/rekeyed/ab2826e18d1b8ee845f01ac87f5dd6ea-authelia-storage.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 +W/xBg vxOWjAe9E2wVCDSYjJ4FzTb6OJof4BME/a0B2/m7zl8
+2jTCWcg7koRgIOUi2QDKFimbE/Gq+F0CAYmkaBmT51g
+-> eI}jc#W-grease $l&5 718 6%B>X
+miZ+OEV0SD7oYl1tqYpVFzxGhQIvMjDTQ4xRwA8Cu8BNghAwbv2vROant8sjWSsv
+WKkljj4/RH2EU1f0y0b2fIbhF8I
+--- X06G5WHTNau24I+ZT20SebQvbxP7S9zrjbk0FFIPyOE
+L\_@DD=*o>YEak",ś2،Iwb(p4Y' Paj&4-J!lkLV[X"2mqVᷩPNëz"9'I۲*5Z2~zYܸ2 \ No newline at end of file
diff --git a/modules/hosts/ltrr-cloud/secrets/wg-priv.key.age b/modules/hosts/ltrr-cloud/secrets/wg-priv.key.age
new file mode 100644
index 0000000..cd52bb3
--- /dev/null
+++ b/modules/hosts/ltrr-cloud/secrets/wg-priv.key.age
Binary files differ
diff --git a/modules/hosts/ltrr-mask/configuration.nix b/modules/hosts/ltrr-mask/configuration.nix
new file mode 100644
index 0000000..3656016
--- /dev/null
+++ b/modules/hosts/ltrr-mask/configuration.nix
@@ -0,0 +1,125 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.nixosConfigurations.ltrr-mask = inputs.nixpkgs-stable.lib.nixosSystem {
+ modules = [
+ self.nixosModules.ltrr-mask
+ ];
+ };
+
+ flake.nixosModules.ltrr-mask = {
+ modulesPath,
+ config,
+ lib,
+ pkgs,
+ ...
+ }: let
+ domain = "kcu.su";
+ in {
+ imports = [
+ (modulesPath + "/installer/scan/not-detected.nix")
+ (modulesPath + "/profiles/qemu-guest.nix")
+
+ inputs.disko.nixosModules.disko
+ self.diskoConfigurations.ltrr-mask
+
+ self.nixosModules.nginxProxy
+ ];
+ nixpkgs.hostPlatform = "x86_64-linux";
+
+ boot.loader.grub = {
+ efiSupport = true;
+ efiInstallAsRemovable = true;
+ };
+
+ networking.firewall.allowedTCPPorts = [
+ # http
+ # 80
+ # 443
+
+ # xray
+ 4876
+ 57625
+ 39701
+ 39482
+ ];
+
+ networking.domain = domain;
+ networking.hostName = "ltrr-vpn";
+ networking = {
+ interfaces.ens3 = {
+ ipv4.addresses = [
+ {
+ address = "64.188.126.186";
+ prefixLength = 32;
+ }
+ ];
+ };
+ defaultGateway = {
+ address = "100.64.0.1";
+ interface = "ens3";
+ };
+ };
+
+ networking.useDHCP = lib.mkDefault false;
+
+ networking.nameservers = ["8.8.8.8" "1.1.1.1"];
+
+ services.openssh = {
+ enable = true;
+ settings.PasswordAuthentication = false;
+ };
+
+ environment.systemPackages = map lib.lowPrio [
+ pkgs.curl
+ pkgs.gitMinimal
+ ];
+
+ users.users = {
+ root = {
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJ8UW1BXDGDmlaiARO3a9boTG8wknUyITMz0Z0OJpHx spleefer6@yandex.ru"
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPuQVHIGm2bfvhW16ZI/4hDK2X8W+ADbPLXwzKZIYXZL user@LAPTOP-72FMD6D0"
+ ];
+ };
+ };
+
+ nginxProxy = {
+ enable = false;
+ acme.enable = true;
+
+ inherit domain;
+ subdomains = {
+ "xray" = {
+ proxyPass = "http://127.0.0.1:2053";
+
+ extraConfig = "
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Range $http_range;
+ proxy_set_header If-Range $http_if_range;
+ proxy_redirect off;
+ ";
+ recommendedProxySettings = false;
+ };
+ };
+ };
+
+ virtualisation.oci-containers = {
+ backend = "docker";
+ containers.xui = {
+ image = "ghcr.io/mhsanaei/3x-ui:latest";
+ ports = ["4876:2053" "57625:57625" "39701:39701"];
+ volumes = [
+ "/root/x-ui:/etc/x-ui"
+ ];
+ };
+ };
+
+ system.stateVersion = "24.05";
+ };
+}
diff --git a/modules/hosts/ltrr-mask/disk-config.nix b/modules/hosts/ltrr-mask/disk-config.nix
new file mode 100644
index 0000000..e772466
--- /dev/null
+++ b/modules/hosts/ltrr-mask/disk-config.nix
@@ -0,0 +1,56 @@
+{lib, ...}: {
+ flake.diskoConfigurations.ltrr-mask = {
+ disko.devices = {
+ disk.disk1 = {
+ device = lib.mkDefault "/dev/vda";
+ type = "disk";
+ content = {
+ type = "gpt";
+ partitions = {
+ boot = {
+ name = "boot";
+ size = "1M";
+ type = "EF02";
+ };
+ esp = {
+ name = "ESP";
+ size = "500M";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ };
+ };
+ root = {
+ name = "root";
+ size = "100%";
+ content = {
+ type = "lvm_pv";
+ vg = "pool";
+ };
+ };
+ };
+ };
+ };
+ lvm_vg = {
+ pool = {
+ type = "lvm_vg";
+ lvs = {
+ root = {
+ size = "100%FREE";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ mountOptions = [
+ "defaults"
+ ];
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
deleted file mode 100644
index bec6184..0000000
--- a/modules/nixos/default.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-# Add your reusable NixOS modules to this directory, on their own file (https://nixos.wiki/wiki/Module).
-# These should be stuff you would like to share with others, not your personal configurations.
-
-{
- # List your module files here
- # my-module = import ./my-module.nix;
-}
diff --git a/modules/nixosModules/booklore.nix b/modules/nixosModules/booklore.nix
new file mode 100644
index 0000000..3eeb3b9
--- /dev/null
+++ b/modules/nixosModules/booklore.nix
@@ -0,0 +1,176 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.nixosModules.booklore = {
+ config,
+ lib,
+ pkgs,
+ ...
+ }:
+ with lib; let
+ cfg = config.services.booklore;
+ in {
+ options = {
+ services.booklore = {
+ enable = mkEnableOption "Enable booklore service";
+ subdomain = mkOption {
+ type = types.str;
+ description = ''
+ Subdomain to use for nginx.
+ '';
+ };
+ uid = mkOption {
+ type = types.str;
+ description = ''
+ UID for the container user.
+ '';
+ };
+ gid = mkOption {
+ type = types.str;
+ description = ''
+ GID for the container user.
+ '';
+ };
+ settings = {
+ timezone = mkOption {
+ type = types.str;
+ description = ''
+ Timezone string;
+ '';
+ };
+ dataDir = mkOption {
+ type = types.path;
+ description = ''
+ Booklore data directory.
+ '';
+ default = "/var/lib/booklore";
+ };
+ bookdropDir = mkOption {
+ type = types.path;
+ description = ''
+ Directory where booklore will injest books. It is not created automatically, you should create it with the sufficient permissions for the uid and gid you provided.
+ '';
+ };
+ booksDir = mkOption {
+ type = types.path;
+ description = ''
+ Directory where booklore will store books. It is not created automatically, you should create it with the sufficient permissions for the uid and gid you provided.
+ '';
+ };
+ };
+ database = {
+ name = mkOption {
+ type = types.str;
+ default = "booklore";
+ };
+ user = mkOption {
+ type = types.str;
+ default = "files";
+ };
+ password = mkOption {
+ type = types.str;
+ default = "booklore";
+ };
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ createPaths = {
+ "${cfg.settings.dataDir}" = {
+ owner = cfg.uid;
+ group = cfg.gid;
+ permissions = "0750";
+ };
+ };
+ nginxProxy = {
+ enable = true;
+ subdomains = {
+ "${cfg.subdomain}" = {
+ proxyPass = "http://127.0.0.1:6060";
+ proxyWebsockets = true;
+ };
+ };
+ };
+
+ virtualisation.oci-containers.containers.booklore = {
+ image = "booklore/booklore:latest";
+ environment = {
+ USER_ID = cfg.uid;
+ GROUP_ID = cfg.gid;
+ TZ = cfg.settings.timezone;
+
+ DATABASE_URL = "jdbc:mariadb://mariadb-booklore:3306/${cfg.database.name}";
+ DATABASE_USERNAME = cfg.database.user;
+ DATABASE_PASSWORD = cfg.database.password;
+ };
+ ports = [
+ "127.0.0.1:6060:6060"
+ ];
+ volumes = [
+ "${cfg.settings.dataDir}:/app/data"
+ "${cfg.settings.booksDir}:/books"
+ "${cfg.settings.bookdropDir}:/bookdrop"
+ ];
+ dependsOn = [
+ "mariadb-booklore"
+ ];
+ networks = [
+ "booklore_default"
+ ];
+ };
+ systemd.services."podman-booklore" = {
+ serviceConfig = {
+ Restart = lib.mkOverride 90 "always";
+ };
+ after = [
+ "podman-network-booklore_default.service"
+ ];
+ requires = [
+ "podman-network-booklore_default.service"
+ ];
+ };
+
+ virtualisation.oci-containers.containers.mariadb-booklore = {
+ image = "lscr.io/linuxserver/mariadb:11.4.5";
+ environment = {
+ PUID = "1000";
+ PGID = "1000";
+ TZ = cfg.settings.timezone;
+ MYSQL_DATABASE = cfg.database.name;
+ MYSQL_USER = cfg.database.user;
+ MYSQL_PASSWORD = cfg.database.password;
+ };
+ networks = [
+ "booklore_default"
+ ];
+ };
+
+ systemd.services."podman-mariadb-booklore" = {
+ serviceConfig = {
+ Restart = lib.mkOverride 90 "always";
+ };
+ after = [
+ "podman-network-booklore_default.service"
+ ];
+ requires = [
+ "podman-network-booklore_default.service"
+ ];
+ };
+
+ systemd.services."podman-network-booklore_default" = {
+ path = [pkgs.podman];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStop = "podman network rm -f booklore_default";
+ };
+ script = ''
+ podman network inspect booklore_default || podman network create booklore_default
+ '';
+ };
+ };
+ };
+}
diff --git a/modules/nixosModules/directories.nix b/modules/nixosModules/directories.nix
new file mode 100644
index 0000000..6a1426f
--- /dev/null
+++ b/modules/nixosModules/directories.nix
@@ -0,0 +1,90 @@
+{inputs, ...}: {
+ flake.nixosModules.directories = {
+ config,
+ lib,
+ ...
+ }:
+ with lib; let
+ cfg = config.createPaths;
+ pathAttrsToListRec = pathsAttrSet: parentPath: parentConfig:
+ lib.flatten (lib.mapAttrsToList (path: config: let
+ filteredConfig = lib.filterAttrs (n: v: v != null) (builtins.removeAttrs config ["subPaths"]);
+ out =
+ {
+ path =
+ if parentPath == ""
+ then path
+ else parentPath + "/" + path;
+ }
+ // parentConfig // filteredConfig;
+ in
+ if config ? subPaths
+ then [out] ++ (pathAttrsToListRec config.subPaths path filteredConfig)
+ else [out])
+ pathsAttrSet);
+ pathConfig = {
+ options = {
+ group = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ owner = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ permissions = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ subPaths = mkOption {
+ type = types.attrsOf (types.submodule pathConfig);
+ default = {};
+ };
+ };
+ };
+ pathList = pathAttrsToListRec cfg "" {};
+ in rec {
+ options = {
+ createPaths = mkOption {
+ type = types.attrsOf (types.submodule {
+ options = {
+ group = mkOption {
+ type = types.str;
+ };
+ owner = mkOption {
+ type = types.str;
+ };
+ permissions = mkOption {
+ type = types.str;
+ default = "0740";
+ };
+ subPaths = mkOption {
+ type = types.attrsOf (types.submodule pathConfig);
+ default = {};
+ description = ''
+ SubPaths to create using systemd tmpfiles.
+ '';
+ };
+ };
+ });
+ default = {};
+ description = ''
+ Paths to create using systemd tmpfiles.
+ '';
+ };
+ };
+
+ config = mkIf (cfg != {}) {
+ systemd.tmpfiles.rules =
+ map
+ (dir: "d ${dir.path} ${dir.permissions} ${dir.owner} ${dir.group}")
+ pathList;
+
+ users = let
+ extraGroups = map (path: path.group) pathList;
+ in {
+ groups = genAttrs extraGroups (group: {});
+ };
+ };
+ };
+}
diff --git a/modules/nixosModules/gonic.nix b/modules/nixosModules/gonic.nix
new file mode 100644
index 0000000..0f1907a
--- /dev/null
+++ b/modules/nixosModules/gonic.nix
@@ -0,0 +1,114 @@
+{inputs, ...}: {
+ flake.nixosModules.gonic = {
+ config,
+ lib,
+ pkgs,
+ ...
+ }:
+ with lib; let
+ cfg = config.gonic;
+ in {
+ options = {
+ gonic = {
+ enable = mkEnableOption "enable gonic configuration";
+
+ listenAddr = mkOption {
+ type = types.str;
+ default = "127.0.0.1:4747";
+ description = ''
+ Address that gonic will listen on.
+ '';
+ };
+
+ extraGroups = mkOption {
+ type = types.listOf (types.str);
+ default = [];
+ description = ''
+ Additional groups for gonic.
+ '';
+ };
+
+ musicPaths = mkOption {
+ type = types.listOf (types.str);
+ description = ''
+ Directories with music in it.
+ '';
+ };
+
+ podcastsPath = mkOption {
+ type = types.str;
+ default = "${cfg.stateDir}/podcasts";
+ description = ''
+ Directory for podcasts.
+ '';
+ };
+
+ playlistsPath = mkOption {
+ type = types.str;
+ default = "${cfg.stateDir}/playlists";
+ description = ''
+ Directory for playlists.
+ '';
+ };
+
+ stateDir = mkOption {
+ type = types.str;
+ default = "/var/lib/gonic";
+ description = ''
+ A directory where gonic will keep their files.
+ '';
+ };
+
+ settings = mkOption {
+ default = {};
+ description = ''
+ Additional gonic settings
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.gonic.serviceConfig = {
+ DynamicUser = lib.mkForce false;
+ User = "gonic";
+ Group = "gonic";
+ SupplementaryGroups = cfg.extraGroups;
+ ReadWritePaths = [
+ cfg.podcastsPath
+ cfg.playlistsPath
+ ];
+ };
+
+ users = {
+ groups = {
+ gonic = {};
+ };
+
+ users.gonic = {
+ isSystemUser = true;
+ group = "gonic";
+ };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.stateDir} 0755 gonic gonic"
+ "d ${cfg.podcastsPath} 0755 gonic gonic"
+ "d ${cfg.playlistsPath} 0755 gonic gonic"
+ ];
+
+ services.gonic = {
+ enable = true;
+ settings =
+ {
+ listen-addr = cfg.listenAddr;
+ music-path = cfg.musicPaths;
+ playlists-path = [cfg.playlistsPath];
+ podcast-path = [cfg.podcastsPath];
+ db-path = ["${cfg.stateDir}/gonic.db"];
+ }
+ // cfg.settings;
+ };
+ };
+ };
+}
diff --git a/modules/nixosModules/nfs.nix b/modules/nixosModules/nfs.nix
new file mode 100644
index 0000000..3f53cc6
--- /dev/null
+++ b/modules/nixosModules/nfs.nix
@@ -0,0 +1,118 @@
+{inputs, ...}: {
+ flake.nixosModules.nfs = {
+ config,
+ lib,
+ ...
+ }:
+ with lib; let
+ cfg = config.nfs;
+ in {
+ options = {
+ nfs.server = mkOption {
+ description = ''
+ NFS server configuration.
+ '';
+ default = {enable = false;};
+ type = types.submodule {
+ options = {
+ enable = mkEnableOption "Enable nfs server";
+ exportsPath = mkOption {
+ type = types.str;
+ default = "/export";
+ description = ''
+ A path to the dir, where exports will be binded.
+ '';
+ };
+
+ defaultExportIps = mkOption {
+ type = types.listOf (types.str);
+ description = ''
+ A list of ip addresses, that will be used as default in exportDirs
+ '';
+ };
+
+ defaultExportParams = mkOption {
+ type = types.str;
+ default = "rw,nohide,insecure,no_subtree_check";
+ description = ''
+ Params, that will be used as default in exportDirs
+ '';
+ };
+
+ exportDirs = mkOption {
+ description = ''
+ A list of directories to export.
+ '';
+ type = types.listOf (types.submodule {
+ options = {
+ path = mkOption {
+ type = types.str;
+ description = ''
+ A path to the directory to export.
+ '';
+ };
+ exportPath = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ A path that will be binded to the export directory in the exportsPath.
+ '';
+ };
+ ips = mkOption {
+ type = types.listOf (types.str);
+ default = cfg.server.defaultExportIps;
+ description = ''
+ A list of ip addresses to export the dir to.
+ '';
+ };
+ params = mkOption {
+ type = types.str;
+ default = cfg.server.defaultExportParams;
+ description = ''
+ Params for the ip addresses.
+ '';
+ };
+ };
+ });
+ };
+ };
+ };
+ };
+ };
+
+ config = mkIf cfg.server.enable {
+ services.nfs.server = {
+ enable = true;
+ exports =
+ "${cfg.server.exportsPath} ${concatMapStrings (ip: "${ip}(rw,fsid=0,no_subtree_check) ") cfg.server.defaultExportIps}\n"
+ + concatMapStrings
+ (dir: let
+ ips = concatMapStrings (ip: "${ip}(${dir.params}) ") dir.ips;
+ exportPath =
+ if dir.exportPath != ""
+ then dir.exportPath
+ else baseNameOf dir.path;
+ in "${cfg.server.exportsPath}/${exportPath} ${ips}\n")
+ cfg.server.exportDirs;
+ };
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.server.exportsPath} 0744 nobody nogroup"
+ ];
+
+ fileSystems = listToAttrs (map (exportDir: let
+ exportPath =
+ if exportDir.exportPath != ""
+ then exportDir.exportPath
+ else baseNameOf exportDir.path;
+ fullExportPath = "${cfg.server.exportsPath}/${exportPath}";
+ in {
+ name = fullExportPath;
+ value = {
+ device = exportDir.path;
+ options = ["bind"];
+ };
+ }) cfg.server.exportDirs);
+ };
+ };
+}
diff --git a/modules/nixosModules/nginxProxy.nix b/modules/nixosModules/nginxProxy.nix
new file mode 100644
index 0000000..36fdc59
--- /dev/null
+++ b/modules/nixosModules/nginxProxy.nix
@@ -0,0 +1,217 @@
+{inputs, ...}: {
+ flake.nixosModules.nginxProxy = {
+ pkgs,
+ config,
+ lib,
+ ...
+ }:
+ with lib; let
+ vhostOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/vhost-options.nix");
+ locationOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/location-options.nix");
+ nginxOptions = import (pkgs.path + "/nixos/modules/services/web-servers/nginx/default.nix");
+
+ autheliaAuth = url: ''
+ auth_request /internal/authelia/authz;
+ auth_request_set $redirection_url $upstream_http_location;
+ error_page 401 =302 $redirection_url;
+
+ auth_request_set $user $upstream_http_remote_user;
+ auth_request_set $groups $upstream_http_remote_groups;
+ auth_request_set $email $upstream_http_remote_email;
+ auth_request_set $name $upstream_http_remote_name;
+
+ proxy_set_header Remote-User $user;
+ proxy_set_header Remote-Groups $groups;
+ proxy_set_header Remote-Email $email;
+ proxy_set_header Remote-Name $name;
+ '';
+
+ autheliaLocation = url: ''
+ internal;
+ set $upstream_authelia ${url}/api/authz/auth-request;
+ proxy_pass $upstream_authelia;
+
+ ## Headers
+ ## The headers starting with X-* are required.
+ proxy_set_header X-Original-Method $request_method;
+ proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header Content-Length "";
+ proxy_set_header Connection "";
+
+ ## Basic Proxy Configuration
+ proxy_pass_request_body off;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead
+ proxy_redirect http:// $scheme://;
+ proxy_http_version 1.1;
+ proxy_cache_bypass $cookie_session;
+ proxy_no_cache $cookie_session;
+ proxy_buffers 4 32k;
+ client_body_buffer_size 128k;
+
+ ## Advanced Proxy Configuration
+ send_timeout 5m;
+ proxy_read_timeout 240;
+ proxy_send_timeout 240;
+ proxy_connect_timeout 240;
+ '';
+
+ cfg = config.nginxProxy;
+ in {
+ options.nginxProxy = {
+ enable = mkEnableOption "Enable nginxProxy";
+
+ domain = mkOption {
+ type = types.str;
+ description = ''
+ Domain to use with subdomains
+ '';
+ };
+
+ recommendedProxySettings = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ Enables global recommended proxy settings
+ '';
+ };
+
+ subdomains = mkOption {
+ type = types.attrsOf (types.submodule (locationOptions {inherit config lib;}));
+ description = ''
+ Subdomains with nginx virtualHosts configuration
+ '';
+ };
+
+ extraVirtualHosts = mkOption {
+ type = types.attrsOf (types.submodule (vhostOptions {inherit config lib;}));
+ default = {};
+ };
+
+ home = {
+ virtualHosts = mkOption {
+ type = types.attrsOf (types.submodule (vhostOptions {inherit config lib;}));
+ default = {};
+ description = ''
+ Virtual hosts from another nginx configuration, that will be used to decrypt ssl and forward traffic to another server.
+ Make sure that the connection between the two is secure.
+ '';
+ };
+
+ subdomains = mkOption {
+ type = types.attrsOf (types.submodule (locationOptions {inherit config lib;}));
+ default = {};
+ description = ''
+ Subdomains from another nginx configuration, that will be used to decrypt ssl and forward traffic to another server.
+ Make sure that the connection between the two is secure.
+ '';
+ };
+
+ domain = mkOption {
+ type = types.str;
+ default = cfg.domain;
+ description = ''
+ Home domain, if no domain provided, the current will be used;
+ '';
+ };
+
+ url = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Url that requests would be passed to;
+ '';
+ };
+
+ authelia = mkOption {
+ type = types.submodule {
+ options = {
+ enable = mkOption {
+ type = types.bool;
+ default = true;
+ };
+ publicUrl = mkOption {
+ type = types.str;
+ default = "https://auth.${cfg.domain}/";
+ };
+ localUrl = mkOption {
+ type = types.str;
+ default = "http://127.0.0.1:9091";
+ };
+ };
+ };
+ default = {};
+ };
+ };
+
+ acme = {
+ enable = mkEnableOption "enable acme certs";
+ email = mkOption {
+ type = types.str;
+ default = "notspl3g+acme@duck.com";
+ };
+ };
+
+ extraConfig = mkOption {
+ type = types.attrsOf (types.submodule nginxOptions);
+ default = {};
+ description = ''
+ Extra nginx config.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ security.acme = mkIf cfg.acme.enable {
+ acceptTerms = true;
+ defaults.email = cfg.acme.email;
+ };
+
+ users.groups.nginx = mkIf cfg.acme.enable {};
+ users.users.nginx = mkIf cfg.acme.enable {
+ group = "nginx";
+ extraGroups = ["acme"];
+ isSystemUser = true;
+ };
+ services.nginx = let
+ ssl = {
+ forceSSL = cfg.acme.enable;
+ enableACME = cfg.acme.enable;
+ };
+
+ makeVhosts = domain: subdomains:
+ lib.concatMapAttrs
+ (name: value: {${name + "." + domain} = {locations."/" = value;} // ssl;})
+ subdomains;
+
+ homeRoutes = homeVirtualHosts: homeUrl:
+ builtins.mapAttrs
+ (name: value:
+ {
+ locations."/" =
+ value.locations."/"
+ // {
+ proxyPass = homeUrl;
+ recommendedProxySettings = true;
+ extraConfig = value.locations."/".extraConfig + (autheliaAuth cfg.home.authelia.publicUrl);
+ };
+ locations."/internal/authelia/authz" = mkIf cfg.home.authelia.enable {
+ extraConfig = autheliaLocation cfg.home.authelia.localUrl;
+ };
+ }
+ // ssl)
+ homeVirtualHosts;
+
+ vhosts = makeVhosts cfg.domain cfg.subdomains;
+ homeVhosts = homeRoutes ((makeVhosts (cfg.home.domain) cfg.home.subdomains) // cfg.home.virtualHosts) cfg.home.url;
+ in
+ {
+ enable = true;
+ recommendedProxySettings = cfg.recommendedProxySettings;
+
+ virtualHosts = vhosts // homeVhosts // cfg.extraVirtualHosts;
+ }
+ // cfg.extraConfig;
+ };
+ };
+}
diff --git a/modules/nixosModules/watcharr.nix b/modules/nixosModules/watcharr.nix
new file mode 100644
index 0000000..2263e4f
--- /dev/null
+++ b/modules/nixosModules/watcharr.nix
@@ -0,0 +1,74 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.nixosModules.watcharr = {
+ config,
+ lib,
+ pkgs,
+ ...
+ }:
+ with lib; let
+ cfg = config.services.watcharr;
+ port = builtins.toString cfg.settings.port;
+ in {
+ options = {
+ services.watcharr = {
+ enable = mkEnableOption "Enable watcharr service";
+ subdomain = mkOption {
+ type = types.str;
+ description = ''
+ Subdomain to use for nginx.
+ '';
+ };
+ settings = {
+ dataDir = mkOption {
+ type = types.path;
+ description = ''
+ Watcharr data directory.
+ '';
+ default = "/var/lib/watcharr";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 3080;
+ description = ''
+ Port to use.
+ '';
+ };
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ createPaths = {
+ "${cfg.settings.dataDir}" = {
+ owner = "root";
+ group = "root";
+ permissions = "0750";
+ };
+ };
+
+ nginxProxy = {
+ enable = true;
+ subdomains = {
+ "${cfg.subdomain}" = {
+ proxyPass = "http://127.0.0.1:${port}";
+ proxyWebsockets = true;
+ };
+ };
+ };
+
+ virtualisation.oci-containers.containers.watcharr = {
+ image = "ghcr.io/sbondco/watcharr:latest";
+ ports = [
+ "127.0.0.1${port}:3080"
+ ];
+ volumes = [
+ "${cfg.settings.dataDir}:/data"
+ ];
+ };
+ };
+ };
+}
diff --git a/modules/overlays.nix b/modules/overlays.nix
new file mode 100644
index 0000000..fc4327f
--- /dev/null
+++ b/modules/overlays.nix
@@ -0,0 +1,30 @@
+{
+ inputs,
+ self,
+ ...
+}: {
+ flake.overlays = {
+ truly-unstable-packages = final: _prev: {
+ unstable = import inputs.nixpkgs-small {
+ system = final.system;
+ config.allowUnfree = true;
+ };
+ };
+
+ unstable-packages = final: _prev: {
+ unstable = import inputs.nixpkgs {
+ system = final.system;
+ config.allowUnfree = true;
+ };
+ };
+
+ stable-packages = final: _prev: {
+ stable = import inputs.nixpkgs-stable {
+ system = final.system;
+ config.allowUnfree = true;
+ };
+ };
+
+ # additions = final: _prev: import self.packages {pkgs = final;};
+ };
+}
diff --git a/modules/pkgs/explo/default.nix b/modules/pkgs/explo/default.nix
new file mode 100644
index 0000000..77fcc39
--- /dev/null
+++ b/modules/pkgs/explo/default.nix
@@ -0,0 +1,28 @@
+let
+ explo = {
+ lib,
+ buildGoModule,
+ fetchFromGitHub,
+ jellyfin-ffmpeg,
+ yt-dlp,
+ }:
+ buildGoModule rec {
+ pname = "explo";
+ version = "0.11.5";
+
+ src = fetchFromGitHub {
+ owner = "LumePart";
+ repo = "Explo";
+ rev = "v${version}";
+ sha256 = "sha256-A3ikFH0/C/dat1pf7t1Gp6bfitmbPHK+RKVzqsLzjc0=";
+ };
+
+ buildInputs = [jellyfin-ffmpeg yt-dlp];
+
+ vendorHash = "sha256-jTvxv0cyE/+BNkrajIj8E3xlftq+PCtGbmz+P3IuMFw=";
+ };
+in {
+ perSystem = {pkgs, ...}: {
+ packages.explo = pkgs.callPackage explo {};
+ };
+}
diff --git a/modules/shell.nix b/modules/shell.nix
new file mode 100644
index 0000000..d3131bf
--- /dev/null
+++ b/modules/shell.nix
@@ -0,0 +1,14 @@
+{
+ perSystem = {
+ pkgs,
+ config,
+ ...
+ }: {
+ devShells.default = pkgs.mkShell {
+ packages = with pkgs; [
+ config.agenix-rekey.package
+ deploy-rs
+ ];
+ };
+ };
+}