diff options
233 files changed, 6614 insertions, 1750 deletions
@@ -1,7 +1,42 @@ keys: - - &SSHDerived age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + - &general age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + - &flex-wg-router age1f0tmpy2nam58skmznjyqd3zf54rxtfrk6fda0vlpq9y3yg6wac7sjf0vja + - &poweredge age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + - &sweetiepc age1fl4rk54n2xv4vssjkw4nywy93s4yqv69uzq2f64zuwd8fjq5u4cq9wmmsw creation_rules: - - path_regex: secrets/.*.yaml$ + - path_regex: timmy/resources/secrets/.* key_groups: - age: - - *SSHDerived + - *general + - *flex-wg-router + - *poweredge + + - path_regex: T495/resources/secrets/.* + key_groups: + - age: + - *general + + - path_regex: X230/resources/secrets/.* + key_groups: + - age: + - *general + + - path_regex: hp-envy-office/resources/secrets/.* + key_groups: + - age: + - *general + + - path_regex: flex-wg-router/resources/secrets/.* + key_groups: + - age: + - *flex-wg-router + + - path_regex: poweredge/resources/secrets/.* + key_groups: + - age: + - *poweredge + + - path_regex: sweetiepc/resources/secrets/.* + key_groups: + - age: + - *sweetiepc diff --git a/archetypes/collections/bluetooth.nix b/archetypes/collections/bluetooth.nix new file mode 100644 index 0000000..749a9f1 --- /dev/null +++ b/archetypes/collections/bluetooth.nix @@ -0,0 +1,12 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.bluetooth; +in { + options._archetypes.collections.bluetooth = { + enable = lib.mkEnableOption "enables bluetooth and blueman"; + }; + + config = lib.mkIf cfg.enable { + hardware.bluetooth.enable = true; + services.blueman.enable = config._archetypes.collections.desktop.utilities.enable; # FIXME + }; +} diff --git a/archetypes/collections/desktop/cad.nix b/archetypes/collections/desktop/cad.nix new file mode 100644 index 0000000..6b2c26b --- /dev/null +++ b/archetypes/collections/desktop/cad.nix @@ -0,0 +1,21 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.cad; +in { + options._archetypes.collections.desktop.cad = { + enable = lib.mkEnableOption "install cad and 3d printing software"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + blender + freecad + prusa-slicer + ]; + # TODO remove once #467783 is resolved + environment.sessionVariables = { + # Make GTK3 file-chooser settings discoverable + # per https://github.com/NixOS/nixpkgs/issues/467783#issuecomment-3648708206 + GSETTINGS_SCHEMA_DIR ="${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}/glib-2.0/schemas"; + }; + }; +} diff --git a/archetypes/collections/desktop/chromium.nix b/archetypes/collections/desktop/chromium.nix new file mode 100644 index 0000000..b4638f0 --- /dev/null +++ b/archetypes/collections/desktop/chromium.nix @@ -0,0 +1,18 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.chromium; +in { + options._archetypes.collections.desktop.chromium = { + enable = lib.mkEnableOption "install chromium browser"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.ungoogled-chromium; + description = "chromium package to install"; + }; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = [ + cfg.package + ]; + }; +} diff --git a/archetypes/collections/desktop/crypto.nix b/archetypes/collections/desktop/crypto.nix new file mode 100644 index 0000000..bfb9186 --- /dev/null +++ b/archetypes/collections/desktop/crypto.nix @@ -0,0 +1,13 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.crypto; +in { + options._archetypes.collections.desktop.crypto = { + enable = lib.mkEnableOption "install crypto wallets"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + unstable.sparrow + ]; + }; +} diff --git a/archetypes/collections/desktop/default.nix b/archetypes/collections/desktop/default.nix new file mode 100644 index 0000000..6ac79dd --- /dev/null +++ b/archetypes/collections/desktop/default.nix @@ -0,0 +1,17 @@ +{ + imports = [ + ./cad.nix + ./chromium.nix + ./crypto.nix + ./extra.nix + ./firefox.nix + ./graphics.nix + ./office.nix + ./pipewire.nix + ./printing.nix + ./thumbnailers.nix + ./utilities.nix + ./video.nix + ./xserver.nix + ]; +} diff --git a/archetypes/collections/desktop/extra.nix b/archetypes/collections/desktop/extra.nix new file mode 100644 index 0000000..5cf4221 --- /dev/null +++ b/archetypes/collections/desktop/extra.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.extraUtilities; +in { + options._archetypes.collections.desktop.extraUtilities = { + enable = lib.mkEnableOption "install extra desktop utilities"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + jellyfin-mpv-shim + qbittorrent + remmina + sent + ]; + }; +} diff --git a/archetypes/collections/desktop/firefox.nix b/archetypes/collections/desktop/firefox.nix new file mode 100644 index 0000000..02656f6 --- /dev/null +++ b/archetypes/collections/desktop/firefox.nix @@ -0,0 +1,12 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.firefox; +in { + options._archetypes.collections.desktop.firefox = { + enable = lib.mkEnableOption "install firefox"; + }; + + config = lib.mkIf cfg.enable { + programs.firefox.enable = true; + }; +} + diff --git a/archetypes/collections/desktop/graphics.nix b/archetypes/collections/desktop/graphics.nix new file mode 100644 index 0000000..f4242d1 --- /dev/null +++ b/archetypes/collections/desktop/graphics.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.graphics; +in { + options._archetypes.collections.desktop.graphics = { + enable = lib.mkEnableOption "install graphic design software"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + blender + geeqie + gimp3 + inkscape + ]; + }; +} diff --git a/archetypes/collections/desktop/office.nix b/archetypes/collections/desktop/office.nix new file mode 100644 index 0000000..23e6862 --- /dev/null +++ b/archetypes/collections/desktop/office.nix @@ -0,0 +1,18 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.office; +in { + options._archetypes.collections.desktop.office = { + enable = lib.mkEnableOption "install office software"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + hunspell # Spell checking in libreoffice + hunspellDicts.en_US + kdePackages.okular + libreoffice + pdfchain + thunderbird + ]; + }; +} diff --git a/archetypes/collections/desktop/pipewire.nix b/archetypes/collections/desktop/pipewire.nix new file mode 100644 index 0000000..d80ac90 --- /dev/null +++ b/archetypes/collections/desktop/pipewire.nix @@ -0,0 +1,14 @@ +{ lib, config, ... }: let + cfg = config._archetypes.collections.desktop.pipewire; +in { + options._archetypes.collections.desktop.pipewire = { + enable = lib.mkEnableOption "enables pipewire"; + }; + + config = lib.mkIf cfg.enable { + services.pipewire = { + enable = true; + pulse.enable = true; + }; + }; +} diff --git a/archetypes/collections/desktop/printing.nix b/archetypes/collections/desktop/printing.nix new file mode 100644 index 0000000..ed31048 --- /dev/null +++ b/archetypes/collections/desktop/printing.nix @@ -0,0 +1,17 @@ +{ lib, config, ... }: let + cfg = config._archetypes.collections.desktop.printing; +in { + options._archetypes.collections.desktop.printing = { + enable = lib.mkEnableOption "enables printing and avahi service"; + }; + + config = lib.mkIf cfg.enable { + services.printing.enable = true; + + services.avahi = { + enable = true; + nssmdns4 = true; + openFirewall = true; + }; + }; +} diff --git a/archetypes/collections/desktop/thumbnailers.nix b/archetypes/collections/desktop/thumbnailers.nix new file mode 100644 index 0000000..50e4aa7 --- /dev/null +++ b/archetypes/collections/desktop/thumbnailers.nix @@ -0,0 +1,23 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.thumbnailers; +in { + options._archetypes.collections.desktop.thumbnailers = { + enable = lib.mkEnableOption "install thumbnailers for graphical file managers"; + }; + + config = lib.mkIf cfg.enable { + # https://wiki.nixos.org/wiki/Thumbnails + # Thumbnailers created in /run/current-system/sw/share/thumbnailers + environment.systemPackages = with pkgs; [ + # Video + ffmpegthumbnailer + # Images + gdk-pixbuf + # HEIF images + libheif + libheif.out + # RAW images + nufraw-thumbnailer + ]; + }; +} diff --git a/archetypes/collections/desktop/utilities.nix b/archetypes/collections/desktop/utilities.nix new file mode 100644 index 0000000..479145b --- /dev/null +++ b/archetypes/collections/desktop/utilities.nix @@ -0,0 +1,32 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.utilities; +in { + options._archetypes.collections.desktop.utilities = { + enable = lib.mkEnableOption "install basic desktop utilities"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + arandr + dex # Execute .desktop files + dmenu + gammastep + libnotify + lowbat + mpv + nsxiv + pavolctld + pavucontrol + pcmanfm + qdirstat + qdiskinfo + scrot + st + wpa_supplicant_gui + zathura + ]; + + programs.localsend.enable = true; # Installs & opens firewall + services.gvfs.enable = true; # GVfs allows for mounting drives in a graphical file manager + }; +} diff --git a/archetypes/collections/desktop/video.nix b/archetypes/collections/desktop/video.nix new file mode 100644 index 0000000..cce6516 --- /dev/null +++ b/archetypes/collections/desktop/video.nix @@ -0,0 +1,17 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.video; +in { + options._archetypes.collections.desktop.video = { + enable = lib.mkEnableOption "install video capture and editing software"; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + dvgrab + ffmpeg + mpv + obs-studio + vlc + ]; + }; +} diff --git a/archetypes/collections/desktop/xserver.nix b/archetypes/collections/desktop/xserver.nix new file mode 100644 index 0000000..9d62048 --- /dev/null +++ b/archetypes/collections/desktop/xserver.nix @@ -0,0 +1,34 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.desktop.xserver; +in { + options._archetypes.collections.desktop.xserver = { + enable = lib.mkEnableOption "installs xserver"; + utilities.enable = lib.mkEnableOption "installs basic xserver utilities"; + }; + + config = lib.mkIf cfg.enable { + services = { + xserver = { + enable = true; + displayManager.startx.enable = true; + enableTearFree = true; + }; + libinput.enable = true; # Enable touchpad support + }; + + # Install basic X utilities + environment.systemPackages = with pkgs; lib.optionals cfg.utilities.enable [ + setxkbmap + unclutter-desktop-entry + unclutter-xfixes + xcape + xclip + xdotool + xinput + xkill + xrandr + xset + xwallpaper + ]; + }; +} diff --git a/archetypes/collections/development/default.nix b/archetypes/collections/development/default.nix new file mode 100644 index 0000000..87fe2d2 --- /dev/null +++ b/archetypes/collections/development/default.nix @@ -0,0 +1,45 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.development; + hugoFirewallPort = 1313; +in { + imports = [ + ./docker.nix + ]; + + options._archetypes.collections.development = { + utilities.enable = lib.mkEnableOption "install basic dev utilities"; + android.enable = lib.mkEnableOption "install android dev tools"; + c.enable = lib.mkEnableOption "install c dev tools"; + lua.enable = lib.mkEnableOption "install lua dev tools"; + web = { + hugo = { + enable = lib.mkEnableOption "install hugo"; + openFirewall = lib.mkEnableOption "open the port ${hugoFirewallPort} for viewing content from hugo serve on other devices"; + }; + node.enable = lib.mkEnableOption "install node"; + }; + }; + + config = { + environment.systemPackages = with pkgs; [ + git + python3 + ] ++ lib.optionals cfg.android.enable [ + #adb-sync + android-tools + ] ++ lib.optionals cfg.c.enable [ + gcc + git + gnumake + pkg-config + ] ++ lib.optionals cfg.lua.enable [ + lua + ] ++ lib.optionals cfg.web.hugo.enable [ + hugo + ] ++ lib.optionals cfg.web.node.enable [ + nodejs + ]; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.web.hugo.openFirewall [ hugoFirewallPort ]; + }; +} diff --git a/archetypes/collections/development/docker.nix b/archetypes/collections/development/docker.nix new file mode 100644 index 0000000..0c9fce4 --- /dev/null +++ b/archetypes/collections/development/docker.nix @@ -0,0 +1,15 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.development.docker; +in { + options._archetypes.collections.development.docker = { + enable = lib.mkEnableOption "enables docker"; + }; + + config = lib.mkIf cfg.enable { + virtualisation.docker.enable = true; + + environment.systemPackages = with pkgs; [ + docker-compose + ]; + }; +} diff --git a/modules/root/software/ddcutil.nix b/archetypes/collections/filesystems.nix index 93e0af5..00ab409 100644 --- a/modules/root/software/ddcutil.nix +++ b/archetypes/collections/filesystems.nix @@ -1,6 +1,7 @@ { pkgs, ... }: { - hardware.i2c.enable = true; environment.systemPackages = with pkgs; [ - ddcutil + cryptsetup + exfat + ntfs3g ]; } diff --git a/archetypes/collections/fonts.nix b/archetypes/collections/fonts.nix new file mode 100644 index 0000000..136e31b --- /dev/null +++ b/archetypes/collections/fonts.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.fonts; +in { + options._archetypes.collections.fonts = { + enable = lib.mkEnableOption "enables fonts"; + }; + + config = lib.mkIf cfg.enable { + fonts.packages = with pkgs; [ + commit-mono + inter + nerd-fonts.jetbrains-mono + tamzen + ]; + }; +} diff --git a/archetypes/collections/neovim.nix b/archetypes/collections/neovim.nix new file mode 100644 index 0000000..2040d77 --- /dev/null +++ b/archetypes/collections/neovim.nix @@ -0,0 +1,18 @@ +{ lib, pkgs, ... }: let + cfg.neovimPackage = pkgs.unstable.neovim; + + # Neovim package that includes aliases vi(m) -> nvim + neovim-aliases = pkgs.symlinkJoin { + name = "neovim-aliases"; + paths = [ cfg.neovimPackage ]; + postBuild = '' + ln -sf $out/bin/nvim $out/bin/vi + ln -sf $out/bin/nvim $out/bin/vim + ''; + }; +in { + # NOTE: Don't use programs.neovim since that will build neovim from source + environment.systemPackages = with pkgs; [ + neovim-aliases + ]; +} diff --git a/archetypes/collections/nvidia.nix b/archetypes/collections/nvidia.nix new file mode 100644 index 0000000..ce22e8b --- /dev/null +++ b/archetypes/collections/nvidia.nix @@ -0,0 +1,14 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.nvidia; +in { + options._archetypes.collections.nvidia = { + enable = lib.mkEnableOption "enables nvidia debugging packages, etc."; + }; + + config = lib.mkIf cfg.enable { + environment.systemPackages = with pkgs; [ + config.hardware.nvidia.package # nvidia-smi + nvtopPackages.nvidia + ]; + }; +} diff --git a/modules/root/software/utilities.nix b/archetypes/collections/utilities.nix index dabf163..1597d75 100644 --- a/modules/root/software/utilities.nix +++ b/archetypes/collections/utilities.nix @@ -1,17 +1,20 @@ { pkgs, ... }: { environment.systemPackages = with pkgs; [ + R + dash # TODO should be default /bin/sh entr fastfetch ffmpeg htop jq - light + killall + #light # TODO find replacement lm_sensors mediainfo - neovim nmap openssl p7zip + pciutils powertop pv rsync @@ -22,11 +25,13 @@ testdisk tmux uhubctl + usbutils + vimv-rs wget wireguard-tools - xxHash + xxhash yt-dlp - (callPackage ./derivations/lowbat {}) - (callPackage ./derivations/pavolctld {}) ]; + + services.gpm.enable = true; } diff --git a/archetypes/collections/virtualization.nix b/archetypes/collections/virtualization.nix new file mode 100644 index 0000000..b91f6e1 --- /dev/null +++ b/archetypes/collections/virtualization.nix @@ -0,0 +1,21 @@ +{ pkgs, lib, config, ... }: let + cfg = config._archetypes.collections.virtualization; +in { + options._archetypes.collections.virtualization = { + enable = lib.mkEnableOption "enables virtualization and virt-manager"; + }; + + config = lib.mkIf cfg.enable { + virtualisation = { + spiceUSBRedirection.enable = true; + libvirtd.enable = true; + # Enable efi support with ovmf firmware + libvirtd.qemu = { + package = pkgs.qemu_kvm; + runAsRoot = true; + swtpm.enable = true; + }; + }; + programs.virt-manager.enable = config._archetypes.collections.desktop.utilities.enable; # FIXME + }; +} diff --git a/archetypes/default.nix b/archetypes/default.nix new file mode 100644 index 0000000..f306073 --- /dev/null +++ b/archetypes/default.nix @@ -0,0 +1,15 @@ +{ + imports = [ + ./collections/bluetooth.nix + ./collections/desktop + ./collections/development + ./collections/filesystems.nix + ./collections/fonts.nix + ./collections/neovim.nix + ./collections/nvidia.nix + ./collections/utilities.nix + ./collections/virtualization.nix + + ./profiles + ]; +} diff --git a/archetypes/profiles/btrfs/default.nix b/archetypes/profiles/btrfs/default.nix new file mode 100644 index 0000000..88fad38 --- /dev/null +++ b/archetypes/profiles/btrfs/default.nix @@ -0,0 +1,17 @@ +{ lib, pkgs, ... }: let + mkBtrfs = lib.mkOverride 820; + + # https://wiki.nixos.org/wiki/Btrfs + nixosConfig = { + services.btrfs = { + autoScrub.enable = mkBtrfs true; + }; + + # Make docker work with btrfs + virtualisation.docker.storageDriver = mkBtrfs "btrfs"; + }; + + homeConfig = {}; +in { + imports = [ (lib._mkProfileArchetype "btrfs" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/cuda/default.nix b/archetypes/profiles/cuda/default.nix new file mode 100644 index 0000000..b57b854 --- /dev/null +++ b/archetypes/profiles/cuda/default.nix @@ -0,0 +1,26 @@ +{ lib, pkgs, ... }: let + mkCuda = lib.mkOverride 800; + + nixosConfig = { + # Binary cache packages built with cudaSupport + nix.settings = { + substituters = mkCuda [ "https://cache.nixos-cuda.org" ]; + trusted-public-keys = mkCuda [ "cache.nixos-cuda.org:74DUi4Ye579gUqzH4ziL9IyiJBlDpMRn9MBN8oNan9M=" ]; + }; + + # Enable cudaSupport + nixpkgs.config.cudaSupport = mkCuda true; + + _archetypes.collections = { + nvidia.enable = mkCuda true; + }; + + # Enable nvidia graphics + services.xserver.videoDrivers = mkCuda [ "nvidia" ]; # xserver.videoDrivers does not imply X + hardware.graphics.enable = mkCuda true; + }; + + homeConfig = {}; +in { + imports = [ (lib._mkProfileArchetype "cuda" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/default.nix b/archetypes/profiles/default.nix new file mode 100644 index 0000000..1664484 --- /dev/null +++ b/archetypes/profiles/default.nix @@ -0,0 +1,11 @@ +{ + imports = [ + ./gnome # 700 + ./cuda # 800 + ./zfs # 810 + ./btrfs # 820 + ./pi # 900 + ./headless # 910 + ./desktop # 920 + ]; +} diff --git a/archetypes/profiles/desktop/default.nix b/archetypes/profiles/desktop/default.nix new file mode 100644 index 0000000..2f71791 --- /dev/null +++ b/archetypes/profiles/desktop/default.nix @@ -0,0 +1,144 @@ +{ lib, pkgs, ... }: let + mkDesktop = lib.mkOverride 920; + + nixosConfig = { + _archetypes.collections = { + desktop = { + firefox.enable = mkDesktop true; + pipewire.enable = mkDesktop true; + printing.enable = mkDesktop true; + thumbnailers.enable = mkDesktop true; + utilities.enable = mkDesktop true; + xserver = { + enable = mkDesktop true; + utilities.enable = mkDesktop true; + }; + }; + development = { + utilities.enable = mkDesktop true; + }; + fonts.enable = mkDesktop true; + }; + + security = { + polkit = { + enable = mkDesktop true; + _gnome.enable = mkDesktop true; + _allowUserPowerControls = mkDesktop true; + }; + }; + + programs = { + _ddcutil.enable = mkDesktop true; + }; + + services = { + _geoclue2.enable = mkDesktop true; + xserver = { + windowManager._awesome.enable = mkDesktop true; + enableTearFree = mkDesktop true; + }; + logind._safePowerKey.enable = mkDesktop true; + openssh = { + enable = mkDesktop true; + settings.X11Forwarding = mkDesktop true; + }; + tlp.enable = mkDesktop true; + + # Ensure video group can change backlight + udev.extraRules = mkDesktop '' + SUBSYSTEM=="backlight", ACTION=="add", RUN+="${pkgs.coreutils}/bin/chgrp video /sys/class/backlight/%k/brightness", RUN+="${pkgs.coreutils}/bin/chmod g+w /sys/class/backlight/%k/brightness" + ''; + }; + + # Disable ipv6 by default + networking.enableIPv6 = mkDesktop false; + + # Install dconf. Home-manager will break without it if gtk theme is changed + environment.systemPackages = [ pkgs.dconf ]; + }; + + homeConfig = { + gtk = { + enable = mkDesktop true; + _mintTheme.enable = mkDesktop true; + font.name = mkDesktop "sans-serif"; + cursorTheme.name = mkDesktop "Adwaita"; + }; + + qt._gtkPlatformTheme.enable = mkDesktop true; + + fonts.fontconfig = { + enable = mkDesktop true; + defaultFonts = { + # These fonts are added above in fonts collection + sansSerif = mkDesktop [ "Inter" ]; + monospace = mkDesktop [ "CommitMono" ]; + }; + configFile = { + tamzen-disable-anti-aliasing = { + enable = true; + source = ./resources/fontconfig/90-tamzen-disable-anti-aliasing.conf; + }; + commit-mono-options = { + enable = true; + source = ./resources/fontconfig/90-commit-mono-options.conf; + }; + }; + }; + + programs = { + _pcmanfm.openAsRootOption.enable = mkDesktop true; + _st = { + enable = mkDesktop true; + font = { + name = mkDesktop "JetBrainsMonoNL Nerd Font Mono"; + # Default attrs + attrs = { + size = 12; + antialias = true; + autohint = true; + }; + }; + }; + firefox._configure = mkDesktop true; + htop._configure = mkDesktop true; + zathura = { + enable = mkDesktop true; + options = { + selection-clipboard = mkDesktop "clipboard"; # Copy highlighted text + }; + }; + }; + + services = { + _gammastep.enable = true; + #polkit-gnome.enable = mkDesktop true; # Doesn't work on X + }; + + xdg = { + autostart = { + enable = mkDesktop true; + entries = mkDesktop [ + "${pkgs.gammastep}/share/applications/gammastep-indicator.desktop" + "${pkgs.unclutter-desktop-entry}/share/applications/unclutter.desktop" + ]; + }; + }; + + xdg.mimeApps._defaultCategoryApplications = { + enable = true; + categoryApplications = { + audio = [ "mpv" ]; + directory = [ "pcmanfm" ]; + email = [ "thunderbird" ]; + image = [ "nsxiv" ]; + pdf = [ "org.pwmt.zathura-pdf-mupdf" ]; + text = [ "nvim" ]; + video = [ "mpv" ]; + }; + }; + }; +in { + imports = [ (lib._mkProfileArchetype "desktop" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/desktop/resources/fontconfig/90-commit-mono-options.conf b/archetypes/profiles/desktop/resources/fontconfig/90-commit-mono-options.conf new file mode 100644 index 0000000..9c7373a --- /dev/null +++ b/archetypes/profiles/desktop/resources/fontconfig/90-commit-mono-options.conf @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> + +<fontconfig> + <description>Enable opentype features for CommitMono to make it match better with Inter as its monospace counterpart</description> + <match target="font"> + <test name="family" compare="eq" ignore-blanks="true"> + <string>CommitMono</string> + </test> + <edit name="fontfeatures" mode="append"> + <string>ss03 on</string> <!-- smart case --> + <string>ss04 on</string> <!-- symbol spacing --> + <string>cv02 on</string> <!-- alt g --> + <string>cv06 on</string> <!-- alt 6 & 9 --> + </edit> + </match> +</fontconfig> diff --git a/archetypes/profiles/desktop/resources/fontconfig/90-tamzen-disable-anti-aliasing.conf b/archetypes/profiles/desktop/resources/fontconfig/90-tamzen-disable-anti-aliasing.conf new file mode 100644 index 0000000..5bf94d7 --- /dev/null +++ b/archetypes/profiles/desktop/resources/fontconfig/90-tamzen-disable-anti-aliasing.conf @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> + +<fontconfig> + <description>Disable anti-aliasing for Tamzen since it is a bitmap font</description> + <match target="pattern"> + <test name="family" compare="eq" qual="any"> + <string>Tamzen</string> + </test> + <edit name="antialias" mode="assign"> + <bool>false</bool> + </edit> + </match> +</fontconfig> diff --git a/archetypes/profiles/gnome/default.nix b/archetypes/profiles/gnome/default.nix new file mode 100644 index 0000000..6b7184b --- /dev/null +++ b/archetypes/profiles/gnome/default.nix @@ -0,0 +1,13 @@ +{ lib, pkgs, ... }: let + mkGnome = lib.mkOverride 700; + + nixosConfig = { + services.displayManager.gdm.enable = mkGnome true; + services.desktopManager.gnome.enable = mkGnome true; + services.tlp.enable = mkGnome false; # Conflicts with power-profiles-daemon + }; + + homeConfig = {}; +in { + imports = [ (lib._mkProfileArchetype "gnome" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/headless/default.nix b/archetypes/profiles/headless/default.nix new file mode 100644 index 0000000..ac1eb80 --- /dev/null +++ b/archetypes/profiles/headless/default.nix @@ -0,0 +1,37 @@ +{ lib, ... }: let + mkHeadless = lib.mkOverride 910; + + nixosConfig = { + _archetypes.collections = { + development = { + utilities.enable = mkHeadless true; + }; + }; + + programs = { + _ddcutil.enable = mkHeadless true; + }; + + services = { + openssh.enable = mkHeadless true; + smartd.enable = mkHeadless true; + }; + + # Disable ipv6 by default + networking.enableIPv6 = mkHeadless false; + }; + + homeConfig = { + # Disable fontconfig features explicitly to avoid warnings + fonts.fontconfig = { + subpixelRendering = mkHeadless "none"; + hinting = mkHeadless "none"; + }; + + programs = { + htop._configure = mkHeadless true; + }; + }; +in { + imports = [ (lib._mkProfileArchetype "headless" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/pi/default.nix b/archetypes/profiles/pi/default.nix new file mode 100644 index 0000000..639111a --- /dev/null +++ b/archetypes/profiles/pi/default.nix @@ -0,0 +1,30 @@ +{ lib, pkgs, ... }: let + mkPi = lib.mkOverride 900; + + nixosConfig = { + boot.loader = { + # Use the extlinux boot loader. (NixOS wants to enable GRUB by default) + grub.enable = false; + # Enables the generation of /boot/extlinux/extlinux.conf + generic-extlinux-compatible.enable = true; + }; + }; + + homeConfig = { + # Use raspian logo in fastfetch + programs.fastfetch.settings = { + logo.source = mkPi "raspbian"; + # Default options for the rest of this + # TODO figure out how to just override logo and keep defaults w/config file + modules = [ + "title" "separator" "os" "host" "kernel" "uptime" "packages" + "shell" "display" "de" "wm" "wmtheme" "theme" "icons" "font" + "cursor" "terminal" "terminalfont" "cpu" "gpu" "memory" "swap" + "disk" "localip" "battery" "poweradapter" "locale" "break" + "colors" + ]; + }; + }; +in { + imports = [ (lib._mkProfileArchetype "pi" nixosConfig homeConfig) ]; +} diff --git a/archetypes/profiles/zfs/default.nix b/archetypes/profiles/zfs/default.nix new file mode 100644 index 0000000..00f3c0b --- /dev/null +++ b/archetypes/profiles/zfs/default.nix @@ -0,0 +1,33 @@ +{ lib, pkgs, ... }: let + mkZfs = lib.mkOverride 810; + + # https://wiki.nixos.org/wiki/ZFS + nixosConfig = { + boot._loader = { + grub.zfsSupport = mkZfs true; + mode = mkZfs "efi"; + }; + + boot.supportedFilesystems = [ "zfs" ]; + + services.zfs = { + trim.enable = mkZfs true; + autoSnapshot.enable = mkZfs true; + autoScrub.enable = mkZfs true; + }; + + # Make docker work with zfs + virtualisation.docker.storageDriver = mkZfs "zfs"; + + # Ensure root pools are safely imported (default after 26.11) + # NOTE: If NixOS fails to boot because it cannot import the root pool, + # you should boot with the zfs_force=1 option as a kernel parameter + # (e.g. by manually editing the kernel params via your bootloader). You + # should only need to do this after unclean shutdowns. + boot.zfs.forceImportRoot = mkZfs false; + }; + + homeConfig = {}; +in { + imports = [ (lib._mkProfileArchetype "zfs" nixosConfig homeConfig) ]; +} diff --git a/derivations/lowbat/default.nix b/derivations/lowbat/default.nix deleted file mode 100644 index 02ff111..0000000 --- a/derivations/lowbat/default.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ pkgs ? import <nixpkgs> {} }: - -pkgs.stdenv.mkDerivation rec { - pname = "lowbat"; - version = "1.2.1"; - - src = pkgs.fetchzip { - url = "https://git.tjkeller.xyz/${pname}/snapshot/${pname}-${version}.tar.xz"; - sha256 = "sha256-xsUHdmicHX/fQQgojVjWfzI82e4NezkmlACOpdPxLE0="; - }; - - nativeBuildInputs = [ pkgs.pkg-config ]; - buildInputs = [ pkgs.libnotify pkgs.glib ]; - - buildPhase = '' - make - ''; - - installPhase = '' - mkdir -p $out/bin $out/share/man/man1 - cp lowbat $out/bin/ - cp lowbat.1 $out/share/man/man1/ - ''; - - meta = with pkgs.lib; { - description = "A minimal battery level monitor daemon, written in C"; - license = licenses.mit; - maintainers = [ maintainers.tjkeller ]; - }; -} diff --git a/derivations/pavolctld/default.nix b/derivations/pavolctld/default.nix deleted file mode 100644 index c78e3fe..0000000 --- a/derivations/pavolctld/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ pkgs ? import <nixpkgs> {} }: - -pkgs.stdenv.mkDerivation rec { - pname = "pavolctld"; - version = "1.0.0"; - - src = pkgs.fetchzip { - url = "https://git.tjkeller.xyz/${pname}/snapshot/${pname}-${version}.tar.xz"; - sha256 = "sha256-eJ5njP2wu2kxsNgDdULOaATJJarLh+LkM5M9+kujRt4="; - }; - - buildInputs = [ pkgs.libpulseaudio ]; - - buildPhase = '' - make - ''; - - installPhase = '' - mkdir -p $out/bin $out/share/man/man1 - cp pavolctld $out/bin/ - cp pavolctld.1 $out/share/man/man1/ - ''; - - meta = with pkgs.lib; { - description = "A minimal PulseAudio volume control/monitoring daemon"; - license = licenses.mit; - maintainers = [ maintainers.tjkeller ]; - }; -} diff --git a/derivations/st/default.nix b/derivations/st/default.nix deleted file mode 100644 index eb7c6bc..0000000 --- a/derivations/st/default.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ - lib, - stdenv, - fetchzip, - fontconfig, - libX11, - libXcursor, - libXext, - libXft, - ncurses, - pkg-config, -}: - -stdenv.mkDerivation rec { - pname = "st"; - version = "tj-0.9.2.2"; - - src = fetchzip { - url = "https://git.tjkeller.xyz/${pname}/snapshot/${pname}-${version}.tar.xz"; - sha256 = "sha256-p1u7TEeLeC5Kjvi8na4YhyQQ0qdPaUWZL6hq9O4HaKA="; - }; - - nativeBuildInputs = [ - pkg-config - ]; - buildInputs = [ - fontconfig - libX11 - libXcursor - libXext - libXft - ncurses - ]; - - installPhase = '' - runHook preInstall - - TERMINFO=$out/share/terminfo make install PREFIX=$out - - runHook postInstall - ''; - - meta = with lib; { - homepage = "https://git.tjkeller.xyz/st"; - description = "My st with various patches"; - mainProgram = "st"; - license = licenses.mit; - maintainers = [ maintainers.tjkeller ]; - }; -} @@ -3,18 +3,17 @@ "arkenfox": { "inputs": { "flake-compat": "flake-compat", - "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" ], "pre-commit": "pre-commit" }, "locked": { - "lastModified": 1725263787, - "narHash": "sha256-OSNjus8VSkLCSikN6Qeq+II1bwqTRJEwl6NJvFoQHoE=", + "lastModified": 1770200864, + "narHash": "sha256-COdM5Lwfw07Q+YefYmcop1VPEX7e2goM9IiwfIrJt0g=", "owner": "dwarfmaster", "repo": "arkenfox-nixos", - "rev": "72addd96455cce49c0c8524c53aecd02cf20adec", + "rev": "94638bbfacfa250e5c88aaa7a8f2145926fb8e08", "type": "github" }, "original": { @@ -26,11 +25,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "type": "github" }, "original": { @@ -39,40 +38,6 @@ "type": "github" } }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "gitignore": { "inputs": { "nixpkgs": [ @@ -102,110 +67,105 @@ ] }, "locked": { - "lastModified": 1733951536, - "narHash": "sha256-Zb5ZCa7Xj+0gy5XVXINTSr71fCfAv+IKtmIXNrykT54=", + "lastModified": 1780361225, + "narHash": "sha256-wnV9ttf4fPWNonBIQmvlrSlNpQYgx5HgWWd007mwIFA=", "owner": "nix-community", "repo": "home-manager", - "rev": "1318c3f3b068cdcea922fa7c1a0a1f0c96c22f5f", + "rev": "e28654b71096e08c019d4861ca26acb646f583d8", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-24.11", + "ref": "release-26.05", "repo": "home-manager", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1719082008, - "narHash": "sha256-jHJSUH619zBQ6WdC21fFAlDxHErKVDJ5fpN0Hgx4sjs=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "9693852a2070b398ee123a329e68f0dab5526681", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1720386169, - "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable_2": { - "locked": { - "lastModified": 1729357638, - "narHash": "sha256-66RHecx+zohbZwJVEPF7uuwHeqf8rykZTMCTqIrOew4=", - "owner": "NixOS", + "lastModified": 1780203844, + "narHash": "sha256-K5sT4jTpGs15ADhviMKNBH38REpPf5Q6mM1+N6cArVE=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "bb8c2cf7ea0dd2e18a52746b2c3a5b0c73b93c22", + "rev": "b51242d7d43689db2f3be91bd05d5b24fbb469c4", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "release-24.05", + "owner": "nixos", + "ref": "nixos-26.05", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs_2": { + "nixpkgs-unstable": { "locked": { - "lastModified": 1734083684, - "narHash": "sha256-5fNndbndxSx5d+C/D0p/VF32xDiJCJzyOqorOYW4JEo=", + "lastModified": 1780243769, + "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "314e12ba369ccdb9b352a4db26ff419f7c49fa84", + "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-24.11", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "pre-commit": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": [ + "arkenfox", + "flake-compat" + ], "gitignore": "gitignore", - "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable" + "nixpkgs": [ + "arkenfox", + "nixpkgs" + ] }, "locked": { - "lastModified": 1724857454, - "narHash": "sha256-Qyl9Q4QMTLZnnBb/8OuQ9LSkzWjBU1T5l5zIzTxkkhk=", + "lastModified": 1769939035, + "narHash": "sha256-Fok2AmefgVA0+eprw2NDwqKkPGEI5wvR+twiZagBvrg=", "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "4509ca64f1084e73bc7a721b20c669a8d4c5ebe6", + "repo": "git-hooks.nix", + "rev": "a8ca480175326551d6c4121498316261cbb5b260", "type": "github" }, "original": { "owner": "cachix", - "repo": "pre-commit-hooks.nix", + "repo": "git-hooks.nix", "type": "github" } }, + "reposync": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1767033825, + "narHash": "sha256-z+V1Tc7+d73+vMh/M+kt2x3ctRzEqZvHxyIB1z/+2rs=", + "ref": "refs/heads/master", + "rev": "6fcb76423a96bca42520cf80b945a193d2a4c377", + "revCount": 6, + "type": "git", + "url": "git://git.tjkeller.xyz/hm-reposync" + }, + "original": { + "type": "git", + "url": "git://git.tjkeller.xyz/hm-reposync" + } + }, "root": { "inputs": { "arkenfox": "arkenfox", "home-manager": "home-manager", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", + "nixpkgs-unstable": "nixpkgs-unstable", + "reposync": "reposync", "sops-nix": "sops-nix" } }, @@ -213,15 +173,14 @@ "inputs": { "nixpkgs": [ "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable_2" + ] }, "locked": { - "lastModified": 1729394972, - "narHash": "sha256-fADlzOzcSaGsrO+THUZ8SgckMMc7bMQftztKFCLVcFI=", + "lastModified": 1777944972, + "narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=", "owner": "Mic92", "repo": "sops-nix", - "rev": "c504fd7ac946d7a1b17944d73b261ca0a0b226a5", + "rev": "c591bf665727040c6cc5cb409079acb22dcce33c", "type": "github" }, "original": { @@ -229,21 +188,6 @@ "repo": "sops-nix", "type": "github" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", @@ -2,69 +2,112 @@ rec { description = "TimmyOS System Config"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05"; + nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; - home-manager = { - url = "github:nix-community/home-manager/release-24.11"; + url = "github:nix-community/home-manager/release-26.05"; inputs.nixpkgs.follows = "nixpkgs"; }; - arkenfox = { url = "github:dwarfmaster/arkenfox-nixos"; inputs.nixpkgs.follows = "nixpkgs"; }; + reposync = { + url = "git://git.tjkeller.xyz/hm-reposync"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { nixpkgs, home-manager, arkenfox, ... }@inputs : - let - system = "x86_64-linux"; - pkgs = nixpkgs.legacyPackages.${system}; - userDetails = { - username = "timmy"; - home.root = "/home/timmy"; - home.downloads = "/home/timmy/dls"; - home.documents = "/home/timmy/docs"; - home.pictures = "/home/timmy/pics"; - email = "tjkeller.xyz"; - fullname = "Tim Keller"; - }; - mkNixosConfiguration = hostname: nixpkgs.lib.nixosSystem { - inherit system; - specialArgs = { - inherit inputs; - inherit userDetails; - }; - modules = [ - ./modules/hosts/${hostname}/configuration.nix - ./modules/root - home-manager.nixosModules.home-manager { - home-manager = { - backupFileExtension = "backup"; - useGlobalPkgs = true; - useUserPackages = true; - users.${userDetails.username} = import ./modules/home; - extraSpecialArgs = { - inherit userDetails; - }; - sharedModules = [ - inputs.arkenfox.hmModules.arkenfox - ./modules/hosts/${hostname}/home.nix - ]; - }; - } - ]; + outputs = { nixpkgs, home-manager, ... }@inputs: let + extLib = nixpkgs.lib.extend (final: prev: import ./lib); + extLibHomeManagerStandalone = nixpkgs.lib.extend (final: prev: import ./lib/home-manager); + homeManagerModules = [ + inputs.arkenfox.hmModules.arkenfox + inputs.reposync.hmModules.reposync + ./home-manager + ]; + mkNixosConfiguration = system: hostname: nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { + inherit inputs; + inherit hostname; + lib = extLib; }; - in { - nixosConfigurations = { - T430 = mkNixosConfiguration "T430"; - T495 = mkNixosConfiguration "T495"; - hp-envy-office = mkNixosConfiguration "hp-envy-office"; - optiplex = mkNixosConfiguration "optiplex"; + modules = [ + ./hosts/${hostname}/configuration.nix + ./hosts/${hostname}/hardware-configuration.nix + ./archetypes + ./nixos + ./pkgs + ./users + inputs.sops-nix.nixosModules.sops + home-manager.nixosModules.home-manager { + home-manager = { + backupFileExtension = "backup"; # In case file is overwritten + useGlobalPkgs = true; + useUserPackages = true; + sharedModules = homeManagerModules; + }; + } + ]; + }; + mkNixosConfigurations = hostsBySystem: builtins.listToAttrs ( + builtins.concatMap (system: + map (host: { + name = host; + value = mkNixosConfiguration system host; + }) hostsBySystem.${system} + ) (builtins.attrNames hostsBySystem) + ); + mkHomeConfiguration = user: system: hostname: home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.${system}; + extraSpecialArgs = { + inherit inputs; + inherit hostname; + lib = extLibHomeManagerStandalone; }; + modules = homeManagerModules ++ [ + ./hosts/${hostname}/home-${user}/configuration.nix + ./archetypes/profiles + ./pkgs + ./users/default-home.nix + ]; + }; + mkHomeConfigurations = hostsBySystem: builtins.listToAttrs ( + builtins.concatMap (system: + map (host: { + name = host; + value = mkHomeConfiguration "timmy" system host; # TODO + }) hostsBySystem.${system} + ) (builtins.attrNames hostsBySystem) + ); + in { + # Configured system hostnames go here under their respective system architecture + nixosConfigurations = mkNixosConfigurations { + x86_64-linux = [ + "T495" + "X230" + "flex-wg-router" + "gnuslashprinter" + "hp-envy-office" + "libreX60" + "optiplex" + "poweredge" + "sweetiepc" + ]; + aarch64-linux = [ + "piframe" + ]; }; + homeConfigurations = mkHomeConfigurations { + x86_64-linux = [ + "libreX60" + ]; + }; + }; } diff --git a/home-manager/default.nix b/home-manager/default.nix new file mode 100644 index 0000000..8d9c722 --- /dev/null +++ b/home-manager/default.nix @@ -0,0 +1,19 @@ +{ + imports = [ + ./firefox.nix + ./fontconfig.nix + ./gammastep.nix + ./home-cleanup.nix + ./htop.nix + ./mint-theme.nix + ./neovim.nix + ./pcmanfm.nix + ./qt-gtk-theme.nix + ./theme-st.nix + ./wallpapers.nix + ./xdg-mime.nix + ]; + + # Let Home Manager install and manage itself. + programs.home-manager.enable = true; +} diff --git a/home-manager/firefox.nix b/home-manager/firefox.nix new file mode 100644 index 0000000..7c95e56 --- /dev/null +++ b/home-manager/firefox.nix @@ -0,0 +1,113 @@ +{ config, lib, pkgs, ... }: let + cfg = config.programs.firefox; + search = { + engines = { + "Timmy Search" = { + urls = [{ + template = "https://search.tjkeller.xyz/search"; + params = [ { name = "q"; value = "{searchTerms}"; } ]; + }]; + iconURI = "https://search.tjkeller.xyz/static/themes/simple/img/favicon.svg"; # TODO doesn't seem to work + }; + "Nix Packages" = { + urls = [{ + template = "https://search.nixos.org/packages"; + params = [ { name = "query"; value = "{searchTerms}"; } ]; + }]; + icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"; + definedAliases = [ "@np" ]; + }; + "NixOS Options" = { + urls = [{ + template = "https://search.nixos.org/options"; + params = [ { name = "query"; value = "{searchTerms}"; } ]; + }]; + icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"; + definedAliases = [ "@no" ]; + }; + }; + default = "Timmy Search"; + privateDefault = "Timmy Search"; + force = true; # Overwrite old + }; + userChrome = builtins.readFile ./resources/firefox/userChrome.css; + uiCustomization = { + placements = { + nav-bar = [ + "back-button" + "forward-button" + "stop-reload-button" + "vertical-spacer" + "home-button" + "urlbar-container" + "downloads-button" + "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" # Bitwarden + "unified-extensions-button" + ]; + TabsToolbar = [ "tabbrowser-tabs" "new-tab-button" ]; + # Ensure these extensions go to overflow menu instead of nav-bar + unified-extensions-area = [ + "ublock0_raymondhill_net-browser-action" + "sponsorblocker_ajay_app-browser-action" + ]; + }; + currentVersion = 23; # Required + }; + arkenfox = { + enable = true; + enableAllSections = true; + "0100"."0102"."browser.startup.page".value = 3; # 0=blank, 1=home, 2=last visited page, 3=resume previous session + "0100"."0103"."browser.startup.homepage".enable = false; + "0100"."0104"."browser.newtabpage.enabled".enable = false; + #"2800"."2811"."privacy.clearOnShutdown.history".enable = false; + }; + settings = { + "browser.compactmode.show" = true; + "browser.uiCustomization.state" = builtins.toJSON uiCustomization; # Toolbar etc. + "browser.uidensity" = 1; # Compact + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; # userchrome + + "app.normandy.first_run" = false; + "browser.aboutConfig.showWarning" = false; # arkenfox does + #"browser.download.dir" = userDetails.userDirs.downloads; + "browser.newtabpage.activity-stream.feeds.section.topstories" = false; + "browser.newtabpage.activity-stream.feeds.topsites" = false; + "browser.urlbar.suggest.topsites" = false; + "devtools.toolbox.host" = "window"; + "dom.push.enabled" = false; # + "extensions.pocket.enabled" = false; + "general.smoothScroll" = false; + #"geo.provider.use_geoclue" = true; + }; + workSettings = settings // { + "extensions.activeThemeID" = "firefox-compact-dark@mozilla.org"; # Use builtin dark theme instead of system theme + }; +in { + options.programs.firefox = { + _configure = lib.mkEnableOption "configure firefox profiles"; + }; + + config = lib.mkIf cfg._configure { + programs.firefox = { + enable = lib.mkDefault true; + arkenfox.enable = lib.mkDefault true; + profiles = { + Personal = { + id = 0; + isDefault = true; + inherit search; + inherit userChrome; + inherit arkenfox; + inherit settings; + }; + Work = { + id = 1; + inherit search; + inherit userChrome; + inherit arkenfox; + settings = workSettings; + }; + }; + }; + }; +} diff --git a/home-manager/fontconfig.nix b/home-manager/fontconfig.nix new file mode 100644 index 0000000..7c12b7e --- /dev/null +++ b/home-manager/fontconfig.nix @@ -0,0 +1,11 @@ +{ lib, config, ... }: { + config = { + # Warn whenever subpixelRendering is not enabled + warnings = [ + (lib.mkIf (config.fonts.fontconfig.subpixelRendering == null) + ''fonts.fontconfig.subpixelRendering is unset; this may result in suboptimal font rendering. Set this value to "none" to disable the feature explicitly.'') + (lib.mkIf (config.fonts.fontconfig.hinting == null) + ''fonts.fontconfig.hinting is unset; this may result in suboptimal font clarity. Set this value to "none" to disable the feature explicitly.'') + ]; + }; +} diff --git a/home-manager/gammastep.nix b/home-manager/gammastep.nix new file mode 100644 index 0000000..f7ea3b3 --- /dev/null +++ b/home-manager/gammastep.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: let + cfg = config.services._gammastep; +in { + options.services._gammastep = { + enable = lib.mkEnableOption "configure gammastep"; + }; + + config = lib.mkIf cfg.enable { + services.gammastep = { + enable = true; + provider = lib.mkDefault "geoclue2"; + temperature = { + day = lib.mkDefault 6500; # gammastep default, the hm module defaults to 5500 + night = lib.mkDefault 3600; + }; + }; + }; +} diff --git a/home-manager/home-cleanup.nix b/home-manager/home-cleanup.nix new file mode 100644 index 0000000..1f7c426 --- /dev/null +++ b/home-manager/home-cleanup.nix @@ -0,0 +1,21 @@ +{ config, lib, ... }: let + cacheHome = config.xdg.cacheHome; + configHome = config.xdg.configHome; + dataHome = config.xdg.dataHome; +in { + # Gtk 2 + # This file may be generated by home-manager, so this will need to be set + # so that it ends up in the correct location + gtk.gtk2.configLocation = "${configHome}/gtk-2.0/gtkrc"; + + # Npm + xdg.configFile.npmrc = { + enable = lib.mkDefault true; + text = builtins.replaceStrings ["\t"] [""] '' + prefix=${dataHome}/npm + cache=${cacheHome}/npm + init-module=${dataHome}/npm/npm-init.js + ''; + target = "npm/npmrc"; + }; +} diff --git a/home-manager/htop.nix b/home-manager/htop.nix new file mode 100644 index 0000000..36ccbac --- /dev/null +++ b/home-manager/htop.nix @@ -0,0 +1,46 @@ +{ config, lib, ... }: let + cfg = config.programs.htop; +in { + options.programs.htop = { + _configure = lib.mkEnableOption "apply sensible defaults to htop"; + }; + + config = lib.mkIf cfg._configure { + programs.htop.enable = lib.mkDefault true; + programs.htop.settings = { + fields = with config.lib.htop.fields; [ + PID + USER + 124 # EXE + PERCENT_CPU + PERCENT_MEM + STARTTIME + ELAPSED # time elapsed since process start + IO_READ_RATE + RBYTES # num read bytes + IO_WRITE_RATE + WBYTES # num written bytes + TIME + STIME # sys time + CSTIME # child sys time + NICE + STATE + PRIORITY + 125 # CWD + ]; + } // (with config.lib.htop; leftMeters [ + (bar "AllCPUs2") + (bar "CPU") + (bar "Memory") + (bar "DiskIO") + (bar "NetworkIO") + ]) // (with config.lib.htop; rightMeters [ + (text "Hostname") + (text "Clock") + (text "Uptime") + (text "Battery") + (text "LoadAverage") + (text "Tasks") + ]); + }; +} diff --git a/home-manager/mint-theme.nix b/home-manager/mint-theme.nix new file mode 100644 index 0000000..a77be6a --- /dev/null +++ b/home-manager/mint-theme.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.gtk._mintTheme; # gtk.theme type is null or submodule under the hood so gtk.theme._mint, etc. can't be used + transformColorValue = color: dark: + "Mint-Y" + lib.optionalString dark "-Dark" + + lib.optionalString (color != "Green") ("-" + color) + ; +in { + options.gtk._mintTheme = { + enable = lib.mkEnableOption "enables mint theme"; + color = lib.mkOption { + type = lib.types.enum [ + "Aqua" "Blue" "Green" "Grey" "Orange" + "Pink" "Purple" "Red" "Sand" "Teal" + ]; + default = "Green"; + description = "mint-y theme color"; + }; + dark = lib.mkEnableOption "whether to use dark variant of mint theme"; + icons.color = lib.mkOption { + type = lib.types.enum [ + "Aqua" "Blue" "Green" "Grey" "Orange" "Pink" "Purple" "Red" + "Sand" "Teal" + ]; + default = "Aqua"; + description = "mint-y icons color eg. 'Aqua' or 'Red'"; + }; + }; + + config = lib.mkIf cfg.enable { + gtk = { + enable = true; + theme = { + package = pkgs.mint-themes; + name = transformColorValue cfg.color cfg.dark; + }; + iconTheme = { + package = pkgs.mint-y-icons; + name = transformColorValue cfg.icons.color false; + }; + }; + }; +} diff --git a/home-manager/neovim.nix b/home-manager/neovim.nix new file mode 100644 index 0000000..2b19257 --- /dev/null +++ b/home-manager/neovim.nix @@ -0,0 +1,108 @@ +{ lib, config, pkgs, ... }: let + cfg = config.programs._neovim; + + pluginDir = let + extraPlugins = cfg.plugins.packages // { + start = (cfg.plugins.packages.start or []) + ++ lib.optionals (cfg.plugins.lsp.enable && cfg.plugins.lsp.lspconfig.enable) [ cfg.plugins.lsp.lspconfig.pluginPackage ] + ++ lib.optionals cfg.plugins.treesitter.enable [ cfg.plugins.treesitter.pluginPackage ]; + }; + in pkgs.linkFarm "nvim-site" + (lib.concatLists (lib.mapAttrsToList (stage: plugins: + map (plugin: { + name = "share/nvim/site/pack/plugins/${stage}/${plugin.name}"; + path = plugin; + }) plugins + ) extraPlugins)); + + extraPackages = pkgs.symlinkJoin { + name = "nvim-extra-packages"; + paths = cfg.extraPackages + ++ lib.optionals cfg.plugins.lsp.enable cfg.plugins.lsp.languageServers.packages; + }; + + parserDir = let + allTSParserPackages = builtins.filter lib.isDerivation + (builtins.attrValues cfg.plugins.treesitter.parsers.installAllFromPackageSet); + in pkgs.symlinkJoin { + name = "nvim-treesitter-parsers"; + paths = if cfg.plugins.treesitter.parsers.installAll then allTSParserPackages else cfg.plugins.treesitter.parsers.packages; + }; + + finalPackage = pkgs.symlinkJoin { + name = "neovim-not-wrapped"; + paths = [ cfg.package ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/nvim \ + --prefix PATH : ${extraPackages} \ + --prefix XDG_DATA_DIRS : ${pluginDir}/share \ + '' + lib.optionalString cfg.plugins.treesitter.enable '' + --add-flags '--cmd "set rtp^=${parserDir}"' \ + --add-flags '--cmd "set rtp^=${cfg.plugins.treesitter.pluginPackage}/runtime"' \ + '' + '' + # blank line here to terminate multiline command string + '' + lib.optionalString cfg.viAlias '' + ln -s $out/bin/nvim $out/bin/vi + '' + lib.optionalString cfg.vimAlias '' + ln -s $out/bin/nvim $out/bin/vim + ''; + }; +in { + options.programs._neovim = { + enable = lib.mkEnableOption "install and configure neovim"; + package = lib.mkPackageOption pkgs "neovim" {}; + viAlias = lib.mkEnableOption "alias vi => nvim"; + vimAlias = lib.mkEnableOption "alias vim => nvim"; + plugins = { + lsp = { + enable = lib.mkEnableOption "install defined language servers into nvim path"; + lspconfig = { + enable = lib.mkEnableOption "install plugin `lspconfig.pluginPackage`"; + pluginPackage = lib.mkPackageOption pkgs.vimPlugins "nvim-lspconfig" {}; + }; + languageServers.packages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "language server packages to add to nvim path when lsp is enabled"; + }; + }; + treesitter = { + enable = lib.mkEnableOption "install treesitter and parsers as nvim plugins"; + pluginPackage = lib.mkPackageOption pkgs.vimPlugins "nvim-treesitter" {}; + parsers.installAll = lib.mkEnableOption "install all available parsers. overrides `parsers.packages`"; + parsers.installAllFromPackageSet = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = pkgs.vimPlugins.nvim-treesitter-parsers; + defaultText = lib.literalExpression "pkgs.vimPlugins.nvim-treesitter-parsers"; + description = "parent package from which to install all treesitter parsers"; + }; + parsers.packages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "treesitter parsers to install as plugins"; + }; + }; + packages = lib.mkOption { + type = lib.types.attrsOf (lib.types.listOf lib.types.package); + default = [ ]; + example = { + start = with pkgs.vimPlugins; [ + nvim-colorizer-lua + ]; + }; + description = "extra plugins to be made available to nvim via packpath, organized by stage"; + }; + }; + extraPackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + description = "extra packages to add to nvim path"; + }; + }; + + config = lib.mkIf cfg.enable { + # NOTE: Don't use programs.neovim since that will build neovim from source + home.packages = [ finalPackage ]; + }; +} diff --git a/home-manager/pcmanfm.nix b/home-manager/pcmanfm.nix new file mode 100644 index 0000000..fc2737a --- /dev/null +++ b/home-manager/pcmanfm.nix @@ -0,0 +1,14 @@ +{ config, lib, ... }: let + cfg = config.programs._pcmanfm.openAsRootOption; +in { + options.programs._pcmanfm.openAsRootOption = { + enable = lib.mkEnableOption "enable 'open as root' option in pcmanfm right-click menu"; + }; + + config = lib.mkIf cfg.enable { + home.file.pcmanfm-root = { + target = ".local/share/file-manager/actions/root.desktop"; + source = ./resources/pcmanfm/root.desktop; + }; + }; +} diff --git a/home-manager/qt-gtk-theme.nix b/home-manager/qt-gtk-theme.nix new file mode 100644 index 0000000..2e53ccc --- /dev/null +++ b/home-manager/qt-gtk-theme.nix @@ -0,0 +1,15 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.qt._gtkPlatformTheme; +in { + options.qt._gtkPlatformTheme = { + enable = lib.mkEnableOption "use gtk theme in qt"; + }; + + config = lib.mkIf cfg.enable { + qt = { + enable = lib.mkDefault true; + platformTheme.name = "gtk3"; + }; + }; +} diff --git a/home-manager/resources/firefox/userChrome.css b/home-manager/resources/firefox/userChrome.css new file mode 100644 index 0000000..7ec4588 --- /dev/null +++ b/home-manager/resources/firefox/userChrome.css @@ -0,0 +1,175 @@ +/* TODO create separate repo for this */ +/* TOP BAR */ +#navigator-toolbox { + --uc-navigationbar-width: 40vw; + + #TabsToolbar { + /* Move new tab to far right */ + #tabbrowser-arrowscrollbox-periphery { margin-left: auto } + /* remove alltabs button */ + #alltabs-button { display: none } + } + + #nav-bar { + /* Remove overflow button */ + #nav-bar-overflow-button { display: none } + } + + #PersonalToolbar { + /* Change background color of bookmarks toolbar */ + background: var(--toolbox-bgcolor) !important; + /* Adjust spacing of toolbar items */ + #PlacesToolbarItems { gap: .35rem; margin-block: .2rem } + } +} + +/* Media queries for width of nav bar */ +@media screen and (max-width: 1000px) { + #navigator-toolbox { --uc-navigationbar-width: 50vw } +} +@media screen and (max-width: 800px) { + #navigator-toolbox { --uc-navigationbar-width: 60vw } +} + +/* Change look of tabs in smaller view */ +@media screen and (max-width: 800px) { + #TabsToolbar-customization-target { + /* TODO fix w/o. Don't allow tabbar to move up when moving tabs */ + height: 37px; + /* Make tabs fill more vertical space */ + .tab-background { margin-block: 1px !important } + } +} + + +/* COMBINE TOP BAR */ +/* Combine top bar into single line if width >= 800px */ +@media screen and (min-width: 800px) { + /* Half height bar */ + :root { --uc-toolbar-height: 36px } + + /* Modify these to change relative widths or default height */ + #navigator-toolbox { + margin-bottom: 0px; + padding-top: 1px; /* Top bar is cut off otherwise */ + + #TabsToolbar { + margin-left: calc(var(--uc-navigationbar-width) + 2px); /* Resize tab bar */ + margin-top: -4px; + margin-bottom: 2px; + + #TabsToolbar-customization-target { + height: var(--uc-toolbar-height); + + /* Tabs extend to bottom of bar */ + .tab-background { + margin-bottom: 0 !important; + margin-top: 3px !important; /* See above navigator-toolbox rule. Avoid cutting off top */ + } + + /* 1px margin on touch density causes tabs to be too high */ + .tab-close-button { margin-top: 0 !important } + } + + /* Center tab buttons */ + #tabs-newtab-button, #alltabs-button { margin-top: 4px !important } + } + + /* Integrate url / nav bar */ + #nav-bar { + margin-right:calc(100vw - var(--uc-navigationbar-width)); + margin-top: calc(0px - var(--uc-toolbar-height)); + border-radius: 0 var(--tab-border-radius) var(--tab-border-radius) 0; + border-right: 1px solid ThreeDShadow !important; + box-shadow: 0 0 4px rgba(0,0,0,.4) !important; + /* Fix spacing around tabs by removing nav bar top border */ + border: none !important; + + #urlbar-container { + /* Remove min and max width of urlbar */ + width: 0 !important; + /* Make opened urlbar overlay the toolbar */ + #urlbar[open]:focus-within { min-width: 50vw !important } + } + } + + /* Bottom of tags is cut off by 1px without this */ + #PersonalToolbar { margin-top: 1px } + } + + /* Fix customization view */ + #customization-panelWrapper .panel-arrowbox .panel-arrow { + margin-inline-end: initial !important; + } + + /* Shorten findbar */ + findbar { + width: 600px !important; + border-radius: 0 0 0 5px; + border-bottom: none !important; + box-shadow: 0 2px 4px rgba(0,0,0,.4); /* Move down 2px so it doesn't go over the tab bar */ + } +} + + +/* FINDBAR */ +findbar { + width: 100vw; + position: absolute; + top: 0; + right: 0; + padding: 0 !important; + padding-top: 1px !important; + background: -moz-headerbar Field !important; + border-top-width: 0px !important; + border-bottom: 1px solid ThreeDShadow; + + .findbar-container { + padding-bottom: 5px !important; /* Move search bar closer to left edge */ + padding-top: 2px !important; /* Move search bar closer to left edge */ + height: max-content !important; + gap: 2px; + justify-content: space-between; + flex-wrap: wrap; + + /* Force textbox to fill up first line */ + .findbar-textbox { + width: 100% !important; + background: Field !important; /* Set the background color to be consistent with found-matches label when unfocused */ + } + + description { + /* Hide description showing wrap conditions etc. */ + display: none; + /* Show description when it says "Phrase Not Found" */ + &[status=notfound] { display: inline-block } + } + + /* Move found matches label (roughly) into the textbox */ + label.found-matches, + description { + position: absolute; + top: 6.5px; + right: 110px; + color: color-mix(in srgb, -moz-headerbartext, transparent 46%) !important; + /* So this text overrides the text below, TODO find a more elegant solution */ + padding-inline: 1ex; + background: Field; + } + + /* Force checkboxes onto second line */ + hbox { width: 100% } + } +} + + +/* BOOKMARK BAR */ +@-moz-document url(chrome://browser/content/browser.xhtml) { + #PersonalToolbar { + background: -moz-headerbar Field !important; + border-top: 1px solid ThreeDShadow !important; + z-index: 1000; /* To not be affected by shadow from navbar */ + /* Space out bookmark items */ + .bookmark-item .toolbarbutton-text { padding: 2.5px 4px } + } +} diff --git a/modules/home/resources/pcmanfm/root.desktop b/home-manager/resources/pcmanfm/root.desktop index 4b4d263..f5863e9 100644 --- a/modules/home/resources/pcmanfm/root.desktop +++ b/home-manager/resources/pcmanfm/root.desktop @@ -7,5 +7,5 @@ Icon=gtk-dialog-authentication [X-Action-Profile profile-zero] MimeTypes=inode/directory; -Exec=/home/timmy/.local/bin/x11/xsudo /usr/bin/pcmanfm %u +Exec=pkexec env DISPLAY="$DISPLAY" XAUTHORITY="$XAUTHORITY" XDG_DATA_DIRS="$XDG_DATA_DIRS" XDG_CONFIG_HOME="$XDG_CONFIG_HOME" pcmanfm %u Name=Default profile diff --git a/home-manager/resources/xdg-mime/audio b/home-manager/resources/xdg-mime/audio new file mode 100644 index 0000000..fd00b26 --- /dev/null +++ b/home-manager/resources/xdg-mime/audio @@ -0,0 +1,65 @@ +application/x-cue +application/x-extension-m4a +application/x-ogm-audio +application/x-shorten +audio/3gpp +audio/3gpp2 +audio/AMR +audio/aac +audio/ac3 +audio/aiff +audio/amr-wb +audio/dv +audio/eac3 +audio/flac +audio/m3u +audio/m4a +audio/mp1 +audio/mp2 +audio/mp3 +audio/mp4 +audio/mpeg +audio/mpeg2 +audio/mpeg3 +audio/mpegurl +audio/mpg +audio/musepack +audio/ogg +audio/opus +audio/rn-mpeg +audio/scpls +audio/vnd.dolby.heaac.1 +audio/vnd.dolby.heaac.2 +audio/vnd.dts +audio/vnd.dts.hd +audio/vnd.rn-realaudio +audio/vorbis +audio/wav +audio/webm +audio/x-aac +audio/x-adpcm +audio/x-aiff +audio/x-ape +audio/x-m4a +audio/x-matroska +audio/x-mp1 +audio/x-mp2 +audio/x-mp3 +audio/x-mpegurl +audio/x-mpg +audio/x-ms-asf +audio/x-ms-wma +audio/x-musepack +audio/x-pls +audio/x-pn-au +audio/x-pn-realaudio +audio/x-pn-wav +audio/x-pn-windows-pcm +audio/x-realaudio +audio/x-scpls +audio/x-shorten +audio/x-tta +audio/x-vorbis +audio/x-vorbis+ogg +audio/x-wav +audio/x-wavpack diff --git a/home-manager/resources/xdg-mime/directory b/home-manager/resources/xdg-mime/directory new file mode 100644 index 0000000..4284766 --- /dev/null +++ b/home-manager/resources/xdg-mime/directory @@ -0,0 +1,2 @@ +inode/directory +inode/mount-point diff --git a/home-manager/resources/xdg-mime/email b/home-manager/resources/xdg-mime/email new file mode 100644 index 0000000..db65256 --- /dev/null +++ b/home-manager/resources/xdg-mime/email @@ -0,0 +1,6 @@ +application/mbox +application/vnd.ms-outlook +message/rfc822 +text/calendar +x-scheme-handler/mailto +x-scheme-handler/mid diff --git a/home-manager/resources/xdg-mime/image b/home-manager/resources/xdg-mime/image new file mode 100644 index 0000000..1e2623b --- /dev/null +++ b/home-manager/resources/xdg-mime/image @@ -0,0 +1,21 @@ +application/postscript +image/avif +image/bmp +image/gif +image/heic +image/heif +image/jp2 +image/jpeg +image/jpg +image/jxl +image/png +image/svg+xml +image/svg+xml-compressed +image/tiff +image/webp +image/x-bmp +image/x-portable-anymap +image/x-portable-bitmap +image/x-portable-graymap +image/x-tga +image/x-xpixmap diff --git a/home-manager/resources/xdg-mime/pdf b/home-manager/resources/xdg-mime/pdf new file mode 100644 index 0000000..73bd5d0 --- /dev/null +++ b/home-manager/resources/xdg-mime/pdf @@ -0,0 +1,4 @@ +application/epub+zip +application/oxps +application/pdf +application/x-fictionbook diff --git a/home-manager/resources/xdg-mime/text b/home-manager/resources/xdg-mime/text new file mode 100644 index 0000000..709cb57 --- /dev/null +++ b/home-manager/resources/xdg-mime/text @@ -0,0 +1,15 @@ +application/x-shellscript +text/english +text/plain +text/x-c +text/x-c++ +text/x-c++hdr +text/x-c++src +text/x-chdr +text/x-csrc +text/x-java +text/x-makefile +text/x-moc +text/x-pascal +text/x-tcl +text/x-tex diff --git a/home-manager/resources/xdg-mime/video b/home-manager/resources/xdg-mime/video new file mode 100644 index 0000000..6a0839e --- /dev/null +++ b/home-manager/resources/xdg-mime/video @@ -0,0 +1,55 @@ +application/mxf +application/ogg +application/sdp +application/smil +application/streamingmedia +application/vnd.apple.mpegurl +application/vnd.ms-asf +application/vnd.rn-realmedia +application/vnd.rn-realmedia-vbr +application/x-extension-mp4 +application/x-matroska +application/x-mpegurl +application/x-ogg +application/x-ogm +application/x-ogm-video +application/x-smil +application/x-streamingmedia +video/3gp +video/3gpp +video/3gpp2 +video/avi +video/divx +video/dv +video/fli +video/flv +video/mkv +video/mp2t +video/mp4 +video/mp4v-es +video/mpeg +video/msvideo +video/ogg +video/quicktime +video/vnd.divx +video/vnd.mpegurl +video/vnd.rn-realvideo +video/webm +video/x-avi +video/x-flc +video/x-flic +video/x-flv +video/x-m4v +video/x-matroska +video/x-mpeg2 +video/x-mpeg3 +video/x-ms-afs +video/x-ms-asf +video/x-ms-wmv +video/x-ms-wmx +video/x-ms-wvxvideo +video/x-msvideo +video/x-ogm +video/x-ogm+ogg +video/x-theora +video/x-theora+ogg diff --git a/home-manager/theme-st.nix b/home-manager/theme-st.nix new file mode 100644 index 0000000..0a2cf34 --- /dev/null +++ b/home-manager/theme-st.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: let + cfg = config.programs._st; + toString = x: if lib.isBool x then (if x then "true" else "false") else builtins.toString x; + # http://freedesktop.org/software/fontconfig/fontconfig-user.html + generateFontConfigString = name: attrs: + name + lib.optionalString (attrs != {}) ( + ":" + lib.concatStringsSep ":" ( + lib.mapAttrsToList (key: value: "${key}=${toString value}") attrs + ) + ) + ; + themed-st = pkgs.stdenv.mkDerivation { + name = "themed-st"; + dontUnpack = true; + buildInputs = [ pkgs.makeWrapper ]; + installPhase = '' + mkdir -p $out/bin + makeWrapper ${pkgs.st}/bin/st $out/bin/st \ + --add-flags '-f"${generateFontConfigString cfg.font.name cfg.font.attrs}"' + ''; + }; +in { + options.programs._st = { + enable = lib.mkEnableOption "enables theming st with home manager"; + font = { + name = lib.mkOption { + type = lib.types.str; + example = "JetBrainsMonoNL Nerd Font Mono"; + default = "monospace"; + }; + attrs = lib.mkOption { + type = lib.types.attrs; + default = {}; + example = { + size = 12; + antialias = true; + autohint = true; + }; + }; + }; + }; + + config = lib.mkIf cfg.enable { + home.packages = [ themed-st ]; + }; +} diff --git a/home-manager/wallpapers.nix b/home-manager/wallpapers.nix new file mode 100644 index 0000000..de64d76 --- /dev/null +++ b/home-manager/wallpapers.nix @@ -0,0 +1,17 @@ +{ lib, config, pkgs, ... }: let + cfg = config.programs._seasonalwallpaper.wallpapers; +in { + options.programs._seasonalwallpaper.wallpapers = { + download = lib.mkEnableOption "enables all wallpapers"; + }; + + config = lib.mkIf cfg.download { + home.file.wallpapers = { + target = ".local/share/wallpaper"; + source = pkgs.fetchzip { + url = "https://tjkeller.xyz/permalinks/wallpapers.tar"; + hash = "sha256-lhj9wUMZfd0XOrUtWY9HB64oXrS/EjsvsaJHzu4ucFg="; + }; + }; + }; +} diff --git a/home-manager/xdg-mime.nix b/home-manager/xdg-mime.nix new file mode 100644 index 0000000..e74b4b3 --- /dev/null +++ b/home-manager/xdg-mime.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: let + cfg = config.xdg.mimeApps._defaultCategoryApplications; + + # Mime-type category files are stored here + mimeResources = ./resources/xdg-mime; + + # Files present in mimeResources with newline separated mime-types + # Would be more fun to load these dynamically with builtins.readDir, but I will be disciplined + categories = [ "audio" "directory" "email" "image" "pdf" "text" "video" ]; + + # Dynamically generate categories + categoryApplications = (lib.genAttrs categories (category: lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "set the default application used for ${category} files. omit '.desktop'"; + })); + + # Create list of mime-types from the category file + getCategoryMimeTypes = category: lib.filter (s: s != "") (lib.splitString "\n" (builtins.readFile "${mimeResources}/${category}")); + + # Structure default applications for a specified category how home-manager expects + assignMimeTypes = applications: category: lib.map (mimetype: + { "${mimetype}" = lib.mkIf (applications != []) (lib.map (app: "${app}.desktop") applications); } + ) (getCategoryMimeTypes category); + + # Combine all category applications + defaultApplications = lib.mkMerge (lib.flatten ( + lib.map (category: assignMimeTypes cfg.categoryApplications.${category} category) categories + )); +in { + options.xdg.mimeApps._defaultCategoryApplications = { + enable = lib.mkEnableOption "set default applications as defined in the resources dir"; + inherit categoryApplications; + }; + + config = lib.mkIf cfg.enable { + xdg.mimeApps = { + enable = lib.mkDefault true; + inherit defaultApplications; + }; + }; +} diff --git a/hosts/T495/configuration.nix b/hosts/T495/configuration.nix new file mode 100644 index 0000000..b80f27e --- /dev/null +++ b/hosts/T495/configuration.nix @@ -0,0 +1,88 @@ +{ config, lib, pkgs, ... }: { + imports = [ ./wg.nix ]; + + # Setup bootloader + boot._loader.enable = true; + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + collections = { + desktop = { + extraUtilities.enable = true; + cad.enable = true; + chromium.enable = true; + crypto.enable = true; + graphics.enable = true; + office.enable = true; + }; + development = { + android.enable = true; + c.enable = true; + docker.enable = true; + lua.enable = true; + web = { + hugo = { + enable = true; + openFirewall = true; + }; + node.enable = true; + }; + }; + bluetooth.enable = true; + }; + }; + + # Enable user timmy + _users.timmy = { + enable = true; + autologin.enable = true; + nas = { + enable = true; + office.enable = true; + }; + wifi.enable = true; + }; + + # Install spotify + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + "spotify" + ]; + environment.systemPackages = with pkgs; [ + spotify + ]; + + # Use amdgpu driver for x11 + services.xserver.videoDrivers = [ "amdgpu" ]; + + # Configure home + home-manager.users.timmy = { + gtk._mintTheme = { + dark = true; + color = "Purple"; + icons.color = "Purple"; + }; + programs._st = { + enable = true; + font = { + name = "TamzenForPowerline"; + attrs = { + pixelsize = 20; + }; + }; + }; + programs._seasonalwallpaper.wallpapers.download = true; + fonts.fontconfig = { + subpixelRendering = "rgb"; + hinting = "slight"; + }; + }; + + system.stateVersion = "24.05"; +} diff --git a/modules/hosts/T495/hardware-configuration.nix b/hosts/T495/hardware-configuration.nix index 6258a83..6258a83 100644 --- a/modules/hosts/T495/hardware-configuration.nix +++ b/hosts/T495/hardware-configuration.nix diff --git a/hosts/T495/resources/secrets/wg.yaml b/hosts/T495/resources/secrets/wg.yaml new file mode 100644 index 0000000..7581228 --- /dev/null +++ b/hosts/T495/resources/secrets/wg.yaml @@ -0,0 +1,17 @@ +wg0: ENC[AES256_GCM,data:ZWvG10UxVMb7e8wxTgQqybp2NsziJdTRDqF/ZJ1N3bf220P3l59jQcHyHi7l9UFbUhErM/zxXc0/B7AE1TPzcFGZRxqQC3TenCCW8RuKkm9bXcctseXtLqRr/EYRH07952dTzvdN/eBxLXLPqgi/K1qnuWBLS2zf/WKAJyCFsKPlzhH6lA7+VAM16Q9m+0nktruQ34DMok1LBlN7DN/gZkJWx6Ipzst4f9lrDiSm1QNmxskGUqDdPWednIJ8d8iW2j5xFBgzE0iUyZu0VwuOqP2ioUTYLiLGbx2/XrRXyz66u5gx1agBtpeAirWmNcmJlDl8olik64tXQoemHUHXQokpsuR1ENnm2DFqB1lVbXA3Q00cyzYX08cii7olGHKbVaty2n4RMAFwTBh2fRYs4LsH+cyn4umhLkOhUEbFInujrA5vVltQv9h/r4Y22osBgVNuSQ4I5hl4d348yc45yCENnW6iSs8Diu141l+IbxvQPIpbR462ctYtM+TRJW4AFbgPUdHrRD7DrURnyP9ZePSIU/1pPFZNeNJjYwDETXaz,iv:WLgAIxHhKLgw8TGfuXTlh7rkcjIawYjb3J6mo3sRSmE=,tag:b7tZzypGa8EmUinCJcDkoQ==,type:str] +wg1: ENC[AES256_GCM,data:MCojgM00HJuTgY+K6KBUnDqH4Y8Jerc37vnYO4SC/srlNXxp5QKAuyTDwb7bjmGboyOHq//GLbRMDN5vZEeUQNGyjsycElmSl76bcLS2XPdGrbxGZD7HQY9PY+Z0V1avwVovRrITfG2irffnVxqfis8R5GBOovy/MRhyjG8B6vhastYoGp1ncAmAV+QsUGn/MTeKr+MpZ1Q+SGyeu8gAvPknOEzrR8WqK0ySbWKCQjeEe38kLNwhSNI1cp4Md02dskNUWnVKRyvfXOuYoPsGAmhVikZ+C4yFlndEJoQ5IJPbTO0mv2394bKtflCyB2hnerrTd+NaZcRgPP72Lvw3gDmx7olweL7f2uzr5Vs4NO1AIWcw/MkJxW2UU4GzHbJZM7H3b/IUY1+y8UDEk4rtBT9t+M5LapuVbpagwulIP7+O7Bjsgb4ZJxaGDjtUe3JcUgMDRAexDa7lioDZMpWT5UPRvUczfncqXs+b40vo02eZqiAxtWVK/8587uo=,iv:lsCA/USQc6ADWdRVYIQHXQtdtukEf7cXNTXIKzlynDU=,tag:xlW4MCIwMC8A8f2maHhPQg==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NEN4NGxDR1oreGVoSGhE + TzMxSEY0QVBhS2Z6MW15ci9aVlJ0a3IyVlZBCldPRVNvcUhJSHhWSEk3akd4RjN0 + ajhUV2d1ZWRsRFU4cTE2dGl6RmM4MGsKLS0tIFhnUjl5aDJqWVB1NE15SlNzR2Iv + YTNydURsOUMrSXZGdk9UOUdUQlA0SFUKxEDJRR6tpYva9qpWo9NxwCxk/xpRVoTl + YJkmDZzMcXikXXiro96AprP9dXJXvMPKYPGl2Zsal8PlGFPBoHW2GA== + -----END AGE ENCRYPTED FILE----- + recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + lastmodified: "2026-06-19T19:30:00Z" + mac: ENC[AES256_GCM,data:tqZqi1l/15EAq/aqiXHmmi1aPUrdKJiK7pQZa5bUOdThfB3CVIQ4s0lfvybhMJfU6gbuiGih/Umr0bJ9joaFozFn/mPPUj7sjuqzHk1bxMG3qp1kHmmegAVBtjv0Fht62N1MFtH8woG3k+iRmd0ndTdFCMa0OVDvsCzw3t11wbw=,iv:/mQWM769XROsm6MEHpEsX8TiBEQd6PWODkb9CkH0eZY=,tag:oMObl25mPwPcqluWwUefcQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/hosts/T495/wg.nix b/hosts/T495/wg.nix new file mode 100644 index 0000000..7b98ad8 --- /dev/null +++ b/hosts/T495/wg.nix @@ -0,0 +1,9 @@ +{ config, pkgs, inputs, ... }: { + sops.secrets.wg0 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg0"; }; # Office + sops.secrets.wg1 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg1"; }; # Home + + networking.wg-quick.interfaces = { + wg0.configFile = config.sops.secrets.wg0.path; + wg1.configFile = config.sops.secrets.wg1.path; + }; +} diff --git a/hosts/X230/configuration.nix b/hosts/X230/configuration.nix new file mode 100644 index 0000000..22856f2 --- /dev/null +++ b/hosts/X230/configuration.nix @@ -0,0 +1,65 @@ +{ config, lib, pkgs, ... }: { + imports = [ ./wg.nix ]; + + # Setup bootloader + boot._loader.enable = true; + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + collections = { + desktop = { + crypto.enable = true; + graphics.enable = true; + office.enable = true; + }; + }; + }; + + # Enable user timmy + _users.timmy = { + enable = true; + autologin.enable = true; + wifi.enable = true; + }; + + # Use intel driver for better performance and vsync + services.xserver.videoDrivers = [ "intel" ]; + + # Configure home + home-manager.users.timmy = { + gtk._mintTheme = { + dark = true; + color = "Teal"; + icons.color = "Teal"; + }; + fonts.fontconfig.defaultFonts.monospace = [ "TamzenForPowerline" ]; + gtk = { + font.name = "monospace"; + font.size = 8; + cursorTheme.size = 24; + }; + programs._st = { + enable = true; + font = { + name = "TamzenForPowerline"; + attrs = { + pixelsize = 14; + }; + }; + }; + programs._seasonalwallpaper.wallpapers.download = true; + fonts.fontconfig = { + subpixelRendering = "rgb"; + hinting = "full"; + }; + }; + + system.stateVersion = "24.05"; +} diff --git a/modules/hosts/T430/hardware-configuration.nix b/hosts/X230/hardware-configuration.nix index 206a525..0791585 100644 --- a/modules/hosts/T430/hardware-configuration.nix +++ b/hosts/X230/hardware-configuration.nix @@ -8,14 +8,29 @@ [ (modulesPath + "/installer/scan/not-detected.nix") ]; - boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "sd_mod" "sr_mod" "sdhci_pci" ]; + boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "sd_mod" "sdhci_pci" ]; boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; fileSystems."/" = - { device = "/dev/disk/by-uuid/b991914b-3a4c-4248-9472-b5403729601a"; + { device = "/dev/disk/by-uuid/41036740-73bc-4004-a302-01233b4d83b8"; fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + boot.initrd.luks.devices."enc".device = "/dev/disk/by-uuid/6019772f-4a1c-4abd-9c70-b1d71cc2de65"; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/41036740-73bc-4004-a302-01233b4d83b8"; + fsType = "btrfs"; + options = [ "subvol=@home" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/72D4-F66A"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; }; swapDevices = [ ]; diff --git a/hosts/X230/resources/secrets/wg0.yaml b/hosts/X230/resources/secrets/wg0.yaml new file mode 100644 index 0000000..5941b70 --- /dev/null +++ b/hosts/X230/resources/secrets/wg0.yaml @@ -0,0 +1,16 @@ +wg0: ENC[AES256_GCM,data:wcgowNptTdrJNjzH6n/ulbec5+GPkuRAUNidWFy4dhClioTg8vdrXhhwP+sykwEZYOjsLurkU0Rw1w9ds+AGe3J+FnW1qKdskcY+8t/CyNY51pUbzMCKxexnNj52+0+VlH6FAUyplo6ESg/vlWCFyuyACWjQfdqDW/1PxJzrYqZ7MIwbCdntjE/84F52BqxePt3LolzvzTGUOx5Lr6Jbv9i3tv1R9NmZxt5t2gwaGbIIPWMVZh972w5HJYa8bfx67vuyj6HE46tMiu8WdQbHfjRvVIA+0OtHihpDGHfi5Q6iXpO/rk4YJZjsiEgTMTqD08HD5Gm+wBFwHSJjCOBpBsq3GsspDLNI+EXel7Gmtk+BhL1tFQdpYPwz7bHd03Znawr4Br7R0gHJg4FXnhKlG+SyDqXKirnCyCTfUotIBmU0dX4tzmwiOMyvImgXZA==,iv:GAk27qkZDopzdWnBeL7yTmyn9dM2wSzKd41NRhsyNNY=,tag:Ba7jZhqEa8dUedIvVFBbHA==,type:str] +sops: + age: + - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NEN4NGxDR1oreGVoSGhE + TzMxSEY0QVBhS2Z6MW15ci9aVlJ0a3IyVlZBCldPRVNvcUhJSHhWSEk3akd4RjN0 + ajhUV2d1ZWRsRFU4cTE2dGl6RmM4MGsKLS0tIFhnUjl5aDJqWVB1NE15SlNzR2Iv + YTNydURsOUMrSXZGdk9UOUdUQlA0SFUKxEDJRR6tpYva9qpWo9NxwCxk/xpRVoTl + YJkmDZzMcXikXXiro96AprP9dXJXvMPKYPGl2Zsal8PlGFPBoHW2GA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-09T12:36:15Z" + mac: ENC[AES256_GCM,data:T8hQbFrPOGYQl8tbqUMLdQN3qjhcAXmKfwqEsLQkDjr2nxUXDz8d58TUsaRvkHC4jPo9lXyPL5SSpua2kzJIcDDLPkiPB/3qV8ksJQ0dgcfLkUnrI1mtoo9AOPnHrTjstSAR2cmiFbCTqRpkLnGwI+NoCLjNjd/GNSAlMl00QMg=,iv:boSHzNAuKGkAhtkApOOJEtW4gt13mMmCin24gf3dXIs=,tag:1+4+BE8ZPgvgf4RlH6Wmhw==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/modules/hosts/T495/wg.nix b/hosts/X230/wg.nix index 3881b63..3881b63 100644 --- a/modules/hosts/T495/wg.nix +++ b/hosts/X230/wg.nix diff --git a/hosts/flex-wg-router/configuration.nix b/hosts/flex-wg-router/configuration.nix new file mode 100644 index 0000000..a0777f0 --- /dev/null +++ b/hosts/flex-wg-router/configuration.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, ... }: let + ipAddress = "10.1.1.1"; +in { + imports = [ ./wg.nix ]; + + # Setup bootloader + boot._loader.enable = true; + + # Enable common options + _archetypes = { + profiles = { + headless = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + }; + + # Network config + networking = { + # Label lan and wan interfaces + _interfaceLabels = { + enable = true; + interfaces = { + lan0 = "98:b7:85:22:9b:43"; # External + wan0 = "54:ee:75:8c:4b:2d"; # Internal + }; + }; + # Set ip addresses + interfaces = { + lan0.ipv4.addresses = [{ + address = ipAddress; + prefixLength = 24; + }]; + wan0.ipv4.addresses = [{ + address = "46.110.173.163"; # Reserved static ip for wg-router + prefixLength = 31; + }]; + }; + defaultGateway = { + address = "46.110.173.161"; + interface = "wan0"; + }; + # Firewall rules + firewall = { + interfaces.wan0 = { + allowedUDPPorts = [ 51820 ]; + }; + }; + }; + + # Router config + services._router = { + routing = { + enable = true; + interfaces = { + lan = [ "lan0" ]; + wan = "wan0"; + }; + }; + dnsDhcpConfig = { + localDomain = "wg-router.pls.lan"; + dhcp = { + defaultGateway = ipAddress; + localhostIp = ipAddress; + rangeStart = "10.1.1.100"; + rangeEnd = "10.1.1.250"; + staticLeases = { + idrac-8HT2W52 = { + macAddress = "18:fb:7b:9d:16:b3"; + staticIp = "10.1.1.10"; + }; + }; + }; + }; + }; + + # Enable user timmy + _users.timmy.enable = true; + + system.stateVersion = "25.05"; +} diff --git a/modules/hosts/optiplex/hardware-configuration.nix b/hosts/flex-wg-router/hardware-configuration.nix index 3d9651e..01cff6d 100644 --- a/modules/hosts/optiplex/hardware-configuration.nix +++ b/hosts/flex-wg-router/hardware-configuration.nix @@ -8,29 +8,29 @@ [ (modulesPath + "/installer/scan/not-detected.nix") ]; - boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "firewire_ohci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ]; + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; boot.initrd.kernelModules = [ ]; boot.kernelModules = [ "kvm-intel" ]; boot.extraModulePackages = [ ]; fileSystems."/" = - { device = "/dev/disk/by-uuid/a9d54117-31f6-4cad-b1b2-250f6bfd7808"; + { device = "/dev/disk/by-uuid/01eae5fd-a46e-4a36-8a9d-247a0b16bcef"; fsType = "btrfs"; options = [ "subvol=@" ]; }; - fileSystems."/home" = - { device = "/dev/disk/by-uuid/a9d54117-31f6-4cad-b1b2-250f6bfd7808"; - fsType = "btrfs"; - options = [ "subvol=@home" ]; - }; - fileSystems."/boot" = - { device = "/dev/disk/by-uuid/6987-81B6"; + { device = "/dev/disk/by-uuid/345A-436A"; fsType = "vfat"; options = [ "fmask=0022" "dmask=0022" ]; }; + fileSystems."/home" = + { device = "/dev/disk/by-uuid/01eae5fd-a46e-4a36-8a9d-247a0b16bcef"; + fsType = "btrfs"; + options = [ "subvol=@home" ]; + }; + swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking @@ -38,7 +38,7 @@ # 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.enp3s0.useDHCP = lib.mkDefault true; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; diff --git a/hosts/flex-wg-router/resources/secrets/wg.yaml b/hosts/flex-wg-router/resources/secrets/wg.yaml new file mode 100644 index 0000000..1f6867b --- /dev/null +++ b/hosts/flex-wg-router/resources/secrets/wg.yaml @@ -0,0 +1,17 @@ +wg1: ENC[AES256_GCM,data:r7jNBzEcItmlEtjhKCbyOBaNYfutKxC2UdUYSLHfYyLnwwdIwM1kfvd5K1/UZNAKoG7sHpBha59M1tvZAOIGAFnzG14YsVrMD8w6Qy4pc0FmdyNHDEM4EwaqHFRjbb5oBAFv6lI2VZ3AgXf6StXoVUYtbEA1QBVqVq4Syk6/CalnhkE2LuZpuVA5GZUZ8aTmFRp9zOnhcNoVJMrokTUswV4Mgn3zt2Tb+3bfoZJ9jbb6H8P/F0NGU+phy0EENZMIqOGBP5aPPIZfVQYphQcG6BYiddti3Copq57vqh/qOB70LPle6b/IsaT/K8Xqjp8PjNI/e5gkZdVwIGx/w3Gk0+CkD0tDEUMBdsFfvm7Dbz3xQxN66/0ZMGQgic0xtytr/DfKCIMIwsr33GKavP7OXEJ6lUF615Y4PQhNNx4ePlgcttt2b7TG5bM8nxKsaQ==,iv:mLYNgKXCp8w2JO90Rsn7gtifEn4Yc6JKnjws7uo1w10=,tag:c51B1fZe1HnJhFDc86HnOg==,type:str] +wg0: ENC[AES256_GCM,data:SJQ21aLwoQ0nEHfoHRd+ksL8pX7HoCRVjGIS/BZxq9JQhHJg9ZHHbwwUkz/3vrq1S+PD7e1bL0FHpgHPuZVHawpaFIeWd6TEPH+6oUxlRbDaEbcWR5POlNyMVV3z9TnOElgmqT0VUqfY80NEqFPbCLdjcWHjnwO4nzrEhPMA9WG2PFCAnZNUtVXh2mnblA61/xmxkSVysahBP+bTHA8a+v/AXy7WrHbnHizTeevdCMqWyDhzHvO8hfH4tU/xJ7GQrG/bxk4JZ6XT8a2CAqmNEKyWicB/zSc5NdILNQL7Kx2mzg/fDp4nltf7iBRZfLuN+r7whrKJ2lJQPATeyjMlIgHUcnohjihiOsGYiBcB3/Y4hIHVt7rRBMoFBB2OgNKC3gx6saZreRxLHZcRZFcVm39G9vaw6EI=,iv:qO8vMlstL/kOxFSlUd/dCtAK9ZzZt+LH/9vfulqHiMc=,tag:yuiwA8Hp8qDrF3UPlCMSUg==,type:str] +sops: + age: + - recipient: age1f0tmpy2nam58skmznjyqd3zf54rxtfrk6fda0vlpq9y3yg6wac7sjf0vja + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNVZ5dmNSd1NRQUhURkl2 + dnk3MkNjMFUyNnU5L1FFNTV0a1NUbUZ6ZUEwCmNNUldIdnoycVpwUHJrcXZvZXp5 + NGVHcUlHUm1uK0QxV1JmdDVyQVoxZDgKLS0tIDJhSHhkYjNML045SHNobytucnVZ + L25wUWRJbzZMZDFseXdvOFJXQVRxN28KJjC3ola24tTEV8tFYpnsId4d0S+jHkS9 + ME6i4jorWRlQKdYn/gTUoqgMAvJEc73hjTfgX6bFshhuhflfGxXQQw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-31T19:40:45Z" + mac: ENC[AES256_GCM,data:XON+JNOWr6WRYYI0+vCC4qiDST8iY/XQZlkB16l5vMsirS3j5iAIi60hn5viFqMn+IIV7GretbVnBVP32R4C59II8bIySzrsdJj5AuvTvdBvglhkelhiDnchqE98KCG9zr41bJsSaQ/8ubRy7b5jiu9aqzQFg9UQZousecIu/MU=,iv:IJNCc05iu0sZxa3RFh5l1TMcwl3YKRtVXn4wfdOy6M8=,tag:OO5uC8nAjqsWoxC1N801GA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/hosts/flex-wg-router/wg.nix b/hosts/flex-wg-router/wg.nix new file mode 100644 index 0000000..b454b81 --- /dev/null +++ b/hosts/flex-wg-router/wg.nix @@ -0,0 +1,9 @@ +{ config, pkgs, inputs, ... }: { + sops.secrets.wg0.sopsFile = ./resources/secrets/wg.yaml; + sops.secrets.wg1.sopsFile = ./resources/secrets/wg.yaml; + + networking.wg-quick.interfaces = { + wg0.configFile = config.sops.secrets.wg0.path; + wg1.configFile = config.sops.secrets.wg1.path; + }; +} diff --git a/hosts/gnuslashprinter/configuration.nix b/hosts/gnuslashprinter/configuration.nix new file mode 100644 index 0000000..9e1f734 --- /dev/null +++ b/hosts/gnuslashprinter/configuration.nix @@ -0,0 +1,29 @@ +{ + imports = [ + ./flask-relay-ctl.nix + ./klipper.nix + ]; + + boot._loader.enable = true; + + _archetypes = { + profiles = { + headless = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + }; + + # Enable user timmy + _users.timmy.enable = true; + + # Name devices + services.udev.extraRules = '' + SUBSYSTEM=="tty", KERNELS=="3-9", SYMLINK+="gsp-power" + SUBSYSTEM=="tty", KERNELS=="3-10", SYMLINK+="gsp-control" + ''; + + system.stateVersion = "25.11"; +} diff --git a/hosts/gnuslashprinter/flask-relay-ctl.nix b/hosts/gnuslashprinter/flask-relay-ctl.nix new file mode 100644 index 0000000..27507b6 --- /dev/null +++ b/hosts/gnuslashprinter/flask-relay-ctl.nix @@ -0,0 +1,37 @@ +{ pkgs, ... }: let + flaskrelayctl = pkgs.python3Packages.buildPythonApplication { + pname = "flaskrelayctl"; + version = "1.0"; + src = ./resources/flaskrelayctl; + pyproject = true; + build-system = with pkgs.python3Packages; [ setuptools ]; + dependencies = with pkgs.python3Packages; [ + flask + pyserial + ]; + }; +in { + users.users.relay-api = { + isSystemUser = true; + group = "relay-api"; + extraGroups = [ "dialout" ]; + }; + users.groups.relay-api = {}; + + systemd.services.relay-api = let + RELAYCTL_DEV = "/dev/gsp-power"; + in { + description = "USB Relay Flask API"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + User = "relay-api"; + Group = "relay-api"; + ExecStart = "${flaskrelayctl}/bin/flaskrelayctl"; + Restart = "always"; + DeviceAllow = [ "${RELAYCTL_DEV} rw" ]; + }; + }; + + environment.systemPackages = [ flaskrelayctl ]; +} diff --git a/hosts/gnuslashprinter/hardware-configuration.nix b/hosts/gnuslashprinter/hardware-configuration.nix new file mode 100644 index 0000000..278d6e6 --- /dev/null +++ b/hosts/gnuslashprinter/hardware-configuration.nix @@ -0,0 +1,44 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a"; + fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a"; + fsType = "btrfs"; + options = [ "subvol=@home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/416a481f-d656-4bd1-acdb-bd0a6f2d2c3a"; + fsType = "btrfs"; + options = [ "subvol=@nix" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/18C4-6D53"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/gnuslashprinter/klipper.nix b/hosts/gnuslashprinter/klipper.nix new file mode 100644 index 0000000..331a0c7 --- /dev/null +++ b/hosts/gnuslashprinter/klipper.nix @@ -0,0 +1,100 @@ +{ + # Klipper firmware + services.klipper = { + enable = true; + firmwares = { + mcu = { + enable = true; + # Serial port connected to the microcontroller + serial = "/dev/gsp-control"; + # Klipper flash must be enabled in order to build mcu firmware + # The resulting `klipper-flash-mcu` command will show the location of the firmware bin in the nix store + enableKlipperFlash = true; + # Run klipper-genconf to generate this + configFile = ./resources/klipper/mcu/config; + }; + }; + configFile = ./resources/klipper/printer.cfg; + logFile = "/var/lib/klipper/klipper.log"; + }; + # Mutable config + services.klipper.mutableConfig = true; + #configDir = "/var/lib/moonraker/config"; # Accessible by moonraker # TODO + + # Moonraker web-api + security.polkit.enable = true; # required for services.moonraker.allowSystemControl + services.moonraker = { + user = "root"; + enable = true; + address = "0.0.0.0"; + allowSystemControl = true; + settings = { + authorization = { + force_logins = true; + cors_domains = [ + "*.local" + "*.lan" + "*://app.fluidd.xyz" + "*://my.mainsail.xyz" + ]; + trusted_clients = [ + "10.0.0.0/8" + "127.0.0.0/8" + "169.254.0.0/16" + "172.16.0.0/12" + "192.168.0.0/16" + "FE80::/10" + "::1/128" + ]; + }; + #file_manager.check_klipper_config_path = false; # Disable warning when klipper config is not accessible by moonraker + # mainsail.cfg + #"update_manager mainsail-config" = { + # type = "git_repo"; + # primary_branch = "master"; + # path = "~/mainsail-config"; + # origin = "https://github.com/mainsail-crew/mainsail-config.git"; + # managed_services = "klipper"; + #}; + + "power printer" = let + relayApiHost = "http://localhost:5050"; + in { + type = "http"; + on_url = "${relayApiHost}/on"; + off_url = "${relayApiHost}/off"; + status_url = "${relayApiHost}/status"; + response_template = '' + + # The module will perform the "GET" request using the appropriate url. + # We use the `last_response` method to fetch the result and decode the + # json response. + {% set resp = http_request.last_response().json() %} + # The expression below will render "on" or "off". + {resp["state"].lower()} + ''; + off_when_shutdown = true; + on_when_job_queued = true; + locked_while_printing = true; + restart_klipper_when_powered = true; + restart_delay = "1"; + bound_services = "klipper"; + initial_state = "off"; + }; + }; + }; + + # Mainsail web-interface + services.mainsail = { + enable = true; + hostName = "0.0.0.0"; + nginx.listenAddresses = [ "0.0.0.0" ]; + }; + services.nginx.clientMaxBodySize = "1000m"; # Allow large gcodes, etc. + networking.firewall.allowedTCPPorts = [ 80 ]; # Port for mainsail via nginx + + # Webcam support in mainsail + # TODO hook to restart ustreamer when webcam is connected + services.ustreamer.enable = true; + services.mainsail.nginx.locations."/webcam/".proxyPass = "http://localhost:8080/stream"; # Default location for ustreamer stream +} diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml new file mode 100644 index 0000000..b151b74 --- /dev/null +++ b/hosts/gnuslashprinter/resources/flaskrelayctl/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "flaskrelayctl" +version = "1.0" + +[project.scripts] +flaskrelayctl = "server:main" diff --git a/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py new file mode 100644 index 0000000..f2c0bc3 --- /dev/null +++ b/hosts/gnuslashprinter/resources/flaskrelayctl/server/__init__.py @@ -0,0 +1,54 @@ +from flask import Flask, jsonify +import serial +import os + +app = Flask(__name__) + +RELAY_DEV = os.environ.get("RELAYCTL_DEV", "/dev/ttyUSB0") +RELAY_HOST = os.environ.get("RELAYCTL_HOST", "127.0.0.1") +RELAY_PORT = os.environ.get("RELAYCTL_PORT", "5050") +RELAY_BAUD = 9600 +RELAY_ID = 1 # NOTE: multi relay boards are not supported (only relay id 1) + +def get_cmd(state: bool): + """ + LCUS relay 4-byte control command syntax is as follows: + start: always 0xA0 + relay: id of relay (counts from 1) + state: coil energized 0 or 1 + checksum: sum of previous 3 bytes + """ + start = 0xA0 + relay = RELAY_ID + state = int(state) + checksum = start + relay + state + return bytes((start, relay, state, checksum)) + +def get_serial(): + return serial.Serial(RELAY_DEV, RELAY_BAUD, timeout=1) + +@app.route("/on", methods=["GET", "POST"]) +def on(): + with get_serial() as ser: + ser.write(get_cmd(True)) + return jsonify({"state": "ON"}) + +@app.route("/off", methods=["GET", "POST"]) +def off(): + with get_serial() as ser: + ser.write(get_cmd(False)) + return jsonify({"state": "OFF"}) + +@app.route("/status", methods=["GET"]) +def status(): + with get_serial() as ser: + ser.write(bytes((0xFF,))) # write 0xFF to get status + r = ser.readline() + state = r.decode("utf-8").removeprefix("CH1: ").strip() + return jsonify({"state": state}) + +def main(): + app.run(host=RELAY_HOST, port=int(RELAY_PORT)) + +if __name__ == "__main__": + main() diff --git a/hosts/gnuslashprinter/resources/klipper/mcu/config b/hosts/gnuslashprinter/resources/klipper/mcu/config new file mode 100644 index 0000000..3e192a3 --- /dev/null +++ b/hosts/gnuslashprinter/resources/klipper/mcu/config @@ -0,0 +1,109 @@ +# CONFIG_LOW_LEVEL_OPTIONS is not set +# CONFIG_MACH_AVR is not set +# CONFIG_MACH_ATSAM is not set +# CONFIG_MACH_ATSAMD is not set +# CONFIG_MACH_LPC176X is not set +CONFIG_MACH_STM32=y +# CONFIG_MACH_HC32F460 is not set +# CONFIG_MACH_RPXXXX is not set +# CONFIG_MACH_PRU is not set +# CONFIG_MACH_AR100 is not set +# CONFIG_MACH_LINUX is not set +# CONFIG_MACH_SIMU is not set +CONFIG_BOARD_DIRECTORY="stm32" +CONFIG_MCU="stm32f103xe" +CONFIG_CLOCK_FREQ=72000000 +CONFIG_SERIAL=y +CONFIG_FLASH_SIZE=0x10000 +CONFIG_FLASH_BOOT_ADDRESS=0x8000000 +CONFIG_RAM_START=0x20000000 +CONFIG_RAM_SIZE=0x5000 +CONFIG_STACK_SIZE=512 +CONFIG_FLASH_APPLICATION_ADDRESS=0x8007000 +CONFIG_STM32_SELECT=y +CONFIG_MACH_STM32F103=y +# CONFIG_MACH_STM32F207 is not set +# CONFIG_MACH_STM32F401 is not set +# CONFIG_MACH_STM32F405 is not set +# CONFIG_MACH_STM32F407 is not set +# CONFIG_MACH_STM32F429 is not set +# CONFIG_MACH_STM32F446 is not set +# CONFIG_MACH_STM32F765 is not set +# CONFIG_MACH_STM32F031 is not set +# CONFIG_MACH_STM32F042 is not set +# CONFIG_MACH_STM32F070 is not set +# CONFIG_MACH_STM32F072 is not set +# CONFIG_MACH_STM32G070 is not set +# CONFIG_MACH_STM32G071 is not set +# CONFIG_MACH_STM32G0B0 is not set +# CONFIG_MACH_STM32G0B1 is not set +# CONFIG_MACH_STM32G431 is not set +# CONFIG_MACH_STM32G474 is not set +# CONFIG_MACH_STM32H723 is not set +# CONFIG_MACH_STM32H743 is not set +# CONFIG_MACH_STM32H750 is not set +# CONFIG_MACH_STM32L412 is not set +# CONFIG_MACH_N32G452 is not set +# CONFIG_MACH_N32G455 is not set +CONFIG_MACH_STM32F1=y +CONFIG_HAVE_STM32_USBFS=y +CONFIG_STM32_USB_DOUBLE_BUFFER_TX=y +CONFIG_HAVE_STM32_CANBUS=y +CONFIG_STM32_DFU_ROM_ADDRESS=0 +# CONFIG_STM32_FLASH_START_2000 is not set +# CONFIG_STM32_FLASH_START_5000 is not set +CONFIG_STM32_FLASH_START_7000=y +# CONFIG_STM32_FLASH_START_8000 is not set +# CONFIG_STM32_FLASH_START_8800 is not set +# CONFIG_STM32_FLASH_START_9000 is not set +# CONFIG_STM32_FLASH_START_10000 is not set +# CONFIG_STM32_FLASH_START_800 is not set +# CONFIG_STM32_FLASH_START_1000 is not set +# CONFIG_STM32_FLASH_START_4000 is not set +# CONFIG_STM32_FLASH_START_0000 is not set +CONFIG_CLOCK_REF_FREQ=8000000 +CONFIG_STM32F0_TRIM=16 +# CONFIG_STM32_USB_PA11_PA12 is not set +CONFIG_STM32_SERIAL_USART1=y +# CONFIG_STM32_CANBUS_PA11_PA12 is not set +# CONFIG_STM32_CANBUS_PA11_PB9 is not set +CONFIG_SERIAL_BAUD=250000 +CONFIG_USB_VENDOR_ID=0x1d50 +CONFIG_USB_DEVICE_ID=0x614e +CONFIG_USB_SERIAL_NUMBER="12345" +CONFIG_WANT_ADC=y +CONFIG_WANT_SPI=y +CONFIG_WANT_SOFTWARE_SPI=y +CONFIG_WANT_I2C=y +CONFIG_WANT_SOFTWARE_I2C=y +CONFIG_WANT_HARD_PWM=y +CONFIG_WANT_BUTTONS=y +CONFIG_WANT_TMCUART=y +CONFIG_WANT_NEOPIXEL=y +CONFIG_WANT_PULSE_COUNTER=y +CONFIG_WANT_ST7920=y +CONFIG_WANT_HD44780=y +CONFIG_WANT_ADXL345=y +CONFIG_WANT_LIS2DW=y +CONFIG_WANT_MPU9250=y +CONFIG_WANT_ICM20948=y +CONFIG_WANT_THERMOCOUPLE=y +CONFIG_WANT_HX71X=y +CONFIG_WANT_ADS1220=y +CONFIG_WANT_LDC1612=y +CONFIG_WANT_SENSOR_ANGLE=y +CONFIG_NEED_SENSOR_BULK=y +CONFIG_WANT_LOAD_CELL_PROBE=y +CONFIG_NEED_SOS_FILTER=y +CONFIG_CANBUS_FREQUENCY=1000000 +CONFIG_INLINE_STEPPER_HACK=y +CONFIG_HAVE_STEPPER_OPTIMIZED_BOTH_EDGE=y +CONFIG_WANT_STEPPER_OPTIMIZED_BOTH_EDGE=y +CONFIG_HAVE_GPIO=y +CONFIG_HAVE_GPIO_ADC=y +CONFIG_HAVE_GPIO_SPI=y +CONFIG_HAVE_GPIO_I2C=y +CONFIG_HAVE_GPIO_HARD_PWM=y +CONFIG_HAVE_STRICT_TIMING=y +CONFIG_HAVE_CHIPID=y +CONFIG_HAVE_BOOTLOADER_REQUEST=y diff --git a/hosts/gnuslashprinter/resources/klipper/printer.cfg b/hosts/gnuslashprinter/resources/klipper/printer.cfg new file mode 100644 index 0000000..4baba56 --- /dev/null +++ b/hosts/gnuslashprinter/resources/klipper/printer.cfg @@ -0,0 +1,180 @@ +# This file contains pin mappings for the stock 2020 Creality Ender 3 +# V2. To use this config, during "make menuconfig" select the +# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9) +# communication. + +# If you prefer a direct serial connection, in "make menuconfig" +# select "Enable extra low-level configuration options" and select +# serial (on USART3 PB11/PB10), which is broken out on the 10 pin IDC +# cable used for the LCD module as follows: +# 3: Tx, 4: Rx, 9: GND, 10: VCC + +# Flash this firmware by copying "out/klipper.bin" to a SD card and +# turning on the printer with the card inserted. The firmware +# filename must end in ".bin" and must not match the last filename +# that was flashed. + +# See docs/Config_Reference.md for a description of parameters. + +[include mainsail.cfg] + +# Default V2 config +[stepper_x] +step_pin: PC2 +dir_pin: PB9 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PA5 +position_endstop: -20 +position_min: -20 +position_max: 235 +homing_speed: 50 + +[stepper_y] +step_pin: PB8 +dir_pin: PB7 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PA6 +position_endstop: -8 +position_min: -8 +position_max: 235 +homing_speed: 50 + +[stepper_z] +step_pin: PB6 +dir_pin: !PB5 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 8 +endstop_pin: probe:z_virtual_endstop +#position_endstop: 0.0 +position_min: -5 +position_max: 250 + +[extruder] +max_extrude_only_distance: 100.0 +step_pin: PB4 +dir_pin: PB3 +enable_pin: !PC3 +microsteps: 16 +rotation_distance: 34.406 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PA1 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PC5 +control: pid +# tuned for stock hardware with 200 degree Celsius target +pid_Kp: 21.527 +pid_Ki: 1.063 +pid_Kd: 108.982 +min_temp: 0 +max_temp: 250 + +[heater_bed] +heater_pin: PA2 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PC4 +control: pid +# tuned for stock hardware with 50 degree Celsius target +pid_Kp: 54.027 +pid_Ki: 0.770 +pid_Kd: 948.182 +min_temp: 0 +max_temp: 130 + +[fan] +pin: PA0 + +[mcu] +serial: /dev/gsp-control +restart_method: command + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +###################################################################### +# 128x64 Full Graphic Creality CR10 / ENDER 3 stockdisplay +###################################################################### + +# This section is used for a Creality "12864" display with a single +# ribbon cable between the display's EXP3 plug and the +# micro-controller board's EXP1 connector. + +[display] +lcd_type: st7920 +cs_pin: EXP1_7 +sclk_pin: EXP1_6 +sid_pin: EXP1_8 +encoder_pins: ^EXP1_5, ^EXP1_3 +click_pin: ^!EXP1_2 + +[output_pin beeper] +pin: EXP1_1 + +[board_pins] +aliases: + EXP1_1=PC6,EXP1_3=PB10,EXP1_5=PB14,EXP1_7=PB12,EXP1_9=<GND>, + EXP1_2=PB2,EXP1_4=PB11,EXP1_6=PB13,EXP1_8=PB15,EXP1_10=<5V>, + PROBE_IN=PB1,PROBE_OUT=PB0,FIL_RUNOUT=PA6 + +[display_status] + +###### +# Bed leveling + +[bltouch] +sensor_pin: ^PROBE_IN +control_pin: PROBE_OUT +x_offset: -40 +y_offset: -12 +#z_offset: 1.1 +probe_with_touch_mode: true +stow_on_each_sample: false + +[bed_mesh] +speed: 120 +mesh_min: 10, 10 +mesh_max: 195, 220 +probe_count: 5,5 +algorithm: bicubic + +[safe_z_home] +home_xy_position: 157.5, 129.5 +speed: 75 +z_hop: 10 +z_hop_speed: 5 +move_to_previous: true + + +#*# <---------------------- SAVE_CONFIG ----------------------> +#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated. +#*# +#*# [bed_mesh default] +#*# version = 1 +#*# points = +#*# -0.017500, 0.025000, 0.050000, 0.122500, 0.155000 +#*# -0.045000, -0.040000, 0.037500, 0.102500, 0.145000 +#*# -0.007500, -0.032500, 0.005000, 0.092500, 0.132500 +#*# -0.060000, -0.045000, -0.007500, 0.087500, 0.127500 +#*# 0.075000, 0.042500, 0.015000, 0.105000, 0.175000 +#*# x_count = 5 +#*# y_count = 5 +#*# mesh_x_pps = 2 +#*# mesh_y_pps = 2 +#*# algo = bicubic +#*# tension = 0.2 +#*# min_x = 10.0 +#*# max_x = 195.0 +#*# min_y = 10.0 +#*# max_y = 220.0 +#*# +#*# [bltouch] +#*# z_offset = 1.342 diff --git a/hosts/hp-envy-office/configuration.nix b/hosts/hp-envy-office/configuration.nix new file mode 100644 index 0000000..5e3a463 --- /dev/null +++ b/hosts/hp-envy-office/configuration.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: { + imports = [ ./wg.nix ]; + + # Setup bootloader + boot._loader.enable = true; + boot.loader.timeout = 15; # Show for longer since it's usually skipped + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + collections = { + desktop = { + extraUtilities.enable = true; + chromium.enable = true; + graphics.enable = true; + office.enable = true; + }; + development = { + docker.enable = true; + web = { + node.enable = true; + }; + }; + virtualization.enable = true; + }; + }; + + # Enable user timmy + _users.timmy = { + enable = true; + nas = { + enable = true; + office.enable = true; + office.automount = true; + }; + }; + + # Disable suspend + systemd._suspend.disable = true; + + # Use amdgpu driver for x11 + services.xserver.videoDrivers = [ "amdgpu" ]; + + # Configure home + home-manager.users.timmy = { + gtk._mintTheme = { + dark = true; + color = "Blue"; + icons.color = "Blue"; + }; + programs._seasonalwallpaper.wallpapers.download = true; + fonts.fontconfig = { + subpixelRendering = "rgb"; + hinting = "none"; + }; + gtk.gtk3.bookmarks = [ + "file:///home/timmy/docs/src/sites/admin Admin" + "file:///media/chexx/chexx cHEXx" + ]; + }; + + system.stateVersion = "24.11"; +} diff --git a/modules/hosts/hp-envy-office/hardware-configuration.nix b/hosts/hp-envy-office/hardware-configuration.nix index 22a8c24..22a8c24 100644 --- a/modules/hosts/hp-envy-office/hardware-configuration.nix +++ b/hosts/hp-envy-office/hardware-configuration.nix diff --git a/hosts/hp-envy-office/resources/secrets/wg.yaml b/hosts/hp-envy-office/resources/secrets/wg.yaml new file mode 100644 index 0000000..f3b53aa --- /dev/null +++ b/hosts/hp-envy-office/resources/secrets/wg.yaml @@ -0,0 +1,16 @@ +wg1: ENC[AES256_GCM,data:XWdnE2QvfvFlMKUW6BoUSsEXDmYj4aNfbxvA6pFeIZM7NEtIwC4/NsplPwFIZwF372/bwDGXGocuh5gd1p/eAlsyz2DrAS+8g1+4T40EPPmXPgh++vUTvcpPlt74Qxp2yAeEU4CU7UPLvlxSvNjh5PGS68Cw7KxSB7kiWFxRWtm5oVfb+U6cBaQE6Biie7wPmXNWOobGHTfFYDeNmH6w33nH4lCV2MC0eYty9ytwHeVS7gUNrk4oxIfd+1FmNzwNHtVZvRg4wRzcc2M9fD0LuyuY6QVS/qaJG4hNNEHZ6qa0VMTnOzQ4jFHtd5jnz2vb7ckE7UWcFPjXYObcykk0End7sHVN/bD+fUv56JKZOHvVYFgs6OwCzUPAufnv10+h,iv:LMEpZW3mwGuIpJoacBYL8M0ROVNeVMzeb7ncZtfxIDA=,tag:aNCziN9CVgm0IB8VvVorEA==,type:str] +sops: + age: + - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhdUJRS21FeFlseWJXU1dt + MnhQRnFvZWM0M1o4bUhBWW9KRDdnZ1pGZkYwCnhPYmFHZmdnRS9lb2xsTXZBcmIx + dHF5dmlrbjJyUk84QVBLTEFwMWdESGMKLS0tIHFyZGpSeTFoNEQyZThFc2RyQkhY + Q1ZvODVWSXE1STlkZ09tVXdVeU1WaVkKhKMfJclNgHXN7pww2w3AaKwcWiBo676g + RWSkV6C+5purA0CzTu1uC3CKz8UK8mVgPfamSZdZQU8+6bGMmseWoQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-31T19:32:51Z" + mac: ENC[AES256_GCM,data:zpEYjHqta6HuRHIKijbLmAND5mCaR14ZUdEeXq/zJ8g4DgWrAkaukhYdXhLH+SEUZt8d3tmj5Eq+6oz9qEjdWhBuPykxVBmBiqIhQBgACCMhSL5v3wY1rxL2ZiQ7szEuwh0GjXpkzPno0Z2+xZ6FzVsJdGnZwykru+JWQcUIfvk=,iv:yUiP/clvI/NnDrji9eMYiTqtO1xsTc7u86V/nlQSMIA=,tag:UyMz/BdYoGxXCJIb8tITcQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/hosts/hp-envy-office/wg.nix b/hosts/hp-envy-office/wg.nix new file mode 100644 index 0000000..763496e --- /dev/null +++ b/hosts/hp-envy-office/wg.nix @@ -0,0 +1,7 @@ +{ config, pkgs, inputs, ... }: { + sops.secrets.wg1.sopsFile = ./resources/secrets/wg.yaml; + + networking.wg-quick.interfaces = { + wg1.configFile = config.sops.secrets.wg1.path; + }; +} diff --git a/hosts/libreX60/bios-flashing.nix b/hosts/libreX60/bios-flashing.nix new file mode 100644 index 0000000..6117813 --- /dev/null +++ b/hosts/libreX60/bios-flashing.nix @@ -0,0 +1,8 @@ +# https://libreboot.org/docs/install/#thinkpad-t60x60x60tabletx60s + +{ pkgs, ... }: { + environment.systemPackages = [ pkgs.flashprog ]; + + boot.kernelParams = [ "iomem=relaxed" ]; +} + diff --git a/hosts/libreX60/configuration.nix b/hosts/libreX60/configuration.nix new file mode 100644 index 0000000..bf4f388 --- /dev/null +++ b/hosts/libreX60/configuration.nix @@ -0,0 +1,47 @@ +{ config, lib, pkgs, ... }: { + imports = [ + ./powertop-auto-tune.nix + # Uncomment this module and reboot to enable bios flashing + #./bios-flashing.nix + ]; + + # Use grub + boot._loader = { + enable = true; + loader = "grub"; + mode = "bios"; + grub.biosDevice = "/dev/sda"; + }; + + # NOTE libre kernel removed, check back later + # Use libre kernel + #boot.kernelPackages = pkgs.linuxPackages-libre; + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + }; + + # Enable user timmy + _users.timmy = { + enable = true; + autologin.enable = true; + wifi.enable = true; + }; + + # i915 Gpu requires intel driver + services.xserver.videoDrivers = [ "intel" ]; + + # Configure home + home-manager.users.timmy = { + imports = [ ./home.nix ]; + }; + + system.stateVersion = "24.11"; +} diff --git a/hosts/libreX60/hardware-configuration.nix b/hosts/libreX60/hardware-configuration.nix new file mode 100644 index 0000000..b0a7868 --- /dev/null +++ b/hosts/libreX60/hardware-configuration.nix @@ -0,0 +1,46 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "firewire_ohci" "usb_storage" "sd_mod" "sdhci_pci" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e"; + fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e"; + fsType = "btrfs"; + options = [ "subvol=@home" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/F618-D6C2"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + # 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.enp1s0.useDHCP = lib.mkDefault true; + networking.interfaces.wlp2s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/libreX60/home-timmy/configuration.nix b/hosts/libreX60/home-timmy/configuration.nix new file mode 100644 index 0000000..a5f064c --- /dev/null +++ b/hosts/libreX60/home-timmy/configuration.nix @@ -0,0 +1,8 @@ +{ + imports = [ ../home.nix ]; + + targets.genericLinux.enable = true; + + _archetypes.profiles.desktop.enable = true; + _users.timmy.home.enable = true; +} diff --git a/hosts/libreX60/home.nix b/hosts/libreX60/home.nix new file mode 100644 index 0000000..b537fc2 --- /dev/null +++ b/hosts/libreX60/home.nix @@ -0,0 +1,22 @@ +{ + gtk._mintTheme = { + dark = true; + color = "Red"; + icons.color = "Red"; + }; + fonts.fontconfig.defaultFonts.monospace = [ "TamzenForPowerline" ]; + gtk = { + font.name = "monospace"; + font.size = 8; + cursorTheme.size = 24; + }; + programs._st = { + enable = true; + font = { + name = "TamzenForPowerline"; + attrs = { + pixelsize = 14; + }; + }; + }; +} diff --git a/hosts/libreX60/powertop-auto-tune.nix b/hosts/libreX60/powertop-auto-tune.nix new file mode 100644 index 0000000..0eb9578 --- /dev/null +++ b/hosts/libreX60/powertop-auto-tune.nix @@ -0,0 +1,17 @@ +# https://en.wikibooks.org/wiki/Libreboot/ThinkPad_X60#Remove_High_Pitched_Whining_Noise +# TLDR; running `powertop --auto-tune` is supposed to kill the high pitched noises produced by the X60 + +{ pkgs, ... }: { + environment.systemPackages = [ pkgs.powertop ]; + + # Create systemd service + systemd.services.powertop-autotune = { + description = "Powertop Auto-Tune"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.powertop}/bin/powertop --auto-tune"; + }; + }; +} diff --git a/hosts/optiplex/configuration.nix b/hosts/optiplex/configuration.nix new file mode 100644 index 0000000..5108e72 --- /dev/null +++ b/hosts/optiplex/configuration.nix @@ -0,0 +1,109 @@ +{ config, lib, pkgs, home-manager, ... }: { + # Setup bootloader + boot._loader.enable = true; + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + btrfs.enable = true; + }; + collections = { + desktop = { + extraUtilities.enable = true; + cad.enable = true; + chromium.enable = true; + crypto.enable = true; + graphics.enable = true; + office.enable = true; + video.enable = true; + }; + development = { + android.enable = true; + c.enable = true; + docker.enable = true; + lua.enable = true; + web = { + hugo = { + enable = true; + openFirewall = true; + }; + node.enable = true; + }; + }; + virtualization.enable = true; + }; + }; + + # Enable user timmy + _users.timmy = { + enable = true; + autologin.enable = true; + nas = { + enable = true; + home.enable = true; + home.automount = true; + }; + }; + + # Disable suspend + systemd._suspend.disable = true; + + # Allow unfree for nvidia + others + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ + "blackmagic-desktop-video" + "decklink" + "nvidia-kernel-modules" + "nvidia-settings" + "nvidia-x11" + "spotify" + "steam" + "steam-original" + "steam-run" + "steam-unwrapped" + ]; + + # Install more software + environment.systemPackages = with pkgs; [ + prismlauncher + spotify + #vintagestory + ]; + + programs.steam.enable = true; + + # Use nvidia driver + services.xserver.videoDrivers = [ "nvidia" ]; + hardware.nvidia = { + modesetting.enable = true; # Required + powerManagement.enable = false; # Can cause bugs + powerManagement.finegrained = false; # Only works on turing and newer + nvidiaSettings = true; + open = false; # Not compatible w/ GTX-1050 + package = config.boot.kernelPackages.nvidiaPackages.legacy_580; + forceFullCompositionPipeline = true; # Enables vsync + }; + + # Enable decklink support for intensity pro + services._decklink.enable = true; + services._decklink.decklinkSupport.enable = true; + + # Configure home + home-manager.users.timmy = { + gtk._mintTheme = { + dark = true; + color = "Green"; + icons.color = "Green"; + }; + programs._seasonalwallpaper.wallpapers.download = true; + fonts.fontconfig = { + subpixelRendering = "rgb"; + hinting = "none"; + }; + }; + + system.stateVersion = "25.11"; +} diff --git a/hosts/optiplex/g610.nix b/hosts/optiplex/g610.nix new file mode 100644 index 0000000..8cceafc --- /dev/null +++ b/hosts/optiplex/g610.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: { + # Turn off backlight on my G610 keyboard + systemd.services.G610DisableBacklight = { + description = "G610 disable backlight"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig.ExecStart = "${pkgs.g810-led}/bin/g610-led -a 00"; + }; +} diff --git a/hosts/optiplex/hardware-configuration.nix b/hosts/optiplex/hardware-configuration.nix new file mode 100644 index 0000000..9485a66 --- /dev/null +++ b/hosts/optiplex/hardware-configuration.nix @@ -0,0 +1,44 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "firewire_ohci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be"; + fsType = "btrfs"; + options = [ "subvol=root" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be"; + fsType = "btrfs"; + options = [ "subvol=home" ]; + }; + + fileSystems."/nix" = + { device = "/dev/disk/by-uuid/e4094dd8-d1fd-4aa1-8f95-82a9144a32be"; + fsType = "btrfs"; + options = [ "subvol=nix" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/9350-1411"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/piframe/configuration.nix b/hosts/piframe/configuration.nix new file mode 100644 index 0000000..b45a293 --- /dev/null +++ b/hosts/piframe/configuration.nix @@ -0,0 +1,77 @@ +{ config, lib, pkgs, ... }: { + _archetypes = { + # Use headless and pi profiles + profiles.headless = { + enable = true; + home.users.timmy.enable = true; + home.users.piframe.enable = true; + }; + profiles.pi = { + enable = true; + home.users.timmy.enable = true; + home.users.piframe.enable = true; + }; + collections = { + desktop.xserver.enable = true; + }; + }; + + # Install twm as basic window manager to boot into and immediately go fullscreen on immich-frame + services.xserver.windowManager.twm.enable = true; + + # Install immich-frame + environment.systemPackages = with pkgs; [ + immich-frame + ]; + + # Open 8080 for immich-frame + networking.firewall.allowedTCPPorts = [ 8080 ]; + + # Enable user timmy for ssh maintenance and wifi secrets + _users.timmy = { + enable = true; + wifi.enable = true; + }; + + # Enable piframe user + users.users.piframe = { + isNormalUser = true; + }; + + # Configure automatic login with getty + services.getty.autologinUser = "piframe"; + + # Configure piframe home + home-manager.users.piframe = { + services = { + _gammastep.enable = true; + # Manual location to avoid having to mess with geoclue + gammastep = { + provider = "manual"; + latitude = 41.881832; + longitude = -87.623177; + }; + }; + programs.bash = { + enable = true; + profileExtra = '' + # startx on tty1 immediately + [ -z $DISPLAY ] && [ `tty` = /dev/tty1 ] && startx + ''; + }; + home.file.".xinitrc".text = '' + #!/bin/sh + gammastep & + unclutter --start-hidden & + immich-frame --fullscreen & + exec twm + ''; + home.file.".twmrc".text = '' + *NoTitle # Hide title bar on all windows + *NoBorder # Hide borders on all windows + ''; + home.stateVersion = "25.11"; + }; + + system.stateVersion = "25.11"; +} diff --git a/hosts/piframe/hardware-configuration.nix b/hosts/piframe/hardware-configuration.nix new file mode 100644 index 0000000..ceb755a --- /dev/null +++ b/hosts/piframe/hardware-configuration.nix @@ -0,0 +1,24 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888"; + fsType = "ext4"; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; +} diff --git a/hosts/poweredge/bitcoind.nix b/hosts/poweredge/bitcoind.nix new file mode 100644 index 0000000..e282234 --- /dev/null +++ b/hosts/poweredge/bitcoind.nix @@ -0,0 +1,41 @@ +{ + containers.bitcoind = { + autoStart = true; + ephemeral = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:04"; + + # Host path + bindMounts = { + "/var/lib/bitcoind-main" = { + hostPath = "/media/ingens/bitcoin"; + isReadOnly = false; + }; + }; + + config = { lib, pkgs, config, ... }: let + p2pPort = 8333; + rpcPort = 8332; + in { + # Network + networking.enableIPv6 = false; + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ p2pPort rpcPort ]; + + # Bitcoin + services.bitcoind."main" = { + enable = true; + port = p2pPort; + rpc.port = rpcPort; + rpc.users.timmy.passwordHMAC = "7557196eec6e3e062a57b0720abff7cf$617211e94b964f548cfbb5c23590f690c2940e424fffe72485276493f1cb0b29"; + extraConfig = '' + rpcallowip=192.168.1.0/24 + rpcbind=0.0.0.0 + ''; + }; + + system.stateVersion = "25.11"; + }; + }; +} diff --git a/hosts/poweredge/configuration.nix b/hosts/poweredge/configuration.nix new file mode 100644 index 0000000..c15f222 --- /dev/null +++ b/hosts/poweredge/configuration.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: { + imports = [ + ./bitcoind.nix + ./ddns-updater.nix + ./filebrowser.nix + ./fileshares.nix + ./gitea.nix + ./immich.nix + ./jellyfin.nix + ./lgtm.nix + ./networking.nix + ./nvidia.nix + ./transmission.nix + #./notification-mailer.nix # TODO move some of this stuff to archetype + ]; + + # Allow unfree packages for nvidia/cuda + nixpkgs.config.allowUnfree = true; + + # Setup bootloader + boot._loader.enable = true; + + # Enable common options + _archetypes = { + profiles = { + headless = { + enable = true; + home.users.timmy.enable = true; + }; + zfs.enable = true; + cuda.enable = true; + }; + }; + + # Import zfs pools + boot.zfs.extraPools = [ "ingens" "memoria" ]; + + # Enable user timmy + _users.timmy.enable = true; + + # Without this, "ZFS requires networking.hostId to be set" will be raised + networking.hostId = "4d9e002f"; + + system.stateVersion = "25.11"; +} diff --git a/hosts/poweredge/ddns-updater.nix b/hosts/poweredge/ddns-updater.nix new file mode 100644 index 0000000..319e3c4 --- /dev/null +++ b/hosts/poweredge/ddns-updater.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: let + credential = "config"; +in { + # Config for ddns-updater, owned by the ddns-updater systemd service user + sops.secrets.ddns-updater-config.sopsFile = ./resources/secrets/ddns-updater-config.yaml; + + # Load secret as a credential in systemd service + systemd.services.ddns-updater.serviceConfig = { + LoadCredential = [ + "${credential}:${config.sops.secrets.ddns-updater-config.path}" + ]; + }; + + # Enable ddns updater + services.ddns-updater = { + enable = true; + environment = { + SERVER_ENABLED="no"; + CONFIG_FILEPATH = "%d/${credential}"; + }; + }; +} diff --git a/hosts/poweredge/filebrowser.nix b/hosts/poweredge/filebrowser.nix new file mode 100644 index 0000000..818b3df --- /dev/null +++ b/hosts/poweredge/filebrowser.nix @@ -0,0 +1,48 @@ +{ + containers.filebrowser = { + autoStart = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:05"; + + # Host path + bindMounts = { + "/var/lib/filebrowser/data" = { + hostPath = "/media"; + isReadOnly = true; + }; + }; + + config = { lib, pkg, config, ... }: let + publicPort = 9000; + in { + # Network + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ 80 publicPort ]; # Caddy (private + public) + + # Filebrowser + services.filebrowser.enable = true; + + # Reverse proxy + services.caddy = { + enable = true; + # Private + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:8080 + ''; + # Public (not sure why toString is needed but ok) + virtualHosts.":${toString publicPort}".extraConfig = '' + @shared { + path /share/* + path /static/* + path /api/public/* + } + + reverse_proxy @shared localhost:8080 + ''; + }; + + system.stateVersion = "26.05"; + }; + }; +} diff --git a/hosts/poweredge/fileshares.nix b/hosts/poweredge/fileshares.nix new file mode 100644 index 0000000..c5fca71 --- /dev/null +++ b/hosts/poweredge/fileshares.nix @@ -0,0 +1,47 @@ +{ + # Configure services + services._fileShares = { + enable = true; + smb.openFirewall = true; + nfs.openFirewall = true; + }; + # Shares + services._fileShares.shares = { + PS2 = { + path = "/media/ingens/games/ps2"; + smb = { + enable = true; + allowUser = "ps2"; + extraOptions = { + "min protocol" = "NT1"; + "max protocol" = "NT1"; + }; + }; + }; + WinBackups = { + path = "/media/ingens/backups/windows"; + smb.enable = true; + }; + pictures = { + path = "/media/ingens/pictures"; + nfs.enable = true; + }; + tapes = { + path = "/media/ingens/tapes"; + nfs.enable = true; + smb.enable = true; + }; + backups = { + path = "/media/ingens/backups"; + nfs.enable = true; + }; + }; + + # ps2 user + users.users.ps2 = { + isSystemUser = true; + password = "ps2"; + group = "ps2"; + }; + users.groups.ps2 = {}; +} diff --git a/hosts/poweredge/gitea.nix b/hosts/poweredge/gitea.nix new file mode 100644 index 0000000..4c800df --- /dev/null +++ b/hosts/poweredge/gitea.nix @@ -0,0 +1,41 @@ +{ + containers.gitea = { + autoStart = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:03"; + + # TODO make a mountpoint for git repositories + + config = { lib, pkgs, config, ... }: { + # Network + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ 80 22 ]; # Caddy + ssh + + # Gitea + services.gitea = { + enable = true; + #user = "git"; # So ssh cloning uses git@gitea + settings = { + repository = { + ENABLE_PUSH_CREATE_USER = true; + ENABLE_PUSH_CREATE_ORG = true; + }; + }; + }; + + # SSH for repositories + services.openssh.enable = true; + + # Reverse proxy + services.caddy = { + enable = true; + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:3000 + ''; + }; + + system.stateVersion = "25.11"; + }; + }; +} diff --git a/hosts/poweredge/hardware-configuration.nix b/hosts/poweredge/hardware-configuration.nix new file mode 100644 index 0000000..0fcc098 --- /dev/null +++ b/hosts/poweredge/hardware-configuration.nix @@ -0,0 +1,50 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "megaraid_sas" "usbhid" "usb_storage" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "zpool/root"; + fsType = "zfs"; + options = [ "zfsutil" ]; + }; + + fileSystems."/nix" = + { device = "zpool/nix"; + fsType = "zfs"; + options = [ "zfsutil" ]; + }; + + fileSystems."/var" = + { device = "zpool/var"; + fsType = "zfs"; + options = [ "zfsutil" ]; + }; + + fileSystems."/home" = + { device = "zpool/home"; + fsType = "zfs"; + options = [ "zfsutil" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/D083-98C0"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/poweredge/immich.nix b/hosts/poweredge/immich.nix new file mode 100644 index 0000000..444a0f2 --- /dev/null +++ b/hosts/poweredge/immich.nix @@ -0,0 +1,78 @@ +{ pkgs, ... }: let + onnxruntimeCuda = pkgs.onnxruntime.override { cudaSupport = true; }; +in { + containers.immich = { + autoStart = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:01"; + + # Host path + bindMounts = { + "/var/lib/immich" = { + hostPath = "/media/ingens/immich"; + isReadOnly = false; + }; + }; + + # GPU + allowedDevices = [ + { node = "/dev/nvidia0"; modifier = "rw"; } + { node = "/dev/nvidiactl"; modifier = "rw"; } + { node = "/dev/nvidia-uvm"; modifier = "rw"; } + { node = "/dev/nvidia-uvm-tools"; modifier = "rw"; } + { node = "/dev/nvidia-modeset"; modifier = "rw"; } + ]; + + bindMounts = { + # NVENC/NVDEC - video transcoding + "/dev/nvidia0" = { hostPath = "/dev/nvidia0"; isReadOnly = false; }; + "/dev/nvidiactl" = { hostPath = "/dev/nvidiactl"; isReadOnly = false; }; + # CUDA - required for ML inference + "/dev/nvidia-uvm" = { hostPath = "/dev/nvidia-uvm"; isReadOnly = false; }; + "/dev/nvidia-uvm-tools" = { hostPath = "/dev/nvidia-uvm-tools"; isReadOnly = false; }; + # Modeset - needed by some driver paths + "/dev/nvidia-modeset" = { hostPath = "/dev/nvidia-modeset"; isReadOnly = false; }; + }; + + config = { lib, pkgs, config, ... }: { + imports = [ ./nvidia.nix ]; + + # Network + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ 80 ]; # Caddy + + # Immich + services.immich = { + enable = true; + accelerationDevices = [ + "/dev/nvidia0" + "/dev/nvidiactl" + "/dev/nvidia-uvm" + "/dev/nvidia-uvm-tools" + "/dev/nvidia-modeset" + ]; + + # Tell immich-server to use NVENC for transcoding + environment = { + NVIDIA_VISIBLE_DEVICES = "all"; + NVIDIA_DRIVER_CAPABILITIES = "compute,video,utility"; + }; + + # Enable the ML microservice with CUDA + machine-learning.enable = true; + }; + environment.systemPackages = with pkgs; [ immich immich-cli ]; + + # Reverse proxy + services.caddy = { + enable = true; + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:2283 + ''; + }; + + system.stateVersion = "25.11"; + }; + }; +} diff --git a/hosts/poweredge/jellyfin.nix b/hosts/poweredge/jellyfin.nix new file mode 100644 index 0000000..761c3ae --- /dev/null +++ b/hosts/poweredge/jellyfin.nix @@ -0,0 +1,91 @@ +{ + containers.jellyfin = { + autoStart = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:02"; + bindMounts = { + "/media" = { + hostPath = "/media/ingens/media"; + isReadOnly = true; + }; + }; + + # GPU + allowedDevices = [ + { node = "/dev/nvidia0"; modifier = "rw"; } + { node = "/dev/nvidiactl"; modifier = "rw"; } + { node = "/dev/nvidia-uvm"; modifier = "rw"; } + { node = "/dev/nvidia-uvm-tools"; modifier = "rw"; } + { node = "/dev/nvidia-modeset"; modifier = "rw"; } + ]; + + bindMounts = { + # NVENC/NVDEC - video transcoding + "/dev/nvidia0" = { hostPath = "/dev/nvidia0"; isReadOnly = false; }; + "/dev/nvidiactl" = { hostPath = "/dev/nvidiactl"; isReadOnly = false; }; + # CUDA - required for ML inference + "/dev/nvidia-uvm" = { hostPath = "/dev/nvidia-uvm"; isReadOnly = false; }; + "/dev/nvidia-uvm-tools" = { hostPath = "/dev/nvidia-uvm-tools"; isReadOnly = false; }; + # Modeset - needed by some driver paths + "/dev/nvidia-modeset" = { hostPath = "/dev/nvidia-modeset"; isReadOnly = false; }; + }; + + config = { lib, config, pkgs, ... }: { + imports = [ ./nvidia.nix ]; + + # Network + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ 80 ]; # Caddy + + # Jellyfin + services.jellyfin = { + enable = true; + openFirewall = true; + + hardwareAcceleration = { + enable = true; + type = "nvenc"; + device = "/dev/nvidia0"; # Required by the module's assertion, unused in encoding.xml for nvenc + }; + + forceEncodingConfig = true; # Override encoding.xml + + transcoding = { + enableHardwareEncoding = true; + + hardwareEncodingCodecs = { + hevc = true; # Pascal 4th-gen NVENC supports HEVC 8/10-bit encode + av1 = false; # NOT supported on Pascal — do not enable + }; + + hardwareDecodingCodecs = { + h264 = true; + hevc = true; + hevc10bit = true; + vp8 = true; + vp9 = true; + mpeg2 = true; + vc1 = true; + av1 = false; # NOT supported on Pascal NVDEC + hevcRExt10bit = false; # Range Extensions decode is a newer-gen feature, skip on Pascal + hevcRExt12bit = false; + }; + + encodingPreset = "medium"; + enableToneMapping = true; # works via CUDA-based tonemap_cuda filter on Pascal+ + }; + }; + + # Reverse proxy + services.caddy = { + enable = true; + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:8096 + ''; + }; + + system.stateVersion = "26.05"; + }; + }; +} diff --git a/hosts/poweredge/lgtm.nix b/hosts/poweredge/lgtm.nix new file mode 100644 index 0000000..6f43c02 --- /dev/null +++ b/hosts/poweredge/lgtm.nix @@ -0,0 +1,201 @@ +{ pkgs, config, ... }: { + # Log shipping via Alloy + services.alloy.enable = true; + + environment.etc."alloy/config.alloy".text = '' + loki.source.journal "journal" { + forward_to = [loki.write.default.receiver] + relabel_rules = loki.relabel.journal.rules + max_age = "12h" + } + + loki.relabel "journal" { + forward_to = [] + rule { + source_labels = ["__journal__systemd_unit"] + target_label = "unit" + } + } + + loki.write "default" { + endpoint { + url = "http://lgtm:3100/loki/api/v1/push" + } + } + ''; + + # Export prometheus data from host for container to access + services.prometheus.exporters.smartctl = { + enable = true; + port = 9633; + listenAddress = "0.0.0.0"; + }; + + services.prometheus.exporters.node = { + enable = true; + port = 9100; + listenAddress = "0.0.0.0"; + enabledCollectors = [ "systemd" "diskstats" "filesystem" "meminfo" "cpu" ]; + # zfs + hwmon already on by default, no need to list them + }; + + # # Host config — zpool_influxdb needs real pool access + # services.telegraf = { + # enable = true; + # extraConfig = { + # agent.interval = "30s"; + # + # inputs.execd = [{ + # command = [ "${pkgs.zpool_influxdb}/bin/zpool_influxdb" "--execd" ]; + # signal = "none"; + # data_format = "influx"; + # }]; + # + # outputs.prometheus_client = [{ + # listen = ":9273"; + # metric_version = 2; + # }]; + # }; + # }; + + # Allow ports for prometheus + networking.firewall.allowedTCPPorts = [ 9100 9273 9633 ]; # at least reachable from container subnet + + # Secret key for grafana + sops.secrets.grafana-secret-key = { sopsFile = ./resources/secrets/grafana.yaml; key = "secret_key"; }; # Office + + # Container + containers.grafana = { + autoStart = true; + privateNetwork = true; + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:06"; + + # Bind wg0-router secret to container + bindMounts."/run/secrets/grafana-secret_key" = { + hostPath = config.sops.secrets.grafana-secret-key.path; + isReadOnly = true; + }; + + config = { lib, pkgs, config, ... }: { + # Network + networking.enableIPv6 = false; + networking.interfaces.eth0.useDHCP = true; + networking.firewall.allowedTCPPorts = [ 80 3100 9090 ]; # Caddy (grafana) + loki + prometheus + + # Loki + services.loki = { + enable = true; + configuration = { + server = { + http_listen_port = 3100; + http_listen_address = "0.0.0.0"; # explicit, since the host's Alloy needs to reach it to push logs + }; + auth_enabled = false; + + common = { + path_prefix = "/var/lib/loki"; + storage.filesystem = { + chunks_directory = "/var/lib/loki/chunks"; + rules_directory = "/var/lib/loki/rules"; + }; + ring = { + instance_addr = "127.0.0.1"; + kvstore.store = "inmemory"; + }; + replication_factor = 1; + }; + + schema_config.configs = [{ + from = "2024-01-01"; + store = "tsdb"; + object_store = "filesystem"; + schema = "v13"; + index = { prefix = "index_"; period = "24h"; }; + }]; + }; + }; + + # Prometheus + services.prometheus = { + enable = true; + scrapeConfigs = [ + { + job_name = "smartctl"; + static_configs = [{ targets = [ "poweredge:9633" ]; }]; + } + { + job_name = "node"; + static_configs = [{ targets = [ "poweredge:9100" ]; }]; + } + { + job_name = "zpool"; + static_configs = [{ targets = [ "poweredge:9273" ]; }]; + } + ]; + }; + + # Grafana + # https://wiki.nixos.org/wiki/Grafana + services.grafana = { + enable = true; + settings.server = { http_addr = "0.0.0.0"; http_port = 3000; }; + provision = { + enable = true; + + # Creates a *mutable* dashboard provider, pulling from /etc/grafana-dashboards. + # With this, you can manually provision dashboards from JSON with `environment.etc` like below. + dashboards.settings.providers = [{ + name = "my dashboards"; + disableDeletion = true; + options = { + path = "/etc/grafana-dashboards"; + foldersFromFilesStructure = true; + }; + }]; + + datasources.settings.datasources = [ + { name = "Prometheus"; type = "prometheus"; url = "http://127.0.0.1:9090"; isDefault = true; } + { name = "Loki"; type = "loki"; url = "http://127.0.0.1:3100"; } + ]; + }; + }; + + # Grafana secret key + systemd.services.grafana.serviceConfig = { + LoadCredential = [ + "secret_key:/run/secrets/grafana-secret_key" + ]; + }; + services.grafana.settings.security.secret_key = "$__file{/run/credentials/grafana.service/secret_key}"; + + # Grafana dashboards + environment.etc = let + mkDashDl = id: rev: "https://grafana.com/api/dashboards/${toString id}/revisions/${toString rev}/download"; + in { + "grafana-dashboards/node-exporter-full.json".source = pkgs.fetchurl { + url = mkDashDl 1860 45; + sha256 = "sha256-GExrdAnzBtp1Ul13cvcZRbEM6iOtFrXXjEaY6g6lGYY="; + }; + "grafana-dashboards/smartctl-exporter.json".source = pkgs.fetchurl { + url = mkDashDl 22604 2; + sha256 = "sha256-ci8WE23fZ+ltEKFoUdNNVXsUIV0jqtas79ia2lYIo88="; + }; + "grafana-dashboards/zfs-pool-metrics.json".source = pkgs.fetchurl { + url = mkDashDl 15362 1; + sha256 = "sha256-latX1gwRjdpIalT5kpm9MfDaaXFLZCJ0ZP8CZJo8guI="; + }; + }; + + # Reverse proxy + services.caddy = { + enable = true; + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:3000 + ''; + }; + + system.stateVersion = "26.05"; + }; + }; +} diff --git a/hosts/poweredge/networking.nix b/hosts/poweredge/networking.nix new file mode 100644 index 0000000..bf0aa1c --- /dev/null +++ b/hosts/poweredge/networking.nix @@ -0,0 +1,157 @@ +let + hostIp = "192.168.1.10"; +in { config, ... }: { + networking = { + # Label lan and wan interfaces + _interfaceLabels = { + enable = true; + interfaces.lan0 = "50:9a:4c:5d:c3:7a"; + interfaces.wan0 = "50:9a:4c:5d:c3:7b"; + }; + # Create bridged lan interface for all containers + bridges.br-lan0.interfaces = [ "lan0" ]; + # Disable dhcp on router interfaces + interfaces = { + veth-router-lan.useDHCP = false; + vb-router-lan0.useDHCP = false; + }; + # Configure network + defaultGateway = "10.255.255.1"; # Read explaination for veth-router-lan below + nameservers = [ "192.168.1.1" ]; # DNS will only be available from this ip address THROUGH the default gateway + # br-lan0 will be the interface used for networking on poweredge host + interfaces.br-lan0.ipv4.addresses = [{ + address = hostIp; + prefixLength = 24; + }]; + }; + + # Secrets + sops.secrets.router-wg0 = { sopsFile = ./resources/secrets/router.yaml; key = "wg0"; }; # Office + sops.secrets.router-wg1 = { sopsFile = ./resources/secrets/router.yaml; key = "wg1"; }; # Remote access + + # Router container + containers.router = { + autoStart = true; + ephemeral = true; + privateNetwork = true; + # Pass wan0 directly into container since it isn't needed elsewhere + interfaces = [ "wan0" ]; + # Setup router lan0 + # NOTE: Host/container communication is not possible through a hostBridge interface + extraVeths.vb-router-lan0.hostBridge = "br-lan0"; + # Setup virtual host-router bridge interface. + # This is the default gateway for host/container communication since + # communication isn't possible through hostBridge interfaces. + # This is essentially equivalent to connecting the host to the + # container with a virtual ethernet cable on a separate interface. + extraVeths.veth-router-lan = { + hostAddress = "10.255.255.2"; + localAddress = "10.255.255.1"; + }; + # Bind secrets to container + bindMounts."/run/secrets/wg0" = { + hostPath = config.sops.secrets.router-wg0.path; + isReadOnly = true; + }; + bindMounts."/run/secrets/wg1" = { + hostPath = config.sops.secrets.router-wg1.path; + isReadOnly = true; + }; + + config = { lib, config, ... }: { + imports = [ + ../../nixos/services/router + ./router-hosts.nix # Contains dhcp config + static leases + overrides + ]; + + networking = { + # Set ip addresses + enableIPv6 = false; + interfaces = { + vb-router-lan0.ipv4.addresses = [{ + address = "192.168.1.1"; + prefixLength = 24; + }]; + wan0.useDHCP = true; + }; + # Setup wireguard + wg-quick.interfaces = { + wg0.configFile = "/run/secrets/wg0"; + wg1.configFile = "/run/secrets/wg1"; + }; + # NAT (port-forwarding) rules + nat.forwardPorts =[ + { # Bitcoin p2p + sourcePort = 8333; + proto = "tcp"; + destination = "192.168.1.44:8333"; + } + { # Filebrowser + sourcePort = 19045; + proto = "tcp"; + destination = "192.168.1.45:9000"; + } + ]; + firewall.allowedUDPPorts = [ 51820 ]; # Allow wg1 running on router host through w/o NAT + # Additional advanced rules + # TODO add multi NAT feature to router service (this is just a normal nat rule) + nftables = { + enable = true; + tables = { + # NAT/masquerade wg0 allowing vb-router-lan0 clients to access wg0 + wg-nat = { + family = "ip"; + content = '' + chain post { + type nat hook postrouting priority srcnat; policy accept; + iifname "vb-router-lan0" oifname "wg0" masquerade comment "vb-router-lan0 => wg0" + iifname "veth-router-lan" oifname "wg0" masquerade comment "veth-router-lan => wg0" + } + ''; + }; + }; + }; + }; + + # Setup router + services._router = { + dnsDhcpConfig.enable = true; + routing = { + enable = true; + interfaces = { + lan = [ "vb-router-lan0" "veth-router-lan" ]; + wan = "wan0"; + }; + }; + }; + + services.unbound._blocklists = { + enable = true; + hageziBlocklists = [ "pro" "nsfw" ]; + }; + + system.stateVersion = "25.11"; + }; + }; + + # FIXME the following snippet will cause the router container to fail to start: + # networking.defaultGateway = { + # address = "10.255.255.1"; + # interface = "veth-router-lan"; + # }; + # Journalctl will report: + # poweredge container router: Bring veth-router-lan up + # poweredge container router: RTNETLINK answers: File exists + # poweredge systemd: container@router.service: Control process exited, code=exited, status=2/INVALIDARGUMENT + # So the issue nixos is creating an interface with that same name. + # As a temporary workaround, the following service is deployed (after the container starts): + systemd.services.router-default-route = { + after = [ "container@router.service" ]; + wants = [ "container@router.service" ]; + serviceConfig.Type = "oneshot"; + script = '' + /run/current-system/sw/bin/ip route replace default via 10.255.255.1 dev veth-router-lan + ''; + wantedBy = [ "multi-user.target" ]; + }; +} diff --git a/hosts/poweredge/notification-mailer.nix b/hosts/poweredge/notification-mailer.nix new file mode 100644 index 0000000..d8fddc7 --- /dev/null +++ b/hosts/poweredge/notification-mailer.nix @@ -0,0 +1,27 @@ +{ config, ... }: let + serverEmail = "server-notifications@tjkeller.xyz"; +in { + # Mailer password secret for mail application password + sops.secrets.mailerPassword.sopsFile = ./resources/secrets/mailer-pass.yaml; + + # Enable mta for system event notifications + services.mail._mailer = { + sender = { + host = "mail.tjkeller.xyz"; + user = serverEmail; + from = serverEmail; + passwordFile = config.sops.secrets.mailerPassword.path; + }; + recipient = serverEmail; + }; + + # Enable zed mailer module + services.zfs._zedMailer.enable = true; + + # Enable smartd notifications + services.smartd.notifications.mail = { + enable = true; + sender = serverEmail; + recipient = serverEmail; + }; +} diff --git a/hosts/poweredge/nvidia.nix b/hosts/poweredge/nvidia.nix new file mode 100644 index 0000000..257a46c --- /dev/null +++ b/hosts/poweredge/nvidia.nix @@ -0,0 +1,22 @@ +{ config, pkgs, ... }: { + # Configure nixpkgs for nvidia/cuda + nixpkgs.config.allowUnfree = true; + nixpkgs.config.cudaSupport = true; + + # NVIDIA + services.xserver.videoDrivers = [ "nvidia" ]; # xserver.videoDrivers does not imply X + hardware.graphics.enable = true; + hardware.nvidia = { + modesetting.enable = true; # Required + nvidiaSettings = false; # Don't need gui + open = false; + package = config.boot.kernelPackages.nvidiaPackages.legacy_580; # Support for P600 + powerManagement.enable = false; # Can cause bugs + powerManagement.finegrained = false; # Only works on turing and newer + }; + + # Packages + environment.systemPackages = with pkgs; [ + config.hardware.nvidia.package # nvidia-smi + ]; +} diff --git a/hosts/poweredge/resources/secrets/ddns-updater-config.yaml b/hosts/poweredge/resources/secrets/ddns-updater-config.yaml new file mode 100644 index 0000000..3be017b --- /dev/null +++ b/hosts/poweredge/resources/secrets/ddns-updater-config.yaml @@ -0,0 +1,16 @@ +ddns-updater-config: ENC[AES256_GCM,data:vJ3z4R6P1gHKfkm6L2mQl68MKDJwpMNmrAOQo+4GkO2NC6EjKTLoSKhFiaGWVjMm7nrVfYRV+U/6b4VJXV4qURWhsm41t3x8zXAtt0viLC6pv+uMtuxadhU2Zxij4U2bSiMn6sSbfHd3uGIym7FnfOIL3LPEanVMuRUk20a0ZgHBdq1BPk6r5V8AoGfsu1XWHTvnO4ggg9oQPtGhurKTXixTD0Rb1Iv43JXLXqK/O3JGD5h4XbDmXB9eTqiBHUgZ0E4F5SE23L5mO0kI0TNNph2lTHXdfB+5,iv:xFry3gzdvvYh127yhYySvp5UHDa8Y+t/bg2+mwJ/HXo=,tag:pH2CE2l2UpNJiLJ+tjVvqQ==,type:str] +sops: + age: + - recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtWitQcVlaTmFVaHIraHlT + VFBDVEtlQUlqckN4eFF3YU95N3ZNU3JQcFNzCnkrR2xmTEtyUHRWQlRnTWZSaGVT + U0wvcGt6R0w4L3dSakVDVWVpTUhWbWMKLS0tIGVKSXVTL1B2L2FlSkQwSDVYd3Fk + WE8rLy81UEU5ZG9SaHRLOHNqOWUzWnMKBFtzJ9frroYk6hoW+1ww/3LpxCEa1Vtr + KNNnHKry8lQQDmalN5ZVYMTVAlTnQQ6QE7DxBukUwWYmizQ+BY8HDg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-31T01:47:37Z" + mac: ENC[AES256_GCM,data:8ozC5JWR/s3nNK+njc7zO32/7ptd//wuWGWZPHXrPV1iVyYndczGgu0ekEyKeRCn/WwGE5pyt32gy0l2Y+k7j7mV6GJguy6qhltani6Mz2Gfy5sRohn5s2rBDTiSYEVAgGTRt56DLxGD36P6xFPm+wHGspjCzNALrPretuN5xFg=,iv:+/mlXEMEO80pDVpFwZmnyywvHR/V9zHkbloF/e/dJ6Q=,tag:O+Ox0xUzERjeB+VftiUNEg==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/hosts/poweredge/resources/secrets/grafana.yaml b/hosts/poweredge/resources/secrets/grafana.yaml new file mode 100644 index 0000000..91b9188 --- /dev/null +++ b/hosts/poweredge/resources/secrets/grafana.yaml @@ -0,0 +1,16 @@ +secret_key: ENC[AES256_GCM,data:9GHuENHGk+VKukRuUJi+fxOX+ZFa5q5t5UiGtxSQOEctu9LOJqvHJerFv3U=,iv:6OJTeWbotDVj/ogg9WC170Q4Kdfd1PXf0hHwXd/YaUQ=,tag:qlnEl2uzDuhwOKp17R76Tg==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrc0tWWHhtM2Q5UC9weSsr + WDdaOXJjZGVoekJKaXUyUXhsRXMxMjQrYUFnCnBqTGJEMFF6clQ1RXd5NE9CenQ5 + U0swNHdNUUxndnlycGVGM0lpY0dFaVEKLS0tIHVlK2MySUJzaGVXd21iWFFzcEJM + Nmh3V2hIbUhWNUxlOE1idTdWNWZ6alEKWae75mhvfpTNOcvm7x1zf19QkmZqrAf7 + jpkyKDE//d4Rn3h4Ogpl5bdXb9RyOoSEJUG+EDPFyynqdodCJ2ucpw== + -----END AGE ENCRYPTED FILE----- + recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + lastmodified: "2026-06-18T22:40:22Z" + mac: ENC[AES256_GCM,data:5HoPrQU8Q+SGEH4jQHy1otFXGILqttDpcbv+edNoKI/QFoUE74M4PrGzltlManZstqK3nfWV3HNIcY6GRe8TMP5ANC/8UIXfEDerP1Q0kj6xeyLumMCJ6iCjygF2SzL5YMWqBSsCJvH5O10sWncc2iPnGrQPJ8PksIxkXCe4YYk=,iv:f0WpJwENciWkyDKpGM/PaE2lmXWvNiMAxNpqlCEFhM8=,tag:nQAPTJPYsgRIUkDxktIvmA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/hosts/poweredge/resources/secrets/mailer-pass.yaml b/hosts/poweredge/resources/secrets/mailer-pass.yaml new file mode 100644 index 0000000..331bd66 --- /dev/null +++ b/hosts/poweredge/resources/secrets/mailer-pass.yaml @@ -0,0 +1,16 @@ +pass: ENC[AES256_GCM,data:RHOvLwbDIb8FZ+dG66e5U43qR0aXlLLZGAnlbRjSl8hxCMEtJ4940nggiaIV75jCaiWyLutay7MrKPKZBHDZwBIqcJYQRWm1zWGkoZi0/bX38vUFWOpI4qku9fIB2qll,iv:bqEnTagxlRqlAmMgFCtXXCSSlODE598yoV4fU0jSYL8=,tag:c/ZiGCDSb8quDoYiIKbMeQ==,type:str] +sops: + age: + - recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEUXlZaUhSUkNGK2xpVzRp + OEhYMTV6bnpPZC9tdHZWbnZxcUp6WWVLMnhFCmZmckVBckdRS1g0MjJQdE80S2Js + aGlNek1nSmU2aGI4cWVXR0NmbjJwa00KLS0tIDJ3N3BoenQ5ZW02K3BLNWxkWU5y + Ym56YzI5Zk9KeFhzZXJXR3NoOUl0ckEKOLweZrk/Pe6BG48+RrwOxyOy0Zb768aZ + YIxTBv/qSzZei6VqZHiIwTUEMyE7z3CS0dBFws6q4fB4LfIpv6fiYg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-31T01:37:29Z" + mac: ENC[AES256_GCM,data:WIGXvuwB4bcBDfMRrrMQ7faUkxFdreyYiuy6bNPI2pzvvUFTSo/lJTv/DjisSARdYmFHFvdResIXUjg75Sc2I5IrvRxZxnYqx/3z5k/WOFWb8HSKH2H+OUHtLkqWJSCQ9YBuX2tys93mEXgwchPpn4nzVaYBgxZl54F3icX7tsE=,iv:BS9KPGkVaH0G0bAZz6+LR0NDcmqw6khOkih5DyvGyug=,tag:dA9YVL1xEqUqe6hDzOH7XQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/hosts/poweredge/resources/secrets/router.yaml b/hosts/poweredge/resources/secrets/router.yaml new file mode 100644 index 0000000..edf7b41 --- /dev/null +++ b/hosts/poweredge/resources/secrets/router.yaml @@ -0,0 +1,17 @@ +wg0: ENC[AES256_GCM,data:hxZrOTMx2lS7dkQPV48jUbHxoNr32KSM/wG0OPY8mcgHX4FsHStdH/CN4qlkH9co90yFKjwqcJ3fHXaSbIhp3oIYTo1R2/8KggHV+az4skb7v4kzZgU5u5V2B57gIRFrPHBBq1UtDkBBpANr7zypeLPuEzvCQaZiexrsCcET7xwhsEYtVc5WlISMHhi6sFzo1AeEVfwFvIUN9geXJwP3JycYRpaNXOVYAbPwer8fYa6mnFZIk+sruCO85QNoWQ2T1wPiC9U7Ki9q+FOaOzJWGdpiG3nQdeYlDgAwoPRNi3c0J6t3CS9xC+qC0y4esOL7ay4S2dDoL9ftChQjhWaGdq5+LeyXAPlATGs5aFtBMwEecQ5OPXTri2D5m/gVLW/TGmUT5ZVPymm/wAX+m4IWK6X3FylWnlEoUh+4cBO+2zjTbXEccRuJTOEZAGIsEg==,iv:ycsGniDPFOvFFGqCgiyCWkCS8AxFV7JbXgUxDPXAFmk=,tag:uXkB8b7S3sW9noTIK4J/Zw==,type:str] +wg1: ENC[AES256_GCM,data:4qwFVFszUQgBquAcgA4dB0TdBmTxprZLaL7+FQjrqG8avDFNxZyUFc9fHXiNRsvbgr+UKGVhdOast0y6kOaeQqqPiHLqQD/MN5wu8+SLFTaNppOKiwsjrK9x43GCxRT/ytmQWgb3pBVf7ruwbmykjDhZ577f3yhnNYhJuHoGLvTdJ18pxSE6DaukrJTnwa6BhxKv62IGlGHLYi5u9keR3LFCBwYyAIEjU9E+Na7mZ4cM2Y61/e0KTekMcQHqlBqvWmIDbdnLsoC/JVl/A8gIlU1TiOqo7geiUte/wwzc3dtkK+WQWuvaDrdtWdsUaKJEFPZhmx6KWZk4gTMdIlbP+KMm0X39rXIkMMyfWqs2rklnwm+BIg1vayQYsuAH3DXofxNlWcOEsjvnw19kPy8CrYOFAx2tPeyUWs1mFEQYeB0gafvRrFJfMUkh4uQuJnznjJH+8fmVM66EA6GLSRFUsjK/v4yEKftQQkYKuWRO9MpO5kTYo0YiSO9xOPbxNe+VVC1oJnQxaTeXNV2JZLjce/3+aZ3oFu5KG11yx+OqfRDBdJVcg6jEgVBlNY88ZrNTUUb9tEqlSXwuradURX1GekYwM0fBRTW+f/oiJFsbbBA8SODWl/50HnxcTqkeiYmnB2PA167nFLFOL/MYRh+txXbpcH4lwoPshPjGnM3XGxUxNIU+xqFFLHn/h6WhiyUZ6SkRgNGaGQ4w6RuRvZm3aHb0wAW0pwOnPoMbh2XDVTkdjFLxmJpAUHfy2W+FIYAGIU0DcT3/r2dW91hDSzjnysRxrPRiOc37nvsrqxjZd0+WI4KtHhpgxGkPyoV6CcFcKr7GrImlh2cVDr3oxrUHI9JrmD7VWKJV/ws6tSZsfHc=,iv:zXw3y9MIyaw/zXXl4CgavbP7QbAritZdhAWBL40vhtw=,tag:SXsG2HtActAlJrt7foqldA==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxOFFVRFl2MDhiUVRPdnZJ + b3BSZ0pSaG8vR3o3SGlmVENvN3ZObHU3eUc4CmJrdk8rbXpBMWhTZ2hvdlk0VWo1 + ejdnTDR4bFlUUXc4Smd0NEp6b3crZFUKLS0tIFBsQUdRWjZLSDY1ZlFaRDdLOVhs + MFFGY0NSU3NaK2U0U1NndUIyYm54ZWsKZ6q9j1fNaNSzBA0rbZYyUWt3U2V/7/9/ + FZTKd8mH/uCoxvK8unlVZ9uYNADRh2smp7LwK1AWie/6khAIFqVBeg== + -----END AGE ENCRYPTED FILE----- + recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + lastmodified: "2026-06-22T19:36:47Z" + mac: ENC[AES256_GCM,data:Dvl3nQrqpPiYUfRHJuLQCbt0QF1duV0/E+S8A2kQ3zjP5NxmO4lKFRXgcWwsDzm+IQl4pcIMQ+FqU/C7ujTqxQ3/PSRx4o51POWDwJA5DmNpVNzH87BuXDxXPiItlPKzAfpbs8TwGCIOvmOcEP7Pd55eWJucRAq29N6nnixFHHQ=,iv:G91DcLnshSlBrtTTyJdJyuOa4wgjNhWsoVRDUk+WJOU=,tag:9Fcj8Oxu5q/u9pAiU18xwg==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/hosts/poweredge/resources/secrets/transmission.yaml b/hosts/poweredge/resources/secrets/transmission.yaml new file mode 100644 index 0000000..5945804 --- /dev/null +++ b/hosts/poweredge/resources/secrets/transmission.yaml @@ -0,0 +1,17 @@ +ovpn-auth: ENC[AES256_GCM,data:3F3/4v0xOsdWguywaKxE6IB6pzHONWk6F6Es,iv:7ZJUyeb0EZTVkTIk5W2rhBjXmUHm2TLe5/YKgOJiqPA=,tag:idiz76VtruJ2NOhUhtbXjw==,type:str] +ovpn-config: ENC[AES256_GCM,data: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,iv:Uy0Z4W4PUcwfkGfR6Mtf6AWShsU3dmLz+H9JOJd463c=,tag:B1KAAANSnRvb0ECwYP0QxQ==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVNkthU0srVEFZMk43ZFFu + dEJ3QkJyMEJ2OUhCR1Nwc3pQSHEzaXhjZFRzCm9idXlpUVM2WTRXQWY2SzFJei93 + bzdEdEdiS1NHYy9mYWY3UU5xcmJMN2MKLS0tIGtSblFnZDdHK1ltUTNyWUFVdFd5 + czZxZzUvSjlzcmtZRURleURwWGZVSjQK+DsUTq8yTkKZGK2rj1c9tFPm9nRoAjkQ + D5HFkN8uRVTB91JEx077I8VZLXJgJ6HFGOpW2EGV4nDilL9YP288Eg== + -----END AGE ENCRYPTED FILE----- + recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + lastmodified: "2026-06-20T18:05:47Z" + mac: ENC[AES256_GCM,data:+w9BPkacf5X9hJhh5Bmm8nf5eANZs07K/BS3c1q6jVdUoHPkVbOz0EbAEQkheW8xQAH5lIQl0D5GXSbGyhLnV3mKwL3RgV532aAY02IT68ygDvFPx6ziBEmqBlWaTZ3DopKOqPzje0olMuefYF1Li1LihmLEX3kMxwBFOr8oSn0=,iv:Lkq8NdRjbMI21bfVVoq08kgZQmgdFrh+xKgd8j/VAkc=,tag:bMwLv5ABrMSzwq5t6/s65g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/hosts/poweredge/router-hosts.nix b/hosts/poweredge/router-hosts.nix new file mode 100644 index 0000000..2a08558 --- /dev/null +++ b/hosts/poweredge/router-hosts.nix @@ -0,0 +1,84 @@ +let + localDomain = "home.lan"; # TODO RFC8375 .home.arpa + dhcp = { + inherit staticLeases; + defaultGateway = "192.168.1.1"; + localhostIp = "192.168.1.1"; + rangeStart = "192.168.1.50"; + rangeEnd = "192.168.1.250"; + }; + staticLeases = { + # Network + idrac-7N94GK2 = { + macAddress = "50:9a:4c:5d:c3:7c"; + staticIp = "192.168.1.2"; + }; + OpenWrt-Attic = { + macAddress = "34:98:b5:60:5e:be"; + staticIp = "192.168.1.3"; + }; + OpenWrt-Basement = { + macAddress = "8c:3b:ad:35:c7:8c"; + staticIp = "192.168.1.4"; + }; + ArcherC54 = { + macAddress = "12:eb:b6:13:f9:e2"; + staticIp = "192.168.1.5"; + }; + # Desktops + T495 = { + macAddress = "04:33:c2:9d:34:74"; + staticIp = "192.168.1.11"; + }; + optiplex = { + macAddress = "e4:54:e8:bc:ba:05"; + staticIp = "192.168.1.12"; + }; + X230 = { + macAddress = "84:3a:4b:60:34:c4"; + staticIp = "192.168.1.13"; + }; + # Services + gnuslashprinter = { + macAddress = "00:23:24:5b:f0:6d"; + staticIp = "192.168.1.40"; + }; + immich = { + macAddress = "02:00:00:00:00:01"; + staticIp = "192.168.1.41"; + }; + jellyfin = { + macAddress = "02:00:00:00:00:02"; + staticIp = "192.168.1.42"; + }; + gitea = { + macAddress = "02:00:00:00:00:03"; + staticIp = "192.168.1.43"; + }; + bitcoind = { + macAddress = "02:00:00:00:00:04"; + staticIp = "192.168.1.44"; + }; + filebrowser = { + macAddress = "02:00:00:00:00:05"; + staticIp = "192.168.1.45"; + }; + grafana = { + macAddress = "02:00:00:00:00:06"; + staticIp = "192.168.1.46"; + }; + transmission = { + macAddress = "02:00:00:00:00:07"; + staticIp = "192.168.1.47"; + }; + }; + dns.hostOverrides = { + "router.${localDomain}" = "192.168.1.1"; + "poweredge.${localDomain}" = "192.168.1.10"; + }; +in { + services._router.dnsDhcpConfig = { + inherit localDomain dhcp dns; + }; + networking.hosts."192.168.1.1" = [ "router.${localDomain}" "router" ]; +} diff --git a/hosts/poweredge/transmission.nix b/hosts/poweredge/transmission.nix new file mode 100644 index 0000000..cded95d --- /dev/null +++ b/hosts/poweredge/transmission.nix @@ -0,0 +1,118 @@ +{ config, pkgs, ... }: { + # Secrets + sops.secrets.transmission-ovpn-config = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-config"; }; + sops.secrets.transmission-ovpn-auth = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-auth"; }; + + # Container + containers.transmission = let + home = "/var/lib/transmission"; + download-dir = "${home}/complete"; + incomplete-dir = "${home}/incomplete"; + in { + autoStart = true; + privateNetwork = true; + enableTun = true; # OpenVPN requires + hostBridge = "br-lan0"; + localMacAddress = "02:00:00:00:00:07"; + + # Download dirs + bindMounts = { + "${download-dir}" = { + hostPath = "/media/ingens/media/.incomplete"; + isReadOnly = false; + }; + "${incomplete-dir}" = { + hostPath = "/media/ingens/media/.complete"; + isReadOnly = false; + }; + }; + + # Bind secrets + bindMounts."/run/secrets/ovpn-config.ovpn" = { + hostPath = config.sops.secrets.transmission-ovpn-config.path; + isReadOnly = true; + }; + bindMounts."/run/secrets/ovpn-auth" = { + hostPath = config.sops.secrets.transmission-ovpn-auth.path; + isReadOnly = true; + }; + + config = { lib, config, ... }: { + # Network + networking.enableIPv6 = false; # Prevent ip leaks + networking.interfaces.eth0.useDHCP = true; + networking.firewall.interfaces = { + eth0.allowedTCPPorts = [ 80 ]; # RPC interface + # Torrent ports + tun0 = { + allowedTCPPorts = [ 51413 ]; + allowedUDPPorts = [ 51413 ]; + }; + }; + + # Transmission + services.transmission = { + inherit home; + enable = true; + + settings = { + inherit download-dir incomplete-dir; + }; + }; + + # TODO remove (#258793) + systemd.services.transmission.serviceConfig = { + RootDirectoryStartOnly = lib.mkForce null; + RootDirectory = lib.mkForce null; + }; + + # Reverse proxy + services.caddy = { + enable = true; + virtualHosts.":80".extraConfig = '' + reverse_proxy localhost:9091 + ''; + }; + + # OpenVPN + services.openvpn.servers.main = { + config = '' + config /run/secrets/ovpn-config.ovpn + auth-user-pass /run/secrets/ovpn-auth + ''; + autoStart = true; + updateResolvConf = true; + }; + + # VPN killswitch + networking.firewall.extraCommands = '' + # Get domain name host and port from ovpn config + SERVER_HOST=$(${pkgs.gawk}/bin/awk '/^remote /{print $2;exit}' /run/secrets/ovpn-config.ovpn) + SERVER_PORT=$(${pkgs.gawk}/bin/awk '/^remote /{print $3;exit}' /run/secrets/ovpn-config.ovpn) + + # Resolve server ip from host + while [ -z "$SERVER_IP" ]; do + sleep 3 + SERVER_IP=$(${pkgs.getent}/bin/getent hosts "$SERVER_HOST" 2>/dev/null | ${pkgs.gawk}/bin/awk '{print $1}') + echo "SERVER_IP: $SERVER_IP" + done + + # Only allow out traffic from tun0 + ${pkgs.iptables}/bin/iptables -P OUTPUT DROP + ${pkgs.iptables}/bin/iptables -A OUTPUT -o lo -j ACCEPT + ${pkgs.iptables}/bin/iptables -A OUTPUT -o tun0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Exception: allow established connections + ${pkgs.iptables}/bin/iptables -A OUTPUT -p udp -d "$SERVER_IP" --dport "$SERVER_PORT" -j ACCEPT + + # Allow DNS + DNS_IP=$(${pkgs.gawk}/bin/awk '/^nameserver /{print $2; exit}' /etc/resolv.conf) + ${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p udp -d "$DNS_IP" --dport 53 -j ACCEPT + ${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p tcp -d "$DNS_IP" --dport 53 -j ACCEPT + + # Allow transmission RPC + ''; + + system.stateVersion = "26.05"; + }; + }; +} diff --git a/hosts/sweetiepc/configuration.nix b/hosts/sweetiepc/configuration.nix new file mode 100644 index 0000000..7bf5fcb --- /dev/null +++ b/hosts/sweetiepc/configuration.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, home-manager, ... }: { + #imports = [ ./wg.nix ]; + + # Setup bootloader + boot._loader = { + enable = true; + loader = "grub"; + }; + + # Enable common options + _archetypes = { + profiles = { + desktop = { + enable = true; + home.users.timmy.enable = true; + }; + zfs.enable = true; + btrfs.enable = true; + gnome.enable = true; + }; + collections = { + desktop = { + office.enable = true; + }; + }; + }; + + # Import zfs pools + boot.zfs.extraPools = [ "geminus" "quattuor" ]; + + # Enable user timmy + _users.timmy = { + enable = true; + }; + + # Disable suspend + systemd._suspend.disable = true; + + # Configure home + home-manager.users.timmy = { + fonts.fontconfig = { + subpixelRendering = "rgb"; + hinting = "none"; + }; + }; + + # Without this, "ZFS requires networking.hostId to be set" will be raised + networking.hostId = "fdde503a"; + + system.stateVersion = "26.05"; +} diff --git a/hosts/sweetiepc/hardware-configuration.nix b/hosts/sweetiepc/hardware-configuration.nix new file mode 100644 index 0000000..5ed162c --- /dev/null +++ b/hosts/sweetiepc/hardware-configuration.nix @@ -0,0 +1,38 @@ +# 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. +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e"; + fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + fileSystems."/home" = + { device = "/dev/disk/by-uuid/91572803-436d-4f43-b41f-dfba2103752e"; + fsType = "btrfs"; + options = [ "subvol=@home" ]; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/F618-D6C2"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/sweetiepc/resources/secrets/wg.yaml b/hosts/sweetiepc/resources/secrets/wg.yaml new file mode 100644 index 0000000..9edaea5 --- /dev/null +++ b/hosts/sweetiepc/resources/secrets/wg.yaml @@ -0,0 +1,16 @@ +wg0: ENC[AES256_GCM,data:xow2iL5l7MqlUeCZ2e9R4ygKlK3+ZP2yIR73aHWiIaO24aF02I88cWajOvs44/CP2tTHJLM6OltN/rKr3+4DLCbhOrngl/uFyWv/Xva4n9ZCWBv0DgtQ0Qbk/Bi6Q+h3Uf7XXlOtsUNvzSXvW4L+EcwsTMQRU5CIpSHcIsyG/IsQ1ypHpzDPM8j3HHtoPp6zaYPpQiELoMIIuIjvnvjTAAEWYMPuiV4OgJy2kiAUFkEwDud3MV/e/D/q861lKSz5qiKWnxE/So9kmA4HaSw9EOi46EcFojat7r3LiludcMify4V+IliVs7LPYf8kaHSOPWdmd8cgDeYP3Np0ydhB2+2xSbRLCgZvJWuxkStbeNjA5sC3tAmA86I+axhjVGRD9NdbpksdDORBMJhx2iWVhBBvM5p/Ya0f5nXHlsLw4MLSCKOZlTf9OM5YlYGw/GA30nJbcOVx1TzK+BJ0cYsz7U4YJWEMHTEPIQ==,iv:TM0Csdn65smwseVzq36IJAcapEZx+yjhRyG3Atz2Jk0=,tag:A6wWrO8PJAPFaI2IFq5Ucw==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlS0pyVGtTekV0MXQ5WHYy + MlRZYnVVaXdKVDlGNlZOekxUWng4NUlWTXlBCnBzbzNwUWtTdExBUXpCNy83YStm + Y0xvTlo5bkczS2xSdUtjenNjQlhDNlkKLS0tIGd6RjlZWGtZNVpXeVNpd0EyTk5h + cWN0b0dXQjNwNFZjdXdiUDZCUDZETkkKJlDzHxpRHbh4+LPEaWjrmiFFGMOL4Hdi + lMhSrn/EmMmf6p82FVpWMfr7fEFc9lCRw1VytKNUc1Le3nIpkM8iJQ== + -----END AGE ENCRYPTED FILE----- + recipient: age1fl4rk54n2xv4vssjkw4nywy93s4yqv69uzq2f64zuwd8fjq5u4cq9wmmsw + lastmodified: "2026-06-22T19:17:43Z" + mac: ENC[AES256_GCM,data:o+p9xd02QZ9VcUwiAMAGExf5FBrDaKCkOen58d1zTzZuiH2Svcmg5xrA+N23XuEi9kVTcqby9fpymgTOlwNZn8JbmMYjLLiykBIWQvOPKNjAGzp2A9KFA1DpIzTegM+xXbnKv+FEZdRagAojkAUftUUzQYAUsmQVDnflzwNsSUI=,iv:s0oM+TgNNUgNZQ99Pb33UCUbhdZJ93Dv1X+GK6oFUwo=,tag:306d418MjlEZnPi6L2qd/g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1 diff --git a/hosts/sweetiepc/wg.nix b/hosts/sweetiepc/wg.nix new file mode 100644 index 0000000..bf459d9 --- /dev/null +++ b/hosts/sweetiepc/wg.nix @@ -0,0 +1,7 @@ +{ config, pkgs, inputs, ... }: { + sops.secrets.wg0 = { sopsFile = ./resources/secrets/wg.yaml; key = "wg0"; }; + + networking.wg-quick.interfaces = { + wg0.configFile = config.sops.secrets.wg0.path; + }; +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..01801a2 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,3 @@ +{ + _mkProfileArchetype = import ./mkprofile.nix; +} diff --git a/lib/home-manager/default.nix b/lib/home-manager/default.nix new file mode 100644 index 0000000..01801a2 --- /dev/null +++ b/lib/home-manager/default.nix @@ -0,0 +1,3 @@ +{ + _mkProfileArchetype = import ./mkprofile.nix; +} diff --git a/lib/home-manager/mkprofile.nix b/lib/home-manager/mkprofile.nix new file mode 100644 index 0000000..dae4d60 --- /dev/null +++ b/lib/home-manager/mkprofile.nix @@ -0,0 +1,9 @@ +profileName: nixosConfig: homeConfig: { config, lib, ... }: let + cfg = config._archetypes.profiles.${profileName}; +in { + options._archetypes.profiles.${profileName} = { + enable = lib.mkEnableOption "enable (standalone) home-manager profile ${profileName} for the configured user"; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ homeConfig ]); +} diff --git a/lib/mkprofile.nix b/lib/mkprofile.nix new file mode 100644 index 0000000..c8cfe12 --- /dev/null +++ b/lib/mkprofile.nix @@ -0,0 +1,22 @@ +profileName: nixosConfig: homeConfig: { config, lib, ... }: let + cfg = config._archetypes.profiles.${profileName}; + enabledUsers = lib.filterAttrs (user: userCfg: userCfg.enable) cfg.home.users; + homeConfigs = { + home-manager.users = lib.mapAttrs (user: userCfg: homeConfig) enabledUsers; + }; +in { + options._archetypes.profiles.${profileName} = { + enable = lib.mkEnableOption "enable nixos profile ${profileName}"; + home.users = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options = { + enable = lib.mkEnableOption "home-manager profile ${profileName} for user"; + }; + }); + default = {}; + description = "enable home-manager profile ${profileName} for user the specified user"; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ nixosConfig homeConfigs ]); +} diff --git a/modules/home/alacritty.nix b/modules/home/alacritty.nix deleted file mode 100644 index f60fe01..0000000 --- a/modules/home/alacritty.nix +++ /dev/null @@ -1,67 +0,0 @@ -{ config, lib, ... }: { - options = { - alacritty = { - enable = lib.mkEnableOption "enables creation of alacritty config file"; - font.size = lib.mkOption { - type = lib.types.int; - default = 12; - }; - font.family = lib.mkOption { - type = lib.types.str; - default = "JetBrainsMonoNL Nerd Font Mono"; - }; - }; - }; - - config = { - programs.alacritty.enable = config.alacritty.enable; - programs.alacritty.settings = lib.mkIf config.alacritty.enable { - colors = { - bright = { - black = "#7f7f7f"; - blue = "#1578c1"; - cyan = "#00c4a3"; - green = "#2bb500"; - magenta = "#b14ff7"; - red = "#ed1207"; - white = "#ffffff"; - yellow = "#fc9700"; - }; - normal = { - black = "#101010"; - blue = "#1578c1"; - cyan = "#00c4a3"; - green = "#2bb500"; - magenta = "#b14ff7"; - red = "#ed1207"; - white = "#ffffff"; - yellow = "#fc9700"; - }; - primary = { - background = "#101010"; - foreground = "#ffffff"; - }; - }; - cursor.style.blinking = "Never"; - env.TERM = "xterm-256color"; - font = { - size = config.alacritty.font.size; - normal.family = config.alacritty.font.family; - }; - mouse.bindings = [{ - action = "PasteSelection"; - mouse = "Right"; - }]; - window = { - dynamic_padding = true; - dynamic_title = true; - title = "Terminal"; - # Pretend to be st - class = { - general = "st"; - instance = "st"; - }; - }; - }; - }; -} diff --git a/modules/home/chrome.nix b/modules/home/chrome.nix deleted file mode 100644 index 976a74b..0000000 --- a/modules/home/chrome.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ pkgs, lib, ... }: { - programs.chromium = { - enable = true; - package = pkgs.ungoogled-chromium; - }; - programs.chromium.extensions = - let - createChromiumExtensionFor = browserVersion: { id, sha256, version }: - { - inherit id; - crxPath = builtins.fetchurl { - url = "https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&prodversion=${browserVersion}&x=id%3D${id}%26installsource%3Dondemand%26uc"; - name = "${id}.crx"; - inherit sha256; - }; - inherit version; - }; - createChromiumExtension = createChromiumExtensionFor (lib.versions.major pkgs.ungoogled-chromium.version); - in - [ - (createChromiumExtension { - # ublock origin - id = "cjpalhdlnbpafiamejdnhcphjbkeiagm"; - sha256 = "sha256:1i0668xhq5iflb4fn0ghwp79iz6mwspgxdqwp6incbvsyzr596kg"; - version = "1.61.0"; - }) - ]; -} diff --git a/modules/home/default.nix b/modules/home/default.nix deleted file mode 100644 index b44f360..0000000 --- a/modules/home/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ lib, userDetails, ... }: { - home = { - username = userDetails.username; - homeDirectory = userDetails.home.root; - stateVersion = "24.05"; - }; - - imports = [ - ./alacritty.nix - #./chrome.nix - ./firefox.nix - ./git.nix - ./gtk-bookmarks.nix - ./htop.nix - ./initial-home-setup.nix - ./pcmanfm.nix # TODO mk name changeable & doesn't seem to work right now - ./theme.nix - ./wallpapers.nix - ]; - - alacritty.enable = lib.mkDefault true; - theme.mint.enable = lib.mkDefault true; - wallpapers.enable = lib.mkDefault false; -} diff --git a/modules/home/firefox.nix b/modules/home/firefox.nix deleted file mode 100644 index 5e015ff..0000000 --- a/modules/home/firefox.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ pkgs, userDetails, ... }: { - programs.firefox = { - # TODO see if there is way to login to moz account in profile - enable = true; - arkenfox = { - enable = true; - }; - profiles = let - search = { - engines = { - "Timmy Search" = { - urls = [{ template = "https://search.tjkeller.xyz/search?q={searchTerms}"; }]; # Don't know how to do w/ POST but I prefer GET anyways - iconURI = "https://search.tjkeller.xyz/static/themes/simple/img/favicon.svg"; # TODO doesn't seem to work - }; - "Nix Packages" = { - urls = [{ - template = "https://search.nixos.org/packages"; - params = [ - { name = "type"; value = "packages"; } - { name = "query"; value = "{searchTerms}"; } - ]; - }]; - - icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"; - definedAliases = [ "@np" ]; - }; - }; - default = "Timmy Search"; - privateDefault = "Timmy Search"; - force = true; # Overwrite old - }; - userChrome = builtins.readFile ./resources/firefox/userChrome.css; - arkenfox = { - enable = true; - enableAllSections = true; - "0100"."0102"."browser.startup.page".value = 3; # 0=blank, 1=home, 2=last visited page, 3=resume previous session - "0100"."0103"."browser.startup.homepage".enable = false; - "0100"."0104"."browser.newtabpage.enabled".enable = false; - "2800"."2811"."privacy.clearOnShutdown.history".enable = false; - }; - settings = { - "browser.compactmode.show" = true; - "browser.uiCustomization.state" = builtins.readFile ./resources/firefox/uiCustomization.json; # Toolbar etc. - "browser.uidensity" = 1; # Compact - "toolkit.legacyUserProfileCustomizations.stylesheets" = true; # userchrome - - "app.normandy.first_run" = false; - "browser.aboutConfig.showWarning" = false; # arkenfox does - "browser.download.dir" = userDetails.home.downloads; # FF will create this dir if it doesn't exist - "browser.newtabpage.activity-stream.feeds.section.topstories" = false; - "browser.newtabpage.activity-stream.feeds.topsites" = false; - "browser.urlbar.suggest.topsites" = false; - "devtools.toolbox.host" = "window"; - "dom.push.enabled" = false; # - "extensions.pocket.enabled" = false; - "general.smoothScroll" = false; - #identity.fxaccounts.account.device.name = "timmy’s Firefox on nixos"; # HOSTNAME - }; - workSettings = settings // { - "extensions.activeThemeID" = "firefox-compact-dark@mozilla.org"; # Use builtin dark theme instead of system theme - }; - in { - Personal = { - id = 0; - isDefault = true; - inherit search; - inherit userChrome; - inherit arkenfox; - inherit settings; - }; - Work = { - id = 1; - inherit search; - inherit userChrome; - inherit arkenfox; - settings = workSettings; - }; - }; - }; -} diff --git a/modules/home/git.nix b/modules/home/git.nix deleted file mode 100644 index 9d15a90..0000000 --- a/modules/home/git.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ userDetails, ... }: { - programs.git = { - enable = true; - userName = userDetails.fullname; - userEmail = userDetails.email; - }; -} diff --git a/modules/home/gtk-bookmarks.nix b/modules/home/gtk-bookmarks.nix deleted file mode 100644 index 067543d..0000000 --- a/modules/home/gtk-bookmarks.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ config, lib, userDetails, ... }: { - options = { - additional-gtk-bookmarks = lib.mkOption { - type = with lib.types; listOf str; - default = []; - description = "gtk bookmarks for file managers, etc."; - }; - }; - - config = { - gtk.gtk3.bookmarks = with userDetails; [ - "file://${home.downloads} Downloads" - "file://${home.documents} Documents" - "file://${home.pictures} Pictures" - ] ++ config.additional-gtk-bookmarks; - }; -} diff --git a/modules/home/htop.nix b/modules/home/htop.nix deleted file mode 100644 index 523fee8..0000000 --- a/modules/home/htop.nix +++ /dev/null @@ -1,38 +0,0 @@ -{ config, ... }: { - programs.htop.enable = true; - programs.htop.settings = { - fields = with config.lib.htop.fields; [ - PID - USER - 124 # EXE - PERCENT_CPU - PERCENT_MEM - STARTTIME - ELAPSED # time elapsed since process start - IO_READ_RATE - RBYTES # num read bytes - IO_WRITE_RATE - WBYTES # num written bytes - TIME - STIME # sys time - CSTIME # child sys time - NICE - STATE - PRIORITY - 125 # CWD - ]; - } // (with config.lib.htop; leftMeters [ - (bar "AllCPUs2") - (bar "CPU") - (bar "Memory") - (bar "DiskIO") - (bar "NetworkIO") - ]) // (with config.lib.htop; rightMeters [ - (text "Hostname") - (text "Clock") - (text "Uptime") - (text "Battery") - (text "LoadAverage") - (text "Tasks") - ]); -} diff --git a/modules/home/initial-home-setup.nix b/modules/home/initial-home-setup.nix deleted file mode 100644 index 046f5a5..0000000 --- a/modules/home/initial-home-setup.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ config, lib, pkgs, userDetails, ... }: { - home.activation = { - cloneRepos = lib.hm.dag.entryAfter ["writeBoundary"] '' - export PATH="${config.home.path}/bin:$PATH" - ${builtins.readFile ./resources/activation-scripts/clone-repos.sh} - ''; - linkZshProfile = lib.hm.dag.entryAfter ["writeBoundary"] '' - run ln -sf $VERBOSE_ARG $HOME/.config/zsh/zprofile $HOME/.zprofile - ''; - cleanupHome = lib.hm.dag.entryAfter ["writeBoundary"] '' - run rm -f $VERBOSE_ARG $HOME/{.zcompdump,.zshrc,.zsh_history,.bash_history} - ''; - mimewiz = lib.hm.dag.entryAfter ["writeBoundary"] '' - export PATH="${pkgs.xdg-utils}/bin:$PATH" - export PATH="$HOME/.local/bin/misc:$PATH" - run mimewiz -i # already verbose - ''; - createDirs = lib.hm.dag.entryAfter ["writeBoundary"] '' - run mkdir -p $VERBOSE_ARG "${userDetails.home.downloads}" - ''; - }; -} diff --git a/modules/home/pcmanfm.nix b/modules/home/pcmanfm.nix deleted file mode 100644 index 3c70521..0000000 --- a/modules/home/pcmanfm.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ - home.file.pcmanfm-root = { - target = ".local/share/file-manager/actions/root.desktop"; - source = ./resources/pcmanfm/root.desktop; - }; -} diff --git a/modules/home/resources/activation-scripts/clone-repos.sh b/modules/home/resources/activation-scripts/clone-repos.sh deleted file mode 100644 index 887968b..0000000 --- a/modules/home/resources/activation-scripts/clone-repos.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -server="https://git.tjkeller.xyz/" - -clonemissing() { - # clone to $2 - [ -d "$2"/.git ] && return - run mkdir -p $VERBOSE_ARG "$2" - run git clone $VERBOSE_ARG "$server$1" "$2" - - # link to $3 - [ -z "$3" ] && return - run mkdir -p $VERBOSE_ARG "$3" - run ln -sf $VERBOSE_ARG "$2"/* "$3" -} - -# # repo # clone to # link to -clonemissing scripts $HOME/docs/src/scripts $HOME/.local/bin -clonemissing dotconfig $HOME/docs/src/config $HOME/.config -clonemissing userscripts $HOME/docs/src/userscripts '' -clonemissing nixos $HOME/docs/src/nixos '' -clonemissing awesome $HOME/.config/awesome '' -clonemissing nvim $HOME/.config/nvim '' -clonemissing zsh $HOME/.config/zsh '' diff --git a/modules/home/resources/firefox/uiCustomization.json b/modules/home/resources/firefox/uiCustomization.json deleted file mode 100644 index 339b716..0000000 --- a/modules/home/resources/firefox/uiCustomization.json +++ /dev/null @@ -1 +0,0 @@ -{"placements":{"widget-overflow-fixed-list":[],"unified-extensions-area":["ublock0_raymondhill_net-browser-action","sponsorblocker_ajay_app-browser-action","_e6e36c9a-8323-446c-b720-a176017e38ff_-browser-action","dearrow_ajay_app-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","idcac-pub_guus_ninja-browser-action","addon_darkreader_org-browser-action","_74145f27-f039-47ce-a470-a662b129930a_-browser-action","_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action","jid1-bofifl9vbdl2zq_jetpack-browser-action","addon_fastforward_team-browser-action","jid1-tsgsxbhncspbwq_jetpack-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action"],"nav-bar":["back-button","forward-button","stop-reload-button","home-button","urlbar-container","downloads-button","unified-extensions-button"],"toolbar-menubar":["menubar-items"],"TabsToolbar":["tabbrowser-tabs","new-tab-button","alltabs-button"],"PersonalToolbar":["personal-bookmarks"]},"seen":["save-to-pocket-button","developer-button","dearrow_ajay_app-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","_e6e36c9a-8323-446c-b720-a176017e38ff_-browser-action","sponsorblocker_ajay_app-browser-action","ublock0_raymondhill_net-browser-action","idcac-pub_guus_ninja-browser-action","addon_darkreader_org-browser-action","_74145f27-f039-47ce-a470-a662b129930a_-browser-action","_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action","jid1-bofifl9vbdl2zq_jetpack-browser-action","addon_fastforward_team-browser-action","jid1-tsgsxbhncspbwq_jetpack-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action"],"dirtyAreaCache":["nav-bar","PersonalToolbar","unified-extensions-area","toolbar-menubar","TabsToolbar"],"currentVersion":20,"newElementCount":5} diff --git a/modules/home/resources/firefox/userChrome.css b/modules/home/resources/firefox/userChrome.css deleted file mode 100644 index de0e112..0000000 --- a/modules/home/resources/firefox/userChrome.css +++ /dev/null @@ -1,193 +0,0 @@ -/* TOP BAR */ -#navigator-toolbox { - --uc-navigationbar-width: 40vw; -} - -/* Remove overflow button */ -#nav-bar-overflow-button { display: none; } - -/* remove alltabs button */ -#alltabs-button { display: none; } - -/* Change background color of toolbar */ -#navigator-toolbox-background { - background: var(--toolbar-field-border-color) !important; -} - -/* Fix vertical spacing of tabs */ -#TabsToolbar { - margin-top: -1px; -} - -/* Move new tab to far right */ -#tabbrowser-arrowscrollbox-periphery { - margin-left: auto; -} - -/* Fix spacing around tabs by removing nav bar top border */ -#nav-bar { - border: none !important; -} - -/* Media queries for width of nav bar */ -@media screen and (max-width: 1000px) { - #navigator-toolbox { - --uc-navigationbar-width: 50vw; - } -} -@media screen and (max-width: 800px) { - #navigator-toolbox { - --uc-navigationbar-width: 60vw; - } -} - -/* Change look of tabs in smaller view */ -@media screen and (max-width: 800px) { - .tab-background { - margin-block: .2em !important; - } - #TabsToolbar { - margin-left: 5px; - } -} - - -/* COMBINE TOP BAR */ -/* Combine top bar into single line if width >= 800px */ -@media screen and (min-width: 800px) { - :root { - --uc-toolbar-height: 36px; /* Half height bar */ - } - - /* Modify these to change relative widths or default height */ - #navigator-toolbox { - margin-bottom: 0px; - padding-top: 1px; /* Top bar is cut off otherwise */ - } - - #TabsToolbar { - margin-left: calc(var(--uc-navigationbar-width) + 2px); /* Resize tab bar */ - margin-top: -4px; - margin-bottom: 2px; - } - - /* Tabs extend to bottom of bar */ - .tab-background { - margin-bottom: 0 !important; - margin-top: 3px !important; /* See above navigator-toolbox rule. Avoid cutting off top */ - } - - /* Center tab buttons */ - #tabs-newtab-button, - #alltabs-button - { - margin-top: 4px !important; - } - - /* Integrate url / nav bar */ - #nav-bar { - margin-right:calc(100vw - var(--uc-navigationbar-width)); - margin-top: calc(0px - var(--uc-toolbar-height)); - border-radius: 0 var(--tab-border-radius) var(--tab-border-radius) 0; - border-right: 1px solid ThreeDShadow !important; - box-shadow: 0 0 4px rgba(0,0,0,.4) !important; - } - - /* 1px margin on touch density causes tabs to be too high */ - .tab-close-button { - margin-top: 0 !important - } - - /* Make opened urlbar overlay the toolbar */ - #urlbar[open]:focus-within { - min-width: 50vw !important; - } - - /* Remove min and max width of urlbar */ - #urlbar-container { - width: 0 !important; - } - - /* Fix customization view */ - #customization-panelWrapper .panel-arrowbox .panel-arrow { - margin-inline-end: initial !important; - } - - /* Shorten findbar */ - findbar { - width: 600px !important; - border-radius: 0 0 0 5px; - border-bottom: none !important; - box-shadow: 0 2px 4px rgba(0,0,0,.4); /* Move down 2px so it doesn't go over the tab bar */ - } -} - - -/* FINDBAR */ -findbar { - width: 100vw; - position: absolute; - top: 0; - right: 0; - padding: 0 !important; - padding-top: 1px !important; - background: -moz-headerbar Field !important; - border-top-width: 0px !important; - border-bottom: 1px solid ThreeDShadow; -} - -findbar .findbar-container { - padding-bottom: 5px !important; /* Move search bar closer to left edge */ - padding-top: 2px !important; /* Move search bar closer to left edge */ - height: max-content !important; - gap: 2px; - justify-content: space-between; - flex-wrap: wrap; -} - -/* Force textbox to fill up first line */ -findbar .findbar-textbox { - width: 100% !important; - background: Field !important; /* Set the background color to be consistent with found-matches label when unfocused */ -} - -/* Hide description showing wrap conditions etc. */ -findbar description { - display: none; -} - -/* Move found matches label (roughly) into the textbox */ -findbar label.found-matches, -findbar description { - position: absolute; - top: 6.5px; - right: 110px; - color: color-mix(in srgb, -moz-headerbartext, transparent 46%) !important; - /* So this text overrides the text below, TODO find a more elegant solution */ - padding-inline: 1ex; - background: Field; -} - -/* Show description when it says "Phrase Not Found" */ -findbar description[status=notfound] { - display: inline-block; -} - -/* Force checkboxes onto second line */ -findbar .findbar-container hbox { - width: 100%; -} - - -/* BOOKMARK BAR */ -@-moz-document url(chrome://browser/content/browser.xhtml) { - #PersonalToolbar { - background: -moz-headerbar Field !important; - border-top: 1px solid ThreeDShadow !important; - z-index: 1000; /* To not be affected by shadow from navbar */ - } - /* Space out bookmark items */ - .bookmark-item .toolbarbutton-text { - padding: 2.5px 4px; - } -} diff --git a/modules/home/theme.nix b/modules/home/theme.nix deleted file mode 100644 index a1c4f9b..0000000 --- a/modules/home/theme.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ config, lib, pkgs, ... }: -let - transformColorValue = color : - if color == "Green" then "Mint-Y" - else if color == "Dark-Green" then "Mint-Y-Dark" - else "Mint-Y-${color}" - ; -in { - options = { - theme.mint = { - enable = lib.mkEnableOption "enables mint theme"; - # TODO add theme.dark option instead of specifying Dark-Color - theme.color = lib.mkOption { - type = lib.types.enum [ - "Aqua" "Blue" "Green" "Grey" "Orange" "Pink" "Purple" "Red" - "Sand" "Teal" "Dark-Aqua" "Dark-Blue" "Dark-Green" - "Dark-Grey" "Dark-Orange" "Dark-Pink" "Dark-Purple" - "Dark-Red" "Dark-Sand" "Dark-Teal" - ]; - default = "Dark-Aqua"; - description = "mint-y theme color eg. 'Dark-Aqua' or 'Red'"; - }; - icons.color = lib.mkOption { - type = lib.types.enum [ - "Aqua" "Blue" "Green" "Grey" "Orange" "Pink" "Purple" "Red" - "Sand" "Teal" - ]; - default = "Aqua"; - description = "mint-y icons color eg. 'Aqua' or 'Red'"; - }; - }; - theme.font = { - sansSerif = lib.mkOption { - type = lib.types.str; - default = "Inter"; - description = "default sans serif font"; - }; - monospace = lib.mkOption { - type = lib.types.str; - default = "CommitMono"; - description = "default monospace font"; - }; - }; - }; - - config = { - gtk = { - enable = true; - theme = lib.mkIf config.theme.mint.enable { - package = pkgs.mint-themes; - name = transformColorValue config.theme.mint.theme.color; - }; - iconTheme = lib.mkIf config.theme.mint.enable { - package = pkgs.mint-y-icons; - name = transformColorValue config.theme.mint.icons.color; - }; - font = { - name = config.theme.font.sansSerif; - }; - cursorTheme = { - name = "Adwaita"; - }; - gtk2.configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc"; - }; - qt = { - enable = true; - platformTheme.name = "gtk3"; - }; - fonts.fontconfig.defaultFonts = { - sansSerif = [ config.theme.font.sansSerif ]; - monospace = [ config.theme.font.monospace ]; - }; - }; -} diff --git a/modules/home/wallpapers.nix b/modules/home/wallpapers.nix deleted file mode 100644 index d7e067a..0000000 --- a/modules/home/wallpapers.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ lib, config, pkgs, ... }: { - options = { - wallpapers.enable = lib.mkEnableOption "enables all wallpapers"; - }; - - config = lib.mkIf config.wallpapers.enable { - home.file.".local/share/wallpaper" = { - source = pkgs.fetchzip { - url = "https://tjkeller.xyz/permalinks/wallpapers.tar"; - hash = "sha256-lhj9wUMZfd0XOrUtWY9HB64oXrS/EjsvsaJHzu4ucFg="; - }; - }; - }; -} diff --git a/modules/hosts/T430/configuration.nix b/modules/hosts/T430/configuration.nix deleted file mode 100644 index 282515d..0000000 --- a/modules/hosts/T430/configuration.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ config, lib, pkgs, ... }: { - imports = [ ./hardware-configuration.nix ]; - networking.hostName = "T430"; - - # TODO this does not function - #boot.initrd.systemd.extraBin = { - # sh = "${pkgs.dash}/bin/dash"; - # vim = "${pkgs.neovim}/bin/nvim"; - #}; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - system.stateVersion = "24.05"; -} diff --git a/modules/hosts/T430/home.nix b/modules/hosts/T430/home.nix deleted file mode 100644 index 9885a06..0000000 --- a/modules/hosts/T430/home.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ ... }: { - imports = [ ../../home ]; - - alacritty.font.size = 9; - wallpapers.enable = true; -} diff --git a/modules/hosts/T495/configuration.nix b/modules/hosts/T495/configuration.nix deleted file mode 100644 index 526b7bb..0000000 --- a/modules/hosts/T495/configuration.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ config, lib, pkgs, ... }: { - imports = [ - ./hardware-configuration.nix - ./games.nix - ./input-leap.nix - ./wg.nix - ]; - networking.hostName = "T495"; - - bootloader.mode = "efi"; - - # Enable extra software pkgs - software.desktop = { - chromium.enable = true; - cad.enable = true; - crypto.enable = true; - graphics.enable = true; - office.enable = true; - utilities.enable = true; - }; - software.development = { - docker.enable = true; - }; - - # Enable network drives - nas.enable = true; - nas.office.enable = true; - nas.office.automount = false; - - networking.hosts = { - "192.168.77.3" = [ "devel" ]; - }; - - # Use amdgpu driver for x11 - services.xserver.videoDrivers = [ "amdgpu" ]; - - # Enable bluetooth - bluetooth.enable = true; - - system.stateVersion = "24.05"; -} diff --git a/modules/hosts/T495/games.nix b/modules/hosts/T495/games.nix deleted file mode 100644 index 419d854..0000000 --- a/modules/hosts/T495/games.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: { - nixpkgs.config.allowUnfree = true; - environment.systemPackages = with pkgs; [ - #vintagestory - prismlauncher - ]; -} diff --git a/modules/hosts/T495/home.nix b/modules/hosts/T495/home.nix deleted file mode 100644 index 817b0c0..0000000 --- a/modules/hosts/T495/home.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ ... }: { - imports = [ ../../home ]; - - theme.mint.theme.color = "Dark-Purple"; - theme.mint.icons.color = "Purple"; - wallpapers.enable = true; - - home.file.input-leap-config = { - source = ./resources/input-leap; - recursive = true; - target = "./.config/input-leap"; - }; -} diff --git a/modules/hosts/T495/input-leap.nix b/modules/hosts/T495/input-leap.nix deleted file mode 100644 index cf6d018..0000000 --- a/modules/hosts/T495/input-leap.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ pkgs, ... }: { - environment.systemPackages = with pkgs; [ - input-leap - ]; - - networking.firewall = { - allowedTCPPorts = [ - 24800 # input-leaps - ]; - }; -} diff --git a/modules/hosts/T495/resources/input-leap/input-leap.conf b/modules/hosts/T495/resources/input-leap/input-leap.conf deleted file mode 100644 index c2d5176..0000000 --- a/modules/hosts/T495/resources/input-leap/input-leap.conf +++ /dev/null @@ -1,12 +0,0 @@ -section: screens - T495: - optiplex: -end - -section: links - T495: - up = optiplex - - optiplex: - down = T495 -end diff --git a/modules/hosts/T495/resources/secrets/wg0.yaml b/modules/hosts/T495/resources/secrets/wg0.yaml deleted file mode 100644 index 6df5ff6..0000000 --- a/modules/hosts/T495/resources/secrets/wg0.yaml +++ /dev/null @@ -1,21 +0,0 @@ -wg0: ENC[AES256_GCM,data:Zxw98YrH2QYyU8u4sk0geOUQXfOZK9scGPECbKruehcDffJlRspmsS2k3sSahdAIcJjIjauGmZ7usXus2wKAiRnYsl2e4P/0IUVp8L8UJp52iPZVEvT9JRELPYQ9gBBNmp/HDYglxP7uHx2bzcCTvCr4fFdQZgsUSl/pd6pBtSMwupsK5U9Xfz9Sh7JNTlpgpU4jlkl676XnrCo0HyL7rXMp4S6Ruhfve8gwJYP8QLn4DiwnP69Flc54MDQRKSvlvYca8dZIrS1N3h9k6aO1eB/z4I3qPN0/p7yT9pdfemaDpEqf1+byRKGYuED1Ge8P3D0+qRWJBd5GmoSHC8NV9KVV3DrAJ/QLOLCqm8oN0UGxQW5lbWi6lIQ708kodEDOsBREexRPOzjYAjajBr6O6wCtRThgce4yOg9ZwqxVDERqIz1fR034b56LrZtKjOSlJtmkmMCuCGrVkKj4L7JvBfKtCjBN+TOyONsClY/Vhd+XzNWWk8d8FVraOE2rJN4+ss7UL+jBth3DfSbr739cEQDhHRKoMPvXy0FhPVw=,iv:CN82pOBNCtICzM0Ac9Gh+x8pUSK1Fc2v2KmM+de3A78=,tag:KuGiC+Ak550d6njSdX4muA==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4NEN4NGxDR1oreGVoSGhE - TzMxSEY0QVBhS2Z6MW15ci9aVlJ0a3IyVlZBCldPRVNvcUhJSHhWSEk3akd4RjN0 - ajhUV2d1ZWRsRFU4cTE2dGl6RmM4MGsKLS0tIFhnUjl5aDJqWVB1NE15SlNzR2Iv - YTNydURsOUMrSXZGdk9UOUdUQlA0SFUKxEDJRR6tpYva9qpWo9NxwCxk/xpRVoTl - YJkmDZzMcXikXXiro96AprP9dXJXvMPKYPGl2Zsal8PlGFPBoHW2GA== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-01-05T18:21:12Z" - mac: ENC[AES256_GCM,data:VEtpQLHMoQK7Qg4PT2DkNNMurjRE0ZadyiQ0uYsPJ0K2lS0gD8pPwb8btiq7KXXOGWWZOMYRDaRKOENy44f/k+16GOpO6jaKAfN6eEcidaWlP7zvpeNzt7LImFqn/Sjv4rq1+DmvGxyyNjuK8BwvvAfnZwG6KXbn7Bh6xgfTnLs=,iv:Jf4f5j6BTkDBmah3izIESsAn7wDMUFU6NbsqpFIdui4=,tag:yNg374unwHaDEK43+Y0eWg==,type:str] - pgp: [] - unencrypted_suffix: _unencrypted - version: 3.9.2 diff --git a/modules/hosts/hp-envy-office/configuration.nix b/modules/hosts/hp-envy-office/configuration.nix deleted file mode 100644 index 9a7a43a..0000000 --- a/modules/hosts/hp-envy-office/configuration.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ config, lib, pkgs, ... }: { - imports = [ - ./hardware-configuration.nix - ]; - networking.hostName = "hp-envy-office"; - - # Enable extra software pkgs - software.desktop = { - chromium.enable = true; - cad.enable = false; - crypto.enable = false; - graphics.enable = true; - office.enable = true; - utilities.enable = true; - }; - software.development = { - docker.enable = true; - }; - - # Enable network drives - nas.enable = true; - nas.office.enable = true; - nas.home.enable = false; - - networking.hosts = { - "192.168.77.3" = [ "devel" ]; - }; - - # Use amdgpu driver for x11 - services.xserver.videoDrivers = [ "amdgpu" ]; - - # Enable bluetooth - bluetooth.enable = true; - - # Disable autologin - autologin.enable = false; - - system.stateVersion = "24.11"; -} diff --git a/modules/hosts/hp-envy-office/home.nix b/modules/hosts/hp-envy-office/home.nix deleted file mode 100644 index ed02130..0000000 --- a/modules/hosts/hp-envy-office/home.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ userDetails, ... }: { - imports = [ ../../home ]; - - theme.mint.theme.color = "Dark-Green"; - theme.mint.icons.color = "Green"; - wallpapers.enable = true; - - additional-gtk-bookmarks = with userDetails; [ - "file://${home.documents}/src/sites/admin Admin" - "file:///media/chexx/chexx cHEXx" - ]; -} diff --git a/modules/hosts/libreX60/configuration.nix b/modules/hosts/libreX60/configuration.nix deleted file mode 100644 index 942793d..0000000 --- a/modules/hosts/libreX60/configuration.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ config, lib, pkgs, ... }: { - imports = [ ./hardware-configuration.nix ]; - networking.hostName = "libreX60"; - - # Enable extra software pkgs - software.desktop = { - office.enable = true; - }; - - # Enable network drives - nas.enable = true; - - system.stateVersion = "24.11"; -} diff --git a/modules/hosts/libreX60/home.nix b/modules/hosts/libreX60/home.nix deleted file mode 100644 index 453bfd6..0000000 --- a/modules/hosts/libreX60/home.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ ... }: { - imports = [ ../../home ]; - - theme.mint.theme.color = "Dark-Red"; - theme.mint.icons.color = "Red"; - theme.font.sansSerif = "Tamzen"; - theme.font.monospace = "Tamzen"; -} diff --git a/modules/hosts/optiplex/configuration.nix b/modules/hosts/optiplex/configuration.nix deleted file mode 100644 index cbde612..0000000 --- a/modules/hosts/optiplex/configuration.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ config, lib, pkgs, ... }: { - imports = [ - ./hardware-configuration.nix - ]; - networking.hostName = "optiplex"; - - bootloader.mode = "efi"; - - # Allow unfree for nvidia + others - nixpkgs.config.allowUnfree = true; - - # Enable extra software pkgs - software.desktop = { - chromium.enable = true; - cad.enable = true; - crypto.enable = true; - graphics.enable = true; - office.enable = true; - utilities.enable = true; - }; - - # Install more programs - environment.systemPackages = with pkgs; [ - prismlauncher - spotify - #vintagestory - ]; - - # Enable network drives - nas.enable = true; - - # Disable wifi - wifi.enable = false; - - networking.hosts = { - "192.168.77.3" = [ "devel" ]; - }; - - # Use nvidia driver - services.xserver.videoDrivers = [ "nvidia" ]; - hardware.nvidia = { - modesetting.enable = true; # Required - powerManagement.enable = false; # Can cause bugs - nvidiaSettings = true; - open = false; # Not compatible w/ GTX-1050 - package = config.boot.kernelPackages.nvidiaPackages.stable; # Still good for 1050 - }; - - system.stateVersion = "24.11"; -} diff --git a/modules/hosts/optiplex/home.nix b/modules/hosts/optiplex/home.nix deleted file mode 100644 index ee56dc6..0000000 --- a/modules/hosts/optiplex/home.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ ... }: { - imports = [ ../../home ]; - - theme.mint.theme.color = "Dark-Green"; - theme.mint.icons.color = "Green"; - wallpapers.enable = true; -} diff --git a/modules/root/autologin.nix b/modules/root/autologin.nix deleted file mode 100644 index 6e66160..0000000 --- a/modules/root/autologin.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ lib, config, userDetails, ... }: { - options = { - autologin.enable = lib.mkEnableOption "enables getty automatic login"; - #autologin.user = lib.mkEnableOption "populate username"; - #autologin.password = lib.mkEnableOption "populate password"; - }; - - config = lib.mkIf config.autologin.enable { - services.getty = { - autologinUser = userDetails.username; - }; - }; -} diff --git a/modules/root/bluetooth.nix b/modules/root/bluetooth.nix deleted file mode 100644 index d55eade..0000000 --- a/modules/root/bluetooth.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ lib, config, ... }: { - options = { - bluetooth.enable = lib.mkEnableOption "enables bluetooth support"; - }; - - config = { - hardware.bluetooth.enable = true; - services.blueman.enable = true; - }; -} diff --git a/modules/root/bootloader.nix b/modules/root/bootloader.nix deleted file mode 100644 index 0a45264..0000000 --- a/modules/root/bootloader.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ lib, config, ... }: { - options = { - bootloader.loader = lib.mkOption { - type = lib.types.enum [ "grub" "systemd-boot" ]; - default = "systemd-boot"; - description = "whether to install grub or systemd-boot as the bootloader"; - }; - bootloader.mode = lib.mkOption { - type = lib.types.enum [ "efi" "bios" ]; - default = "efi"; - description = "whether to install the bootloader in efi or bios mode"; - }; - bootloader.grub = { - biosDevice = lib.mkOption { - type = lib.types.str; - description = "device to install grub on"; - }; - }; - bootloader.memtest86.enable = lib.mkEnableOption "make Memtest86+ available from the bootloader"; - }; - - config = { - boot.loader = { - grub = { - enable = config.bootloader.loader == "grub"; - efiSupport = config.bootloader.mode == "efi"; - efiInstallAsRemovable = config.bootloader.mode == "efi"; - device = if config.bootloader.mode == "bios" then config.bootloader.grub.biosDevice else "nodev"; - enableCryptodisk = true; - memtest86.enable = config.bootloader.memtest86.enable; - }; - systemd-boot = { - enable = config.bootloader.loader == "systemd-boot"; - editor = false; - memtest86.enable = config.bootloader.memtest86.enable; - }; - efi = lib.mkIf (config.bootloader.mode == "efi") { - efiSysMountPoint = lib.mkIf (config.bootloader.loader == "grub") "/boot/efi"; - canTouchEfiVariables = true; - }; - }; - }; -} diff --git a/modules/root/default.nix b/modules/root/default.nix deleted file mode 100644 index e108318..0000000 --- a/modules/root/default.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ lib, ... }: { - imports = [ - ./software - ./autologin.nix - ./bluetooth.nix - ./bootloader.nix - ./doas.nix - ./fonts.nix - ./home-manager.nix - ./hosts.nix - ./localization.nix - ./nas.nix - ./nix.nix - ./normaluser.nix - ./pipewire.nix - ./powerkeys.nix - ./printing.nix - ./secrets.nix - ./ssh.nix - ./tlp.nix - ./wifi.nix - ./x11.nix - ./zsh.nix - ]; - - autologin.enable = lib.mkDefault true; - avahi.enable = lib.mkDefault true; - bluetooth.enable = lib.mkDefault false; - doas.enable = lib.mkDefault true; - fonts.enable = lib.mkDefault true; - nas = { - enable = lib.mkDefault false; - home.enable = lib.mkDefault true; - home.automount = lib.mkDefault false; - office.enable = lib.mkDefault false; - office.automount = lib.mkDefault false; - }; - bootloader = { - mode = lib.mkDefault "bios"; - memtest86.enable = lib.mkDefault true; - }; - home-manager.enable = lib.mkDefault true; - pipewire.enable = lib.mkDefault true; - printing.enable = lib.mkDefault true; - tlp.enable = lib.mkDefault true; - scanning.enable = lib.mkDefault true; - wifi.enable = lib.mkDefault true; - xserver.enable = lib.mkDefault true; - zsh.enable = lib.mkDefault true; -} diff --git a/modules/root/doas.nix b/modules/root/doas.nix deleted file mode 100644 index c6707ce..0000000 --- a/modules/root/doas.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ lib, config, ... }: { - options = { - doas.enable = lib.mkEnableOption "enables doas"; - sudo.enable = lib.mkEnableOption "enables sudo"; - }; - - config = { - security = { - #sudo.enable = config.sudo.enable; - sudo.enable = true; # TODO remove once can be built from flake w git - sudo.wheelNeedsPassword = false; - doas.enable = config.doas.enable; - doas.extraRules = lib.mkIf config.doas.enable [{ - keepEnv = true; - }]; - doas.wheelNeedsPassword = false; - }; - }; -} diff --git a/modules/root/fonts.nix b/modules/root/fonts.nix deleted file mode 100644 index 3ae8eef..0000000 --- a/modules/root/fonts.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - fonts.enable = lib.mkEnableOption "enables fonts"; - }; - - config = lib.mkIf config.fonts.enable { - fonts.packages = with pkgs; [ - commit-mono - inter - tamzen - (nerdfonts.override { - fonts = [ "JetBrainsMono" ]; - }) - ]; - }; -} diff --git a/modules/root/home-manager.nix b/modules/root/home-manager.nix deleted file mode 100644 index d271523..0000000 --- a/modules/root/home-manager.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - home-manager.enable = lib.mkEnableOption "enables home-manager"; - }; - - config = lib.mkIf config.home-manager.enable { - environment.systemPackages = with pkgs; [ - home-manager - ]; - }; -} diff --git a/modules/root/hosts.nix b/modules/root/hosts.nix deleted file mode 100644 index 14daaf1..0000000 --- a/modules/root/hosts.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - networking.hosts = { - "192.168.1.9" = [ "optiplex" ]; - "192.168.1.30" = [ "localgit" ]; - "192.168.1.11" = [ "truenas-home" ]; - "192.168.77.11" = [ "truenas-office" ]; - "192.168.77.8" = [ "publicgit" "tjkeller" ]; - "173.9.253.3" = [ - "git.tjkeller.xyz" - "piped.tjkeller.xyz" - "search.tjkeller.xyz" - "tjkeller.xyz" - ]; - }; -} diff --git a/modules/root/localization.nix b/modules/root/localization.nix deleted file mode 100644 index 8313f07..0000000 --- a/modules/root/localization.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - time.timeZone = "America/Chicago"; - i18n.defaultLocale = "en_US.UTF-8"; - services.xserver.xkb.layout = "us"; -} diff --git a/modules/root/nas.nix b/modules/root/nas.nix deleted file mode 100644 index 0116ba3..0000000 --- a/modules/root/nas.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ lib, config, ... } : -let - mkNetworkFileSystem = device: automount: { - device = "${device}"; - fsType = "nfs"; - options = [ "defaults" ] ++ lib.optionals (!automount) [ "noauto" ]; - }; -in { - options = { - nas = { - enable = lib.mkEnableOption "enable network shares"; - home = { - enable = lib.mkEnableOption "enable home network shares"; - automount = lib.mkEnableOption "automount home network shares"; - }; - office = { - enable = lib.mkEnableOption "enable office network shares"; - automount = lib.mkEnableOption "automount home network shares"; - }; - }; - }; - - config = lib.mkIf config.nas.enable { - fileSystems = lib.optionalAttrs config.nas.home.enable { - "/media/Storage/Media" = mkNetworkFileSystem "truenas-home:/mnt/Storage/Media" config.nas.home.automount; - "/media/Storage/Backups" = mkNetworkFileSystem "truenas-home:/mnt/Storage/Backups" config.nas.home.automount; - "/media/Storage/Tapes" = mkNetworkFileSystem "truenas-home:/mnt/Storage/Backups/Tapes" config.nas.home.automount; - "/media/Family Photos" = mkNetworkFileSystem "truenas-home:/mnt/Media/Photos" config.nas.home.automount; - } // lib.optionalAttrs config.nas.office.enable { - "/media/chexx/chexx" = mkNetworkFileSystem "truenas-office:/mnt/Storage/chexx" config.nas.office.automount; - "/media/chexx/tkdocs" = mkNetworkFileSystem "truenas-office:/mnt/Storage/Users/Tim-Keller" config.nas.office.automount; - "/media/chexx/scans" = mkNetworkFileSystem "truenas-office:/mnt/Storage/Scans" config.nas.office.automount; - }; - # TODO auto mkdirz - }; -} diff --git a/modules/root/normaluser.nix b/modules/root/normaluser.nix deleted file mode 100644 index fc243ea..0000000 --- a/modules/root/normaluser.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ config, userDetails, ... }: { - users.users.root = { - hashedPasswordFile = config.sops.secrets.hashed-root-password.path; - }; - users.users.${userDetails.username} = { - description = userDetails.fullname; - #home = userDetails.home.root; - isNormalUser = true; - hashedPasswordFile = config.sops.secrets.hashed-root-password.path; - extraGroups = [ "wheel" "nixbld" ]; - }; -} diff --git a/modules/root/pipewire.nix b/modules/root/pipewire.nix deleted file mode 100644 index fd97d42..0000000 --- a/modules/root/pipewire.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ lib, config, ... }: { - options = { - pipewire.enable = lib.mkEnableOption "enables pipewire"; - }; - - config = lib.mkIf config.pipewire.enable { - services.pipewire = { - enable = true; - pulse.enable = true; - }; - }; -} diff --git a/modules/root/powerkeys.nix b/modules/root/powerkeys.nix deleted file mode 100644 index fd4aa00..0000000 --- a/modules/root/powerkeys.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ - services.logind = { - hibernateKey = "suspend"; - powerKey = "ignore"; - powerKeyLongPress = "poweroff"; - }; -} diff --git a/modules/root/resources/secrets/secrets.yaml b/modules/root/resources/secrets/secrets.yaml deleted file mode 100644 index 03f9517..0000000 --- a/modules/root/resources/secrets/secrets.yaml +++ /dev/null @@ -1,22 +0,0 @@ -wpa_supplicant-conf: ENC[AES256_GCM,data:0FI1Re1PbiJmtsqb5Ddj1g/e22FkSOxHtbhchybFJAn1Q6PBYpMM/myMUQqZDqCNDhR8f+b2LYcrFx+c9g+yDsR9VcgVe/NK1U5jvep5go3JIibR0NmtfPVZNMvThmVnzO+6aGtggjN8PTx4nm+GKzf7YZPV/buYRdWExJRf0loXgNM8iLtjnu1QGZjWNBtFGbTRHeiax1QhvPrawp76PNrdpzD3EkY26HZ2TRfXFP8ta93T43sac9iVj+U3ggn9MTNvLDGiOF1lAN/W69EeCnyzw3sbxCsuSYFQ4GKkggaehje58VpsG7rZMIHYhI6PQcctO6WUupBi80KcBCnQQKy0Pir7GUPwhw1NPKuOgTX9Otz1dXxJgNk+gA4NTmyCWh2LO0utW0bjAAoqhn5sti5TssxqT2q+bw8KiAqRMZKyj8JDKg4cpMCVUbSH4fcIK1ADXa0OzItgVZnhEKJeD0SYqScXoRlExiDxHG/yrYuJYKtUQSrYJXjJJcTreTyMXWUh1E/nvS7egnXFMYYxDdTvJ5bQ/Zp94FI+twhNfMxKF5qo2gcGUHjQu/M=,iv:LKr6fcQ2emSjQmEt1HgyLpFLg4ZxDOVgJEfkm4nQzbY=,tag:M+oo8dpWclIRaPyW17Ldwg==,type:str] -hashed-root-password: ENC[AES256_GCM,data:KUoB8Z0ifh7lE9ir9AqkiMRHfw6rusXw3KC1dLIRd4YpbTiNI+cAdC474LR721+LNWoj5ZytSdDsVyS+t3o076rV4sgWgL17jPPf+H2KE5FOmQKYTUiHfSBsLKyyhpie4tpFJWv/3cCW8Q==,iv:0sZPz3V7IqTGbF3Fnm+FbgBS3GTnHsRx0OzIoAE1H64=,tag:H6CQlANfiD6ZuQhONKyMAQ==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvTUlBZXQ5Z080UUxoUGdr - dm9hRE5uTzFwWXhOWkJnbXNvazd1UnplcUdZCnRKQ3RVT1RGZURLYUxINStBSU4x - bUZudFp2SC9DSkVhNTRHV0MrRFFMckEKLS0tIGNBb3FLQVJsTGVsY3hMdy94WWZx - UkJ1SGJrWXNtbmlmc2c4M1IxdUpVOWMKjaakq+n8ZijGjaNVM8/dQApaVFp9+q3K - nhvon4p5KUFE+myABnEknaSZ5UcvW6ZLff9AB7l35NZhGXAhv+y6HA== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-01-09T16:42:38Z" - mac: ENC[AES256_GCM,data:LUBRGB/NdT2Lvrecb4w3Xbq4ulMyhHwNjuGyH/fjFJOcNfOCNmwaxIRN59CBi65UxGe93mgYYKJtbCKUZA9JhEfC81e+wkD0ZpEaNBu2YAYetf6hE9LqlYO05QIf/qwXySkCXRKdDl5afcmBVXTj+6qDEljkGtWX7CPLlodvuSQ=,iv:EfYL215e52Ir3SSTba7WGFSTQHgtqzyfWUWTBS+lwrU=,tag:VjE1o7WCT/PWIxk2/b/eow==,type:str] - pgp: [] - unencrypted_suffix: _unencrypted - version: 3.9.2 diff --git a/modules/root/resources/x11/xinit-startx-xdg.patch b/modules/root/resources/x11/xinit-startx-xdg.patch deleted file mode 100644 index c1bca97..0000000 --- a/modules/root/resources/x11/xinit-startx-xdg.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/startx.cpp b/startx.cpp -index dfbebe1..472a1b0 100644 ---- a/startx.cpp -+++ b/startx.cpp -@@ -272,7 +272,7 @@ if [ x"$enable_xauth" = x1 ] ; then - dummy=0 - - XCOMM create a file with auth information for the server. ':0' is a dummy. -- xserverauthfile=$HOME/.serverauth.$$ -+ xserverauthfile="${XAUTHORITY:-$HOME/.Xauthority}" - trap "rm -f '$xserverauthfile'" HUP INT QUIT ILL TRAP BUS TERM - xauth -q -f "$xserverauthfile" << EOF - add :$dummy . $mcookie - diff --git a/modules/root/software/awesome.nix b/modules/root/software/awesome.nix deleted file mode 100644 index fdc86e8..0000000 --- a/modules/root/software/awesome.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - xserver.awesome.enable = lib.mkEnableOption "enables awesomewm"; - }; - - config = lib.mkIf (config.xserver.enable && config.xserver.awesome.enable ) { - services.xserver.windowManager.awesome = { - enable = true; - noArgb = true; # disables transparency. why not? - luaModules = with pkgs.luajitPackages; [ - lgi - ]; - package = with pkgs; awesome.override { - gtk3Support = true; - gtk3 = gtk3; - lua = luajit; - }; - }; - }; -} diff --git a/modules/root/software/default.nix b/modules/root/software/default.nix deleted file mode 100644 index 8d1e987..0000000 --- a/modules/root/software/default.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ lib, config, ... }: { - imports = [ - ./awesome.nix - ./ddcutil.nix - ./desktop.nix - ./development.nix - ./docker.nix - ./system.nix - ./utilities.nix - ./virtualisation.nix - ]; - - software.desktop = { - enable = lib.mkDefault config.xserver.enable; - chromium.enable = lib.mkDefault false; - cad.enable = lib.mkDefault false; - crypto.enable = lib.mkDefault false; - firefox.enable = lib.mkDefault true; - graphics.enable = lib.mkDefault false; - office.enable = lib.mkDefault false; - utilities.enable = lib.mkDefault false; - }; - - software.development = { - enable = lib.mkDefault true; - docker = { - enable = lib.mkDefault false; - btrfsSupport = lib.mkDefault true; - }; - }; - - virtualisation.enable = lib.mkDefault false; - xserver.awesome.enable = lib.mkDefault true; -} diff --git a/modules/root/software/derivations b/modules/root/software/derivations deleted file mode 120000 index a075779..0000000 --- a/modules/root/software/derivations +++ /dev/null @@ -1 +0,0 @@ -../../../derivations
\ No newline at end of file diff --git a/modules/root/software/desktop.nix b/modules/root/software/desktop.nix deleted file mode 100644 index 88101d8..0000000 --- a/modules/root/software/desktop.nix +++ /dev/null @@ -1,79 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - software.desktop = { - enable = lib.mkEnableOption "enables desktop apps"; - chromium = { - enable = lib.mkEnableOption "enables selected chromium browser package"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.ungoogled-chromium; - description = "chromium package to install"; - }; - }; - cad.enable = lib.mkEnableOption "enables cad and 3d printing apps"; - crypto.enable = lib.mkEnableOption "enables crypto wallet apps"; - firefox.enable = lib.mkEnableOption "enables firefox"; - graphics.enable = lib.mkEnableOption "enables graphic design apps"; - office.enable = lib.mkEnableOption "enables office apps"; - utilities.enable = lib.mkEnableOption "enables miscellaneous utility apps"; - }; - }; - - config = lib.mkIf config.software.desktop.enable { - environment.systemPackages = with pkgs; [ - # Default - arandr - dmenu - libnotify - mpv - pavucontrol - pcmanfm - redshift - scrot - sxiv - wpa_supplicant_gui - zathura - (callPackage ./derivations/st {}) - ] ++ pkgs.lib.optionals config.software.desktop.chromium.enable [ - # Chrome - config.software.desktop.chromium.package - ] ++ pkgs.lib.optionals config.software.desktop.cad.enable [ - # CAD - blender - freecad - prusa-slicer - ] ++ pkgs.lib.optionals config.software.desktop.crypto.enable [ - # Crypto Wallets - bisq2 - electrum - monero-gui - ] ++ pkgs.lib.optionals config.software.desktop.graphics.enable [ - # Graphics - blender - geeqie - gimp - inkscape - ] ++ pkgs.lib.optionals config.software.desktop.office.enable [ - # Office - libreoffice - pdfchain - thunderbird - ] ++ pkgs.lib.optionals config.software.desktop.utilities.enable [ - # Misc Utilities - jellyfin-mpv-shim - qbittorrent - qdirstat - remmina - ]; - - programs.localsend.enable = config.software.desktop.utilities.enable; # Installs & opens firewall - programs.firefox.enable = config.software.desktop.firefox.enable; - - # GVfs allows for mounting drives in a graphical file manager - services.gvfs.enable = true; - - # For home-manager to configure gtk - # TODO this should be there instead - programs.dconf.enable = config.software.desktop.enable; - }; -} diff --git a/modules/root/software/development.nix b/modules/root/software/development.nix deleted file mode 100644 index 2a4dfba..0000000 --- a/modules/root/software/development.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - software.development.enable = lib.mkEnableOption "enables development tools"; - }; - - config = lib.mkIf config.software.development.enable { - environment.systemPackages = with pkgs; [ - adb-sync - android-tools - gcc - git - gnumake - hugo - lua - pkg-config - ]; - }; -} diff --git a/modules/root/software/docker.nix b/modules/root/software/docker.nix deleted file mode 100644 index bf1898c..0000000 --- a/modules/root/software/docker.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ pkgs, lib, config, userDetails, ... }: { - options = { - software.development.docker = { - enable = lib.mkEnableOption "enables docker"; - btrfsSupport = lib.mkEnableOption "changes docker storageDriver to btrfs"; - }; - }; - - config = lib.mkIf config.software.development.docker.enable { - virtualisation.docker = { - enable = true; - storageDriver = lib.mkIf config.software.development.docker.btrfsSupport "btrfs"; - }; - - environment.systemPackages = with pkgs; [ - docker-compose - ]; - - users.groups.docker.members = [ userDetails.username ]; - }; -} diff --git a/modules/root/software/system.nix b/modules/root/software/system.nix deleted file mode 100644 index f0a31f2..0000000 --- a/modules/root/software/system.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs, ... }: { - environment.systemPackages = with pkgs; [ - age # Secrets - cryptsetup - dash # TODO should be default /bin/sh - exfat - git # Needed for home-manager - python3 - sops # Secrets - ]; - - services.gpm.enable = true; -} diff --git a/modules/root/software/virtualisation.nix b/modules/root/software/virtualisation.nix deleted file mode 100644 index 4ae15f5..0000000 --- a/modules/root/software/virtualisation.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ lib, config, ... }: { - options = { - virtualisation.enable = lib.mkEnableOption "enables virtualisation and virt-manager"; - }; - - config = lib.mkIf config.virtualisation.enable { - virtualisation.libvirtd.enable = true; - programs.virt-manager.enable = config.software.desktop.enable; - }; -} diff --git a/modules/root/ssh.nix b/modules/root/ssh.nix deleted file mode 100644 index 9f6d54d..0000000 --- a/modules/root/ssh.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - services.openssh = { - enable = true; - settings = { - X11Forwarding = true; - }; - }; -} diff --git a/modules/root/tlp.nix b/modules/root/tlp.nix deleted file mode 100644 index 3414c03..0000000 --- a/modules/root/tlp.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ lib, config, ... }: { - options = { - tlp.enable = lib.mkEnableOption "enables tlp"; - }; - - config = lib.mkIf config.tlp.enable { - services.tlp.enable = true; - }; -} diff --git a/modules/root/wifi.nix b/modules/root/wifi.nix deleted file mode 100644 index 542cfd7..0000000 --- a/modules/root/wifi.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ lib, config, ... }: { - options = { - wifi.enable = lib.mkEnableOption "enables wifi"; - }; - - config = lib.mkIf config.wifi.enable { - networking.wireless = { - enable = true; # Enables wireless support via wpa_supplicant. - userControlled.enable = true; - allowAuxiliaryImperativeNetworks = true; # Networks defined in aux imperitive networks (/etc/wpa_supplicant.conf) - }; - - # Link /etc/wpa_supplicant.conf -> secret config - environment.etc."wpa_supplicant.conf" = { - source = config.sops.secrets.wpa_supplicant-conf.path; - }; - }; -} diff --git a/modules/root/x11.nix b/modules/root/x11.nix deleted file mode 100644 index fd15c52..0000000 --- a/modules/root/x11.nix +++ /dev/null @@ -1,30 +0,0 @@ -{ pkgs, lib, config, ... }: { - options = { - xserver.enable = lib.mkEnableOption "enables xserver"; - }; - - config = lib.mkIf config.xserver.enable { - services.xserver.enable = true; - services.xserver.displayManager.startx.enable = true; - services.libinput.enable = true; # Enable touchpad support - - environment.systemPackages = with pkgs; [ - unclutter - xcape - xclip - xdotool - xorg.setxkbmap - xorg.xinput - xorg.xkill - xorg.xrandr - xorg.xset - xwallpaper - # Patch startx to be compliant with xdg base dir spec - (xorg.xinit.overrideAttrs (old: rec { - patches = [ - ./resources/x11/xinit-startx-xdg.patch - ]; - })) - ]; - }; -} diff --git a/modules/root/zsh.nix b/modules/root/zsh.nix deleted file mode 100644 index 697cb4c..0000000 --- a/modules/root/zsh.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ lib, config, pkgs, ... }: { - options = { - zsh.enable = lib.mkEnableOption "use zsh as default shell"; - }; - - config = lib.mkIf config.zsh.enable { - programs.zsh.enable = true; - users.defaultUserShell = pkgs.zsh; - }; -} diff --git a/nixos/bootloader.nix b/nixos/bootloader.nix new file mode 100644 index 0000000..333f584 --- /dev/null +++ b/nixos/bootloader.nix @@ -0,0 +1,60 @@ +{ lib, config, ... }: let + cfg = config.boot._loader; +in { + options.boot._loader = { + enable = lib.mkEnableOption "enable unified bootloader config"; + loader = lib.mkOption { + type = lib.types.enum [ "grub" "systemd-boot" ]; + default = "systemd-boot"; + description = "whether to install grub or systemd-boot as the bootloader"; + }; + mode = lib.mkOption { + type = lib.types.enum [ "efi" "bios" ]; + default = "efi"; + description = "whether to install the bootloader in efi or bios mode"; + }; + grub = { + biosDevice = lib.mkOption { + type = lib.types.str; + description = "device to install grub on"; + }; + zfsSupport = lib.mkEnableOption "zfs boot device support"; + }; + memtest86.enable = lib.mkEnableOption "make Memtest86+ available from the bootloader"; + }; + + config = let + usingEfi = cfg.mode == "efi"; + usingBios = cfg.mode == "bios"; + usingGrub = cfg.loader == "grub"; + usingSystemdBoot = cfg.loader == "systemd-boot"; + in lib.mkIf cfg.enable { + boot.loader = { + grub = lib.mkIf usingGrub { + enable = true; + # bios + device = lib.mkIf usingBios cfg.grub.biosDevice; + # efi + efiSupport = usingEfi; + efiInstallAsRemovable = usingEfi; + # zfs + zfsSupport = cfg.grub.zfsSupport; + mirroredBoots = lib.mkIf cfg.grub.zfsSupport [ + { devices = [ "nodev" ]; path = "/boot"; } + ]; + # misc + enableCryptodisk = true; + memtest86.enable = cfg.memtest86.enable; + }; + systemd-boot = lib.mkIf usingSystemdBoot { + enable = true; + editor = false; + memtest86.enable = cfg.memtest86.enable; + }; + efi = lib.mkIf usingEfi { + efiSysMountPoint = lib.mkIf usingGrub "/boot/efi"; + canTouchEfiVariables = lib.mkIf usingSystemdBoot true; + }; + }; + }; +} diff --git a/nixos/decklink.nix b/nixos/decklink.nix new file mode 100644 index 0000000..d86fde3 --- /dev/null +++ b/nixos/decklink.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, ... }: let + cfg = config.services._decklink; +in { + options.services._decklink = { + enable = lib.mkEnableOption "enables decklink support"; + decklinkSupport.enable = lib.mkEnableOption "enable decklinkSupport flag for all packages that support it by default"; + }; + + config = lib.mkIf cfg.enable { + # Kernel modules + hardware.decklink.enable = true; + + # DesktopVideoHelper + environment.systemPackages = [ pkgs.blackmagic-desktop-video ]; + + # Enable decklinkSupport flag globally for all packages that support it (e.g. obs) + nixpkgs.overlays = [ + (final: prev: { decklinkSupport = cfg.decklinkSupport.enable; }) + ]; + }; +} diff --git a/nixos/default.nix b/nixos/default.nix new file mode 100644 index 0000000..ea8a317 --- /dev/null +++ b/nixos/default.nix @@ -0,0 +1,28 @@ +{ + imports = [ + ./programs/awesome.nix + ./programs/ddcutil.nix + + ./services/cgit.nix + ./services/fileshares.nix + ./services/mailer.nix + ./services/searxng.nix + ./services/zfs/zed-mailer.nix + ./services/router + + #./services/mainsail.nix + + ./bootloader.nix + ./decklink.nix + ./geoclue.nix + ./hosts.nix + ./net-iface-labels.nix + ./nix.nix + ./polkit.nix + ./powerkeys.nix + ./secrets.nix + ./suspend.nix + ./wireless.nix + ./zshenv.nix + ]; +} diff --git a/nixos/geoclue.nix b/nixos/geoclue.nix new file mode 100644 index 0000000..344ef24 --- /dev/null +++ b/nixos/geoclue.nix @@ -0,0 +1,14 @@ +{ config, lib, ... }: let + cfg = config.services._geoclue2; +in { + options.services._geoclue2 = { + enable = lib.mkEnableOption "enable geoclue2 service with minimal sources and set timezone automatically"; + }; + + config = lib.mkIf cfg.enable { + services.geoclue2 = { + enable = true; + }; + services.automatic-timezoned.enable = lib.mkDefault true; + }; +} diff --git a/nixos/hosts.nix b/nixos/hosts.nix new file mode 100644 index 0000000..a87f3b4 --- /dev/null +++ b/nixos/hosts.nix @@ -0,0 +1,4 @@ +{ hostname, ... }: { + networking.hostName = hostname; # From flake.nix + environment.etc.hosts.mode = "0644"; # Allow temporary imperative modifications +} diff --git a/nixos/net-iface-labels.nix b/nixos/net-iface-labels.nix new file mode 100644 index 0000000..b7ac655 --- /dev/null +++ b/nixos/net-iface-labels.nix @@ -0,0 +1,28 @@ +{ config, lib, ... }: let + cfg = config.networking._interfaceLabels; + validMac = str: builtins.match ''^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$'' str != null; + macAddressType = lib.types.str // { + check = validMac; + description = "a mac address (xx:xx:xx:xx:xx:xx)"; + }; +in { + options.networking._interfaceLabels = { + enable = lib.mkEnableOption "manually label network interfaces"; + interfaces = lib.mkOption { + type = lib.types.attrsOf macAddressType; + default = {}; + example = { + "lan0" = "00:11:22:33:44:55"; + "lan1" = "66:77:88:99:AA:BB"; + }; + description = "label network interfaces by their mac address"; + }; + }; + + config = lib.mkIf cfg.enable { + networking.usePredictableInterfaceNames = false; + services.udev.extraRules = lib.concatStringsSep "\n" ( + lib.mapAttrsToList (name: mac: ''ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="${mac}", NAME="${name}"'') cfg.interfaces + ); + }; +} diff --git a/modules/root/nix.nix b/nixos/nix.nix index ff8dd28..ff8dd28 100644 --- a/modules/root/nix.nix +++ b/nixos/nix.nix diff --git a/nixos/polkit.nix b/nixos/polkit.nix new file mode 100644 index 0000000..d78130a --- /dev/null +++ b/nixos/polkit.nix @@ -0,0 +1,48 @@ +{ lib, config, pkgs, ... }: let + cfg = config.security.polkit; + + # This authentication agent will only autostart in a select few environments (e.g. GNOME, XFCE) by default. + # This derivation will allow the polkit_gnome agent to start in any environment so long as it is enabled. + polkit_gnome-autostart = pkgs.stdenv.mkDerivation { + name = "polkit_gnome-autostart"; + priority = 5; + + # Copy the autostart desktop entry and replace OnlyShowIn with NotShowIn to invert the selection. + # The default one will still select the same environments, but this new one will select the inverse, + # e.g. any environment that is not listed in the stock desktop entry. + buildCommand = '' + mkdir -p $out/etc/xdg/autostart + cp ${cfg._gnome.package}/etc/xdg/autostart/polkit-gnome-authentication-agent-1.desktop $out/etc/xdg/autostart/polkit-gnome-authentication-agent-1-de-agnostic.desktop + substituteInPlace $out/etc/xdg/autostart/polkit-gnome-authentication-agent-1-de-agnostic.desktop \ + --replace-fail 'OnlyShowIn=' 'NotShowIn=' + ''; + }; +in { + options.security.polkit = { + _gnome = { + enable = lib.mkEnableOption "enables polkit_gnome authentication agent"; + package = lib.mkPackageOption pkgs "polkit_gnome" { }; + }; + _allowUserPowerControls = lib.mkEnableOption "allow users to control the power state of the machine using poweroff/reboot commands"; + _wheelNoPass = lib.mkEnableOption "allow users in wheel group to execute all commands without password"; + }; + + config = lib.mkIf cfg.enable { + # Gnome + environment.systemPackages = lib.mkIf cfg._gnome.enable [ cfg._gnome.package polkit_gnome-autostart ]; + + # Optional rules + environment.etc = let + pkRulesD = "polkit-1/rules.d"; + in { + "${pkRulesD}/10-reboot-poweroff-users.rules" = { + enable = cfg._allowUserPowerControls; + source = ./resources/polkit-1/rules.d/10-reboot-poweroff-users.rules; + }; + "${pkRulesD}/0-wheel-no-pass.rules" = { + enable = cfg._wheelNoPass; + source = ./resources/polkit-1/rules.d/0-wheel-no-pass.rules; + }; + }; + }; +} diff --git a/nixos/powerkeys.nix b/nixos/powerkeys.nix new file mode 100644 index 0000000..040cfa8 --- /dev/null +++ b/nixos/powerkeys.nix @@ -0,0 +1,14 @@ +{ lib, config, ... }: let + cfg = config.services.logind._safePowerKey; +in { + options.services.logind._safePowerKey = { + enable = lib.mkEnableOption "do not immediately shutdown when powerkey is pressed"; + }; + + config = lib.mkIf cfg.enable { + services.logind.settings.Login = { + HandlePowerKey = "ignore"; # Allow for powerkey to activate power script + HandlePowerKeyLongPress = "poweroff"; + }; + }; +} diff --git a/nixos/programs/awesome.nix b/nixos/programs/awesome.nix new file mode 100644 index 0000000..580aec1 --- /dev/null +++ b/nixos/programs/awesome.nix @@ -0,0 +1,23 @@ +{ pkgs, lib, config, ... }: let + cfg = config.services.xserver.windowManager._awesome; +in { + options.services.xserver.windowManager._awesome = { + enable = lib.mkEnableOption "installs awesome window manager"; + }; + + config = lib.mkIf cfg.enable { + services.xserver.windowManager.awesome = { + # TODO use git version of awesome, maybe version on archlinux, notify when new version available + enable = true; + noArgb = true; # disables transparency. why not? + luaModules = with pkgs.luajitPackages; [ + lgi + ]; + package = with pkgs; awesome.override { + gtk3Support = true; + gtk3 = gtk3; + lua = luajit; + }; + }; + }; +} diff --git a/nixos/programs/ddcutil.nix b/nixos/programs/ddcutil.nix new file mode 100644 index 0000000..ef1801b --- /dev/null +++ b/nixos/programs/ddcutil.nix @@ -0,0 +1,12 @@ +{ pkgs, lib, config, ... }: let + cfg = config.programs._ddcutil; +in { + options.programs._ddcutil = { + enable = lib.mkEnableOption "enables ddcutil and i2c control"; + }; + + config = lib.mkIf cfg.enable { + hardware.i2c.enable = true; + environment.systemPackages = [ pkgs.ddcutil ]; + }; +} diff --git a/nixos/resources/polkit-1/rules.d/0-wheel-no-pass.rules b/nixos/resources/polkit-1/rules.d/0-wheel-no-pass.rules new file mode 100644 index 0000000..93abf0d --- /dev/null +++ b/nixos/resources/polkit-1/rules.d/0-wheel-no-pass.rules @@ -0,0 +1,5 @@ +polkit.addRule(function(action, subject) { + if (subject.isInGroup("wheel")) { + return polkit.Result.YES; + } +}); diff --git a/nixos/resources/polkit-1/rules.d/10-reboot-poweroff-users.rules b/nixos/resources/polkit-1/rules.d/10-reboot-poweroff-users.rules new file mode 100644 index 0000000..ed9dbf7 --- /dev/null +++ b/nixos/resources/polkit-1/rules.d/10-reboot-poweroff-users.rules @@ -0,0 +1,17 @@ +polkit.addRule(function (action, subject) { + if ( + subject.isInGroup("users") && + [ + "org.freedesktop.login1.reboot", + "org.freedesktop.login1.reboot-multiple-sessions", + "org.freedesktop.login1.power-off", + "org.freedesktop.login1.power-off-multiple-sessions", + "org.freedesktop.login1.suspend", + "org.freedesktop.login1.suspend-multiple-sessions", + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + ].indexOf(action.id) !== -1 + ) { + return polkit.Result.YES; + } +}); diff --git a/modules/root/secrets.nix b/nixos/secrets.nix index 47262fd..3d21e62 100644 --- a/modules/root/secrets.nix +++ b/nixos/secrets.nix @@ -1,25 +1,17 @@ -{ pkgs, inputs, config, userDetails, ... }: { - imports = [ inputs.sops-nix.nixosModules.sops ]; +{ inputs, config, pkgs, ... }: { + environment.systemPackages = with pkgs; [ age sops ]; sops = { - defaultSopsFile = ./resources/secrets/secrets.yaml; defaultSopsFormat = "yaml"; - age.keyFile = "${userDetails.home.root}/.config/sops/age/keys.txt"; - - secrets = { - wpa_supplicant-conf = { }; - hashed-root-password = { }; - }; + age.sshKeyPaths = [ "/root/.ssh/id_ed25519" ]; }; + # This service is a workaround to ensure that secrets are available on # reboot when the secret keys are on a separate subvolume systemd.services.npcnix-force-rebuild-sops-hack = { wantedBy = [ "multi-user.target" ]; - before = [ - # List all services that require secrets - "wpa_supplicant.service" - ]; + before = [ "wpa_supplicant.service" ]; serviceConfig = { ExecStart = "/run/current-system/activate"; Type = "oneshot"; diff --git a/nixos/services/cgit.nix b/nixos/services/cgit.nix new file mode 100644 index 0000000..66de1d8 --- /dev/null +++ b/nixos/services/cgit.nix @@ -0,0 +1,142 @@ +{ lib, pkgs, config, ... }: +let + cfg = config.services._cgit; +in { + options.services._cgit = { + enable = lib.mkEnableOption "enables cgit service"; + hostAddress = lib.mkOption { + type = lib.types.str; + description = "hostAddress for the container"; + default = "10.0.1.1"; + }; + localAddress = lib.mkOption { + type = lib.types.str; + description = "localAddress for the container"; + default = "10.0.1.2"; + }; + rootTitle = lib.mkOption { + type = lib.types.str; + description = "cgit site title"; + default = ""; + }; + rootDesc = lib.mkOption { + type = lib.types.str; + description = "cgit site description"; + default = ""; + }; + extraConfig = lib.mkOption { + type = lib.types.str; + description = "cgitrc lines inserted verbatim at the end"; + default = ""; + }; + ssh.authorizedKeys = { + keys = lib.mkOption { + type = lib.types.listOf lib.types.singleLineStr; + default = [ ]; + description = '' + see `options.openssh.authorizedKeys.keys`. + ssh authorized keys for git user in container. + ''; + }; + keyFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + description = '' + see `options.openssh.authorizedKeys.keyFiles`. + ssh authorized keys for git user in container. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # Configure cgit container + containers.cgit = { + autoStart = true; + privateNetwork = true; + hostAddress = cfg.hostAddress; + localAddress = cfg.localAddress; + + specialArgs = { + authorizedKeys = cfg.ssh.authorizedKeys; + cgitrc = with cfg; { + inherit rootTitle; + inherit rootDesc; + inherit extraConfig; + }; + }; + + config = { lib, config, authorizedKeys, cgitrc, ... }: { + # Create git user for ssh access + users.users.git = { + isNormalUser = true; + home = "/srv/git"; # Serve from git user's home to allow cloning git@cgit:repo + group = "git"; + createHome = true; + homeMode = "750"; # Allow read permissions for group members + shell = pkgs.bash; + openssh = { inherit authorizedKeys; }; + }; + users.groups.git.members = [ "lighttpd" ]; # Create the git group and add lighttpd user as a member so /srv/git can be served by cgit + + # Enable git + programs.git.enable = true; + + # Enable ssh service + services.openssh.enable = true; + + # Enable cgit service + services.lighttpd.enable = true; + services.lighttpd.cgit = { + enable = true; + #subdir = ""; # FIXME this does not work for some reason + configText = '' + # Based on joseluisq/alpine-cgit + root-title=${cgitrc.rootTitle} + root-desc=${cgitrc.rootDesc} + + source-filter=${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py + about-filter=${pkgs.cgit}/lib/cgit/filters/about-formatting.sh + + readme=:README.md + readme=:README.html + readme=:README.txt + readme=:README + readme=:INSTALL.md + readme=:INSTALL.html + readme=:INSTALL.txt + readme=:INSTALL + + # Cache + #cache-root=/var/cache/cgit + #cache-size=2000 + + enable-index-links=1 + enable-index-owner=0 + enable-remote-branches=1 + enable-log-filecount=1 + enable-log-linecount=1 + enable-git-config=1 + snapshots=tar.xz zip + + robots=noindex, nofollow + + virtual-root=/cgit + section-from-path=0 + max-repo-count=100 + scan-path=/srv/git + + # extra config + ${cgitrc.extraConfig} + ''; + }; + + # Networking, etc. + networking.firewall.allowedTCPPorts = [ 80 22 ]; + networking.hostName = "cgit"; + + system.stateVersion = "25.05"; + }; + }; + }; +} diff --git a/nixos/services/fileshares.nix b/nixos/services/fileshares.nix new file mode 100644 index 0000000..e61797f --- /dev/null +++ b/nixos/services/fileshares.nix @@ -0,0 +1,150 @@ +{ lib, config, ... }: let + cfg = config.services._fileShares; + + fileShareType = lib.types.submodule ( + { name, ... }: { + options = { + enable = lib.mkEnableOption "enable this file share"; # FIXME mk default + name = lib.mkOption { + type = lib.types.str; + default = name; + description = "name of share"; + }; + path = lib.mkOption { + type = lib.types.str; + default = name; + description = "path to share"; + }; + readOnly = lib.mkEnableOption "make share read only"; + allowHosts = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + example = [ "192.168.1.100" "192.168.0.0/24" ]; + description = "ip hosts to allow"; + }; + # TODO denyHosts etc + nfs = { + enable = lib.mkEnableOption "share file with nfs protocol"; + }; + smb = { + enable = lib.mkEnableOption "share file with smb protocol"; + allowGuests = lib.mkEnableOption "allow unauthenticated users to mount"; + allowUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "allow user"; + }; + allowGroup = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "allow group"; + }; + extraOptions = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + description = "extra smb options for this share"; + }; + }; + }; + } + ); + + # Shares + sambaShares = lib.filterAttrs (_: s: s.smb.enable) cfg.shares; + nfsShares = lib.filterAttrs (_: s: s.nfs.enable) cfg.shares; + + # SMB + sambaGlobalSettings.global = let + hostname = config.networking.hostName; + in { + "workgroup" = lib.mkDefault "WORKGROUP"; + "server string" = lib.mkDefault hostname; + "netbios name" = lib.mkDefault hostname; + "security" = lib.mkDefault "user"; + #"use sendfile" = "yes"; + #"max protocol" = "smb2"; + "hosts allow" = lib.mkDefault "192.168.1. 127.0.0.1 localhost"; # TODO does this work? + "hosts deny" = lib.mkDefault "0.0.0.0/0"; + "guest account" = lib.mkDefault "nobody"; + "map to guest" = lib.mkDefault "bad user"; + }; + mkSambaShare = s: { + "path" = s.path; + "browsable" = lib.boolToYesNo true; + "read only" = lib.boolToYesNo s.readOnly; + "guest ok" = lib.boolToYesNo s.smb.allowGuests; + "create mask" = "0644"; # TODO configure + "directory mask" = "0755"; # TODO configure + # allow user/group + "force user" = lib.mkIf (s.smb.allowUser != null) s.smb.allowUser; + "force group" = lib.mkIf (s.smb.allowGroup != null) s.smb.allowGroup; + # allow hosts + "hosts deny" = lib.mkIf (s.allowHosts != []) "ALL"; + "hosts allow" = lib.mkIf (s.allowHosts != []) (lib.concatStringsSep " " s.allowHosts); + } // s.smb.extraOptions; + sambaSettings = sambaGlobalSettings // lib.mapAttrs (_: v: mkSambaShare v) sambaShares; + + # NFS + mkNFSShareHost = s: host: ''${host}(${if s.readOnly then "ro" else "rw"},sync,no_subtree_check)''; # TODO configure + mkNFSShare = s: '' + ${s.path} ${lib.concatMapStringsSep " " (mkNFSShareHost s) (if s.allowHosts == [] then ["*"] else s.allowHosts) } + ''; + nfsExports = lib.concatMapAttrsStringSep "\n" (_: v: mkNFSShare v) nfsShares; +in { + options.services._fileShares = { + enable = lib.mkEnableOption "protocol agnostic fileshares module"; + shares = lib.mkOption { + type = lib.types.attrsOf fileShareType; + default = {}; + description = "nfs/smb fileshares"; + }; + smb = { + openFirewall = lib.mkEnableOption "open firewall for smb"; + }; + nfs = { + enableNFSv3 = lib.mkEnableOption "enable support for nfsv3"; + openFirewall = lib.mkEnableOption "open firewall for nfs(v4/v3)"; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + # SMB config + (lib.mkIf (sambaShares != []) { + services.samba = { + enable = true; + openFirewall = lib.mkDefault cfg.smb.openFirewall; + settings = sambaSettings; + }; + }) + + # NFS(v4/v3) config + (lib.mkIf (nfsShares != []) { + services.nfs.server = lib.mkMerge [ + # Base config + { + enable = true; + exports = nfsExports; + } + + # NFSv3 additional config + (lib.mkIf cfg.nfs.enableNFSv3 { + # fixed rpc.statd port; for firewall + lockdPort = 4001; + mountdPort = 4002; + statdPort = 4000; + extraNfsdConfig = ''''; + }) + ]; + + # NFS firewall + networking.firewall = lib.mkIf cfg.nfs.openFirewall ( + if cfg.nfs.enableNFSv3 then { + allowedTCPPorts = [ 111 2049 4000 4001 4002 20048 ]; + allowedUDPPorts = [ 111 2049 4000 4001 4002 20048 ]; + } else { + allowedTCPPorts = [ 2049 ]; + } + ); + }) + ]); +} diff --git a/nixos/services/mailer.nix b/nixos/services/mailer.nix new file mode 100644 index 0000000..fadaaf1 --- /dev/null +++ b/nixos/services/mailer.nix @@ -0,0 +1,67 @@ +{ lib, pkgs, config, ... }: let + cfg = config.services.mail._mailer; +in { + options.services.mail._mailer = { + enable = lib.mkEnableOption "enable msmtp mailer service"; + sender = { + host = lib.mkOption { + type = lib.types.str; + description = ""; + example = "smtp.mail.example.com"; + }; + user = lib.mkOption { + type = lib.types.str; + description = ""; + example = "myname@example.com"; + }; + from = lib.mkOption { + type = lib.types.str; + description = ""; + example = "myname@example.com"; + }; + passwordFile = lib.mkOption { + type = lib.types.path; + description = ""; + }; + }; + recipient = lib.mkOption { + type = lib.types.str; + description = ""; + example = "admin@example.com"; + }; + }; + + # https://wiki.nixos.org/wiki/ZFS#Mail_notifications_(ZFS_Event_Daemon) + config = lib.mkIf cfg.enable { + # MTA + programs.msmtp = { + enable = true; + setSendmail = true; + defaults = { + aliases = "/etc/aliases"; + port = 587; + auth = "plain"; + tls = "on"; + tls_starttls = "on"; + }; + accounts = { + default = with cfg.sender; { + inherit host user from; + passwordeval = "cat ${passwordFile}"; + }; + }; + }; + + # Configure an alias for root account. + # With this alias configured, all mails sent to root, such as cron job + # results and failed sudo login events, will be redirected to the + # configured email account. + environment.etc.aliases.text = '' + root: ${cfg.recipient} + ''; + + # For zed enableMail, enable sendmailSetuidWrapper + services.mail.sendmailSetuidWrapper.enable = true; + }; +} + diff --git a/nixos/services/router/default.nix b/nixos/services/router/default.nix new file mode 100644 index 0000000..4e59752 --- /dev/null +++ b/nixos/services/router/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./dns-dhcp.nix + ./hairpinning.nix + ./routing.nix + ./unbound-blocklist.nix + ]; +} diff --git a/nixos/services/router/dns-dhcp.nix b/nixos/services/router/dns-dhcp.nix new file mode 100644 index 0000000..1839b0f --- /dev/null +++ b/nixos/services/router/dns-dhcp.nix @@ -0,0 +1,229 @@ +{ config, lib, ... }: let + cfg = config.services._router.dnsDhcpConfig; + staticLeaseType = lib.types.submodule ( + { name, ... }: { + options = { + enable = lib.mkEnableOption "enable this static lease"; + macAddress = lib.mkOption { + type = lib.types.str; + default = ""; + example = "00:11:22:33:44:55"; + }; + hostname = lib.mkOption { + type = lib.types.str; + default = name; + }; + staticIp = lib.mkOption { + type = lib.types.str; + default = ""; + example = "192.168.1.2"; + }; + }; + + config.enable = lib.mkDefault true; + } + ); +in { + options.services._router.dnsDhcpConfig = { + enable = lib.mkEnableOption "enable pre-configured unbound(outbound) + dnsmasq(local) dns(+dhcp) server"; + #enableUnbound = lib.mkEnableOption "enable unbound as outbound dns server"; + dns = { + hostOverrides = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + example = { + "poweredge.home.lan" = "192.168.1.10"; + "router.home.lan" = "192.168.1.1"; + "example.com" = "192.168.1.100"; + }; + description = "dns overrides for specific hosts"; + }; + }; + dhcp = { + defaultGateway = lib.mkOption { + type = lib.types.str; + default = "192.168.1.1"; + }; + localhostIp = lib.mkOption { + type = lib.types.str; + default = "192.168.1.1"; + description = "ip of the dns+dhcp host"; + }; + rangeStart = lib.mkOption { + type = lib.types.str; + default = "192.168.1.100"; + description = "dhcp range start"; + }; + rangeEnd = lib.mkOption { + type = lib.types.str; + default = "192.168.1.250"; + description = "dhcp range end"; + }; + rangeSubnetMask = lib.mkOption { + type = lib.types.str; + default = "255.255.255.0"; + description = "dhcp range subnet"; + }; + leaseTime = lib.mkOption { + type = lib.types.str; + default = "12h"; + description = "how long a dhcp lease will last. suffix is one of [h,m,s]"; + }; + staticLeases = lib.mkOption { + type = lib.types.attrsOf staticLeaseType; + default = {}; + example = { + hostname0 = { + macAddress = "00:11:22:33:44:55"; + staticIp = "192.168.1.6"; + }; + hostname1 = { + macAddress = "66:77:88:99:AA:BB"; + staticIp = "192.168.1.7"; + }; + }; + description = "dhcp static leases"; + }; + }; + localDomain = lib.mkOption { + type = lib.types.str; + default = "home.lan"; + description = ""; + }; + #ipv6 = lib.mkEnableOption "enable ipv6"; # TODO + }; + + config = lib.mkIf cfg.enable { + # Unbound as a recursive DNS resolver. + # Preferred for its security/privacy features + performance + services.unbound = { + enable = true; + settings = { + server = { + # Listen on all interfaces (or specify specific IPs) + interface = [ "0.0.0.0" ]; # ++ lib.optionals cfg.ipv6 [ "::0" ]; + + # Allow queries from local networks + access-control = [ + "127.0.0.0/8 allow" + "192.168.0.0/16 allow" + "10.0.0.0/8 allow" + "172.16.0.0/12 allow" + ]; + + # Cache settings + cache-min-ttl = 300; # 5 min + cache-max-ttl = 60 * 60 * 24; # 1 day + + # Prefetch DNS records for better performance + prefetch-key = true; + prefetch = true; + + # Enable DNSSEC validation (signed DNS, prevents man-in-the-middle attacks) + # `auto-trust-anchor-file` is set by default to "/var/unbound/root.key" + trust-anchor-file = ''""''; + domain-insecure = cfg.localDomain; # Local domain is not DNSSEC-signed + harden-below-nxdomain = true; # Protect against non-existent domain response attacks + harden-glue = true; # Protect against incorrect DNS glue record attacks + harden-dnssec-stripped = true; # Ensures that DNSSEC signatures are not stripped from DNS responses + + # Allow queries to localhost for dnsmasq local domain resolution + do-not-query-localhost = false; + + # Privacy / Security + harden-referral-path = true; # Prevents receiving data from servers that are outside the zone in the DNS referral chain + qname-minimisation = true; # Minimizes the amount of information exposed in DNS queries by only sending the essential parts of the query + hide-identity = true; # Hide software identity in response + hide-version = true; # Hide unbound version in response + use-caps-for-id = false; # Disables using uppercase characters in the DNS transaction ID, for compatibility + edns-buffer-size = 1232; # Sets the EDNS (Extension mechanisms for DNS) buffer size to 1232 bytes, which is the default for many DNS resolvers + + # Logging + #verbosity = 3; + #log-queries = true; + #log-replies = true; + #log-local-actions = true; + }; + # Forward unknown to public resolver via DoT + forward-zone = [ + # Local DNS: forward to dnsmasq + { + name = ''"${cfg.localDomain}."''; # TODO mk config + forward-addr = "127.0.0.1@5353"; # TODO mk config + } + # Local reverse DNS: forward reverse lookups (PTR records) + { + name = ''"1.168.192.in-addr.arpa"''; # NOTE: 192.168.1.0 -> 1.168.192 + forward-addr = "127.0.0.1@5353"; + } + # Upstream DNS + { + name = ''"."''; + forward-addr = [ + "9.9.9.9#dns.quad9.net" + "149.112.112.112#dns.quad9.net" + ]; + forward-tls-upstream = true; # Encrypted DNS + } + ]; + remote-control.control-enable = true; + }; + }; + + # Configure dnsmasq for dhcp and local hostname resolution + services.dnsmasq = { + enable = true; + settings = let + mkDNSHostOverride = host: ip: "${host},${ip}"; + dnsHostOverrides = lib.mapAttrsToList (host: ip: mkDNSHostOverride host ip) cfg.dns.hostOverrides; + mkDHCPRange = ipRangeStart: ipRangeEnd: subnetMask: leaseTime: "${ipRangeStart},${ipRangeEnd},${subnetMask},${leaseTime}"; + mkDHCPOption = option: value: "option:${option},${value}"; + mkDHCPStaticLease = macAddress: hostname: staticIp: "${macAddress},${hostname},${staticIp},infinite"; + dhcpStaticLeases = lib.mapAttrsToList (_: l: mkDHCPStaticLease l.macAddress l.hostname l.staticIp) cfg.dhcp.staticLeases; + in { + # General + no-resolv = true; # Do not read /etc/resolv.conf, resolve only the LAN + no-poll = true; # Do not poll /etc/resolv.conf for changes + # TODO config local domain + local = "/${cfg.localDomain}/"; # Use local-only for the defined domain (prevents upstream leaks) + domain = cfg.localDomain; # Define the local domain name + expand-hosts = true; # Create both fully-qualified and short-name entries from DHCP hostnames + + # DNS Server + port = 5353; # Use port 5353 for DNS server since unbound is the main DNS resolver + + host-record = dnsHostOverrides; # Setup host overrides for dns + + # DHCP Server + # TODO config + #dhcp-range = mkDHCPRange "192.168.1.50" "192.168.1.150" "255.255.255.0" "12h"; # Enable DHCP on the LAN interface + dhcp-range = with cfg.dhcp; mkDHCPRange rangeStart rangeEnd rangeSubnetMask leaseTime; # Enable DHCP on the LAN interface + + dhcp-host = dhcpStaticLeases; # Setup static leases + + dhcp-option = with cfg.dhcp; [ + (mkDHCPOption "router" defaultGateway) # Set default gateway for clients + #(mkDHCPOption "ntp-server" defaultGateway) # Set ntp server for clients + (mkDHCPOption "dns-server" localhostIp) # Set dns server for clients + (mkDHCPOption "domain-search" cfg.localDomain) # Add search rule to clients so they can resolve hostnames w/o the local domain suffix + ]; + + #dhcp-no-override = true; # don't use kernel DHCP helpers that bypass macvlan + + # Logging + #log-dhcp = true; # Log DHCP events + #log-queries = true; # Log DNS queries + + # Cache + cache-size = 1000; # Small cache limit is fine since Unbound does the heavy caching + }; + }; + + # Search localDomain so host can resolve short names + # This is eq. to dnsmasq's dhcp-option "domain-search" for clients, it just adds a search rule to resolv.conf + networking.search = [ cfg.localDomain ]; + + # Add localhost as default nameserver + networking.nameservers = lib.mkDefault [ cfg.dhcp.localhostIp ]; + }; +} diff --git a/nixos/services/router/hairpinning.nix b/nixos/services/router/hairpinning.nix new file mode 100644 index 0000000..e449525 --- /dev/null +++ b/nixos/services/router/hairpinning.nix @@ -0,0 +1,47 @@ +{ config, lib, ... }: let + cfg = config.networking.nat._hairpinning; + + forwardPorts = config.networking.nat.forwardPorts; + internalInterfaces = config.networking.nat.internalInterfaces; + + # DNAT + mkIfacesMap = ifs: lib.concatMapStringsSep ", " (i: ''"${i}"'') ifs; + mkDnatRule = ifs: proto: sourcePort: destination: '' + iifname { ${mkIfacesMap ifs} } ${proto} dport ${toString sourcePort} dnat to ${destination} + ''; + dnatRules = lib.concatMapStrings (p: mkDnatRule internalInterfaces p.proto p.sourcePort p.destination) forwardPorts; + + # SNAT + mkSnatTargets = proto: destIp: destPort: '' + ip daddr ${destIp} ${proto} dport ${destPort} masquerade + ''; + snatTargets = lib.concatMapStrings(p: let + d = lib.splitString ":" p.destination; + destIp = builtins.elemAt d 0; + destPort = builtins.elemAt d 1; + in mkSnatTargets p.proto destIp destPort) forwardPorts; +in { + options.networking.nat._hairpinning = { + enable = lib.mkEnableOption "enable nat hairpinning (loopback) to allow internal interfaces to reach forwarded services via the external ip"; + }; + config = lib.mkIf (config.networking.nat.enable && cfg.enable) { + warnings = [ + (lib.mkIf (forwardPorts == []) + "nat hairpinning enabled but no forwardPorts have been configured. skipping configuration.") + ]; + + networking.nftables.tables."hairpin" = lib.mkIf (forwardPorts != []) { + family = "ip"; + content = '' + chain pre { + type nat hook prerouting priority dstnat; policy accept; + ${dnatRules} + } + chain post { + type nat hook postrouting priority srcnat; policy accept; + ${snatTargets} + } + ''; + }; + }; +} diff --git a/nixos/services/router/routing.nix b/nixos/services/router/routing.nix new file mode 100644 index 0000000..c86301f --- /dev/null +++ b/nixos/services/router/routing.nix @@ -0,0 +1,39 @@ +{ config, lib, ... }: let + cfg = config.services._router.routing; +in { + options.services._router.routing = { + enable = lib.mkEnableOption "enable nftables routing"; + interfaces = { + wan = lib.mkOption { + type = lib.types.str; + default = ""; + description = "wan interface"; + }; + lan = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = ""; + description = "lan interfaces"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # https://hackers-arise.com/linux-basics-for-hackers-building-a-router-with-nftables/ + networking = { + nftables.enable = true; + firewall = { + enable = true; + backend = "nftables"; # Set explicitly + + # Allow lan interfaces to access the router + trustedInterfaces = cfg.interfaces.lan; + }; + nat = { + enable = lib.mkDefault true; + externalInterface = lib.mkDefault cfg.interfaces.wan; + internalInterfaces = lib.mkDefault cfg.interfaces.lan; + _hairpinning.enable = lib.mkDefault (config.networking.nat.forwardPorts != []); + }; + }; + }; +} diff --git a/nixos/services/router/unbound-blocklist.nix b/nixos/services/router/unbound-blocklist.nix new file mode 100644 index 0000000..27f2a04 --- /dev/null +++ b/nixos/services/router/unbound-blocklist.nix @@ -0,0 +1,82 @@ +{ lib, config, pkgs, ... }: let + cfg = config.services.unbound._blocklists; + hageziList = list: [ + "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/rpz/${list}.txt" + "https://gitlab.com/hagezi/mirror/-/raw/main/dns-blocklists/rpz/${list}.txt" + "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/rpz/${list}.txt" + ]; + hageziBlocklists = lib.listToAttrs ( + map (n: { name = "hagezi_${n}"; value = n; }) cfg.hageziBlocklists + ); + blocklists = hageziBlocklists // cfg.extraBlocklists; +in { + options.services.unbound._blocklists = { + enable = lib.mkEnableOption "enable rpz blocklist generation in unbound"; + hageziBlocklists = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "hagezi blocklists to enable"; + example = [ "pro" "nsfw" ]; + }; + extraBlocklists = lib.mkOption { + type = lib.types.attrsOf (lib.types.listOf lib.types.str); + example = { + hageziNSFW = [ + "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/rpz/nsfw.txt" + "https://gitlab.com/hagezi/mirror/-/raw/main/dns-blocklists/rpz/nsfw.txt" + "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/rpz/nsfw.txt" + ]; + hageziPro = [ + "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/rpz/pro.txt" + "https://gitlab.com/hagezi/mirror/-/raw/main/dns-blocklists/rpz/pro.txt" + "https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/rpz/pro.txt" + ]; + }; + default = {}; + description = "blocklist urls in response policy zone (rpz) format"; + }; + # TODO + #extraBlacklistedDomains = lib.mkOption { + # type = lib.types.listOf lib.types.str; + # example = [ + # "example.com" + # "*.example.com" + # "elpmaxe.com" + # "*.elpmaxe.com" + # ]; + # default = []; + # description = "additional domains to block"; + #}; + #extraWhitelistedDomains = lib.mkOption { + # type = lib.types.listOf lib.types.str; + # example = [ + # "example.com" + # "*.example.com" + # "elpmaxe.com" + # "*.elpmaxe.com" + # ]; + # default = []; + # description = "whitelist domains that would otherwise be blocked"; + #}; + }; + + config = lib.mkIf (cfg.enable && config.services.unbound.enable) { + # Configure rpz + blocklists in unbound + services.unbound.settings = let + # https://unbound.docs.nlnetlabs.nl/en/latest/topics/filtering/rpz.html + rpzEntry = name: url: { inherit name url; rpz-action-override = "nxdomain"; }; # TODO extra attrs option instead of adding rpz-action-override by default + ## Generate extraBlockedDomains + #extraBlockedDomainsRPZ = lib.strings.concatStringsSep "\n" (builtins.map (domain: "${domain} CNAME .")); + #extraBlockedDomainsRPZFile = pkgs.writeText "extraBlockedDomains" '' + # $TTL 300 + # @ SOA localhost. root.localhost. 1 43200 3600 86400 300 + # NS localhost. + # ${extraBlockedDomainsRPZ} + #''; + #extraBlockedDomainsRPZEntries = rpzEntry "extraBlockedDomains" extraBlockedDomainsRPZFile; + rpz = lib.mapAttrsToList rpzEntry blocklists; + in { + server.module-config = ''"respip validator iterator"''; # Adds respip before validator and iterator. Needed for rpz config + inherit rpz; + }; + }; +} diff --git a/nixos/services/searxng.nix b/nixos/services/searxng.nix new file mode 100644 index 0000000..f90ad4e --- /dev/null +++ b/nixos/services/searxng.nix @@ -0,0 +1,120 @@ +{ pkgs, lib, config, ... }: let + cfg = config.services._searxng; + environmentFile = "/run/searx/searxng.env"; + generateEnvironmentFile = '' + umask 077 + echo "SEARXNG_SECRET=$(head -c 56 /dev/urandom | base64)" > ${environmentFile} + ls /run/searx + ''; +in { + options.services._searxng = { + enable = lib.mkEnableOption "enables searxng service"; + uwsgi.enable = lib.mkEnableOption "enables searxng uwsgi"; + }; + + config = lib.mkIf cfg.enable { + # Generate secret key + systemd.services.searx-environment-file = { + description = "Generate environment file with secret key for searx"; + wantedBy = [ "searx-init.service" ]; + partOf = [ "searx-init.service" ]; + before = [ "searx-init.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "searx"; + RuntimeDirectory = "searx"; + RuntimeDirectoryMode = "750"; + ConditionPathExists = "!${environmentFile}"; + }; + script = generateEnvironmentFile; + }; + + # Configure searxng + services.searx = { + enable = true; + redisCreateLocally = true; + package = pkgs.searxng; + inherit environmentFile; # Provides secret key + + # UWSGI configuration + runInUwsgi = cfg.uwsgi.enable; + + uwsgiConfig = { + socket = "/run/searx/searx.sock"; + http = ":8888"; + chmod-socket = "660"; + }; + + settings = { + general = { + instance_name = "TJK Search"; + donation_url = "https://tjkeller.xyz"; + enable_metrics = false; + }; + + # Search engine settings + search = { + safe_search = 2; # Strict + autocomplete = ""; + default_lang = "en-US"; + }; + + preferences.lock = [ "safesearch" ]; # Lock safe_search at strict + + # https://docs.searxng.org/admin/plugins.html + enabled_plugins = [ + "Tor check plugin" + "Tracker URL remover" + "Basic Calculator" + "Unit converter plugin" + "Hash plugin" + "Self Information" + "Open Access DOI rewrite" + "Hostnames plugin" + ]; + + hostnames.replace = { + "(.*\.)?youtube\.com$" = "piped.tjkeller.xyz"; + "(.*\.)?youtu\.be$" = "piped.tjkeller.xyz"; + "(.*\.)?reddit\.com$" = "old.reddit.com"; + }; + + # Enable / disabled search engines from default list + engines = lib.mapAttrsToList (name: value: { inherit name; disabled = !value; }) { + # Images + "artic" = false; + "deviantart" = false; + "flickr" = false; + "library of congress" = false; + "openverse" = false; + "pinterest" = false; + "public domain image archive" = false; + "unsplash" = false; + "wallhaven" = false; + "wikicommons.images" = false; + + # Videos + "bitchute" = true; + "dailymotion" = false; + "piped" = false; + "rumble" = true; + "sepiasearch" = false; + "vimeo" = false; + "wikicommons.videos" = false; + + # Music + "piped.music" = false; + + # Files + "1337x" = true; + "annas archive" = true; + "library genesis" = true; + + # Apps + "fdroid" = true; + }; + }; + }; + }; +} diff --git a/nixos/services/zfs/zed-mailer.nix b/nixos/services/zfs/zed-mailer.nix new file mode 100644 index 0000000..06acd1f --- /dev/null +++ b/nixos/services/zfs/zed-mailer.nix @@ -0,0 +1,20 @@ +{ lib, pkgs, config, ... }: let + cfg = config.services.zfs._zedMailer; +in { + options.services.zfs._zedMailer = { + enable = lib.mkEnableOption "enable zed mailer service"; + }; + + # https://wiki.nixos.org/wiki/ZFS#Mail_notifications_(ZFS_Event_Daemon) + config = lib.mkIf cfg.enable { + services.zfs.zed = { + enableMail = true; + settings = { + ZED_EMAIL_ADDR = [ "root" ]; + # send notification if scrub succeeds + ZED_NOTIFY_VERBOSE = true; + }; + }; + }; +} + diff --git a/nixos/suspend.nix b/nixos/suspend.nix new file mode 100644 index 0000000..0e719ab --- /dev/null +++ b/nixos/suspend.nix @@ -0,0 +1,21 @@ +{ lib, config, ... }: let + cfg = config.systemd._suspend; +in { + options.systemd._suspend = { + disable = lib.mkEnableOption "disables suspend"; + }; + + config = lib.mkIf cfg.disable { + # Disable suspend targets + systemd.targets = builtins.listToAttrs (map (name: { + inherit name; + value = { + enable = false; + unitConfig.DefaultDependencies = "no"; + }; + }) ["sleep" "suspend" "hibernate" "hybrid-sleep"]); + + # Hibernate key => suspend + services.logind.settings.Login.HandleHibernateKey = "suspend"; + }; +} diff --git a/nixos/wireless.nix b/nixos/wireless.nix new file mode 100644 index 0000000..22c759f --- /dev/null +++ b/nixos/wireless.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: let + cfg = config.networking.wireless; + mkCredential = cred: file: "${cred}:${file}"; + mkCredentialPath = cred: _: "/run/credentials/wpa_supplicant.service/${cred}"; +in { + options.networking.wireless = { + _extraSecretConfigs = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + description = "extra config files loaded into systemd credentials"; + default = {}; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.wpa_supplicant.serviceConfig = { + LoadCredential = lib.mapAttrsToList mkCredential cfg._extraSecretConfigs; + }; + networking.wireless = { + extraConfigFiles = lib.mapAttrsToList mkCredentialPath cfg._extraSecretConfigs; + }; + }; +} diff --git a/nixos/zshenv.nix b/nixos/zshenv.nix new file mode 100644 index 0000000..4c2fc7c --- /dev/null +++ b/nixos/zshenv.nix @@ -0,0 +1,24 @@ +{ lib, config, ... }: let + cfg = config.programs.zsh._zshenv; + generatedExports = lib.concatLines ( + lib.mapAttrsToList (var: value: ''export ${var}="${value}"'') cfg.exports + ); +in { + options.programs.zsh._zshenv = { + enable = lib.mkEnableOption "generate /etc/zshenv.local"; + exports = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + example = { + ZDOTDIR = "$HOME/.config/zsh"; + }; + }; + }; + + config = lib.mkIf cfg.enable { + environment.etc."zshenv.local".text = '' + # Exports + ${generatedExports} + ''; + }; +} diff --git a/pkgs/crazydiskinfo/default.nix b/pkgs/crazydiskinfo/default.nix new file mode 100644 index 0000000..61de338 --- /dev/null +++ b/pkgs/crazydiskinfo/default.nix @@ -0,0 +1,41 @@ +{ lib +, cmake +, fetchFromGitHub +, fetchpatch +, libatasmart +, ncurses5 +, stdenv +}: + +stdenv.mkDerivation rec { + pname = "crazydiskinfo"; + version = "1.1.0"; + + src = fetchFromGitHub { + owner = "otakuto"; + repo = pname; + rev = "refs/tags/${version}"; + sha256 = "sha256-+6ShoaggQ256sLSJDj4d16OipUYX/4JjEwLL5hswjiQ="; + }; + + patches = [ + # Patch to fix build warnings + (fetchpatch { + url = "https://github.com/otakuto/crazydiskinfo/compare/1.1.0...8563aa8.diff"; + sha256 = "sha256-z9aOQFsivu6sYgsZBOlnTvqnoR2ro/jyuO/WOimLSXk="; + }) + ]; + + nativeBuildInputs = [ cmake ]; + buildInputs = [ libatasmart ncurses5 ]; + + cmakeFlags = [ "-DCMAKE_INSTALL_PREFIX=$(out)" ]; + + meta = with lib; { + description = "CrazyDiskInfo is an interactive TUI S.M.A.R.T viewer for Unix systems"; + homepage = "https://github.com/otakuto/crazydiskinfo"; + mainProgram = "crazy"; + license = licenses.mit; + maintainers = [ maintainers.tjkeller ]; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 0000000..1de13ff --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,40 @@ +{ pkgs, inputs, ... }: { + nixpkgs.overlays = with pkgs; [ + (final: prev: { + immich-frame = (callPackage ./immich-frame {}); + lowbat = (callPackage ./lowbat {}); + workcentre-7800-series = (callPackage ./xerox-workcentre-7800-series-driver {}); + unclutter-desktop-entry = (callPackage ./unclutter-desktop-entry {}); + + # Use my vimv-rs until pr gets merged + vimv-rs = prev.vimv-rs.overrideAttrs (oldAttrs: { + src = fetchFromGitHub { + owner = "tjkeller-xyz"; + repo = "vimv-rs"; + rev = "5deb76fb81dd4acf3c4809087ff3a1d846ab4769"; + sha256 = "sha256-XMn+5mIxSEHaR31ixMi6o7PSkN1iYjDT4aOiQkfEwpA="; + }; + }); + + # Use my tamzen until pr gets merged + tamzen = prev.tamzen.overrideAttrs (oldAttrs: { + src = fetchFromGitHub { + owner = "tjkeller-xyz"; + repo = "tamzen-font"; + rev = "middledot"; + sha256 = "sha256-mVZ8SdYKTdMG1qBKKYdNjuoYvWkEq2ph2O1ztsNJEhs="; + }; + }); + }) + # Import overlays + (import ./st/overrides.nix) + (import ./vimPlugins/overrides.nix) + (import ./xorg/overrides.nix) + # Allow installing unstable packages via pkgs.unstable.<name> + (final: prev: { + unstable = import inputs.nixpkgs-unstable { + inherit (final) system; + }; + }) + ]; +} diff --git a/pkgs/immich-frame/default.nix b/pkgs/immich-frame/default.nix new file mode 100644 index 0000000..9c2c0f0 --- /dev/null +++ b/pkgs/immich-frame/default.nix @@ -0,0 +1,46 @@ +{ + fetchgit, + pkgs, + python3Packages, + buildNpmPackage, +}: +let + version = "0.4.1"; + src = fetchgit { + url = "https://git.tjkeller.xyz/immich-frame"; + tag = "v${version}"; + hash = "sha256-1E//uhk/egkt5y1XMn52EHumdgswxq45bwntNIoCeRM="; + }; + + frontend = buildNpmPackage { + inherit version src; + pname = "immich-frame-frontend"; + + npmWorkspace = "src/client"; + npmDepsHash = "sha256-/8dHzpqvnDZb+SV9O/GvTyspKLaPDs4vt3QrzoLsyEI="; + + installPhase = '' + mkdir -p $out + cp -R dist $out/ + ''; + }; +in python3Packages.buildPythonApplication { + inherit version src; + pname = "immich-frame"; + + makeWrapperArgs = [ "--set" "IMMICH_FRAME_STATIC_WEB_ASSETS" "${frontend}/dist" ]; + + pyproject = true; + build-system = with python3Packages; [ setuptools ]; + propagatedBuildInputs = with python3Packages; [ + flask + flask-cors # DEBUG + flask-socketio + numpy + pillow + platformdirs + pygame + pyopengl + requests + ]; +} diff --git a/pkgs/lowbat/default.nix b/pkgs/lowbat/default.nix new file mode 100644 index 0000000..64ec205 --- /dev/null +++ b/pkgs/lowbat/default.nix @@ -0,0 +1,28 @@ +{ lib +, fetchzip +, glib +, libnotify +, pkg-config +, stdenv +}: + +stdenv.mkDerivation rec { + pname = "lowbat"; + version = "1.2.2"; + + src = fetchzip { + url = "https://git.tjkeller.xyz/${pname}/snapshot/${pname}-${version}.tar.xz"; + sha256 = "sha256-utdcY9qoDcrBWnGC8m/LUsnoxQ0ilssPDF44A6D/C+8="; + }; + + nativeBuildInputs = [ pkg-config ]; + buildInputs = [ libnotify glib ]; + + installFlags = [ "PREFIX=$(out)" ]; + + meta = with lib; { + description = "A minimal battery level monitor daemon, written in C"; + license = licenses.mit; + maintainers = [ maintainers.tjkeller ]; + }; +} diff --git a/pkgs/st/overrides.nix b/pkgs/st/overrides.nix new file mode 100644 index 0000000..744cbf8 --- /dev/null +++ b/pkgs/st/overrides.nix @@ -0,0 +1,19 @@ +final: prev: { + st = prev.st.overrideAttrs (finalAttrs: previousAttrs: { + version = "tj-0.9.3.1"; + + src = prev.fetchgit { + url = "https://git.tjkeller.xyz/${previousAttrs.pname}"; + tag = finalAttrs.version; + sha256 = "sha256-MYVGoGQsUMiIzvCXTjpYLv9sRpcoaO9wZ65fTGaY0Vw="; + }; + + makeFlags = previousAttrs.makeFlags ++ [ "CFLAGS=-Ofast" ]; + buildInputs = with prev.pkgs; previousAttrs.buildInputs ++ [ libxcursor libxext ]; + + meta = with prev.lib; previousAttrs.meta // { + homepage = "https://git.tjkeller.xyz/st"; + maintainers = previousAttrs.meta.maintainers ++ [ maintainers.tjkeller ]; + }; + }); +} diff --git a/pkgs/unclutter-desktop-entry/default.nix b/pkgs/unclutter-desktop-entry/default.nix new file mode 100644 index 0000000..c98cbda --- /dev/null +++ b/pkgs/unclutter-desktop-entry/default.nix @@ -0,0 +1,14 @@ +{ pkgs }: + +pkgs.makeDesktopItem { + name = "unclutter"; + desktopName = "Unclutter"; + comment = "Hides mouse pointer while not in use"; + exec = "${pkgs.unclutter-xfixes}/bin/unclutter"; + icon = "input-mouse"; + categories = [ "Utility" ]; + keywords = [ "mouse" "cursor" "hide" "idle" "pointer" ]; + #extraConfig = '' + # NoDisplay=true + #''; +} diff --git a/pkgs/vimPlugins/overrides.nix b/pkgs/vimPlugins/overrides.nix new file mode 100644 index 0000000..a56511b --- /dev/null +++ b/pkgs/vimPlugins/overrides.nix @@ -0,0 +1,46 @@ +final: prev: { + vimPlugins = prev.vimPlugins // { + minitab-nvim = prev.vimUtils.buildVimPlugin rec { + pname = "minitab.nvim"; + version = "2024-11-28"; + rev = "f6183e8cb6f408e54dd5d1d13c0075376655a3ec"; + src = prev.fetchzip { + url = "https://git.tjkeller.xyz/${pname}/snapshot/${pname}-${rev}.tar.xz"; + sha256 = "sha256-INJC+6GNTF5MpvtOh6/eYD5CMiXxlP6aHT5TX2lJqiM="; + }; + meta.homepage = "https://git.tjkeller.xyz/minitab.nvim"; + }; + + # TODO + #nvim-treesitter-parsers = prev.vimPlugins.nvim-treesitter-parsers // { + # #jinja2 = prev.tree-sitter.buildGrammar rec { + # # language = "jinja2"; + # # version = "2025-10-24"; + # # src = prev.fetchFromGitHub { + # # owner = "geigerzaehler"; + # # repo = "tree-sitter-jinja2"; + # # rev = "ecab6215c53da5c1126d81d1bf664b3b5cf87d15"; + # # sha256 = "sha256-gYU9gQZMlLKYHVAISa5AF/KXzr0PA0M8gK0/B7gXcYs="; + # # }; + # # meta.homepage = "https://github.com/geigerzaehler/tree-sitter-jinja2"; + # #}; + # #jinja2 = prev.vimUtils.buildVimPlugin rec { + # # pname = "jinja2"; + # # version = "2025-10-24"; + # # src = prev.fetchFromGitHub { + # # owner = "geigerzaehler"; + # # repo = "tree-sitter-jinja2"; + # # rev = "ecab6215c53da5c1126d81d1bf664b3b5cf87d15"; + # # sha256 = "sha256-gYU9gQZMlLKYHVAISa5AF/KXzr0PA0M8gK0/B7gXcYs="; + # # }; + # # patches = [ + # # (prev.fetchpatch { + # # url = "https://github.com/geigerzaehler/tree-sitter-jinja2/pull/6.diff"; + # # sha256 = "sha256-CBUAwb9hz1Cpa4RaY+6aTMJNU+hX9D3BGpNxL+MoAq0="; + # # }) + # # ]; + # # meta.homepage = "https://github.com/geigerzaehler/tree-sitter-jinja2"; + # #}; + #}; + }; +} diff --git a/pkgs/xerox-workcentre-7800-series-driver/default.nix b/pkgs/xerox-workcentre-7800-series-driver/default.nix new file mode 100644 index 0000000..cfdaaae --- /dev/null +++ b/pkgs/xerox-workcentre-7800-series-driver/default.nix @@ -0,0 +1,86 @@ +{ + stdenv, + lib, + fetchurl, + dpkg, + autoPatchelfHook, + cups, + xorg, +}: +let + debPlatform = + if stdenv.hostPlatform.system == "x86_64-linux" then + "x86_64" + else if stdenv.hostPlatform.system == "i686-linux" then + "i686" + else + throw "Unsupported system: ${stdenv.hostPlatform.system}"; +in +stdenv.mkDerivation rec { + pname = "xerox-workcentre-7800-series-driver"; + version = "5.20.661.4684"; + debpkg = "XeroxOfficev5Pkg-Linux${debPlatform}-${version}.deb"; + + src = fetchurl { + url = "https://download.support.xerox.com/pub/drivers/CQ8580/drivers/linux/pt_BR/${debpkg}"; + sha256 = "014k0r9ij3401mnab1qzv96bjl9x7rf11aw1ibf0q370pk9jqqjb"; # TODO correct hash for i686 + }; + + nativeBuildInputs = [ dpkg autoPatchelfHook ]; + # TODO add support for disable xorg + buildInputs = [ + cups + stdenv.cc.cc.lib + xorg.libX11 + xorg.libXrender + xorg.libXfixes + xorg.libXdamage + xorg.libXcomposite + xorg.libXcursor + xorg.libXrandr + xorg.libXext + xorg.libXinerama + ]; + + sourceRoot = "."; + unpackCmd = "dpkg-deb -x $curSrc ."; + + dontConfigure = true; + dontBuild = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out + + # Copy and patch the binaries and libraries + cp -r opt $out/ + cp -r usr $out/ + + # Move the PPD to CUPS model dir + mkdir -p $out/share/cups/model + cp opt/XeroxOffice/prtsys/ppd/*.ppd $out/share/cups/model/ + + # Install the CUPS filters + mkdir -p $out/lib/cups/filter + cp usr/lib/cups/filter/* $out/lib/cups/filter/ + + # Install man pages + mkdir -p $out/share/man + cp -r usr/share/man/* $out/share/man/ + + runHook postInstall + ''; + + meta = with lib; { + description = "Xerox WorkCentre 7800 Series Linux Printer Driver"; + longDescription = '' + WorkCentre 7830/7835/7845/7855 + ''; + homepage = "https://www.support.xerox.com/en-us/product/workcentre-7800-series/downloads?platform=linux"; + sourceProvenance = with sourceTypes; [ binaryNativeCode ]; + #license = licenses.unfree; + maintainers = []; + platforms = platforms.linux; + }; +} diff --git a/pkgs/xorg/overrides.nix b/pkgs/xorg/overrides.nix new file mode 100644 index 0000000..0cd9490 --- /dev/null +++ b/pkgs/xorg/overrides.nix @@ -0,0 +1,18 @@ +final: prev: { + # Apply startx patch to create serverauth file in /tmp instead of home directory + xinit = prev.xinit.overrideAttrs (finalAttrs: previousAttrs: { + version = "1.4.4"; + patchtag = "${finalAttrs.version}-1"; # Archlinux xinit package tagged release to fetch patch from + # Override src since is hardcoded to 1.4.2 + src = prev.fetchurl { + url = "mirror://xorg/individual/app/xinit-${finalAttrs.version}.tar.xz"; + sha256 = "sha256-QKR8ehZMf5gc43h7Szf35BH7QyMdzeVD1wCUB12s/vk="; + }; + patches = [ + (prev.fetchpatch { + url = "https://gitlab.archlinux.org/archlinux/packaging/packages/xorg-xinit/-/raw/${finalAttrs.patchtag}/06_move_serverauthfile_into_tmp.diff"; + sha256 = "1whzs5bw7ph12r3abs1g9fydibkr291jh56a0zp17d4x070jnkda"; + }) + ]; + }); +} diff --git a/poweredge-pro outline b/poweredge-pro outline new file mode 100644 index 0000000..bdb7fb8 --- /dev/null +++ b/poweredge-pro outline @@ -0,0 +1,58 @@ +poweredge-pro + +https://wiki.nixos.org/wiki/ZFS + +SERVICES: +- nfs/smb file shares +- cronjobs / systemd units + - zfs scrubbing + - zfs snapshotting +- services.smartd +- ups safe power down +- allow backup to truenas +- virtual machines + - virtual router opnsense +- unbound + dnsmasq + - local hostname resolving e.g. ping poweredge-pro + - overrides and blocklists (manual black/whitelist) +- router profile +- web services + - admin streamline (docker for now) + - chexx-internal-webservices (docker) + - chexx-webservices (docker) + - filebrowser (potential container to avoid break out) + - gitea (container with ssh & git) + - mailcow (docker) +- tjkeller web services + - cgit (public git) (container with ssh & git) + - searxng + - minitube + - smtm + +SECURITY +- boot security + - boots without password + - certain services start without password + - chexx-webservices + - all tjkeller services + - others require password to be entered for encryption (all not mentioned above) +- file security + - encrypted zfs pools + +DISKS +- boot + services disk + - zfs + - encryption + - datasets + - /var/lib/machines + - /var/lib/docker + + +IPS +- streamline: 10.0.0.2 : 5021 +- chexx-internal-webservices: 10.0.0.2 : 8080 +- chexx-webservices: 10.0.0.2 : 8081 +- filebrowser: 10.0.1.2 +- gitea: 10.0.1.3 +- mailcow: 10.0.0.2 : 21 445 etc +- tj cgit: 10.0.1.4 @@ -0,0 +1,10 @@ +#!/bin/sh +if [ ! -f ~/.config/sops/age/keys.txt ]; then + mkdir -p ~/.config/sops/age + echo "---------------------------------------------------------------------------------------------------" + echo "| WARNING: Sops key not found. Please generate one from your ssh key using the following command: |" + echo "| nix run nixpkgs#ssh-to-age -- -private-key -i ~/.ssh/private > ~/.config/sops/age/keys.txt |" + echo "| cat ~/.config/sops/age/keys.txt | age-keygen -y # Public key |" + echo "---------------------------------------------------------------------------------------------------" +fi +nixos-rebuild switch --sudo --flake "$(dirname "$0")/#$(hostname)" $@ @@ -1,18 +1,29 @@ # Misc / low priority fix screen turning off when playing video for extended period -sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target - ^- systemctl restart systemd-logind.service - -ff violentmonkey userscripts +ff violentmonkey userscripts import pcmanfm config FIX -fonts.fontconfig.antialias = false && gtk font override on librex60 libreoffice calibre dark+svg icon theme and dark mode -# Secrets +thunderbird config + fix firefox no show http/s and no hist -clone repos activation script fix when cloning from ssh -thunderbird (https://home-manager-options.extranix.com/?query=thunderbird&release=release-24.05) # Not needed install zsh as root + +# +git-sync +geoclue cache location +geoclue continuously try to get location if not available +get rid of launch script, move x config to new repo, retire dotconfig repo +font improvements hinting etc home manager + +# +zsh support +zsh plugins installed via nix pkg manager instead of zplug +headless virtual machines / view display with vnc in browser +support office xerox printer properly + +# +create mountpoints for nas diff --git a/users/default-home.nix b/users/default-home.nix new file mode 100644 index 0000000..d0cc18b --- /dev/null +++ b/users/default-home.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./timmy/home.nix + ] +} diff --git a/users/default.nix b/users/default.nix new file mode 100644 index 0000000..8eb5373 --- /dev/null +++ b/users/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./timmy + ]; +} diff --git a/users/timmy/default.nix b/users/timmy/default.nix new file mode 100644 index 0000000..c38cbb7 --- /dev/null +++ b/users/timmy/default.nix @@ -0,0 +1,92 @@ +{ lib, config, pkgs, home-manager, ... }: let + cfg = config._users.timmy; + username = "timmy"; + fullname = "Tim Keller"; + home = "/home/${username}"; + sshKeyPaths = [ "${home}/.ssh/id_ed25519" ]; + #sshPublicKeyPaths = lib.map (keyPath: keyPath + ".pub") sshKeyPaths; + sshPublicKeys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDnsnAWcz46OVi1MWSxpOIUtUvwalijDwvW+oEvNjzep" ]; +in { + options._users.${username} = { + enable = lib.mkEnableOption "create user ${username}"; + autologin.enable = lib.mkEnableOption "enables getty automatic login"; + }; + + # FIXME + imports = [ + ./hosts.nix + ./localization.nix + ./nas.nix + ./printing.nix + ./repos.nix + ./user.nix + ./wifi.nix + ]; + + config = lib.mkIf cfg.enable { + # Setup zsh + programs.zsh = { + enable = true; + _zshenv = { + enable = true; + exports = { + ZDOTDIR = "$HOME/.config/zsh"; + }; + }; + }; + #environment.systemPackages = [ pkgs.zsh-fast-syntax-highlighting ]; # TODO + + # Setup normal user + users.users.${username} = { + inherit home; + description = fullname; + isNormalUser = true; + shell = pkgs.zsh; + extraGroups = [ + "nixbld" + "video" + "wheel" + ] ++ lib.optionals config.hardware.i2c.enable [ + "i2c" + ] ++ lib.optionals config.virtualisation.libvirtd.enable [ + "libvirtd" + ] ++ lib.optionals config.virtualisation.docker.enable [ + "docker" + ]; + }; + + # Enable sudo, doas, polkit nopass + security.sudo = { + enable = true; + wheelNeedsPassword = false; + }; + security.doas = { + enable = true; + wheelNeedsPassword = false; + extraRules = [{ keepEnv = true; }]; + }; + security.polkit = { + _wheelNoPass = true; + }; + + # Configure automatic login with getty + services.getty = lib.mkIf cfg.autologin.enable { + autologinUser = username; + }; + + # Configure sops age key paths since age keys are generated via ssh private key + sops.age = { inherit sshKeyPaths; }; + + # Add authorizedKeys to cgit service + services._cgit.ssh.authorizedKeys = { + keys = sshPublicKeys; + #keyFiles = sshPublicKeyPaths; + }; + + # Configure user home + home-manager.users.${username} = { + imports = [ ./home.nix ]; + _users.${username}.home.enable = lib.mkDefault true; + }; + }; +} diff --git a/users/timmy/home.nix b/users/timmy/home.nix new file mode 100644 index 0000000..4d61600 --- /dev/null +++ b/users/timmy/home.nix @@ -0,0 +1,106 @@ +{ lib, pkgs, config, ... }: let + cfg = config._users.${username}.home; + username = "timmy"; + email = "tjk@tjkeller.xyz"; + fullname = "Tim Keller"; + home = "/home/${username}"; + userDirs = { + desktop = "${home}"; + download = "${home}/dls"; + documents = "${home}/docs"; + pictures = "${home}/pics"; + projects = "${home}/docs/src"; + # Set these as null so they're not created + music = null; + publicShare = null; + templates = null; + videos = null; + }; +in { + options._users.${username}.home = { + enable = lib.mkEnableOption "configure home for user ${username}"; + #neovim = { + # enableLSP = lib.mkEnableOption "whether to enable lsp"; + #}; + }; + + config = lib.mkIf cfg.enable { + # Setup home-manager + home = { + username = username; + homeDirectory = home; + stateVersion = "26.05"; + }; + + # Setup userdirs + xdg.userDirs = { + enable = true; + createDirectories = true; + } // userDirs; + + # Setup git + programs.git = { + enable = true; + settings = { + user = { + name = fullname; + email = email; + }; + merge.tool = "nvimdiff"; + mergetool.nvimdiff = { + cmd = ''nvim -d "$LOCAL" "$MERGED" "$REMOTE"''; + prompt = false; + }; + # Unfortunately, this doesn't work for some reason + #url = { + # "ssh://git@publicgit/".insteadOf = "https://git.tjkeller.xyz/"; + #}; + }; + }; + + # Setup gtk bookmarks + gtk.gtk3.bookmarks = let + mkBookmark = name: dir: ''file://${dir} ${lib.toUpper (lib.substring 0 1 name)}${lib.substring 1 (-1) name}''; # Make first letter upper case + attrsToBookmarks = attrs: lib.mapAttrsToList (name: dir: lib.mkIf (dir != null) (mkBookmark name dir)) attrs; + bookmarksUserDirs = attrsToBookmarks userDirs; + in bookmarksUserDirs; + + # Setup neovim + programs._neovim = { + enable = true; + viAlias = true; + vimAlias = true; + plugins = { + lsp = { + enable = lib.mkDefault true; + lspconfig.enable = true; + languageServers.packages = with pkgs; [ + python313Packages.python-lsp-server + svelte-language-server + tailwindcss-language-server + typescript-language-server + vscode-langservers-extracted + ]; + }; + treesitter = { + enable = lib.mkDefault true; + parsers.installAll = lib.mkDefault true; + }; + packages.start = with pkgs.vimPlugins; [ + autoclose-nvim + blink-cmp + gruvbox-nvim + minitab-nvim + nvim-colorizer-lua + nvim-ts-autotag # depends on treesitter + rainbow-delimiters-nvim # depends on treesitter + snacks-nvim + ]; + }; + extraPackages = with pkgs; [ + gcc # Treesitter requires a C compiler + unzip # For zip, xlsx, etc. files + ]; + }; + }; +} diff --git a/users/timmy/hosts.nix b/users/timmy/hosts.nix new file mode 100644 index 0000000..4f0c0ac --- /dev/null +++ b/users/timmy/hosts.nix @@ -0,0 +1,7 @@ +{ + networking.hosts = { + "192.168.77.11" = [ "truenas-office" ]; + "192.168.77.8" = [ "publicgit" "tjkeller" ]; + "192.168.77.3" = [ "devel" ]; + }; +} diff --git a/users/timmy/localization.nix b/users/timmy/localization.nix new file mode 100644 index 0000000..bcbb422 --- /dev/null +++ b/users/timmy/localization.nix @@ -0,0 +1,14 @@ +{ lib, config, ... }: { + time.timeZone = lib.mkDefault "America/Chicago"; # Overwritten by automatic-timezoned + i18n.defaultLocale = "en_US.UTF-8"; + services.xserver.xkb.layout = "us"; + + # Setup static geoclue2 if wifi is disabled + services.geoclue2 = { + enableStatic = lib.mkDefault (!config._users.timmy.wifi.enable); + staticLatitude = 41.881832; + staticLongitude = -87.623177; + staticAltitude = 600; + staticAccuracy = 500; + }; +} diff --git a/users/timmy/nas.nix b/users/timmy/nas.nix new file mode 100644 index 0000000..d878ac9 --- /dev/null +++ b/users/timmy/nas.nix @@ -0,0 +1,35 @@ +{ lib, config, ... } : +let + cfg = config._users.timmy.nas; + mkNetworkFileSystem = device: automount: { + device = "${device}"; + fsType = "nfs"; + options = [ "defaults" ] ++ lib.optionals (!automount) [ "noauto" ]; + }; +in { + options._users.timmy.nas = { + enable = lib.mkEnableOption "enable network shares"; + home = { + enable = lib.mkEnableOption "enable home network shares"; + automount = lib.mkEnableOption "automount home network shares"; + }; + office = { + enable = lib.mkEnableOption "enable office network shares"; + automount = lib.mkEnableOption "automount home network shares"; + }; + }; + + config = lib.mkIf cfg.enable { + # TODO clean this up + fileSystems = lib.optionalAttrs cfg.home.enable { + "/media/ingens/backups" = mkNetworkFileSystem "poweredge:/media/ingens/backups" cfg.home.automount; + "/media/ingens/tapes" = mkNetworkFileSystem "poweredge:/media/ingens/tapes" cfg.home.automount; + "/media/ingens/pictures" = mkNetworkFileSystem "poweredge:/media/ingens/pictures" cfg.home.automount; + } // lib.optionalAttrs cfg.office.enable { + "/media/chexx/chexx" = mkNetworkFileSystem "truenas-office:/mnt/Storage/chexx" cfg.office.automount; + "/media/chexx/tkdocs" = mkNetworkFileSystem "truenas-office:/mnt/Storage/Users/Tim-Keller" cfg.office.automount; + "/media/chexx/scans" = mkNetworkFileSystem "truenas-office:/mnt/Storage/Scans" cfg.office.automount; + }; + # TODO auto mkdirz + }; +} diff --git a/modules/root/printing.nix b/users/timmy/printing.nix index 69ff573..9dbba29 100644 --- a/modules/root/printing.nix +++ b/users/timmy/printing.nix @@ -1,33 +1,23 @@ { lib, config, pkgs, ... }: { - options = { - printing.enable = lib.mkEnableOption "enables printing"; - avahi.enable = lib.mkEnableOption "enables avahi"; - scanning.enable = lib.mkEnableOption "enables scanning"; - }; - - config = lib.mkIf config.printing.enable { - services.printing = { - enable = true; - drivers = [ - pkgs.epson-escpr2 - ]; - }; - - services.avahi = { - enable = true; - nssmdns4 = true; - openFirewall = true; - }; + config = lib.mkIf config.services.printing.enable { + # Printer drivers + services.printing.drivers = [ + pkgs.epson-escpr2 + #pkgs.workcentre-7800-series + ]; - environment.systemPackages = with pkgs; pkgs.lib.optionals config.scanning.enable [ + # Scanning programs + environment.systemPackages = with pkgs; [ epsonscan2 ]; + # Printers networking.hosts = { "192.168.1.35" = [ "Epson_ET-8500" ]; - "192.168.77.40" = [ "Xerox_WorkCentre_7855" ]; + #"192.168.77.40" = [ "Xerox_WorkCentre_7855" ]; }; + # Add printers to cups hardware.printers.ensurePrinters = [ { name = "Epson_ET-8500"; diff --git a/users/timmy/repos.nix b/users/timmy/repos.nix new file mode 100644 index 0000000..eed0219 --- /dev/null +++ b/users/timmy/repos.nix @@ -0,0 +1,48 @@ +{ config, ... }: let + server = "https://git.tjkeller.xyz/"; + # TODO find how to get home manager config instead of nixos config + #srcHome = "${config.xdg.userDirs.documents}/src"; + #configHome = config.xdg.configHome; + home = "/home/timmy"; + srcHome = "${home}/docs/src"; + configHome = "${home}/.config"; +in { + config = { + home-manager.users.timmy = { + reposync.enable = true; + reposync.outOfStoreGitRepository = { + config = { + inherit server; + repository = "dotconfig"; + targetPrefix = srcHome; + stow.".".targetPrefix = configHome; + }; + nixos = { + inherit server; + targetPrefix = srcHome; + }; + scripts = { + inherit server; + targetPrefix = srcHome; + stow."*".target = ".local/bin"; + }; + userscripts = { + inherit server; + targetPrefix = srcHome; + }; + awesome = { + inherit server; + targetPrefix = configHome; + }; + nvim = { + inherit server; + targetPrefix = configHome; + }; + zsh = { + inherit server; + targetPrefix = configHome; + }; + }; + }; + }; +} diff --git a/users/timmy/resources/secrets/hashed-root-password.yaml b/users/timmy/resources/secrets/hashed-root-password.yaml new file mode 100644 index 0000000..aead57a --- /dev/null +++ b/users/timmy/resources/secrets/hashed-root-password.yaml @@ -0,0 +1,34 @@ +hashed-root-password: ENC[AES256_GCM,data:7Qgoeb/6JPNupkHCBEzCs0FMP2cDEw972bjCRWeMrBrAMZzLsZc3Mbv03s1zLztUp6Ie93R5lVsamxKPUnaPt+Tnr/l+0E9aTmt7j7L6UzmWr12nj3FHxxTSU9ief6+ioIk+S4eICJspIQ==,iv:VoWP4qBCGzuYRpQw4nilUXByJ+ZwyZR/BdKowi+53DM=,tag:x6A00VCm8BEOhtv/WySXrQ==,type:str] +sops: + age: + - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBraEhrNTZaMjVQQUdYOUU5 + MURZVTcxRWxBRC9HMEg3amtsNE1qWEdsSkc4CmRYaVZxT0I3eHphbHAydzUwZEVH + UU1FL0J2bW5yMWYxeDAydlhOZ3dvbmcKLS0tIGwzcWtZbmI5aG9tSXF1d3hlelg3 + dEczZGlSWmg0OEhoeERSSUMxVHR4a00Kwe7zenWUpfI+NxCM6m208smw6vGuPb7x + UF3d0LvQ7YJqlUsuuUjaBQcx0EHgjYH6NiT3ZimeBJJ4WfYEi87hkw== + -----END AGE ENCRYPTED FILE----- + - recipient: age1f0tmpy2nam58skmznjyqd3zf54rxtfrk6fda0vlpq9y3yg6wac7sjf0vja + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBISklHVjNLWkN6WmlRWGhv + LzczV3g5N1NKYWFGd09laWtmYU5RUUJDWkdRCkxBb2FRNnh1d3lIYVVNd0h3NG9M + VHkrZmpYbVdtc1hjR29vaTduOUtBQzQKLS0tIFF5Z1l3N25kaHdVNGI2aTJaQU90 + SXdjTmdnUU5zdXlEVzM1cnl6R096QjgK1ZscVvYvEpiDgCXOaduqZ+aT1lCD2HBS + eOpseCvD78JQym55CWvZEGHjmZOH3+Ay2Wd0+W2Z9E43yKxIkT/Nng== + -----END AGE ENCRYPTED FILE----- + - recipient: age1zfvmt2avdlfz0fvchczplc84u7m8vqausm7zytl9s4x9m9yax4cqy30zpz + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIZHVaTTNsWlQ2Ynk0WVNY + QkNEOUwvNXI0ZUdNSFZUdVJsQnh1NUZlbm13Ckg0NG1HU1pMOS9UZWJGaFBCQWth + VHZkdFB3RHEwMEhqZ3RtNmpzYUZPczQKLS0tIEwrVGViSTZGTGIrUnBBeGl0Mk1R + ZmZvWk9paVc2YmN5R0xZUmU2cmc0VDQKQVbliGNMYdEKW+z5f/yEnVvxIJFeA5h8 + l6d9kxegWkQtQCBqEAC7+0ftDC/BnzdZD9aQAA/VeNNwtkrXib7YZQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-07-17T22:34:07Z" + mac: ENC[AES256_GCM,data:B95HuJC2o8B+P1f9kAtJTcSty7YSAByuqe/Xs6ce6780p05FuzWM5X9bwvwsYXngGNKqCHksWf50UXzJ3eyc6y4ISxdxljAv2FmJFKw4NkfGaOMiRLlGPMn1uFpOtkRT+qL0+mupWG/Ap3zcpbxjsDx46PUur+e6yRxlAHw8mGw=,iv:DYobhWK+4+7vOog7BrBASiHrEzzz0P6zqgWxexfcLG8=,tag:skGwUpDEB8e3TCjrxs5peA==,type:str] + unencrypted_suffix: _unencrypted + version: 3.10.2 diff --git a/users/timmy/resources/secrets/wpa_supplicant-conf.yaml b/users/timmy/resources/secrets/wpa_supplicant-conf.yaml new file mode 100644 index 0000000..5f82025 --- /dev/null +++ b/users/timmy/resources/secrets/wpa_supplicant-conf.yaml @@ -0,0 +1,16 @@ +wpa_supplicant-conf: ENC[AES256_GCM,data:Hntld2CdmE9ef+2PANrTT4+IHGxYEkKaMFg9xUmPz6RXvrtS2oAIVYV8lFFQ8jltbtzW8eCgFOqz2NfwyDaOhqRv/xLK/4MVpVRG0njmYV2e0YtfSe3XBOwoQaVxHKl7hk92Yat5vFBJY79+w3DiM36/WBEY0RlxsKIViEU76JYMpNhqipaqlZG2IjfNTIv4y6DlHaKiolzNQagtvqhid3GmOkMoHaQp5nIp9taW81hWFZZbIZMnEFIO60z3p3LXvhDPCXfkoZRv7obbl/rXzLzp2lSOV7sBLRqy3it2SplP1tGOkePsDiBppHIrr9PXoyebY2+e8ACGPKv0tNmreEYthlHOxsmOuAGdWD3oZrGz/HA7h2YhHCnT4fQTpUj3vJ03LpnZ8RMzyxexAiAFVFh1JJ34etX+rHTzVgF7dN0nbZk3hb0YXQAj1lynDFYXIXkVfGq1hDRM/z9Q0JfB9Ef7iQVz4k0nO5QlyElWl1yBOjOWNoIibaysodDWRTxw21AWQ5GEEzMgG0AenHfzsrjUfSWUP5EKakiBGN0LduRyY5RmHQYbVHpkpP1PqCBmWBwm1xQ6JsOaajvNnc/x3fNZjIzCbGWprB/KbLnAbsF279cTIxzfMZEO3rHKxxsp,iv:h59506YhHe5Sq61StpMzGLRtx1Vr+pUoWquJeNjALh4=,tag:dS+7M5p8Xdpk6jfgzTk30w==,type:str] +sops: + age: + - recipient: age1w80rc0dnuu8nw99gw64c596qqetm78jdnsqajr0u7ephykekr39qfz8vnv + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxdXBUWXEvRU85Tk1lNWgz + ZHpENjdudmRuaEJIeVRHOHlDbWNzK0tQMGs4CmV6MVBpdE5PMTBWMm1PcDVFQ2VM + b0IwWDFxLy8xYUcxRVZFSEsyYlBFS1UKLS0tIHpCbDQ0a29TZlVFTGp4aXJCSmJ4 + ZGxqMFQ1NDk1OHJIOUd0cVV0dzNNQlkKzYX36u0rEq6dMTCJf6OON6LzcEEnAB5A + +M9t3OKUUNtwgksjBUEwqBLJ1sU9amijpK63GUxwp74YDtsb0YXHiw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-07T16:00:28Z" + mac: ENC[AES256_GCM,data:pHvHTZVa9xxiblcWhemSaG6f9tu1KIk8eS8bj3F9guitTwXKdtSpAD6ZXn1IOWRFsAui8yrRjt9COdMjThOaOP2QjnOthJ3TDnfRaZnGoPBRzscsFLf2DApm6QBLokWEFP/1w6WxZQM/ajFo4qsxq0llym1146CEJ/hCqMMmWD8=,iv:GLLi3lIo2wusrZCBXfNMwj5fSoy/6tiP84yyRBXZhsc=,tag:8U+2YBYzx0Pw6OtXB5MsYQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.12.1 diff --git a/users/timmy/user.nix b/users/timmy/user.nix new file mode 100644 index 0000000..2695549 --- /dev/null +++ b/users/timmy/user.nix @@ -0,0 +1,22 @@ +{ lib, config, pkgs, userDetails, ... }: let + cfg = config._archetypes.tjkeller.setPasswords; + hashedPasswordFile = config.sops.secrets.hashed-root-password.path; +in { + options._archetypes.tjkeller.setPasswords = { + enable = lib.mkEnableOption "set users password. requires hashed root password from sops"; + }; + + config = lib.mkIf cfg.enable { + # Load hashed root password secret + sops.secrets.hashed-root-password = { + sopsFile = ./resources/secrets/hashed-root-password.yaml; + neededForUsers = true; + }; + + # Apply password file + users.users = { + root = { inherit hashedPasswordFile; }; + ${userDetails.username} = lib.mkIf config._archetypes.users.primary.enable { inherit hashedPasswordFile; }; + }; + }; +} diff --git a/users/timmy/wifi.nix b/users/timmy/wifi.nix new file mode 100644 index 0000000..ab5332f --- /dev/null +++ b/users/timmy/wifi.nix @@ -0,0 +1,17 @@ +{ lib, config, ... }: let + cfg = config._users.timmy.wifi; +in { + options._users.timmy.wifi = { + enable = lib.mkEnableOption "enables wifi"; + }; + + config = lib.mkIf cfg.enable { + # Load wpa_supplicant.conf secret config + sops.secrets.wpa_supplicant-conf.sopsFile = ./resources/secrets/wpa_supplicant-conf.yaml; + + networking.wireless = { + enable = true; # Enables wireless support via wpa_supplicant. + _extraSecretConfigs.networks = config.sops.secrets.wpa_supplicant-conf.path; # Load secret network config via systemd credential + }; + }; +} |
