diff --git a/configurations/darwin/hesperium/configuration.nix b/configurations/darwin/hesperium/configuration.nix new file mode 100644 index 0000000..f8af5c8 --- /dev/null +++ b/configurations/darwin/hesperium/configuration.nix @@ -0,0 +1,16 @@ +{...}: { + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isNormalUser = false; + }; + }; + + system.stateVersion = 5; + + nixpkgs.hostPlatform = "aarch64-darwin"; +} diff --git a/configurations/darwin/hesperium/default.nix b/configurations/darwin/hesperium/default.nix new file mode 100644 index 0000000..220a6fb --- /dev/null +++ b/configurations/darwin/hesperium/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./configuration.nix + ]; +} diff --git a/configurations/home-manager/default.nix b/configurations/home-manager/default.nix new file mode 100644 index 0000000..a7fa478 --- /dev/null +++ b/configurations/home-manager/default.nix @@ -0,0 +1,12 @@ +{ + lib, + config, + osConfig, + ... +}: let + users = config.host.users; +in { + leyla = lib.mkIf users.leyla.isNormalUser (import ./leyla); + eve = lib.mkIf users.eve.isNormalUser (import ./eve); + git = lib.mkIf (osConfig.services.forgejo.enable or false) (import ./git); +} diff --git a/configurations/home-manager/eve/default.nix b/configurations/home-manager/eve/default.nix new file mode 100644 index 0000000..192c980 --- /dev/null +++ b/configurations/home-manager/eve/default.nix @@ -0,0 +1,56 @@ +{osConfig, ...}: let + userConfig = osConfig.host.users.eve; +in { + imports = [ + ./packages.nix + ./gnomeconf.nix + ]; + + home = { + username = userConfig.name; + homeDirectory = osConfig.users.users.eve.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + + # Home Manager is pretty good at managing dotfiles. The primary way to manage + # plain files is through 'home.file'. + file = { + # # Building this configuration will create a copy of 'dotfiles/screenrc' in + # # the Nix store. Activating the configuration will then make '~/.screenrc' a + # # symlink to the Nix store copy. + # ".screenrc".source = dotfiles/screenrc; + + # # You can also set the file content immediately. + # ".gradle/gradle.properties".text = '' + # org.gradle.console=verbose + # org.gradle.daemon.idletimeout=3600000 + # ''; + }; + + # Home Manager can also manage your environment variables through + # 'home.sessionVariables'. If you don't want to manage your shell through Home + # Manager then you have to manually source 'hm-session-vars.sh' located at + # either + # + # ~/.nix-profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # /etc/profiles/per-user/leyla/etc/profile.d/hm-session-vars.sh + # + sessionVariables = { + # EDITOR = "emacs"; + }; + }; +} diff --git a/configurations/home-manager/eve/gnomeconf.nix b/configurations/home-manager/eve/gnomeconf.nix new file mode 100644 index 0000000..7cd3863 --- /dev/null +++ b/configurations/home-manager/eve/gnomeconf.nix @@ -0,0 +1,39 @@ +{ + osConfig, + lib, + ... +}: { + config = { + gnome = lib.mkMerge [ + { + colorScheme = "prefer-dark"; + accentColor = "slate"; + clockFormat = "24h"; + nightLight = { + enable = true; + automatic = false; + fromTime = 12.0; + toTime = 11.999999999999; + temperature = 2700; + }; + extraWindowControls = true; + extensions = { + dash-to-panel = { + enable = true; + }; + }; + } + + (lib.mkIf (osConfig.networking.hostName == "horizon") { + displayScaling = 125; + experimentalFeatures = { + scaleMonitorFramebuffer = true; + }; + }) + ]; + + dconf = { + enable = true; + }; + }; +} diff --git a/configurations/home-manager/eve/packages.nix b/configurations/home-manager/eve/packages.nix new file mode 100644 index 0000000..ac24fa2 --- /dev/null +++ b/configurations/home-manager/eve/packages.nix @@ -0,0 +1,88 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + userConfig = osConfig.host.users.eve; + hardware = osConfig.host.hardware; +in { + config = { + nixpkgs.config = { + allowUnfree = true; + }; + + # Packages that can be installed without any extra configuration + # See https://search.nixos.org/packages for all options + home.packages = lib.lists.optionals userConfig.isDesktopUser ( + with pkgs; [ + gnomeExtensions.dash-to-panel + claude-code + friture + ] + ); + + # Packages that need to be installed with some extra configuration + # See https://home-manager-options.extranix.com/ for all options + programs = lib.mkMerge [ + { + # Let Home Manager install and manage itself. + home-manager.enable = true; + } + (lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) { + git = { + enable = true; + settings = { + user.name = "Eve"; + user.email = "evesnrobins@gmail.com"; + init.defaultBranch = "main"; + }; + }; + + openssh = { + enable = true; + hostKeys = [ + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + } + ]; + }; + }) + (lib.mkIf config.user.isDesktopUser { + vscode = { + enable = true; + package = pkgs.vscodium; + }; + + firefox.enable = true; + bitwarden.enable = true; + discord.enable = true; + makemkv.enable = true; + signal-desktop.enable = true; + steam.enable = true; + piper.enable = hardware.piperMouse.enable; + krita.enable = true; + ungoogled-chromium.enable = true; + + inkscape.enable = true; + obsidian.enable = true; + obs-studio.enable = true; + kdenlive.enable = true; + tor-browser.enable = true; + olympus.enable = true; + libreoffice.enable = true; + noita-entangled-worlds.enable = true; + + claude-code.enable = osConfig.host.ai.enable; + + # Windows applications that we need to figure out how to install + guild-wars-2.enable = false; + vortex.enable = false; + dungeon-draft.enable = false; + vmware-workstation.enable = true; + }) + ]; + }; +} diff --git a/configurations/home-manager/git/default.nix b/configurations/home-manager/git/default.nix new file mode 100644 index 0000000..1ea29cc --- /dev/null +++ b/configurations/home-manager/git/default.nix @@ -0,0 +1,22 @@ +{osConfig, ...}: { + impermanence.fallbackPersistence.enable = false; + + home = { + username = osConfig.users.users.git.name; + homeDirectory = osConfig.users.users.git.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + }; + + programs.ssh.extraConfig = '' + AuthorizedKeysFile + /var/lib/forgejo/.ssh/authorized_keys + ''; +} diff --git a/configurations/home-manager/leyla/dconf.nix b/configurations/home-manager/leyla/dconf.nix new file mode 100644 index 0000000..9aa61f7 --- /dev/null +++ b/configurations/home-manager/leyla/dconf.nix @@ -0,0 +1,101 @@ +{...}: { + config = { + gnome = { + extraWindowControls = true; + colorScheme = "prefer-dark"; + clockFormat = "24h"; + nightLight = { + enable = true; + automatic = false; + fromTime = 12.0; + toTime = 11.999999999999; + temperature = 2700; + }; + extensions = { + dash-to-dock = { + enable = true; + options = { + "dock-position" = "LEFT"; + "intellihide-mode" = "ALL_WINDOWS"; + "show-trash" = false; + "require-pressure-to-show" = false; + "show-mounts" = false; + }; + }; + }; + hotkeys = { + "Open Terminal" = { + binding = "t"; + command = "kgx"; + }; + "Open Firefox" = { + binding = "f"; + command = "firefox"; + }; + }; + }; + + dconf = { + enable = true; + settings = { + "org/gnome/shell" = { + favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"]; + # app-picker-layout = + # builtins.map ( + # applications: + # lib.hm.gvariant (builtins.listToAttrs (lib.lists.imap0 (i: v: lib.attrsets.nameValuePair v (lib.hm.gvariant.mkVariant "{'position': <${i}>}")) applications)) + # ) [ + # [ + # "org.gnome.Nautilus.desktop" + # "bitwarden.desktop" + # "firefox.desktop" + # "torbrowser.desktop" + # "chromium-browser.desktop" + # "codium.desktop" + # "idea-community.desktop" + # "org.gnome.TextEditor.desktop" + # "dbeaver.desktop" + # "bruno.desktop" + # "anki.desktop" + # "obsidian.desktop" + # "signal-desktop.desktop" + # "discord.desktop" + # "gimp.desktop" + # "org.inkscape.Inkscape.desktop" + # "org.kde.krita.desktop" + # "davinci-resolve.desktop" + # "com.obsproject.Studio.desktop" + # "org.freecad.FreeCAD.desktop" + # "makemkv.desktop" + # "easytag.desktop" + # "transmission-gtk.desktop" + # ] + # [ + # "SteamVR.desktop" + # "Beat Saber.desktop" + # "Noun Town.desktop" + # "WEBFISHING.desktop" + # "Factorio.desktop" + # ] + # [ + # "org.gnome.Settings.desktop" + # "org.gnome.SystemMonitor.desktop" + # "org.gnome.Snapshot.desktop" + # "org.gnome.Usage.desktop" + # "org.gnome.DiskUtility.desktop" + # "org.gnome.Evince.desktop" + # "org.gnome.fonts.desktop" + # "noisetorch.desktop" + # "nvidia-settings.desktop" + # "OpnRGB.desktop" + # "org.freedesktop.Piper.desktop" + # "via-nativia.desktop" + # "protonvpn-app.desktop" + # "simple-scan.desktop" + # ] + # ]; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/default.nix b/configurations/home-manager/leyla/default.nix new file mode 100644 index 0000000..20b04c7 --- /dev/null +++ b/configurations/home-manager/leyla/default.nix @@ -0,0 +1,95 @@ +{ + pkgs, + config, + osConfig, + ... +}: { + imports = [ + ./packages + ./i18n.nix + ./impermanence.nix + ./dconf.nix + ]; + + config = { + impermanence.enable = osConfig.storage.impermanence.enable; + + # Home Manager needs a bit of information about you and the paths it should + # manage. + home = { + username = osConfig.host.users.leyla.name; + homeDirectory = osConfig.users.users.leyla.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + + # Home Manager is pretty good at managing dotfiles. The primary way to manage + # plain files is through 'home.file'. + file = { + # # Building this configuration will create a copy of 'dotfiles/screenrc' in + # # the Nix store. Activating the configuration will then make '~/.screenrc' a + # # symlink to the Nix store copy. + # ".screenrc".source = dotfiles/screenrc; + + # # You can also set the file content immediately. + # ".gradle/gradle.properties".text = '' + # org.gradle.console=verbose + # org.gradle.daemon.idletimeout=3600000 + # ''; + "${config.xdg.configHome}/user-dirs.dirs" = { + force = true; + text = '' + # This file is written by xdg-user-dirs-update + # If you want to change or add directories, just edit the line you're + # interested in. All local changes will be retained on the next run. + # Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped + # homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an + # absolute path. No other format is supported. + # + XDG_DESKTOP_DIR="$HOME/desktop" + XDG_DOWNLOAD_DIR="$HOME/downloads" + XDG_DOCUMENTS_DIR="$HOME/documents" + XDG_TEMPLATES_DIR="$HOME/documents/templates" + XDG_MUSIC_DIR="$HOME/documents/music" + XDG_PICTURES_DIR="$HOME/documents/photos" + XDG_VIDEOS_DIR="$HOME/documents/videos" + XDG_PUBLICSHARE_DIR="$HOME/documents/public" + ''; + }; + }; + + keyboard.layout = "us,it,de"; + + # Home Manager can also manage your environment variables through + # 'home.sessionVariables'. If you don't want to manage your shell through Home + # Manager then you have to manually source 'hm-session-vars.sh' located at + # either + # + # ~/.nix-profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # /etc/profiles/per-user/leyla/etc/profile.d/hm-session-vars.sh + # + sessionVariables = { + # EDITOR = "emacs"; + }; + }; + + # TODO: move this into a fonts module + home.packages = with pkgs; [ + aileron + ]; + fonts.fontconfig.enable = true; + }; +} diff --git a/configurations/home-manager/leyla/i18n.nix b/configurations/home-manager/leyla/i18n.nix new file mode 100644 index 0000000..f12cd95 --- /dev/null +++ b/configurations/home-manager/leyla/i18n.nix @@ -0,0 +1,12 @@ +{...}: { + i18n = { + defaultLocale = "en_IE.UTF-8"; + + extraLocaleSettings = { + # LC_ADDRESS = "en_IE.UTF-8"; # lets just get used to this one now + # LC_TELEPHONE = "en_IE.UTF-8"; # lets just get used to this one now + LC_MONETARY = "en_US.UTF-8"; # to be changed once I move + LC_PAPER = "en_US.UTF-8"; # convenient for american printers until I move + }; + }; +} diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix new file mode 100644 index 0000000..8fbff41 --- /dev/null +++ b/configurations/home-manager/leyla/impermanence.nix @@ -0,0 +1,19 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.impermanence.enable) { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "desktop" + "downloads" + "documents" + ]; + files = [ + ".bash_history" # keep shell history around + "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files + ]; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/default.nix b/configurations/home-manager/leyla/packages/default.nix new file mode 100644 index 0000000..5f64742 --- /dev/null +++ b/configurations/home-manager/leyla/packages/default.nix @@ -0,0 +1,98 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + hardware = osConfig.host.hardware; +in { + imports = [ + ./vscode + ./firefox + ./direnv.nix + ./openssh.nix + ./git.nix + ./makemkv.nix + ]; + + config = lib.mkMerge [ + { + programs = lib.mkMerge [ + { + # Let Home Manager install and manage itself. + home-manager.enable = true; + } + (lib.mkIf (config.user.isTerminalUser || config.user.isDesktopUser) { + bash.enable = true; + git.enable = true; + openssh.enable = true; + }) + (lib.mkIf config.user.isDesktopUser { + bitwarden.enable = true; + obs-studio.enable = hardware.graphicsAcceleration.enable; + qbittorrent.enable = true; + prostudiomasters.enable = true; + protonvpn-gui.enable = true; + dbeaver-bin.enable = true; + bruno.enable = true; + piper.enable = hardware.piperMouse.enable; + proxmark3.enable = true; + openrgb.enable = hardware.openRGB.enable; + via.enable = hardware.viaKeyboard.enable; + claude-code.enable = osConfig.host.ai.enable; + opencode.enable = osConfig.host.ai.enable; + davinci-resolve.enable = hardware.graphicsAcceleration.enable; + mfoc.enable = true; + }) + (lib.mkIf (hardware.directAccess.enable && config.user.isDesktopUser) { + anki.enable = true; + android-studio.enable = true; + makemkv.enable = true; + discord.enable = true; + signal-desktop.enable = true; + calibre.enable = true; + obsidian.enable = true; + jetbrains.idea-oss.enable = true; + vscode.enable = true; + firefox.enable = true; + steam.enable = true; + krita.enable = true; + ungoogled-chromium.enable = true; + libreoffice.enable = true; + mapillary-uploader.enable = true; + inkscape.enable = true; + gimp.enable = true; + freecad.enable = true; + onionshare.enable = true; + pdfarranger.enable = true; + picard.enable = true; + qflipper.enable = true; + openvpn.enable = true; + noisetorch.enable = true; + noita-entangled-worlds.enable = true; + tor-browser.enable = true; + gdx-liftoff.enable = true; + proton-mail-pwa.enable = true; + proton-calendar-pwa.enable = true; + matrix-cyberia-pwa.enable = true; + kicad.enable = true; + }) + ]; + } + (lib.mkIf config.user.isTerminalUser { + home.packages = with pkgs; [ + # command line tools + sox + yt-dlp + ffmpeg + imagemagick + ]; + }) + (lib.mkIf config.user.isDesktopUser { + nixpkgs.config = { + allowUnfree = true; + }; + }) + ]; +} diff --git a/configurations/home-manager/leyla/packages/direnv.nix b/configurations/home-manager/leyla/packages/direnv.nix new file mode 100644 index 0000000..038c149 --- /dev/null +++ b/configurations/home-manager/leyla/packages/direnv.nix @@ -0,0 +1,22 @@ +{ + lib, + config, + osConfig, + ... +}: let + userConfig = osConfig.host.users.leyla; +in { + config = lib.mkIf userConfig.isDesktopUser { + programs = { + direnv = { + enable = true; + enableBashIntegration = true; + nix-direnv.enable = true; + config = { + global.hide_env_diff = true; + whitelist.exact = ["${config.home.homeDirectory}/documents/code/nix-config"]; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/firefox/bookmarks.nix b/configurations/home-manager/leyla/packages/firefox/bookmarks.nix new file mode 100644 index 0000000..bd172e7 --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/bookmarks.nix @@ -0,0 +1,161 @@ +{...}: { + programs.firefox = { + profiles.leyla = { + bookmarks = { + force = true; + settings = [ + # Personal Services + { + name = "Media"; + url = "https://media.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Photos"; + url = "https://photos.jan-leila.com"; + keyword = ""; + tags = [""]; + } + { + name = "Git"; + url = "https://git.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Home Automation"; + url = "https://home.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Search"; + url = "https://search.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Budget"; + url = "https://budget.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Documents"; + url = "https://documents.jan-leila.com/"; + keyword = ""; + tags = [""]; + } + + # Defiant Server Services + { + name = "QBittorrent"; + url = "http://defiant:8084"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Sonarr"; + url = "http://defiant:8989"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Radarr"; + url = "http://defiant:7878"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Bazarr"; + url = "http://defiant:6767"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Lidarr"; + url = "http://defiant:8686"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Jackett"; + url = "http://defiant:9117"; + keyword = ""; + tags = ["defiant"]; + } + { + name = "Crab-hole DNS"; + url = "http://defiant:8085"; + keyword = ""; + tags = ["defiant"]; + } + + # External Services + { + name = "Mail"; + url = "https://mail.protonmail.com"; + keyword = ""; + tags = [""]; + } + { + name = "Open Street Map"; + url = "https://www.openstreetmap.org/"; + keyword = ""; + tags = [""]; + } + { + name = "Password Manager"; + url = "https://vault.bitwarden.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Mastodon"; + url = "https://mspsocial.net"; + keyword = ""; + tags = [""]; + } + { + name = "Linked In"; + url = "https://www.linkedin.com/"; + keyword = ""; + tags = [""]; + } + { + name = "Job Search"; + url = "https://www.jobsinnetwork.com/?state=cleaned_history&language%5B%5D=en&query=react&locations.countryCode%5B%5D=IT&locations.countryCode%5B%5D=DE&locations.countryCode%5B%5D=NL&experience%5B%5D=medior&experience%5B%5D=junior&page=1"; + keyword = ""; + tags = [""]; + } + { + name = "React Docs"; + url = "https://react.dev/"; + keyword = ""; + tags = [""]; + } + { + name = "Cyberia Matrix"; + url = "https://chat.cyberia.club"; + keyword = ""; + tags = [""]; + } + { + name = "Cyberia Git"; + url = "https://git.cyberia.club"; + keyword = ""; + tags = [""]; + } + # Template + # { + # name = ""; + # url = ""; + # keyword = ""; + # tags = [""]; + # } + ]; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/firefox/default.nix b/configurations/home-manager/leyla/packages/firefox/default.nix new file mode 100644 index 0000000..4246c68 --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/default.nix @@ -0,0 +1,18 @@ +{ + lib, + pkgs, + inputs, + ... +}: { + imports = [ + ./firefox.nix + ./bookmarks.nix + ./harden.nix + ]; + + config = { + programs.firefox = { + enable = true; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/firefox/firefox.nix b/configurations/home-manager/leyla/packages/firefox/firefox.nix new file mode 100644 index 0000000..ef6d202 --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/firefox.nix @@ -0,0 +1,191 @@ +{ + lib, + pkgs, + inputs, + ... +}: { + programs.firefox = { + profiles.leyla = { + settings = { + "browser.search.defaultenginename" = "Searx"; + "browser.search.order.1" = "Searx"; + }; + + search = { + force = true; + default = "Searx"; + engines = { + "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"]; + }; + "NixOS Wiki" = { + urls = [{template = "https://nixos.wiki/index.php?search={searchTerms}";}]; + icon = "https://nixos.wiki/favicon.png"; + updateInterval = 24 * 60 * 60 * 1000; # every day + definedAliases = ["@nw"]; + }; + "Searx" = { + urls = [{template = "https://search.jan-leila.com/?q={searchTerms}";}]; + icon = "https://nixos.wiki/favicon.png"; + updateInterval = 24 * 60 * 60 * 1000; # every day + definedAliases = ["@searx"]; + }; + }; + }; + + extensions.packages = with inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}; [ + bitwarden + terms-of-service-didnt-read + multi-account-containers + shinigami-eyes + + ublock-origin + sponsorblock + dearrow + df-youtube + return-youtube-dislikes + + privacy-badger + decentraleyes + clearurls + localcdn + + snowflake + + pkgs.firefox-extensions.deutsch-de-language-pack + dictionary-german + + tab-session-manager + + pkgs.firefox-extensions.italiano-it-language-pack + pkgs.firefox-extensions.dizionario-italiano + ]; + + settings = { + # Disable irritating first-run stuff + "browser.disableResetPrompt" = true; + "browser.download.panel.shown" = true; + "browser.feeds.showFirstRunUI" = false; + "browser.messaging-system.whatsNewPanel.enabled" = false; + "browser.rights.3.shown" = true; + "browser.shell.checkDefaultBrowser" = false; + "browser.shell.defaultBrowserCheckCount" = 1; + "browser.startup.homepage_override.mstone" = "ignore"; + "browser.uitour.enabled" = false; + "startup.homepage_override_url" = ""; + "trailhead.firstrun.didSeeAboutWelcome" = true; + "browser.bookmarks.restore_default_bookmarks" = false; + "browser.bookmarks.addedImportButton" = true; + "browser.newtabpage.activity-stream.feeds.section.topstories" = false; + + # Usage Experience + "browser.startup.homepage" = "about:home"; + "browser.download.useDownloadDir" = false; + "browser.uiCustomization.state" = builtins.toJSON { + "currentVersion" = 20; + "newElementCount" = 6; + "dirtyAreaCache" = [ + "nav-bar" + "PersonalToolbar" + "toolbar-menubar" + "TabsToolbar" + "unified-extensions-area" + "vertical-tabs" + ]; + "placements" = { + "widget-overflow-fixed-list" = []; + "unified-extensions-area" = [ + # bitwarden + "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" + "ublock0_raymondhill_net-browser-action" + "sponsorblocker_ajay_app-browser-action" + "dearrow_ajay_app-browser-action" + "jid1-mnnxcxisbpnsxq_jetpack-browser-action" + "_testpilot-containers-browser-action" + "addon_simplelogin-browser-action" + "_74145f27-f039-47ce-a470-a662b129930a_-browser-action" + "jid1-bofifl9vbdl2zq_jetpack-browser-action" + "dfyoutube_example_com-browser-action" + "_b86e4813-687a-43e6-ab65-0bde4ab75758_-browser-action" + "_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action" + "_b11bea1f-a888-4332-8d8a-cec2be7d24b9_-browse-action" + "jid0-3guet1r69sqnsrca5p8kx9ezc3u_jetpack-browser-action" + ]; + "nav-bar" = [ + "back-button" + "forward-button" + "stop-reload-button" + "urlbar-container" + "downloads-button" + "unified-extensions-button" + "reset-pbm-toolbar-button" + ]; + "toolbar-menubar" = [ + "menubar-items" + ]; + "TabsToolbar" = [ + "firefox-view-button" + "tabbrowser-tabs" + "new-tab-button" + "alltabs-button" + ]; + "vertical-tabs" = []; + "PersonalToolbar" = [ + "import-button" + "personal-bookmarks" + ]; + }; + "seen" = [ + "save-to-pocket-button" + "developer-button" + "privacy_privacy_com-browser-action" + "sponsorblocker_ajay_app-browser-action" + "ublock0_raymondhill_net-browser-action" + "addon_simplelogin-browser-action" + "dearrow_ajay_app-browser-action" + "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" + "_74145f27-f039-47ce-a470-a662b129930a_-browser-action" + "jid1-bofifl9vbdl2zq_jetpack-browser-action" + "dfyoutube_example_com-browser-action" + "_testpilot-containers-browser-action" + "_b86e4813-687a-43e6-ab65-0bde4ab75758_-browser-action" + "jid1-mnnxcxisbpnsxq_jetpack-browser-action" + "_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action" + "_b11bea1f-a888-4332-8d8a-cec2be7d24b9_-browser-action" + "jid0-3guet1r69sqnsrca5p8kx9ezc3u_jetpack-browser-action" + ]; + }; + "browser.newtabpage.activity-stream.feeds.topsites" = false; + "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; + "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts" = false; + "browser.newtabpage.blocked" = lib.genAttrs [ + # Facebook + "4gPpjkxgZzXPVtuEoAL9Ig==" + # Reddit + "gLv0ja2RYVgxKdp0I5qwvA==" + # Amazon + "K00ILysCaEq8+bEqV/3nuw==" + # Twitter + "T9nJot5PurhJSy8n038xGA==" + ] (_: 1); + "identity.fxaccounts.enabled" = false; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/firefox/harden.nix b/configurations/home-manager/leyla/packages/firefox/harden.nix new file mode 100644 index 0000000..66310c2 --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/harden.nix @@ -0,0 +1,50 @@ +{...}: { + programs.firefox = { + profiles.leyla = { + settings = { + # Security + "privacy.trackingprotection.enabled" = true; + "dom.security.https_only_mode" = true; + "dom.security.https_only_mode_pbm" = true; + "dom.security.https_only_mode_error_page_user_suggestions" = true; + + # Privacy & Data Protection + "extensions.formautofill.addresses.enabled" = false; + "extensions.formautofill.creditCards.enabled" = false; + "signon.rememberSignons" = false; + "privacy.sanitize.sanitizeOnShutdown" = true; + "privacy.clearOnShutdown_v2.cache" = true; + "privacy.clearOnShutdown_v2.cookiesAndStorage" = true; + "privacy.clearOnShutdown_v2.historyFormDataAndDownloads" = true; + "urlclassifier.trackingSkipURLs" = ""; + "urlclassifier.features.socialtracking.skipURLs" = ""; + + # Disable telemetry and data collection + "app.shield.optoutstudies.enabled" = false; + "browser.discovery.enabled" = false; + "browser.newtabpage.activity-stream.feeds.telemetry" = false; + "browser.newtabpage.activity-stream.telemetry" = false; + "browser.ping-centre.telemetry" = false; + "datareporting.healthreport.service.enabled" = false; + "datareporting.healthreport.uploadEnabled" = false; + "datareporting.policy.dataSubmissionEnabled" = false; + "datareporting.sessions.current.clean" = true; + "devtools.onboarding.telemetry.logged" = false; + "toolkit.telemetry.archive.enabled" = false; + "toolkit.telemetry.bhrPing.enabled" = false; + "toolkit.telemetry.enabled" = false; + "toolkit.telemetry.firstShutdownPing.enabled" = false; + "toolkit.telemetry.hybridContent.enabled" = false; + "toolkit.telemetry.newProfilePing.enabled" = false; + "toolkit.telemetry.prompted" = 2; + "toolkit.telemetry.rejected" = true; + "toolkit.telemetry.reportingpolicy.firstRun" = false; + "toolkit.telemetry.server" = ""; + "toolkit.telemetry.shutdownPingSender.enabled" = false; + "toolkit.telemetry.unified" = false; + "toolkit.telemetry.unifiedIsOptIn" = false; + "toolkit.telemetry.updatePing.enabled" = false; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/git.nix b/configurations/home-manager/leyla/packages/git.nix new file mode 100644 index 0000000..499e37b --- /dev/null +++ b/configurations/home-manager/leyla/packages/git.nix @@ -0,0 +1,13 @@ +{...}: { + config = { + programs = { + git = { + settings = { + user.name = "Leyla Becker"; + user.email = "git@jan-leila.com"; + init.defaultBranch = "main"; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/makemkv.nix b/configurations/home-manager/leyla/packages/makemkv.nix new file mode 100644 index 0000000..ee71955 --- /dev/null +++ b/configurations/home-manager/leyla/packages/makemkv.nix @@ -0,0 +1,17 @@ +{ + config, + inputs, + ... +}: { + config = { + sops.secrets = { + "application-keys/makemkv" = { + sopsFile = "${inputs.secrets}/application-keys.yaml"; + }; + }; + programs.makemkv = { + appKeyFile = config.sops.placeholder."application-keys/makemkv"; + destinationDir = "/home/leyla/downloads/makemkv"; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/openssh.nix b/configurations/home-manager/leyla/packages/openssh.nix new file mode 100644 index 0000000..91aec11 --- /dev/null +++ b/configurations/home-manager/leyla/packages/openssh.nix @@ -0,0 +1,23 @@ +{ + config, + osConfig, + ... +}: { + config = { + programs = { + openssh = { + authorizedKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHeItmt8TRW43uNcOC+eIurYC7Eunc0V3LGocQqLaYj leyla@horizon" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIILimFIW2exEH/Xo7LtXkqgE04qusvnPNpPWSCeNrFkP leyla@defiant" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKBiZkg1c2aaNHiieBX4cEziqvJVj9pcDfzUrKU/mO0I leyla@twilight" + ]; + hostKeys = [ + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + } + ]; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/vscode/default.nix b/configurations/home-manager/leyla/packages/vscode/default.nix new file mode 100644 index 0000000..e0b2d98 --- /dev/null +++ b/configurations/home-manager/leyla/packages/vscode/default.nix @@ -0,0 +1,142 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + nix-development-enabled = osConfig.host.nix-development.enable; + ai-tooling-enabled = osConfig.host.ai.enable; +in { + imports = [ + ./user-words.nix + ]; + + config = lib.mkIf config.user.isDesktopUser { + programs = { + bash.shellAliases = { + code = "codium"; + }; + + vscode = { + package = pkgs.vscodium; + + mutableExtensionsDir = false; + + profiles.default = { + enableUpdateCheck = false; + enableExtensionUpdateCheck = false; + + userSettings = lib.mkMerge [ + { + "javascript.updateImportsOnFileMove.enabled" = "always"; + "editor.tabSize" = 2; + "editor.insertSpaces" = false; + # "terminal.integrated.fontFamily" = "'Droid Sans Mono', 'monospace', monospace"; + } + ]; + + extraExtensions = { + # vs code feel + oneDark.enable = true; + atomKeybindings.enable = true; + openRemoteSsh.enable = true; + # openDyslexicFont.enable = false; + + # html development + autoRenameTag.enable = true; + liveServer.enable = true; + + # js development + es7ReactJsSnippets.enable = true; + tauriVscode.enable = true; + vscodeEslint.enable = true; + vscodeJest.enable = true; + vitest.enable = true; + vscodeStandard.enable = true; + vscodeStylelint.enable = true; + + nearley.enable = true; + + # graphql + graphql.enable = true; + + # astro development + vscodeMdx.enable = true; + astroVscode.enable = true; + + # nix development + alejandra.enable = nix-development-enabled; + nixIde.enable = nix-development-enabled; + + # go development + go.enable = true; + + # rust development + rustAnalyzer.enable = true; + + # arduino development + platformIO.enable = false; + + # claude development + claudeDev = lib.mkIf ai-tooling-enabled { + enable = false; + mcp = { + nixos = { + enable = true; + autoApprove = { + nixos_search = true; + nixos_info = true; + home_manager_search = true; + home_manager_info = true; + darwin_search = true; + darwin_info = true; + nixos_flakes_search = true; + }; + }; + eslint = { + enable = true; + autoApprove = { + lint-files = true; + }; + }; + vitest = { + enable = true; + autoApprove = { + list_tests = true; + run_tests = true; + analyze_coverage = true; + set_project_root = true; + }; + }; + sleep = { + enable = true; + timeout = 18000; # 5 hours to match claude codes timeout + autoApprove = { + sleep = true; + }; + }; + }; + }; + + # misc extensions + evenBetterToml.enable = true; + direnv.enable = config.programs.direnv.enable; + conventionalCommits.enable = true; + }; + + extensions = let + extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + in ( + with extension-pkgs.open-vsx; [ + # vs code feel extensions + streetsidesoftware.code-spell-checker + streetsidesoftware.code-spell-checker-german + streetsidesoftware.code-spell-checker-italian + ] + ); + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/vscode/user-words.nix b/configurations/home-manager/leyla/packages/vscode/user-words.nix new file mode 100644 index 0000000..112269e --- /dev/null +++ b/configurations/home-manager/leyla/packages/vscode/user-words.nix @@ -0,0 +1,127 @@ +{ + pkgs, + lib, + ... +}: { + config.programs.vscode.profiles.default.userSettings = { + "cSpell.userWords" = [ + "leyla" + "Cyberia" + ]; + + "cSpell.languageSettings" = [ + { + "languageId" = "nix"; + "locale" = "*"; + "dictionaries" = [ + "applications" + "ai-words" + "nix-words" + + # We need to include all other dictionaries in the nix language settings because they exist in this file + # TODO: see if there is a way to make this only apply for this file + "js-words" + ]; + } + { + "languageId" = "javascript,typescript,js,ts"; + "locale" = "*"; + "dictionaries" = [ + "js-words" + ]; + } + ]; + + "cSpell.customDictionaries" = { + applications = { + name = "applications"; + description = "application names"; + path = pkgs.writeText "applications.txt" (lib.strings.concatLines [ + "ollama" + "syncthing" + "immich" + "sonos" + "makemkv" + "hass" + "qbittorent" + "prostudiomasters" + "protonmail" + "pulseaudio" + ]); + }; + + ai-words = { + name = "ai-words"; + description = "common words used for ai development"; + path = pkgs.writeText "ai-words.txt" (lib.strings.concatLines [ + "ollama" + "deepseek" + "qwen" + ]); + }; + + nix-words = { + name = "nix-words"; + description = "words used in nix configurations"; + path = pkgs.writeText "nix-words.txt" (lib.strings.concatLines [ + "pname" + "direnv" + "tmpfiles" + "Networkd" + "networkmanager" + "dialout" + "adbusers" + "authkey" + "netdevs" + "atomix" + "geary" + "gedit" + "hitori" + "iagno" + "alsa" + "timezoned" + "pipewire" + "rtkit" + "disko" + "ashift" + "autotrim" + "canmount" + "mountpoint" + "xattr" + "acltype" + "relatime" + "keyformat" + "keylocation" + "vdevs" + + # codium extensions + "akamud" + "onedark" + "jeanp" + "dsznajder" + "dbaeumer" + "orta" + "tauri" + "unifiedjs" + "tamasfe" + "pinage" + "jnoortheen" + "kamadorueda" + "karyfoundation" + "nearley" + + # nix.optimise is spelled wrong + "optimise" + ]); + }; + + js-words = { + name = "js-words"; + description = "words used in js development"; + path = pkgs.writeText "js-words.txt" (lib.strings.concatLines [ + "webdav" + ]); + }; + }; + }; +} diff --git a/configurations/installer/basic/configuration.nix b/configurations/installer/basic/configuration.nix new file mode 100644 index 0000000..4e63727 --- /dev/null +++ b/configurations/installer/basic/configuration.nix @@ -0,0 +1,19 @@ +{ + lib, + pkgs, + modulesPath, + ... +}: { + imports = [(modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")]; + + systemd.services.sshd.wantedBy = pkgs.lib.mkForce ["multi-user.target"]; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AaAeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee username@host" + ]; + + isoImage.squashfsCompression = "gzip -Xcompression-level 1"; + + networking.hostName = "installer"; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/configurations/installer/basic/default.nix b/configurations/installer/basic/default.nix new file mode 100644 index 0000000..220a6fb --- /dev/null +++ b/configurations/installer/basic/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./configuration.nix + ]; +} diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix new file mode 100644 index 0000000..40adbd5 --- /dev/null +++ b/configurations/nixos/defiant/configuration.nix @@ -0,0 +1,446 @@ +# server nas +{ + inputs, + config, + ... +}: { + sops.secrets = { + "vpn-keys/tailscale-authkey/defiant" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + "vpn-keys/proton-wireguard/defiant-p2p" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + mode = "0640"; + owner = "root"; + group = "systemd-network"; + }; + "services/zfs_smtp_token" = { + sopsFile = "${inputs.secrets}/defiant-services.yaml"; + }; + "services/paperless_password" = { + sopsFile = "${inputs.secrets}/defiant-services.yaml"; + mode = "0700"; + owner = "paperless"; + group = "paperless"; + }; + }; + + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + }; + network_storage = { + enable = true; + directories = [ + { + folder = "leyla_documents"; + user = "leyla"; + group = "leyla"; + bind = "/home/leyla/documents"; + } + { + folder = "eve_documents"; + user = "eve"; + group = "eve"; + } + { + folder = "users_documents"; + user = "root"; + group = "users"; + } + { + folder = "media"; + user = "jellyfin"; + group = "jellyfin_media"; + bind = config.services.jellyfin.media_directory; + } + ]; + nfs = { + enable = true; + directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; + }; + }; + }; + + storage = { + zfs = { + enable = true; + notifications = { + enable = true; + host = "smtp.protonmail.ch"; + port = 587; + to = "leyla@jan-leila.com"; + user = "noreply@jan-leila.com"; + tokenFile = config.sops.secrets."services/zfs_smtp_token".path; + }; + pool = { + encryption = { + enable = true; + }; + vdevs = [ + [ + "ata-ST18000NE000-3G6101_ZVTCXVEB" + "ata-ST18000NE000-3G6101_ZVTCXWSC" + "ata-ST18000NE000-3G6101_ZVTD10EH" + "ata-ST18000NT001-3NF101_ZVTE0S3Q" + "ata-ST18000NT001-3NF101_ZVTEF27J" + "ata-ST18000NE000-3G6101_ZVTJ7359" + ] + [ + "ata-ST4000NE001-2MA101_WS2275P3" + "ata-ST4000NE001-2MA101_WS227B9F" + "ata-ST4000NE001-2MA101_WS227CEW" + "ata-ST4000NE001-2MA101_WS227CYN" + "ata-ST4000NE001-2MA101_WS23TBWV" + "ata-ST4000NE001-2MA101_WS23TC5F" + ] + ]; + # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA + cache = [ + { + device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"; + boot = true; + } + ]; + }; + }; + impermanence = { + enable = true; + }; + }; + + systemd.network = { + enable = true; + + netdevs = { + "10-bond0" = { + netdevConfig = { + Kind = "bond"; + Name = "bond0"; + }; + bondConfig = { + Mode = "802.3ad"; + TransmitHashPolicy = "layer3+4"; + }; + }; + + "20-wg0" = { + netdevConfig = { + Kind = "wireguard"; + Name = "wg0"; + }; + wireguardConfig = { + PrivateKeyFile = config.sops.secrets."vpn-keys/proton-wireguard/defiant-p2p".path; + ListenPort = 51820; + }; + wireguardPeers = [ + { + PublicKey = "rRO6yJim++Ezz6scCLMaizI+taDjU1pzR2nfW6qKbW0="; + Endpoint = "185.230.126.146:51820"; + # Allow all traffic but use policy routing to prevent system-wide VPN + AllowedIPs = ["0.0.0.0/0"]; + PersistentKeepalive = 25; + } + ]; + }; + }; + networks = { + "40-bond0" = { + matchConfig.Name = "bond0"; + linkConfig = { + RequiredForOnline = "degraded-carrier"; + RequiredFamilyForOnline = "any"; + }; + networkConfig.DHCP = "yes"; + + address = [ + "192.168.1.10/32" + ]; + + # Set lower priority for default gateway to allow WireGuard interface binding + routes = [ + { + Destination = "0.0.0.0/0"; + Gateway = "192.168.1.1"; + Metric = 100; + } + ]; + dns = ["192.168.1.1"]; + }; + + "50-wg0" = { + matchConfig.Name = "wg0"; + networkConfig = { + DHCP = "no"; + }; + address = [ + "10.2.0.2/32" + ]; + # Configure routing for application binding + routingPolicyRules = [ + { + # Route traffic from VPN interface through VPN table + From = "10.2.0.2/32"; + Table = 200; + Priority = 100; + } + ]; + routes = [ + { + # Direct route to VPN gateway + Destination = "10.2.0.1/32"; + Scope = "link"; + } + { + # Route VPN subnet through VPN gateway in custom table + Destination = "10.2.0.0/16"; + Gateway = "10.2.0.1"; + Table = 200; + } + { + # Route all traffic through VPN gateway in custom table + Destination = "0.0.0.0/0"; + Gateway = "10.2.0.1"; + Table = 200; + } + ]; + }; + }; + }; + + # limit arc usage to 50gb because ollama doesn't play nice with zfs using up all of the memory + boot.kernelParams = ["zfs.zfs_arc_max=53687091200"]; + + # Enable policy routing and source routing for application-specific VPN binding + boot.kernel.sysctl = { + "net.ipv4.conf.all.rp_filter" = 2; + "net.ipv4.conf.default.rp_filter" = 2; + "net.ipv4.conf.wg0.rp_filter" = 2; + }; + + services = { + # PostgreSQL database server + postgresql = { + enable = true; + adminUsers = ["leyla"]; + impermanence.enable = false; + }; + + # temp enable desktop environment for setup + # Enable the X11 windowing system. + xserver.enable = true; + + # Enable the GNOME Desktop Environment. + displayManager = { + gdm.enable = true; + }; + desktopManager = { + gnome.enable = true; + }; + + # Enable new reverse proxy system + reverseProxy = { + enable = true; + openFirewall = true; + impermanence.enable = false; + acme = { + enable = true; + email = "jan-leila@protonmail.com"; + }; + }; + + ollama = { + enable = true; + exposePort = true; + impermanence.enable = false; + + environmentVariables = { + OLLAMA_KEEP_ALIVE = "24h"; + }; + + loadModels = [ + # conversation models + "llama3.1:8b" + "deepseek-r1:8b" + "deepseek-r1:32b" + "deepseek-r1:70b" + + # auto complete models + "qwen2.5-coder:1.5b-base" + "qwen2.5-coder:7b" + "deepseek-coder:6.7b" + "deepseek-coder:33b" + + # agent models + "qwen3:8b" + "qwen3:32b" + "qwen3:235b-a22b" + + "qwen3-coder:30b" + "qwen3-coder:30b-a3b-fp16" + + # embedding models + "nomic-embed-text:latest" + ]; + }; + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; + useRoutingFeatures = "server"; + impermanence.enable = false; + extraUpFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + "--accept-dns=false" + ]; + extraSetFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + "--accept-dns=false" + ]; + }; + + syncthing = { + enable = true; + impermanence.enable = false; + }; + + fail2ban = { + enable = true; + impermanence.enable = false; + }; + + jellyfin = { + enable = true; + domain = "media.jan-leila.com"; + extraDomains = ["jellyfin.jan-leila.com"]; + impermanence.enable = false; + }; + + immich = { + enable = true; + domain = "photos.jan-leila.com"; + impermanence.enable = false; + }; + + forgejo = { + enable = true; + reverseProxy.domain = "git.jan-leila.com"; + impermanence.enable = false; + }; + + searx = { + enable = true; + domain = "search.jan-leila.com"; + }; + + actual = { + enable = false; + domain = "budget.jan-leila.com"; + impermanence.enable = false; + }; + + home-assistant = { + enable = true; + domain = "home.jan-leila.com"; + openFirewall = true; + postgres.enable = true; + impermanence.enable = false; + + extensions = { + sonos.enable = true; + jellyfin.enable = true; + wyoming.enable = false; # Temporarily disabled due to dependency conflict in wyoming-piper + }; + }; + + paperless = { + enable = true; + domain = "documents.jan-leila.com"; + passwordFile = config.sops.secrets."services/paperless_password".path; + impermanence.enable = false; + }; + + panoramax = { + enable = false; + openFirewall = true; + impermanence.enable = false; + }; + + crab-hole = { + enable = true; + port = 8085; + openFirewall = true; + show_doc = true; + impermanence.enable = false; + downstreams = { + host = { + enable = true; + openFirewall = true; + }; + }; + upstreams.cloudFlare.enable = true; + blocklists.ad_malware.enable = true; + }; + + qbittorrent = { + enable = true; + mediaDir = "/srv/qbittorent"; + openFirewall = true; + webuiPort = 8084; + impermanence.enable = false; + }; + + sonarr = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + radarr = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + bazarr = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + lidarr = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + jackett = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + flaresolverr = { + enable = true; + openFirewall = true; + impermanence.enable = false; + }; + }; + + # disable computer sleeping + systemd.targets = { + sleep.enable = false; + suspend.enable = false; + hibernate.enable = false; + hybrid-sleep.enable = false; + }; + services.displayManager.gdm.autoSuspend = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix new file mode 100644 index 0000000..dd2383f --- /dev/null +++ b/configurations/nixos/defiant/default.nix @@ -0,0 +1,10 @@ +# server nas +{...}: { + imports = [ + ./hardware-configuration.nix + ./configuration.nix + ./packages.nix + ./legacy-storage.nix + ./legacy-impermanence.nix + ]; +} diff --git a/configurations/nixos/defiant/hardware-configuration.nix b/configurations/nixos/defiant/hardware-configuration.nix new file mode 100644 index 0000000..d4a638b --- /dev/null +++ b/configurations/nixos/defiant/hardware-configuration.nix @@ -0,0 +1,63 @@ +# 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, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot = { + initrd = { + availableKernelModules = ["xhci_pci" "aacraid" "ahci" "usbhid" "nvme" "usb_storage" "sd_mod"]; + kernelModules = []; + }; + kernelModules = ["kvm-amd"]; + extraModulePackages = []; + + # Bootloader. + loader = { + systemd-boot.enable = true; + efi = { + canTouchEfiVariables = true; + efiSysMountPoint = "/boot"; + }; + }; + supportedFilesystems = ["zfs"]; + + zfs.extraPools = ["rpool"]; + }; + + networking = { + hostName = "defiant"; # Define your hostname. + hostId = "c51763d6"; + useNetworkd = true; + }; + + systemd.network = { + enable = true; + + networks = { + "30-eno1" = { + matchConfig.Name = "eno1"; + networkConfig.Bond = "bond0"; + }; + "30-eno2" = { + matchConfig.Name = "eno2"; + networkConfig.Bond = "bond0"; + }; + }; + }; + + networking.networkmanager.enable = true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware = { + # TODO: hardware graphics + cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + }; +} diff --git a/configurations/nixos/defiant/legacy-impermanence.nix b/configurations/nixos/defiant/legacy-impermanence.nix new file mode 100644 index 0000000..4cfe18b --- /dev/null +++ b/configurations/nixos/defiant/legacy-impermanence.nix @@ -0,0 +1,296 @@ +# Legacy impermanence module for defiant +# See legacy-storage.nix for the full incremental migration plan. +# +# This file is consumed in two phases: +# +# Phase 3 (after generateBase is enabled): +# Remove the SYSTEM-LEVEL entries marked [PHASE 3] below. These will be +# handled automatically by storage.nix, ssh.nix, and the impermanence module: +# - var-lib-private-permissions activation script +# - /etc/machine-id +# - SSH host keys +# - /var/lib/nixos +# - /var/lib/systemd/coredump +# - /persist/system/var/log persistence block +# +# Phase 4 (migrate services one at a time, any order): +# For each service: +# 1. Remove the service's section marked [PHASE 4] from this file +# 2. Remove `impermanence.enable = false` for that service in configuration.nix +# For jellyfin/qbittorrent, also remove the separate media persistence blocks. +# +# Phase 5: Delete this file once empty. +{ + config, + lib, + ... +}: { + config = lib.mkIf config.storage.impermanence.enable { + # [PHASE 3] Remove this activation script after enabling generateBase + system.activationScripts = { + "var-lib-private-permissions" = { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/system/root/var/lib/private + chmod 0700 /persist/system/root/var/lib/private + ''; + }; + }; + + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + # [PHASE 3] Remove this files block after enabling generateBase + files = lib.mkMerge [ + ["/etc/machine-id"] + # SSH host keys + (lib.mkIf config.services.openssh.enable ( + lib.lists.flatten ( + builtins.map (hostKey: [ + hostKey.path + "${hostKey.path}.pub" + ]) + config.services.openssh.hostKeys + ) + )) + ]; + directories = lib.mkMerge [ + # [PHASE 3] Remove these system directories after enabling generateBase + [ + "/var/lib/nixos" + "/var/lib/systemd/coredump" + ] + + # [PHASE 4] PostgreSQL + (lib.mkIf config.services.postgresql.enable [ + { + directory = "/var/lib/postgresql/16"; + user = "postgres"; + group = "postgres"; + } + ]) + + # [PHASE 4] Reverse Proxy (ACME) + (lib.mkIf config.services.reverseProxy.enable [ + { + directory = "/var/lib/acme"; + user = "acme"; + group = "acme"; + } + ]) + + # [PHASE 4] Ollama + (lib.mkIf config.services.ollama.enable [ + { + directory = "/var/lib/private/ollama"; + user = config.services.ollama.user; + group = config.services.ollama.group; + mode = "0700"; + } + ]) + + # [PHASE 4] Tailscale + (lib.mkIf config.services.tailscale.enable [ + { + directory = "/var/lib/tailscale"; + user = "root"; + group = "root"; + } + ]) + + # [PHASE 4] Syncthing + (lib.mkIf config.services.syncthing.enable [ + { + directory = "/mnt/sync"; + user = "syncthing"; + group = "syncthing"; + } + { + directory = "/etc/syncthing"; + user = "syncthing"; + group = "syncthing"; + } + ]) + + # [PHASE 4] Fail2ban + (lib.mkIf config.services.fail2ban.enable [ + { + directory = "/var/lib/fail2ban"; + user = "fail2ban"; + group = "fail2ban"; + } + ]) + + # [PHASE 4] Jellyfin (data/cache only - media is on separate dataset) + (lib.mkIf config.services.jellyfin.enable [ + { + directory = "/var/lib/jellyfin"; + user = "jellyfin"; + group = "jellyfin"; + } + { + directory = "/var/cache/jellyfin"; + user = "jellyfin"; + group = "jellyfin"; + } + ]) + + # [PHASE 4] Immich + (lib.mkIf config.services.immich.enable [ + { + directory = "/var/lib/immich"; + user = "immich"; + group = "immich"; + } + ]) + + # [PHASE 4] Forgejo + (lib.mkIf config.services.forgejo.enable [ + { + directory = "/var/lib/forgejo"; + user = "forgejo"; + group = "forgejo"; + } + ]) + + # [PHASE 4] Actual + (lib.mkIf config.services.actual.enable [ + { + directory = "/var/lib/private/actual"; + user = "actual"; + group = "actual"; + } + ]) + + # [PHASE 4] Home Assistant + (lib.mkIf config.services.home-assistant.enable [ + { + directory = "/var/lib/hass"; + user = "hass"; + group = "hass"; + } + ]) + + # [PHASE 4] Paperless + (lib.mkIf config.services.paperless.enable [ + { + directory = "/var/lib/paperless"; + user = "paperless"; + group = "paperless"; + } + ]) + + # [PHASE 4] Crab-hole + (lib.mkIf config.services.crab-hole.enable [ + { + directory = "/var/lib/private/crab-hole"; + user = "crab-hole"; + group = "crab-hole"; + } + ]) + + # [PHASE 4] qBittorrent (config only - media is on separate dataset) + (lib.mkIf config.services.qbittorrent.enable [ + { + directory = "/var/lib/qBittorrent/"; + user = "qbittorrent"; + group = "qbittorrent"; + } + ]) + + # [PHASE 4] Sonarr + (lib.mkIf config.services.sonarr.enable [ + { + directory = "/var/lib/sonarr/.config/NzbDrone"; + user = "sonarr"; + group = "sonarr"; + } + ]) + + # [PHASE 4] Radarr + (lib.mkIf config.services.radarr.enable [ + { + directory = "/var/lib/radarr/.config/Radarr"; + user = "radarr"; + group = "radarr"; + } + ]) + + # [PHASE 4] Bazarr + (lib.mkIf config.services.bazarr.enable [ + { + directory = "/var/lib/bazarr"; + user = "bazarr"; + group = "bazarr"; + } + ]) + + # [PHASE 4] Lidarr + (lib.mkIf config.services.lidarr.enable [ + { + directory = "/var/lib/lidarr/.config/Lidarr"; + user = "lidarr"; + group = "lidarr"; + } + ]) + + # [PHASE 4] Jackett + (lib.mkIf config.services.jackett.enable [ + { + directory = "/var/lib/jackett/.config/Jackett"; + user = "jackett"; + group = "jackett"; + } + ]) + + # [PHASE 4] FlareSolverr + (lib.mkIf config.services.flaresolverr.enable [ + { + directory = "/var/lib/flaresolverr"; + user = "flaresolverr"; + group = "flaresolverr"; + } + ]) + ]; + }; + + # [PHASE 4 - LAST] Jellyfin media on separate dataset + # Requires Phase 2 media dataset merge before migrating (several days of data copy) + environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.jellyfin.media_directory; + user = "jellyfin"; + group = "jellyfin_media"; + mode = "1770"; + } + ]; + }; + + # [PHASE 4 - LAST] qBittorrent media on separate dataset + # Requires Phase 2 media dataset merge before migrating (several days of data copy) + environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.qbittorrent.mediaDir; + user = "qbittorrent"; + group = "qbittorrent"; + mode = "1775"; + } + ]; + }; + + # [PHASE 3] /var/log persistence - handled by storage.nix after generateBase + environment.persistence."/persist/system/var/log" = { + enable = true; + hideMounts = true; + directories = [ + "/var/log" + ]; + }; + }; +} diff --git a/configurations/nixos/defiant/legacy-storage.nix b/configurations/nixos/defiant/legacy-storage.nix new file mode 100644 index 0000000..9ab79a6 --- /dev/null +++ b/configurations/nixos/defiant/legacy-storage.nix @@ -0,0 +1,218 @@ +# Legacy storage configuration for defiant +# This file manually defines ZFS datasets matching the existing on-disk layout +# to allow incremental migration to the new storage module (generateBase = true). +# +# ============================================================================ +# INCREMENTAL MIGRATION PLAN +# ============================================================================ +# +# Current disk usage (for reference): +# rpool/local/system/nix ~26G (renamed in place, no copy) +# rpool/local/system/sops ~328K (renamed in place, no copy) +# rpool/persist/system/jellyfin ~32T (renamed in place, no copy) +# rpool/persist/system/qbittorrent ~6.5T (copied into media dataset, ~6.5T temp) +# rpool free space ~30T +# +# Phase 1: Migrate base datasets on disk (boot from live USB or rescue) +# All operations in this phase are instant renames -- no data is copied. +# +# Unlock the pool: +# zfs load-key -a +# +# Step 1a: Move nix and sops out of local/ (they go to persist/local/) +# The -p flag auto-creates the parent datasets. +# +# zfs rename -p rpool/local/system/nix rpool/persist/local/nix +# zfs rename -p rpool/local/system/sops rpool/persist/local/system/sops +# +# Step 1b: Rename local/ -> ephemeral/ (takes remaining children with it) +# zfs rename rpool/local rpool/ephemeral +# # This moves: local/system/root -> ephemeral/system/root +# # local/home/leyla -> ephemeral/home/leyla +# +# Step 1c: Recreate blank snapshots on ephemeral datasets +# zfs destroy rpool/ephemeral/system/root@blank +# zfs snapshot rpool/ephemeral/system/root@blank +# zfs destroy rpool/ephemeral/home/leyla@blank +# zfs snapshot rpool/ephemeral/home/leyla@blank +# +# Step 1d: Move persist/ children under persist/replicate/ +# zfs create -o canmount=off rpool/persist/replicate +# zfs create -o canmount=off rpool/persist/replicate/system +# zfs rename rpool/persist/system/root rpool/persist/replicate/system/root +# zfs rename rpool/persist/system/var rpool/persist/replicate/system/var +# zfs rename rpool/persist/home/leyla rpool/persist/replicate/home +# # Clean up the now-empty home parent +# zfs destroy rpool/persist/home +# # NOTE: Do NOT destroy rpool/persist/system -- it still contains +# # persist/system/jellyfin and persist/system/qbittorrent which are +# # migrated in Phase 2. +# +# Verify the new layout: +# zfs list -r rpool -o name,used,mountpoint +# +# Phase 2: Merge media into a single dataset (do this last) +# Strategy: Rename the jellyfin dataset to become the shared media dataset +# (zero copy, instant), then copy qbittorrent data into it (~6.5T copy). +# This avoids duplicating the 32T jellyfin dataset. +# +# Step 2a: Rename jellyfin dataset to the shared media name +# zfs rename rpool/persist/system/jellyfin rpool/persist/replicate/system/media +# +# Step 2b: Copy qbittorrent data into the media dataset +# This copies ~6.5T and may take several hours/days depending on disk speed. +# The qbittorrent data is not critical to back up so no snapshot needed. +# +# systemctl stop qbittorrent +# rsync -avPHAX /persist/system/qbittorrent/ /persist/replicate/system/media/ +# +# Step 2c: Verify the data and clean up +# ls -la /persist/replicate/system/media/ +# zfs destroy rpool/persist/system/qbittorrent +# # persist/system should now be empty, clean it up: +# zfs destroy rpool/persist/system +# +# Phase 3: Enable generateBase +# In the nix config: +# - Delete this file (legacy-storage.nix) and remove its import from default.nix +# - Remove [PHASE 3] entries from legacy-impermanence.nix: +# - var-lib-private-permissions activation script +# - /etc/machine-id, SSH host keys (files block) +# - /var/lib/nixos, /var/lib/systemd/coredump (directories) +# - /persist/system/var/log persistence block +# These are now handled automatically by storage.nix and ssh.nix. +# Rebuild and verify: +# sudo nixos-rebuild switch --flake .#defiant +# # Verify mounts: findmnt -t fuse.bindfs,fuse +# # Verify persist: ls /persist/replicate/system/root/var/lib/nixos +# # Verify boot: reboot and confirm system comes up cleanly +# +# Phase 4: Migrate services (one at a time, any order) +# For each service (except jellyfin/qbittorrent): +# 1. Remove the service's [PHASE 4] section from legacy-impermanence.nix +# 2. Remove `impermanence.enable = false` for that service in configuration.nix +# 3. Rebuild: sudo nixos-rebuild switch --flake .#defiant +# 4. Verify: systemctl status , check the service's data is intact +# No data migration is needed -- the data already lives on the renamed +# dataset at the new path. +# +# Migrate jellyfin and qbittorrent LAST (after Phase 2 media merge): +# 1. Remove [PHASE 4 - LAST] jellyfin entries from legacy-impermanence.nix +# 2. Remove [PHASE 4 - LAST] qbittorrent entries from legacy-impermanence.nix +# 3. Remove `impermanence.enable = false` for both in configuration.nix +# 4. Rebuild: sudo nixos-rebuild switch --flake .#defiant +# 5. Verify: systemctl status jellyfin qbittorrent +# +# Phase 5: Cleanup +# Once all services are migrated and legacy-impermanence.nix is empty: +# - Delete legacy-impermanence.nix and remove its import from default.nix +# - Rebuild: sudo nixos-rebuild switch --flake .#defiant +# +# ============================================================================ +# +# Current on-disk dataset layout: +# rpool/local/ - ephemeral parent +# rpool/local/home/leyla - ephemeral user home (rolled back on boot) +# rpool/local/system/nix - nix store +# rpool/local/system/root - root filesystem (rolled back on boot) +# rpool/local/system/sops - sops age key +# rpool/persist/ - persistent parent +# rpool/persist/home/leyla - persistent user home +# rpool/persist/system/jellyfin - jellyfin media +# rpool/persist/system/qbittorrent - qbittorrent media +# rpool/persist/system/root - persistent root data +# rpool/persist/system/var/log - log persistence +{lib, ...}: { + # Disable automatic base dataset generation so we can define them manually + storage.generateBase = false; + + # Manually define ZFS datasets matching main's structure + storage.zfs.datasets = { + # Ephemeral datasets (local/) + "local" = { + type = "zfs_fs"; + mount = null; + }; + "local/home/leyla" = { + type = "zfs_fs"; + mount = "/home/leyla"; + snapshot = { + blankSnapshot = true; + }; + }; + "local/system/nix" = { + type = "zfs_fs"; + mount = "/nix"; + atime = "off"; + relatime = "off"; + snapshot = { + autoSnapshot = false; + }; + }; + "local/system/root" = { + type = "zfs_fs"; + mount = "/"; + snapshot = { + blankSnapshot = true; + }; + }; + "local/system/sops" = { + type = "zfs_fs"; + mount = "/var/lib/sops-nix"; + }; + + # Persistent datasets (persist/) + "persist" = { + type = "zfs_fs"; + mount = null; + }; + "persist/home/leyla" = { + type = "zfs_fs"; + mount = "/persist/home/leyla"; + snapshot = { + autoSnapshot = true; + }; + }; + "persist/system/jellyfin" = { + type = "zfs_fs"; + mount = "/persist/system/jellyfin"; + atime = "off"; + relatime = "off"; + }; + "persist/system/qbittorrent" = { + type = "zfs_fs"; + mount = "/persist/system/qbittorrent"; + atime = "off"; + relatime = "off"; + }; + "persist/system/root" = { + type = "zfs_fs"; + mount = "/persist/system/root"; + snapshot = { + autoSnapshot = true; + }; + }; + "persist/system/var/log" = { + type = "zfs_fs"; + mount = "/persist/system/var/log"; + }; + }; + + # Boot commands to rollback ephemeral root and user homes on boot + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/local/system/root@blank + zfs rollback -r rpool/local/home/leyla@blank + ''; + + # FileSystems needed for boot + fileSystems = { + "/".neededForBoot = true; + "/persist/system/root".neededForBoot = true; + "/persist/system/var/log".neededForBoot = true; + "/persist/system/jellyfin".neededForBoot = true; + "/persist/system/qbittorrent".neededForBoot = true; + "/var/lib/sops-nix".neededForBoot = true; + "/persist/home/leyla".neededForBoot = true; + "/home/leyla".neededForBoot = true; + }; +} diff --git a/configurations/nixos/defiant/packages.nix b/configurations/nixos/defiant/packages.nix new file mode 100644 index 0000000..45780b0 --- /dev/null +++ b/configurations/nixos/defiant/packages.nix @@ -0,0 +1,9 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [ + ffsubsync + sox + yt-dlp + ffmpeg + imagemagick + ]; +} diff --git a/configurations/nixos/emergent/configuration.nix b/configurations/nixos/emergent/configuration.nix new file mode 100644 index 0000000..35ef445 --- /dev/null +++ b/configurations/nixos/emergent/configuration.nix @@ -0,0 +1,185 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page, on +# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). +{ + lib, + pkgs, + ... +}: { + imports = [ + ./nvidia-drivers.nix + ]; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # networking.hostName = "nixos"; # Define your hostname. + # Pick only one of the below networking options. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + # networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. + + # Set your time zone. + # time.timeZone = "Europe/Amsterdam"; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + # i18n.defaultLocale = "en_US.UTF-8"; + # console = { + # font = "Lat2-Terminus16"; + # keyMap = "us"; + # useXkbConfig = true; # use xkb.options in tty. + # }; + + # Enable the X11 windowing system. + services.xserver.enable = true; + # Enable wacom touchscreen device + services.xserver.wacom.enable = true; + + # installed opentabletdriver + # hardware.opentabletdriver.enable = true; + hardware.keyboard.qmk.enable = true; + + # Enable the GNOME Desktop Environment. + services.displayManager.gdm.enable = true; + services.desktopManager.gnome.enable = true; + + host = { + ai.enable = true; + users = { + eve = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + }; + hardware = { + piperMouse.enable = true; + }; + }; + + storage = { + zfs = { + enable = true; + pool = { + mode = "stripe"; + vdevs = [ + [ + { + device = "wwn-0x5000039fd0cf05eb"; + boot = true; + } + ] + ]; + cache = []; + }; + }; + }; + + virtualisation.libvirtd.enable = true; + + users.users.eve = { + extraGroups = ["libvirtd"]; + }; + + services.tailscale.enable = true; + # We were having weird build errors so this is disabled right now + # error: The option `devices.emergent.folders.eve_records.path' was accessed but has no value defined. Try setting the option + services.syncthing.enable = false; + + # Configure keymap in X11 + # services.xserver.xkb.layout = "us"; + # services.xserver.xkb.options = "eurosign:e,caps:escape"; + + # Enable CUPS to print documents. + # services.printing.enable = true; + + # Enable sound. + # services.pulseaudio.enable = true; + # OR + # services.pipewire = { + # enable = true; + # pulse.enable = true; + # }; + + # Enable touchpad support (enabled default in most desktopManager). + # services.libinput.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + # users.users.alice = { + # isNormalUser = true; + # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + # packages = with pkgs; [ + # tree + # ]; + # }; + + # programs.firefox.enable = true; + + nixpkgs.config.allowUnfree = true; + + # Packages that can be installed without any extra configuration + # See https://search.nixos.org/packages for all options + environment.systemPackages = with pkgs; [ + wget + gnome-boxes + libvirt + ]; + + # Packages that need to be installed with some extra configuration + # See https://search.nixos.org/options for all options + programs = {}; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + # services.openssh.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + networking = { + networkmanager.enable = true; + useDHCP = lib.mkDefault true; + hostId = "7e35eb97"; # arbitrary id number generated via this command: `head -c4 /dev/urandom | od -A none -t x4` + hostName = "emergent"; # Define your hostname. + }; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + # system.copySystemConfiguration = true; + + # This option defines the first version of NixOS you have installed on this particular machine, + # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. + # + # Most users should NEVER change this value after the initial install, for any reason, + # even if you've upgraded your system to a new NixOS release. + # + # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, + # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how + # to actually do that. + # + # This value being lower than the current NixOS release does NOT mean your system is + # out of date, out of support, or vulnerable. + # + # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, + # and migrated your data accordingly. + # + # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . + system.stateVersion = "25.05"; # Did you read the comment? +} diff --git a/configurations/nixos/emergent/default.nix b/configurations/nixos/emergent/default.nix new file mode 100644 index 0000000..3acaeda --- /dev/null +++ b/configurations/nixos/emergent/default.nix @@ -0,0 +1,8 @@ +# evs desktop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + ./legacy-storage.nix + ]; +} diff --git a/configurations/nixos/emergent/hardware-configuration.nix b/configurations/nixos/emergent/hardware-configuration.nix new file mode 100644 index 0000000..b077f9c --- /dev/null +++ b/configurations/nixos/emergent/hardware-configuration.nix @@ -0,0 +1,32 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + pkgs, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "wacom" "kvm" "kvm_amd"]; + boot.initrd.kernelModules = []; + boot.kernelModules = []; + boot.extraModulePackages = []; + + 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..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp42s0.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp4s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/emergent/legacy-storage.nix b/configurations/nixos/emergent/legacy-storage.nix new file mode 100644 index 0000000..2b24729 --- /dev/null +++ b/configurations/nixos/emergent/legacy-storage.nix @@ -0,0 +1,51 @@ +# Legacy storage configuration for emergent +# This file manually defines ZFS datasets matching the existing on-disk layout +# to allow incremental migration to the new storage module (generateBase = true). +# +# Current on-disk dataset layout: +# rpool/local/ - parent (canmount=off) +# rpool/local/system/nix - nix store +# rpool/local/system/root - root filesystem +# +# Migration plan: +# Phase 1: Rename datasets on disk (boot from live USB) +# zfs rename -p rpool/local/system/nix rpool/persist/local/nix +# zfs rename rpool/local rpool/persist/local +# # This moves: local/system/root -> persist/local/root (need to rename after) +# # Actually, since local/system/root needs to become persist/local/root: +# zfs rename rpool/persist/local/system/root rpool/persist/local/root +# zfs destroy rpool/persist/local/system # now empty +# # Recreate blank snapshot: +# zfs destroy rpool/persist/local/root@blank +# zfs snapshot rpool/persist/local/root@blank +# +# Phase 2: Delete this file, remove its import from default.nix, rebuild. +{...}: { + # Disable automatic base dataset generation so we can define them manually + storage.generateBase = false; + + # Manually define ZFS datasets matching the existing on-disk layout + storage.zfs.datasets = { + "local" = { + type = "zfs_fs"; + mount = null; + }; + "local/system/nix" = { + type = "zfs_fs"; + mount = "/nix"; + atime = "off"; + relatime = "off"; + snapshot = { + autoSnapshot = false; + }; + }; + "local/system/root" = { + type = "zfs_fs"; + mount = "/"; + snapshot = { + blankSnapshot = true; + autoSnapshot = true; + }; + }; + }; +} diff --git a/configurations/nixos/emergent/nvidia-drivers.nix b/configurations/nixos/emergent/nvidia-drivers.nix new file mode 100644 index 0000000..05b7205 --- /dev/null +++ b/configurations/nixos/emergent/nvidia-drivers.nix @@ -0,0 +1,46 @@ +{config, ...}: { + # Enable OpenGL + hardware.graphics = { + enable = true; + }; + + # Load nvidia driver for Xorg and Wayland + services = { + xserver = { + # Load nvidia driver for Xorg and Wayland + videoDrivers = ["nvidia"]; + }; + # Use X instead of wayland + displayManager.gdm.wayland = true; + }; + + hardware.nvidia = { + # Modesetting is required. + modesetting.enable = true; + + # Nvidia power management. Experimental, and can cause sleep/suspend to fail. + # Enable this if you have graphical corruption issues or application crashes after waking + # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead + # of just the bare essentials. + powerManagement.enable = true; + + # Fine-grained power management. Turns off GPU when not in use. + # Experimental and only works on modern Nvidia GPUs (Turing or newer). + powerManagement.finegrained = false; + + # Use the NVidia open source kernel module (not to be confused with the + # independent third-party "nouveau" open source driver). + # Support is limited to the Turing and later architectures. Full list of + # supported GPUs is at: + # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus + # Only available from driver 515.43.04+ + open = true; + + # Enable the Nvidia settings menu, + # accessible via `nvidia-settings`. + nvidiaSettings = true; + + # Optionally, you may need to select the appropriate driver version for your specific GPU. + package = config.boot.kernelPackages.nvidiaPackages.stable; + }; +} diff --git a/configurations/nixos/horizon/configuration.nix b/configurations/nixos/horizon/configuration.nix new file mode 100644 index 0000000..b81a895 --- /dev/null +++ b/configurations/nixos/horizon/configuration.nix @@ -0,0 +1,156 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: { + imports = [ + inputs.nixos-hardware.nixosModules.framework-11th-gen-intel + ]; + + nixpkgs.config.allowUnfree = true; + + boot = { + initrd = { + availableKernelModules = ["usb_storage" "sd_mod"]; + }; + kernelModules = ["sg"]; + + # Bootloader. + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + }; + + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isDesktopUser = true; + }; + + hardware = { + directAccess.enable = true; + }; + + ai = { + enable = true; + models = { + "Llama 3.1 8B" = { + model = "llama3.1:8b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + "Deepseek Coder:6.7B" = { + model = "deepseek-coder:6.7b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + "Deepseek Coder:33B" = { + model = "deepseek-coder:33b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + + "Deepseek r1:8B" = { + model = "deepseek-r1:8b"; + roles = ["chat"]; + apiBase = "http://defiant:11434"; + }; + + "Deepseek r1:32B" = { + model = "deepseek-r1:32b"; + roles = ["chat"]; + apiBase = "http://defiant:11434"; + }; + + "qwen2.5-coder:1.5b-base" = { + model = "qwen2.5-coder:1.5b-base"; + roles = ["autocomplete"]; + apiBase = "http://defiant:11434"; + }; + + "nomic-embed-text:latest" = { + model = "nomic-embed-text:latest"; + roles = ["embed"]; + apiBase = "http://defiant:11434"; + }; + }; + }; + }; + + virtualisation.docker.enable = true; + + environment.systemPackages = with pkgs; [ + cachefilesd + webtoon-dl + android-tools + ]; + services.cachefilesd.enable = true; + + networking = { + networkmanager.enable = true; + hostName = "horizon"; # Define your hostname. + }; + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + + hardware = { + graphics.enable = true; + }; + + sops.secrets = { + "vpn-keys/tailscale-authkey/horizon" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + }; + + services = { + # sudo fprintd-enroll + fprintd = { + enable = true; + }; + # firmware update tool + fwupd = { + enable = true; + }; + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/horizon".path; + useRoutingFeatures = "client"; + }; + + syncthing.enable = true; + + ollama = { + enable = true; + loadModels = [ + "llama3.1:8b" + ]; + }; + }; + + # Enable network-online.target for better network dependency handling + systemd.services.NetworkManager-wait-online.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + # services.xserver.libinput.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/horizon/default.nix b/configurations/nixos/horizon/default.nix new file mode 100644 index 0000000..b916d82 --- /dev/null +++ b/configurations/nixos/horizon/default.nix @@ -0,0 +1,8 @@ +# leyla laptop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + # ./network-mount.nix + ]; +} diff --git a/configurations/nixos/horizon/hardware-configuration.nix b/configurations/nixos/horizon/hardware-configuration.nix new file mode 100644 index 0000000..cec4914 --- /dev/null +++ b/configurations/nixos/horizon/hardware-configuration.nix @@ -0,0 +1,45 @@ +# 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, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "nvme"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-intel"]; + boot.extraModulePackages = []; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/866d422b-f816-4ad9-9846-791839cb9337"; + fsType = "ext4"; + }; + + "/boot" = { + device = "/dev/disk/by-uuid/E138-65B5"; + fsType = "vfat"; + }; + }; + + swapDevices = [ + {device = "/dev/disk/by-uuid/be98e952-a072-4c3a-8c12-69500b5a2fff";} + ]; + + # 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..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.tailscale0.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp170s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/horizon/network-mount.nix b/configurations/nixos/horizon/network-mount.nix new file mode 100644 index 0000000..fde16f5 --- /dev/null +++ b/configurations/nixos/horizon/network-mount.nix @@ -0,0 +1,76 @@ +{...}: { + boot.supportedFilesystems = ["nfs"]; + + fileSystems = { + "/mnt/leyla_documents" = { + device = "defiant:/exports/leyla_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" # Allow interruption of NFS calls + "timeo=30" # 3 second timeout (30 deciseconds) + "retrans=2" # Only 2 retries before giving up + "x-systemd.idle-timeout=300" # 5 minute idle timeout for mobile + "x-systemd.device-timeout=15" # 15 second device timeout + "bg" # Background mount - don't block boot + "fsc" # Enable caching + "_netdev" # Network device - wait for network + "x-systemd.requires=network-online.target" # Require network to be online + "x-systemd.after=network-online.target" # Start after network is online + "x-systemd.mount-timeout=30" # 30 second mount timeout + ]; + }; + + "/mnt/users_documents" = { + device = "defiant:/exports/users_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "nofail" + "soft" + "intr" + "timeo=30" + "retrans=2" + "x-systemd.idle-timeout=300" + "x-systemd.device-timeout=15" + "bg" + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=30" + ]; + }; + + "/mnt/media" = { + device = "defiant:/exports/media"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" + "timeo=30" + "retrans=2" + "x-systemd.idle-timeout=300" + "x-systemd.device-timeout=15" + "bg" + # Mobile-optimized read settings + "rsize=8192" # Smaller read size for mobile + "wsize=8192" # Smaller write size for mobile + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=30" + ]; + }; + }; +} diff --git a/configurations/nixos/twilight/configuration.nix b/configurations/nixos/twilight/configuration.nix new file mode 100644 index 0000000..d02af0a --- /dev/null +++ b/configurations/nixos/twilight/configuration.nix @@ -0,0 +1,156 @@ +{ + inputs, + config, + pkgs, + ... +}: { + nixpkgs.config.allowUnfree = true; + + boot.initrd.availableKernelModules = ["usb_storage"]; + boot.kernelModules = ["sg"]; + + boot.loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + + sops.secrets = { + "vpn-keys/tailscale-authkey/twilight" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + }; + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isDesktopUser = true; + }; + hardware = { + piperMouse.enable = true; + viaKeyboard.enable = true; + openRGB.enable = true; + graphicsAcceleration.enable = true; + directAccess.enable = true; + }; + ai = { + enable = true; + # TODO: benchmark twilight against defiant and prune this list of models that are faster on defiant + models = { + # conversation models + "Llama 3.1 8B" = { + model = "lamma3.1:8b"; + roles = ["chat" "edit" "apply"]; + }; + "deepseek-r1:8b" = { + model = "deepseek-r1:8b"; + roles = ["chat" "edit" "apply"]; + }; + "deepseek-r1:32b" = { + model = "deepseek-r1:32b"; + roles = ["chat" "edit" "apply"]; + }; + + # auto complete models + "qwen2.5-coder:1.5b-base" = { + model = "qwen2.5-coder:1.5b-base"; + roles = ["autocomplete"]; + }; + "qwen2.5-coder:7b" = { + model = "qwen2.5-coder:7b"; + roles = ["autocomplete"]; + }; + "deepseek-coder:6.7b" = { + model = "deepseek-coder:6.7b"; + roles = ["autocomplete"]; + }; + "deepseek-coder:33b" = { + model = "deepseek-coder:33b"; + roles = ["autocomplete"]; + }; + + # agent models + "qwen3:32b" = { + model = "qwen3:32b"; + roles = ["chat" "edit" "apply"]; + }; + + # embedding models + "nomic-embed-text:latest" = { + model = "nomic-embed-text:latest"; + roles = ["embed"]; + }; + }; + }; + }; + services = { + ollama = { + enable = true; + exposePort = true; + + loadModels = [ + # conversation models + "llama3.1:8b" + "deepseek-r1:8b" + "deepseek-r1:32b" + + # auto complete models + "qwen2.5-coder:1.5b-base" + "qwen2.5-coder:7b" + "deepseek-coder:6.7b" + "deepseek-coder:33b" + + # agent models + "qwen3:32b" + + # embedding models + "nomic-embed-text:latest" + ]; + }; + + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/twilight".path; + useRoutingFeatures = "both"; + extraUpFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + ]; + extraSetFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + ]; + }; + + syncthing.enable = true; + }; + + # Enable network-online.target for better network dependency handling + systemd.services.NetworkManager-wait-online.enable = true; + + environment.systemPackages = with pkgs; [ + cachefilesd + ]; + hardware.steam-hardware.enable = true; # Provides udev rules for controller, HTC vive, and Valve Index + + networking = { + networkmanager.enable = true; + hostName = "twilight"; # Define your hostname. + }; + + # enabled virtualisation for docker + # virtualisation.docker.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + # services.xserver.libinput.enable = true; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/twilight/default.nix b/configurations/nixos/twilight/default.nix new file mode 100644 index 0000000..aa841f8 --- /dev/null +++ b/configurations/nixos/twilight/default.nix @@ -0,0 +1,9 @@ +# leyla desktop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + ./nvidia-drivers.nix + # ./network-mount.nix + ]; +} diff --git a/configurations/nixos/twilight/hardware-configuration.nix b/configurations/nixos/twilight/hardware-configuration.nix new file mode 100644 index 0000000..1288343 --- /dev/null +++ b/configurations/nixos/twilight/hardware-configuration.nix @@ -0,0 +1,42 @@ +# 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, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "sd_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-amd"]; + boot.extraModulePackages = []; + + fileSystems = { + "/" = { + device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part2"; + fsType = "ext4"; + }; + + "/boot" = { + device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part1"; + 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..useDHCP`. + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/twilight/network-mount.nix b/configurations/nixos/twilight/network-mount.nix new file mode 100644 index 0000000..9f84b04 --- /dev/null +++ b/configurations/nixos/twilight/network-mount.nix @@ -0,0 +1,72 @@ +{...}: { + boot.supportedFilesystems = ["nfs"]; + + fileSystems = { + "/mnt/leyla_documents" = { + device = "defiant:/exports/leyla_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" # Allow interruption of NFS calls + "timeo=50" # 5 second timeout (50 deciseconds) - longer than mobile + "retrans=3" # 3 retries for desktop + "x-systemd.idle-timeout=600" # 10 minute idle timeout for desktop + "x-systemd.device-timeout=30" # 30 second device timeout + "bg" # Background mount - don't block boot + "fsc" # Enable caching + "_netdev" # Network device - wait for network + "x-systemd.requires=network-online.target" # Require network to be online + "x-systemd.after=network-online.target" # Start after network is online + ]; + }; + + "/mnt/users_documents" = { + device = "defiant:/exports/users_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "nofail" + "soft" + "intr" + "timeo=50" + "retrans=3" + "x-systemd.idle-timeout=600" + "bg" + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + ]; + }; + + "/mnt/media" = { + device = "defiant:/exports/media"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" + "timeo=50" + "retrans=3" + "x-systemd.idle-timeout=600" + "x-systemd.device-timeout=30" + "bg" + # Desktop-optimized read settings + "rsize=32768" # Larger read size for desktop + "wsize=32768" # Larger write size for desktop + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + ]; + }; + }; +} diff --git a/modules/hosts/nixos/emergent/nvidia-drivers.nix b/configurations/nixos/twilight/nvidia-drivers.nix similarity index 69% rename from modules/hosts/nixos/emergent/nvidia-drivers.nix rename to configurations/nixos/twilight/nvidia-drivers.nix index e8759d2..2842d0a 100644 --- a/modules/hosts/nixos/emergent/nvidia-drivers.nix +++ b/configurations/nixos/twilight/nvidia-drivers.nix @@ -1,21 +1,20 @@ -{...}: { - flake.nixosModules.emergentNvidiaDriver = {config, ...}: { +{config, ...}: { + services = { + xserver = { + # Load nvidia driver for Xorg and Wayland + videoDrivers = ["nvidia"]; + }; + # Temporarily enable wayland to fix boot issue + # TODO: Investigate proper X11 session generation for gaming + displayManager.gdm.wayland = true; + }; + + hardware = { # Enable OpenGL - hardware.graphics = { - enable = true; - }; + graphics.enable = true; - # Load nvidia driver for Xorg and Wayland - services = { - xserver = { - # Load nvidia driver for Xorg and Wayland - videoDrivers = ["nvidia"]; - }; - # Use X instead of wayland - displayManager.gdm.wayland = true; - }; - - hardware.nvidia = { + # install graphics drivers + nvidia = { # Modesetting is required. modesetting.enable = true; @@ -35,6 +34,7 @@ # supported GPUs is at: # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus # Only available from driver 515.43.04+ + # Currently alpha-quality/buggy, so false is currently the recommended setting. open = true; # Enable the Nvidia settings menu, @@ -42,7 +42,7 @@ nvidiaSettings = true; # Optionally, you may need to select the appropriate driver version for your specific GPU. - package = config.boot.kernelPackages.nvidiaPackages.stable; + package = config.boot.kernelPackages.nvidiaPackages.production; }; }; } diff --git a/configurations/syncthing/default.nix b/configurations/syncthing/default.nix new file mode 100644 index 0000000..397f678 --- /dev/null +++ b/configurations/syncthing/default.nix @@ -0,0 +1,119 @@ +{config, ...}: { + folders = { + leyla_documents = { + id = "hvrj0-9bm1p"; + }; + leyla_calendar = { + id = "8oatl-1rv6w"; + }; + leyla_supernote_notes = { + id = "dwbuv-zffnf"; + }; + eve_records = { + id = "by6at-d4h9n"; + }; + share = { + id = "73ot0-cxmkx"; + }; + }; + devices = { + defiant = { + id = "3R6E6Y4-2F7MF2I-IGB4WE6-A3SQSMV-LIBYSAM-2OXHHU2-KJ6CGIV-QNMCPAR"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + leyla_calendar = { + folder = config.folders.leyla_calendar; + path = "/mnt/sync/leyla/calendar"; + }; + leyla_supernote_notes = { + folder = config.folders.leyla_supernote_notes; + path = "/mnt/sync/leyla/notes"; + }; + eve_records = { + folder = config.folders.eve_records; + path = "/mnt/sync/eve/records"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + twilight = { + id = "UDIYL7V-OAZ2BI3-EJRAWFB-GZYVDWR-JNUYW3F-FFQ35MU-XBTGWEF-QD6K6QN"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + horizon = { + id = "OGPAEU6-5UR56VL-SP7YC4Y-IMVCRTO-XFD4CYN-Z6T5TZO-PFZNAT6-4MKWPQS"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + coven = { + id = "QGU7NN6-OMXTWVA-YCZ73S5-2O7ECTS-MUCTN4M-YH6WLEL-U4U577I-7PBNCA5"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + }; + share = { + folder = config.folders.share; + }; + }; + }; + ceder = { + id = "MGXUJBS-7AENXHB-7YQRNWG-QILKEJD-5462U2E-WAQW4R4-I2TVK5H-SMK6LAA"; + folders = { + share = { + folder = config.folders.share; + }; + leyla_documents = { + folder = config.folders.leyla_documents; + }; + leyla_calendar = { + folder = config.folders.leyla_calendar; + }; + leyla_notes = { + folder = config.folders.leyla_supernote_notes; + }; + }; + }; + emergent = { + id = "6MIDMKJ-7IFHXVX-FIR3YTB-KVE75LN-PA6IOTN-I257LWR-MMC4K6C-5H4SHQN"; + folders = { + eve_records = { + folder = config.folders.eve_records; + }; + share = { + folder = config.folders.share; + }; + }; + }; + shale = { + id = "AOAXEVD-QJ2IVRA-6G44Q7Q-TGUPXU2-FWWKOBH-DPKWC5N-LBAEHWJ-7EQF4AM"; + folders = { + share = { + folder = config.folders.share; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index 7e9fd03..14c8561 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1773889306, - "narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=", + "lastModified": 1772867152, + "narHash": "sha256-RIFgZ4O6Eg+5ysZ8Tqb3YvcqiRaNy440GEY22ltjRrs=", "owner": "nix-community", "repo": "disko", - "rev": "5ad85c82cc52264f4beddc934ba57f3789f28347", + "rev": "eaafb89b56e948661d618eefd4757d9ea8d77514", "type": "github" }, "original": { @@ -28,11 +28,11 @@ }, "locked": { "dir": "pkgs/firefox-addons", - "lastModified": 1775966594, - "narHash": "sha256-pnRtaqTr7ut8dz8b04OWAanUM4tGhDUJz8SWmeTRp7U=", + "lastModified": 1772856163, + "narHash": "sha256-xD+d1+FVhKJ+oFYMTWOdVSBoXS4yeMyVZyDjMXqWEJE=", "owner": "rycee", "repo": "nur-expressions", - "rev": "000d1d2322d28fa0a51b8db9f85a227aa5413b52", + "rev": "d358a550c7beac5f04fbc5a786e14af079606689", "type": "gitlab" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1775087534, - "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", + "lastModified": 1767609335, + "narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", + "rev": "250481aafeb741edfe23d29195671c19b36b6dca", "type": "github" }, "original": { @@ -115,11 +115,11 @@ ] }, "locked": { - "lastModified": 1776030105, - "narHash": "sha256-b4cNpWPDSH+/CTTiw8++yGh1UYG2kQNrbIehV2iGoeo=", + "lastModified": 1772845525, + "narHash": "sha256-Dp5Ir2u4jJDGCgeMRviHvEQDe+U37hMxp6RSNOoMMPc=", "owner": "nix-community", "repo": "home-manager", - "rev": "49088dc2e7a876e338e510c5f5f60f659819c650", + "rev": "27b93804fbef1544cb07718d3f0a451f4c4cd6c0", "type": "github" }, "original": { @@ -151,21 +151,6 @@ "type": "github" } }, - "import-tree": { - "locked": { - "lastModified": 1773693634, - "narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=", - "owner": "vic", - "repo": "import-tree", - "rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1", - "type": "github" - }, - "original": { - "owner": "vic", - "repo": "import-tree", - "type": "github" - } - }, "lix": { "flake": false, "locked": { @@ -190,11 +175,11 @@ ] }, "locked": { - "lastModified": 1773460763, - "narHash": "sha256-y9kC3ff89btXS8RD6pAtM50g0qtsim1I8HXBtgSqdbI=", + "lastModified": 1767364176, + "narHash": "sha256-l6YdEBYQxXjD8ujqvc0tKdwWc3K8UQOi+E4Y3DKQ318=", "ref": "refs/heads/main", - "rev": "5e56f5a973e24292b125dca9e9d506b0a91d6903", - "revCount": 180, + "rev": "1688100bba140492658d597f6b307c327f35c780", + "revCount": 179, "type": "git", "url": "https://git.lix.systems/lix-project/nixos-module.git" }, @@ -203,6 +188,25 @@ "url": "https://git.lix.systems/lix-project/nixos-module.git" } }, + "mcp-nixos": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1772769318, + "narHash": "sha256-RAyOW5JMXRhiREqxFPOzw80fVsYVBnOPFgBSjnJ6gbY=", + "owner": "utensils", + "repo": "mcp-nixos", + "rev": "60c1efbba0de1268b42f1144c904e6c8a9627dde", + "type": "github" + }, + "original": { + "owner": "utensils", + "repo": "mcp-nixos", + "type": "github" + } + }, "nix-darwin": { "inputs": { "nixpkgs": [ @@ -210,11 +214,11 @@ ] }, "locked": { - "lastModified": 1775037210, - "narHash": "sha256-KM2WYj6EA7M/FVZVCl3rqWY+TFV5QzSyyGE2gQxeODU=", + "lastModified": 1772379624, + "narHash": "sha256-NG9LLTWlz4YiaTAiRGChbrzbVxBfX+Auq4Ab/SWmk4A=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "06648f4902343228ce2de79f291dd5a58ee12146", + "rev": "52d061516108769656a8bd9c6e811c677ec5b462", "type": "github" }, "original": { @@ -251,11 +255,11 @@ ] }, "locked": { - "lastModified": 1775964551, - "narHash": "sha256-PFqgleO6rpvnLsSWzkihL1fLTbE2DW9qtpXJZjyNMTg=", + "lastModified": 1772850876, + "narHash": "sha256-Ga19zlfMpakCY4GMwBSOljNLOF0nEYrYBXv0hP/d4rw=", "owner": "nix-community", "repo": "nix-vscode-extensions", - "rev": "8cb99bb38a916ac38c73003730d7bdd9f67ab657", + "rev": "22f084d4c280dfc8a9d764f7b85af38e5d69c3dc", "type": "github" }, "original": { @@ -266,11 +270,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1775490113, - "narHash": "sha256-2ZBhDNZZwYkRmefK5XLOusCJHnoeKkoN95hoSGgMxWM=", + "lastModified": 1771969195, + "narHash": "sha256-qwcDBtrRvJbrrnv1lf/pREQi8t2hWZxVAyeMo7/E9sw=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "c775c2772ba56e906cbeb4e0b2db19079ef11ff7", + "rev": "41c6b421bdc301b2624486e11905c9af7b8ec68e", "type": "github" }, "original": { @@ -282,15 +286,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775710090, - "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", - "owner": "nixos", + "lastModified": 1767640445, + "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "4c1018dae018162ec878d42fec712642d214fdfa", + "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", "type": "github" }, "original": { - "owner": "nixos", + "owner": "NixOS", "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" @@ -298,11 +302,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1774748309, - "narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=", + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "333c4e0545a6da976206c74db8773a1645b5870a", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", "type": "github" }, "original": { @@ -312,6 +316,22 @@ } }, "nixpkgs_2": { + "locked": { + "lastModified": 1772773019, + "narHash": "sha256-E1bxHxNKfDoQUuvriG71+f+s/NT0qWkImXsYZNFFfCs=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "aca4d95fce4914b3892661bcb80b8087293536c6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1759070547, "narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=", @@ -329,16 +349,16 @@ }, "noita-entangled-worlds": { "inputs": { - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs_3", "rust-overlay": "rust-overlay", "systems": "systems_2" }, "locked": { - "lastModified": 1775939911, - "narHash": "sha256-PduHafcXfzsZBG25JrhX+scVoQnCCDjlkspxCNe2264=", + "lastModified": 1771445312, + "narHash": "sha256-8uOcu+ZurGx0LmGFCf87Zbj4ikhVPQtP+PuBscEBCv0=", "owner": "IntQuant", "repo": "noita_entangled_worlds", - "rev": "bff11677dd50cffe249f6d6f547f56d917e31118", + "rev": "4a842f29d0e5fb8dc6df73d87f7bb8d2a16f0fc8", "type": "github" }, "original": { @@ -353,16 +373,15 @@ "disko": "disko", "firefox-addons": "firefox-addons", "flake-compat": "flake-compat", - "flake-parts": "flake-parts", "home-manager": "home-manager", "impermanence": "impermanence", - "import-tree": "import-tree", "lix-module": "lix-module", + "mcp-nixos": "mcp-nixos", "nix-darwin": "nix-darwin", "nix-syncthing": "nix-syncthing", "nix-vscode-extensions": "nix-vscode-extensions", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "noita-entangled-worlds": "noita-entangled-worlds", "secrets": "secrets", "sops-nix": "sops-nix" @@ -412,11 +431,11 @@ ] }, "locked": { - "lastModified": 1775971308, - "narHash": "sha256-VKp9bhVSm0bT6JWctFy06ocqxGGnWHi1NfoE90IgIcY=", + "lastModified": 1772495394, + "narHash": "sha256-hmIvE/slLKEFKNEJz27IZ8BKlAaZDcjIHmkZ7GCEjfw=", "owner": "Mic92", "repo": "sops-nix", - "rev": "31ac5fe5d015f76b54058c69fcaebb66a55871a4", + "rev": "1d9b98a29a45abe9c4d3174bd36de9f28755e3ff", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 9fdde83..df5f6e9 100644 --- a/flake.nix +++ b/flake.nix @@ -5,10 +5,6 @@ # base packages nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - # flake structure - flake-parts.url = "github:hercules-ci/flake-parts"; - import-tree.url = "github:vic/import-tree"; - lix-module = { url = "git+https://git.lix.systems/lix-project/nixos-module.git"; inputs.nixpkgs.follows = "nixpkgs"; @@ -78,6 +74,12 @@ url = "github:edolstra/flake-compat"; }; + # MCP NixOS server for Claude Dev + mcp-nixos = { + url = "github:utensils/mcp-nixos"; + # Not following nixpkgs because aws-sam-translator doesn't support Python 3.14 yet + }; + # Noita Entangled Worlds package # Not following our nixpkgs so it can use its own rust-overlay configuration noita-entangled-worlds = { @@ -85,22 +87,95 @@ }; }; - outputs = inputs: - inputs.flake-parts.lib.mkFlake {inherit inputs;} { - imports = [ - inputs.home-manager.flakeModules.home-manager - inputs.nix-darwin.flakeModules.default - ({lib, ...}: { - options.flake.darwinModules = lib.mkOption { - type = lib.types.attrsOf lib.types.unspecified; - default = {}; - }; - options.flake.commonModules = lib.mkOption { - type = lib.types.attrsOf lib.types.unspecified; - default = {}; + outputs = { + self, + nixpkgs, + sops-nix, + nix-syncthing, + home-manager, + impermanence, + ... + } @ inputs: let + util = import ./util {inherit inputs;}; + forEachPkgs = util.forEachPkgs; + + mkNixosSystem = util.mkNixosSystem; + mkDarwinSystem = util.mkDarwinSystem; + mkHome = util.mkHome; + + nixosSystems = { + horizon = mkNixosSystem "horizon"; + twilight = mkNixosSystem "twilight"; + defiant = mkNixosSystem "defiant"; + emergent = mkNixosSystem "emergent"; + }; + + darwinSystems = { + hesperium = mkDarwinSystem "hesperium"; + }; + + homeSystems = { + # stand alone home manager configurations here: + # name = mkHome "name" + }; + + systemsHomes = nixpkgs.lib.attrsets.mergeAttrsList ( + nixpkgs.lib.attrsets.mapAttrsToList (hostname: system: ( + nixpkgs.lib.attrsets.mapAttrs' (user: _: { + name = "${user}@${hostname}"; + value = mkHome { + user = user; + host = hostname; + system = system.pkgs.hostPlatform.system; + osConfig = system.config; }; }) - (inputs.import-tree ./modules) - ]; - }; + system.config.home-manager.users + )) + (nixosSystems // darwinSystems) + ); + + homeConfigurations = + systemsHomes + // homeSystems; + in { + formatter = forEachPkgs (system: pkgs: pkgs.alejandra); + + # templates = import ./templates; + + devShells = forEachPkgs (system: pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ + # for version controlling this repo + git + # for formatting code in this repo + alejandra + # for editing secrets in the secrets repo + sops + # for viewing configuration options defined in this repo + nix-inspect + # for installing flakes from this repo onto other systems + nixos-anywhere + # for updating disko configurations + disko + # for viewing dconf entries + dconf-editor + # for MCP NixOS server support in development + inputs.mcp-nixos.packages.${system}.default + ]; + + SOPS_AGE_KEY_DIRECTORY = import ./const/sops_age_key_directory.nix; + + shellHook = '' + git config core.hooksPath .hooks + ''; + }; + }); + + nixosConfigurations = nixosSystems; + + darwinConfigurations = darwinSystems; + + homeConfigurations = homeConfigurations; + }; } diff --git a/modules/common-modules/default.nix b/modules/common-modules/default.nix new file mode 100644 index 0000000..3dd1923 --- /dev/null +++ b/modules/common-modules/default.nix @@ -0,0 +1,7 @@ +# this folder is for modules that are common between nixos, home-manager, and darwin +{...}: { + imports = [ + ./overlays + ./pkgs + ]; +} diff --git a/modules/common-modules/overlays/default.nix b/modules/common-modules/overlays/default.nix new file mode 100644 index 0000000..3def9e9 --- /dev/null +++ b/modules/common-modules/overlays/default.nix @@ -0,0 +1,10 @@ +# this folder is for derivation overlays +{inputs, ...}: { + nixpkgs.overlays = [ + inputs.nix-vscode-extensions.overlays.default + # Add noita_entangled_worlds from upstream flake to pkgs + (final: prev: { + noita_entangled_worlds = inputs.noita-entangled-worlds.packages.${prev.stdenv.hostPlatform.system}.noita-proxy; + }) + ]; +} diff --git a/modules/common-modules/pkgs/cline/cline-package-lock.json b/modules/common-modules/pkgs/cline/cline-package-lock.json new file mode 100644 index 0000000..b8d0d6e --- /dev/null +++ b/modules/common-modules/pkgs/cline/cline-package-lock.json @@ -0,0 +1,4102 @@ +{ + "name": "cline", + "version": "2.4.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cline", + "version": "2.4.2", + "cpu": [ + "x64", + "arm64" + ], + "license": "Apache-2.0", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@agentclientprotocol/sdk": "^0.13.1", + "aws4fetch": "^1.0.20", + "chalk": "^5.3.0", + "commander": "^12.1.0", + "ink": "npm:@jrichman/ink@6.4.7", + "ink-picture": "^1.3.3", + "ink-spinner": "^5.0.0", + "nanoid": "^5.1.6", + "ora": "^8.0.1", + "pino": "^10.0.0", + "pino-roll": "^4.0.0", + "prompts": "^2.4.2", + "react": "^19.2.3" + }, + "bin": { + "cline": "dist/cli.mjs" + }, + "devDependencies": { + "@types/node": "20.x", + "@types/prompts": "^2.4.9", + "@types/react": "^19.2.9", + "dotenv": "^16.4.5", + "esbuild": "^0.25.0", + "ink-testing-library": "^4.0.0", + "rimraf": "^6.0.1", + "typescript": "^5.4.5", + "vitest": "^4.0.17" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.13.1.tgz", + "integrity": "sha512-6byvu+F/xc96GBkdAx4hq6/tB3vT63DSBO4i3gYCz8nuyZMerVFna2Gkhm8EHNpZX0J9DjUxzZCW+rnHXUg0FA==", + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/@alcalzone/ansi-tokenize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.2.5.tgz", + "integrity": "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", + "integrity": "sha512-mr0tmS/4FoVk1cnaeN244A/wjvGDNItZKR8hRhnmCzygyRXYtKF5jVDSIILR1U97CTzAYmbgIj/Dukg62ggG5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.58.0.tgz", + "integrity": "sha512-+s++dbp+/RTte62mQD9wLSbiMTV+xr/PeRJEc/sFZFSBRlHPNPVaf5FXlzAL77Mr8FtSfQqCN+I598M8U41ccQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.58.0.tgz", + "integrity": "sha512-MFWBwTcYs0jZbINQBXHfSrpSQJq3IUOakcKPzfeSznONop14Pxuqa0Kg19GD0rNBMPQI2tFtu3UzapZpH0Uc1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.58.0.tgz", + "integrity": "sha512-yiKJY7pj9c9JwzuKYLFaDZw5gma3fI9bkPEIyofvVfsPqjCWPglSHdpdwXpKGvDeYDms3Qal8qGMEHZ1M/4Udg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.58.0.tgz", + "integrity": "sha512-x97kCoBh5MOevpn/CNK9W1x8BEzO238541BGWBc315uOlN0AD/ifZ1msg+ZQB05Ux+VF6EcYqpiagfLJ8U3LvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.58.0.tgz", + "integrity": "sha512-Aa8jPoZ6IQAG2eIrcXPpjRcMjROMFxCt1UYPZZtCxRV68WkuSigYtQ/7Zwrcr2IvtNJo7T2JfDXyMLxq5L4Jlg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.58.0.tgz", + "integrity": "sha512-Ob8YgT5kD/lSIYW2Rcngs5kNB/44Q2RzBSPz9brf2WEtcGR7/f/E9HeHn1wYaAwKBni+bdXEwgHvUd0x12lQSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.58.0.tgz", + "integrity": "sha512-K+RI5oP1ceqoadvNt1FecL17Qtw/n9BgRSzxif3rTL2QlIu88ccvY+Y9nnHe/cmT5zbH9+bpiJuG1mGHRVwF4Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.58.0.tgz", + "integrity": "sha512-T+17JAsCKUjmbopcKepJjHWHXSjeW7O5PL7lEFaeQmiVyw4kkc5/lyYKzrv6ElWRX/MrEWfPiJWqbTvfIvjM1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.58.0.tgz", + "integrity": "sha512-cCePktb9+6R9itIJdeCFF9txPU7pQeEHB5AbHu/MKsfH/k70ZtOeq1k4YAtBv9Z7mmKI5/wOLYjQ+B9QdxR6LA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.58.0.tgz", + "integrity": "sha512-iekUaLkfliAsDl4/xSdoCJ1gnnIXvoNz85C8U8+ZxknM5pBStfZjeXgB8lXobDQvvPRCN8FPmmuTtH+z95HTmg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.58.0.tgz", + "integrity": "sha512-68ofRgJNl/jYJbxFjCKE7IwhbfxOl1muPN4KbIqAIe32lm22KmU7E8OPvyy68HTNkI2iV/c8y2kSPSm2mW/Q9Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.58.0.tgz", + "integrity": "sha512-dpz8vT0i+JqUKuSNPCP5SYyIV2Lh0sNL1+FhM7eLC457d5B9/BC3kDPp5BBftMmTNsBarcPcoz5UGSsnCiw4XQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.58.0.tgz", + "integrity": "sha512-4gdkkf9UJ7tafnweBCR/mk4jf3Jfl0cKX9Np80t5i78kjIH0ZdezUv/JDI2VtruE5lunfACqftJ8dIMGN4oHew==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.58.0.tgz", + "integrity": "sha512-YFS4vPnOkDTD/JriUeeZurFYoJhPf9GQQEF/v4lltp3mVcBmnsAdjEWhr2cjUCZzZNzxCG0HZOvJU44UGHSdzw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.58.0.tgz", + "integrity": "sha512-x2xgZlFne+QVNKV8b4wwaCS8pwq3y14zedZ5DqLzjdRITvreBk//4Knbcvm7+lWmms9V9qFp60MtUd0/t/PXPw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.58.0.tgz", + "integrity": "sha512-jIhrujyn4UnWF8S+DHSkAkDEO3hLX0cjzxJZPLF80xFyzyUIYgSMRcYQ3+uqEoyDD2beGq7Dj7edi8OnJcS/hg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.58.0.tgz", + "integrity": "sha512-+410Srdoh78MKSJxTQ+hZ/Mx+ajd6RjjPwBPNd0R3J9FtL6ZA0GqiiyNjCO9In0IzZkCNrpGymSfn+kgyPQocg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.58.0.tgz", + "integrity": "sha512-ZjMyby5SICi227y1MTR3VYBpFTdZs823Rs/hpakufleBoufoOIB6jtm9FEoxn/cgO7l6PM2rCEl5Kre5vX0QrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.58.0.tgz", + "integrity": "sha512-ds4iwfYkSQ0k1nb8LTcyXw//ToHOnNTJtceySpL3fa7tc/AsE+UpUFphW126A6fKBGJD5dhRvg8zw1rvoGFxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.58.0.tgz", + "integrity": "sha512-fd/zpJniln4ICdPkjWFhZYeY/bpnaN9pGa6ko+5WD38I0tTqk9lXMgXZg09MNdhpARngmxiCg0B0XUamNw/5BQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.58.0.tgz", + "integrity": "sha512-YpG8dUOip7DCz3nr/JUfPbIUo+2d/dy++5bFzgi4ugOGBIox+qMbbqt/JoORwvI/C9Kn2tz6+Bieoqd5+B1CjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.58.0.tgz", + "integrity": "sha512-b9DI8jpFQVh4hIXFr0/+N/TzLdpBIoPzjt0Rt4xJbW3mzguV3mduR9cNgiuFcuL/TeORejJhCWiAXe3E/6PxWA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.58.0.tgz", + "integrity": "sha512-CSrVpmoRJFN06LL9xhkitkwUcTZtIotYAF5p6XOR2zW0Zz5mzb3IPpcoPhB02frzMHFNo1reQ9xSF5fFm3hUsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.58.0.tgz", + "integrity": "sha512-QFsBgQNTnh5K0t/sBsjJLq24YVqEIVkGpfN2VHsnN90soZyhaiA9UUHufcctVNL4ypJY0wrwad0wslx2KJQ1/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", + "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "kleur": "^3.0.3" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-path/-/app-path-4.0.0.tgz", + "integrity": "sha512-mgBO9PZJ3MpbKbwFTljTi36ZKBvG5X/fkVR1F85ANsVcVllEb+C0LGNdJfGUm84GpC4xxgN6HFkmkMU8VEO4mA==", + "license": "MIT", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/auto-bind": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", + "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/aws4fetch": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz", + "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", + "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-toolkit": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ink": { + "name": "@jrichman/ink", + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.7.tgz", + "integrity": "sha512-QHyxhNF5VonF5cRmdAJD/UPucB9nRx3FozWMjQrDGfBxfAL9lpyu72/MlFPgloS1TMTGsOt7YN6dTPPA6mh0Aw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@alcalzone/ansi-tokenize": "^0.2.1", + "ansi-escapes": "^7.0.0", + "ansi-styles": "^6.2.1", + "auto-bind": "^5.0.1", + "chalk": "^5.6.0", + "cli-boxes": "^3.0.0", + "cli-cursor": "^4.0.0", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "es-toolkit": "^1.39.10", + "indent-string": "^5.0.0", + "is-in-ci": "^2.0.0", + "mnemonist": "^0.40.3", + "patch-console": "^2.0.0", + "react-reconciler": "^0.32.0", + "signal-exit": "^3.0.7", + "slice-ansi": "^7.1.0", + "stack-utils": "^2.0.6", + "string-width": "^8.1.0", + "type-fest": "^4.27.0", + "wrap-ansi": "^9.0.0", + "ws": "^8.18.0", + "yoga-layout": "~3.2.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@types/react": ">=19.0.0", + "react": ">=19.0.0", + "react-devtools-core": "^6.1.2" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-devtools-core": { + "optional": true + } + } + }, + "node_modules/ink-picture": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/ink-picture/-/ink-picture-1.3.3.tgz", + "integrity": "sha512-kFDZaiqnvbM2cU46ptIHFeh4RJkfsZfQUh657JmbOmD/swANdCkUvcb8c3Qv6270qFWKwRehf04fghMwtHwEGw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.6.0", + "is-unicode-supported": "^2.1.0", + "iterm2-version": "^5.0.0", + "node-fetch": "^3.3.2", + "sharp": "^0.34.3", + "sixel": "^0.16.0", + "supports-color": "^10.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "ink": ">=5", + "react": ">=18" + } + }, + "node_modules/ink-spinner": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-5.0.0.tgz", + "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==", + "license": "MIT", + "dependencies": { + "cli-spinners": "^2.7.0" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "ink": ">=4.0.0", + "react": ">=18.0.0" + } + }, + "node_modules/ink-testing-library": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ink-testing-library/-/ink-testing-library-4.0.0.tgz", + "integrity": "sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/react": ">=18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-in-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-2.0.0.tgz", + "integrity": "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==", + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/iterm2-version": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/iterm2-version/-/iterm2-version-5.0.0.tgz", + "integrity": "sha512-WdLXcMYvN3SXT6vEtuW78vnZs4pVWm2nBnb4VKjOPPXmdlR1xTHmBgqKacOzAe4RXOiY/V+0u/0zsU3LoGQoBg==", + "license": "MIT", + "dependencies": { + "app-path": "^4.0.0", + "plist": "^3.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", + "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mnemonist": { + "version": "0.40.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.3.tgz", + "integrity": "sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==", + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.4" + } + }, + "node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/patch-console": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", + "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-roll": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-roll/-/pino-roll-4.0.0.tgz", + "integrity": "sha512-axI1aQaIxXdw1F4OFFli1EDxIrdYNGLowkw/ZoZogX8oCSLHUghzwVVXUS8U+xD/Savwa5IXpiXmsSGKFX/7Sg==", + "license": "MIT", + "dependencies": { + "date-fns": "^4.1.0", + "sonic-boom": "^4.0.1" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-reconciler": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz", + "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.58.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.58.0.tgz", + "integrity": "sha512-wbT0mBmWbIvvq8NeEYWWvevvxnOyhKChir47S66WCxw1SXqhw7ssIYejnQEVt7XYQpsj2y8F9PM+Cr3SNEa0gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.58.0", + "@rollup/rollup-android-arm64": "4.58.0", + "@rollup/rollup-darwin-arm64": "4.58.0", + "@rollup/rollup-darwin-x64": "4.58.0", + "@rollup/rollup-freebsd-arm64": "4.58.0", + "@rollup/rollup-freebsd-x64": "4.58.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.58.0", + "@rollup/rollup-linux-arm-musleabihf": "4.58.0", + "@rollup/rollup-linux-arm64-gnu": "4.58.0", + "@rollup/rollup-linux-arm64-musl": "4.58.0", + "@rollup/rollup-linux-loong64-gnu": "4.58.0", + "@rollup/rollup-linux-loong64-musl": "4.58.0", + "@rollup/rollup-linux-ppc64-gnu": "4.58.0", + "@rollup/rollup-linux-ppc64-musl": "4.58.0", + "@rollup/rollup-linux-riscv64-gnu": "4.58.0", + "@rollup/rollup-linux-riscv64-musl": "4.58.0", + "@rollup/rollup-linux-s390x-gnu": "4.58.0", + "@rollup/rollup-linux-x64-gnu": "4.58.0", + "@rollup/rollup-linux-x64-musl": "4.58.0", + "@rollup/rollup-openbsd-x64": "4.58.0", + "@rollup/rollup-openharmony-arm64": "4.58.0", + "@rollup/rollup-win32-arm64-msvc": "4.58.0", + "@rollup/rollup-win32-ia32-msvc": "4.58.0", + "@rollup/rollup-win32-x64-gnu": "4.58.0", + "@rollup/rollup-win32-x64-msvc": "4.58.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sixel": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/sixel/-/sixel-0.16.0.tgz", + "integrity": "sha512-xicu6Y6Cyhmv5rjyHxq2r5RnKerlL/nyZEGjOU5bLCshXkZryc9JFJThTCKPOAtWXCfeWquEKFVFfMPcTD25PA==", + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", + "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/thread-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz", + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", + "license": "MIT" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/modules/common-modules/pkgs/cline/default.nix b/modules/common-modules/pkgs/cline/default.nix new file mode 100644 index 0000000..05dbf48 --- /dev/null +++ b/modules/common-modules/pkgs/cline/default.nix @@ -0,0 +1,53 @@ +{ + lib, + buildNpmPackage, + fetchurl, + ripgrep, + makeWrapper, + jq, + ... +}: +buildNpmPackage rec { + pname = "cline"; + version = "2.4.2"; + + src = fetchurl { + url = "https://registry.npmjs.org/cline/-/cline-${version}.tgz"; + hash = "sha256-2utOBC0vhoj5fR+cG+Vdo3N6+i/pNW1E4mESF/dZS/c="; + }; + + sourceRoot = "package"; + + postPatch = '' + cp ${./cline-package-lock.json} package-lock.json + + # Remove @vscode/ripgrep from package.json since it tries to download + # a binary from GitHub during install, which fails in the nix sandbox. + # We provide ripgrep from nixpkgs instead via PATH wrapping. + # Also remove the man field since the man page is not included in the npm tarball. + ${jq}/bin/jq 'del(.dependencies["@vscode/ripgrep"]) | del(.man)' package.json > package.json.tmp + mv package.json.tmp package.json + ''; + + npmDepsHash = "sha256-oHo60ghR7A4SUT0cLmIe7glPDYBK3twJ0F71RKVrxQc="; + + dontNpmBuild = true; + + # Skip post-install scripts to be safe + npmFlags = ["--ignore-scripts"]; + + nativeBuildInputs = [makeWrapper jq]; + + # Provide ripgrep from nixpkgs since @vscode/ripgrep was removed + postInstall = '' + wrapProgram $out/bin/cline \ + --prefix PATH : ${lib.makeBinPath [ripgrep]} + ''; + + meta = with lib; { + description = "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more"; + homepage = "https://cline.bot"; + license = licenses.asl20; + mainProgram = "cline"; + }; +} diff --git a/modules/common-modules/pkgs/codium-extensions/ai-code.nix b/modules/common-modules/pkgs/codium-extensions/ai-code.nix new file mode 100644 index 0000000..9c9efe3 --- /dev/null +++ b/modules/common-modules/pkgs/codium-extensions/ai-code.nix @@ -0,0 +1,42 @@ +{ + buildNpmPackage, + vscode-utils, + pkgs, + ... +}: let + version = "0.0.1"; + pname = "ai-code"; + publisher = "jan-leila"; + vsix = buildNpmPackage { + inherit version pname; + + src = builtins.fetchGit { + url = "ssh://git@git.jan-leila.com/jan-leila/ai-code.git"; + rev = "d48e01713021dbb30de0ebbee2cfaf99e4e9b5a6"; + }; + + npmDepsHash = "sha256-kjMyEnT3dz0yH5Ydh+aGoFDocKpBYGRmfnwbEdvvgpY="; + + nativeBuildInputs = with pkgs; [ + vsce + ]; + + buildPhase = '' + ${pkgs.vsce}/bin/vsce package -o ${pname}.zip + ''; + + installPhase = '' + mkdir -p $out + mv ${pname}.zip $out/${pname}.zip + ''; + }; +in + vscode-utils.buildVscodeExtension { + inherit pname version; + + src = "${vsix}/${pname}.zip"; + + vscodeExtUniqueId = "${publisher}.${pname}"; + vscodeExtPublisher = publisher; + vscodeExtName = pname; + } diff --git a/modules/common-modules/pkgs/codium-extensions/default.nix b/modules/common-modules/pkgs/codium-extensions/default.nix new file mode 100644 index 0000000..a60e8a0 --- /dev/null +++ b/modules/common-modules/pkgs/codium-extensions/default.nix @@ -0,0 +1,3 @@ +{pkgs, ...}: { + ai-code = pkgs.callPackage ./ai-code.nix {}; +} diff --git a/modules/common-modules/pkgs/default.nix b/modules/common-modules/pkgs/default.nix new file mode 100644 index 0000000..81af054 --- /dev/null +++ b/modules/common-modules/pkgs/default.nix @@ -0,0 +1,51 @@ +{ + pkgs, + inputs, + ... +}: { + imports = [ + ./python + ]; + + nixpkgs.overlays = [ + (final: prev: { + webtoon-dl = + pkgs.callPackage + ./webtoon-dl.nix + {}; + }) + (final: prev: { + prostudiomasters = + pkgs.callPackage + ./prostudiomasters.nix + {}; + }) + (final: prev: { + gdx-liftoff = pkgs.callPackage ./gdx-liftoff.nix {}; + }) + (final: prev: { + codium-extensions = pkgs.callPackage ./codium-extensions {}; + }) + (final: prev: { + firefox-extensions = pkgs.callPackage ./firefox-extensions { + inherit inputs; + }; + }) + (final: prev: { + mapillary-uploader = pkgs.callPackage ./mapillary-uploader.nix {}; + }) + (final: prev: { + panoramax = pkgs.python3.pkgs.callPackage ./panoramax.nix {}; + }) + (final: prev: { + sgblur = pkgs.python3.pkgs.callPackage ./sgblur.nix {}; + }) + (final: prev: { + # Override h3 C library to version 4.3.0 + h3 = pkgs.callPackage ./h3-c-lib.nix {}; + }) + (final: prev: { + cline = pkgs.callPackage ./cline/default.nix {}; + }) + ]; +} diff --git a/modules/common-modules/pkgs/firefox-extensions/default.nix b/modules/common-modules/pkgs/firefox-extensions/default.nix new file mode 100644 index 0000000..922dfc7 --- /dev/null +++ b/modules/common-modules/pkgs/firefox-extensions/default.nix @@ -0,0 +1,17 @@ +{ + pkgs, + inputs, + ... +}: let + inherit (inputs.firefox-addons.lib.${pkgs.stdenv.hostPlatform.system}) buildFirefoxXpiAddon; +in { + italiano-it-language-pack = pkgs.callPackage ./italiano-it-language-pack.nix { + inherit buildFirefoxXpiAddon; + }; + dizionario-italiano = pkgs.callPackage ./dizionario-italiano.nix { + inherit buildFirefoxXpiAddon; + }; + deutsch-de-language-pack = pkgs.callPackage ./deutsch-de-language-pack.nix { + inherit buildFirefoxXpiAddon; + }; +} diff --git a/modules/common-modules/pkgs/firefox-extensions/deutsch-de-language-pack.nix b/modules/common-modules/pkgs/firefox-extensions/deutsch-de-language-pack.nix new file mode 100644 index 0000000..b769bfd --- /dev/null +++ b/modules/common-modules/pkgs/firefox-extensions/deutsch-de-language-pack.nix @@ -0,0 +1,18 @@ +{ + lib, + buildFirefoxXpiAddon, + ... +}: +buildFirefoxXpiAddon rec { + pname = "deutsch-de-language-pack"; + version = "145.0.20251106.194447"; + addonId = "langpack-de@firefox.mozilla.org"; + url = "https://addons.mozilla.org/firefox/downloads/file/4614311/deutsch_de_language_pack-${version}.xpi"; + sha256 = "aaaa95c29984fb3802a5e7edb6b7e5020c391d81f389b8a8133c163959ea4299"; + meta = with lib; { + description = "Firefox Language Pack for Deutsch (de) – German"; + license = licenses.mpl20; + mozPermissions = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/firefox-extensions/dizionario-italiano.nix b/modules/common-modules/pkgs/firefox-extensions/dizionario-italiano.nix new file mode 100644 index 0000000..4bfca14 --- /dev/null +++ b/modules/common-modules/pkgs/firefox-extensions/dizionario-italiano.nix @@ -0,0 +1,18 @@ +{ + lib, + buildFirefoxXpiAddon, + ... +}: +buildFirefoxXpiAddon rec { + pname = "dizionario-italiano"; + version = "5.1"; + addonId = "it-IT@dictionaries.addons.mozilla.org"; + url = "https://addons.mozilla.org/firefox/downloads/file/3693497/dizionario_italiano-${version}.xpi"; + sha256 = "90b173ffdde34a77108152a5ff51879767b1dd84e0aa0dfb7b2bab94cd2e7f53"; + meta = with lib; { + description = "Add support for Italian to spellchecking"; + license = licenses.gpl3; + mozPermissions = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/firefox-extensions/italiano-it-language-pack.nix b/modules/common-modules/pkgs/firefox-extensions/italiano-it-language-pack.nix new file mode 100644 index 0000000..35f4243 --- /dev/null +++ b/modules/common-modules/pkgs/firefox-extensions/italiano-it-language-pack.nix @@ -0,0 +1,18 @@ +{ + lib, + buildFirefoxXpiAddon, + ... +}: +buildFirefoxXpiAddon rec { + pname = "italiano-it-language-pack"; + version = "145.0.20251106.194447"; + addonId = "langpack-it@firefox.mozilla.org"; + url = "https://addons.mozilla.org/firefox/downloads/file/4614309/italiano_it_language_pack-${version}.xpi"; + sha256 = "1eb271cedbf326543e222ba1b9a1da62fceef9d3c523ac02a098df296f155038"; + meta = with lib; { + description = "Firefox Language Pack for Italiano (it) – Italian"; + license = licenses.mpl20; + mozPermissions = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/gdx-liftoff.nix b/modules/common-modules/pkgs/gdx-liftoff.nix new file mode 100644 index 0000000..da7d51f --- /dev/null +++ b/modules/common-modules/pkgs/gdx-liftoff.nix @@ -0,0 +1,48 @@ +{ + stdenv, + fetchurl, + makeWrapper, + jdk, + lib, + libGL, + libx11, + libxcursor, + libxext, + libxrandr, + libxxf86vm, + ... +}: +stdenv.mkDerivation rec { + pname = "gdx-liftoff"; + version = "1.13.5.1"; + + src = fetchurl { + url = "https://github.com/libgdx/gdx-liftoff/releases/download/v${version}/gdx-liftoff-${version}.jar"; + hash = "sha256-9vCXGNGwI/P4VmcdIzTv2GPAX8bZb7nkfopaRAf6yMA="; + }; + + dontUnpack = true; + + nativeBuildInputs = [makeWrapper]; + + runtimeDependencies = lib.makeLibraryPath [ + # glfw + libGL + libx11 + libxcursor + libxext + libxrandr + libxxf86vm + ]; + + installPhase = '' + runHook preInstall + + install -Dm644 $src $out/lib/gdx-liftoff-${version}.jar + + makeWrapper ${lib.getExe jdk} $out/bin/gdx-liftoff-${version} \ + --append-flags "-jar $out/lib/gdx-liftoff-${version}.jar"\ + ${lib.optionalString stdenv.hostPlatform.isLinux "--prefix LD_LIBRARY_PATH : ${runtimeDependencies}"} + runHook postInstall + ''; +} diff --git a/modules/common-modules/pkgs/h3-c-lib.nix b/modules/common-modules/pkgs/h3-c-lib.nix new file mode 100644 index 0000000..2615d3c --- /dev/null +++ b/modules/common-modules/pkgs/h3-c-lib.nix @@ -0,0 +1,36 @@ +{ + lib, + stdenv, + fetchFromGitHub, + cmake, + doxygen, +}: +stdenv.mkDerivation rec { + pname = "h3"; + version = "4.3.0"; + + src = fetchFromGitHub { + owner = "uber"; + repo = "h3"; + rev = "v${version}"; + hash = "sha256-DUILKZ1QvML6qg+WdOxir6zRsgTvk+En6yjeFf6MQBg="; + }; + + nativeBuildInputs = [ + cmake + doxygen + ]; + + cmakeFlags = [ + "-DBUILD_SHARED_LIBS=ON" + "-DBUILD_TESTING=OFF" + ]; + + meta = with lib; { + homepage = "https://github.com/uber/h3"; + description = "Hexagonal hierarchical geospatial indexing system"; + license = licenses.asl20; + maintainers = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/mapillary-uploader.nix b/modules/common-modules/pkgs/mapillary-uploader.nix new file mode 100644 index 0000000..acff772 --- /dev/null +++ b/modules/common-modules/pkgs/mapillary-uploader.nix @@ -0,0 +1,39 @@ +{ + lib, + fetchurl, + appimageTools, +}: let + pname = "mapillary-uploader"; + version = "4.7.2"; + + src = fetchurl { + url = "http://tools.mapillary.com/uploader/download/linux/${version}"; + name = "mapillary-uploader.AppImage"; + sha256 = "sha256-hpWdfeuhYylO+SFD3BsKI0s/xtObCDd5OcuJ6i/aEuI="; + }; + + appimageContents = appimageTools.extractType2 { + inherit pname version src; + }; +in + appimageTools.wrapType2 { + inherit pname version src; + + extraInstallCommands = '' + # Install desktop file + install -Dm644 ${appimageContents}/mapillary-desktop-uploader.desktop $out/share/applications/mapillary-uploader.desktop + + # Fix desktop file paths + substituteInPlace $out/share/applications/mapillary-uploader.desktop \ + --replace 'Exec=AppRun' 'Exec=${pname}' + ''; + + meta = with lib; { + description = "Mapillary Desktop Uploader - Upload street-level imagery to Mapillary"; + homepage = "https://www.mapillary.com/"; + license = licenses.unfree; # Mapillary's license terms + maintainers = []; + platforms = ["x86_64-linux"]; + sourceProvenance = with sourceTypes; [binaryNativeCode]; + }; + } diff --git a/modules/common-modules/pkgs/panoramax.nix b/modules/common-modules/pkgs/panoramax.nix new file mode 100644 index 0000000..75b5e0e --- /dev/null +++ b/modules/common-modules/pkgs/panoramax.nix @@ -0,0 +1,105 @@ +{ + lib, + fetchFromGitLab, + buildPythonPackage, + flit-core, + flask, + pillow, + requests, + python-dotenv, + authlib, + sentry-sdk, + python-dateutil, + dateparser, + croniter, + pydantic, + flask-cors, + flask-compress, + flask-babel, + flasgger, + yoyo-migrations, + psycopg, + psycopg-pool, + tzdata, + email-validator, + pydantic-extra-types, + python-multipart, + fs, + fs-s3fs, + geopic-tag-reader, + pygeofilter, + pygeoif, + rfeed, + geojson-pydantic, + ... +}: let + pname = "geovisio"; + version = "2.10.0"; + repo = fetchFromGitLab { + owner = "panoramax"; + repo = "server/api"; + rev = version; + hash = "sha256-kCLcrOe7jJdIfmWWOmxQ5dOj8ZG2B7s0qFpHXs02B/E="; + }; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = repo; + + build-system = [ + flit-core + ]; + + dependencies = [ + flask + pillow + requests + python-dotenv + authlib + sentry-sdk + python-dateutil + dateparser + croniter + pydantic + flask-cors + flask-compress + flask-babel + flasgger + yoyo-migrations + psycopg + psycopg-pool + tzdata + email-validator + pydantic-extra-types + python-multipart + fs + fs-s3fs + geopic-tag-reader + pygeofilter + pygeoif + rfeed + geojson-pydantic + # Missing from nixpkgs - may need custom packages: + # flask-executor + ]; + + # Skip tests as they may require network access or specific setup + doCheck = false; + + # Disable runtime dependencies check as many dependencies are not available in nixpkgs + dontCheckRuntimeDeps = true; + + # Disable imports check as many dependencies are not available in nixpkgs + pythonImportsCheck = []; + + meta = with lib; { + description = "Panoramax API client and tools for street-level imagery platform"; + homepage = "https://gitlab.com/panoramax/server/api"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/prostudiomasters.nix b/modules/common-modules/pkgs/prostudiomasters.nix new file mode 100644 index 0000000..1a3ad01 --- /dev/null +++ b/modules/common-modules/pkgs/prostudiomasters.nix @@ -0,0 +1,33 @@ +{ + fetchurl, + appimageTools, + writeShellScript, +}: let + pname = "prostudiomasters"; + version = "2.5.6"; + src = fetchurl { + url = "https://download.prostudiomasters.com/linux/ProStudioMasters-${version}.AppImage"; + hash = "sha256-7owOwdcucFfl+JsVj+Seau2KOz0J4P/ep7WrBSNSmbs="; + }; + + # Create the base AppImage wrapper + baseApp = appimageTools.wrapType2 { + inherit pname version src; + }; + + # Create a wrapper script that automatically adds the --in-process-gpu flag + wrapper = writeShellScript "prostudiomasters-wrapper" '' + exec ${baseApp}/bin/prostudiomasters --in-process-gpu "$@" + ''; +in + # Override the base app to use our wrapper script + baseApp.overrideAttrs (oldAttrs: { + buildCommand = + oldAttrs.buildCommand + + '' + # Replace the original binary with our wrapper + rm $out/bin/prostudiomasters + cp ${wrapper} $out/bin/prostudiomasters + chmod +x $out/bin/prostudiomasters + ''; + }) diff --git a/modules/common-modules/pkgs/python/default.nix b/modules/common-modules/pkgs/python/default.nix new file mode 100644 index 0000000..f69c512 --- /dev/null +++ b/modules/common-modules/pkgs/python/default.nix @@ -0,0 +1,18 @@ +{...}: { + nixpkgs.overlays = [ + (final: prev: { + python3 = prev.python3.override { + packageOverrides = pythonPrev: pythonFinal: { + h3 = pythonPrev.callPackage ./h3.nix {h3 = final.h3;}; + pygeofilter = pythonPrev.callPackage ./pygeofilter.nix {}; + pygeoif = pythonPrev.callPackage ./pygeoif.nix {}; + rfeed = pythonPrev.callPackage ./rfeed.nix {}; + pyexiv2 = pythonPrev.callPackage ./pyexiv2.nix {}; + geojson-pydantic = pythonPrev.callPackage ./geojson-pydantic.nix {}; + geopic-tag-reader = pythonPrev.callPackage ./geopic-tag-reader.nix {}; + }; + }; + python3Packages = final.python3.pkgs; + }) + ]; +} diff --git a/modules/common-modules/pkgs/python/geojson-pydantic.nix b/modules/common-modules/pkgs/python/geojson-pydantic.nix new file mode 100644 index 0000000..96ec6b5 --- /dev/null +++ b/modules/common-modules/pkgs/python/geojson-pydantic.nix @@ -0,0 +1,48 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + flit-core, + pydantic, + geojson, + ... +}: let + pname = "geojson_pydantic"; + version = "2.0.0"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-ti6LRFAt0a1Ri19zkDWoGSSnb5gMvbOk6JFu+RO+JC4="; + }; + + build-system = [ + flit-core + ]; + + dependencies = [ + pydantic + geojson + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["geojson_pydantic"]; + + meta = with lib; { + description = "Pydantic models for GeoJSON objects"; + homepage = "https://github.com/developmentseed/geojson-pydantic"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/geopic-tag-reader.nix b/modules/common-modules/pkgs/python/geopic-tag-reader.nix new file mode 100644 index 0000000..bd8451f --- /dev/null +++ b/modules/common-modules/pkgs/python/geopic-tag-reader.nix @@ -0,0 +1,70 @@ +{ + lib, + fetchFromGitLab, + buildPythonPackage, + flit-core, + typer, + xmltodict, + timezonefinder, + pytz, + types-pytz, + types-python-dateutil, + rtree, + python-dateutil, + pyexiv2, + ... +}: let + pname = "geopic-tag-reader"; + version = "1.8.0"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchFromGitLab { + owner = "panoramax"; + repo = "server/geo-picture-tag-reader"; + rev = version; + sha256 = "0lzf5xxxcdqmq28bpvgpkxf5jxmh2nawwa4rl4yg04bdsi16rf1j"; + }; + + build-system = [ + flit-core + ]; + + dependencies = [ + typer + xmltodict + pyexiv2 + timezonefinder + pytz + types-pytz + types-python-dateutil + rtree + ]; + + optional-dependencies = { + write-exif = [ + python-dateutil + types-python-dateutil + ]; + }; + + # Skip tests as they may require network access or specific setup + doCheck = false; + + # Disable runtime dependencies check as some dependencies might have issues + dontCheckRuntimeDeps = true; + + # Disable imports check initially to avoid dependency issues + pythonImportsCheck = []; + + meta = with lib; { + description = "GeoPic Tag Reader - Python library to read and write standardized metadata from geolocated pictures EXIF metadata"; + homepage = "https://gitlab.com/panoramax/server/geo-picture-tag-reader"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/h3.nix b/modules/common-modules/pkgs/python/h3.nix new file mode 100644 index 0000000..2dc3d26 --- /dev/null +++ b/modules/common-modules/pkgs/python/h3.nix @@ -0,0 +1,81 @@ +{ + autoPatchelfHook, + buildPythonPackage, + cmake, + cython, + fetchFromGitHub, + h3, + lib, + ninja, + numpy, + pytestCheckHook, + pytest-cov-stub, + scikit-build-core, + stdenv, +}: +buildPythonPackage rec { + pname = "h3"; + version = "4.3.1"; + pyproject = true; + + # pypi version does not include tests + src = fetchFromGitHub { + owner = "uber"; + repo = "h3-py"; + tag = "v${version}"; + hash = "sha256-zt7zbBgSp2P9q7mObZeQZpW9Szip62dAYdPZ2cGTmi4="; + }; + + dontConfigure = true; + + nativeCheckInputs = [ + pytestCheckHook + pytest-cov-stub + ]; + + build-system = + [ + scikit-build-core + cmake + cython + ninja + ] + ++ lib.optionals stdenv.hostPlatform.isLinux [ + # On Linux the .so files ends up referring to libh3.so instead of the full + # Nix store path. I'm not sure why this is happening! On Darwin it works + # fine. + autoPatchelfHook + ]; + + # This is not needed per-se, it's only added for autoPatchelfHook to work + # correctly. See the note above ^^ + buildInputs = lib.optionals stdenv.hostPlatform.isLinux [h3]; + + dependencies = [numpy]; + + # The following prePatch replaces the h3lib compilation with using the h3 packaged in nixpkgs. + # + # - Remove the h3lib submodule. + # - Patch CMakeLists to avoid building h3lib, and use h3 instead. + prePatch = let + cmakeCommands = '' + include_directories(${lib.getDev h3}/include/h3) + link_directories(${h3}/lib) + ''; + in '' + rm -r src/h3lib + substituteInPlace CMakeLists.txt \ + --replace-fail "add_subdirectory(src/h3lib)" "${cmakeCommands}" \ + --replace-fail "\''${CMAKE_CURRENT_BINARY_DIR}/src/h3lib/src/h3lib/include/h3api.h" "${lib.getDev h3}/include/h3/h3api.h" + ''; + + # Extra check to make sure we can import it from Python + pythonImportsCheck = ["h3"]; + + meta = { + homepage = "https://github.com/uber/h3-py"; + description = "Hierarchical hexagonal geospatial indexing system"; + license = lib.licenses.asl20; + maintainers = [lib.maintainers.kalbasit]; + }; +} diff --git a/modules/common-modules/pkgs/python/pyexiv2.nix b/modules/common-modules/pkgs/python/pyexiv2.nix new file mode 100644 index 0000000..69fa537 --- /dev/null +++ b/modules/common-modules/pkgs/python/pyexiv2.nix @@ -0,0 +1,49 @@ +{ + lib, + fetchFromGitHub, + buildPythonPackage, + exiv2, + boost, + pybind11, + setuptools, + ... +}: let + pname = "pyexiv2"; + version = "2.15.3"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + build-system = [setuptools]; + + src = fetchFromGitHub { + owner = "LeoHsiao1"; + repo = "pyexiv2"; + rev = "v${version}"; + sha256 = "sha256-83bFMaoXncvhRJNcCgkkC7B29wR5pjuLO/EdkQdqxxo="; + }; + + buildInputs = [ + exiv2 + boost + ]; + + nativeBuildInputs = [ + pybind11 + ]; + + # Skip tests as they may require specific test images + doCheck = false; + + # Disable runtime dependencies check initially + dontCheckRuntimeDeps = true; + + meta = with lib; { + description = "Python binding to the library exiv2"; + homepage = "https://github.com/LeoHsiao1/pyexiv2"; + license = licenses.gpl3Plus; + maintainers = []; + platforms = platforms.linux; + }; + } diff --git a/modules/common-modules/pkgs/python/pygeofilter.nix b/modules/common-modules/pkgs/python/pygeofilter.nix new file mode 100644 index 0000000..aa310f9 --- /dev/null +++ b/modules/common-modules/pkgs/python/pygeofilter.nix @@ -0,0 +1,52 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + wheel, + lark, + python-dateutil, + shapely, + ... +}: let + pname = "pygeofilter"; + version = "0.3.1"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-+SvAYiCZ+H/os23nq92GBZ1hWontYIInNwgiI6V44VA="; + }; + + build-system = [ + setuptools + wheel + ]; + + dependencies = [ + lark + python-dateutil + shapely + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["pygeofilter"]; + + meta = with lib; { + description = "A pure Python parser implementation of OGC filtering standards"; + homepage = "https://github.com/geopython/pygeofilter"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/pygeoif.nix b/modules/common-modules/pkgs/python/pygeoif.nix new file mode 100644 index 0000000..12b8b12 --- /dev/null +++ b/modules/common-modules/pkgs/python/pygeoif.nix @@ -0,0 +1,48 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + wheel, + typing-extensions, + ... +}: let + pname = "pygeoif"; + version = "1.5.1"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-8nprah7Lh66swrUbzFnKeb5w7RKgEE3oYBR4shPdXYE="; + }; + + build-system = [ + setuptools + wheel + ]; + + dependencies = [ + typing-extensions + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["pygeoif"]; + + meta = with lib; { + description = "A basic implementation of the __geo_interface__"; + homepage = "https://github.com/cleder/pygeoif"; + license = licenses.lgpl21Plus; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/rfeed.nix b/modules/common-modules/pkgs/python/rfeed.nix new file mode 100644 index 0000000..0be8ab9 --- /dev/null +++ b/modules/common-modules/pkgs/python/rfeed.nix @@ -0,0 +1,40 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + python-dateutil, +}: +buildPythonPackage rec { + pname = "rfeed"; + version = "1.1.1"; + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-qpUG8oZrdPWjItOUoUpjwZpoJcLZR1X/GdRt0eJDSBk="; + }; + + build-system = [ + setuptools + ]; + + dependencies = [ + python-dateutil + ]; + + # No tests available in the package + doCheck = false; + + pythonImportsCheck = [ + "rfeed" + ]; + + meta = with lib; { + description = "RSS feed generation library for Python"; + homepage = "https://pypi.org/project/rfeed/"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/sgblur.nix b/modules/common-modules/pkgs/sgblur.nix new file mode 100644 index 0000000..d007b4e --- /dev/null +++ b/modules/common-modules/pkgs/sgblur.nix @@ -0,0 +1,65 @@ +{ + lib, + python3Packages, + fetchFromGitHub, + pkg-config, + libjpeg_turbo, + exiftran ? libjpeg_turbo, +}: +python3Packages.buildPythonPackage { + pname = "sgblur"; + version = "1.0.0"; + + pyproject = true; + + src = fetchFromGitHub { + owner = "cquest"; + repo = "sgblur"; + rev = "master"; + hash = "sha256-17wpif2sa021kaa1pbkry4l1967la1qd7knhngvxblrvd7jqqz4y="; + }; + + nativeBuildInputs = [ + pkg-config + ]; + + buildInputs = [ + libjpeg_turbo + exiftran + ]; + + build-system = with python3Packages; [ + setuptools + wheel + ]; + + dependencies = with python3Packages; [ + # Core dependencies from pyproject.toml + ultralytics + # pyturbojpeg # May need special handling + pillow + # uuid # Built into Python + # exifread + python-multipart + fastapi + uvicorn + requests + # piexif + pydantic-settings + pydantic + ]; + + # Skip tests as they may require GPU or specific setup + doCheck = false; + + # The package may have import issues due to system dependencies + pythonImportsCheck = []; + + meta = with lib; { + description = "Panoramax Speedy Gonzales Blurring Algorithm - AI-powered face and license plate blurring API"; + homepage = "https://github.com/cquest/sgblur"; + license = licenses.mit; + maintainers = []; + platforms = platforms.unix; + }; +} diff --git a/modules/common-modules/pkgs/webtoon-dl.nix b/modules/common-modules/pkgs/webtoon-dl.nix new file mode 100644 index 0000000..4341098 --- /dev/null +++ b/modules/common-modules/pkgs/webtoon-dl.nix @@ -0,0 +1,18 @@ +{ + buildGoModule, + fetchFromGitHub, + ... +}: +buildGoModule rec { + pname = "webtoon-dl"; + version = "0.0.10"; + + src = fetchFromGitHub { + owner = "robinovitch61"; + repo = "webtoon-dl"; + rev = "v${version}"; + hash = "sha256-geVb3LFPZxPQYARZnaqOr5sgaN6mqkEX5ZiLvg8mF5k="; + }; + + vendorHash = "sha256-NTqUygJ6b6kTnLUnJqxCo/URzaRouPLACEPi2Ob1s9w="; +} diff --git a/modules/darwin-modules/default.nix b/modules/darwin-modules/default.nix new file mode 100644 index 0000000..5f4447b --- /dev/null +++ b/modules/darwin-modules/default.nix @@ -0,0 +1,8 @@ +# this folder container modules that are for darwin only +{...}: { + imports = [ + ./home-manager + ./users.nix + ./system.nix + ]; +} diff --git a/modules/darwin-modules/home-manager/default.nix b/modules/darwin-modules/home-manager/default.nix new file mode 100644 index 0000000..1ebec5f --- /dev/null +++ b/modules/darwin-modules/home-manager/default.nix @@ -0,0 +1,2 @@ +# modules in this folder are to adapt home-manager modules configs to darwin-module configs +{...}: {} diff --git a/modules/darwin-modules/system.nix b/modules/darwin-modules/system.nix new file mode 100644 index 0000000..ee56162 --- /dev/null +++ b/modules/darwin-modules/system.nix @@ -0,0 +1,27 @@ +{self, ...}: { + system.configurationRevision = self.rev or self.dirtyRev or null; + + nix = { + gc = { + automatic = true; + interval = [ + { + Hour = 4; + Minute = 15; + Weekday = 7; + } + ]; + options = "--delete-older-than 7d"; + }; + optimise = { + automatic = true; + interval = [ + { + Hour = 4; + Minute = 15; + Weekday = 7; + } + ]; + }; + }; +} diff --git a/modules/darwin-modules/users.nix b/modules/darwin-modules/users.nix new file mode 100644 index 0000000..72fd1b1 --- /dev/null +++ b/modules/darwin-modules/users.nix @@ -0,0 +1,16 @@ +{ + lib, + config, + ... +}: let + host = config.host; +in { + users = { + users = { + leyla = { + name = lib.mkForce host.users.leyla.name; + home = lib.mkForce "/home/${host.users.leyla.name}"; + }; + }; + }; +} diff --git a/modules/darwin/default.nix b/modules/darwin/default.nix deleted file mode 100644 index 132200d..0000000 --- a/modules/darwin/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.darwinModules; -in { - flake.darwinModules.darwin-modules-all = { - imports = [ - mod.darwin-system - mod.darwin-users - ]; - }; -} diff --git a/modules/darwin/system.nix b/modules/darwin/system.nix deleted file mode 100644 index eca9a84..0000000 --- a/modules/darwin/system.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.darwinModules.darwin-system = {self, ...}: { - system.configurationRevision = self.rev or self.dirtyRev or null; - - nix = { - gc = { - automatic = true; - interval = [ - { - Hour = 4; - Minute = 15; - Weekday = 7; - } - ]; - options = "--delete-older-than 7d"; - }; - optimise = { - automatic = true; - interval = [ - { - Hour = 4; - Minute = 15; - Weekday = 7; - } - ]; - }; - }; - }; -} diff --git a/modules/darwin/users.nix b/modules/darwin/users.nix deleted file mode 100644 index 3cd4de3..0000000 --- a/modules/darwin/users.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.darwinModules.darwin-users = { - lib, - config, - ... - }: let - host = config.host; - in { - users = { - users = { - leyla = { - name = lib.mkForce host.users.leyla.name; - home = lib.mkForce "/home/${host.users.leyla.name}"; - }; - }; - }; - }; -} diff --git a/modules/home-manager-modules/default.nix b/modules/home-manager-modules/default.nix new file mode 100644 index 0000000..29d3414 --- /dev/null +++ b/modules/home-manager-modules/default.nix @@ -0,0 +1,13 @@ +# this folder container modules that are for home manager only +{...}: { + imports = [ + ./sops.nix + ./user.nix + ./flipperzero.nix + ./i18n.nix + ./impermanence.nix + ./openssh.nix + ./gnome.nix + ./programs + ]; +} diff --git a/modules/home-manager-modules/flipperzero.nix b/modules/home-manager-modules/flipperzero.nix new file mode 100644 index 0000000..6354bc0 --- /dev/null +++ b/modules/home-manager-modules/flipperzero.nix @@ -0,0 +1,3 @@ +{lib, ...}: { + options.hardware.flipperzero.enable = lib.mkEnableOption "enable flipperzero hardware"; +} diff --git a/modules/home-manager-modules/gnome.nix b/modules/home-manager-modules/gnome.nix new file mode 100644 index 0000000..ab56189 --- /dev/null +++ b/modules/home-manager-modules/gnome.nix @@ -0,0 +1,203 @@ +{ + lib, + config, + pkgs, + ... +}: let + enabledExtensions = + [] + ++ lib.optional config.gnome.extensions.dash-to-dock.enable pkgs.gnomeExtensions.dash-to-dock + ++ lib.optional config.gnome.extensions.dash-to-panel.enable pkgs.gnomeExtensions.dash-to-panel; + + extensions = config.gnome.extraExtensions ++ enabledExtensions; +in { + options.gnome = { + extraWindowControls = lib.mkEnableOption "Should we add back in the minimize and maximize window controls?"; + clockFormat = lib.mkOption { + type = lib.types.enum [ + "12h" + "24h" + ]; + default = "24h"; + }; + colorScheme = lib.mkOption { + type = lib.types.enum [ + "default" + "prefer-dark" + "prefer-light" + ]; + default = "default"; + }; + accentColor = lib.mkOption { + type = lib.types.enum [ + "blue" + "teal" + "green" + "yellow" + "orange" + "red" + "pink" + "purple" + "slate" + ]; + default = "blue"; + }; + extraExtensions = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = []; + description = "The set of extensions to install and enable in the user environment."; + }; + hotkeys = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + key = lib.mkOption { + type = lib.types.strMatching "[a-zA-Z0-9-]+"; + default = builtins.replaceStrings [" " "/" "_"] ["-" "-" "-"] name; + }; + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + binding = lib.mkOption { + type = lib.types.str; + }; + command = lib.mkOption { + type = lib.types.str; + }; + }; + })); + default = {}; + }; + displayScaling = lib.mkOption { + type = lib.types.nullOr (lib.types.enum [100 125 150 175 200]); + default = null; + description = "Display scaling percentage for GNOME"; + }; + experimentalFeatures = lib.mkOption { + type = lib.types.submodule { + options = { + scaleMonitorFramebuffer = lib.mkEnableOption "scale-monitor-framebuffer experimental feature"; + }; + }; + default = {}; + description = "GNOME experimental features to enable"; + }; + + nightLight = lib.mkOption { + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "night light (blue light filter)"; + automatic = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically schedule night light based on sunset/sunrise"; + }; + fromTime = lib.mkOption { + type = lib.types.float; + default = 20.0; + description = "Start time for night light in 24-hour format (e.g., 20.0 for 8:00 PM)"; + }; + toTime = lib.mkOption { + type = lib.types.float; + default = 6.0; + description = "End time for night light in 24-hour format (e.g., 6.0 for 6:00 AM)"; + }; + temperature = lib.mkOption { + type = lib.types.int; + default = 4000; + description = "Color temperature for night light (1000-10000K, lower is warmer)"; + }; + }; + }; + default = {}; + description = "Night light configuration"; + }; + + extensions = { + dash-to-dock = { + enable = lib.mkEnableOption "Dash to Dock extension"; + options = lib.mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; + description = "Dash to Dock configuration options. If null, no custom configuration will be applied."; + }; + }; + + dash-to-panel = { + enable = lib.mkEnableOption "Dash to Panel extension"; + options = lib.mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; + description = "Dash to Panel configuration options. If null, no custom configuration will be applied."; + }; + }; + }; + }; + + config = { + home.packages = extensions; + dconf = { + settings = lib.mkMerge [ + { + "org/gnome/shell" = { + disable-user-extensions = false; # enables user extensions + enabled-extensions = builtins.map (extension: extension.extensionUuid) extensions; + }; + + "org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close"; + + "org/gnome/desktop/interface".color-scheme = config.gnome.colorScheme; + "org/gnome/desktop/interface".accent-color = config.gnome.accentColor; + "org/gnome/desktop/interface".clock-format = config.gnome.clockFormat; + "org/gnome/desktop/interface".text-scaling-factor = lib.mkIf (config.gnome.displayScaling != null) (config.gnome.displayScaling / 100.0); + + "org/gnome/mutter".experimental-features = lib.mkIf (builtins.any (x: x) (builtins.attrValues config.gnome.experimentalFeatures)) ( + lib.optional config.gnome.experimentalFeatures.scaleMonitorFramebuffer "scale-monitor-framebuffer" + ); + } + + # Night light configuration + (lib.mkIf config.gnome.nightLight.enable { + "org/gnome/settings-daemon/plugins/color" = { + night-light-enabled = true; + night-light-schedule-automatic = config.gnome.nightLight.automatic; + night-light-schedule-from = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.fromTime; + night-light-schedule-to = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.toTime; + night-light-temperature = config.gnome.nightLight.temperature; + }; + }) + ( + lib.mkMerge ( + builtins.map (value: let + entry = "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${value.key}"; + in { + ${entry} = { + binding = value.binding; + command = value.command; + name = value.name; + }; + + "org/gnome/settings-daemon/plugins/media-keys" = { + custom-keybindings = [ + "/${entry}/" + ]; + }; + }) + ( + lib.attrsets.mapAttrsToList (_: value: value) config.gnome.hotkeys + ) + ) + ) + + # Extension configurations + (lib.mkIf (config.gnome.extensions.dash-to-dock.enable && config.gnome.extensions.dash-to-dock.options != null) { + "org/gnome/shell/extensions/dash-to-dock" = config.gnome.extensions.dash-to-dock.options; + }) + + (lib.mkIf (config.gnome.extensions.dash-to-panel.enable && config.gnome.extensions.dash-to-panel.options != null) { + "org/gnome/shell/extensions/dash-to-panel" = config.gnome.extensions.dash-to-panel.options; + }) + ]; + }; + }; +} diff --git a/modules/home-manager-modules/i18n.nix b/modules/home-manager-modules/i18n.nix new file mode 100644 index 0000000..2c93e59 --- /dev/null +++ b/modules/home-manager-modules/i18n.nix @@ -0,0 +1,42 @@ +{ + lib, + config, + ... +}: { + options = { + i18n = { + defaultLocale = lib.mkOption { + type = lib.types.str; + default = "en_US.UTF-8"; + example = "nl_NL.UTF-8"; + description = '' + The default locale. It determines the language for program + messages, the format for dates and times, sort order, and so on. + It also determines the character set, such as UTF-8. + ''; + }; + + extraLocaleSettings = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + example = { + LC_MESSAGES = "en_US.UTF-8"; + LC_TIME = "de_DE.UTF-8"; + }; + description = '' + A set of additional system-wide locale settings other than + `LANG` which can be configured with + {option}`i18n.defaultLocale`. + ''; + }; + }; + }; + + config = { + home.sessionVariables = + { + LANG = config.i18n.defaultLocale; + } + // config.i18n.extraLocaleSettings; + }; +} diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix new file mode 100644 index 0000000..e8b3ec4 --- /dev/null +++ b/modules/home-manager-modules/impermanence.nix @@ -0,0 +1,43 @@ +{ + config, + lib, + osConfig, + ... +}: let + cfg = config.impermanence; +in { + options.impermanence = { + enable = lib.mkEnableOption "impermanence for home directory"; + fallbackPersistence.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + persistencePath = lib.mkOption { + type = lib.types.str; + default = + if osConfig.storage.generateBase + then "/persist/replicate/home" + else "/persist"; + description = "The base path for user home persistence. The impermanence module will automatically append the user's home directory path. Automatically adapts based on whether the system uses the new dataset layout or the legacy one."; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf config.impermanence.enable { + assertions = [ + { + assertion = osConfig.storage.impermanence.enable; + message = "impermanence can not be enabled for a user when it is not enabled for the system"; + } + ]; + }) + # If impermanence is not enabled for this user but system impermanence is enabled, + # persist the entire home directory as fallback + (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { + home.persistence."${cfg.persistencePath}" = { + directories = ["."]; + allowOther = true; + }; + }) + ]; +} diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix new file mode 100644 index 0000000..2f44957 --- /dev/null +++ b/modules/home-manager-modules/openssh.nix @@ -0,0 +1,107 @@ +{ + pkgs, + config, + osConfig, + lib, + ... +}: { + options.programs.openssh = { + enable = lib.mkEnableOption "should we enable openssh"; + authorizedKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + hostKeys = lib.mkOption { + type = lib.types.listOf lib.types.attrs; + default = []; + example = [ + { + type = "rsa"; + bits = 4096; + path = "${config.home.username}_${osConfig.networking.hostName}_rsa"; + rounds = 100; + openSSHFormat = true; + } + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + rounds = 100; + comment = "key comment"; + } + ]; + description = '' + NixOS can automatically generate SSH host keys. This option + specifies the path, type and size of each key. See + {manpage}`ssh-keygen(1)` for supported types + and sizes. Paths are relative to home directory + ''; + }; + }; + + config = lib.mkIf config.programs.openssh.enable ( + lib.mkMerge [ + ( + lib.mkIf ((builtins.length config.programs.openssh.hostKeys) != 0) { + services.ssh-agent.enable = true; + programs.ssh = { + enable = true; + enableDefaultConfig = false; + matchBlocks = { + "*" = { + compression = true; + addKeysToAgent = "confirm"; + }; + }; + # extraConfig = lib.strings.concatLines ( + # builtins.map (hostKey: "IdentityFile ~/.ssh/${hostKey.path}") config.programs.openssh.hostKeys + # ); + }; + + systemd.user.services = builtins.listToAttrs ( + builtins.map (hostKey: + lib.attrsets.nameValuePair "ssh-gen-keys-${hostKey.path}" { + Install = { + WantedBy = ["default.target"]; + }; + Service = let + path = "${config.home.homeDirectory}/.ssh/${hostKey.path}"; + in { + Restart = "always"; + Type = "simple"; + ExecStart = "${ + pkgs.writeShellScript "ssh-gen-keys" '' + if ! [ -s "${path}" ]; then + if ! [ -h "${path}" ]; then + rm -f "${path}" + fi + mkdir -p "$(dirname '${path}')" + chmod 0755 "$(dirname '${path}')" + ${pkgs.openssh}/bin/ssh-keygen \ + -t "${hostKey.type}" \ + ${lib.optionalString (hostKey ? bits) "-b ${toString hostKey.bits}"} \ + ${lib.optionalString (hostKey ? rounds) "-a ${toString hostKey.rounds}"} \ + ${lib.optionalString (hostKey ? comment) "-C '${hostKey.comment}'"} \ + ${lib.optionalString (hostKey ? openSSHFormat && hostKey.openSSHFormat) "-o"} \ + -f "${path}" \ + -N "" + chown ${config.home.username} ${path}* + chgrp ${config.home.username} ${path}* + fi + '' + }"; + }; + }) + config.programs.openssh.hostKeys + ); + } + ) + (lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + files = lib.lists.flatten ( + builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys + ); + }; + }) + ] + ); +} diff --git a/modules/home-manager-modules/programs/android-studio.nix b/modules/home-manager-modules/programs/android-studio.nix new file mode 100644 index 0000000..8d1e28c --- /dev/null +++ b/modules/home-manager-modules/programs/android-studio.nix @@ -0,0 +1,30 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.android-studio = { + enable = lib.mkEnableOption "enable android-studio"; + }; + + config = lib.mkIf config.programs.android-studio.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + android-studio + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/Google/AndroidStudio" + ".android" + ".gradle" + "${config.xdg.cacheHome}/Google/AndroidStudio" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/anki.nix b/modules/home-manager-modules/programs/anki.nix new file mode 100644 index 0000000..dcabce8 --- /dev/null +++ b/modules/home-manager-modules/programs/anki.nix @@ -0,0 +1,13 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + ".local/share/Anki2" + ]; + }; + }; +} diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix new file mode 100644 index 0000000..bbd2086 --- /dev/null +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.bitwarden = { + enable = lib.mkEnableOption "enable bitwarden"; + }; + + config = lib.mkIf config.programs.bitwarden.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + bitwarden-desktop + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/Bitwarden" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix new file mode 100644 index 0000000..7bc64b6 --- /dev/null +++ b/modules/home-manager-modules/programs/bruno.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.bruno = { + enable = lib.mkEnableOption "enable bruno"; + }; + + config = lib.mkIf config.programs.bruno.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + bruno + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/bruno/" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix new file mode 100644 index 0000000..7174b43 --- /dev/null +++ b/modules/home-manager-modules/programs/calibre.nix @@ -0,0 +1,23 @@ +{ + lib, + pkgs, + config, + ... +}: { + config = lib.mkIf config.programs.calibre.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + calibre + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/calibre" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix new file mode 100644 index 0000000..5956578 --- /dev/null +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.davinci-resolve = { + enable = lib.mkEnableOption "enable davinci-resolve"; + }; + + config = lib.mkIf config.programs.davinci-resolve.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + davinci-resolve + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.dataHome}/DaVinciResolve" + "${config.xdg.configHome}/blackmagic" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix new file mode 100644 index 0000000..1595a02 --- /dev/null +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.dbeaver-bin = { + enable = lib.mkEnableOption "enable dbeaver"; + }; + + config = lib.mkIf config.programs.dbeaver-bin.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + dbeaver-bin + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.dataHome}/DBeaverData/" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/default.nix b/modules/home-manager-modules/programs/default.nix new file mode 100644 index 0000000..8a8e8b5 --- /dev/null +++ b/modules/home-manager-modules/programs/default.nix @@ -0,0 +1,54 @@ +{...}: { + imports = [ + ./android-studio.nix + ./firefox.nix + ./signal.nix + ./bitwarden.nix + ./makemkv.nix + ./obs.nix + ./anki.nix + ./piper.nix + ./qbittorrent.nix + ./discord.nix + ./obsidian.nix + ./prostudiomasters.nix + ./idea.nix + ./kdenlive.nix + ./kicad.nix + ./krita.nix + ./protonvpn.nix + ./calibre.nix + ./bruno.nix + ./dbeaver.nix + ./dungeon-draft.nix + ./steam.nix + ./vscode + ./ungoogled-chromium.nix + ./libreoffice.nix + ./mapillary-uploader.nix + ./inkscape.nix + ./gimp.nix + ./guild-wars-2.nix + ./proxmark3.nix + ./freecad.nix + ./onionshare.nix + ./mfoc.nix + ./noita-entangled-worlds.nix + ./pdfarranger.nix + ./picard.nix + ./qflipper.nix + ./openvpn.nix + ./noisetorch.nix + ./olympus.nix + ./openrgb.nix + ./via.nix + ./vortex.nix + ./davinci-resolve.nix + ./gdx-liftoff.nix + ./tor-browser.nix + ./vmware-workstation.nix + ./proton-mail-pwa.nix + ./proton-calendar-pwa.nix + ./matrix-cyberia-pwa.nix + ]; +} diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix new file mode 100644 index 0000000..e42367b --- /dev/null +++ b/modules/home-manager-modules/programs/discord.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/discord/" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/dungeon-draft.nix b/modules/home-manager-modules/programs/dungeon-draft.nix new file mode 100644 index 0000000..faa69c6 --- /dev/null +++ b/modules/home-manager-modules/programs/dungeon-draft.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: let + cfg = config.programs.dungeon-draft; +in { + options.programs.dungeon-draft = { + enable = lib.mkEnableOption "Dungeon Draft"; + }; + + config = { + assertions = [ + { + assertion = !cfg.enable; + message = '' + Dungeon Draft module is not yet fully configured. + Please download the Dungeon Draft executable (.exe) from the official website, + then configure the Wine environment and executable path as needed. + ''; + } + ]; + }; +} diff --git a/modules/home-manager-modules/programs/firefox.nix b/modules/home-manager-modules/programs/firefox.nix new file mode 100644 index 0000000..2756e31 --- /dev/null +++ b/modules/home-manager-modules/programs/firefox.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + ... +}: let + buildProfilePersistence = profile: { + directories = [ + ".mozilla/firefox/${profile}/extensions" + ]; + files = [ + ".mozilla/firefox/${profile}/cookies.sqlite" + ".mozilla/firefox/${profile}/favicons.sqlite" + # Permissions and ${profileName} levels for each site + ".mozilla/firefox/${profile}/permissions.sqlite" + ".mozilla/firefox/${profile}/content-prefs.sqlite" + # Browser history and bookmarks + ".mozilla/firefox/${profile}/places.sqlite" + # I guess this is useful? + # https://bugzilla.mozilla.org/show_bug.cgi?id=1511384 + # https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria + ".mozilla/firefox/${profile}/storage.sqlite" + # Extension configuration + ".mozilla/firefox/${profile}/extension-settings.json" + ]; + }; +in { + config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { + home.persistence."${config.impermanence.persistencePath}" = lib.mkMerge ( + ( + lib.attrsets.mapAttrsToList + (profile: _: buildProfilePersistence profile) + config.programs.firefox.profiles + ) + ++ ( + lib.lists.optional + ((builtins.length (lib.attrsets.mapAttrsToList (key: value: value) config.programs.firefox.profiles)) == 0) + (buildProfilePersistence "default") + ) + ); + }; +} diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix new file mode 100644 index 0000000..50600db --- /dev/null +++ b/modules/home-manager-modules/programs/freecad.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.freecad = { + enable = lib.mkEnableOption "enable freecad"; + }; + + config = lib.mkIf config.programs.freecad.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + freecad + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/FreeCAD" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/gdx-liftoff.nix b/modules/home-manager-modules/programs/gdx-liftoff.nix new file mode 100644 index 0000000..4440831 --- /dev/null +++ b/modules/home-manager-modules/programs/gdx-liftoff.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.gdx-liftoff = { + enable = lib.mkEnableOption "enable gdx-liftoff"; + }; + + config = lib.mkIf config.programs.gdx-liftoff.enable { + home.packages = with pkgs; [ + gdx-liftoff + ]; + }; +} diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix new file mode 100644 index 0000000..95c87e6 --- /dev/null +++ b/modules/home-manager-modules/programs/gimp.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.gimp = { + enable = lib.mkEnableOption "enable gimp"; + }; + + config = lib.mkIf config.programs.gimp.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + gimp + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/GIMP" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/guild-wars-2.nix b/modules/home-manager-modules/programs/guild-wars-2.nix new file mode 100644 index 0000000..3f68ec6 --- /dev/null +++ b/modules/home-manager-modules/programs/guild-wars-2.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: let + cfg = config.programs.guild-wars-2; +in { + options.programs.guild-wars-2 = { + enable = lib.mkEnableOption "Guild Wars 2"; + }; + + config = { + assertions = [ + { + assertion = !cfg.enable; + message = '' + Guild Wars 2 module is not yet fully configured. + Please install Guild Wars 2 manually via Steam or the official client, + then configure the Wine environment as needed. + ''; + } + ]; + }; +} diff --git a/modules/home-manager-modules/programs/idea.nix b/modules/home-manager-modules/programs/idea.nix new file mode 100644 index 0000000..a1aebda --- /dev/null +++ b/modules/home-manager-modules/programs/idea.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.jetbrains.idea-oss = { + enable = lib.mkEnableOption "enable idea-oss"; + }; + + config = lib.mkIf config.programs.jetbrains.idea-oss.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + jetbrains.idea-oss + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + # configuration + "${config.xdg.configHome}/JetBrains/" + # plugins + "${config.xdg.dataHome}/JetBrains/" + # System and Logs + "${config.xdg.cacheHome}/JetBrains/" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix new file mode 100644 index 0000000..28eb334 --- /dev/null +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.inkscape = { + enable = lib.mkEnableOption "enable inkscape"; + }; + + config = lib.mkIf config.programs.inkscape.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + inkscape + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/inkscape" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/kdenlive.nix b/modules/home-manager-modules/programs/kdenlive.nix new file mode 100644 index 0000000..2c4bac8 --- /dev/null +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -0,0 +1,35 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.programs.kdenlive; +in { + options.programs.kdenlive = { + enable = lib.mkEnableOption "kdenlive"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.kdePackages.kdenlive; + description = "The kdenlive package to install."; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + home.packages = [ + cfg.package + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/kdenliverc" + "${config.xdg.dataHome}/kdenlive" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/kicad.nix b/modules/home-manager-modules/programs/kicad.nix new file mode 100644 index 0000000..c2414c1 --- /dev/null +++ b/modules/home-manager-modules/programs/kicad.nix @@ -0,0 +1,23 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.kicad = { + enable = lib.mkEnableOption "enable kicad"; + }; + + config = lib.mkIf config.programs.kicad.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + kicad + ]; + } + ( + lib.mkIf config.impermanence.enable { + # TODO: + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix new file mode 100644 index 0000000..dd7bb12 --- /dev/null +++ b/modules/home-manager-modules/programs/krita.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.krita = { + enable = lib.mkEnableOption "enable krita"; + }; + + config = lib.mkIf config.programs.krita.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + krita + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/kritarc" + "${config.xdg.dataHome}/krita" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix new file mode 100644 index 0000000..283c8db --- /dev/null +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.libreoffice = { + enable = lib.mkEnableOption "enable libreoffice"; + }; + + config = lib.mkIf config.programs.libreoffice.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + libreoffice + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/libreoffice" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix new file mode 100644 index 0000000..f748f68 --- /dev/null +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -0,0 +1,41 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.makemkv = { + enable = lib.mkEnableOption "enable makemkv"; + appKeyFile = lib.mkOption { + type = lib.types.str; + }; + destinationDir = lib.mkOption { + type = lib.types.str; + }; + }; + + config = lib.mkIf config.programs.makemkv.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + makemkv + ]; + + sops.templates."MakeMKV.settings.conf".content = '' + app_DestinationDir = "${config.programs.makemkv.destinationDir}" + app_DestinationType = "2" + app_Key = "${config.programs.makemkv.appKeyFile}" + ''; + + home.file.".MakeMKV/settings.conf".source = config.lib.file.mkOutOfStoreSymlink config.sops.templates."MakeMKV.settings.conf".path; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + ".MakeMKV" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix new file mode 100644 index 0000000..0d9ad5f --- /dev/null +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -0,0 +1,29 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.programs.mapillary-uploader; +in { + options.programs.mapillary-uploader = { + enable = mkEnableOption "Mapillary Desktop Uploader"; + }; + + config = mkIf cfg.enable (mkMerge [ + { + home.packages = [pkgs.mapillary-uploader]; + } + ( + mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/mapillary-uploader" + "${config.xdg.dataHome}/mapillary-uploader" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/matrix-cyberia-pwa.nix b/modules/home-manager-modules/programs/matrix-cyberia-pwa.nix new file mode 100644 index 0000000..644df92 --- /dev/null +++ b/modules/home-manager-modules/programs/matrix-cyberia-pwa.nix @@ -0,0 +1,56 @@ +{ + lib, + pkgs, + config, + ... +}: let + cfg = config.programs.matrix-cyberia-pwa; + isChromium = cfg.package == pkgs.chromium; + isBrowserImpermanenceSupported = cfg.package == pkgs.chromium; +in { + options.programs.matrix-cyberia-pwa = { + enable = lib.mkEnableOption "enable Matrix Cyberia PWA"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.chromium; + description = "Browser package to use for the PWA"; + }; + impermanence = { + enable = lib.mkOption { + type = lib.types.bool; + default = isBrowserImpermanenceSupported; + description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium."; + }; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + warnings = + lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported) + "matrix-cyberia-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}"; + } + ( + lib.mkIf isChromium { + xdg.desktopEntries.matrix-cyberia-pwa = { + name = "Matrix (Cyberia)"; + type = "Application"; + exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://chat.cyberia.club/"; + icon = "matrix"; + terminal = false; + categories = ["Network" "InstantMessaging"]; + }; + } + ) + ( + lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/chromium" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/mfoc.nix b/modules/home-manager-modules/programs/mfoc.nix new file mode 100644 index 0000000..6006c9b --- /dev/null +++ b/modules/home-manager-modules/programs/mfoc.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.mfoc = { + enable = lib.mkEnableOption "enable mfoc"; + }; + + config = lib.mkIf config.programs.mfoc.enable { + home.packages = with pkgs; [ + mfoc + ]; + }; +} diff --git a/modules/home-manager-modules/programs/noisetorch.nix b/modules/home-manager-modules/programs/noisetorch.nix new file mode 100644 index 0000000..4b42638 --- /dev/null +++ b/modules/home-manager-modules/programs/noisetorch.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.noisetorch = { + enable = lib.mkEnableOption "enable noisetorch"; + }; + + config = lib.mkIf config.programs.noisetorch.enable { + home.packages = with pkgs; [ + noisetorch + ]; + }; +} diff --git a/modules/home-manager-modules/programs/noita-entangled-worlds.nix b/modules/home-manager-modules/programs/noita-entangled-worlds.nix new file mode 100644 index 0000000..3f3af64 --- /dev/null +++ b/modules/home-manager-modules/programs/noita-entangled-worlds.nix @@ -0,0 +1,18 @@ +{ + lib, + pkgs, + config, + ... +}: { + options = { + programs.noita-entangled-worlds = { + enable = lib.mkEnableOption "Noita Entangled Worlds multiplayer mod"; + }; + }; + + config = lib.mkIf config.programs.noita-entangled-worlds.enable { + home.packages = with pkgs; [ + noita_entangled_worlds + ]; + }; +} diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix new file mode 100644 index 0000000..0a4caf7 --- /dev/null +++ b/modules/home-manager-modules/programs/obs.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/obs-studio" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix new file mode 100644 index 0000000..6676ecd --- /dev/null +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/obsidian" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/olympus.nix b/modules/home-manager-modules/programs/olympus.nix new file mode 100644 index 0000000..2d5adb6 --- /dev/null +++ b/modules/home-manager-modules/programs/olympus.nix @@ -0,0 +1,35 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.programs.olympus; +in { + options.programs.olympus = { + enable = lib.mkEnableOption "olympus"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.olympus; + description = "The olympus package to install."; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + home.packages = [ + cfg.package + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/olympus" + "${config.xdg.dataHome}/olympus" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/onionshare.nix b/modules/home-manager-modules/programs/onionshare.nix new file mode 100644 index 0000000..475f993 --- /dev/null +++ b/modules/home-manager-modules/programs/onionshare.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.onionshare = { + enable = lib.mkEnableOption "enable onionshare"; + }; + + config = lib.mkIf config.programs.onionshare.enable { + home.packages = with pkgs; [ + onionshare + ]; + }; +} diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix new file mode 100644 index 0000000..c350b1e --- /dev/null +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.openrgb = { + enable = lib.mkEnableOption "enable openrgb"; + }; + + config = lib.mkIf config.programs.openrgb.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + openrgb + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/OpenRGB" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/openvpn.nix b/modules/home-manager-modules/programs/openvpn.nix new file mode 100644 index 0000000..dcd499c --- /dev/null +++ b/modules/home-manager-modules/programs/openvpn.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.openvpn = { + enable = lib.mkEnableOption "enable openvpn"; + }; + + config = lib.mkIf config.programs.openvpn.enable { + home.packages = with pkgs; [ + openvpn + ]; + }; +} diff --git a/modules/home-manager-modules/programs/pdfarranger.nix b/modules/home-manager-modules/programs/pdfarranger.nix new file mode 100644 index 0000000..9246efd --- /dev/null +++ b/modules/home-manager-modules/programs/pdfarranger.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.pdfarranger = { + enable = lib.mkEnableOption "enable pdfarranger"; + }; + + config = lib.mkIf config.programs.pdfarranger.enable { + home.packages = with pkgs; [ + pdfarranger + ]; + }; +} diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix new file mode 100644 index 0000000..ffc4289 --- /dev/null +++ b/modules/home-manager-modules/programs/picard.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.picard = { + enable = lib.mkEnableOption "enable picard"; + }; + + config = lib.mkIf config.programs.picard.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + picard + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/MusicBrainz" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/piper.nix b/modules/home-manager-modules/programs/piper.nix new file mode 100644 index 0000000..3ed25fd --- /dev/null +++ b/modules/home-manager-modules/programs/piper.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.piper = { + enable = lib.mkEnableOption "enable piper"; + }; + + config = lib.mkIf config.programs.piper.enable { + home.packages = with pkgs; [ + piper + ]; + }; +} diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix new file mode 100644 index 0000000..d61b7e5 --- /dev/null +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.prostudiomasters = { + enable = lib.mkEnableOption "enable prostudiomasters"; + }; + + config = lib.mkIf config.programs.prostudiomasters.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + prostudiomasters + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/ProStudioMasters" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/proton-calendar-pwa.nix b/modules/home-manager-modules/programs/proton-calendar-pwa.nix new file mode 100644 index 0000000..33796e2 --- /dev/null +++ b/modules/home-manager-modules/programs/proton-calendar-pwa.nix @@ -0,0 +1,55 @@ +{ + lib, + pkgs, + config, + ... +}: let + cfg = config.programs.proton-calendar-pwa; + isChromium = cfg.package == pkgs.chromium; + isBrowserImpermanenceSupported = cfg.package == pkgs.chromium; +in { + options.programs.proton-calendar-pwa = { + enable = lib.mkEnableOption "enable Proton Calendar PWA"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.chromium; + description = "Browser package to use for the PWA"; + }; + impermanence = { + enable = lib.mkOption { + type = lib.types.bool; + default = isBrowserImpermanenceSupported; + description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium."; + }; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + warnings = + lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported) + "proton-calendar-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}"; + } + ( + lib.mkIf isChromium { + xdg.desktopEntries.proton-calendar-pwa = { + name = "Proton Calendar"; + type = "Application"; + exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://calendar.proton.me"; + icon = "chrome-ojibjkjikcpjonjjngfkegflhmffeemk-Default"; + terminal = false; + }; + } + ) + ( + lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/chromium" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/proton-mail-pwa.nix b/modules/home-manager-modules/programs/proton-mail-pwa.nix new file mode 100644 index 0000000..3a3fe89 --- /dev/null +++ b/modules/home-manager-modules/programs/proton-mail-pwa.nix @@ -0,0 +1,55 @@ +{ + lib, + pkgs, + config, + ... +}: let + cfg = config.programs.proton-mail-pwa; + isChromium = cfg.package == pkgs.chromium; + isBrowserImpermanenceSupported = cfg.package == pkgs.chromium; +in { + options.programs.proton-mail-pwa = { + enable = lib.mkEnableOption "enable Proton Mail PWA"; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.chromium; + description = "Browser package to use for the PWA"; + }; + impermanence = { + enable = lib.mkOption { + type = lib.types.bool; + default = isBrowserImpermanenceSupported; + description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium."; + }; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + warnings = + lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported) + "proton-mail-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}"; + } + ( + lib.mkIf isChromium { + xdg.desktopEntries.proton-mail-pwa = { + name = "Proton Mail"; + type = "Application"; + exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://mail.proton.me"; + icon = "chrome-jnpecgipniidlgicjocehkhajgdnjekh-Default"; + terminal = false; + }; + } + ) + ( + lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/chromium" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix new file mode 100644 index 0000000..5742948 --- /dev/null +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.protonvpn-gui = { + enable = lib.mkEnableOption "enable protonvpn"; + }; + + config = lib.mkIf config.programs.protonvpn-gui.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + protonvpn-gui + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/protonvpn" + "${config.xdg.configHome}/Proton" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/proxmark3.nix b/modules/home-manager-modules/programs/proxmark3.nix new file mode 100644 index 0000000..656be19 --- /dev/null +++ b/modules/home-manager-modules/programs/proxmark3.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.proxmark3 = { + enable = lib.mkEnableOption "enable proxmark3"; + }; + + config = lib.mkIf config.programs.proxmark3.enable { + home.packages = with pkgs; [ + proxmark3 + ]; + }; +} diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix new file mode 100644 index 0000000..b2e0f50 --- /dev/null +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.qbittorrent = { + enable = lib.mkEnableOption "enable qbittorrent"; + }; + + config = lib.mkIf config.programs.qbittorrent.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + qbittorrent + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/qBittorrent" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix new file mode 100644 index 0000000..bb141a4 --- /dev/null +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.qflipper = { + enable = lib.mkEnableOption "enable qflipper"; + }; + + config = lib.mkIf config.programs.qflipper.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + qFlipper + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/qFlipper" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix new file mode 100644 index 0000000..a50a49e --- /dev/null +++ b/modules/home-manager-modules/programs/signal.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.signal-desktop = { + enable = lib.mkEnableOption "enable signal"; + }; + + config = lib.mkIf config.programs.signal-desktop.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + signal-desktop + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/Signal" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix new file mode 100644 index 0000000..4e0644e --- /dev/null +++ b/modules/home-manager-modules/programs/steam.nix @@ -0,0 +1,35 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.steam = { + enable = lib.mkEnableOption "enable steam"; + }; + + config = lib.mkIf config.programs.steam.enable ( + lib.mkMerge [ + { + home.packages = with pkgs; [ + steam + steam.run + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + { + directory = "${config.xdg.dataHome}/Steam"; + method = "symlink"; + } + ]; + }; + } + ) + ] + ); + + # TODO: bind impermanence config +} diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix new file mode 100644 index 0000000..c108805 --- /dev/null +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.tor-browser = { + enable = lib.mkEnableOption "enable tor-browser"; + }; + + config = lib.mkIf config.programs.tor-browser.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + tor-browser + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.dataHome}/torbrowser" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix new file mode 100644 index 0000000..32f4b40 --- /dev/null +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.ungoogled-chromium = { + enable = lib.mkEnableOption "enable ungoogled-chromium"; + }; + + config = lib.mkIf config.programs.ungoogled-chromium.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + ungoogled-chromium + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/chromium" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix new file mode 100644 index 0000000..ad6f45a --- /dev/null +++ b/modules/home-manager-modules/programs/via.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.via = { + enable = lib.mkEnableOption "enable via"; + }; + + config = lib.mkIf config.programs.via.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + via + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + "${config.xdg.configHome}/via" + "${config.xdg.dataHome}/via" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/vmware-workstation.nix b/modules/home-manager-modules/programs/vmware-workstation.nix new file mode 100644 index 0000000..76f260b --- /dev/null +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -0,0 +1,36 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.vmware-workstation = { + enable = lib.mkEnableOption "enable VMware Workstation"; + }; + + config = lib.mkIf config.programs.vmware-workstation.enable ( + lib.mkMerge [ + { + home.packages = with pkgs; [ + vmware-workstation + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."${config.impermanence.persistencePath}" = { + directories = [ + { + directory = ".vmware"; + method = "symlink"; + } + { + directory = "vmware"; + method = "symlink"; + } + ]; + }; + } + ) + ] + ); +} diff --git a/modules/home-manager-modules/programs/vortex.nix b/modules/home-manager-modules/programs/vortex.nix new file mode 100644 index 0000000..cb86526 --- /dev/null +++ b/modules/home-manager-modules/programs/vortex.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: let + cfg = config.programs.vortex; +in { + options.programs.vortex = { + enable = lib.mkEnableOption "Vortex (Nexus Mods manager)"; + }; + + config = { + assertions = [ + { + assertion = !cfg.enable; + message = '' + Vortex module is not yet fully configured. + Please download and install Vortex manually from the Nexus Mods website, + then configure the Wine environment and dependencies as needed. + ''; + } + ]; + }; +} diff --git a/modules/home-manager-modules/programs/vscode/aiCode.nix b/modules/home-manager-modules/programs/vscode/aiCode.nix new file mode 100644 index 0000000..838a439 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/aiCode.nix @@ -0,0 +1,45 @@ +{ + lib, + pkgs, + ... +}: let + pkgsRepository = pkgs.codium-extensions; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.aiCode = { + enable = lib.mkEnableOption "should the ai code extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "ai-code" {}; + ollamaHost = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "what host should be used for ollama"; + default = null; + }; + inlineCompletion = { + enable = lib.mkOption { + type = lib.types.bool; + description = "should inline completion be enabled"; + default = true; + }; + model = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "what model should be used for ollama"; + default = null; + }; + }; + }; + }; + config = lib.mkIf config.extraExtensions.aiCode.enable { + extensions = [ + config.extraExtensions.aiCode.extension + ]; + userSettings = { + "aiCode.ollamaHost" = lib.mkIf (config.extraExtensions.aiCode.ollamaHost != null) config.extraExtensions.aiCode.ollamaHost; + "aiCode.inlineCompletion.enable" = config.extraExtensions.aiCode.inlineCompletion.enable; + "aiCode.inlineCompletion.model" = lib.mkIf (config.extraExtensions.aiCode.inlineCompletion.model != null) config.extraExtensions.aiCode.inlineCompletion.model; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/alejandra.nix b/modules/home-manager-modules/programs/vscode/alejandra.nix new file mode 100644 index 0000000..ffeaf96 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/alejandra.nix @@ -0,0 +1,34 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.alejandra = { + enable = lib.mkEnableOption "Enable Alejandra extension for Nix formatting"; + extension = lib.mkPackageOption pkgsRepository "alejandra" { + default = ["kamadorueda" "alejandra"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.alejandra.enable { + extensions = [config.extraExtensions.alejandra.extension]; + userSettings = { + "[nix]" = { + "editor.defaultFormatter" = "kamadorueda.alejandra"; + "editor.formatOnPaste" = true; + "editor.formatOnSave" = true; + "editor.formatOnType" = true; + }; + "alejandra.program" = "alejandra"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/astroVscode.nix b/modules/home-manager-modules/programs/vscode/astroVscode.nix new file mode 100644 index 0000000..4bae34a --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/astroVscode.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.astroVscode = { + enable = lib.mkEnableOption "should the astro-vscode extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "astro-vscode" { + default = ["astro-build" "astro-vscode"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.astroVscode.enable { + extensions = [ + config.extraExtensions.astroVscode.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/atomKeybindings.nix b/modules/home-manager-modules/programs/vscode/atomKeybindings.nix new file mode 100644 index 0000000..95cd928 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/atomKeybindings.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.atomKeybindings = { + enable = lib.mkEnableOption "should the atom keybindings extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "atom-keybindings" { + default = ["ms-vscode" "atom-keybindings"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.atomKeybindings.enable { + extensions = [ + config.extraExtensions.atomKeybindings.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/autoRenameTag.nix b/modules/home-manager-modules/programs/vscode/autoRenameTag.nix new file mode 100644 index 0000000..5f24a32 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/autoRenameTag.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.autoRenameTag = { + enable = lib.mkEnableOption "should the auto-rename-tag extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "auto-rename-tag" { + default = ["formulahendry" "auto-rename-tag"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.autoRenameTag.enable { + extensions = [ + config.extraExtensions.autoRenameTag.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/claudeDev.nix b/modules/home-manager-modules/programs/vscode/claudeDev.nix new file mode 100644 index 0000000..c4d2dd7 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/claudeDev.nix @@ -0,0 +1,237 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; + + mcp-nixos = inputs.mcp-nixos.packages.${pkgs.stdenv.hostPlatform.system}.default; + + anyProfileHasInstallTool = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.installTool + ) (lib.attrValues config.programs.vscode.profiles); + + getInstallToolPackage = lib.findFirst (package: package != null) pkgs.cline (map ( + profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.installTool + then profile.extraExtensions.claudeDev.package + else null + ) (lib.attrValues config.programs.vscode.profiles)); + + anyProfileHasMcpNixos = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.nixos.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpEslint = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.eslint.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpVitest = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.vitest.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpSleep = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.sleep.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcp = anyProfileHasMcpNixos || anyProfileHasMcpEslint || anyProfileHasMcpVitest || anyProfileHasMcpSleep; + + getMcpTimeout = serverName: + lib.findFirst (timeout: timeout != null) null (map ( + profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then profile.extraExtensions.claudeDev.mcp.${serverName}.timeout + else null + ) (lib.attrValues config.programs.vscode.profiles)); + + getMcpAutoApprove = serverName: + lib.foldl' ( + acc: profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then acc // profile.extraExtensions.claudeDev.mcp.${serverName}.autoApprove + else acc + ) {} (lib.attrValues config.programs.vscode.profiles); + + getMcpPackage = serverName: + lib.findFirst (package: package != null) null (map ( + profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then profile.extraExtensions.claudeDev.mcp.${serverName}.package + else null + ) (lib.attrValues config.programs.vscode.profiles)); +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.claudeDev = { + enable = lib.mkEnableOption "should the claude-dev extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "claude-dev" { + default = ["saoudrizwan" "claude-dev"]; + }; + + installTool = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to install the cline CLI tool for subagent support when the extension is enabled"; + }; + package = lib.mkOption { + type = lib.types.package; + default = pkgs.cline; + description = "The package to install for the cline CLI tool"; + }; + + mcp = { + nixos = { + enable = lib.mkEnableOption "enable NixOS MCP server for Claude Dev"; + autoApprove = { + nixos_search = lib.mkEnableOption "should the nixos_search tool be auto approved for the nixos MCP server"; + nixos_info = lib.mkEnableOption "should the nixos_info tool be auto approved for the nixos MCP server"; + home_manager_search = lib.mkEnableOption "should the home_manager_search tool be auto approved for the nixos MCP server"; + home_manager_info = lib.mkEnableOption "should the home_manager_info tool be auto approved for the nixos MCP server"; + darwin_search = lib.mkEnableOption "should the darwin_search tool be auto approved for the nixos MCP server"; + darwin_info = lib.mkEnableOption "should the darwin_info tool be auto approved for the nixos MCP server"; + nixos_flakes_search = lib.mkEnableOption "should the nixos_flakes_search tool be auto approved for the nixos MCP server"; + }; + }; + eslint = { + enable = lib.mkEnableOption "enable ESLint MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "@eslint/mcp@latest"; + description = "NPM package to use for ESLint MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for ESLint MCP server operations"; + }; + autoApprove = { + lint-files = lib.mkEnableOption "Should the lint-files tool be auto approved for ESLint MCP server"; + }; + }; + vitest = { + enable = lib.mkEnableOption "enable Vitest MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "@djankies/vitest-mcp"; + description = "NPM package to use for Vitest MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for Vitest MCP server operations"; + }; + autoApprove = { + list_tests = lib.mkEnableOption "Should the list_tests tool be auto approved for Vitest MCP server"; + run_tests = lib.mkEnableOption "Should the run_tests tool be auto approved for Vitest MCP server"; + analyze_coverage = lib.mkEnableOption "Should the analyze_coverage tool be auto approved for Vitest MCP server"; + set_project_root = lib.mkEnableOption "Should the set_project_root tool be auto approved for Vitest MCP server"; + }; + }; + sleep = { + enable = lib.mkEnableOption "enable Sleep MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "sleep-mcp"; + description = "NPM package to use for Sleep MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for Sleep MCP server operations"; + }; + autoApprove = { + sleep = lib.mkEnableOption "Should the sleep tool be auto approved for Sleep MCP server"; + }; + }; + }; + }; + }; + config = lib.mkIf config.extraExtensions.claudeDev.enable { + extensions = [ + config.extraExtensions.claudeDev.extension + ]; + }; + })); + }; + + config = lib.mkMerge [ + (lib.mkIf anyProfileHasInstallTool { + home.packages = [ + getInstallToolPackage + ]; + }) + + (lib.mkIf anyProfileHasMcpNixos { + home.packages = [ + mcp-nixos + ]; + }) + + (lib.mkIf anyProfileHasMcp { + home.file."${config.xdg.configHome}/VSCodium/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json" = { + text = builtins.toJSON { + mcpServers = + (lib.optionalAttrs anyProfileHasMcpNixos { + nixos = { + command = "${mcp-nixos}/bin/mcp-nixos"; + }; + }) + // (lib.optionalAttrs anyProfileHasMcpEslint { + eslint = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "eslint")]; + } + // (lib.optionalAttrs ((getMcpTimeout "eslint") != null) { + timeout = getMcpTimeout "eslint"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "eslint") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "eslint")); + }); + }) + // (lib.optionalAttrs anyProfileHasMcpVitest { + vitest = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "vitest")]; + } + // (lib.optionalAttrs ((getMcpTimeout "vitest") != null) { + timeout = getMcpTimeout "vitest"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "vitest") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "vitest")); + }); + }) + // (lib.optionalAttrs anyProfileHasMcpSleep { + sleep-mcp = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "sleep")]; + } + // (lib.optionalAttrs ((getMcpTimeout "sleep") != null) { + timeout = getMcpTimeout "sleep"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "sleep") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "sleep")); + }); + }); + }; + force = true; + }; + }) + ]; +} diff --git a/modules/home-manager-modules/programs/vscode/conventionalCommits.nix b/modules/home-manager-modules/programs/vscode/conventionalCommits.nix new file mode 100644 index 0000000..b667c27 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/conventionalCommits.nix @@ -0,0 +1,40 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.conventionalCommits = { + enable = lib.mkEnableOption "Enable VSCode Conventional Commits extension"; + extension = lib.mkPackageOption pkgsRepository "conventional-commits" { + default = ["vivaxy" "vscode-conventional-commits"]; + }; + + gitmoji = lib.mkEnableOption "should emoji be prompted for as a part of the commit message./"; + + promptScopes = lib.mkEnableOption "prompting for scopes in conventional commits"; + + promptFooter = lib.mkEnableOption "prompting for footer in conventional commits"; + + showNewVersionNotes = lib.mkEnableOption "showing new version notes for conventional commits"; + }; + }; + config = lib.mkIf config.extraExtensions.conventionalCommits.enable { + extensions = [config.extraExtensions.conventionalCommits.extension]; + + userSettings = { + "conventionalCommits.gitmoji" = config.extraExtensions.conventionalCommits.gitmoji; + "conventionalCommits.promptScopes" = config.extraExtensions.conventionalCommits.promptScopes; + "conventionalCommits.promptFooter" = config.extraExtensions.conventionalCommits.promptFooter; + "conventionalCommits.showNewVersionNotes" = config.extraExtensions.conventionalCommits.showNewVersionNotes; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/default.nix b/modules/home-manager-modules/programs/vscode/default.nix new file mode 100644 index 0000000..6b7fbb9 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/default.nix @@ -0,0 +1,31 @@ +{...}: { + imports = [ + ./oneDark.nix + ./atomKeybindings.nix + ./aiCode.nix + ./alejandra.nix + ./nixIde.nix + ./autoRenameTag.nix + ./es7ReactJsSnippets.nix + ./liveServer.nix + ./tauriVscode.nix + ./vscodeEslint.nix + ./vscodeJest.nix + ./vscodeStandard.nix + ./vscodeStylelint.nix + ./go.nix + ./evenBetterToml.nix + ./openRemoteSsh.nix + ./platformIO.nix + ./rustAnalyzer.nix + ./astroVscode.nix + ./vscodeMdx.nix + ./claudeDev.nix + ./nearley.nix + ./vitest.nix + ./direnv.nix + ./conventionalCommits.nix + ./openDyslexicFont.nix + ./graphql.nix + ]; +} diff --git a/modules/home-manager-modules/programs/vscode/direnv.nix b/modules/home-manager-modules/programs/vscode/direnv.nix new file mode 100644 index 0000000..231ea17 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/direnv.nix @@ -0,0 +1,25 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.direnv = { + enable = lib.mkEnableOption "Enable direnv extension"; + extension = lib.mkPackageOption pkgsRepository "direnv" { + default = ["mkhl" "direnv"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.direnv.enable { + extensions = [config.extraExtensions.direnv.extension]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix b/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix new file mode 100644 index 0000000..09e6da3 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.es7ReactJsSnippets = { + enable = lib.mkEnableOption "should the es7-react-js-snippets extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "es7-react-js-snippets" { + default = ["dsznajder" "es7-react-js-snippets"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.es7ReactJsSnippets.enable { + extensions = [ + config.extraExtensions.es7ReactJsSnippets.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/evenBetterToml.nix b/modules/home-manager-modules/programs/vscode/evenBetterToml.nix new file mode 100644 index 0000000..9813ee1 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/evenBetterToml.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.evenBetterToml = { + enable = lib.mkEnableOption "should the even-better-toml extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "even-better-toml" { + default = ["tamasfe" "even-better-toml"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.evenBetterToml.enable { + extensions = [ + config.extraExtensions.evenBetterToml.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/go.nix b/modules/home-manager-modules/programs/vscode/go.nix new file mode 100644 index 0000000..bd9b771 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/go.nix @@ -0,0 +1,34 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.go = { + enable = lib.mkEnableOption "should the go extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "go" { + default = ["golang" "go"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.go.enable { + extensions = [ + config.extraExtensions.go.extension + ]; + userSettings = { + "go.alternateTools" = { + "gopls" = "gopls"; + }; + "go.toolsManagement.autoUpdate" = false; + "go.useLanguageServer" = true; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/graphql.nix b/modules/home-manager-modules/programs/vscode/graphql.nix new file mode 100644 index 0000000..fde08f3 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/graphql.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.graphql = { + enable = lib.mkEnableOption "should the graphql highlighting extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-graphql" { + default = ["graphql" "vscode-graphql-syntax"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.graphql.enable { + extensions = [ + config.extraExtensions.graphql.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/liveServer.nix b/modules/home-manager-modules/programs/vscode/liveServer.nix new file mode 100644 index 0000000..3f53ca3 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/liveServer.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.liveServer = { + enable = lib.mkEnableOption "should the live-server extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "live-server" { + default = ["ms-vscode" "live-server"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.liveServer.enable { + extensions = [ + config.extraExtensions.liveServer.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/nearley.nix b/modules/home-manager-modules/programs/vscode/nearley.nix new file mode 100644 index 0000000..3020a9e --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/nearley.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.nearley = { + enable = lib.mkEnableOption "should the nearley extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "nearley" { + default = ["karyfoundation" "nearley"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.nearley.enable { + extensions = [ + config.extraExtensions.nearley.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/nixIde.nix b/modules/home-manager-modules/programs/vscode/nixIde.nix new file mode 100644 index 0000000..bc79b69 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/nixIde.nix @@ -0,0 +1,29 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.nixIde = { + enable = lib.mkEnableOption "Enable Nix IDE extension"; + extension = lib.mkPackageOption pkgsRepository "nix-ide" { + default = ["jnoortheen" "nix-ide"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.nixIde.enable { + extensions = [config.extraExtensions.nixIde.extension]; + userSettings = { + "nix.enableLanguageServer" = true; + "nix.serverPath" = "nil"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/oneDark.nix b/modules/home-manager-modules/programs/vscode/oneDark.nix new file mode 100644 index 0000000..5ed43f4 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/oneDark.nix @@ -0,0 +1,30 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.oneDark = { + enable = lib.mkEnableOption "should the one dark theme for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "onedark" { + default = ["akamud" "vscode-theme-onedark"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.oneDark.enable { + extensions = [ + config.extraExtensions.oneDark.extension + ]; + userSettings = { + "workbench.colorTheme" = "Atom One Dark"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/openDyslexicFont.nix b/modules/home-manager-modules/programs/vscode/openDyslexicFont.nix new file mode 100644 index 0000000..f1f6215 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/openDyslexicFont.nix @@ -0,0 +1,49 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.openDyslexicFont = { + enable = lib.mkEnableOption "should OpenDyslexic font be set as the default font for VSCode"; + package = lib.mkPackageOption pkgs "nerd-fonts.open-dyslexic" { + default = ["nerd-fonts" "open-dyslexic"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.openDyslexicFont.enable { + userSettings = { + "editor.fontFamily" = "'OpenDyslexicM Nerd Font Mono', Droid Sans Mono, monospace"; + "editor.fontSize" = 14; + "editor.letterSpacing" = -0.3; + }; + }; + })); + }; + + config = let + enabledProfiles = + lib.filter (profile: profile.extraExtensions.openDyslexicFont.enable or false) + (lib.attrValues config.programs.vscode.profiles); + + anyProfileUsesOpenDyslexicFont = enabledProfiles != []; + + fontPackages = lib.unique (map (profile: profile.extraExtensions.openDyslexicFont.package) enabledProfiles); + in { + # Ensure OpenDyslexic font packages are installed when any VSCode profile uses them + home.packages = fontPackages; + + fonts.fontconfig.enable = lib.mkIf anyProfileUsesOpenDyslexicFont true; + + # Add assertion to ensure the fonts are available + assertions = + map (fontPkg: { + assertion = lib.elem fontPkg config.home.packages; + message = "OpenDyslexic font package '${fontPkg.name or "unknown"}' must be installed when using openDyslexicFont extension for VSCode."; + }) + fontPackages; + }; +} diff --git a/modules/home-manager-modules/programs/vscode/openRemoteSsh.nix b/modules/home-manager-modules/programs/vscode/openRemoteSsh.nix new file mode 100644 index 0000000..c1b6daa --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/openRemoteSsh.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.openRemoteSsh = { + enable = lib.mkEnableOption "should the open-remote-ssh extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "open-remote-ssh" { + default = ["jeanp413" "open-remote-ssh"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.openRemoteSsh.enable { + extensions = [ + config.extraExtensions.openRemoteSsh.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/platformIO.nix b/modules/home-manager-modules/programs/vscode/platformIO.nix new file mode 100644 index 0000000..ace83b7 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/platformIO.nix @@ -0,0 +1,30 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.platformIO = { + enable = lib.mkEnableOption "should the platformIO extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "platformIO" { + default = ["pioarduino" "pioarduino-ide"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.platformIO.enable { + extensions = [ + config.extraExtensions.platformIO.extension + ]; + userSettings = { + "platformio-ide.useBuiltinPIOCore" = false; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/rustAnalyzer.nix b/modules/home-manager-modules/programs/vscode/rustAnalyzer.nix new file mode 100644 index 0000000..66e9ebe --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/rustAnalyzer.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.rustAnalyzer = { + enable = lib.mkEnableOption "should the rust-analyzer extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "rust-analyzer" { + default = ["rust-lang" "rust-analyzer"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.rustAnalyzer.enable { + extensions = [ + config.extraExtensions.rustAnalyzer.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/tauriVscode.nix b/modules/home-manager-modules/programs/vscode/tauriVscode.nix new file mode 100644 index 0000000..9185fb3 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/tauriVscode.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.tauriVscode = { + enable = lib.mkEnableOption "should the tauri-vscode extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "tauri-vscode" { + default = ["tauri-apps" "tauri-vscode"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.tauriVscode.enable { + extensions = [ + config.extraExtensions.tauriVscode.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vitest.nix b/modules/home-manager-modules/programs/vscode/vitest.nix new file mode 100644 index 0000000..446d25b --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vitest.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vitest = { + enable = lib.mkEnableOption "should the vitest extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vitest" { + default = ["vitest" "explorer"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vitest.enable { + extensions = [ + config.extraExtensions.vitest.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeEslint.nix b/modules/home-manager-modules/programs/vscode/vscodeEslint.nix new file mode 100644 index 0000000..64d979f --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeEslint.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeEslint = { + enable = lib.mkEnableOption "should the vscode-eslint extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-eslint" { + default = ["dbaeumer" "vscode-eslint"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeEslint.enable { + extensions = [ + config.extraExtensions.vscodeEslint.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeJest.nix b/modules/home-manager-modules/programs/vscode/vscodeJest.nix new file mode 100644 index 0000000..7c24f2a --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeJest.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeJest = { + enable = lib.mkEnableOption "should the vscode-jest extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-jest" { + default = ["orta" "vscode-jest"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeJest.enable { + extensions = [ + config.extraExtensions.vscodeJest.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeMdx.nix b/modules/home-manager-modules/programs/vscode/vscodeMdx.nix new file mode 100644 index 0000000..c49fe51 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeMdx.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeMdx = { + enable = lib.mkEnableOption "should the vscode-mdx extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-mdx" { + default = ["unifiedjs" "vscode-mdx"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeMdx.enable { + extensions = [ + config.extraExtensions.vscodeMdx.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeStandard.nix b/modules/home-manager-modules/programs/vscode/vscodeStandard.nix new file mode 100644 index 0000000..31c8ad0 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeStandard.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeStandard = { + enable = lib.mkEnableOption "should the vscode-standard extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-standard" { + default = ["standard" "vscode-standard"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeStandard.enable { + extensions = [ + config.extraExtensions.vscodeStandard.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix b/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix new file mode 100644 index 0000000..0d43b29 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeStylelint = { + enable = lib.mkEnableOption "should the vscode-stylelint extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-stylelint" { + default = ["stylelint" "vscode-stylelint"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeStylelint.enable { + extensions = [ + config.extraExtensions.vscodeStylelint.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/sops.nix b/modules/home-manager-modules/sops.nix new file mode 100644 index 0000000..910fbb6 --- /dev/null +++ b/modules/home-manager-modules/sops.nix @@ -0,0 +1,7 @@ +{...}: { + config = { + sops = { + age.keyFile = "/var/lib/sops-nix/key.txt"; + }; + }; +} diff --git a/modules/home-manager-modules/user.nix b/modules/home-manager-modules/user.nix new file mode 100644 index 0000000..efce22d --- /dev/null +++ b/modules/home-manager-modules/user.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + osConfig, + ... +}: { + options.user = { + isDesktopUser = lib.mkOption { + type = lib.types.bool; + default = osConfig.host.users.${config.home.username}.isDesktopUser; + }; + isTerminalUser = lib.mkOption { + type = lib.types.bool; + default = osConfig.host.users.${config.home.username}.isTerminalUser; + }; + }; +} diff --git a/modules/home-manager/default.nix b/modules/home-manager/default.nix deleted file mode 100644 index 2f62982..0000000 --- a/modules/home-manager/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{config, ...}: let - mod = config.flake.homeModules; -in { - flake.homeModules.home-manager-modules-all = { - imports = [ - mod.home-manager-sops - mod.home-manager-user - mod.home-manager-flipperzero - mod.home-manager-i18n - mod.home-manager-impermanence - mod.home-manager-openssh - mod.home-manager-gnome - mod.home-manager-programs - ]; - }; -} diff --git a/modules/home-manager/flipperzero.nix b/modules/home-manager/flipperzero.nix deleted file mode 100644 index a671df3..0000000 --- a/modules/home-manager/flipperzero.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - flake.homeModules.home-manager-flipperzero = {lib, ...}: { - options.hardware.flipperzero.enable = lib.mkEnableOption "enable flipperzero hardware"; - }; -} diff --git a/modules/home-manager/gnome.nix b/modules/home-manager/gnome.nix deleted file mode 100644 index 242cd15..0000000 --- a/modules/home-manager/gnome.nix +++ /dev/null @@ -1,205 +0,0 @@ -{...}: { - flake.homeModules.home-manager-gnome = { - lib, - config, - pkgs, - ... - }: let - enabledExtensions = - [] - ++ lib.optional config.gnome.extensions.dash-to-dock.enable pkgs.gnomeExtensions.dash-to-dock - ++ lib.optional config.gnome.extensions.dash-to-panel.enable pkgs.gnomeExtensions.dash-to-panel; - - extensions = config.gnome.extraExtensions ++ enabledExtensions; - in { - options.gnome = { - extraWindowControls = lib.mkEnableOption "Should we add back in the minimize and maximize window controls?"; - clockFormat = lib.mkOption { - type = lib.types.enum [ - "12h" - "24h" - ]; - default = "24h"; - }; - colorScheme = lib.mkOption { - type = lib.types.enum [ - "default" - "prefer-dark" - "prefer-light" - ]; - default = "default"; - }; - accentColor = lib.mkOption { - type = lib.types.enum [ - "blue" - "teal" - "green" - "yellow" - "orange" - "red" - "pink" - "purple" - "slate" - ]; - default = "blue"; - }; - extraExtensions = lib.mkOption { - type = lib.types.listOf lib.types.package; - default = []; - description = "The set of extensions to install and enable in the user environment."; - }; - hotkeys = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - key = lib.mkOption { - type = lib.types.strMatching "[a-zA-Z0-9-]+"; - default = builtins.replaceStrings [" " "/" "_"] ["-" "-" "-"] name; - }; - name = lib.mkOption { - type = lib.types.str; - default = name; - }; - binding = lib.mkOption { - type = lib.types.str; - }; - command = lib.mkOption { - type = lib.types.str; - }; - }; - })); - default = {}; - }; - displayScaling = lib.mkOption { - type = lib.types.nullOr (lib.types.enum [100 125 150 175 200]); - default = null; - description = "Display scaling percentage for GNOME"; - }; - experimentalFeatures = lib.mkOption { - type = lib.types.submodule { - options = { - scaleMonitorFramebuffer = lib.mkEnableOption "scale-monitor-framebuffer experimental feature"; - }; - }; - default = {}; - description = "GNOME experimental features to enable"; - }; - - nightLight = lib.mkOption { - type = lib.types.submodule { - options = { - enable = lib.mkEnableOption "night light (blue light filter)"; - automatic = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to automatically schedule night light based on sunset/sunrise"; - }; - fromTime = lib.mkOption { - type = lib.types.float; - default = 20.0; - description = "Start time for night light in 24-hour format (e.g., 20.0 for 8:00 PM)"; - }; - toTime = lib.mkOption { - type = lib.types.float; - default = 6.0; - description = "End time for night light in 24-hour format (e.g., 6.0 for 6:00 AM)"; - }; - temperature = lib.mkOption { - type = lib.types.int; - default = 4000; - description = "Color temperature for night light (1000-10000K, lower is warmer)"; - }; - }; - }; - default = {}; - description = "Night light configuration"; - }; - - extensions = { - dash-to-dock = { - enable = lib.mkEnableOption "Dash to Dock extension"; - options = lib.mkOption { - type = lib.types.nullOr lib.types.attrs; - default = null; - description = "Dash to Dock configuration options. If null, no custom configuration will be applied."; - }; - }; - - dash-to-panel = { - enable = lib.mkEnableOption "Dash to Panel extension"; - options = lib.mkOption { - type = lib.types.nullOr lib.types.attrs; - default = null; - description = "Dash to Panel configuration options. If null, no custom configuration will be applied."; - }; - }; - }; - }; - - config = { - home.packages = extensions; - dconf = { - settings = lib.mkMerge [ - { - "org/gnome/shell" = { - disable-user-extensions = false; # enables user extensions - enabled-extensions = builtins.map (extension: extension.extensionUuid) extensions; - }; - - "org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close"; - - "org/gnome/desktop/interface".color-scheme = config.gnome.colorScheme; - "org/gnome/desktop/interface".accent-color = config.gnome.accentColor; - "org/gnome/desktop/interface".clock-format = config.gnome.clockFormat; - "org/gnome/desktop/interface".text-scaling-factor = lib.mkIf (config.gnome.displayScaling != null) (config.gnome.displayScaling / 100.0); - - "org/gnome/mutter".experimental-features = lib.mkIf (builtins.any (x: x) (builtins.attrValues config.gnome.experimentalFeatures)) ( - lib.optional config.gnome.experimentalFeatures.scaleMonitorFramebuffer "scale-monitor-framebuffer" - ); - } - - # Night light configuration - (lib.mkIf config.gnome.nightLight.enable { - "org/gnome/settings-daemon/plugins/color" = { - night-light-enabled = true; - night-light-schedule-automatic = config.gnome.nightLight.automatic; - night-light-schedule-from = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.fromTime; - night-light-schedule-to = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.toTime; - night-light-temperature = config.gnome.nightLight.temperature; - }; - }) - ( - lib.mkMerge ( - builtins.map (value: let - entry = "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${value.key}"; - in { - ${entry} = { - binding = value.binding; - command = value.command; - name = value.name; - }; - - "org/gnome/settings-daemon/plugins/media-keys" = { - custom-keybindings = [ - "/${entry}/" - ]; - }; - }) - ( - lib.attrsets.mapAttrsToList (_: value: value) config.gnome.hotkeys - ) - ) - ) - - # Extension configurations - (lib.mkIf (config.gnome.extensions.dash-to-dock.enable && config.gnome.extensions.dash-to-dock.options != null) { - "org/gnome/shell/extensions/dash-to-dock" = config.gnome.extensions.dash-to-dock.options; - }) - - (lib.mkIf (config.gnome.extensions.dash-to-panel.enable && config.gnome.extensions.dash-to-panel.options != null) { - "org/gnome/shell/extensions/dash-to-panel" = config.gnome.extensions.dash-to-panel.options; - }) - ]; - }; - }; - }; -} diff --git a/modules/home-manager/i18n.nix b/modules/home-manager/i18n.nix deleted file mode 100644 index a488d7c..0000000 --- a/modules/home-manager/i18n.nix +++ /dev/null @@ -1,44 +0,0 @@ -{...}: { - flake.homeModules.home-manager-i18n = { - lib, - config, - ... - }: { - options = { - i18n = { - defaultLocale = lib.mkOption { - type = lib.types.str; - default = "en_US.UTF-8"; - example = "nl_NL.UTF-8"; - description = '' - The default locale. It determines the language for program - messages, the format for dates and times, sort order, and so on. - It also determines the character set, such as UTF-8. - ''; - }; - - extraLocaleSettings = lib.mkOption { - type = lib.types.attrsOf lib.types.str; - default = {}; - example = { - LC_MESSAGES = "en_US.UTF-8"; - LC_TIME = "de_DE.UTF-8"; - }; - description = '' - A set of additional system-wide locale settings other than - `LANG` which can be configured with - {option}`i18n.defaultLocale`. - ''; - }; - }; - }; - - config = { - home.sessionVariables = - { - LANG = config.i18n.defaultLocale; - } - // config.i18n.extraLocaleSettings; - }; - }; -} diff --git a/modules/home-manager/impermanence.nix b/modules/home-manager/impermanence.nix deleted file mode 100644 index a8971c8..0000000 --- a/modules/home-manager/impermanence.nix +++ /dev/null @@ -1,45 +0,0 @@ -{...}: { - flake.homeModules.home-manager-impermanence = { - config, - lib, - osConfig, - ... - }: let - cfg = config.impermanence; - in { - options.impermanence = { - enable = lib.mkEnableOption "impermanence for home directory"; - fallbackPersistence.enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - persistencePath = lib.mkOption { - type = lib.types.str; - default = - if osConfig.storage.generateBase - then "/persist/replicate/home" - else "/persist"; - description = "The base path for user home persistence. The impermanence module will automatically append the user's home directory path. Automatically adapts based on whether the system uses the new dataset layout or the legacy one."; - }; - }; - - config = lib.mkMerge [ - (lib.mkIf config.impermanence.enable { - assertions = [ - { - assertion = osConfig.storage.impermanence.enable; - message = "impermanence can not be enabled for a user when it is not enabled for the system"; - } - ]; - }) - # If impermanence is not enabled for this user but system impermanence is enabled, - # persist the entire home directory as fallback - (lib.mkIf (osConfig.storage.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { - home.persistence."${cfg.persistencePath}" = { - directories = ["."]; - allowOther = true; - }; - }) - ]; - }; -} diff --git a/modules/home-manager/openssh.nix b/modules/home-manager/openssh.nix deleted file mode 100644 index 662c5ee..0000000 --- a/modules/home-manager/openssh.nix +++ /dev/null @@ -1,109 +0,0 @@ -{...}: { - flake.homeModules.home-manager-openssh = { - pkgs, - config, - osConfig, - lib, - ... - }: { - options.programs.openssh = { - enable = lib.mkEnableOption "should we enable openssh"; - authorizedKeys = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - }; - hostKeys = lib.mkOption { - type = lib.types.listOf lib.types.attrs; - default = []; - example = [ - { - type = "rsa"; - bits = 4096; - path = "${config.home.username}_${osConfig.networking.hostName}_rsa"; - rounds = 100; - openSSHFormat = true; - } - { - type = "ed25519"; - path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; - rounds = 100; - comment = "key comment"; - } - ]; - description = '' - NixOS can automatically generate SSH host keys. This option - specifies the path, type and size of each key. See - {manpage}`ssh-keygen(1)` for supported types - and sizes. Paths are relative to home directory - ''; - }; - }; - - config = lib.mkIf config.programs.openssh.enable ( - lib.mkMerge [ - ( - lib.mkIf ((builtins.length config.programs.openssh.hostKeys) != 0) { - services.ssh-agent.enable = true; - programs.ssh = { - enable = true; - enableDefaultConfig = false; - matchBlocks = { - "*" = { - compression = true; - addKeysToAgent = "confirm"; - }; - }; - # extraConfig = lib.strings.concatLines ( - # builtins.map (hostKey: "IdentityFile ~/.ssh/${hostKey.path}") config.programs.openssh.hostKeys - # ); - }; - - systemd.user.services = builtins.listToAttrs ( - builtins.map (hostKey: - lib.attrsets.nameValuePair "ssh-gen-keys-${hostKey.path}" { - Install = { - WantedBy = ["default.target"]; - }; - Service = let - path = "${config.home.homeDirectory}/.ssh/${hostKey.path}"; - in { - Restart = "always"; - Type = "simple"; - ExecStart = "${ - pkgs.writeShellScript "ssh-gen-keys" '' - if ! [ -s "${path}" ]; then - if ! [ -h "${path}" ]; then - rm -f "${path}" - fi - mkdir -p "$(dirname '${path}')" - chmod 0755 "$(dirname '${path}')" - ${pkgs.openssh}/bin/ssh-keygen \ - -t "${hostKey.type}" \ - ${lib.optionalString (hostKey ? bits) "-b ${toString hostKey.bits}"} \ - ${lib.optionalString (hostKey ? rounds) "-a ${toString hostKey.rounds}"} \ - ${lib.optionalString (hostKey ? comment) "-C '${hostKey.comment}'"} \ - ${lib.optionalString (hostKey ? openSSHFormat && hostKey.openSSHFormat) "-o"} \ - -f "${path}" \ - -N "" - chown ${config.home.username} ${path}* - chgrp ${config.home.username} ${path}* - fi - '' - }"; - }; - }) - config.programs.openssh.hostKeys - ); - } - ) - (lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - files = lib.lists.flatten ( - builtins.map (hostKey: [".ssh/${hostKey.path}" ".ssh/${hostKey.path}.pub"]) config.programs.openssh.hostKeys - ); - }; - }) - ] - ); - }; -} diff --git a/modules/home-manager/programs/android-studio.nix b/modules/home-manager/programs/android-studio.nix deleted file mode 100644 index e953c40..0000000 --- a/modules/home-manager/programs/android-studio.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.homeModules.home-manager-android-studio = { - lib, - pkgs, - config, - ... - }: { - options.programs.android-studio = { - enable = lib.mkEnableOption "enable android-studio"; - }; - - config = lib.mkIf config.programs.android-studio.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - android-studio - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/Google/AndroidStudio" - ".android" - ".gradle" - "${config.xdg.cacheHome}/Google/AndroidStudio" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/anki.nix b/modules/home-manager/programs/anki.nix deleted file mode 100644 index 222ae85..0000000 --- a/modules/home-manager/programs/anki.nix +++ /dev/null @@ -1,15 +0,0 @@ -{...}: { - flake.homeModules.home-manager-anki = { - lib, - config, - ... - }: { - config = lib.mkIf (config.programs.anki.enable && config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - ".local/share/Anki2" - ]; - }; - }; - }; -} diff --git a/modules/home-manager/programs/bitwarden.nix b/modules/home-manager/programs/bitwarden.nix deleted file mode 100644 index 594520a..0000000 --- a/modules/home-manager/programs/bitwarden.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-bitwarden = { - lib, - pkgs, - config, - ... - }: { - options.programs.bitwarden = { - enable = lib.mkEnableOption "enable bitwarden"; - }; - - config = lib.mkIf config.programs.bitwarden.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - bitwarden-desktop - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/Bitwarden" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/bruno.nix b/modules/home-manager/programs/bruno.nix deleted file mode 100644 index 58aeadf..0000000 --- a/modules/home-manager/programs/bruno.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-bruno = { - lib, - pkgs, - config, - ... - }: { - options.programs.bruno = { - enable = lib.mkEnableOption "enable bruno"; - }; - - config = lib.mkIf config.programs.bruno.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - bruno - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/bruno/" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/calibre.nix b/modules/home-manager/programs/calibre.nix deleted file mode 100644 index a7b289c..0000000 --- a/modules/home-manager/programs/calibre.nix +++ /dev/null @@ -1,25 +0,0 @@ -{...}: { - flake.homeModules.home-manager-calibre = { - lib, - pkgs, - config, - ... - }: { - config = lib.mkIf config.programs.calibre.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - calibre - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/calibre" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/davinci-resolve.nix b/modules/home-manager/programs/davinci-resolve.nix deleted file mode 100644 index cb29a81..0000000 --- a/modules/home-manager/programs/davinci-resolve.nix +++ /dev/null @@ -1,30 +0,0 @@ -{...}: { - flake.homeModules.home-manager-davinci-resolve = { - lib, - pkgs, - config, - ... - }: { - options.programs.davinci-resolve = { - enable = lib.mkEnableOption "enable davinci-resolve"; - }; - - config = lib.mkIf config.programs.davinci-resolve.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - davinci-resolve - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.dataHome}/DaVinciResolve" - "${config.xdg.configHome}/blackmagic" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/dbeaver.nix b/modules/home-manager/programs/dbeaver.nix deleted file mode 100644 index c567571..0000000 --- a/modules/home-manager/programs/dbeaver.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-dbeaver = { - lib, - pkgs, - config, - ... - }: { - options.programs.dbeaver-bin = { - enable = lib.mkEnableOption "enable dbeaver"; - }; - - config = lib.mkIf config.programs.dbeaver-bin.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - dbeaver-bin - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.dataHome}/DBeaverData/" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/default.nix b/modules/home-manager/programs/default.nix deleted file mode 100644 index 4451ef7..0000000 --- a/modules/home-manager/programs/default.nix +++ /dev/null @@ -1,59 +0,0 @@ -{config, ...}: let - mod = config.flake.homeModules; -in { - flake.homeModules.home-manager-programs = { - imports = [ - mod.home-manager-android-studio - mod.home-manager-anki - mod.home-manager-bitwarden - mod.home-manager-bruno - mod.home-manager-calibre - mod.home-manager-davinci-resolve - mod.home-manager-dbeaver - mod.home-manager-discord - mod.home-manager-dungeon-draft - mod.home-manager-e621-downloader - mod.home-manager-firefox - mod.home-manager-freecad - mod.home-manager-gdx-liftoff - mod.home-manager-gimp - mod.home-manager-guild-wars-2 - mod.home-manager-idea - mod.home-manager-inkscape - mod.home-manager-kdenlive - mod.home-manager-kicad - mod.home-manager-krita - mod.home-manager-libreoffice - mod.home-manager-makemkv - mod.home-manager-mapillary-uploader - mod.home-manager-mfoc - mod.home-manager-noisetorch - mod.home-manager-noita-entangled-worlds - mod.home-manager-obs - mod.home-manager-obsidian - mod.home-manager-olympus - mod.home-manager-onionshare - mod.home-manager-openrgb - mod.home-manager-openvpn - mod.home-manager-pdfarranger - mod.home-manager-picard - mod.home-manager-piper - mod.home-manager-prostudiomasters - mod.home-manager-proton-calendar-pwa - mod.home-manager-proton-mail-pwa - mod.home-manager-proton-pass - mod.home-manager-protonvpn - mod.home-manager-proxmark3 - mod.home-manager-qbittorrent - mod.home-manager-qflipper - mod.home-manager-signal - mod.home-manager-steam - mod.home-manager-tor-browser - mod.home-manager-ungoogled-chromium - mod.home-manager-via - mod.home-manager-vmware-workstation - mod.home-manager-vortex - mod.home-manager-vscode - ]; - }; -} diff --git a/modules/home-manager/programs/discord.nix b/modules/home-manager/programs/discord.nix deleted file mode 100644 index 60fbd5b..0000000 --- a/modules/home-manager/programs/discord.nix +++ /dev/null @@ -1,19 +0,0 @@ -{...}: { - flake.homeModules.home-manager-discord = { - lib, - config, - ... - }: { - config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/discord/" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/dungeon-draft.nix b/modules/home-manager/programs/dungeon-draft.nix deleted file mode 100644 index 79738cd..0000000 --- a/modules/home-manager/programs/dungeon-draft.nix +++ /dev/null @@ -1,26 +0,0 @@ -{...}: { - flake.homeModules.home-manager-dungeon-draft = { - config, - lib, - ... - }: let - cfg = config.programs.dungeon-draft; - in { - options.programs.dungeon-draft = { - enable = lib.mkEnableOption "Dungeon Draft"; - }; - - config = { - assertions = [ - { - assertion = !cfg.enable; - message = '' - Dungeon Draft module is not yet fully configured. - Please download the Dungeon Draft executable (.exe) from the official website, - then configure the Wine environment and executable path as needed. - ''; - } - ]; - }; - }; -} diff --git a/modules/home-manager/programs/e621-downloader.nix b/modules/home-manager/programs/e621-downloader.nix deleted file mode 100644 index 37979f4..0000000 --- a/modules/home-manager/programs/e621-downloader.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-e621-downloader = { - lib, - pkgs, - config, - ... - }: { - options.programs.e621-downloader = { - enable = lib.mkEnableOption "enable e621-downloader"; - }; - - config = lib.mkIf config.programs.e621-downloader.enable { - home.packages = with pkgs; [ - e621-downloader - ]; - }; - }; -} diff --git a/modules/home-manager/programs/element-desktop.nix b/modules/home-manager/programs/element-desktop.nix deleted file mode 100644 index 2c69ef7..0000000 --- a/modules/home-manager/programs/element-desktop.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.homeModules.home-manager-element-desktop = { - lib, - pkgs, - config, - ... - }: let - cfg = config.programs.element-desktop; - in { - options.programs.element-desktop = { - enable = lib.mkEnableOption "enable Element Desktop"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.element-desktop; - description = "Browser package to use for the PWA"; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - element-desktop - ]; - } - ( - lib.mkIf (config.impermanence.enable) { - # TODO: create me - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/firefox.nix b/modules/home-manager/programs/firefox.nix deleted file mode 100644 index a31be65..0000000 --- a/modules/home-manager/programs/firefox.nix +++ /dev/null @@ -1,43 +0,0 @@ -{...}: { - flake.homeModules.home-manager-firefox = { - lib, - config, - ... - }: let - buildProfilePersistence = profile: { - directories = [ - ".mozilla/firefox/${profile}/extensions" - ]; - files = [ - ".mozilla/firefox/${profile}/cookies.sqlite" - ".mozilla/firefox/${profile}/favicons.sqlite" - # Permissions and ${profileName} levels for each site - ".mozilla/firefox/${profile}/permissions.sqlite" - ".mozilla/firefox/${profile}/content-prefs.sqlite" - # Browser history and bookmarks - ".mozilla/firefox/${profile}/places.sqlite" - # I guess this is useful? - # https://bugzilla.mozilla.org/show_bug.cgi?id=1511384 - # https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria - ".mozilla/firefox/${profile}/storage.sqlite" - # Extension configuration - ".mozilla/firefox/${profile}/extension-settings.json" - ]; - }; - in { - config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = lib.mkMerge ( - ( - lib.attrsets.mapAttrsToList - (profile: _: buildProfilePersistence profile) - config.programs.firefox.profiles - ) - ++ ( - lib.lists.optional - ((builtins.length (lib.attrsets.mapAttrsToList (key: value: value) config.programs.firefox.profiles)) == 0) - (buildProfilePersistence "default") - ) - ); - }; - }; -} diff --git a/modules/home-manager/programs/freecad.nix b/modules/home-manager/programs/freecad.nix deleted file mode 100644 index 6e25b29..0000000 --- a/modules/home-manager/programs/freecad.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-freecad = { - lib, - pkgs, - config, - ... - }: { - options.programs.freecad = { - enable = lib.mkEnableOption "enable freecad"; - }; - - config = lib.mkIf config.programs.freecad.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - freecad - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/FreeCAD" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/gdx-liftoff.nix b/modules/home-manager/programs/gdx-liftoff.nix deleted file mode 100644 index df1692a..0000000 --- a/modules/home-manager/programs/gdx-liftoff.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-gdx-liftoff = { - lib, - pkgs, - config, - ... - }: { - options.programs.gdx-liftoff = { - enable = lib.mkEnableOption "enable gdx-liftoff"; - }; - - config = lib.mkIf config.programs.gdx-liftoff.enable { - home.packages = with pkgs; [ - gdx-liftoff - ]; - }; - }; -} diff --git a/modules/home-manager/programs/gimp.nix b/modules/home-manager/programs/gimp.nix deleted file mode 100644 index 89e1d04..0000000 --- a/modules/home-manager/programs/gimp.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-gimp = { - lib, - pkgs, - config, - ... - }: { - options.programs.gimp = { - enable = lib.mkEnableOption "enable gimp"; - }; - - config = lib.mkIf config.programs.gimp.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - gimp - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/GIMP" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/guild-wars-2.nix b/modules/home-manager/programs/guild-wars-2.nix deleted file mode 100644 index 4c6e5fe..0000000 --- a/modules/home-manager/programs/guild-wars-2.nix +++ /dev/null @@ -1,26 +0,0 @@ -{...}: { - flake.homeModules.home-manager-guild-wars-2 = { - config, - lib, - ... - }: let - cfg = config.programs.guild-wars-2; - in { - options.programs.guild-wars-2 = { - enable = lib.mkEnableOption "Guild Wars 2"; - }; - - config = { - assertions = [ - { - assertion = !cfg.enable; - message = '' - Guild Wars 2 module is not yet fully configured. - Please install Guild Wars 2 manually via Steam or the official client, - then configure the Wine environment as needed. - ''; - } - ]; - }; - }; -} diff --git a/modules/home-manager/programs/idea.nix b/modules/home-manager/programs/idea.nix deleted file mode 100644 index 68527c1..0000000 --- a/modules/home-manager/programs/idea.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - flake.homeModules.home-manager-idea = { - lib, - pkgs, - config, - ... - }: { - options.programs.jetbrains.idea-oss = { - enable = lib.mkEnableOption "enable idea-oss"; - }; - - config = lib.mkIf config.programs.jetbrains.idea-oss.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - jetbrains.idea-oss - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - # configuration - "${config.xdg.configHome}/JetBrains/" - # plugins - "${config.xdg.dataHome}/JetBrains/" - # System and Logs - "${config.xdg.cacheHome}/JetBrains/" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/inkscape.nix b/modules/home-manager/programs/inkscape.nix deleted file mode 100644 index f28546f..0000000 --- a/modules/home-manager/programs/inkscape.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-inkscape = { - lib, - pkgs, - config, - ... - }: { - options.programs.inkscape = { - enable = lib.mkEnableOption "enable inkscape"; - }; - - config = lib.mkIf config.programs.inkscape.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - inkscape - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/inkscape" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/kdenlive.nix b/modules/home-manager/programs/kdenlive.nix deleted file mode 100644 index 142fda3..0000000 --- a/modules/home-manager/programs/kdenlive.nix +++ /dev/null @@ -1,37 +0,0 @@ -{...}: { - flake.homeModules.home-manager-kdenlive = { - config, - lib, - pkgs, - ... - }: let - cfg = config.programs.kdenlive; - in { - options.programs.kdenlive = { - enable = lib.mkEnableOption "kdenlive"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.kdePackages.kdenlive; - description = "The kdenlive package to install."; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - home.packages = [ - cfg.package - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/kdenliverc" - "${config.xdg.dataHome}/kdenlive" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/kicad.nix b/modules/home-manager/programs/kicad.nix deleted file mode 100644 index 7bea756..0000000 --- a/modules/home-manager/programs/kicad.nix +++ /dev/null @@ -1,25 +0,0 @@ -{...}: { - flake.homeModules.home-manager-kicad = { - lib, - pkgs, - config, - ... - }: { - options.programs.kicad = { - enable = lib.mkEnableOption "enable kicad"; - }; - - config = lib.mkIf config.programs.kicad.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - kicad - ]; - } - ( - lib.mkIf config.impermanence.enable { - # TODO: - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/krita.nix b/modules/home-manager/programs/krita.nix deleted file mode 100644 index 5821c21..0000000 --- a/modules/home-manager/programs/krita.nix +++ /dev/null @@ -1,30 +0,0 @@ -{...}: { - flake.homeModules.home-manager-krita = { - lib, - pkgs, - config, - ... - }: { - options.programs.krita = { - enable = lib.mkEnableOption "enable krita"; - }; - - config = lib.mkIf config.programs.krita.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - krita - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/kritarc" - "${config.xdg.dataHome}/krita" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/libreoffice.nix b/modules/home-manager/programs/libreoffice.nix deleted file mode 100644 index 007b1e4..0000000 --- a/modules/home-manager/programs/libreoffice.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-libreoffice = { - lib, - pkgs, - config, - ... - }: { - options.programs.libreoffice = { - enable = lib.mkEnableOption "enable libreoffice"; - }; - - config = lib.mkIf config.programs.libreoffice.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - libreoffice - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/libreoffice" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/makemkv.nix b/modules/home-manager/programs/makemkv.nix deleted file mode 100644 index 8d7f349..0000000 --- a/modules/home-manager/programs/makemkv.nix +++ /dev/null @@ -1,43 +0,0 @@ -{...}: { - flake.homeModules.home-manager-makemkv = { - lib, - pkgs, - config, - ... - }: { - options.programs.makemkv = { - enable = lib.mkEnableOption "enable makemkv"; - appKeyFile = lib.mkOption { - type = lib.types.str; - }; - destinationDir = lib.mkOption { - type = lib.types.str; - }; - }; - - config = lib.mkIf config.programs.makemkv.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - makemkv - ]; - - sops.templates."MakeMKV.settings.conf".content = '' - app_DestinationDir = "${config.programs.makemkv.destinationDir}" - app_DestinationType = "2" - app_Key = "${config.programs.makemkv.appKeyFile}" - ''; - - home.file.".MakeMKV/settings.conf".source = config.lib.file.mkOutOfStoreSymlink config.sops.templates."MakeMKV.settings.conf".path; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - ".MakeMKV" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/mapillary-uploader.nix b/modules/home-manager/programs/mapillary-uploader.nix deleted file mode 100644 index 825dd6a..0000000 --- a/modules/home-manager/programs/mapillary-uploader.nix +++ /dev/null @@ -1,31 +0,0 @@ -{...}: { - flake.homeModules.home-manager-mapillary-uploader = { - config, - lib, - pkgs, - ... - }: - with lib; let - cfg = config.programs.mapillary-uploader; - in { - options.programs.mapillary-uploader = { - enable = mkEnableOption "Mapillary Desktop Uploader"; - }; - - config = mkIf cfg.enable (mkMerge [ - { - home.packages = [pkgs.mapillary-uploader]; - } - ( - mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/mapillary-uploader" - "${config.xdg.dataHome}/mapillary-uploader" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/mfoc.nix b/modules/home-manager/programs/mfoc.nix deleted file mode 100644 index b8d07cf..0000000 --- a/modules/home-manager/programs/mfoc.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-mfoc = { - lib, - pkgs, - config, - ... - }: { - options.programs.mfoc = { - enable = lib.mkEnableOption "enable mfoc"; - }; - - config = lib.mkIf config.programs.mfoc.enable { - home.packages = with pkgs; [ - mfoc - ]; - }; - }; -} diff --git a/modules/home-manager/programs/noisetorch.nix b/modules/home-manager/programs/noisetorch.nix deleted file mode 100644 index 1087eca..0000000 --- a/modules/home-manager/programs/noisetorch.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-noisetorch = { - lib, - pkgs, - config, - ... - }: { - options.programs.noisetorch = { - enable = lib.mkEnableOption "enable noisetorch"; - }; - - config = lib.mkIf config.programs.noisetorch.enable { - home.packages = with pkgs; [ - noisetorch - ]; - }; - }; -} diff --git a/modules/home-manager/programs/noita-entangled-worlds.nix b/modules/home-manager/programs/noita-entangled-worlds.nix deleted file mode 100644 index 4776ada..0000000 --- a/modules/home-manager/programs/noita-entangled-worlds.nix +++ /dev/null @@ -1,20 +0,0 @@ -{...}: { - flake.homeModules.home-manager-noita-entangled-worlds = { - lib, - pkgs, - config, - ... - }: { - options = { - programs.noita-entangled-worlds = { - enable = lib.mkEnableOption "Noita Entangled Worlds multiplayer mod"; - }; - }; - - config = lib.mkIf config.programs.noita-entangled-worlds.enable { - home.packages = with pkgs; [ - noita_entangled_worlds - ]; - }; - }; -} diff --git a/modules/home-manager/programs/obs.nix b/modules/home-manager/programs/obs.nix deleted file mode 100644 index e98c82b..0000000 --- a/modules/home-manager/programs/obs.nix +++ /dev/null @@ -1,19 +0,0 @@ -{...}: { - flake.homeModules.home-manager-obs = { - lib, - config, - ... - }: { - config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/obs-studio" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/obsidian.nix b/modules/home-manager/programs/obsidian.nix deleted file mode 100644 index 1018c20..0000000 --- a/modules/home-manager/programs/obsidian.nix +++ /dev/null @@ -1,19 +0,0 @@ -{...}: { - flake.homeModules.home-manager-obsidian = { - lib, - config, - ... - }: { - config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/obsidian" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/olympus.nix b/modules/home-manager/programs/olympus.nix deleted file mode 100644 index d92e845..0000000 --- a/modules/home-manager/programs/olympus.nix +++ /dev/null @@ -1,37 +0,0 @@ -{...}: { - flake.homeModules.home-manager-olympus = { - config, - lib, - pkgs, - ... - }: let - cfg = config.programs.olympus; - in { - options.programs.olympus = { - enable = lib.mkEnableOption "olympus"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.olympus; - description = "The olympus package to install."; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - home.packages = [ - cfg.package - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/olympus" - "${config.xdg.dataHome}/olympus" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/onionshare.nix b/modules/home-manager/programs/onionshare.nix deleted file mode 100644 index 6717eff..0000000 --- a/modules/home-manager/programs/onionshare.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-onionshare = { - lib, - pkgs, - config, - ... - }: { - options.programs.onionshare = { - enable = lib.mkEnableOption "enable onionshare"; - }; - - config = lib.mkIf config.programs.onionshare.enable { - home.packages = with pkgs; [ - onionshare - ]; - }; - }; -} diff --git a/modules/home-manager/programs/openrgb.nix b/modules/home-manager/programs/openrgb.nix deleted file mode 100644 index e098876..0000000 --- a/modules/home-manager/programs/openrgb.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-openrgb = { - lib, - pkgs, - config, - ... - }: { - options.programs.openrgb = { - enable = lib.mkEnableOption "enable openrgb"; - }; - - config = lib.mkIf config.programs.openrgb.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - openrgb - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/OpenRGB" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/openvpn.nix b/modules/home-manager/programs/openvpn.nix deleted file mode 100644 index 8d5ea5a..0000000 --- a/modules/home-manager/programs/openvpn.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-openvpn = { - lib, - pkgs, - config, - ... - }: { - options.programs.openvpn = { - enable = lib.mkEnableOption "enable openvpn"; - }; - - config = lib.mkIf config.programs.openvpn.enable { - home.packages = with pkgs; [ - openvpn - ]; - }; - }; -} diff --git a/modules/home-manager/programs/pdfarranger.nix b/modules/home-manager/programs/pdfarranger.nix deleted file mode 100644 index b55dd8d..0000000 --- a/modules/home-manager/programs/pdfarranger.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-pdfarranger = { - lib, - pkgs, - config, - ... - }: { - options.programs.pdfarranger = { - enable = lib.mkEnableOption "enable pdfarranger"; - }; - - config = lib.mkIf config.programs.pdfarranger.enable { - home.packages = with pkgs; [ - pdfarranger - ]; - }; - }; -} diff --git a/modules/home-manager/programs/picard.nix b/modules/home-manager/programs/picard.nix deleted file mode 100644 index 50aeb0b..0000000 --- a/modules/home-manager/programs/picard.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-picard = { - lib, - pkgs, - config, - ... - }: { - options.programs.picard = { - enable = lib.mkEnableOption "enable picard"; - }; - - config = lib.mkIf config.programs.picard.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - picard - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/MusicBrainz" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/piper.nix b/modules/home-manager/programs/piper.nix deleted file mode 100644 index b5eb3b3..0000000 --- a/modules/home-manager/programs/piper.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-piper = { - lib, - pkgs, - config, - ... - }: { - options.programs.piper = { - enable = lib.mkEnableOption "enable piper"; - }; - - config = lib.mkIf config.programs.piper.enable { - home.packages = with pkgs; [ - piper - ]; - }; - }; -} diff --git a/modules/home-manager/programs/prostudiomasters.nix b/modules/home-manager/programs/prostudiomasters.nix deleted file mode 100644 index f96b90e..0000000 --- a/modules/home-manager/programs/prostudiomasters.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-prostudiomasters = { - lib, - pkgs, - config, - ... - }: { - options.programs.prostudiomasters = { - enable = lib.mkEnableOption "enable prostudiomasters"; - }; - - config = lib.mkIf config.programs.prostudiomasters.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - prostudiomasters - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/ProStudioMasters" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/proton-calendar-pwa.nix b/modules/home-manager/programs/proton-calendar-pwa.nix deleted file mode 100644 index 7417287..0000000 --- a/modules/home-manager/programs/proton-calendar-pwa.nix +++ /dev/null @@ -1,57 +0,0 @@ -{...}: { - flake.homeModules.home-manager-proton-calendar-pwa = { - lib, - pkgs, - config, - ... - }: let - cfg = config.programs.proton-calendar-pwa; - isChromium = cfg.package == pkgs.chromium; - isBrowserImpermanenceSupported = cfg.package == pkgs.chromium; - in { - options.programs.proton-calendar-pwa = { - enable = lib.mkEnableOption "enable Proton Calendar PWA"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.chromium; - description = "Browser package to use for the PWA"; - }; - impermanence = { - enable = lib.mkOption { - type = lib.types.bool; - default = isBrowserImpermanenceSupported; - description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium."; - }; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - warnings = - lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported) - "proton-calendar-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}"; - } - ( - lib.mkIf isChromium { - xdg.desktopEntries.proton-calendar-pwa = { - name = "Proton Calendar"; - type = "Application"; - exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://calendar.proton.me"; - icon = "chrome-ojibjkjikcpjonjjngfkegflhmffeemk-Default"; - terminal = false; - }; - } - ) - ( - lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) { - home.persistence."/persist${config.home.homeDirectory}" = { - directories = [ - "${config.xdg.configHome}/chromium" - ]; - allowOther = true; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/proton-mail-pwa.nix b/modules/home-manager/programs/proton-mail-pwa.nix deleted file mode 100644 index d599263..0000000 --- a/modules/home-manager/programs/proton-mail-pwa.nix +++ /dev/null @@ -1,57 +0,0 @@ -{...}: { - flake.homeModules.home-manager-proton-mail-pwa = { - lib, - pkgs, - config, - ... - }: let - cfg = config.programs.proton-mail-pwa; - isChromium = cfg.package == pkgs.chromium; - isBrowserImpermanenceSupported = cfg.package == pkgs.chromium; - in { - options.programs.proton-mail-pwa = { - enable = lib.mkEnableOption "enable Proton Mail PWA"; - package = lib.mkOption { - type = lib.types.package; - default = pkgs.chromium; - description = "Browser package to use for the PWA"; - }; - impermanence = { - enable = lib.mkOption { - type = lib.types.bool; - default = isBrowserImpermanenceSupported; - description = "Enable impermanence configuration for the PWA. Only automatically enabled when using chromium."; - }; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - warnings = - lib.optional (config.impermanence.enable && !isBrowserImpermanenceSupported) - "proton-mail-pwa: Using unsupported package. You will need to manually configure pwa for ${cfg.package.pname}. Supported package(s) ${pkgs.chromium.pname}"; - } - ( - lib.mkIf isChromium { - xdg.desktopEntries.proton-mail-pwa = { - name = "Proton Mail"; - type = "Application"; - exec = "${cfg.package}/bin/${cfg.package.pname} --app=https://mail.proton.me"; - icon = "chrome-jnpecgipniidlgicjocehkhajgdnjekh-Default"; - terminal = false; - }; - } - ) - ( - lib.mkIf (config.impermanence.enable && cfg.impermanence.enable && isChromium) { - home.persistence."/persist${config.home.homeDirectory}" = { - directories = [ - "${config.xdg.configHome}/chromium" - ]; - allowOther = true; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/proton-pass.nix b/modules/home-manager/programs/proton-pass.nix deleted file mode 100644 index 69cfd24..0000000 --- a/modules/home-manager/programs/proton-pass.nix +++ /dev/null @@ -1,31 +0,0 @@ -{...}: { - flake.homeModules.home-manager-proton-pass = { - lib, - pkgs, - config, - ... - }: let - cfg = config.programs.proton-pass; - in { - options.programs.proton-pass = { - enable = lib.mkEnableOption "enable Proton Pass"; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - proton-pass - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/Proton Pass" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/protonvpn.nix b/modules/home-manager/programs/protonvpn.nix deleted file mode 100644 index 3985165..0000000 --- a/modules/home-manager/programs/protonvpn.nix +++ /dev/null @@ -1,55 +0,0 @@ -{...}: { - flake.homeModules.home-manager-protonvpn = { - lib, - pkgs, - config, - ... - }: let - cfg = config.programs.protonvpn-gui; - package = - if builtins.compareVersions pkgs.protonvpn-gui.version cfg.minVersion < 0 - then - pkgs.protonvpn-gui.overrideAttrs (old: { - version = cfg.minVersion; - src = pkgs.fetchFromGitHub { - owner = "ProtonVPN"; - repo = "proton-vpn-gtk-app"; - tag = "v${cfg.minVersion}"; - hash = cfg.minVersionHash; - }; - }) - else pkgs.protonvpn-gui; - in { - options.programs.protonvpn-gui = { - enable = lib.mkEnableOption "enable protonvpn"; - minVersion = lib.mkOption { - type = lib.types.str; - default = "4.15.1"; - description = "Minimum version of protonvpn-gui to use. If nixpkgs has a lower version, it will be overridden."; - }; - minVersionHash = lib.mkOption { - type = lib.types.str; - default = "sha256-mWQW/KR2zQxSMkcu5k79H3TNATmFB6J2vgFhgXNpM2s="; - description = "Source hash for the minimum version override."; - }; - }; - - config = lib.mkIf cfg.enable (lib.mkMerge [ - { - home.packages = [ - package - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/protonvpn" - "${config.xdg.configHome}/Proton" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/proxmark3.nix b/modules/home-manager/programs/proxmark3.nix deleted file mode 100644 index 1305a26..0000000 --- a/modules/home-manager/programs/proxmark3.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.homeModules.home-manager-proxmark3 = { - lib, - pkgs, - config, - ... - }: { - options.programs.proxmark3 = { - enable = lib.mkEnableOption "enable proxmark3"; - }; - - config = lib.mkIf config.programs.proxmark3.enable { - home.packages = with pkgs; [ - proxmark3 - ]; - }; - }; -} diff --git a/modules/home-manager/programs/qbittorrent.nix b/modules/home-manager/programs/qbittorrent.nix deleted file mode 100644 index 143a344..0000000 --- a/modules/home-manager/programs/qbittorrent.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-qbittorrent = { - lib, - pkgs, - config, - ... - }: { - options.programs.qbittorrent = { - enable = lib.mkEnableOption "enable qbittorrent"; - }; - - config = lib.mkIf config.programs.qbittorrent.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - qbittorrent - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/qBittorrent" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/qflipper.nix b/modules/home-manager/programs/qflipper.nix deleted file mode 100644 index 5e93b60..0000000 --- a/modules/home-manager/programs/qflipper.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-qflipper = { - lib, - pkgs, - config, - ... - }: { - options.programs.qflipper = { - enable = lib.mkEnableOption "enable qflipper"; - }; - - config = lib.mkIf config.programs.qflipper.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - qFlipper - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/qFlipper" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/signal.nix b/modules/home-manager/programs/signal.nix deleted file mode 100644 index de24fe0..0000000 --- a/modules/home-manager/programs/signal.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-signal = { - lib, - pkgs, - config, - ... - }: { - options.programs.signal-desktop = { - enable = lib.mkEnableOption "enable signal"; - }; - - config = lib.mkIf config.programs.signal-desktop.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - signal-desktop - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/Signal" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/steam.nix b/modules/home-manager/programs/steam.nix deleted file mode 100644 index 07c1d05..0000000 --- a/modules/home-manager/programs/steam.nix +++ /dev/null @@ -1,37 +0,0 @@ -{...}: { - flake.homeModules.home-manager-steam = { - lib, - pkgs, - config, - ... - }: { - options.programs.steam = { - enable = lib.mkEnableOption "enable steam"; - }; - - config = lib.mkIf config.programs.steam.enable ( - lib.mkMerge [ - { - home.packages = with pkgs; [ - steam - steam.run - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - { - directory = "${config.xdg.dataHome}/Steam"; - method = "symlink"; - } - ]; - }; - } - ) - ] - ); - - # TODO: bind impermanence config - }; -} diff --git a/modules/home-manager/programs/tor-browser.nix b/modules/home-manager/programs/tor-browser.nix deleted file mode 100644 index d17a904..0000000 --- a/modules/home-manager/programs/tor-browser.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-tor-browser = { - lib, - pkgs, - config, - ... - }: { - options.programs.tor-browser = { - enable = lib.mkEnableOption "enable tor-browser"; - }; - - config = lib.mkIf config.programs.tor-browser.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - tor-browser - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.dataHome}/torbrowser" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/ungoogled-chromium.nix b/modules/home-manager/programs/ungoogled-chromium.nix deleted file mode 100644 index 1481353..0000000 --- a/modules/home-manager/programs/ungoogled-chromium.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-ungoogled-chromium = { - lib, - pkgs, - config, - ... - }: { - options.programs.ungoogled-chromium = { - enable = lib.mkEnableOption "enable ungoogled-chromium"; - }; - - config = lib.mkIf config.programs.ungoogled-chromium.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - ungoogled-chromium - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/chromium" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/via.nix b/modules/home-manager/programs/via.nix deleted file mode 100644 index b41bd11..0000000 --- a/modules/home-manager/programs/via.nix +++ /dev/null @@ -1,30 +0,0 @@ -{...}: { - flake.homeModules.home-manager-via = { - lib, - pkgs, - config, - ... - }: { - options.programs.via = { - enable = lib.mkEnableOption "enable via"; - }; - - config = lib.mkIf config.programs.via.enable (lib.mkMerge [ - { - home.packages = with pkgs; [ - via - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "${config.xdg.configHome}/via" - "${config.xdg.dataHome}/via" - ]; - }; - } - ) - ]); - }; -} diff --git a/modules/home-manager/programs/vmware-workstation.nix b/modules/home-manager/programs/vmware-workstation.nix deleted file mode 100644 index e208240..0000000 --- a/modules/home-manager/programs/vmware-workstation.nix +++ /dev/null @@ -1,38 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vmware-workstation = { - lib, - pkgs, - config, - ... - }: { - options.programs.vmware-workstation = { - enable = lib.mkEnableOption "enable VMware Workstation"; - }; - - config = lib.mkIf config.programs.vmware-workstation.enable ( - lib.mkMerge [ - { - home.packages = with pkgs; [ - vmware-workstation - ]; - } - ( - lib.mkIf config.impermanence.enable { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - { - directory = ".vmware"; - method = "symlink"; - } - { - directory = "vmware"; - method = "symlink"; - } - ]; - }; - } - ) - ] - ); - }; -} diff --git a/modules/home-manager/programs/vortex.nix b/modules/home-manager/programs/vortex.nix deleted file mode 100644 index 3de59f9..0000000 --- a/modules/home-manager/programs/vortex.nix +++ /dev/null @@ -1,26 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vortex = { - config, - lib, - ... - }: let - cfg = config.programs.vortex; - in { - options.programs.vortex = { - enable = lib.mkEnableOption "Vortex (Nexus Mods manager)"; - }; - - config = { - assertions = [ - { - assertion = !cfg.enable; - message = '' - Vortex module is not yet fully configured. - Please download and install Vortex manually from the Nexus Mods website, - then configure the Wine environment and dependencies as needed. - ''; - } - ]; - }; - }; -} diff --git a/modules/home-manager/programs/vscode/alejandra.nix b/modules/home-manager/programs/vscode/alejandra.nix deleted file mode 100644 index 8871771..0000000 --- a/modules/home-manager/programs/vscode/alejandra.nix +++ /dev/null @@ -1,36 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-alejandra = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.alejandra = { - enable = lib.mkEnableOption "Enable Alejandra extension for Nix formatting"; - extension = lib.mkPackageOption pkgsRepository "alejandra" { - default = ["kamadorueda" "alejandra"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.alejandra.enable { - extensions = [config.extraExtensions.alejandra.extension]; - userSettings = { - "[nix]" = { - "editor.defaultFormatter" = "kamadorueda.alejandra"; - "editor.formatOnPaste" = true; - "editor.formatOnSave" = true; - "editor.formatOnType" = true; - }; - "alejandra.program" = "alejandra"; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/astroVscode.nix b/modules/home-manager/programs/vscode/astroVscode.nix deleted file mode 100644 index 7ac1dad..0000000 --- a/modules/home-manager/programs/vscode/astroVscode.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-astro-vscode = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.astroVscode = { - enable = lib.mkEnableOption "should the astro-vscode extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "astro-vscode" { - default = ["astro-build" "astro-vscode"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.astroVscode.enable { - extensions = [ - config.extraExtensions.astroVscode.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/atomKeybindings.nix b/modules/home-manager/programs/vscode/atomKeybindings.nix deleted file mode 100644 index 36edfdc..0000000 --- a/modules/home-manager/programs/vscode/atomKeybindings.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-atom-keybindings = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.atomKeybindings = { - enable = lib.mkEnableOption "should the atom keybindings extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "atom-keybindings" { - default = ["ms-vscode" "atom-keybindings"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.atomKeybindings.enable { - extensions = [ - config.extraExtensions.atomKeybindings.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/autoRenameTag.nix b/modules/home-manager/programs/vscode/autoRenameTag.nix deleted file mode 100644 index 91fa899..0000000 --- a/modules/home-manager/programs/vscode/autoRenameTag.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-auto-rename-tag = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.autoRenameTag = { - enable = lib.mkEnableOption "should the auto-rename-tag extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "auto-rename-tag" { - default = ["formulahendry" "auto-rename-tag"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.autoRenameTag.enable { - extensions = [ - config.extraExtensions.autoRenameTag.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/conventionalCommits.nix b/modules/home-manager/programs/vscode/conventionalCommits.nix deleted file mode 100644 index 916c84f..0000000 --- a/modules/home-manager/programs/vscode/conventionalCommits.nix +++ /dev/null @@ -1,42 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-conventional-commits = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.vscode-marketplace; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.conventionalCommits = { - enable = lib.mkEnableOption "Enable VSCode Conventional Commits extension"; - extension = lib.mkPackageOption pkgsRepository "conventional-commits" { - default = ["vivaxy" "vscode-conventional-commits"]; - }; - - gitmoji = lib.mkEnableOption "should emoji be prompted for as a part of the commit message./"; - - promptScopes = lib.mkEnableOption "prompting for scopes in conventional commits"; - - promptFooter = lib.mkEnableOption "prompting for footer in conventional commits"; - - showNewVersionNotes = lib.mkEnableOption "showing new version notes for conventional commits"; - }; - }; - config = lib.mkIf config.extraExtensions.conventionalCommits.enable { - extensions = [config.extraExtensions.conventionalCommits.extension]; - - userSettings = { - "conventionalCommits.gitmoji" = config.extraExtensions.conventionalCommits.gitmoji; - "conventionalCommits.promptScopes" = config.extraExtensions.conventionalCommits.promptScopes; - "conventionalCommits.promptFooter" = config.extraExtensions.conventionalCommits.promptFooter; - "conventionalCommits.showNewVersionNotes" = config.extraExtensions.conventionalCommits.showNewVersionNotes; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/default.nix b/modules/home-manager/programs/vscode/default.nix deleted file mode 100644 index 410f510..0000000 --- a/modules/home-manager/programs/vscode/default.nix +++ /dev/null @@ -1,33 +0,0 @@ -{config, ...}: let - mod = config.flake.homeModules; -in { - flake.homeModules.home-manager-vscode = { - imports = [ - mod.home-manager-vscode-alejandra - mod.home-manager-vscode-astro-vscode - mod.home-manager-vscode-atom-keybindings - mod.home-manager-vscode-auto-rename-tag - mod.home-manager-vscode-conventional-commits - mod.home-manager-vscode-direnv - mod.home-manager-vscode-es7-react-js-snippets - mod.home-manager-vscode-eslint - mod.home-manager-vscode-even-better-toml - mod.home-manager-vscode-go - mod.home-manager-vscode-graphql - mod.home-manager-vscode-jest - mod.home-manager-vscode-live-server - mod.home-manager-vscode-mdx - mod.home-manager-vscode-nearley - mod.home-manager-vscode-nix-ide - mod.home-manager-vscode-one-dark - mod.home-manager-vscode-open-dyslexic-font - mod.home-manager-vscode-open-remote-ssh - mod.home-manager-vscode-platform-io - mod.home-manager-vscode-rust-analyzer - mod.home-manager-vscode-standard - mod.home-manager-vscode-stylelint - mod.home-manager-vscode-tauri-vscode - mod.home-manager-vscode-vitest - ]; - }; -} diff --git a/modules/home-manager/programs/vscode/direnv.nix b/modules/home-manager/programs/vscode/direnv.nix deleted file mode 100644 index d8ef14d..0000000 --- a/modules/home-manager/programs/vscode/direnv.nix +++ /dev/null @@ -1,27 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-direnv = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.vscode-marketplace; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.direnv = { - enable = lib.mkEnableOption "Enable direnv extension"; - extension = lib.mkPackageOption pkgsRepository "direnv" { - default = ["mkhl" "direnv"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.direnv.enable { - extensions = [config.extraExtensions.direnv.extension]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/es7ReactJsSnippets.nix b/modules/home-manager/programs/vscode/es7ReactJsSnippets.nix deleted file mode 100644 index 86b8ba4..0000000 --- a/modules/home-manager/programs/vscode/es7ReactJsSnippets.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-es7-react-js-snippets = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.es7ReactJsSnippets = { - enable = lib.mkEnableOption "should the es7-react-js-snippets extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "es7-react-js-snippets" { - default = ["dsznajder" "es7-react-js-snippets"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.es7ReactJsSnippets.enable { - extensions = [ - config.extraExtensions.es7ReactJsSnippets.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/evenBetterToml.nix b/modules/home-manager/programs/vscode/evenBetterToml.nix deleted file mode 100644 index fa4bc1f..0000000 --- a/modules/home-manager/programs/vscode/evenBetterToml.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-even-better-toml = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.evenBetterToml = { - enable = lib.mkEnableOption "should the even-better-toml extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "even-better-toml" { - default = ["tamasfe" "even-better-toml"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.evenBetterToml.enable { - extensions = [ - config.extraExtensions.evenBetterToml.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/go.nix b/modules/home-manager/programs/vscode/go.nix deleted file mode 100644 index d1a0dc3..0000000 --- a/modules/home-manager/programs/vscode/go.nix +++ /dev/null @@ -1,36 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-go = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.go = { - enable = lib.mkEnableOption "should the go extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "go" { - default = ["golang" "go"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.go.enable { - extensions = [ - config.extraExtensions.go.extension - ]; - userSettings = { - "go.alternateTools" = { - "gopls" = "gopls"; - }; - "go.toolsManagement.autoUpdate" = false; - "go.useLanguageServer" = true; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/graphql.nix b/modules/home-manager/programs/vscode/graphql.nix deleted file mode 100644 index bdb9a70..0000000 --- a/modules/home-manager/programs/vscode/graphql.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-graphql = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.graphql = { - enable = lib.mkEnableOption "should the graphql highlighting extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-graphql" { - default = ["graphql" "vscode-graphql-syntax"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.graphql.enable { - extensions = [ - config.extraExtensions.graphql.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/liveServer.nix b/modules/home-manager/programs/vscode/liveServer.nix deleted file mode 100644 index c9a3d32..0000000 --- a/modules/home-manager/programs/vscode/liveServer.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-live-server = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.liveServer = { - enable = lib.mkEnableOption "should the live-server extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "live-server" { - default = ["ms-vscode" "live-server"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.liveServer.enable { - extensions = [ - config.extraExtensions.liveServer.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/nearley.nix b/modules/home-manager/programs/vscode/nearley.nix deleted file mode 100644 index f4afa10..0000000 --- a/modules/home-manager/programs/vscode/nearley.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-nearley = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.vscode-marketplace; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.nearley = { - enable = lib.mkEnableOption "should the nearley extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "nearley" { - default = ["karyfoundation" "nearley"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.nearley.enable { - extensions = [ - config.extraExtensions.nearley.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/nixIde.nix b/modules/home-manager/programs/vscode/nixIde.nix deleted file mode 100644 index 0880e49..0000000 --- a/modules/home-manager/programs/vscode/nixIde.nix +++ /dev/null @@ -1,31 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-nix-ide = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.nixIde = { - enable = lib.mkEnableOption "Enable Nix IDE extension"; - extension = lib.mkPackageOption pkgsRepository "nix-ide" { - default = ["jnoortheen" "nix-ide"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.nixIde.enable { - extensions = [config.extraExtensions.nixIde.extension]; - userSettings = { - "nix.enableLanguageServer" = true; - "nix.serverPath" = "nil"; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/oneDark.nix b/modules/home-manager/programs/vscode/oneDark.nix deleted file mode 100644 index 8f7864b..0000000 --- a/modules/home-manager/programs/vscode/oneDark.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-one-dark = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.oneDark = { - enable = lib.mkEnableOption "should the one dark theme for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "onedark" { - default = ["akamud" "vscode-theme-onedark"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.oneDark.enable { - extensions = [ - config.extraExtensions.oneDark.extension - ]; - userSettings = { - "workbench.colorTheme" = "Atom One Dark"; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/openDyslexicFont.nix b/modules/home-manager/programs/vscode/openDyslexicFont.nix deleted file mode 100644 index e9a128a..0000000 --- a/modules/home-manager/programs/vscode/openDyslexicFont.nix +++ /dev/null @@ -1,51 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-open-dyslexic-font = { - lib, - pkgs, - config, - ... - }: { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.openDyslexicFont = { - enable = lib.mkEnableOption "should OpenDyslexic font be set as the default font for VSCode"; - package = lib.mkPackageOption pkgs "nerd-fonts.open-dyslexic" { - default = ["nerd-fonts" "open-dyslexic"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.openDyslexicFont.enable { - userSettings = { - "editor.fontFamily" = "'OpenDyslexicM Nerd Font Mono', Droid Sans Mono, monospace"; - "editor.fontSize" = 14; - "editor.letterSpacing" = -0.3; - }; - }; - })); - }; - - config = let - enabledProfiles = - lib.filter (profile: profile.extraExtensions.openDyslexicFont.enable or false) - (lib.attrValues config.programs.vscode.profiles); - - anyProfileUsesOpenDyslexicFont = enabledProfiles != []; - - fontPackages = lib.unique (map (profile: profile.extraExtensions.openDyslexicFont.package) enabledProfiles); - in { - # Ensure OpenDyslexic font packages are installed when any VSCode profile uses them - home.packages = fontPackages; - - fonts.fontconfig.enable = lib.mkIf anyProfileUsesOpenDyslexicFont true; - - # Add assertion to ensure the fonts are available - assertions = - map (fontPkg: { - assertion = lib.elem fontPkg config.home.packages; - message = "OpenDyslexic font package '${fontPkg.name or "unknown"}' must be installed when using openDyslexicFont extension for VSCode."; - }) - fontPackages; - }; - }; -} diff --git a/modules/home-manager/programs/vscode/openRemoteSsh.nix b/modules/home-manager/programs/vscode/openRemoteSsh.nix deleted file mode 100644 index 343e4e0..0000000 --- a/modules/home-manager/programs/vscode/openRemoteSsh.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-open-remote-ssh = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.openRemoteSsh = { - enable = lib.mkEnableOption "should the open-remote-ssh extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "open-remote-ssh" { - default = ["jeanp413" "open-remote-ssh"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.openRemoteSsh.enable { - extensions = [ - config.extraExtensions.openRemoteSsh.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/platformIO.nix b/modules/home-manager/programs/vscode/platformIO.nix deleted file mode 100644 index 29decda..0000000 --- a/modules/home-manager/programs/vscode/platformIO.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-platform-io = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.platformIO = { - enable = lib.mkEnableOption "should the platformIO extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "platformIO" { - default = ["pioarduino" "pioarduino-ide"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.platformIO.enable { - extensions = [ - config.extraExtensions.platformIO.extension - ]; - userSettings = { - "platformio-ide.useBuiltinPIOCore" = false; - }; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/rustAnalyzer.nix b/modules/home-manager/programs/vscode/rustAnalyzer.nix deleted file mode 100644 index 8c122ec..0000000 --- a/modules/home-manager/programs/vscode/rustAnalyzer.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-rust-analyzer = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.rustAnalyzer = { - enable = lib.mkEnableOption "should the rust-analyzer extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "rust-analyzer" { - default = ["rust-lang" "rust-analyzer"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.rustAnalyzer.enable { - extensions = [ - config.extraExtensions.rustAnalyzer.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/tauriVscode.nix b/modules/home-manager/programs/vscode/tauriVscode.nix deleted file mode 100644 index c5d76a7..0000000 --- a/modules/home-manager/programs/vscode/tauriVscode.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-tauri-vscode = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.tauriVscode = { - enable = lib.mkEnableOption "should the tauri-vscode extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "tauri-vscode" { - default = ["tauri-apps" "tauri-vscode"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.tauriVscode.enable { - extensions = [ - config.extraExtensions.tauriVscode.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vitest.nix b/modules/home-manager/programs/vscode/vitest.nix deleted file mode 100644 index 845a036..0000000 --- a/modules/home-manager/programs/vscode/vitest.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-vitest = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vitest = { - enable = lib.mkEnableOption "should the vitest extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vitest" { - default = ["vitest" "explorer"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vitest.enable { - extensions = [ - config.extraExtensions.vitest.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vscodeEslint.nix b/modules/home-manager/programs/vscode/vscodeEslint.nix deleted file mode 100644 index faf34f9..0000000 --- a/modules/home-manager/programs/vscode/vscodeEslint.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-eslint = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vscodeEslint = { - enable = lib.mkEnableOption "should the vscode-eslint extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-eslint" { - default = ["dbaeumer" "vscode-eslint"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vscodeEslint.enable { - extensions = [ - config.extraExtensions.vscodeEslint.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vscodeJest.nix b/modules/home-manager/programs/vscode/vscodeJest.nix deleted file mode 100644 index 981c5d8..0000000 --- a/modules/home-manager/programs/vscode/vscodeJest.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-jest = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vscodeJest = { - enable = lib.mkEnableOption "should the vscode-jest extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-jest" { - default = ["orta" "vscode-jest"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vscodeJest.enable { - extensions = [ - config.extraExtensions.vscodeJest.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vscodeMdx.nix b/modules/home-manager/programs/vscode/vscodeMdx.nix deleted file mode 100644 index 6ece0db..0000000 --- a/modules/home-manager/programs/vscode/vscodeMdx.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-mdx = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vscodeMdx = { - enable = lib.mkEnableOption "should the vscode-mdx extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-mdx" { - default = ["unifiedjs" "vscode-mdx"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vscodeMdx.enable { - extensions = [ - config.extraExtensions.vscodeMdx.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vscodeStandard.nix b/modules/home-manager/programs/vscode/vscodeStandard.nix deleted file mode 100644 index 3cd7a5f..0000000 --- a/modules/home-manager/programs/vscode/vscodeStandard.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-standard = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vscodeStandard = { - enable = lib.mkEnableOption "should the vscode-standard extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-standard" { - default = ["standard" "vscode-standard"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vscodeStandard.enable { - extensions = [ - config.extraExtensions.vscodeStandard.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/programs/vscode/vscodeStylelint.nix b/modules/home-manager/programs/vscode/vscodeStylelint.nix deleted file mode 100644 index 8e224cd..0000000 --- a/modules/home-manager/programs/vscode/vscodeStylelint.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.homeModules.home-manager-vscode-stylelint = { - lib, - pkgs, - config, - ... - }: let - pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - pkgsRepository = pkgsRepositories.open-vsx; - in { - options.programs.vscode.profiles = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { - options = { - extraExtensions.vscodeStylelint = { - enable = lib.mkEnableOption "should the vscode-stylelint extension for vscode be enabled"; - extension = lib.mkPackageOption pkgsRepository "vscode-stylelint" { - default = ["stylelint" "vscode-stylelint"]; - }; - }; - }; - config = lib.mkIf config.extraExtensions.vscodeStylelint.enable { - extensions = [ - config.extraExtensions.vscodeStylelint.extension - ]; - }; - })); - }; - }; -} diff --git a/modules/home-manager/sops.nix b/modules/home-manager/sops.nix deleted file mode 100644 index 1336c0c..0000000 --- a/modules/home-manager/sops.nix +++ /dev/null @@ -1,9 +0,0 @@ -{...}: { - flake.homeModules.home-manager-sops = {...}: { - config = { - sops = { - age.keyFile = "/var/lib/sops-nix/key.txt"; - }; - }; - }; -} diff --git a/modules/home-manager/user.nix b/modules/home-manager/user.nix deleted file mode 100644 index 15c4d77..0000000 --- a/modules/home-manager/user.nix +++ /dev/null @@ -1,19 +0,0 @@ -{...}: { - flake.homeModules.home-manager-user = { - lib, - config, - osConfig, - ... - }: { - options.user = { - isDesktopUser = lib.mkOption { - type = lib.types.bool; - default = osConfig.host.users.${config.home.username}.isDesktopUser; - }; - isTerminalUser = lib.mkOption { - type = lib.types.bool; - default = osConfig.host.users.${config.home.username}.isTerminalUser; - }; - }; - }; -} diff --git a/modules/hosts/darwin/hesperium/configuration.nix b/modules/hosts/darwin/hesperium/configuration.nix deleted file mode 100644 index 306596c..0000000 --- a/modules/hosts/darwin/hesperium/configuration.nix +++ /dev/null @@ -1,18 +0,0 @@ -{...}: { - flake.darwinModules.hesperiumConfiguration = {...}: { - host = { - users = { - leyla = { - isDesktopUser = true; - isTerminalUser = true; - isPrincipleUser = true; - }; - eve.isNormalUser = false; - }; - }; - - system.stateVersion = 5; - - nixpkgs.hostPlatform = "aarch64-darwin"; - }; -} diff --git a/modules/hosts/darwin/hesperium/default.nix b/modules/hosts/darwin/hesperium/default.nix deleted file mode 100644 index 8dcde58..0000000 --- a/modules/hosts/darwin/hesperium/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -# leyla macbook -{ - inputs, - config, - ... -}: { - flake.darwinConfigurations.hesperium = inputs.nix-darwin.lib.darwinSystem { - system = "aarch64-darwin"; - modules = [ - config.flake.darwinModules.darwinModules - config.flake.darwinModules.hesperiumConfiguration - ]; - specialArgs = {inherit inputs;}; - }; -} diff --git a/modules/hosts/home/eve/configuration.nix b/modules/hosts/home/eve/configuration.nix deleted file mode 100644 index 8ec061e..0000000 --- a/modules/hosts/home/eve/configuration.nix +++ /dev/null @@ -1,53 +0,0 @@ -{...}: { - flake.homeModules.eveBaseConfiguration = {osConfig, ...}: let - userConfig = osConfig.host.users.eve; - in { - home = { - username = userConfig.name; - homeDirectory = osConfig.users.users.eve.home; - - # This value determines the Home Manager release that your configuration is - # compatible with. This helps avoid breakage when a new Home Manager release - # introduces backwards incompatible changes. - # - # You should not change this value, even if you update Home Manager. If you do - # want to update the value, then make sure to first check the Home Manager - # release notes. - stateVersion = "23.11"; # Please read the comment before changing. - - # Home Manager is pretty good at managing dotfiles. The primary way to manage - # plain files is through 'home.file'. - file = { - # # Building this configuration will create a copy of 'dotfiles/screenrc' in - # # the Nix store. Activating the configuration will then make '~/.screenrc' a - # # symlink to the Nix store copy. - # ".screenrc".source = dotfiles/screenrc; - - # # You can also set the file content immediately. - # ".gradle/gradle.properties".text = '' - # org.gradle.console=verbose - # org.gradle.daemon.idletimeout=3600000 - # ''; - }; - - # Home Manager can also manage your environment variables through - # 'home.sessionVariables'. If you don't want to manage your shell through Home - # Manager then you have to manually source 'home-manager-session-vars.sh' located at - # either - # - # ~/.nix-profile/etc/profile.d/home-manager-session-vars.sh - # - # or - # - # ~/.local/state/nix/profiles/profile/etc/profile.d/home-manager-session-vars.sh - # - # or - # - # /etc/profiles/per-user/leyla/etc/profile.d/home-manager-session-vars.sh - # - sessionVariables = { - # EDITOR = "emacs"; - }; - }; - }; -} diff --git a/modules/hosts/home/eve/default.nix b/modules/hosts/home/eve/default.nix deleted file mode 100644 index 0aa5a54..0000000 --- a/modules/hosts/home/eve/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{config, ...}: let - hm = config.flake.homeModules; -in { - flake.homeModules.eveConfiguration = {...}: { - imports = [ - hm.eveBaseConfiguration - hm.eveGnomeconf - hm.evePackages - ]; - }; -} diff --git a/modules/hosts/home/eve/gnomeconf.nix b/modules/hosts/home/eve/gnomeconf.nix deleted file mode 100644 index 7bb0fe1..0000000 --- a/modules/hosts/home/eve/gnomeconf.nix +++ /dev/null @@ -1,41 +0,0 @@ -{...}: { - flake.homeModules.eveGnomeconf = { - osConfig, - lib, - ... - }: { - config = { - gnome = lib.mkMerge [ - { - colorScheme = "prefer-dark"; - accentColor = "slate"; - clockFormat = "24h"; - nightLight = { - enable = true; - automatic = false; - fromTime = 12.0; - toTime = 11.999999999999; - temperature = 2700; - }; - extraWindowControls = true; - extensions = { - dash-to-panel = { - enable = true; - }; - }; - } - - (lib.mkIf (osConfig.networking.hostName == "horizon") { - displayScaling = 125; - experimentalFeatures = { - scaleMonitorFramebuffer = true; - }; - }) - ]; - - dconf = { - enable = true; - }; - }; - }; -} diff --git a/modules/hosts/home/eve/packages.nix b/modules/hosts/home/eve/packages.nix deleted file mode 100644 index 3a70209..0000000 --- a/modules/hosts/home/eve/packages.nix +++ /dev/null @@ -1,102 +0,0 @@ -{...}: { - flake.homeModules.evePackages = { - lib, - pkgs, - config, - osConfig, - ... - }: let - userConfig = osConfig.host.users.eve; - hardware = osConfig.host.hardware; - in { - config = { - nixpkgs.config = { - allowUnfree = true; - }; - - # Packages that can be installed without any extra configuration - # See https://search.nixos.org/packages for all options - home.packages = lib.lists.optionals userConfig.isDesktopUser ( - with pkgs; [ - gnomeExtensions.dash-to-panel - friture - ] - ); - - # Packages that need to be installed with some extra configuration - # See https://home-manager-options.extranix.com/ for all options - programs = lib.mkMerge [ - { - # Let Home Manager install and manage itself. - home-manager.enable = true; - } - (lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) { - git = { - enable = true; - signing.format = "openpgp"; - settings = { - user.name = "Eve"; - user.email = "evesnrobins@gmail.com"; - init.defaultBranch = "main"; - }; - }; - - openssh = { - enable = true; - hostKeys = [ - { - type = "ed25519"; - path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; - } - ]; - }; - }) - (lib.mkIf config.user.isDesktopUser { - vscode = { - enable = true; - package = pkgs.vscodium; - }; - - firefox.enable = true; - bitwarden.enable = true; - proton-pass.enable = true; - element-desktop.enable = true; - discord.enable = true; - makemkv.enable = true; - signal-desktop.enable = true; - steam.enable = true; - piper.enable = hardware.piperMouse.enable; - krita.enable = true; - ungoogled-chromium.enable = true; - - inkscape.enable = true; - obsidian.enable = true; - obs-studio.enable = true; - kdenlive.enable = true; - tor-browser.enable = true; - olympus.enable = true; - libreoffice.enable = true; - noita-entangled-worlds.enable = true; - - claude-code.enable = true; - opencode.enable = true; - # TODO: enable defiant provider once emergent is on the same tailnet - # opencode.settings.provider.defiant = { - # npm = "@ai-sdk/openai-compatible"; - # name = "Ollama (defiant)"; - # options.baseURL = "http://defiant:11434/v1"; - # models."gpt-oss:120b".name = "GPT-OSS 120B"; - # }; - - e621-downloader.enable = true; - - # Windows applications that we need to figure out how to install - guild-wars-2.enable = false; - vortex.enable = false; - dungeon-draft.enable = false; - vmware-workstation.enable = true; - }) - ]; - }; - }; -} diff --git a/modules/hosts/home/leyla/configuration.nix b/modules/hosts/home/leyla/configuration.nix deleted file mode 100644 index a7f7a05..0000000 --- a/modules/hosts/home/leyla/configuration.nix +++ /dev/null @@ -1,90 +0,0 @@ -{...}: { - flake.homeModules.leylaBaseConfiguration = { - pkgs, - config, - osConfig, - ... - }: { - config = { - impermanence.enable = osConfig.storage.impermanence.enable; - - # Home Manager needs a bit of information about you and the paths it should - # manage. - home = { - username = osConfig.host.users.leyla.name; - homeDirectory = osConfig.users.users.leyla.home; - - # This value determines the Home Manager release that your configuration is - # compatible with. This helps avoid breakage when a new Home Manager release - # introduces backwards incompatible changes. - # - # You should not change this value, even if you update Home Manager. If you do - # want to update the value, then make sure to first check the Home Manager - # release notes. - stateVersion = "23.11"; # Please read the comment before changing. - - # Home Manager is pretty good at managing dotfiles. The primary way to manage - # plain files is through 'home.file'. - file = { - # # Building this configuration will create a copy of 'dotfiles/screenrc' in - # # the Nix store. Activating the configuration will then make '~/.screenrc' a - # # symlink to the Nix store copy. - # ".screenrc".source = dotfiles/screenrc; - - # # You can also set the file content immediately. - # ".gradle/gradle.properties".text = '' - # org.gradle.console=verbose - # org.gradle.daemon.idletimeout=3600000 - # ''; - "${config.xdg.configHome}/user-dirs.dirs" = { - force = true; - text = '' - # This file is written by xdg-user-dirs-update - # If you want to change or add directories, just edit the line you're - # interested in. All local changes will be retained on the next run. - # Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped - # homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an - # absolute path. No other format is supported. - # - XDG_DESKTOP_DIR="$HOME/desktop" - XDG_DOWNLOAD_DIR="$HOME/downloads" - XDG_DOCUMENTS_DIR="$HOME/documents" - XDG_TEMPLATES_DIR="$HOME/documents/templates" - XDG_MUSIC_DIR="$HOME/documents/music" - XDG_PICTURES_DIR="$HOME/documents/photos" - XDG_VIDEOS_DIR="$HOME/documents/videos" - XDG_PUBLICSHARE_DIR="$HOME/documents/public" - ''; - }; - }; - - keyboard.layout = "us,it,de"; - - # Home Manager can also manage your environment variables through - # 'home.sessionVariables'. If you don't want to manage your shell through Home - # Manager then you have to manually source 'home-manager-session-vars.sh' located at - # either - # - # ~/.nix-profile/etc/profile.d/home-manager-session-vars.sh - # - # or - # - # ~/.local/state/nix/profiles/profile/etc/profile.d/home-manager-session-vars.sh - # - # or - # - # /etc/profiles/per-user/leyla/etc/profile.d/home-manager-session-vars.sh - # - sessionVariables = { - # EDITOR = "emacs"; - }; - }; - - # TODO: move this into a fonts module - home.packages = with pkgs; [ - aileron - ]; - fonts.fontconfig.enable = true; - }; - }; -} diff --git a/modules/hosts/home/leyla/dconf.nix b/modules/hosts/home/leyla/dconf.nix deleted file mode 100644 index 3c61452..0000000 --- a/modules/hosts/home/leyla/dconf.nix +++ /dev/null @@ -1,49 +0,0 @@ -{...}: { - flake.homeModules.leylaDconf = {...}: { - config = { - gnome = { - extraWindowControls = true; - colorScheme = "prefer-dark"; - clockFormat = "24h"; - nightLight = { - enable = true; - automatic = false; - fromTime = 12.0; - toTime = 11.999999999999; - temperature = 2700; - }; - extensions = { - dash-to-dock = { - enable = true; - options = { - "dock-position" = "LEFT"; - "intellihide-mode" = "ALL_WINDOWS"; - "show-trash" = false; - "require-pressure-to-show" = false; - "show-mounts" = false; - }; - }; - }; - hotkeys = { - "Open Terminal" = { - binding = "t"; - command = "kgx"; - }; - "Open Firefox" = { - binding = "f"; - command = "firefox"; - }; - }; - }; - - dconf = { - enable = true; - settings = { - "org/gnome/shell" = { - favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"]; - }; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/default.nix b/modules/hosts/home/leyla/default.nix deleted file mode 100644 index daea8fc..0000000 --- a/modules/hosts/home/leyla/default.nix +++ /dev/null @@ -1,23 +0,0 @@ -{config, ...}: let - hm = config.flake.homeModules; -in { - flake.homeModules.leylaConfiguration = {...}: { - imports = [ - hm.leylaBaseConfiguration - hm.leylaDconf - hm.leylaI18n - hm.leylaImpermanence - hm.leylaPackages - hm.leylaDirenv - hm.leylaGit - hm.leylaMakemkv - hm.leylaOpenssh - hm.leylaFirefox - hm.leylaFirefoxProfile - hm.leylaFirefoxBookmarks - hm.leylaFirefoxHarden - hm.leylaVscode - hm.leylaVscodeUserWords - ]; - }; -} diff --git a/modules/hosts/home/leyla/i18n.nix b/modules/hosts/home/leyla/i18n.nix deleted file mode 100644 index 3709756..0000000 --- a/modules/hosts/home/leyla/i18n.nix +++ /dev/null @@ -1,14 +0,0 @@ -{...}: { - flake.homeModules.leylaI18n = {...}: { - i18n = { - defaultLocale = "en_IE.UTF-8"; - - extraLocaleSettings = { - # LC_ADDRESS = "en_IE.UTF-8"; # lets just get used to this one now - # LC_TELEPHONE = "en_IE.UTF-8"; # lets just get used to this one now - LC_MONETARY = "en_US.UTF-8"; # to be changed once I move - LC_PAPER = "en_US.UTF-8"; # convenient for american printers until I move - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/impermanence.nix b/modules/hosts/home/leyla/impermanence.nix deleted file mode 100644 index c21c13f..0000000 --- a/modules/hosts/home/leyla/impermanence.nix +++ /dev/null @@ -1,21 +0,0 @@ -{...}: { - flake.homeModules.leylaImpermanence = { - lib, - config, - ... - }: { - config = lib.mkIf (config.impermanence.enable) { - home.persistence."${config.impermanence.persistencePath}" = { - directories = [ - "desktop" - "downloads" - "documents" - ]; - files = [ - ".bash_history" # keep shell history around - "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files - ]; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/default.nix b/modules/hosts/home/leyla/packages/default.nix deleted file mode 100644 index b3d3512..0000000 --- a/modules/hosts/home/leyla/packages/default.nix +++ /dev/null @@ -1,100 +0,0 @@ -{...}: { - flake.homeModules.leylaPackages = { - lib, - pkgs, - config, - osConfig, - ... - }: let - hardware = osConfig.host.hardware; - in { - config = lib.mkMerge [ - { - programs = lib.mkMerge [ - { - # Let Home Manager install and manage itself. - home-manager.enable = true; - } - (lib.mkIf (config.user.isTerminalUser || config.user.isDesktopUser) { - bash.enable = true; - git.enable = true; - openssh.enable = true; - }) - (lib.mkIf config.user.isDesktopUser { - bitwarden.enable = true; - obs-studio.enable = hardware.graphicsAcceleration.enable; - qbittorrent.enable = true; - prostudiomasters.enable = true; - dbeaver-bin.enable = true; - bruno.enable = true; - piper.enable = hardware.piperMouse.enable; - proxmark3.enable = true; - openrgb.enable = hardware.openRGB.enable; - via.enable = hardware.viaKeyboard.enable; - claude-code.enable = true; - opencode = { - enable = true; - settings.provider.defiant = { - npm = "@ai-sdk/openai-compatible"; - name = "Ollama (defiant)"; - options.baseURL = "http://defiant:11434/v1"; - models."gpt-oss:120b".name = "GPT-OSS 120B"; - }; - }; - davinci-resolve.enable = hardware.graphicsAcceleration.enable; - mfoc.enable = true; - }) - (lib.mkIf (hardware.directAccess.enable && config.user.isDesktopUser) { - anki.enable = true; - android-studio.enable = true; - makemkv.enable = true; - discord.enable = true; - signal-desktop.enable = true; - calibre.enable = true; - obsidian.enable = true; - jetbrains.idea-oss.enable = true; - vscode.enable = true; - firefox.enable = true; - steam.enable = true; - krita.enable = true; - ungoogled-chromium.enable = true; - libreoffice.enable = true; - mapillary-uploader.enable = true; - inkscape.enable = true; - gimp.enable = true; - freecad.enable = true; - onionshare.enable = true; - pdfarranger.enable = true; - picard.enable = true; - qflipper.enable = true; - openvpn.enable = true; - noisetorch.enable = true; - noita-entangled-worlds.enable = true; - tor-browser.enable = true; - gdx-liftoff.enable = true; - proton-pass.enable = true; - protonvpn-gui.enable = true; - proton-mail-pwa.enable = true; - proton-calendar-pwa.enable = true; - element-desktop.enable = true; - kicad.enable = true; - }) - ]; - } - (lib.mkIf config.user.isTerminalUser { - home.packages = with pkgs; [ - # command line tools - sox - yt-dlp - ffmpeg - imagemagick - ]; - }) - (lib.mkIf config.user.isDesktopUser { - nixpkgs.config = { - allowUnfree = true; - }; - }) - ]; - }; -} diff --git a/modules/hosts/home/leyla/packages/direnv.nix b/modules/hosts/home/leyla/packages/direnv.nix deleted file mode 100644 index 18033e7..0000000 --- a/modules/hosts/home/leyla/packages/direnv.nix +++ /dev/null @@ -1,24 +0,0 @@ -{...}: { - flake.homeModules.leylaDirenv = { - lib, - config, - osConfig, - ... - }: let - userConfig = osConfig.host.users.leyla; - in { - config = lib.mkIf userConfig.isDesktopUser { - programs = { - direnv = { - enable = true; - enableBashIntegration = true; - nix-direnv.enable = true; - config = { - global.hide_env_diff = true; - whitelist.exact = ["${config.home.homeDirectory}/documents/code/nix-config"]; - }; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/firefox/bookmarks.nix b/modules/hosts/home/leyla/packages/firefox/bookmarks.nix deleted file mode 100644 index 21da87f..0000000 --- a/modules/hosts/home/leyla/packages/firefox/bookmarks.nix +++ /dev/null @@ -1,163 +0,0 @@ -{...}: { - flake.homeModules.leylaFirefoxBookmarks = {...}: { - programs.firefox = { - profiles.leyla = { - bookmarks = { - force = true; - settings = [ - # Personal Services - { - name = "Media"; - url = "https://media.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Photos"; - url = "https://photos.jan-leila.com"; - keyword = ""; - tags = [""]; - } - { - name = "Git"; - url = "https://git.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Home Automation"; - url = "https://home.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Search"; - url = "https://search.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Budget"; - url = "https://search.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Documents"; - url = "https://documents.jan-leila.com/"; - keyword = ""; - tags = [""]; - } - - # Defiant Server Services - { - name = "QBittorrent"; - url = "http://defiant:8084"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Sonarr"; - url = "http://defiant:8989"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Radarr"; - url = "http://defiant:7878"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Bazarr"; - url = "http://defiant:6767"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Lidarr"; - url = "http://defiant:8686"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Jackett"; - url = "http://defiant:9117"; - keyword = ""; - tags = ["defiant"]; - } - { - name = "Crab-hole DNS"; - url = "http://defiant:8085"; - keyword = ""; - tags = ["defiant"]; - } - - # External Services - { - name = "Mail"; - url = "https://mail.protonmail.com"; - keyword = ""; - tags = [""]; - } - { - name = "Open Street Map"; - url = "https://www.openstreetmap.org/"; - keyword = ""; - tags = [""]; - } - { - name = "Password Manager"; - url = "https://vault.bitwarden.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Mastodon"; - url = "https://mspsocial.net"; - keyword = ""; - tags = [""]; - } - { - name = "Linked In"; - url = "https://www.linkedin.com/"; - keyword = ""; - tags = [""]; - } - { - name = "Job Search"; - url = "https://www.jobsinnetwork.com/?state=cleaned_history&language%5B%5D=en&query=react&locations.countryCode%5B%5D=IT&locations.countryCode%5B%5D=DE&locations.countryCode%5B%5D=NL&experience%5B%5D=medior&experience%5B%5D=junior&page=1"; - keyword = ""; - tags = [""]; - } - { - name = "React Docs"; - url = "https://react.dev/"; - keyword = ""; - tags = [""]; - } - { - name = "Cyberia Matrix"; - url = "https://chat.cyberia.club"; - keyword = ""; - tags = [""]; - } - { - name = "Cyberia Git"; - url = "https://git.cyberia.club"; - keyword = ""; - tags = [""]; - } - # Template - # { - # name = ""; - # url = ""; - # keyword = ""; - # tags = [""]; - # } - ]; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/firefox/default.nix b/modules/hosts/home/leyla/packages/firefox/default.nix deleted file mode 100644 index 713e8c7..0000000 --- a/modules/hosts/home/leyla/packages/firefox/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{...}: { - flake.homeModules.leylaFirefox = {...}: { - config = { - programs.firefox = { - enable = true; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/firefox/firefox.nix b/modules/hosts/home/leyla/packages/firefox/firefox.nix deleted file mode 100644 index 908ec97..0000000 --- a/modules/hosts/home/leyla/packages/firefox/firefox.nix +++ /dev/null @@ -1,210 +0,0 @@ -{...}: { - flake.homeModules.leylaFirefoxProfile = { - lib, - pkgs, - inputs, - ... - }: { - programs.firefox = { - profiles.leyla = { - settings = { - "browser.search.defaultenginename" = "Searx"; - "browser.search.order.1" = "Searx"; - }; - - search = { - force = true; - default = "Searx"; - engines = { - "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"]; - }; - "NixOS Wiki" = { - urls = [{template = "https://nixos.wiki/index.php?search={searchTerms}";}]; - icon = "https://nixos.wiki/favicon.png"; - updateInterval = 24 * 60 * 60 * 1000; # every day - definedAliases = ["@nw"]; - }; - "Searx" = { - urls = [{template = "https://search.jan-leila.com/?q={searchTerms}";}]; - icon = "https://nixos.wiki/favicon.png"; - updateInterval = 24 * 60 * 60 * 1000; # every day - definedAliases = ["@searx"]; - }; - }; - }; - - extensions.packages = with inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}; [ - bitwarden - proton-pass - terms-of-service-didnt-read - multi-account-containers - shinigami-eyes - - ublock-origin - sponsorblock - dearrow - df-youtube - return-youtube-dislikes - - privacy-badger - decentraleyes - clearurls - localcdn - - snowflake - - pkgs.firefox-extensions.deutsch-de-language-pack - dictionary-german - - tab-session-manager - - pkgs.firefox-extensions.italiano-it-language-pack - pkgs.firefox-extensions.dizionario-italiano - ]; - - settings = { - # Disable irritating first-run stuff - "browser.disableResetPrompt" = true; - "browser.download.panel.shown" = true; - "browser.feeds.showFirstRunUI" = false; - "browser.messaging-system.whatsNewPanel.enabled" = false; - "browser.rights.3.shown" = true; - "browser.shell.checkDefaultBrowser" = false; - "browser.shell.defaultBrowserCheckCount" = 1; - "browser.startup.homepage_override.mstone" = "ignore"; - "browser.uitour.enabled" = false; - "startup.homepage_override_url" = ""; - "trailhead.firstrun.didSeeAboutWelcome" = true; - "browser.bookmarks.restore_default_bookmarks" = false; - "browser.bookmarks.addedImportButton" = true; - "browser.newtabpage.activity-stream.feeds.section.topstories" = false; - - # Usage Experience - "browser.startup.homepage" = "about:home"; - "browser.download.useDownloadDir" = false; - "browser.uiCustomization.state" = builtins.toJSON { - "currentVersion" = 20; - "newElementCount" = 6; - "dirtyAreaCache" = [ - "nav-bar" - "PersonalToolbar" - "toolbar-menubar" - "TabsToolbar" - "unified-extensions-area" - "vertical-tabs" - ]; - "placements" = { - "widget-overflow-fixed-list" = []; - "unified-extensions-area" = [ - # # bitwarden - # "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" - # proton pass - "78272b6fa58f4a1abaac99321d503a20_proton_me-browser-action" - # ublock origin - "ublock0_raymondhill_net-browser-action" - # sponsor block - "sponsorblocker_ajay_app-browser-action" - # de arrow - "dearrow_ajay_app-browser-action" - # privacy badger - "jid1-mnnxcxisbpnsxq_jetpack-browser-action" - # acount containers - "_testpilot-containers-browser-action" - # simple login - "addon_simplelogin-browser-action" - # clear URLs - "_74145f27-f039-47ce-a470-a662b129930a_-browser-action" - # Decentraleyes - "jid1-bofifl9vbdl2zq_jetpack-browser-action" - # - "dfyoutube_example_com-browser-action" - # Local CDN - "_b86e4813-687a-43e6-ab65-0bde4ab75758_-browser-action" - # Return youtube dislike - "_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action" - # Snow flake - "_b11bea1f-a888-4332-8d8a-cec2be7d24b9_-browse-action" - # Terms of service didn't read - "jid0-3guet1r69sqnsrca5p8kx9ezc3u_jetpack-browser-action" - ]; - "nav-bar" = [ - "back-button" - "forward-button" - "stop-reload-button" - "urlbar-container" - "downloads-button" - "unified-extensions-button" - "reset-pbm-toolbar-button" - ]; - "toolbar-menubar" = [ - "menubar-items" - ]; - "TabsToolbar" = [ - "firefox-view-button" - "tabbrowser-tabs" - "new-tab-button" - "alltabs-button" - ]; - "vertical-tabs" = []; - "PersonalToolbar" = [ - "import-button" - "personal-bookmarks" - ]; - }; - "seen" = [ - "save-to-pocket-button" - "developer-button" - "privacy_privacy_com-browser-action" - "sponsorblocker_ajay_app-browser-action" - "ublock0_raymondhill_net-browser-action" - "addon_simplelogin-browser-action" - "dearrow_ajay_app-browser-action" - "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action" - "_74145f27-f039-47ce-a470-a662b129930a_-browser-action" - "jid1-bofifl9vbdl2zq_jetpack-browser-action" - "dfyoutube_example_com-browser-action" - "_testpilot-containers-browser-action" - "_b86e4813-687a-43e6-ab65-0bde4ab75758_-browser-action" - "jid1-mnnxcxisbpnsxq_jetpack-browser-action" - "_762f9885-5a13-4abd-9c77-433dcd38b8fd_-browser-action" - "_b11bea1f-a888-4332-8d8a-cec2be7d24b9_-browser-action" - "jid0-3guet1r69sqnsrca5p8kx9ezc3u_jetpack-browser-action" - "78272b6fa58f4a1abaac99321d503a20_proton_me-browser-action" - ]; - }; - "browser.newtabpage.activity-stream.feeds.topsites" = false; - "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; - "browser.newtabpage.activity-stream.improvesearch.topSiteSearchShortcuts" = false; - "browser.newtabpage.blocked" = lib.genAttrs [ - # Facebook - "4gPpjkxgZzXPVtuEoAL9Ig==" - # Reddit - "gLv0ja2RYVgxKdp0I5qwvA==" - # Amazon - "K00ILysCaEq8+bEqV/3nuw==" - # Twitter - "T9nJot5PurhJSy8n038xGA==" - ] (_: 1); - "identity.fxaccounts.enabled" = false; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/firefox/harden.nix b/modules/hosts/home/leyla/packages/firefox/harden.nix deleted file mode 100644 index 48841d6..0000000 --- a/modules/hosts/home/leyla/packages/firefox/harden.nix +++ /dev/null @@ -1,52 +0,0 @@ -{...}: { - flake.homeModules.leylaFirefoxHarden = {...}: { - programs.firefox = { - profiles.leyla = { - settings = { - # Security - "privacy.trackingprotection.enabled" = true; - "dom.security.https_only_mode" = true; - "dom.security.https_only_mode_pbm" = true; - "dom.security.https_only_mode_error_page_user_suggestions" = true; - - # Privacy & Data Protection - "extensions.formautofill.addresses.enabled" = false; - "extensions.formautofill.creditCards.enabled" = false; - "signon.rememberSignons" = false; - "privacy.sanitize.sanitizeOnShutdown" = true; - "privacy.clearOnShutdown_v2.cache" = true; - "privacy.clearOnShutdown_v2.cookiesAndStorage" = true; - "privacy.clearOnShutdown_v2.historyFormDataAndDownloads" = true; - "urlclassifier.trackingSkipURLs" = ""; - "urlclassifier.features.socialtracking.skipURLs" = ""; - - # Disable telemetry and data collection - "app.shield.optoutstudies.enabled" = false; - "browser.discovery.enabled" = false; - "browser.newtabpage.activity-stream.feeds.telemetry" = false; - "browser.newtabpage.activity-stream.telemetry" = false; - "browser.ping-centre.telemetry" = false; - "datareporting.healthreport.service.enabled" = false; - "datareporting.healthreport.uploadEnabled" = false; - "datareporting.policy.dataSubmissionEnabled" = false; - "datareporting.sessions.current.clean" = true; - "devtools.onboarding.telemetry.logged" = false; - "toolkit.telemetry.archive.enabled" = false; - "toolkit.telemetry.bhrPing.enabled" = false; - "toolkit.telemetry.enabled" = false; - "toolkit.telemetry.firstShutdownPing.enabled" = false; - "toolkit.telemetry.hybridContent.enabled" = false; - "toolkit.telemetry.newProfilePing.enabled" = false; - "toolkit.telemetry.prompted" = 2; - "toolkit.telemetry.rejected" = true; - "toolkit.telemetry.reportingpolicy.firstRun" = false; - "toolkit.telemetry.server" = ""; - "toolkit.telemetry.shutdownPingSender.enabled" = false; - "toolkit.telemetry.unified" = false; - "toolkit.telemetry.unifiedIsOptIn" = false; - "toolkit.telemetry.updatePing.enabled" = false; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/git.nix b/modules/hosts/home/leyla/packages/git.nix deleted file mode 100644 index 47689a1..0000000 --- a/modules/hosts/home/leyla/packages/git.nix +++ /dev/null @@ -1,16 +0,0 @@ -{...}: { - flake.homeModules.leylaGit = {...}: { - config = { - programs = { - git = { - signing.format = "openpgp"; - settings = { - user.name = "Leyla Becker"; - user.email = "git@jan-leila.com"; - init.defaultBranch = "main"; - }; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/makemkv.nix b/modules/hosts/home/leyla/packages/makemkv.nix deleted file mode 100644 index 9330730..0000000 --- a/modules/hosts/home/leyla/packages/makemkv.nix +++ /dev/null @@ -1,19 +0,0 @@ -{...}: { - flake.homeModules.leylaMakemkv = { - config, - inputs, - ... - }: { - config = { - sops.secrets = { - "application-keys/makemkv" = { - sopsFile = "${inputs.secrets}/application-keys.yaml"; - }; - }; - programs.makemkv = { - appKeyFile = config.sops.placeholder."application-keys/makemkv"; - destinationDir = "/home/leyla/downloads/makemkv"; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/openssh.nix b/modules/hosts/home/leyla/packages/openssh.nix deleted file mode 100644 index 1c5e6ec..0000000 --- a/modules/hosts/home/leyla/packages/openssh.nix +++ /dev/null @@ -1,25 +0,0 @@ -{...}: { - flake.homeModules.leylaOpenssh = { - config, - osConfig, - ... - }: { - config = { - programs = { - openssh = { - authorizedKeys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHeItmt8TRW43uNcOC+eIurYC7Eunc0V3LGocQqLaYj leyla@horizon" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIILimFIW2exEH/Xo7LtXkqgE04qusvnPNpPWSCeNrFkP leyla@defiant" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKBiZkg1c2aaNHiieBX4cEziqvJVj9pcDfzUrKU/mO0I leyla@twilight" - ]; - hostKeys = [ - { - type = "ed25519"; - path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; - } - ]; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/vscode/default.nix b/modules/hosts/home/leyla/packages/vscode/default.nix deleted file mode 100644 index c3d93c1..0000000 --- a/modules/hosts/home/leyla/packages/vscode/default.nix +++ /dev/null @@ -1,98 +0,0 @@ -{...}: { - flake.homeModules.leylaVscode = { - lib, - pkgs, - config, - osConfig, - ... - }: let - nix-development-enabled = osConfig.host.nix-development.enable; - in { - config = lib.mkIf config.user.isDesktopUser { - programs = { - bash.shellAliases = { - code = "codium"; - }; - - vscode = { - package = pkgs.vscodium; - - mutableExtensionsDir = false; - - profiles.default = { - enableUpdateCheck = false; - enableExtensionUpdateCheck = false; - - userSettings = lib.mkMerge [ - { - "javascript.updateImportsOnFileMove.enabled" = "always"; - "editor.tabSize" = 2; - "editor.insertSpaces" = false; - # "terminal.integrated.fontFamily" = "'Droid Sans Mono', 'monospace', monospace"; - } - ]; - - extraExtensions = { - # vs code feel - oneDark.enable = true; - atomKeybindings.enable = true; - openRemoteSsh.enable = true; - # openDyslexicFont.enable = false; - - # html development - autoRenameTag.enable = true; - liveServer.enable = true; - - # js development - es7ReactJsSnippets.enable = true; - tauriVscode.enable = true; - vscodeEslint.enable = true; - vscodeJest.enable = true; - vitest.enable = true; - vscodeStandard.enable = true; - vscodeStylelint.enable = true; - - nearley.enable = true; - - # graphql - graphql.enable = true; - - # astro development - vscodeMdx.enable = true; - astroVscode.enable = true; - - # nix development - alejandra.enable = nix-development-enabled; - nixIde.enable = nix-development-enabled; - - # go development - go.enable = true; - - # rust development - rustAnalyzer.enable = true; - - # arduino development - platformIO.enable = false; - - # misc extensions - evenBetterToml.enable = true; - direnv.enable = config.programs.direnv.enable; - conventionalCommits.enable = true; - }; - - extensions = let - extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; - in ( - with extension-pkgs.open-vsx; [ - # vs code feel extensions - streetsidesoftware.code-spell-checker - streetsidesoftware.code-spell-checker-german - streetsidesoftware.code-spell-checker-italian - ] - ); - }; - }; - }; - }; - }; -} diff --git a/modules/hosts/home/leyla/packages/vscode/user-words.nix b/modules/hosts/home/leyla/packages/vscode/user-words.nix deleted file mode 100644 index e22e458..0000000 --- a/modules/hosts/home/leyla/packages/vscode/user-words.nix +++ /dev/null @@ -1,129 +0,0 @@ -{...}: { - flake.homeModules.leylaVscodeUserWords = { - pkgs, - lib, - ... - }: { - config.programs.vscode.profiles.default.userSettings = { - "cSpell.userWords" = [ - "leyla" - "Cyberia" - ]; - - "cSpell.languageSettings" = [ - { - "languageId" = "nix"; - "locale" = "*"; - "dictionaries" = [ - "applications" - "ai-words" - "nix-words" - - # We need to include all other dictionaries in the nix language settings because they exist in this file - # TODO: see if there is a way to make this only apply for this file - "js-words" - ]; - } - { - "languageId" = "javascript,typescript,js,ts"; - "locale" = "*"; - "dictionaries" = [ - "js-words" - ]; - } - ]; - - "cSpell.customDictionaries" = { - applications = { - name = "applications"; - description = "application names"; - path = pkgs.writeText "applications.txt" (lib.strings.concatLines [ - "ollama" - "syncthing" - "immich" - "sonos" - "makemkv" - "hass" - "qbittorent" - "prostudiomasters" - "protonmail" - "pulseaudio" - ]); - }; - - ai-words = { - name = "ai-words"; - description = "common words used for ai development"; - path = pkgs.writeText "ai-words.txt" (lib.strings.concatLines [ - "ollama" - "deepseek" - "qwen" - ]); - }; - - nix-words = { - name = "nix-words"; - description = "words used in nix configurations"; - path = pkgs.writeText "nix-words.txt" (lib.strings.concatLines [ - "pname" - "direnv" - "tmpfiles" - "Networkd" - "networkmanager" - "dialout" - "adbusers" - "authkey" - "netdevs" - "atomix" - "geary" - "gedit" - "hitori" - "iagno" - "alsa" - "timezoned" - "pipewire" - "rtkit" - "disko" - "ashift" - "autotrim" - "canmount" - "mountpoint" - "xattr" - "acltype" - "relatime" - "keyformat" - "keylocation" - "vdevs" - - # codium extensions - "akamud" - "onedark" - "jeanp" - "dsznajder" - "dbaeumer" - "orta" - "tauri" - "unifiedjs" - "tamasfe" - "pinage" - "jnoortheen" - "kamadorueda" - "karyfoundation" - "nearley" - - # nix.optimise is spelled wrong - "optimise" - ]); - }; - - js-words = { - name = "js-words"; - description = "words used in js development"; - path = pkgs.writeText "js-words.txt" (lib.strings.concatLines [ - "webdav" - ]); - }; - }; - }; - }; -} diff --git a/modules/hosts/nixos/defiant/configuration.nix b/modules/hosts/nixos/defiant/configuration.nix deleted file mode 100644 index bd5d052..0000000 --- a/modules/hosts/nixos/defiant/configuration.nix +++ /dev/null @@ -1,432 +0,0 @@ -{...}: { - # server nas - flake.nixosModules.defiantConfiguration = { - inputs, - config, - ... - }: { - sops.secrets = { - "vpn-keys/tailscale-authkey/defiant" = { - sopsFile = "${inputs.secrets}/vpn-keys.yaml"; - }; - "vpn-keys/proton-wireguard/defiant-p2p" = { - sopsFile = "${inputs.secrets}/vpn-keys.yaml"; - mode = "0640"; - owner = "root"; - group = "systemd-network"; - }; - "services/zfs_smtp_token" = { - sopsFile = "${inputs.secrets}/defiant-services.yaml"; - }; - "services/paperless_password" = { - sopsFile = "${inputs.secrets}/defiant-services.yaml"; - mode = "0700"; - owner = "paperless"; - group = "paperless"; - }; - }; - - host = { - users = { - leyla = { - isDesktopUser = true; - isTerminalUser = true; - isPrincipleUser = true; - }; - }; - network_storage = { - enable = true; - directories = [ - { - folder = "leyla_documents"; - user = "leyla"; - group = "leyla"; - bind = "/home/leyla/documents"; - } - { - folder = "eve_documents"; - user = "eve"; - group = "eve"; - } - { - folder = "users_documents"; - user = "root"; - group = "users"; - } - { - folder = "media"; - user = "jellyfin"; - group = "jellyfin_media"; - bind = config.services.jellyfin.media_directory; - } - ]; - nfs = { - enable = true; - directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; - }; - }; - }; - - storage = { - zfs = { - enable = true; - notifications = { - enable = true; - host = "smtp.protonmail.ch"; - port = 587; - to = "leyla@jan-leila.com"; - user = "noreply@jan-leila.com"; - tokenFile = config.sops.secrets."services/zfs_smtp_token".path; - }; - pool = { - encryption = { - enable = true; - }; - vdevs = [ - [ - "ata-ST18000NE000-3G6101_ZVTCXVEB" - "ata-ST18000NE000-3G6101_ZVTCXWSC" - "ata-ST18000NE000-3G6101_ZVTD10EH" - "ata-ST18000NT001-3NF101_ZVTE0S3Q" - "ata-ST18000NT001-3NF101_ZVTEF27J" - "ata-ST18000NE000-3G6101_ZVTJ7359" - ] - [ - "ata-ST4000NE001-2MA101_WS2275P3" - "ata-ST4000NE001-2MA101_WS227B9F" - "ata-ST4000NE001-2MA101_WS227CEW" - "ata-ST4000NE001-2MA101_WS227CYN" - "ata-ST4000NE001-2MA101_WS23TBWV" - "ata-ST4000NE001-2MA101_WS23TC5F" - ] - ]; - # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA - cache = [ - { - device = "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"; - boot = true; - } - ]; - }; - }; - impermanence = { - enable = true; - }; - }; - - # bond0 and wg0 are managed by systemd-networkd; tell NetworkManager to - # leave them alone so NM-wait-online doesn't time out waiting for them. - networking.networkmanager.unmanaged = ["bond0" "wg0" "eno1" "eno2"]; - - # Only expose ollama over tailscale - networking.firewall.interfaces."tailscale0".allowedTCPPorts = [11434]; - - systemd.network = { - enable = true; - - netdevs = { - "10-bond0" = { - netdevConfig = { - Kind = "bond"; - Name = "bond0"; - }; - bondConfig = { - Mode = "active-backup"; - PrimaryReselectPolicy = "always"; - }; - }; - - "20-wg0" = { - netdevConfig = { - Kind = "wireguard"; - Name = "wg0"; - }; - wireguardConfig = { - PrivateKeyFile = config.sops.secrets."vpn-keys/proton-wireguard/defiant-p2p".path; - ListenPort = 51820; - }; - wireguardPeers = [ - { - PublicKey = "rRO6yJim++Ezz6scCLMaizI+taDjU1pzR2nfW6qKbW0="; - Endpoint = "185.230.126.146:51820"; - # Allow all traffic but use policy routing to prevent system-wide VPN - AllowedIPs = ["0.0.0.0/0"]; - PersistentKeepalive = 25; - } - ]; - }; - }; - networks = { - "40-bond0" = { - matchConfig.Name = "bond0"; - linkConfig = { - RequiredForOnline = "degraded-carrier"; - RequiredFamilyForOnline = "any"; - }; - networkConfig.DHCP = "yes"; - - address = [ - "192.168.1.2/24" - ]; - - # Set lower priority for default gateway to allow WireGuard interface binding - routes = [ - { - Destination = "0.0.0.0/0"; - Gateway = "192.168.1.1"; - Metric = 100; - } - ]; - dns = ["192.168.1.1"]; - }; - - "50-wg0" = { - matchConfig.Name = "wg0"; - # Don't block networkd-wait-online on the VPN tunnel coming up - linkConfig.RequiredForOnline = "no"; - networkConfig = { - DHCP = "no"; - }; - address = [ - "10.2.0.2/32" - ]; - # Configure routing for application binding - routingPolicyRules = [ - { - # Route traffic from VPN interface through VPN table - From = "10.2.0.2/32"; - Table = 200; - Priority = 100; - } - ]; - routes = [ - { - # Direct route to VPN gateway - Destination = "10.2.0.1/32"; - Scope = "link"; - } - { - # Route VPN subnet through VPN gateway in custom table - Destination = "10.2.0.0/16"; - Gateway = "10.2.0.1"; - Table = 200; - } - { - # Route all traffic through VPN gateway in custom table - Destination = "0.0.0.0/0"; - Gateway = "10.2.0.1"; - Table = 200; - } - ]; - }; - }; - }; - - # limit arc usage to 50gb because ollama doesn't play nice with zfs using up all of the memory - boot.kernelParams = ["zfs.zfs_arc_max=53687091200"]; - - # Enable policy routing and source routing for application-specific VPN binding - boot.kernel.sysctl = { - "net.ipv4.conf.all.rp_filter" = 2; - "net.ipv4.conf.default.rp_filter" = 2; - "net.ipv4.conf.wg0.rp_filter" = 2; - }; - - services = { - # PostgreSQL database server - postgresql = { - enable = true; - adminUsers = ["leyla"]; - impermanence.enable = false; - }; - - # temp enable desktop environment for setup - # Enable the X11 windowing system. - xserver.enable = true; - - # Enable the GNOME Desktop Environment. - displayManager = { - gdm.enable = true; - }; - desktopManager = { - gnome.enable = true; - }; - - # Enable new reverse proxy system - reverseProxy = { - enable = true; - openFirewall = true; - impermanence.enable = false; - acme = { - enable = true; - email = "jan-leila@protonmail.com"; - }; - }; - - ollama = { - enable = true; - host = "0.0.0.0"; - user = "ollama"; - group = "ollama"; - loadModels = [ - "gpt-oss:120b" - ]; - }; - tailscale = { - enable = true; - authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; - useRoutingFeatures = "server"; - impermanence.enable = false; - extraUpFlags = [ - "--advertise-exit-node" - "--advertise-routes=192.168.0.0/24" - "--accept-dns=false" - ]; - extraSetFlags = [ - "--advertise-exit-node" - "--advertise-routes=192.168.0.0/24" - "--accept-dns=false" - ]; - }; - - syncthing = { - enable = true; - impermanence.enable = false; - }; - - fail2ban = { - enable = true; - impermanence.enable = false; - }; - - jellyfin = { - enable = true; - domain = "media.jan-leila.com"; - extraDomains = ["jellyfin.jan-leila.com"]; - impermanence.enable = false; - }; - - immich = { - enable = true; - domain = "photos.jan-leila.com"; - impermanence.enable = false; - }; - - forgejo = { - enable = true; - reverseProxy.domain = "git.jan-leila.com"; - impermanence.enable = false; - }; - - searx = { - enable = true; - domain = "search.jan-leila.com"; - }; - - actual = { - enable = false; - domain = "budget.jan-leila.com"; - impermanence.enable = false; - }; - - home-assistant = { - enable = true; - domain = "home.jan-leila.com"; - openFirewall = true; - postgres.enable = true; - impermanence.enable = false; - - extensions = { - sonos.enable = true; - jellyfin.enable = true; - wyoming.enable = false; # Temporarily disabled due to dependency conflict in wyoming-piper - }; - }; - - paperless = { - enable = true; - domain = "documents.jan-leila.com"; - passwordFile = config.sops.secrets."services/paperless_password".path; - impermanence.enable = false; - }; - - panoramax = { - enable = false; - openFirewall = true; - impermanence.enable = false; - }; - - crab-hole = { - enable = true; - port = 8085; - openFirewall = true; - show_doc = true; - impermanence.enable = false; - downstreams = { - host = { - enable = true; - openFirewall = true; - }; - }; - upstreams.cloudFlare.enable = true; - blocklists.ad_malware.enable = true; - }; - - qbittorrent = { - enable = true; - mediaDir = "/srv/qbittorent"; - openFirewall = true; - webuiPort = 8084; - impermanence.enable = false; - }; - - sonarr = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - radarr = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - bazarr = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - lidarr = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - jackett = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - flaresolverr = { - enable = true; - openFirewall = true; - impermanence.enable = false; - }; - }; - - # disable computer sleeping - systemd.targets = { - sleep.enable = false; - suspend.enable = false; - hibernate.enable = false; - hybrid-sleep.enable = false; - }; - services.displayManager.gdm.autoSuspend = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It's perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? - }; -} diff --git a/modules/hosts/nixos/defiant/default.nix b/modules/hosts/nixos/defiant/default.nix deleted file mode 100644 index 5f58a7d..0000000 --- a/modules/hosts/nixos/defiant/default.nix +++ /dev/null @@ -1,22 +0,0 @@ -# server nas -{ - inputs, - config, - ... -}: { - flake.nixosConfigurations.defiant = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config.flake.nixosModules.nixosModules - config.flake.nixosModules.defiantConfiguration - config.flake.nixosModules.defiantHardwareConfiguration - config.flake.nixosModules.defiantPackages - config.flake.nixosModules.defiantLegacyStorage - config.flake.nixosModules.defiantLegacyImpermanence - ]; - specialArgs = { - inherit inputs; - syncthingConfiguration = inputs.self.syncthingConfiguration; - }; - }; -} diff --git a/modules/hosts/nixos/defiant/hardware-configuration.nix b/modules/hosts/nixos/defiant/hardware-configuration.nix deleted file mode 100644 index f85e31b..0000000 --- a/modules/hosts/nixos/defiant/hardware-configuration.nix +++ /dev/null @@ -1,70 +0,0 @@ -{...}: { - # Do not modify this file! It was generated by 'nixos-generate-config' - # and may be overwritten by future invocations. Please make changes - # to /etc/nixos/configuration.nix instead. - flake.nixosModules.defiantHardwareConfiguration = { - config, - lib, - modulesPath, - ... - }: { - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot = { - initrd = { - availableKernelModules = ["xhci_pci" "aacraid" "ahci" "usbhid" "nvme" "usb_storage" "sd_mod"]; - kernelModules = []; - }; - kernelModules = ["kvm-amd"]; - extraModulePackages = []; - - # Bootloader. - loader = { - systemd-boot.enable = true; - efi = { - canTouchEfiVariables = true; - efiSysMountPoint = "/boot"; - }; - }; - supportedFilesystems = ["zfs"]; - - zfs.extraPools = ["rpool"]; - }; - - networking = { - hostName = "defiant"; # Define your hostname. - hostId = "c51763d6"; - useNetworkd = true; - }; - - systemd.network = { - enable = true; - - networks = { - "30-eno1" = { - matchConfig.Name = "eno1"; - networkConfig = { - Bond = "bond0"; - PrimarySlave = true; - }; - linkConfig.RequiredForOnline = "enslaved"; - }; - "30-eno2" = { - matchConfig.Name = "eno2"; - networkConfig.Bond = "bond0"; - linkConfig.RequiredForOnline = "enslaved"; - }; - }; - }; - - networking.networkmanager.enable = true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware = { - # TODO: hardware graphics - cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - }; - }; -} diff --git a/modules/hosts/nixos/defiant/legacy-impermanence.nix b/modules/hosts/nixos/defiant/legacy-impermanence.nix deleted file mode 100644 index 684176b..0000000 --- a/modules/hosts/nixos/defiant/legacy-impermanence.nix +++ /dev/null @@ -1,298 +0,0 @@ -{...}: { - # Legacy impermanence module for defiant - # See legacy-storage.nix for the full incremental migration plan. - # - # This file is consumed in two phases: - # - # Phase 3 (after generateBase is enabled): - # Remove the SYSTEM-LEVEL entries marked [PHASE 3] below. These will be - # handled automatically by storage.nix, ssh.nix, and the impermanence module: - # - var-lib-private-permissions activation script - # - /etc/machine-id - # - SSH host keys - # - /var/lib/nixos - # - /var/lib/systemd/coredump - # - /persist/system/var/log persistence block - # - # Phase 4 (migrate services one at a time, any order): - # For each service: - # 1. Remove the service's section marked [PHASE 4] from this file - # 2. Remove `impermanence.enable = false` for that service in configuration.nix - # For jellyfin/qbittorrent, also remove the separate media persistence blocks. - # - # Phase 5: Delete this file once empty. - flake.nixosModules.defiantLegacyImpermanence = { - config, - lib, - ... - }: { - config = lib.mkIf config.storage.impermanence.enable { - # [PHASE 3] Remove this activation script after enabling generateBase - system.activationScripts = { - "var-lib-private-permissions" = { - deps = ["specialfs"]; - text = '' - mkdir -p /persist/system/root/var/lib/private - chmod 0700 /persist/system/root/var/lib/private - ''; - }; - }; - - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - # [PHASE 3] Remove this files block after enabling generateBase - files = lib.mkMerge [ - ["/etc/machine-id"] - # SSH host keys - (lib.mkIf config.services.openssh.enable ( - lib.lists.flatten ( - builtins.map (hostKey: [ - hostKey.path - "${hostKey.path}.pub" - ]) - config.services.openssh.hostKeys - ) - )) - ]; - directories = lib.mkMerge [ - # [PHASE 3] Remove these system directories after enabling generateBase - [ - "/var/lib/nixos" - "/var/lib/systemd/coredump" - ] - - # [PHASE 4] PostgreSQL - (lib.mkIf config.services.postgresql.enable [ - { - directory = "/var/lib/postgresql/16"; - user = "postgres"; - group = "postgres"; - } - ]) - - # [PHASE 4] Reverse Proxy (ACME) - (lib.mkIf config.services.reverseProxy.enable [ - { - directory = "/var/lib/acme"; - user = "acme"; - group = "acme"; - } - ]) - - # [PHASE 4] Ollama - (lib.mkIf config.services.ollama.enable [ - { - directory = "/var/lib/private/ollama"; - user = config.services.ollama.user; - group = config.services.ollama.group; - mode = "0700"; - } - ]) - - # [PHASE 4] Tailscale - (lib.mkIf config.services.tailscale.enable [ - { - directory = "/var/lib/tailscale"; - user = "root"; - group = "root"; - } - ]) - - # [PHASE 4] Syncthing - (lib.mkIf config.services.syncthing.enable [ - { - directory = "/mnt/sync"; - user = "syncthing"; - group = "syncthing"; - } - { - directory = "/etc/syncthing"; - user = "syncthing"; - group = "syncthing"; - } - ]) - - # [PHASE 4] Fail2ban - (lib.mkIf config.services.fail2ban.enable [ - { - directory = "/var/lib/fail2ban"; - user = "fail2ban"; - group = "fail2ban"; - } - ]) - - # [PHASE 4] Jellyfin (data/cache only - media is on separate dataset) - (lib.mkIf config.services.jellyfin.enable [ - { - directory = "/var/lib/jellyfin"; - user = "jellyfin"; - group = "jellyfin"; - } - { - directory = "/var/cache/jellyfin"; - user = "jellyfin"; - group = "jellyfin"; - } - ]) - - # [PHASE 4] Immich - (lib.mkIf config.services.immich.enable [ - { - directory = "/var/lib/immich"; - user = "immich"; - group = "immich"; - } - ]) - - # [PHASE 4] Forgejo - (lib.mkIf config.services.forgejo.enable [ - { - directory = "/var/lib/forgejo"; - user = "forgejo"; - group = "forgejo"; - } - ]) - - # [PHASE 4] Actual - (lib.mkIf config.services.actual.enable [ - { - directory = "/var/lib/private/actual"; - user = "actual"; - group = "actual"; - } - ]) - - # [PHASE 4] Home Assistant - (lib.mkIf config.services.home-assistant.enable [ - { - directory = "/var/lib/hass"; - user = "hass"; - group = "hass"; - } - ]) - - # [PHASE 4] Paperless - (lib.mkIf config.services.paperless.enable [ - { - directory = "/var/lib/paperless"; - user = "paperless"; - group = "paperless"; - } - ]) - - # [PHASE 4] Crab-hole - (lib.mkIf config.services.crab-hole.enable [ - { - directory = "/var/lib/private/crab-hole"; - user = "crab-hole"; - group = "crab-hole"; - } - ]) - - # [PHASE 4] qBittorrent (config only - media is on separate dataset) - (lib.mkIf config.services.qbittorrent.enable [ - { - directory = "/var/lib/qBittorrent/"; - user = "qbittorrent"; - group = "qbittorrent"; - } - ]) - - # [PHASE 4] Sonarr - (lib.mkIf config.services.sonarr.enable [ - { - directory = "/var/lib/sonarr/.config/NzbDrone"; - user = "sonarr"; - group = "sonarr"; - } - ]) - - # [PHASE 4] Radarr - (lib.mkIf config.services.radarr.enable [ - { - directory = "/var/lib/radarr/.config/Radarr"; - user = "radarr"; - group = "radarr"; - } - ]) - - # [PHASE 4] Bazarr - (lib.mkIf config.services.bazarr.enable [ - { - directory = "/var/lib/bazarr"; - user = "bazarr"; - group = "bazarr"; - } - ]) - - # [PHASE 4] Lidarr - (lib.mkIf config.services.lidarr.enable [ - { - directory = "/var/lib/lidarr/.config/Lidarr"; - user = "lidarr"; - group = "lidarr"; - } - ]) - - # [PHASE 4] Jackett - (lib.mkIf config.services.jackett.enable [ - { - directory = "/var/lib/jackett/.config/Jackett"; - user = "jackett"; - group = "jackett"; - } - ]) - - # [PHASE 4] FlareSolverr - (lib.mkIf config.services.flaresolverr.enable [ - { - directory = "/var/lib/flaresolverr"; - user = "flaresolverr"; - group = "flaresolverr"; - } - ]) - ]; - }; - - # [PHASE 4 - LAST] Jellyfin media on separate dataset - # Requires Phase 2 media dataset merge before migrating (several days of data copy) - environment.persistence."/persist/system/jellyfin" = lib.mkIf config.services.jellyfin.enable { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.jellyfin.media_directory; - user = "jellyfin"; - group = "jellyfin_media"; - mode = "1770"; - } - ]; - }; - - # [PHASE 4 - LAST] qBittorrent media on separate dataset - # Requires Phase 2 media dataset merge before migrating (several days of data copy) - environment.persistence."/persist/system/qbittorrent" = lib.mkIf config.services.qbittorrent.enable { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.qbittorrent.mediaDir; - user = "qbittorrent"; - group = "qbittorrent"; - mode = "1775"; - } - ]; - }; - - # [PHASE 3] /var/log persistence - handled by storage.nix after generateBase - environment.persistence."/persist/system/var/log" = { - enable = true; - hideMounts = true; - directories = [ - "/var/log" - ]; - }; - }; - }; -} diff --git a/modules/hosts/nixos/defiant/legacy-storage.nix b/modules/hosts/nixos/defiant/legacy-storage.nix deleted file mode 100644 index 9b8f7d3..0000000 --- a/modules/hosts/nixos/defiant/legacy-storage.nix +++ /dev/null @@ -1,220 +0,0 @@ -{...}: { - # Legacy storage configuration for defiant - # This file manually defines ZFS datasets matching the existing on-disk layout - # to allow incremental migration to the new storage module (generateBase = true). - # - # ============================================================================ - # INCREMENTAL MIGRATION PLAN - # ============================================================================ - # - # Current disk usage (for reference): - # rpool/local/system/nix ~26G (renamed in place, no copy) - # rpool/local/system/sops ~328K (renamed in place, no copy) - # rpool/persist/system/jellyfin ~32T (renamed in place, no copy) - # rpool/persist/system/qbittorrent ~6.5T (copied into media dataset, ~6.5T temp) - # rpool free space ~30T - # - # Phase 1: Migrate base datasets on disk (boot from live USB or rescue) - # All operations in this phase are instant renames -- no data is copied. - # - # Unlock the pool: - # zfs load-key -a - # - # Step 1a: Move nix and sops out of local/ (they go to persist/local/) - # The -p flag auto-creates the parent datasets. - # - # zfs rename -p rpool/local/system/nix rpool/persist/local/nix - # zfs rename -p rpool/local/system/sops rpool/persist/local/system/sops - # - # Step 1b: Rename local/ -> ephemeral/ (takes remaining children with it) - # zfs rename rpool/local rpool/ephemeral - # # This moves: local/system/root -> ephemeral/system/root - # # local/home/leyla -> ephemeral/home/leyla - # - # Step 1c: Recreate blank snapshots on ephemeral datasets - # zfs destroy rpool/ephemeral/system/root@blank - # zfs snapshot rpool/ephemeral/system/root@blank - # zfs destroy rpool/ephemeral/home/leyla@blank - # zfs snapshot rpool/ephemeral/home/leyla@blank - # - # Step 1d: Move persist/ children under persist/replicate/ - # zfs create -o canmount=off rpool/persist/replicate - # zfs create -o canmount=off rpool/persist/replicate/system - # zfs rename rpool/persist/system/root rpool/persist/replicate/system/root - # zfs rename rpool/persist/system/var rpool/persist/replicate/system/var - # zfs rename rpool/persist/home/leyla rpool/persist/replicate/home - # # Clean up the now-empty home parent - # zfs destroy rpool/persist/home - # # NOTE: Do NOT destroy rpool/persist/system -- it still contains - # # persist/system/jellyfin and persist/system/qbittorrent which are - # # migrated in Phase 2. - # - # Verify the new layout: - # zfs list -r rpool -o name,used,mountpoint - # - # Phase 2: Merge media into a single dataset (do this last) - # Strategy: Rename the jellyfin dataset to become the shared media dataset - # (zero copy, instant), then copy qbittorrent data into it (~6.5T copy). - # This avoids duplicating the 32T jellyfin dataset. - # - # Step 2a: Rename jellyfin dataset to the shared media name - # zfs rename rpool/persist/system/jellyfin rpool/persist/replicate/system/media - # - # Step 2b: Copy qbittorrent data into the media dataset - # This copies ~6.5T and may take several hours/days depending on disk speed. - # The qbittorrent data is not critical to back up so no snapshot needed. - # - # systemctl stop qbittorrent - # rsync -avPHAX /persist/system/qbittorrent/ /persist/replicate/system/media/ - # - # Step 2c: Verify the data and clean up - # ls -la /persist/replicate/system/media/ - # zfs destroy rpool/persist/system/qbittorrent - # # persist/system should now be empty, clean it up: - # zfs destroy rpool/persist/system - # - # Phase 3: Enable generateBase - # In the nix config: - # - Delete this file (legacy-storage.nix) and remove its import from default.nix - # - Remove [PHASE 3] entries from legacy-impermanence.nix: - # - var-lib-private-permissions activation script - # - /etc/machine-id, SSH host keys (files block) - # - /var/lib/nixos, /var/lib/systemd/coredump (directories) - # - /persist/system/var/log persistence block - # These are now handled automatically by storage.nix and ssh.nix. - # Rebuild and verify: - # sudo nixos-rebuild switch --flake .#defiant - # # Verify mounts: findmnt -t fuse.bindfs,fuse - # # Verify persist: ls /persist/replicate/system/root/var/lib/nixos - # # Verify boot: reboot and confirm system comes up cleanly - # - # Phase 4: Migrate services (one at a time, any order) - # For each service (except jellyfin/qbittorrent): - # 1. Remove the service's [PHASE 4] section from legacy-impermanence.nix - # 2. Remove `impermanence.enable = false` for that service in configuration.nix - # 3. Rebuild: sudo nixos-rebuild switch --flake .#defiant - # 4. Verify: systemctl status , check the service's data is intact - # No data migration is needed -- the data already lives on the renamed - # dataset at the new path. - # - # Migrate jellyfin and qbittorrent LAST (after Phase 2 media merge): - # 1. Remove [PHASE 4 - LAST] jellyfin entries from legacy-impermanence.nix - # 2. Remove [PHASE 4 - LAST] qbittorrent entries from legacy-impermanence.nix - # 3. Remove `impermanence.enable = false` for both in configuration.nix - # 4. Rebuild: sudo nixos-rebuild switch --flake .#defiant - # 5. Verify: systemctl status jellyfin qbittorrent - # - # Phase 5: Cleanup - # Once all services are migrated and legacy-impermanence.nix is empty: - # - Delete legacy-impermanence.nix and remove its import from default.nix - # - Rebuild: sudo nixos-rebuild switch --flake .#defiant - # - # ============================================================================ - # - # Current on-disk dataset layout: - # rpool/local/ - ephemeral parent - # rpool/local/home/leyla - ephemeral user home (rolled back on boot) - # rpool/local/system/nix - nix store - # rpool/local/system/root - root filesystem (rolled back on boot) - # rpool/local/system/sops - sops age key - # rpool/persist/ - persistent parent - # rpool/persist/home/leyla - persistent user home - # rpool/persist/system/jellyfin - jellyfin media - # rpool/persist/system/qbittorrent - qbittorrent media - # rpool/persist/system/root - persistent root data - # rpool/persist/system/var/log - log persistence - flake.nixosModules.defiantLegacyStorage = {lib, ...}: { - # Disable automatic base dataset generation so we can define them manually - storage.generateBase = false; - - # Manually define ZFS datasets matching main's structure - storage.zfs.datasets = { - # Ephemeral datasets (local/) - "local" = { - type = "zfs_fs"; - mount = null; - }; - "local/home/leyla" = { - type = "zfs_fs"; - mount = "/home/leyla"; - snapshot = { - blankSnapshot = true; - }; - }; - "local/system/nix" = { - type = "zfs_fs"; - mount = "/nix"; - atime = "off"; - relatime = "off"; - snapshot = { - autoSnapshot = false; - }; - }; - "local/system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - }; - }; - "local/system/sops" = { - type = "zfs_fs"; - mount = "/var/lib/sops-nix"; - }; - - # Persistent datasets (persist/) - "persist" = { - type = "zfs_fs"; - mount = null; - }; - "persist/home/leyla" = { - type = "zfs_fs"; - mount = "/persist/home/leyla"; - snapshot = { - autoSnapshot = true; - }; - }; - "persist/system/jellyfin" = { - type = "zfs_fs"; - mount = "/persist/system/jellyfin"; - atime = "off"; - relatime = "off"; - }; - "persist/system/qbittorrent" = { - type = "zfs_fs"; - mount = "/persist/system/qbittorrent"; - atime = "off"; - relatime = "off"; - }; - "persist/system/root" = { - type = "zfs_fs"; - mount = "/persist/system/root"; - snapshot = { - autoSnapshot = true; - }; - }; - "persist/system/var/log" = { - type = "zfs_fs"; - mount = "/persist/system/var/log"; - }; - }; - - # Boot commands to rollback ephemeral root and user homes on boot - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/local/system/root@blank - zfs rollback -r rpool/local/home/leyla@blank - ''; - - # FileSystems needed for boot - fileSystems = { - "/".neededForBoot = true; - "/persist/system/root".neededForBoot = true; - "/persist/system/var/log".neededForBoot = true; - "/persist/system/jellyfin".neededForBoot = true; - "/persist/system/qbittorrent".neededForBoot = true; - "/var/lib/sops-nix".neededForBoot = true; - "/persist/home/leyla".neededForBoot = true; - "/home/leyla".neededForBoot = true; - }; - }; -} diff --git a/modules/hosts/nixos/defiant/packages.nix b/modules/hosts/nixos/defiant/packages.nix deleted file mode 100644 index 4759e95..0000000 --- a/modules/hosts/nixos/defiant/packages.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.defiantPackages = {pkgs, ...}: { - environment.systemPackages = with pkgs; [ - ffsubsync - sox - yt-dlp - ffmpeg - imagemagick - ]; - }; -} diff --git a/modules/hosts/nixos/emergent/configuration.nix b/modules/hosts/nixos/emergent/configuration.nix deleted file mode 100644 index 0598c30..0000000 --- a/modules/hosts/nixos/emergent/configuration.nix +++ /dev/null @@ -1,182 +0,0 @@ -{...}: { - # Edit this configuration file to define what should be installed on - # your system. Help is available in the configuration.nix(5) man page, on - # https://search.nixos.org/options and in the NixOS manual (`nixos-help`). - flake.nixosModules.emergentConfiguration = { - lib, - pkgs, - ... - }: { - # Use the systemd-boot EFI boot loader. - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - - # networking.hostName = "nixos"; # Define your hostname. - # Pick only one of the below networking options. - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - # networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. - - # Set your time zone. - # time.timeZone = "Europe/Amsterdam"; - - # Configure network proxy if necessary - # networking.proxy.default = "http://user:password@proxy:port/"; - # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; - - # Select internationalisation properties. - # i18n.defaultLocale = "en_US.UTF-8"; - # console = { - # font = "Lat2-Terminus16"; - # keyMap = "us"; - # useXkbConfig = true; # use xkb.options in tty. - # }; - - # Enable the X11 windowing system. - services.xserver.enable = true; - # Enable wacom touchscreen device - services.xserver.wacom.enable = true; - - # installed opentabletdriver - # hardware.opentabletdriver.enable = true; - hardware.keyboard.qmk.enable = true; - - # Enable the GNOME Desktop Environment. - services.displayManager.gdm.enable = true; - services.desktopManager.gnome.enable = true; - - host = { - users = { - eve = { - isDesktopUser = true; - isTerminalUser = true; - isPrincipleUser = true; - }; - }; - hardware = { - piperMouse.enable = true; - }; - }; - - storage = { - zfs = { - enable = true; - pool = { - mode = "stripe"; - vdevs = [ - [ - { - device = "wwn-0x5000039fd0cf05eb"; - boot = true; - } - ] - ]; - cache = []; - }; - }; - }; - - virtualisation.libvirtd.enable = true; - - users.users.eve = { - extraGroups = ["libvirtd"]; - }; - - services.tailscale.enable = true; - # We were having weird build errors so this is disabled right now - # error: The option `devices.emergent.folders.eve_records.path' was accessed but has no value defined. Try setting the option - services.syncthing.enable = false; - - # Configure keymap in X11 - # services.xserver.xkb.layout = "us"; - # services.xserver.xkb.options = "eurosign:e,caps:escape"; - - # Enable CUPS to print documents. - # services.printing.enable = true; - - # Enable sound. - # services.pulseaudio.enable = true; - # OR - # services.pipewire = { - # enable = true; - # pulse.enable = true; - # }; - - # Enable touchpad support (enabled default in most desktopManager). - # services.libinput.enable = true; - - # Define a user account. Don't forget to set a password with ‘passwd’. - # users.users.alice = { - # isNormalUser = true; - # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. - # packages = with pkgs; [ - # tree - # ]; - # }; - - # programs.firefox.enable = true; - - nixpkgs.config.allowUnfree = true; - - # Packages that can be installed without any extra configuration - # See https://search.nixos.org/packages for all options - environment.systemPackages = with pkgs; [ - wget - gnome-boxes - libvirt - ]; - - # Packages that need to be installed with some extra configuration - # See https://search.nixos.org/options for all options - programs = {}; - - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: - - # Enable the OpenSSH daemon. - # services.openssh.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - networking = { - networkmanager.enable = true; - useDHCP = lib.mkDefault true; - hostId = "7e35eb97"; # arbitrary id number generated via this command: `head -c4 /dev/urandom | od -A none -t x4` - hostName = "emergent"; # Define your hostname. - }; - - # Copy the NixOS configuration file and link it from the resulting system - # (/run/current-system/configuration.nix). This is useful in case you - # accidentally delete configuration.nix. - # system.copySystemConfiguration = true; - - # This option defines the first version of NixOS you have installed on this particular machine, - # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. - # - # Most users should NEVER change this value after the initial install, for any reason, - # even if you've upgraded your system to a new NixOS release. - # - # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, - # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how - # to actually do that. - # - # This value being lower than the current NixOS release does NOT mean your system is - # out of date, out of support, or vulnerable. - # - # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, - # and migrated your data accordingly. - # - # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . - system.stateVersion = "25.05"; # Did you read the comment? - }; -} diff --git a/modules/hosts/nixos/emergent/default.nix b/modules/hosts/nixos/emergent/default.nix deleted file mode 100644 index afc3735..0000000 --- a/modules/hosts/nixos/emergent/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -# evs desktop -{ - inputs, - config, - ... -}: { - flake.nixosConfigurations.emergent = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config.flake.nixosModules.nixosModules - config.flake.nixosModules.emergentConfiguration - config.flake.nixosModules.emergentHardwareConfiguration - config.flake.nixosModules.emergentLegacyStorage - config.flake.nixosModules.emergentNvidiaDriver - ]; - specialArgs = { - inherit inputs; - syncthingConfiguration = inputs.self.syncthingConfiguration; - }; - }; -} diff --git a/modules/hosts/nixos/emergent/hardware-configuration.nix b/modules/hosts/nixos/emergent/hardware-configuration.nix deleted file mode 100644 index f88238b..0000000 --- a/modules/hosts/nixos/emergent/hardware-configuration.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - # Do not modify this file! It was generated by ‘nixos-generate-config’ - # and may be overwritten by future invocations. Please make changes - # to /etc/nixos/configuration.nix instead. - flake.nixosModules.emergentHardwareConfiguration = { - config, - lib, - pkgs, - modulesPath, - ... - }: { - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "wacom" "kvm" "kvm_amd"]; - boot.initrd.kernelModules = []; - boot.kernelModules = []; - boot.extraModulePackages = []; - - 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..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp42s0.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp4s0.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - }; -} diff --git a/modules/hosts/nixos/emergent/legacy-storage.nix b/modules/hosts/nixos/emergent/legacy-storage.nix deleted file mode 100644 index ae64933..0000000 --- a/modules/hosts/nixos/emergent/legacy-storage.nix +++ /dev/null @@ -1,53 +0,0 @@ -{...}: { - # Legacy storage configuration for emergent - # This file manually defines ZFS datasets matching the existing on-disk layout - # to allow incremental migration to the new storage module (generateBase = true). - # - # Current on-disk dataset layout: - # rpool/local/ - parent (canmount=off) - # rpool/local/system/nix - nix store - # rpool/local/system/root - root filesystem - # - # Migration plan: - # Phase 1: Rename datasets on disk (boot from live USB) - # zfs rename -p rpool/local/system/nix rpool/persist/local/nix - # zfs rename rpool/local rpool/persist/local - # # This moves: local/system/root -> persist/local/root (need to rename after) - # # Actually, since local/system/root needs to become persist/local/root: - # zfs rename rpool/persist/local/system/root rpool/persist/local/root - # zfs destroy rpool/persist/local/system # now empty - # # Recreate blank snapshot: - # zfs destroy rpool/persist/local/root@blank - # zfs snapshot rpool/persist/local/root@blank - # - # Phase 2: Delete this file, remove its import from default.nix, rebuild. - flake.nixosModules.emergentLegacyStorage = {...}: { - # Disable automatic base dataset generation so we can define them manually - storage.generateBase = false; - - # Manually define ZFS datasets matching the existing on-disk layout - storage.zfs.datasets = { - "local" = { - type = "zfs_fs"; - mount = null; - }; - "local/system/nix" = { - type = "zfs_fs"; - mount = "/nix"; - atime = "off"; - relatime = "off"; - snapshot = { - autoSnapshot = false; - }; - }; - "local/system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - autoSnapshot = true; - }; - }; - }; - }; -} diff --git a/modules/hosts/nixos/horizon/configuration.nix b/modules/hosts/nixos/horizon/configuration.nix deleted file mode 100644 index 9c29137..0000000 --- a/modules/hosts/nixos/horizon/configuration.nix +++ /dev/null @@ -1,106 +0,0 @@ -{...}: { - flake.nixosModules.horizonConfiguration = { - lib, - pkgs, - config, - inputs, - ... - }: { - imports = [ - inputs.nixos-hardware.nixosModules.framework-11th-gen-intel - ]; - - nixpkgs.config.allowUnfree = true; - - boot = { - initrd = { - availableKernelModules = ["usb_storage" "sd_mod"]; - }; - kernelModules = ["sg"]; - - # Bootloader. - loader = { - systemd-boot.enable = true; - efi.canTouchEfiVariables = true; - }; - }; - - host = { - users = { - leyla = { - isDesktopUser = true; - isTerminalUser = true; - isPrincipleUser = true; - }; - eve.isDesktopUser = true; - }; - - hardware = { - directAccess.enable = true; - }; - }; - - virtualisation.docker.enable = true; - - environment.systemPackages = with pkgs; [ - cachefilesd - webtoon-dl - android-tools - ]; - services.cachefilesd.enable = true; - - networking = { - networkmanager.enable = true; - hostName = "horizon"; # Define your hostname. - }; - powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; - - hardware = { - graphics.enable = true; - }; - - sops.secrets = { - "vpn-keys/tailscale-authkey/horizon" = { - sopsFile = "${inputs.secrets}/vpn-keys.yaml"; - }; - }; - - services = { - # sudo fprintd-enroll - fprintd = { - enable = true; - }; - # firmware update tool - fwupd = { - enable = true; - }; - tailscale = { - enable = true; - authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/horizon".path; - useRoutingFeatures = "client"; - }; - - syncthing.enable = true; - }; - - # Enable network-online.target for better network dependency handling - systemd.services.NetworkManager-wait-online.enable = true; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It's perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? - }; -} diff --git a/modules/hosts/nixos/horizon/default.nix b/modules/hosts/nixos/horizon/default.nix deleted file mode 100644 index b5887f3..0000000 --- a/modules/hosts/nixos/horizon/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -# leyla laptop -{ - inputs, - config, - ... -}: { - flake.nixosConfigurations.horizon = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config.flake.nixosModules.nixosModules - config.flake.nixosModules.horizonConfiguration - config.flake.nixosModules.horizonHardwareConfiguration - # config.flake.nixosModules.horizonNetworkMount - ]; - specialArgs = { - inherit inputs; - syncthingConfiguration = inputs.self.syncthingConfiguration; - }; - }; -} diff --git a/modules/hosts/nixos/horizon/hardware-configuration.nix b/modules/hosts/nixos/horizon/hardware-configuration.nix deleted file mode 100644 index edf08c6..0000000 --- a/modules/hosts/nixos/horizon/hardware-configuration.nix +++ /dev/null @@ -1,47 +0,0 @@ -{...}: { - # Do not modify this file! It was generated by 'nixos-generate-config' - # and may be overwritten by future invocations. Please make changes - # to /etc/nixos/configuration.nix instead. - flake.nixosModules.horizonHardwareConfiguration = { - config, - lib, - modulesPath, - ... - }: { - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "nvme"]; - boot.initrd.kernelModules = []; - boot.kernelModules = ["kvm-intel"]; - boot.extraModulePackages = []; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/866d422b-f816-4ad9-9846-791839cb9337"; - fsType = "ext4"; - }; - - "/boot" = { - device = "/dev/disk/by-uuid/E138-65B5"; - fsType = "vfat"; - }; - }; - - swapDevices = [ - {device = "/dev/disk/by-uuid/be98e952-a072-4c3a-8c12-69500b5a2fff";} - ]; - - # 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..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.tailscale0.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp170s0.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - }; -} diff --git a/modules/hosts/nixos/horizon/network-mount.nix b/modules/hosts/nixos/horizon/network-mount.nix deleted file mode 100644 index 345b26c..0000000 --- a/modules/hosts/nixos/horizon/network-mount.nix +++ /dev/null @@ -1,78 +0,0 @@ -{...}: { - flake.nixosModules.horizonNetworkMount = {...}: { - boot.supportedFilesystems = ["nfs"]; - - fileSystems = { - "/mnt/leyla_documents" = { - device = "defiant:/exports/leyla_documents"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "noatime" - "nofail" - "soft" - "intr" # Allow interruption of NFS calls - "timeo=30" # 3 second timeout (30 deciseconds) - "retrans=2" # Only 2 retries before giving up - "x-systemd.idle-timeout=300" # 5 minute idle timeout for mobile - "x-systemd.device-timeout=15" # 15 second device timeout - "bg" # Background mount - don't block boot - "fsc" # Enable caching - "_netdev" # Network device - wait for network - "x-systemd.requires=network-online.target" # Require network to be online - "x-systemd.after=network-online.target" # Start after network is online - "x-systemd.mount-timeout=30" # 30 second mount timeout - ]; - }; - - "/mnt/users_documents" = { - device = "defiant:/exports/users_documents"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "nofail" - "soft" - "intr" - "timeo=30" - "retrans=2" - "x-systemd.idle-timeout=300" - "x-systemd.device-timeout=15" - "bg" - "fsc" - "_netdev" - "x-systemd.requires=network-online.target" - "x-systemd.after=network-online.target" - "x-systemd.mount-timeout=30" - ]; - }; - - "/mnt/media" = { - device = "defiant:/exports/media"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "noatime" - "nofail" - "soft" - "intr" - "timeo=30" - "retrans=2" - "x-systemd.idle-timeout=300" - "x-systemd.device-timeout=15" - "bg" - # Mobile-optimized read settings - "rsize=8192" # Smaller read size for mobile - "wsize=8192" # Smaller write size for mobile - "fsc" - "_netdev" - "x-systemd.requires=network-online.target" - "x-systemd.after=network-online.target" - "x-systemd.mount-timeout=30" - ]; - }; - }; - }; -} diff --git a/modules/hosts/nixos/installer/configuration.nix b/modules/hosts/nixos/installer/configuration.nix deleted file mode 100644 index e8a3179..0000000 --- a/modules/hosts/nixos/installer/configuration.nix +++ /dev/null @@ -1,26 +0,0 @@ -{...}: { - flake.nixosModules.installer-configuration = { - lib, - pkgs, - modulesPath, - ... - }: { - imports = [(modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")]; - - systemd.services.sshd.wantedBy = pkgs.lib.mkForce ["multi-user.target"]; - users.users.root.openssh.authorizedKeys.keys = [ - "ssh-ed25519 AaAeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee username@host" - ]; - - isoImage.squashfsCompression = "gzip -Xcompression-level 1"; - - networking.hostName = "installer"; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - - host.users.leyla = { - isPrincipleUser = true; - isTerminalUser = true; - }; - }; -} diff --git a/modules/hosts/nixos/installer/default.nix b/modules/hosts/nixos/installer/default.nix deleted file mode 100644 index 0bd769c..0000000 --- a/modules/hosts/nixos/installer/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -# installer ISO -{ - inputs, - config, - ... -}: { - flake.nixosConfigurations.installer = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config.flake.nixosModules.nixosModules - config.flake.nixosModules.installer-configuration - ]; - specialArgs = { - inherit inputs; - syncthingConfiguration = inputs.self.syncthingConfiguration; - }; - }; -} diff --git a/modules/hosts/nixos/twilight/configuration.nix b/modules/hosts/nixos/twilight/configuration.nix deleted file mode 100644 index a343890..0000000 --- a/modules/hosts/nixos/twilight/configuration.nix +++ /dev/null @@ -1,85 +0,0 @@ -{...}: { - flake.nixosModules.twilightConfiguration = { - inputs, - config, - pkgs, - ... - }: { - nixpkgs.config.allowUnfree = true; - - boot.initrd.availableKernelModules = ["usb_storage"]; - boot.kernelModules = ["sg"]; - - boot.loader = { - systemd-boot.enable = true; - efi.canTouchEfiVariables = true; - }; - - sops.secrets = { - "vpn-keys/tailscale-authkey/twilight" = { - sopsFile = "${inputs.secrets}/vpn-keys.yaml"; - }; - }; - host = { - users = { - leyla = { - isDesktopUser = true; - isTerminalUser = true; - isPrincipleUser = true; - }; - eve.isDesktopUser = true; - }; - hardware = { - piperMouse.enable = true; - viaKeyboard.enable = true; - openRGB.enable = true; - graphicsAcceleration.enable = true; - directAccess.enable = true; - }; - }; - services = { - tailscale = { - enable = true; - authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/twilight".path; - useRoutingFeatures = "both"; - extraUpFlags = [ - "--advertise-exit-node" - "--advertise-routes=192.168.0.0/24" - ]; - extraSetFlags = [ - "--advertise-exit-node" - "--advertise-routes=192.168.0.0/24" - ]; - }; - - syncthing.enable = true; - }; - - # Enable network-online.target for better network dependency handling - systemd.services.NetworkManager-wait-online.enable = true; - - environment.systemPackages = with pkgs; [ - cachefilesd - ]; - hardware.steam-hardware.enable = true; # Provides udev rules for controller, HTC vive, and Valve Index - - networking = { - networkmanager.enable = true; - hostName = "twilight"; # Define your hostname. - }; - - # enabled virtualisation for docker - # virtualisation.docker.enable = true; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It's perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? - }; -} diff --git a/modules/hosts/nixos/twilight/default.nix b/modules/hosts/nixos/twilight/default.nix deleted file mode 100644 index 303c2cf..0000000 --- a/modules/hosts/nixos/twilight/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -# leyla desktop -{ - inputs, - config, - ... -}: { - flake.nixosConfigurations.twilight = inputs.nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - config.flake.nixosModules.nixosModules - config.flake.nixosModules.twilightConfiguration - config.flake.nixosModules.twilightHardwareConfiguration - config.flake.nixosModules.twilightNvidiaDriver - # config.flake.nixosModules.twilightNetworkMount - ]; - specialArgs = { - inherit inputs; - syncthingConfiguration = inputs.self.syncthingConfiguration; - }; - }; -} diff --git a/modules/hosts/nixos/twilight/hardware-configuration.nix b/modules/hosts/nixos/twilight/hardware-configuration.nix deleted file mode 100644 index cce7d45..0000000 --- a/modules/hosts/nixos/twilight/hardware-configuration.nix +++ /dev/null @@ -1,44 +0,0 @@ -{...}: { - # Do not modify this file! It was generated by 'nixos-generate-config' - # and may be overwritten by future invocations. Please make changes - # to /etc/nixos/configuration.nix instead. - flake.nixosModules.twilightHardwareConfiguration = { - config, - lib, - modulesPath, - ... - }: { - imports = [ - (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "sd_mod"]; - boot.initrd.kernelModules = []; - boot.kernelModules = ["kvm-amd"]; - boot.extraModulePackages = []; - - fileSystems = { - "/" = { - device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part2"; - fsType = "ext4"; - }; - - "/boot" = { - device = "/dev/disk/by-id/nvme-Samsung_SSD_980_500GB_S64ENJ0RA06463Z-part1"; - 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..useDHCP`. - networking.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - }; -} diff --git a/modules/hosts/nixos/twilight/network-mount.nix b/modules/hosts/nixos/twilight/network-mount.nix deleted file mode 100644 index 83432d8..0000000 --- a/modules/hosts/nixos/twilight/network-mount.nix +++ /dev/null @@ -1,74 +0,0 @@ -{...}: { - flake.nixosModules.twilightNetworkMount = {...}: { - boot.supportedFilesystems = ["nfs"]; - - fileSystems = { - "/mnt/leyla_documents" = { - device = "defiant:/exports/leyla_documents"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "noatime" - "nofail" - "soft" - "intr" # Allow interruption of NFS calls - "timeo=50" # 5 second timeout (50 deciseconds) - longer than mobile - "retrans=3" # 3 retries for desktop - "x-systemd.idle-timeout=600" # 10 minute idle timeout for desktop - "x-systemd.device-timeout=30" # 30 second device timeout - "bg" # Background mount - don't block boot - "fsc" # Enable caching - "_netdev" # Network device - wait for network - "x-systemd.requires=network-online.target" # Require network to be online - "x-systemd.after=network-online.target" # Start after network is online - ]; - }; - - "/mnt/users_documents" = { - device = "defiant:/exports/users_documents"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "nofail" - "soft" - "intr" - "timeo=50" - "retrans=3" - "x-systemd.idle-timeout=600" - "bg" - "fsc" - "_netdev" - "x-systemd.requires=network-online.target" - "x-systemd.after=network-online.target" - ]; - }; - - "/mnt/media" = { - device = "defiant:/exports/media"; - fsType = "nfs"; - options = [ - "x-systemd.automount" - "noauto" - "noatime" - "nofail" - "soft" - "intr" - "timeo=50" - "retrans=3" - "x-systemd.idle-timeout=600" - "x-systemd.device-timeout=30" - "bg" - # Desktop-optimized read settings - "rsize=32768" # Larger read size for desktop - "wsize=32768" # Larger write size for desktop - "fsc" - "_netdev" - "x-systemd.requires=network-online.target" - "x-systemd.after=network-online.target" - ]; - }; - }; - }; -} diff --git a/modules/hosts/nixos/twilight/nvidia-drivers.nix b/modules/hosts/nixos/twilight/nvidia-drivers.nix deleted file mode 100644 index ed69b05..0000000 --- a/modules/hosts/nixos/twilight/nvidia-drivers.nix +++ /dev/null @@ -1,50 +0,0 @@ -{...}: { - flake.nixosModules.twilightNvidiaDriver = {config, ...}: { - services = { - xserver = { - # Load nvidia driver for Xorg and Wayland - videoDrivers = ["nvidia"]; - }; - # Temporarily enable wayland to fix boot issue - # TODO: Investigate proper X11 session generation for gaming - displayManager.gdm.wayland = true; - }; - - hardware = { - # Enable OpenGL - graphics.enable = true; - - # install graphics drivers - nvidia = { - # Modesetting is required. - modesetting.enable = true; - - # Nvidia power management. Experimental, and can cause sleep/suspend to fail. - # Enable this if you have graphical corruption issues or application crashes after waking - # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead - # of just the bare essentials. - powerManagement.enable = true; - - # Fine-grained power management. Turns off GPU when not in use. - # Experimental and only works on modern Nvidia GPUs (Turing or newer). - powerManagement.finegrained = false; - - # Use the NVidia open source kernel module (not to be confused with the - # independent third-party "nouveau" open source driver). - # Support is limited to the Turing and later architectures. Full list of - # supported GPUs is at: - # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus - # Only available from driver 515.43.04+ - # Currently alpha-quality/buggy, so false is currently the recommended setting. - open = true; - - # Enable the Nvidia settings menu, - # accessible via `nvidia-settings`. - nvidiaSettings = true; - - # Optionally, you may need to select the appropriate driver version for your specific GPU. - package = config.boot.kernelPackages.nvidiaPackages.production; - }; - }; - }; -} diff --git a/modules/nixos-modules/ai.nix b/modules/nixos-modules/ai.nix new file mode 100644 index 0000000..d8cd63d --- /dev/null +++ b/modules/nixos-modules/ai.nix @@ -0,0 +1,46 @@ +{lib, ...}: { + options.host = { + ai = { + enable = lib.mkEnableOption "should we use AI on this machine"; + models = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + model = lib.mkOption { + type = lib.types.str; + }; + provider = lib.mkOption { + type = lib.types.str; + default = "ollama"; + }; + apiBase = lib.mkOption { + type = lib.types.str; + default = "http://localhost:11434"; + }; + roles = lib.mkOption { + type = lib.types.listOf (lib.types.enum [ + "chat" + "autocomplete" + "embed" + "rerank" + "edit" + "apply" + "summarize" + ]); + default = []; + }; + }; + })); + }; + default = {}; + }; + }; + + config = { + # TODO: configure ollama to download any modules listed in options.host.ai.models.{name}.model if options.host.ai.models.{name}.apiBase is localhost + # TODO: if we have any models that have a non localhost options.host.ai.models.{name}.apiBase then set services.ollama.enable to a lib.mkAfter true + }; +} diff --git a/modules/nixos-modules/default.nix b/modules/nixos-modules/default.nix new file mode 100644 index 0000000..34e041e --- /dev/null +++ b/modules/nixos-modules/default.nix @@ -0,0 +1,23 @@ +# this folder container modules that are for nixos only +{...}: { + imports = [ + ./home-manager + ./system.nix + ./hardware.nix + ./users.nix + ./desktop.nix + ./ssh.nix + ./i18n.nix + ./sync + ./ollama + ./ai.nix + ./tailscale + ./steam.nix + ./server + ./storage + ]; + + nixpkgs.config.permittedInsecurePackages = [ + "dotnet-sdk-6.0.428" + ]; +} diff --git a/modules/nixos-modules/desktop.nix b/modules/nixos-modules/desktop.nix new file mode 100644 index 0000000..66a2433 --- /dev/null +++ b/modules/nixos-modules/desktop.nix @@ -0,0 +1,84 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.host.desktop.enable = lib.mkEnableOption "should desktop configuration be enabled"; + + config = lib.mkMerge [ + { + host.desktop.enable = lib.mkDefault true; + } + (lib.mkIf config.host.desktop.enable { + environment.gnome.excludePackages = with pkgs; [ + xterm # default terminal + atomix # puzzle game + cheese # webcam tool + epiphany # web browser + geary # email reader + gedit # text editor + decibels # audio player + gnome-characters # character set viewer + gnome-music # music player + gnome-photos # photo viewer + gnome-logs # log viewer + gnome-maps # map viewer + gnome-tour # welcome tour + hitori # sudoku game + iagno # go game + tali # poker game + yelp # help viewer + ]; + services = { + # Enable CUPS to print documents. + printing = { + enable = true; + drivers = [ + pkgs.hplip + pkgs.gutenprint + pkgs.gutenprintBin + ]; + }; + + xserver = { + # Enable the X11 windowing system. + enable = true; + + # Get rid of xTerm + desktopManager.xterm.enable = false; + excludePackages = with pkgs; [ + xterm + ]; + }; + + # Enable the GNOME Desktop Environment. + displayManager.gdm.enable = true; + desktopManager.gnome.enable = true; + + pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + + # If you want to use JACK applications, uncomment this + #jack.enable = true; + + # use the example session manager (no others are packaged yet so this is enabled by default, + # no need to redefine it in your config for now) + #media-session.enable = true; + }; + automatic-timezoned = { + enable = true; + }; + + # Enable sound with pipewire. + pulseaudio.enable = false; + }; + + # enable RealtimeKit for pulse audio + security.rtkit.enable = true; + }) + ]; +} diff --git a/modules/nixos-modules/hardware.nix b/modules/nixos-modules/hardware.nix new file mode 100644 index 0000000..07e6fa8 --- /dev/null +++ b/modules/nixos-modules/hardware.nix @@ -0,0 +1,34 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.host.hardware = { + piperMouse = { + enable = lib.mkEnableOption "host has a piper mouse"; + }; + viaKeyboard = { + enable = lib.mkEnableOption "host has a via keyboard"; + }; + openRGB = { + enable = lib.mkEnableOption "host has open rgb hardware"; + }; + graphicsAcceleration = { + enable = lib.mkEnableOption "host has a gpu for graphical acceleration"; + }; + directAccess = { + enable = lib.mkEnableOption "can a host be used on its own"; + }; + }; + config = lib.mkMerge [ + (lib.mkIf config.host.hardware.piperMouse.enable { + services.ratbagd.enable = true; + }) + (lib.mkIf config.host.hardware.viaKeyboard.enable { + hardware.keyboard.qmk.enable = true; + + services.udev.packages = [pkgs.via]; + }) + ]; +} diff --git a/modules/nixos-modules/home-manager/default.nix b/modules/nixos-modules/home-manager/default.nix new file mode 100644 index 0000000..10f86c7 --- /dev/null +++ b/modules/nixos-modules/home-manager/default.nix @@ -0,0 +1,9 @@ +# modules in this folder are to adapt home-manager modules configs to nixos-module configs +{...}: { + imports = [ + ./flipperzero.nix + ./i18n.nix + ./openssh.nix + ./steam.nix + ]; +} diff --git a/modules/nixos-modules/home-manager/flipperzero.nix b/modules/nixos-modules/home-manager/flipperzero.nix new file mode 100644 index 0000000..6c94773 --- /dev/null +++ b/modules/nixos-modules/home-manager/flipperzero.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: let + home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; +in { + hardware.flipperzero.enable = lib.lists.any (home-user: home-user.hardware.flipperzero.enable) home-users; +} diff --git a/modules/nixos-modules/home-manager/i18n.nix b/modules/nixos-modules/home-manager/i18n.nix new file mode 100644 index 0000000..78b86fa --- /dev/null +++ b/modules/nixos-modules/home-manager/i18n.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: let + home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; +in { + config = { + i18n.supportedLocales = + lib.unique + (builtins.map (l: (lib.replaceStrings ["utf8" "utf-8" "UTF8"] ["UTF-8" "UTF-8" "UTF-8"] l) + "/UTF-8") ( + [ + "C.UTF-8" + "en_US.UTF-8" + config.i18n.defaultLocale + ] + ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings)) + ++ ( + map (user-config: user-config.i18n.defaultLocale) home-users + ) + ++ (lib.lists.flatten ( + map (user-config: lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") user-config.i18n.extraLocaleSettings)) home-users + )) + )); + }; +} diff --git a/modules/nixos-modules/home-manager/openssh.nix b/modules/nixos-modules/home-manager/openssh.nix new file mode 100644 index 0000000..31a785f --- /dev/null +++ b/modules/nixos-modules/home-manager/openssh.nix @@ -0,0 +1,11 @@ +{ + config, + lib, + ... +}: { + users.users = + lib.attrsets.mapAttrs (name: value: { + openssh.authorizedKeys.keys = value.programs.openssh.authorizedKeys; + }) + config.home-manager.users; +} diff --git a/modules/nixos-modules/home-manager/steam.nix b/modules/nixos-modules/home-manager/steam.nix new file mode 100644 index 0000000..d151bca --- /dev/null +++ b/modules/nixos-modules/home-manager/steam.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + ... +}: let + setupSteam = + lib.lists.any + (value: value) + (lib.attrsets.mapAttrsToList (name: value: value.programs.steam.enable) config.home-manager.users); +in { + config = lib.mkIf setupSteam { + programs.steam = { + enable = true; + # TODO: figure out how to not install steam here + # package = lib.mkDefault pkgs.emptyFile; + }; + }; +} diff --git a/modules/nixos-modules/i18n.nix b/modules/nixos-modules/i18n.nix new file mode 100644 index 0000000..eada12c --- /dev/null +++ b/modules/nixos-modules/i18n.nix @@ -0,0 +1,3 @@ +{...}: { + i18n.defaultLocale = "en_IE.UTF-8"; +} diff --git a/modules/nixos-modules/ollama/default.nix b/modules/nixos-modules/ollama/default.nix new file mode 100644 index 0000000..896526a --- /dev/null +++ b/modules/nixos-modules/ollama/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./ollama.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/ollama/ollama.nix b/modules/nixos-modules/ollama/ollama.nix new file mode 100644 index 0000000..dc7cdd9 --- /dev/null +++ b/modules/nixos-modules/ollama/ollama.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: { + options = { + services.ollama.exposePort = lib.mkEnableOption "should we expose ollama on tailscale"; + }; + + config = lib.mkIf config.services.ollama.enable ( + lib.mkMerge [ + { + services.ollama = { + # TODO: these should match whats set in the users file + group = "ollama"; + user = "ollama"; + }; + } + (lib.mkIf config.services.ollama.exposePort (let + ports = [ + config.services.ollama.port + ]; + in { + services.ollama.host = "0.0.0.0"; + networking.firewall.interfaces.${config.services.tailscale.interfaceName} = { + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + })) + ] + ); +} diff --git a/modules/nixos-modules/ollama/storage.nix b/modules/nixos-modules/ollama/storage.nix new file mode 100644 index 0000000..6ab0fc8 --- /dev/null +++ b/modules/nixos-modules/ollama/storage.nix @@ -0,0 +1,37 @@ +{ + config, + lib, + ... +}: { + options = { + services.ollama.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.ollama.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf (config.services.ollama.enable) { + storage.datasets.replicate."system/root" = { + directories."/var/lib/private/ollama" = lib.mkIf config.services.ollama.impermanence.enable { + enable = true; + owner.name = config.services.ollama.user; + group.name = config.services.ollama.group; + owner.permissions = { + read = true; + write = true; + execute = false; + }; + group.permissions = { + read = false; + write = false; + execute = false; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/actual/actual.nix b/modules/nixos-modules/server/actual/actual.nix new file mode 100644 index 0000000..4cca449 --- /dev/null +++ b/modules/nixos-modules/server/actual/actual.nix @@ -0,0 +1,24 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + dataDirectory = const.dataDirectory; +in { + options.services.actual = { + port = lib.mkOption { + type = lib.types.port; + description = "The port to listen on"; + default = 5006; + }; + }; + config = lib.mkIf config.services.actual.enable { + services.actual = { + settings = { + port = config.services.actual.port; + dataDir = dataDirectory; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/actual/const.nix b/modules/nixos-modules/server/actual/const.nix new file mode 100644 index 0000000..14b715e --- /dev/null +++ b/modules/nixos-modules/server/actual/const.nix @@ -0,0 +1,3 @@ +{ + dataDirectory = "/var/lib/private/actual"; +} diff --git a/modules/nixos-modules/server/actual/default.nix b/modules/nixos-modules/server/actual/default.nix new file mode 100644 index 0000000..99778af --- /dev/null +++ b/modules/nixos-modules/server/actual/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./actual.nix + ./proxy.nix + ./fail2ban.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/actual/fail2ban.nix b/modules/nixos-modules/server/actual/fail2ban.nix new file mode 100644 index 0000000..3ad754e --- /dev/null +++ b/modules/nixos-modules/server/actual/fail2ban.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.services.actual.enable && config.services.fail2ban.enable) { + # TODO: configuration for fail2ban for actual + }; +} diff --git a/modules/nixos-modules/server/actual/proxy.nix b/modules/nixos-modules/server/actual/proxy.nix new file mode 100644 index 0000000..9d37574 --- /dev/null +++ b/modules/nixos-modules/server/actual/proxy.nix @@ -0,0 +1,34 @@ +{ + lib, + config, + ... +}: { + options.services.actual = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that actual will be hosted at"; + default = "actual.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for actual"; + default = []; + }; + reverseProxy.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.services.reverseProxy.enable; + }; + }; + + config = lib.mkIf config.services.actual.reverseProxy.enable { + services.reverseProxy.services.actual = { + target = "http://localhost:${toString config.services.actual.settings.port}"; + domain = config.services.actual.domain; + extraDomains = config.services.actual.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/actual/storage.nix b/modules/nixos-modules/server/actual/storage.nix new file mode 100644 index 0000000..d6b904e --- /dev/null +++ b/modules/nixos-modules/server/actual/storage.nix @@ -0,0 +1,22 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + dataDirectory = const.dataDirectory; +in { + options.services.actual.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.actual.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDirectory}" = lib.mkIf config.services.actual.impermanence.enable { + owner.name = "actual"; + group.name = "actual"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/bazarr/default.nix b/modules/nixos-modules/server/bazarr/default.nix new file mode 100644 index 0000000..cb2a5f0 --- /dev/null +++ b/modules/nixos-modules/server/bazarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/bazarr/storage.nix b/modules/nixos-modules/server/bazarr/storage.nix new file mode 100644 index 0000000..a243d4c --- /dev/null +++ b/modules/nixos-modules/server/bazarr/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + bazarr_data_directory = "/var/lib/bazarr"; +in { + options.services.bazarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.bazarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.bazarr.enable { + storage.datasets.replicate."system/root" = { + directories."${bazarr_data_directory}" = lib.mkIf config.services.bazarr.impermanence.enable { + owner.name = "bazarr"; + group.name = "bazarr"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/crab-hole/crab-hole.nix b/modules/nixos-modules/server/crab-hole/crab-hole.nix new file mode 100644 index 0000000..d76323a --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/crab-hole.nix @@ -0,0 +1,193 @@ +{ + config, + lib, + ... +}: let + cfg = config.services.crab-hole; +in { + options.services.crab-hole = { + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Port for the crab-hole API to listen on."; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to open the firewall for the crab-hole API port."; + }; + + listen = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = "Address for the crab-hole API to listen on."; + }; + + show_doc = lib.mkEnableOption "OpenAPI documentation (loads content from third party websites)"; + + downstreams = { + host = { + enable = lib.mkEnableOption "host downstream DNS server accessible from network on all interfaces"; + port = lib.mkOption { + type = lib.types.port; + default = 53; + description = "Port for the host downstream DNS server to listen on."; + }; + openFirewall = lib.mkEnableOption "automatic port forwarding for the host downstream"; + disableSystemdResolved = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically disable systemd-resolved when using port 53. Set to false if you want to handle the conflict manually."; + }; + }; + }; + + extraDownstreams = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + protocol = lib.mkOption { + type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"]; + description = "Protocol for the downstream server."; + }; + + listen = lib.mkOption { + type = lib.types.str; + description = "Address to listen on for downstream connections."; + }; + + port = lib.mkOption { + type = lib.types.port; + description = "Port to listen on for downstream connections."; + }; + }; + }); + default = []; + description = "List of additional downstream DNS server configurations."; + }; + + upstreams = { + cloudFlare = { + enable = lib.mkEnableOption "Cloudflare DNS over TLS upstream servers (1.1.1.1 and 1.0.0.1)"; + }; + }; + + extraUpstreams = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + socket_addr = lib.mkOption { + type = lib.types.str; + description = "Socket address of the upstream DNS server (e.g., \"1.1.1.1:853\" or \"[2606:4700:4700::1111]:853\")."; + }; + + protocol = lib.mkOption { + type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"]; + description = "Protocol to use for upstream DNS queries."; + }; + }; + }); + default = []; + description = "List of additional upstream DNS server configurations."; + }; + + blocklists = { + ad_malware = { + enable = lib.mkEnableOption "Host file for blocking ads and malware"; + url = lib.mkOption { + type = lib.types.str; + default = "http://sbc.io/hosts/hosts"; + description = "URL of the ad and malware blocklist host file"; + }; + }; + }; + + extraBlocklists = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Additional blocklist URLs to be added to the configuration"; + }; + }; + + config = lib.mkIf cfg.enable { + # Assertions for proper configuration + assertions = [ + { + assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && config.services.resolved.enable && cfg.downstreams.host.disableSystemdResolved); + message = "crab-hole host downstream cannot use port 53 while systemd-resolved is enabled. Either disable systemd-resolved or use a different port."; + } + { + assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && !cfg.downstreams.host.disableSystemdResolved && config.services.resolved.enable); + message = "crab-hole host downstream is configured to use port 53 but systemd-resolved is still enabled and disableSystemdResolved is false. Set disableSystemdResolved = true or manually disable systemd-resolved."; + } + ]; + + # Automatically disable systemd-resolved if using port 53 + services.resolved.enable = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkForce false); + + # Configure DNS nameservers when disabling systemd-resolved + networking.nameservers = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkDefault ["127.0.0.1" "1.1.1.1" "8.8.8.8"]); + + services.crab-hole.settings = lib.mkMerge [ + { + api = { + port = cfg.port; + listen = cfg.listen; + show_doc = cfg.show_doc; + }; + downstream = cfg.extraDownstreams; + upstream.name_servers = cfg.extraUpstreams; + blocklist.lists = cfg.extraBlocklists; + } + (lib.mkIf cfg.blocklists.ad_malware.enable { + blocklist.lists = [cfg.blocklists.ad_malware.url]; + }) + (lib.mkIf cfg.downstreams.host.enable { + downstream = [ + { + protocol = "udp"; + listen = "0.0.0.0"; + port = cfg.downstreams.host.port; + } + ]; + }) + (lib.mkIf cfg.upstreams.cloudFlare.enable { + upstream.name_servers = [ + { + socket_addr = "1.1.1.1:853"; + protocol = "tls"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + { + socket_addr = "1.0.0.1:853"; + protocol = "tls"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + { + socket_addr = "[2606:4700:4700::1111]:853"; + protocol = "tls"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + { + socket_addr = "[2606:4700:4700::1001]:853"; + protocol = "tls"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + ]; + }) + ]; + + # Open firewall if requested + networking.firewall = lib.mkMerge [ + (lib.mkIf cfg.openFirewall { + allowedTCPPorts = [cfg.port]; + }) + (lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.openFirewall) { + allowedUDPPorts = [cfg.downstreams.host.port]; + }) + ]; + }; +} diff --git a/modules/nixos-modules/server/crab-hole/default.nix b/modules/nixos-modules/server/crab-hole/default.nix new file mode 100644 index 0000000..9f990c5 --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./crab-hole.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/crab-hole/storage.nix b/modules/nixos-modules/server/crab-hole/storage.nix new file mode 100644 index 0000000..827fb25 --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + workingDirectory = "/var/lib/private/crab-hole"; +in { + options.services.crab-hole.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.crab-hole.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.crab-hole.enable { + storage.datasets.replicate."system/root" = { + directories."${workingDirectory}" = lib.mkIf config.services.crab-hole.impermanence.enable { + owner.name = "crab-hole"; + group.name = "crab-hole"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/default.nix b/modules/nixos-modules/server/default.nix new file mode 100644 index 0000000..2b33089 --- /dev/null +++ b/modules/nixos-modules/server/default.nix @@ -0,0 +1,26 @@ +{...}: { + imports = [ + ./reverseProxy + ./fail2ban + ./postgres + ./network_storage + + ./actual + ./bazarr + ./crab-hole + ./flaresolverr + ./forgejo + ./home-assistant + ./immich + ./jackett + ./jellyfin + ./lidarr + ./panoramax + ./paperless + ./qbittorent + ./radarr + ./searx + ./sonarr + ./wyoming.nix + ]; +} diff --git a/modules/nixos-modules/server/fail2ban/default.nix b/modules/nixos-modules/server/fail2ban/default.nix new file mode 100644 index 0000000..84a46d4 --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./fail2ban.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/fail2ban/fail2ban.nix b/modules/nixos-modules/server/fail2ban/fail2ban.nix new file mode 100644 index 0000000..261c68f --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/fail2ban.nix @@ -0,0 +1,51 @@ +{ + lib, + pkgs, + config, + ... +}: { + config = lib.mkIf config.services.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/nginx.local".text = lib.mkIf config.services.nginx.enable ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = "limiting requests, excess:.* by zone.*client: " + '') + ); + }; + + services.fail2ban = { + maxretry = 5; + ignoreIP = [ + # Whitelist local networks + "10.0.0.0/8" + "172.16.0.0/12" + "192.168.0.0/16" + + # tail scale tailnet + "100.64.0.0/10" + "fd7a:115c:a1e0::/48" + ]; + bantime = "24h"; # Ban IPs for one day on the first ban + bantime-increment = { + enable = true; # Enable increment of bantime after each violation + formula = "ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)"; + maxtime = "168h"; # Do not ban for more than 1 week + overalljails = true; # Calculate the ban time based on all the violations + }; + jails = { + nginx-iptables.settings = lib.mkIf config.services.nginx.enable { + enabled = true; + filter = "nginx"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + # TODO; figure out if there is any fail2ban things we can do on searx + # searx-iptables.settings = lib.mkIf config.services.searx.enable {}; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/fail2ban/storage.nix b/modules/nixos-modules/server/fail2ban/storage.nix new file mode 100644 index 0000000..1ef02c7 --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/storage.nix @@ -0,0 +1,22 @@ +{ + lib, + config, + ... +}: let + dataFolder = "/var/lib/fail2ban"; + dataFile = "fail2ban.sqlite3"; +in { + options.services.fail2ban.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.fail2ban.enable { + storage.datasets.replicate."system/root" = { + directories."${dataFolder}" = lib.mkIf config.services.fail2ban.impermanence.enable { + owner.name = "fail2ban"; + group.name = "fail2ban"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/flaresolverr/default.nix b/modules/nixos-modules/server/flaresolverr/default.nix new file mode 100644 index 0000000..cb2a5f0 --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/flaresolverr/storage.nix b/modules/nixos-modules/server/flaresolverr/storage.nix new file mode 100644 index 0000000..919318c --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/storage.nix @@ -0,0 +1,19 @@ +{ + lib, + config, + ... +}: { + options.services.flaresolverr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.flaresolverr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.flaresolverr.enable { + storage.datasets.replicate."system/root" = { + directories."/var/lib/flaresolverr" = lib.mkIf config.services.flaresolverr.impermanence.enable { + owner.name = "flaresolverr"; + group.name = "flaresolverr"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/const.nix b/modules/nixos-modules/server/forgejo/const.nix new file mode 100644 index 0000000..10e3974 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/const.nix @@ -0,0 +1,4 @@ +{ + httpPort = 8081; + sshPort = 22222; +} diff --git a/modules/nixos-modules/server/forgejo/database.nix b/modules/nixos-modules/server/forgejo/database.nix new file mode 100644 index 0000000..bb8781c --- /dev/null +++ b/modules/nixos-modules/server/forgejo/database.nix @@ -0,0 +1,32 @@ +{ + lib, + config, + ... +}: let + usingPostgres = config.services.forgejo.database.type == "postgres"; +in { + config = lib.mkIf config.services.forgejo.enable { + assertions = [ + { + assertion = !usingPostgres || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when Forgejo database type is postgres"; + } + { + assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (db: db == "forgejo") config.services.postgresql.ensureDatabases); + message = "Forgejo built-in database creation failed - expected 'forgejo' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (user: user.name == "forgejo") config.services.postgresql.ensureUsers); + message = "Forgejo built-in user creation failed - expected user 'forgejo' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + services.forgejo.database.createDatabase = lib.mkDefault usingPostgres; + + systemd.services.forgejo = lib.mkIf usingPostgres { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/default.nix b/modules/nixos-modules/server/forgejo/default.nix new file mode 100644 index 0000000..c990e57 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./forgejo.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/forgejo/fail2ban.nix b/modules/nixos-modules/server/forgejo/fail2ban.nix new file mode 100644 index 0000000..dfe221a --- /dev/null +++ b/modules/nixos-modules/server/forgejo/fail2ban.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.services.forgejo = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.services.fail2ban.enable; + }; + }; + }; + + config = lib.mkIf config.services.forgejo.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/forgejo.local".text = lib.mkIf config.services.forgejo.enable ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = ".*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from " + '') + ); + }; + + services.fail2ban = { + jails = { + forgejo-iptables.settings = lib.mkIf config.services.forgejo.enable { + enabled = true; + filter = "forgejo"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.forgejo.settings.log.ROOT_PATH}/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/forgejo.nix b/modules/nixos-modules/server/forgejo/forgejo.nix new file mode 100644 index 0000000..70d3087 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/forgejo.nix @@ -0,0 +1,46 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + httpPort = const.httpPort; + sshPort = const.sshPort; + db_user = "forgejo"; +in { + config = lib.mkIf config.services.forgejo.enable { + assertions = [ + { + assertion = config.services.forgejo.settings.server.BUILTIN_SSH_SERVER_USER == config.users.users.git.name; + message = "Forgejo BUILTIN_SSH_SERVER_USER hardcoded value does not match expected git user name"; + } + ]; + + services.forgejo = { + database = { + type = "postgres"; + socket = "/run/postgresql"; + }; + lfs.enable = true; + settings = { + server = { + DOMAIN = config.services.forgejo.reverseProxy.domain; + HTTP_PORT = httpPort; + START_SSH_SERVER = true; + SSH_LISTEN_PORT = sshPort; + SSH_PORT = 22; + BUILTIN_SSH_SERVER_USER = "git"; + ROOT_URL = "https://git.jan-leila.com"; + }; + service = { + DISABLE_REGISTRATION = true; + }; + database = { + DB_TYPE = "postgres"; + NAME = db_user; + USER = db_user; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/proxy.nix b/modules/nixos-modules/server/forgejo/proxy.nix new file mode 100644 index 0000000..c2d3131 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/proxy.nix @@ -0,0 +1,43 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + httpPort = const.httpPort; +in { + options.services.forgejo = { + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.services.reverseProxy.enable; + }; + domain = lib.mkOption { + type = lib.types.str; + description = "domain that forgejo will be hosted at"; + default = "git.jan-leila.com"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for forgejo"; + default = []; + }; + }; + }; + + config = lib.mkIf config.services.forgejo.reverseProxy.enable { + services.reverseProxy.services.forgejo = { + target = "http://localhost:${toString httpPort}"; + domain = config.services.forgejo.reverseProxy.domain; + extraDomains = config.services.forgejo.reverseProxy.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + + networking.firewall.allowedTCPPorts = [ + config.services.forgejo.settings.server.SSH_LISTEN_PORT + ]; + }; +} diff --git a/modules/nixos-modules/server/forgejo/storage.nix b/modules/nixos-modules/server/forgejo/storage.nix new file mode 100644 index 0000000..da30ed9 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + stateDir = "/var/lib/forgejo"; +in { + options.services.forgejo.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.forgejo.enable { + storage.datasets.replicate."system/root" = { + directories."${stateDir}" = lib.mkIf config.services.forgejo.impermanence.enable { + owner.name = "forgejo"; + group.name = "forgejo"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/database.nix b/modules/nixos-modules/server/home-assistant/database.nix new file mode 100644 index 0000000..f1927ed --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/database.nix @@ -0,0 +1,53 @@ +{ + lib, + config, + ... +}: { + options.services.home-assistant = { + postgres = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use PostgreSQL instead of SQLite"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "hass"; + description = "Database user name"; + }; + database = lib.mkOption { + type = lib.types.str; + default = "hass"; + description = "Database name"; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.enable { + assertions = [ + { + assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; + } + ]; + + services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + enable = true; + user = config.services.home-assistant.postgres.user; + database = config.services.home-assistant.postgres.database; + }; + + services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + extraPackages = python3Packages: + with python3Packages; [ + psycopg2 + ]; + }; + + systemd.services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/default.nix b/modules/nixos-modules/server/home-assistant/default.nix new file mode 100644 index 0000000..d213964 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/default.nix @@ -0,0 +1,10 @@ +{ + imports = [ + ./home-assistant.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./storage.nix + ./extensions + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/default.nix b/modules/nixos-modules/server/home-assistant/extensions/default.nix new file mode 100644 index 0000000..9ef84a3 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/default.nix @@ -0,0 +1,12 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + ./sonos.nix + ./jellyfin.nix + ./wyoming.nix + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix b/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix new file mode 100644 index 0000000..29af274 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.jellyfin.enable) { + services.home-assistant.extraComponents = ["jellyfin"]; + # TODO: configure port, address, and login information here +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/sonos.nix b/modules/nixos-modules/server/home-assistant/extensions/sonos.nix new file mode 100644 index 0000000..c70649f --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/sonos.nix @@ -0,0 +1,11 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.sonos.enable) { + services.home-assistant.extraComponents = ["sonos"]; + networking.firewall.allowedTCPPorts = [ + config.services.home-assistant.extensions.sonos.port + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix b/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix new file mode 100644 index 0000000..840d360 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.wyoming.enable) { + services.home-assistant.extraComponents = ["wyoming"]; + services.wyoming.enable = true; +} diff --git a/modules/nixos-modules/server/home-assistant/fail2ban.nix b/modules/nixos-modules/server/home-assistant/fail2ban.nix new file mode 100644 index 0000000..25194ef --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/fail2ban.nix @@ -0,0 +1,49 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.services.home-assistant = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.services.home-assistant.enable; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/hass.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [INCLUDES] + before = common.conf + + [Definition] + failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from .*$ + + ignoreregex = + + [Init] + datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S + '') + ); + }; + + services.fail2ban = { + jails = { + home-assistant-iptables.settings = { + enabled = true; + filter = "hass"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.home-assistant.configDir}/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/home-assistant.nix b/modules/nixos-modules/server/home-assistant/home-assistant.nix new file mode 100644 index 0000000..fa58d5e --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/home-assistant.nix @@ -0,0 +1,104 @@ +{ + lib, + config, + ... +}: { + options.services.home-assistant = { + database = lib.mkOption { + type = lib.types.enum [ + "builtin" + "postgres" + ]; + description = "what database do we want to use"; + default = "builtin"; + }; + + extensions = { + sonos = { + enable = lib.mkEnableOption "enable the sonos plugin"; + port = lib.mkOption { + type = lib.types.int; + default = 1400; + description = "what port to use for sonos discovery"; + }; + }; + jellyfin = { + enable = lib.mkEnableOption "enable the jellyfin plugin"; + }; + wyoming = { + enable = lib.mkEnableOption "enable wyoming"; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.enable (lib.mkMerge [ + { + services.home-assistant = { + configDir = "/var/lib/hass"; + extraComponents = [ + "default_config" + "esphome" + "met" + "radio_browser" + "isal" + "zha" + "webostv" + "tailscale" + "syncthing" + "analytics_insights" + "unifi" + "openweathermap" + "ollama" + "mobile_app" + "logbook" + "ssdp" + "usb" + "webhook" + "bluetooth" + "dhcp" + "energy" + "history" + "backup" + "assist_pipeline" + "conversation" + "sun" + "zeroconf" + "cpuspeed" + ]; + config = { + http = { + server_port = 8123; + use_x_forwarded_for = true; + trusted_proxies = ["127.0.0.1" "::1"]; + ip_ban_enabled = true; + login_attempts_threshold = 10; + }; + homeassistant = { + external_url = "https://${config.services.home-assistant.domain}"; + # internal_url = "http://192.168.1.2:8123"; + }; + recorder.db_url = "postgresql://@/${config.services.home-assistant.configDir}"; + "automation manual" = []; + "automation ui" = "!include automations.yaml"; + mobile_app = {}; + }; + extraPackages = python3Packages: + with python3Packages; [ + hassil + numpy + gtts + ]; + }; + + # TODO: configure /var/lib/hass/secrets.yaml via sops + + networking.firewall.allowedUDPPorts = [ + 1900 + ]; + + systemd.tmpfiles.rules = [ + "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" + ]; + } + ]); +} diff --git a/modules/nixos-modules/server/home-assistant/proxy.nix b/modules/nixos-modules/server/home-assistant/proxy.nix new file mode 100644 index 0000000..b756459 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/proxy.nix @@ -0,0 +1,43 @@ +{ + lib, + config, + ... +}: { + options.services.home-assistant = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that home-assistant will be hosted at"; + default = "home-assistant.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for home-assistant"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.reverseProxy.enable && config.services.home-assistant.enable; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.reverseProxy.enable { + services.reverseProxy.services.home-assistant = { + target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; + domain = config.services.home-assistant.domain; + extraDomains = config.services.home-assistant.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 90; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/storage.nix b/modules/nixos-modules/server/home-assistant/storage.nix new file mode 100644 index 0000000..60e5085 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + configDir = "/var/lib/hass"; +in { + options.services.home-assistant.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.home-assistant.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.home-assistant.enable { + storage.datasets.replicate."system/root" = { + directories."${configDir}" = lib.mkIf config.services.home-assistant.impermanence.enable { + owner.name = "hass"; + group.name = "hass"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/database.nix b/modules/nixos-modules/server/immich/database.nix new file mode 100644 index 0000000..52af51e --- /dev/null +++ b/modules/nixos-modules/server/immich/database.nix @@ -0,0 +1,30 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.services.immich.enable { + assertions = [ + { + assertion = !config.services.immich.database.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Immich"; + } + { + assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (db: db == "immich") config.services.postgresql.ensureDatabases); + message = "Immich built-in database creation failed - expected 'immich' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers); + message = "Immich built-in user creation failed - expected user 'immich' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + # Note: Immich has built-in database creation via services.immich.database.createDB we only add the systemd dependency + + systemd.services.immich-server = lib.mkIf config.services.immich.database.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/default.nix b/modules/nixos-modules/server/immich/default.nix new file mode 100644 index 0000000..75ae2fd --- /dev/null +++ b/modules/nixos-modules/server/immich/default.nix @@ -0,0 +1,20 @@ +{...}: { + imports = [ + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./storage.nix + ]; + + # NOTE: This shouldn't be needed now that we are out of testing + # config = lib.mkIf config.services.immich.enable { + # networking.firewall.interfaces.${config.services.tailscale.interfaceName} = { + # allowedUDPPorts = [ + # config.services.immich.port + # ]; + # allowedTCPPorts = [ + # config.services.immich.port + # ]; + # }; + # }; +} diff --git a/modules/nixos-modules/server/immich/fail2ban.nix b/modules/nixos-modules/server/immich/fail2ban.nix new file mode 100644 index 0000000..21593e7 --- /dev/null +++ b/modules/nixos-modules/server/immich/fail2ban.nix @@ -0,0 +1,35 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.services.immich = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.services.immich.enable; + }; + }; + }; + + config = lib.mkIf config.services.immich.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/immich.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = immich-server.*Failed login attempt for user.+from ip address\s? + journalmatch = CONTAINER_TAG=immich-server + ''); + }; + + services.fail2ban = { + jails = { + immich-iptables.settings = { + enabled = true; + filter = "immich"; + backend = "systemd"; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/proxy.nix b/modules/nixos-modules/server/immich/proxy.nix new file mode 100644 index 0000000..9c8c165 --- /dev/null +++ b/modules/nixos-modules/server/immich/proxy.nix @@ -0,0 +1,44 @@ +{ + lib, + config, + ... +}: { + options.services.immich = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that immich will be hosted at"; + default = "immich.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for immich"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.immich.reverseProxy.enable { + services.reverseProxy.services.immich = { + target = "http://localhost:${toString config.services.immich.port}"; + domain = config.services.immich.domain; + extraDomains = config.services.immich.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 50000; + + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 600; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/storage.nix b/modules/nixos-modules/server/immich/storage.nix new file mode 100644 index 0000000..de24329 --- /dev/null +++ b/modules/nixos-modules/server/immich/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + mediaLocation = "/var/lib/immich"; +in { + options.services.immich.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.immich.enable { + storage.datasets.replicate."system/root" = { + directories."${mediaLocation}" = lib.mkIf config.services.immich.impermanence.enable { + owner.name = "immich"; + group.name = "immich"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jackett/default.nix b/modules/nixos-modules/server/jackett/default.nix new file mode 100644 index 0000000..5043814 --- /dev/null +++ b/modules/nixos-modules/server/jackett/default.nix @@ -0,0 +1,17 @@ +{...}: { + imports = [ + ./storage.nix + ]; + + config = { + nixpkgs.overlays = [ + # Disable jackett tests due to date-related test failures + # (ParseDateTimeGoLangTest expects 2024-09-14 but gets 2025-09-14 due to year rollover logic) + (final: prev: { + jackett = prev.jackett.overrideAttrs (oldAttrs: { + doCheck = false; + }); + }) + ]; + }; +} diff --git a/modules/nixos-modules/server/jackett/storage.nix b/modules/nixos-modules/server/jackett/storage.nix new file mode 100644 index 0000000..5f202e6 --- /dev/null +++ b/modules/nixos-modules/server/jackett/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + jackett_data_directory = "/var/lib/jackett/.config/Jackett"; +in { + options.services.jackett.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jackett.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.jackett.enable { + storage.datasets.replicate."system/root" = { + directories."${jackett_data_directory}" = lib.mkIf config.services.jackett.impermanence.enable { + owner.name = "jackett"; + group.name = "jackett"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/default.nix b/modules/nixos-modules/server/jellyfin/default.nix new file mode 100644 index 0000000..4770ae1 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./jellyfin.nix + ./proxy.nix + ./fail2ban.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/jellyfin/fail2ban.nix b/modules/nixos-modules/server/jellyfin/fail2ban.nix new file mode 100644 index 0000000..ba8d8ba --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/fail2ban.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: { + config = lib.mkIf (config.services.jellyfin.enable && config.services.fail2ban.enable) { + environment.etc = { + "fail2ban/filter.d/jellyfin.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = "^.*Authentication request for .* has been denied \\\\\\(IP: \\\"\\\"\\\\\\)\\\\\\." + '') + ); + }; + + services.fail2ban = { + jails = { + jellyfin-iptables.settings = { + enabled = true; + filter = "jellyfin"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.jellyfin.dataDir}/log/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/jellyfin.nix b/modules/nixos-modules/server/jellyfin/jellyfin.nix new file mode 100644 index 0000000..9bfa921 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/jellyfin.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: let + jellyfinPort = 8096; + dlanPort = 1900; +in { + options.services.jellyfin = { + media_directory = lib.mkOption { + type = lib.types.str; + description = "directory jellyfin media will be hosted at"; + default = "/srv/jellyfin/media"; + }; + }; + + config = lib.mkIf config.services.jellyfin.enable { + environment.systemPackages = [ + pkgs.jellyfin + pkgs.jellyfin-web + pkgs.jellyfin-ffmpeg + ]; + + networking.firewall.allowedTCPPorts = [jellyfinPort dlanPort]; + + systemd.tmpfiles.rules = [ + "d ${config.services.jellyfin.media_directory} 2770 jellyfin jellyfin_media" + "A ${config.services.jellyfin.media_directory} - - - - u:jellyfin:rwX,g:jellyfin_media:rwX,o::-" + ]; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/proxy.nix b/modules/nixos-modules/server/jellyfin/proxy.nix new file mode 100644 index 0000000..35289e7 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/proxy.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + ... +}: let + jellyfinPort = 8096; +in { + options.services.jellyfin = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that jellyfin will be hosted at"; + default = "jellyfin.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for jellyfin"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.jellyfin.reverseProxy.enable { + services.reverseProxy.services.jellyfin = { + target = "http://localhost:${toString jellyfinPort}"; + domain = config.services.jellyfin.domain; + extraDomains = config.services.jellyfin.extraDomains; + + settings = { + forwardHeaders.enable = true; + maxBodySize = 20; + noSniff.enable = true; + proxyBuffering.enable = false; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/storage.nix b/modules/nixos-modules/server/jellyfin/storage.nix new file mode 100644 index 0000000..5cff3e8 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/storage.nix @@ -0,0 +1,56 @@ +{ + lib, + config, + ... +}: let + jellyfin_data_directory = "/var/lib/jellyfin"; + jellyfin_cache_directory = "/var/cache/jellyfin"; +in { + options.services.jellyfin.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.jellyfin.enable { + storage.datasets.replicate = { + "system/root" = { + directories = { + "${jellyfin_data_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; + }; + "${jellyfin_cache_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin"; + }; + }; + }; + "system/media" = { + mount = "/persist/replicate/system/media"; + + directories."${config.services.jellyfin.media_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { + enable = true; + owner.name = "jellyfin"; + group.name = "jellyfin_media"; + owner.permissions = { + read = true; + write = true; + execute = true; + }; + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = false; + write = false; + execute = false; + }; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/lidarr/default.nix b/modules/nixos-modules/server/lidarr/default.nix new file mode 100644 index 0000000..cb2a5f0 --- /dev/null +++ b/modules/nixos-modules/server/lidarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/lidarr/storage.nix b/modules/nixos-modules/server/lidarr/storage.nix new file mode 100644 index 0000000..c4c020e --- /dev/null +++ b/modules/nixos-modules/server/lidarr/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; +in { + options.services.lidarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.lidarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.lidarr.enable { + storage.datasets.replicate."system/root" = { + directories."${lidarr_data_directory}" = lib.mkIf config.services.lidarr.impermanence.enable { + owner.name = "lidarr"; + group.name = "lidarr"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/network_storage/default.nix b/modules/nixos-modules/server/network_storage/default.nix new file mode 100644 index 0000000..cd100ab --- /dev/null +++ b/modules/nixos-modules/server/network_storage/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./network_storage.nix + ./nfs.nix + ]; +} diff --git a/modules/nixos-modules/server/network_storage/network_storage.nix b/modules/nixos-modules/server/network_storage/network_storage.nix new file mode 100644 index 0000000..b9d0446 --- /dev/null +++ b/modules/nixos-modules/server/network_storage/network_storage.nix @@ -0,0 +1,86 @@ +{ + config, + lib, + ... +}: let + export_directory = config.host.network_storage.export_directory; +in { + options = { + host.network_storage = { + enable = lib.mkEnableOption "is this machine going to export network storage"; + export_directory = lib.mkOption { + type = lib.types.path; + description = "what are exports going to be stored in"; + default = "/exports"; + }; + directories = lib.mkOption { + type = lib.types.listOf (lib.types.submodule ({config, ...}: { + options = { + folder = lib.mkOption { + type = lib.types.str; + description = "what is the name of this export directory"; + }; + bind = lib.mkOption { + type = lib.types.nullOr lib.types.path; + description = "is this directory bound to anywhere"; + default = null; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user owns this directory"; + default = "nouser"; + }; + group = lib.mkOption { + type = lib.types.str; + description = "what group owns this directory"; + default = "nogroup"; + }; + _directory = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.path; + default = "${export_directory}/${config.folder}"; + }; + }; + })); + description = "list of directory names to export"; + }; + }; + }; + + config = lib.mkIf config.host.network_storage.enable (lib.mkMerge [ + { + # create any folders that we need to have for our exports + systemd.tmpfiles.rules = + [ + "d ${config.host.network_storage.export_directory} 2775 nobody nogroup -" + ] + ++ ( + builtins.map ( + directory: "d ${directory._directory} 2770 ${directory.user} ${directory.group}" + ) + config.host.network_storage.directories + ); + + # set up any bind mounts that we need for our exports + fileSystems = builtins.listToAttrs ( + builtins.map (directory: + lib.attrsets.nameValuePair directory._directory { + device = directory.bind; + options = ["bind"]; + }) ( + builtins.filter (directory: directory.bind != null) config.host.network_storage.directories + ) + ); + } + # (lib.mkIf config.host.impermanence.enable { + # environment.persistence."/persist/replicate/system/root" = { + # enable = true; + # hideMounts = true; + # directories = [ + # config.host.network_storage.export_directory + # ]; + # }; + # }) + ]); +} diff --git a/modules/nixos-modules/server/network_storage/nfs.nix b/modules/nixos-modules/server/network_storage/nfs.nix new file mode 100644 index 0000000..297dc1a --- /dev/null +++ b/modules/nixos-modules/server/network_storage/nfs.nix @@ -0,0 +1,107 @@ +{ + config, + lib, + ... +}: { + options = { + host.network_storage.nfs = { + enable = lib.mkEnableOption "is this server going to export network storage as nfs shares"; + port = lib.mkOption { + type = lib.types.int; + default = 2049; + description = "port that nfs will run on"; + }; + directories = lib.mkOption { + type = lib.types.listOf ( + lib.types.enum ( + builtins.map ( + directory: directory.folder + ) + config.host.network_storage.directories + ) + ); + description = "list of exported directories to be exported via nfs"; + }; + }; + }; + config = lib.mkMerge [ + { + assertions = [ + { + assertion = !(config.host.network_storage.nfs.enable && !config.host.network_storage.enable); + message = "nfs cant be enabled with network storage disabled"; + } + ]; + } + ( + lib.mkIf (config.host.network_storage.nfs.enable && config.host.network_storage.enable) { + services.nfs = { + settings = { + nfsd = { + threads = 32; + port = config.host.network_storage.nfs.port; + }; + }; + server = { + enable = true; + + lockdPort = 4001; + mountdPort = 4002; + statdPort = 4000; + + exports = lib.strings.concatLines ( + [ + "${config.host.network_storage.export_directory} 100.64.0.0/10(rw,fsid=0,no_subtree_check)" + ] + ++ ( + lib.lists.imap0 ( + i: directory: let + createOptions = fsid: "(rw,fsid=${toString fsid},nohide,insecure,no_subtree_check)"; + addresses = [ + # loopback + "127.0.0.1" + "::1" + # tailscale + "100.64.0.0/10" + "fd7a:115c:a1e0::/48" + ]; + options = lib.strings.concatStrings ( + lib.strings.intersperse " " ( + lib.lists.imap0 (index: address: "${address}${createOptions (1 + (i * (builtins.length addresses)) + index)}") addresses + ) + ); + in "${directory._directory} ${options}" + ) + ( + builtins.filter ( + directory: lib.lists.any (target: target == directory.folder) config.host.network_storage.nfs.directories + ) + config.host.network_storage.directories + ) + ) + ); + }; + }; + networking.firewall = let + ports = [ + 111 + config.host.network_storage.nfs.port + config.services.nfs.server.lockdPort + config.services.nfs.server.mountdPort + config.services.nfs.server.statdPort + 20048 + ]; + in { + # Allow NFS on Tailscale interface + interfaces.${config.services.tailscale.interfaceName} = { + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + # Allow NFS on local network (assuming default interface) + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + } + ) + ]; +} diff --git a/modules/nixos-modules/server/panoramax/database.nix b/modules/nixos-modules/server/panoramax/database.nix new file mode 100644 index 0000000..1721726 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/database.nix @@ -0,0 +1,48 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax = { + database = { + postgres = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use PostgreSQL instead of SQLite"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "Database user name"; + }; + database = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "Database name"; + }; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.enable { + assertions = [ + { + assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Panoramax"; + } + ]; + + services.postgresql.databases.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { + enable = true; + user = config.services.panoramax.database.postgres.user; + database = config.services.panoramax.database.postgres.database; + }; + + systemd.services.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/panoramax/default.nix b/modules/nixos-modules/server/panoramax/default.nix new file mode 100644 index 0000000..f5a514f --- /dev/null +++ b/modules/nixos-modules/server/panoramax/default.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./proxy.nix + ./fail2ban.nix + ./storage.nix + ./panoramax.nix + ./database.nix + ]; +} diff --git a/modules/nixos-modules/server/panoramax/fail2ban.nix b/modules/nixos-modules/server/panoramax/fail2ban.nix new file mode 100644 index 0000000..649b53a --- /dev/null +++ b/modules/nixos-modules/server/panoramax/fail2ban.nix @@ -0,0 +1,11 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.services.panoramax.enable && config.services.fail2ban.enable) { + # TODO: configure options for fail2ban + # This is a placeholder - panoramax fail2ban configuration would need to be defined + # based on the specific log patterns and security requirements + }; +} diff --git a/modules/nixos-modules/server/panoramax/panoramax.nix b/modules/nixos-modules/server/panoramax/panoramax.nix new file mode 100644 index 0000000..fd77db7 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/panoramax.nix @@ -0,0 +1,359 @@ +{ + config, + lib, + pkgs, + ... +}: { + options.services = { + panoramax = { + enable = lib.mkEnableOption "panoramax"; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.panoramax; + description = "The panoramax package to use"; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The user panoramax should run as."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The group panoramax should run as."; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "Host to bind the panoramax service to"; + }; + + port = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = 5000; + description = "Port for the panoramax service"; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to open the panoramax port in the firewall"; + }; + + settings = { + urlScheme = lib.mkOption { + type = lib.types.enum ["http" "https"]; + default = "https"; + description = "URL scheme for the application"; + }; + + storage = { + fsUrl = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "/var/lib/panoramax/storage"; + description = "File system URL for storage"; + }; + }; + + infrastructure = { + nbProxies = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = 1; + description = "Number of proxies in front of the application"; + }; + }; + + flask = { + secretKey = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Flask secret key for session security"; + }; + + sessionCookieDomain = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Flask session cookie domain"; + }; + }; + + api = { + pictures = { + licenseSpdxId = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "SPDX license identifier for API pictures"; + }; + + licenseUrl = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "License URL for API pictures"; + }; + }; + }; + + extraEnvironment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + description = "Additional environment variables"; + example = { + CUSTOM_SETTING = "value"; + DEBUG = "true"; + }; + }; + }; + + database = { + createDB = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically create the database and user"; + }; + + name = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The name of the panoramax database"; + }; + + host = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "/run/postgresql"; + description = "Hostname or address of the postgresql server. If an absolute path is given here, it will be interpreted as a unix socket path."; + }; + + port = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = 5432; + description = "Port of the postgresql server."; + }; + + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "panoramax"; + description = "The database user for panoramax."; + }; + + # TODO: password file for external database + }; + + sgblur = { + # TODO: configs to bind to sgblur + }; + }; + sgblur = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enable sgblur integration for face and license plate blurring"; + }; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.sgblur; + description = "The sgblur package to use"; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Port for the sgblur service"; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "Host to bind the sgblur service to"; + }; + + url = lib.mkOption { + type = lib.types.str; + default = "http://127.0.0.1:8080"; + description = "URL where sgblur service is accessible"; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ + { + # Create panoramax user and group + users.users.${config.services.panoramax.user} = { + isSystemUser = true; + group = config.services.panoramax.group; + home = "/var/lib/panoramax"; + createHome = true; + }; + + users.groups.${config.services.panoramax.group} = {}; + + # Ensure storage directory exists with correct permissions + systemd.tmpfiles.rules = [ + "d '${config.services.panoramax.settings.storage.fsUrl}' 0755 ${config.services.panoramax.user} ${config.services.panoramax.group} - -" + ]; + + systemd.services.panoramax-api = { + description = "Panoramax API server (self hosted map street view)"; + after = ["network.target" "postgresql.service"]; + wantedBy = ["multi-user.target"]; + + environment = + { + # Core Flask configuration + FLASK_APP = "geovisio"; + + # Storage configuration + FS_URL = config.services.panoramax.settings.storage.fsUrl; + + # Infrastructure configuration + INFRA_NB_PROXIES = toString config.services.panoramax.settings.infrastructure.nbProxies; + + # Application configuration + PORT = toString config.services.panoramax.port; + + # Python path to include the panoramax package + PYTHONPATH = "${config.services.panoramax.package}/${pkgs.python3.sitePackages}"; + } + // ( + if config.services.panoramax.database.host == "/run/postgresql" + then { + DB_URL = "postgresql://${config.services.panoramax.database.user}@/${config.services.panoramax.database.name}?host=/run/postgresql"; + } + else { + DB_HOST = config.services.panoramax.database.host; + DB_PORT = toString config.services.panoramax.database.port; + DB_USERNAME = config.services.panoramax.database.user; + DB_NAME = config.services.panoramax.database.name; + } + ) + // (lib.optionalAttrs (config.services.panoramax.settings.flask.secretKey != null) { + FLASK_SECRET_KEY = config.services.panoramax.settings.flask.secretKey; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.flask.sessionCookieDomain != null) { + FLASK_SESSION_COOKIE_DOMAIN = config.services.panoramax.settings.flask.sessionCookieDomain; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseSpdxId != null) { + API_PICTURES_LICENSE_SPDX_ID = config.services.panoramax.settings.api.pictures.licenseSpdxId; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseUrl != null) { + API_PICTURES_LICENSE_URL = config.services.panoramax.settings.api.pictures.licenseUrl; + }) + // (lib.optionalAttrs config.services.sgblur.enable { + SGBLUR_API_URL = config.services.sgblur.url; + }) + // config.services.panoramax.settings.extraEnvironment; + + path = with pkgs; [ + (python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])) + ]; + + serviceConfig = { + ExecStart = "${pkgs.python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])}/bin/waitress-serve --port ${toString config.services.panoramax.port} --call geovisio:create_app"; + User = config.services.panoramax.user; + Group = config.services.panoramax.group; + WorkingDirectory = "/var/lib/panoramax"; + Restart = "always"; + RestartSec = 5; + + # Security hardening + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + ReadWritePaths = [ + "/var/lib/panoramax" + config.services.panoramax.settings.storage.fsUrl + ]; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + }; + }; + + # Open firewall if requested + networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ + config.services.panoramax.port + ]; + } + (lib.mkIf config.services.sgblur.enable { + # SGBlur service configuration + systemd.services.sgblur = { + description = "SGBlur face and license plate blurring service"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + + path = with pkgs; [ + config.services.sgblur.package + python3 + python3Packages.waitress + ]; + + serviceConfig = { + ExecStart = "${pkgs.python3Packages.waitress}/bin/waitress-serve --host ${config.services.sgblur.host} --port ${toString config.services.sgblur.port} src.detect.detect_api:app"; + WorkingDirectory = "${config.services.sgblur.package}"; + Restart = "always"; + RestartSec = 5; + + # Basic security hardening + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ + config.services.sgblur.port + ]; + }) + (lib.mkIf config.services.panoramax.database.createDB { + services.postgresql = { + enable = true; + ensureDatabases = lib.mkIf config.services.panoramax.database.createDB [config.services.panoramax.database.name]; + ensureUsers = lib.mkIf config.services.panoramax.database.createDB [ + { + name = config.services.panoramax.database.user; + ensureDBOwnership = true; + ensureClauses.login = true; + } + ]; + extensions = ps: with ps; [postgis]; + }; + systemd.services.postgresql.serviceConfig.ExecStartPost = let + sqlFile = pkgs.writeText "panoramax-postgis-setup.sql" '' + CREATE EXTENSION IF NOT EXISTS postgis; + + -- TODO: how can we ensure that this runs after the databases have been created + -- ALTER DATABASE ${config.services.panoramax.database.name} SET TIMEZONE TO 'UTC'; + + GRANT SET ON PARAMETER session_replication_role TO ${config.services.panoramax.database.user}; + ''; + in [ + '' + ${lib.getExe' config.services.postgresql.package "psql"} -d "${config.services.panoramax.database.user}" -f "${sqlFile}" + '' + ]; + }) + ]); +} diff --git a/modules/nixos-modules/server/panoramax/proxy.nix b/modules/nixos-modules/server/panoramax/proxy.nix new file mode 100644 index 0000000..7cd7111 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/proxy.nix @@ -0,0 +1,39 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that panoramax will be hosted at"; + default = "panoramax.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for panoramax"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.reverseProxy.enable { + services.reverseProxy.services.panoramax = { + target = "http://localhost:${toString config.services.panoramax.port}"; + domain = config.services.panoramax.domain; + extraDomains = config.services.panoramax.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 100000; + timeout = 300; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/panoramax/storage.nix b/modules/nixos-modules/server/panoramax/storage.nix new file mode 100644 index 0000000..b36e087 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/storage.nix @@ -0,0 +1,19 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.panoramax.enable { + storage.datasets.replicate."system/root" = { + directories."/var/lib/panoramax" = lib.mkIf config.services.panoramax.impermanence.enable { + owner.name = "panoramax"; + group.name = "panoramax"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/database.nix b/modules/nixos-modules/server/paperless/database.nix new file mode 100644 index 0000000..c63e59d --- /dev/null +++ b/modules/nixos-modules/server/paperless/database.nix @@ -0,0 +1,30 @@ +{ + config, + lib, + ... +}: { + config = lib.mkIf config.services.paperless.enable { + assertions = [ + { + assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using local postgres database for Paperless"; + } + { + assertion = !config.services.paperless.database.createLocally || (builtins.any (db: db == "paperless") config.services.postgresql.ensureDatabases); + message = "Paperless built-in database creation failed - expected 'paperless' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !config.services.paperless.database.createLocally || (builtins.any (user: user.name == "paperless") config.services.postgresql.ensureUsers); + message = "Paperless built-in user creation failed - expected user 'paperless' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + services.paperless.database.createLocally = lib.mkDefault true; + + systemd.services.paperless-scheduler = lib.mkIf config.services.paperless.database.createLocally { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/default.nix b/modules/nixos-modules/server/paperless/default.nix new file mode 100644 index 0000000..f7a5aa7 --- /dev/null +++ b/modules/nixos-modules/server/paperless/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./paperless.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/paperless/fail2ban.nix b/modules/nixos-modules/server/paperless/fail2ban.nix new file mode 100644 index 0000000..e1a70f9 --- /dev/null +++ b/modules/nixos-modules/server/paperless/fail2ban.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + pkgs, + ... +}: { + config = lib.mkIf (config.services.paperless.enable && config.services.fail2ban.enable) { + environment.etc = { + "fail2ban/filter.d/paperless.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = Login failed for user `.*` from (?:IP|private IP) ``\.$ + ignoreregex = + + '') + ); + }; + + services.fail2ban = { + jails = { + paperless.settings = { + enabled = true; + filter = "paperless"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.paperless.dataDir}/log/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/paperless.nix b/modules/nixos-modules/server/paperless/paperless.nix new file mode 100644 index 0000000..5bcbfed --- /dev/null +++ b/modules/nixos-modules/server/paperless/paperless.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: { + options.services.paperless = { + database = { + user = lib.mkOption { + type = lib.types.str; + description = "what is the user and database that we are going to use for paperless"; + default = "paperless"; + }; + }; + }; + + config = lib.mkIf config.services.paperless.enable { + services.paperless = { + configureTika = true; + settings = { + PAPERLESS_DBENGINE = "postgresql"; + PAPERLESS_DBHOST = "/run/postgresql"; + PAPERLESS_DBNAME = config.services.paperless.database.user; + PAPERLESS_DBUSER = config.services.paperless.database.user; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/proxy.nix b/modules/nixos-modules/server/paperless/proxy.nix new file mode 100644 index 0000000..9d152c9 --- /dev/null +++ b/modules/nixos-modules/server/paperless/proxy.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + ... +}: { + options.services.paperless = { + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for paperless"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.paperless.reverseProxy.enable { + services.reverseProxy.services.paperless = { + target = "http://${config.services.paperless.address}:${toString config.services.paperless.port}"; + domain = config.services.paperless.domain; + extraDomains = config.services.paperless.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 50000; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/storage.nix b/modules/nixos-modules/server/paperless/storage.nix new file mode 100644 index 0000000..6e17bc2 --- /dev/null +++ b/modules/nixos-modules/server/paperless/storage.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/paperless"; +in { + options.services.paperless.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.paperless.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.paperless.impermanence.enable { + owner.name = "paperless"; + group.name = "paperless"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/postgres/default.nix b/modules/nixos-modules/server/postgres/default.nix new file mode 100644 index 0000000..50d90d4 --- /dev/null +++ b/modules/nixos-modules/server/postgres/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./postgres.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/postgres/postgres.nix b/modules/nixos-modules/server/postgres/postgres.nix new file mode 100644 index 0000000..af7d1b4 --- /dev/null +++ b/modules/nixos-modules/server/postgres/postgres.nix @@ -0,0 +1,122 @@ +{ + config, + lib, + pkgs, + ... +}: let + enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases; + extraDatabasesList = config.services.postgresql.extraDatabases; + + serviceDatabaseUsers = lib.mapAttrsToList (_: db: { + name = db.user; + ensureDBOwnership = true; + }) (lib.filterAttrs (_: db: db.ensureUser) enabledDatabases); + + extraDatabaseUsers = + builtins.map (dbName: { + name = dbName; + ensureDBOwnership = true; + }) + extraDatabasesList; + + serviceDatabases = lib.mapAttrsToList (_: db: db.database) enabledDatabases; + extraDatabaseNames = extraDatabasesList; + + serviceUserMappings = lib.mapAttrsToList (_: db: "user_map ${db.user} ${db.user}") enabledDatabases; + extraUserMappings = builtins.map (dbName: "user_map ${dbName} ${dbName}") extraDatabasesList; + + builtinServiceMappings = let + forgejoMapping = lib.optional (config.services.forgejo.enable && config.services.forgejo.database.type == "postgres") "user_map forgejo forgejo"; + immichMapping = lib.optional (config.services.immich.enable && config.services.immich.database.enable) "user_map immich immich"; + paperlessMapping = lib.optional (config.services.paperless.enable && config.services.paperless.database.createLocally) "user_map paperless paperless"; + in + forgejoMapping ++ immichMapping ++ paperlessMapping; +in { + options = { + services.postgresql = { + databases = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to create this database and user"; + }; + user = lib.mkOption { + type = lib.types.str; + default = name; + description = "Database user name"; + }; + database = lib.mkOption { + type = lib.types.str; + default = name; + description = "Database name"; + }; + ensureUser = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to ensure the user exists"; + }; + }; + })); + default = {}; + description = "Databases to create for services"; + }; + + extraDatabases = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "Additional databases to create (user name will match database name)"; + example = ["custom_db" "test_db"]; + }; + + adminUsers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + description = "System users who should have PostgreSQL superuser access"; + example = ["leyla" "admin"]; + }; + }; + }; + + config = lib.mkIf config.services.postgresql.enable { + services = { + postgresql = { + package = pkgs.postgresql_16; + + ensureUsers = + [ + {name = "postgres";} + ] + ++ serviceDatabaseUsers ++ extraDatabaseUsers; + + ensureDatabases = serviceDatabases ++ extraDatabaseNames; + + identMap = + '' + # ArbitraryMapName systemUser DBUser + + # Administration Users + superuser_map root postgres + superuser_map postgres postgres + '' + + ( + lib.strings.concatLines (builtins.map (user: "superuser_map ${user} postgres") config.services.postgresql.adminUsers) + ) + + '' + + # Client Users + '' + + ( + lib.strings.concatLines (serviceUserMappings ++ extraUserMappings ++ builtinServiceMappings) + ); + + authentication = pkgs.lib.mkOverride 10 '' + # type database DBuser origin-address auth-method optional_ident_map + local all postgres peer map=superuser_map + local sameuser all peer map=user_map + ''; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/postgres/storage.nix b/modules/nixos-modules/server/postgres/storage.nix new file mode 100644 index 0000000..58a84a6 --- /dev/null +++ b/modules/nixos-modules/server/postgres/storage.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/postgresql/16"; +in { + options.services.postgresql.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.postgresql.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.postgresql.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.postgresql.impermanence.enable { + owner.name = "postgres"; + group.name = "postgres"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/qbittorent/default.nix b/modules/nixos-modules/server/qbittorent/default.nix new file mode 100644 index 0000000..11cc449 --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./qbittorent.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/qbittorent/qbittorent.nix b/modules/nixos-modules/server/qbittorent/qbittorent.nix new file mode 100644 index 0000000..44603c8 --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/qbittorent.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + ... +}: { + options.services.qbittorrent = { + mediaDir = lib.mkOption { + type = lib.types.path; + description = lib.mdDoc '' + The directory to create to store qbittorrent media. + ''; + }; + }; + + config = lib.mkIf config.services.qbittorrent.enable { + # Main qbittorrent configuration goes here if needed + }; +} diff --git a/modules/nixos-modules/server/qbittorent/storage.nix b/modules/nixos-modules/server/qbittorent/storage.nix new file mode 100644 index 0000000..da82bcc --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/storage.nix @@ -0,0 +1,46 @@ +{ + lib, + config, + ... +}: let + qbittorent_profile_directory = "/var/lib/qBittorrent/"; +in { + options.services.qbittorrent.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.qbittorrent.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.qbittorrent.enable { + storage.datasets.replicate = { + "system/root" = { + directories."${qbittorent_profile_directory}" = lib.mkIf config.services.qbittorrent.impermanence.enable { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + }; + }; + "system/media" = { + mount = "/persist/replicate/system/media"; + + directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { + owner.name = "qbittorrent"; + group.name = "qbittorrent"; + owner.permissions = { + read = true; + write = true; + execute = true; + }; + group.permissions = { + read = true; + write = true; + execute = true; + }; + other.permissions = { + read = true; + write = false; + execute = true; + }; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/radarr/default.nix b/modules/nixos-modules/server/radarr/default.nix new file mode 100644 index 0000000..cb2a5f0 --- /dev/null +++ b/modules/nixos-modules/server/radarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/radarr/storage.nix b/modules/nixos-modules/server/radarr/storage.nix new file mode 100644 index 0000000..8f991c0 --- /dev/null +++ b/modules/nixos-modules/server/radarr/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + radarr_data_directory = "/var/lib/radarr/.config/Radarr"; +in { + options.services.radarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.radarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.radarr.enable { + storage.datasets.replicate."system/root" = { + directories."${radarr_data_directory}" = lib.mkIf config.services.radarr.impermanence.enable { + owner.name = "radarr"; + group.name = "radarr"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/reverseProxy/default.nix b/modules/nixos-modules/server/reverseProxy/default.nix new file mode 100644 index 0000000..336e28b --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./reverseProxy.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/reverseProxy/reverseProxy.nix b/modules/nixos-modules/server/reverseProxy/reverseProxy.nix new file mode 100644 index 0000000..eecc9bf --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/reverseProxy.nix @@ -0,0 +1,176 @@ +{ + lib, + config, + ... +}: { + options.services.reverseProxy = { + enable = lib.mkEnableOption "turn on the reverse proxy"; + openFirewall = lib.mkEnableOption "open the firewall"; + refuseUnmatchedDomains = lib.mkOption { + type = lib.types.bool; + description = "refuse connections for domains that don't match any configured virtual hosts"; + default = true; + }; + ports = { + http = lib.mkOption { + type = lib.types.port; + description = "HTTP port for the reverse proxy"; + default = 80; + }; + https = lib.mkOption { + type = lib.types.port; + description = "HTTPS port for the reverse proxy"; + default = 443; + }; + }; + acme = { + enable = lib.mkOption { + type = lib.types.bool; + description = "enable ACME certificate management"; + default = true; + }; + email = lib.mkOption { + type = lib.types.str; + description = "email address for ACME certificate registration"; + }; + }; + services = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + target = lib.mkOption { + type = lib.types.str; + description = "what url will all traffic to this application be forwarded to"; + }; + domain = lib.mkOption { + type = lib.types.str; + description = "what is the default subdomain to be used for this application to be used for"; + default = name; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for this domain"; + default = []; + }; + settings = { + certificateRenewal.enable = lib.mkOption { + type = lib.types.bool; + description = "auto renew certificates"; + default = true; + }; + forceSSL.enable = lib.mkOption { + type = lib.types.bool; + description = "auto renew certificates"; + default = true; + }; + proxyHeaders = { + enable = lib.mkEnableOption "should we proxy headers"; + timeout = lib.mkOption { + type = lib.types.int; + default = 60; + }; + }; + proxyWebsockets.enable = lib.mkEnableOption "should the default config proxy websockets"; + forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers"; + noSniff.enable = lib.mkEnableOption "should the no sniff flags be set"; + proxyBuffering.enable = lib.mkOption { + type = lib.types.bool; + description = "should proxy buffering be enabled"; + default = true; + }; + maxBodySize = lib.mkOption { + type = lib.types.nullOr lib.types.int; + description = ""; + default = null; + }; + }; + }; + })); + }; + }; + + config = let + httpPort = config.services.reverseProxy.ports.http; + httpsPort = config.services.reverseProxy.ports.https; + in + lib.mkIf config.services.reverseProxy.enable { + security.acme = lib.mkIf config.services.reverseProxy.acme.enable { + acceptTerms = true; + defaults.email = config.services.reverseProxy.acme.email; + }; + + services.nginx = { + enable = true; + virtualHosts = lib.mkMerge ( + (lib.optionals config.services.reverseProxy.refuseUnmatchedDomains [ + { + "_" = { + default = true; + serverName = "_"; + locations."/" = { + extraConfig = '' + return 444; + ''; + }; + }; + } + ]) + ++ lib.lists.flatten ( + lib.attrsets.mapAttrsToList ( + name: service: let + hostConfig = { + forceSSL = service.settings.forceSSL.enable; + enableACME = service.settings.certificateRenewal.enable; + locations = { + "/" = { + proxyPass = service.target; + proxyWebsockets = service.settings.proxyWebsockets.enable; + recommendedProxySettings = service.settings.forwardHeaders.enable; + extraConfig = let + # Client upload size configuration + maxBodySizeConfig = + lib.optionalString (service.settings.maxBodySize != null) + "client_max_body_size ${toString service.settings.maxBodySize}M;"; + + # Security header configuration + noSniffConfig = + lib.optionalString service.settings.noSniff.enable + "add_header X-Content-Type-Options nosniff;"; + + # Proxy buffering configuration + proxyBufferingConfig = + lib.optionalString (!service.settings.proxyBuffering.enable) + "proxy_buffering off;"; + + # Proxy timeout configuration + proxyTimeoutConfig = + lib.optionalString service.settings.proxyHeaders.enable + '' + proxy_read_timeout ${toString service.settings.proxyHeaders.timeout}s; + proxy_connect_timeout ${toString service.settings.proxyHeaders.timeout}s; + proxy_send_timeout ${toString service.settings.proxyHeaders.timeout}s; + ''; + in + maxBodySizeConfig + noSniffConfig + proxyBufferingConfig + proxyTimeoutConfig; + }; + }; + }; + in ( + [ + { + ${service.domain} = hostConfig; + } + ] + ++ builtins.map (domain: {${domain} = hostConfig;}) + service.extraDomains + ) + ) + config.services.reverseProxy.services + ) + ); + }; + networking.firewall.allowedTCPPorts = lib.mkIf config.services.reverseProxy.openFirewall [ + httpPort + httpsPort + ]; + }; +} diff --git a/modules/nixos-modules/server/reverseProxy/storage.nix b/modules/nixos-modules/server/reverseProxy/storage.nix new file mode 100644 index 0000000..62b5451 --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + dataDir = "/var/lib/acme"; +in { + options.services.reverseProxy.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.reverseProxy.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.reverseProxy.enable { + storage.datasets.replicate."system/root" = { + directories."${dataDir}" = lib.mkIf config.services.reverseProxy.impermanence.enable { + owner.name = "acme"; + group.name = "acme"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/searx/default.nix b/modules/nixos-modules/server/searx/default.nix new file mode 100644 index 0000000..5426380 --- /dev/null +++ b/modules/nixos-modules/server/searx/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./searx.nix + ./proxy.nix + ]; +} diff --git a/modules/nixos-modules/server/searx/proxy.nix b/modules/nixos-modules/server/searx/proxy.nix new file mode 100644 index 0000000..e994e4a --- /dev/null +++ b/modules/nixos-modules/server/searx/proxy.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + ... +}: { + options.services.searx = { + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for searx"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.searx.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.searx.reverseProxy.enable { + services.reverseProxy.services.searx = { + target = "http://localhost:${toString config.services.searx.settings.server.port}"; + domain = config.services.searx.domain; + extraDomains = config.services.searx.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/searx/searx.nix b/modules/nixos-modules/server/searx/searx.nix new file mode 100644 index 0000000..d4d4012 --- /dev/null +++ b/modules/nixos-modules/server/searx/searx.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + inputs, + ... +}: { + config = lib.mkIf config.services.searx.enable { + sops.secrets = { + "services/searx" = { + sopsFile = "${inputs.secrets}/defiant-services.yaml"; + }; + }; + + services.searx = { + environmentFile = config.sops.secrets."services/searx".path; + + # Rate limiting + limiterSettings = { + real_ip = { + x_for = 1; + ipv4_prefix = 32; + ipv6_prefix = 56; + }; + + botdetection = { + ip_limit = { + filter_link_local = true; + link_token = true; + }; + }; + }; + + settings = { + server = { + port = 8083; + secret_key = "@SEARXNG_SECRET@"; + }; + + # Search engine settings + search = { + safe_search = 2; + autocomplete_min = 2; + autocomplete = "duckduckgo"; + }; + + # Enabled plugins + enabled_plugins = [ + "Basic Calculator" + "Hash plugin" + "Tor check plugin" + "Open Access DOI rewrite" + "Hostnames plugin" + "Unit converter plugin" + "Tracker URL remover" + ]; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/sonarr/default.nix b/modules/nixos-modules/server/sonarr/default.nix new file mode 100644 index 0000000..cb2a5f0 --- /dev/null +++ b/modules/nixos-modules/server/sonarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/server/sonarr/storage.nix b/modules/nixos-modules/server/sonarr/storage.nix new file mode 100644 index 0000000..8587751 --- /dev/null +++ b/modules/nixos-modules/server/sonarr/storage.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; +in { + options.services.sonarr.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.sonarr.enable && config.storage.impermanence.enable; + }; + + config = lib.mkIf config.services.sonarr.enable { + storage.datasets.replicate."system/root" = { + directories."${sonarr_data_directory}" = lib.mkIf config.services.sonarr.impermanence.enable { + owner.name = "sonarr"; + group.name = "sonarr"; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/wyoming.nix b/modules/nixos-modules/server/wyoming.nix new file mode 100644 index 0000000..1df6877 --- /dev/null +++ b/modules/nixos-modules/server/wyoming.nix @@ -0,0 +1,63 @@ +{ + lib, + config, + ... +}: { + options.services.wyoming.enable = lib.mkEnableOption "should wyoming be enabled on this device"; + config = lib.mkIf config.services.wyoming.enable (lib.mkMerge [ + { + services.wyoming = { + # Text to speech + piper = { + servers = { + "en" = { + enable = true; + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py + voice = "en-us-amy-low"; + uri = "tcp://0.0.0.0:10200"; + speaker = 0; + }; + }; + }; + + # Speech to text + faster-whisper = { + servers = { + "en" = { + enable = true; + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/asr/faster-whisper/script/download.py + model = "tiny-int8"; + language = "en"; + uri = "tcp://0.0.0.0:10300"; + device = "cpu"; + }; + }; + }; + + openwakeword = { + enable = true; + uri = "tcp://0.0.0.0:10400"; + # preloadModels = [ + # "ok_nabu" + # ]; + # TODO: custom models + }; + }; + + # needs access to /proc/cpuinfo + systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all"; + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/replicate/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = "/var/lib/private/wyoming"; + mode = "0700"; + } + ]; + }; + }) + ]); +} diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix new file mode 100644 index 0000000..6fe8e5c --- /dev/null +++ b/modules/nixos-modules/ssh.nix @@ -0,0 +1,44 @@ +{ + lib, + config, + ... +}: { + options = { + services.openssh.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.openssh.enable && config.storage.impermanence.enable; + }; + }; + + config = { + services = { + openssh = { + enable = true; + ports = [22]; + settings = { + PasswordAuthentication = false; + UseDns = true; + X11Forwarding = false; + }; + }; + }; + + storage.datasets.replicate."system/root" = { + files = lib.mkIf config.services.openssh.impermanence.enable (builtins.listToAttrs ( + lib.lists.flatten ( + builtins.map (hostKey: [ + { + name = hostKey.path; + value = {enable = true;}; + } + { + name = "${hostKey.path}.pub"; + value = {enable = true;}; + } + ]) + config.services.openssh.hostKeys + ) + )); + }; + }; +} diff --git a/modules/nixos-modules/steam.nix b/modules/nixos-modules/steam.nix new file mode 100644 index 0000000..20c0978 --- /dev/null +++ b/modules/nixos-modules/steam.nix @@ -0,0 +1,9 @@ +{...}: { + programs = { + steam = { + remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play + dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server + localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers + }; + }; +} diff --git a/modules/nixos/storage/default.nix b/modules/nixos-modules/storage/default.nix similarity index 67% rename from modules/nixos/storage/default.nix rename to modules/nixos-modules/storage/default.nix index f227f70..ebf990a 100644 --- a/modules/nixos/storage/default.nix +++ b/modules/nixos-modules/storage/default.nix @@ -1,17 +1,13 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { +{...}: { # TODO: we should have an impermanence module for home manager that proxies its values namespaced to the user down here that matches the same interface # TODO: we should have a way of enabling impermanence for a systemd config # these should have an option to put their folder into their own dataset (this needs to support private vs non private) # options for features that can be added to the dataset - flake.nixosModules.storage = {...}: { - imports = [ - mod.storage-impermanence - mod.storage-zfs - mod.storage-config - ]; - }; + imports = [ + ./impermanence.nix + ./zfs.nix + ./storage.nix + ]; } diff --git a/modules/nixos-modules/storage/impermanence.nix b/modules/nixos-modules/storage/impermanence.nix new file mode 100644 index 0000000..4fdf803 --- /dev/null +++ b/modules/nixos-modules/storage/impermanence.nix @@ -0,0 +1,142 @@ +args @ { + lib, + config, + ... +}: let + datasetSubmodules = (import ./submodules/dataset.nix) args; + impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; + + permissionsToMode = permissions: let + permSetToDigit = permSet: + ( + if permSet.read + then 4 + else 0 + ) + + ( + if permSet.write + then 2 + else 0 + ) + + ( + if permSet.execute + then 1 + else 0 + ); + + ownerDigit = permSetToDigit permissions.owner.permissions; + groupDigit = permSetToDigit permissions.group.permissions; + otherDigit = permSetToDigit permissions.other.permissions; + in + toString ownerDigit + toString groupDigit + toString otherDigit; + + # Get the option names from both submodules to automatically determine which are impermanence-specific + regularDatasetEval = lib.evalModules { + modules = [datasetSubmodules]; + specialArgs = args; + }; + impermanenceDatasetEval = lib.evalModules { + modules = [impermanenceDatasetSubmodule]; + specialArgs = args; + }; + + regularDatasetOptions = builtins.attrNames regularDatasetEval.options; + impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; + + # Find options that are only in impermanence datasets (not in regular ZFS datasets) + impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; +in { + options.storage = { + impermanence = { + enable = lib.mkEnableOption "should impermanence be enabled for this system"; + + datasets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + }; + }; + + config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ + { + assertions = [ + { + assertion = config.storage.zfs.enable; + message = "storage.impermanence can not be used without storage.zfs."; + } + ]; + + system.activationScripts = { + # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 + "createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; + + "var-lib-private-permissions" = lib.mkIf config.storage.generateBase { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/replicate/system/root/var/lib/private + chmod 0700 /persist/replicate/system/root/var/lib/private + ''; + }; + }; + + programs.fuse.userAllowOther = true; + + # Suppress sudo lecture on every boot since impermanence wipes the lecture status file + security.sudo.extraConfig = "Defaults lecture=never"; + + fileSystems = + lib.mapAttrs' ( + datasetName: dataset: + lib.nameValuePair "/${datasetName}" { + device = "rpool/${datasetName}"; + fsType = "zfs"; + neededForBoot = true; + } + ) + (lib.filterAttrs ( + datasetName: dataset: dataset.impermanence.enable + ) + config.storage.impermanence.datasets); + + environment.persistence = + lib.mapAttrs (datasetName: dataset: { + enable = true; + hideMounts = true; + persistentStoragePath = "/${datasetName}"; + directories = lib.mapAttrsToList (path: dirConfig: { + directory = path; + user = dirConfig.owner.name; + group = dirConfig.group.name; + mode = permissionsToMode dirConfig; + }) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories); + files = lib.mapAttrsToList (path: fileConfig: { + file = path; + parentDirectory = { + user = fileConfig.owner.name; + group = fileConfig.group.name; + mode = permissionsToMode fileConfig; + }; + }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); + }) + (lib.filterAttrs ( + datasetName: dataset: let + enabledDirectories = lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories; + enabledFiles = lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files; + in + (enabledDirectories != {}) || (enabledFiles != {}) + ) + (lib.filterAttrs ( + datasetName: dataset: dataset.impermanence.enable + ) + config.storage.impermanence.datasets)); + } + (lib.mkIf config.storage.zfs.enable { + storage.zfs.datasets = + lib.mapAttrs ( + datasetName: dataset: + builtins.removeAttrs dataset impermanenceOnlyOptions + ) + config.storage.impermanence.datasets; + }) + ]); +} diff --git a/modules/nixos-modules/storage/storage.nix b/modules/nixos-modules/storage/storage.nix new file mode 100644 index 0000000..771d661 --- /dev/null +++ b/modules/nixos-modules/storage/storage.nix @@ -0,0 +1,216 @@ +args @ { + lib, + config, + ... +}: let + datasetSubmodule = (import ./submodules/dataset.nix) args; + impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args; + + # Get the option names from both submodules to automatically determine which are impermanence-specific + regularDatasetEval = lib.evalModules { + modules = [datasetSubmodule]; + specialArgs = args; + }; + impermanenceDatasetEval = lib.evalModules { + modules = [impermanenceDatasetSubmodule]; + specialArgs = args; + }; + + regularDatasetOptions = builtins.attrNames regularDatasetEval.options; + impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; + + # Find options that are only in impermanence datasets (not in regular ZFS datasets) + impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; +in { + options.storage = { + generateBase = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + When enabled, enables automatic generation of base datasets (ephemeral, local, replicate roots). + This allows manual definition of datasets matching an existing system layout for migration purposes. + ''; + }; + datasets = { + ephemeral = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + default = {}; + }; + local = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + replicate = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule); + default = {}; + }; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf (config.storage.zfs.enable && config.storage.generateBase) { + # Create ZFS datasets based on storage.datasets configuration + storage.datasets = { + local = { + "nix" = { + impermanence.enable = false; + type = "zfs_fs"; + mount = "/nix"; + snapshot = { + autoSnapshot = false; + }; + atime = "off"; + relatime = "off"; + }; + }; + }; + }) + (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable && config.storage.generateBase) { + storage.datasets = { + ephemeral = { + "" = { + type = "zfs_fs"; + mount = null; + }; + "system/root" = { + type = "zfs_fs"; + mount = "/"; + snapshot = { + blankSnapshot = true; + }; + }; + }; + # TODO: can we auto set the mount points on these to just be `"/persist/local/${name}"` + local = { + "" = { + mount = "/persist/local"; + }; + }; + # TODO: can we auto set the mount points on these to just be `"/persist/replicate/${name}"` + replicate = { + "" = { + mount = "/persist/replicate"; + }; + "system/root" = { + mount = "/persist/replicate/system/root"; + snapshot = { + autoSnapshot = true; + }; + directories = { + "/var/lib/nixos".enable = true; + "/var/lib/systemd/coredump".enable = true; + }; + files = { + "/etc/machine-id".enable = true; + }; + }; + "home" = { + mount = "/persist/replicate/home"; + snapshot = { + autoSnapshot = true; + }; + }; + "system/var/log" = { + type = "zfs_fs"; + directories = { + "/var/log".enable = true; + }; + }; + }; + }; + + storage.zfs.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "ephemeral" + else "ephemeral/${name}"; + value = dataset; + }) + config.storage.datasets.ephemeral) + ]; + + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/ephemeral/system/root@blank + ''; + + storage.impermanence.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/local" + else "persist/local/${name}"; + value = dataset; + }) + config.storage.datasets.local) + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/replicate" + else "persist/replicate/${name}"; + value = dataset; + }) + config.storage.datasets.replicate) + ]; + }) + (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable && config.storage.generateBase) { + storage.datasets = { + # Base organizational datasets (only needed when impermanence is disabled) + local = { + "" = { + type = "zfs_fs"; + mount = null; + }; + "root" = { + type = "zfs_fs"; + mount = "/"; + compression = "lz4"; + acltype = "posixacl"; + relatime = "on"; + xattr = "sa"; + snapshot = { + autoSnapshot = true; + blankSnapshot = true; + }; + }; + }; + replicate = { + "" = { + type = "zfs_fs"; + mount = null; + }; + "system/var/log" = { + type = "zfs_fs"; + mount = "/var/log"; + }; + }; + }; + + storage.zfs.datasets = lib.mkMerge [ + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/local" + else "persist/local/${name}"; + value = builtins.removeAttrs dataset impermanenceOnlyOptions; + }) + config.storage.datasets.local) + (lib.mapAttrs' (name: dataset: { + name = + if name == "" + then "persist/replicate" + else "persist/replicate/${name}"; + value = builtins.removeAttrs dataset impermanenceOnlyOptions; + }) + config.storage.datasets.replicate) + ]; + }) + ]; + + # TODO: set up datasets for systemd services that want a dataset created + # TODO: home-manager.users..storage.impermanence.enable + # is false then persist the entire directory of the user + # if true persist home-manager.users..storage.impermanence.datasets + # TODO: systemd.services..storage.datasets persists + # TODO: configure other needed storage modes here +} diff --git a/modules/nixos-modules/storage/submodules/dataset.nix b/modules/nixos-modules/storage/submodules/dataset.nix new file mode 100644 index 0000000..2a45552 --- /dev/null +++ b/modules/nixos-modules/storage/submodules/dataset.nix @@ -0,0 +1,86 @@ +{lib, ...}: {name, ...}: { + options = { + type = lib.mkOption { + type = lib.types.enum ["zfs_fs" "zfs_volume"]; + default = "zfs_fs"; + description = "Type of ZFS dataset (filesystem or volume)"; + }; + + acltype = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["off" "nfsv4" "posixacl"]); + default = null; + description = "Access control list type"; + }; + + relatime = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off"]); + default = null; + description = "Controls when access time is updated"; + }; + + atime = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off"]); + default = null; + description = "Controls whether access time is updated"; + }; + + xattr = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); + default = null; + description = "Extended attribute storage method"; + }; + + compression = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["on" "off" "lz4" "gzip" "zstd" "lzjb" "zle"]); + default = null; + description = "Compression algorithm to use"; + }; + + sync = lib.mkOption { + type = lib.types.nullOr (lib.types.enum ["standard" "always" "disabled"]); + default = null; + description = "Synchronous write behavior"; + }; + + mount = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "Controls the mount point used for this file system"; + default = null; + }; + + encryption = { + enable = lib.mkEnableOption "should encryption be enabled"; + type = lib.mkOption { + type = lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]; + description = "What encryption type to use"; + }; + keyformat = lib.mkOption { + type = lib.types.enum ["raw" "hex" "passphrase"]; + description = "Format of the encryption key"; + }; + keylocation = lib.mkOption { + type = lib.types.str; + description = "Location of the encryption key"; + }; + }; + + snapshot = { + # This option should set this option flag + autoSnapshot = lib.mkEnableOption "Enable automatic snapshots for this dataset"; + # Creates a blank snapshot in the post create hook for rollback purposes + blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; + }; + + recordSize = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Suggested block size for files in the file system"; + }; + + postCreateHook = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Script to run after dataset creation"; + }; + }; +} diff --git a/modules/nixos-modules/storage/submodules/impermanenceDataset.nix b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix new file mode 100644 index 0000000..e4d3584 --- /dev/null +++ b/modules/nixos-modules/storage/submodules/impermanenceDataset.nix @@ -0,0 +1,56 @@ +args @ {lib, ...}: {name, ...}: let + datasetSubmodule = (import ./dataset.nix) args; + pathPermissions = { + read = lib.mkEnableOption "should the path have read permissions"; + write = lib.mkEnableOption "should the path have read permissions"; + execute = lib.mkEnableOption "should the path have read permissions"; + }; + pathTypeSubmodule = {name, ...}: { + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + owner = { + name = lib.mkOption { + type = lib.types.str; + default = "root"; + }; + permissions = pathPermissions; + }; + group = { + name = lib.mkOption { + type = lib.types.str; + default = "root"; + }; + permissions = pathPermissions; + }; + other = { + permissions = pathPermissions; + }; + }; + }; +in { + imports = [ + datasetSubmodule + ]; + + options = { + files = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + directories = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); + default = {}; + }; + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + }; + + config = { + mount = lib.mkDefault "/${name}"; + }; +} diff --git a/modules/nixos-modules/storage/zfs.nix b/modules/nixos-modules/storage/zfs.nix new file mode 100644 index 0000000..2fc6cb4 --- /dev/null +++ b/modules/nixos-modules/storage/zfs.nix @@ -0,0 +1,347 @@ +args @ { + lib, + pkgs, + config, + ... +}: let + datasetSubmodule = (import ./submodules/dataset.nix) args; + + # Hash function for disk names (max 27 chars to fit GPT limitations) + hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + + # Map "stripe" to "" for disko compatibility (disko uses "" for stripe mode) + diskoPoolMode = + if config.storage.zfs.pool.mode == "stripe" + then "" + else config.storage.zfs.pool.mode; + + # Helper to flatten vdevs into list of devices with names + allVdevDevices = lib.lists.flatten (builtins.map ( + vdev: + builtins.map ( + device: + lib.attrsets.nameValuePair (hashDisk device.device) device + ) + vdev + ) + config.storage.zfs.pool.vdevs); + + # Cache devices with names + allCacheDevices = builtins.map ( + device: + lib.attrsets.nameValuePair (hashDisk device.device) device + ) (config.storage.zfs.pool.cache); + + # All devices (vdevs + cache) + allDevices = allVdevDevices ++ allCacheDevices; + + # Boot devices - filter devices that have boot = true + bootDevices = builtins.filter (device: device.value.boot) allDevices; + + # Helper function to convert dataset options to ZFS properties + datasetToZfsOptions = dataset: let + baseOptions = + (lib.attrsets.optionalAttrs (dataset.acltype != null) {acltype = dataset.acltype;}) + // (lib.attrsets.optionalAttrs (dataset.relatime != null) {relatime = dataset.relatime;}) + // (lib.attrsets.optionalAttrs (dataset.atime != null) {atime = dataset.atime;}) + // (lib.attrsets.optionalAttrs (dataset.xattr != null) {xattr = dataset.xattr;}) + // (lib.attrsets.optionalAttrs (dataset.compression != null) {compression = dataset.compression;}) + // (lib.attrsets.optionalAttrs (dataset.sync != null) {sync = dataset.sync;}) + // (lib.attrsets.optionalAttrs (dataset.recordSize != null) {recordSize = dataset.recordSize;}); + + encryptionOptions = lib.attrsets.optionalAttrs (dataset.encryption.enable) ( + (lib.attrsets.optionalAttrs (dataset.encryption ? type) {encryption = dataset.encryption.type;}) + // (lib.attrsets.optionalAttrs (dataset.encryption ? keyformat) {keyformat = dataset.encryption.keyformat;}) + // (lib.attrsets.optionalAttrs (dataset.encryption ? keylocation) {keylocation = dataset.encryption.keylocation;}) + ); + + mountOptions = lib.attrsets.optionalAttrs (dataset ? mount && dataset.mount ? enable) ( + if builtins.isBool dataset.mount.enable + then { + canmount = + if dataset.mount.enable + then "on" + else "off"; + } + else {canmount = dataset.mount.enable;} + ); + + snapshotOptions = lib.attrsets.optionalAttrs (dataset ? snapshot && dataset.snapshot ? autoSnapshot) { + "com.sun:auto-snapshot" = + if dataset.snapshot.autoSnapshot + then "true" + else "false"; + }; + in + baseOptions // encryptionOptions // mountOptions // snapshotOptions; + + # Helper to generate post create hooks + generatePostCreateHook = name: dataset: + dataset.postCreateHook + + (lib.optionalString dataset.snapshot.blankSnapshot '' + zfs snapshot rpool/${name}@blank + ''); + + # Convert datasets to disko format + convertedDatasets = builtins.listToAttrs ( + (lib.attrsets.mapAttrsToList ( + name: dataset: + lib.attrsets.nameValuePair name { + type = dataset.type; + options = datasetToZfsOptions dataset; + mountpoint = dataset.mount or null; + postCreateHook = generatePostCreateHook name dataset; + } + ) + config.storage.zfs.datasets) + ++ (lib.optional (config.storage.zfs.rootDataset != null) ( + lib.attrsets.nameValuePair "" { + type = config.storage.zfs.rootDataset.type; + options = datasetToZfsOptions config.storage.zfs.rootDataset; + mountpoint = config.storage.zfs.rootDataset.mount or null; + postCreateHook = generatePostCreateHook "" config.storage.zfs.rootDataset; + } + )) + ); +in { + options.storage = { + zfs = { + enable = lib.mkEnableOption "Should zfs be enabled on this system."; + + notifications = { + enable = lib.mkEnableOption "are notifications enabled"; + host = lib.mkOption { + type = lib.types.str; + description = "what is the host that we are going to send the email to"; + }; + port = lib.mkOption { + type = lib.types.port; + description = "what port is the host using to receive mail on"; + }; + to = lib.mkOption { + type = lib.types.str; + description = "what account is the email going to be sent to"; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user is the email going to be set from"; + }; + tokenFile = lib.mkOption { + type = lib.types.str; + description = "file containing the password to be used by msmtp for notifications"; + }; + }; + + pool = let + deviceType = + lib.types.coercedTo lib.types.str (device: { + device = device; + boot = false; + }) (lib.types.submodule { + options = { + device = lib.mkOption { + type = lib.types.str; + }; + boot = lib.mkEnableOption "should this device be a boot device"; + }; + }); + in { + encryption = { + enable = lib.mkEnableOption "Should encryption be enabled on this pool."; + keyformat = lib.mkOption { + type = lib.types.enum ["raw" "hex" "passphrase"]; + default = "hex"; + description = "Format of the encryption key"; + }; + keylocation = lib.mkOption { + type = lib.types.str; + default = "prompt"; + description = "Location of the encryption key"; + }; + }; + mode = lib.mkOption { + type = lib.types.enum ["stripe" "mirror" "raidz1" "raidz2" "raidz3"]; + default = "raidz2"; + description = "ZFS redundancy mode for the pool"; + }; + bootPartitionSize = lib.mkOption { + type = lib.types.str; + default = "2G"; + description = "Size of the boot partition on boot drives"; + }; + vdevs = lib.mkOption { + type = lib.types.listOf (lib.types.listOf deviceType); + default = []; + description = "List of vdevs, where each vdev is a list of devices"; + }; + cache = lib.mkOption { + type = lib.types.listOf deviceType; + default = []; + }; + }; + + rootDataset = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule datasetSubmodule); + description = "Root ZFS dataset to create"; + default = null; + }; + + datasets = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule datasetSubmodule); + description = "Additional ZFS datasets to create"; + default = {}; + }; + }; + }; + + config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # Assertion that we have at least one boot device + assertions = [ + { + assertion = (builtins.length bootDevices) > 0; + message = "ZFS configuration requires at least one boot device. Set boot = true for at least one device in your vdevs or cache."; + } + ]; + + # # Warning about disk/dataset mismatches - these would be runtime checks + # warnings = let + # configuredDisks = builtins.map (device: device.device) (builtins.map (dev: dev.value) allDevices); + # diskWarnings = + # lib.optional (config.storage.zfs.enable) + # "ZFS: Please ensure the following disks are available on your system: ${builtins.concatStringsSep ", " configuredDisks}"; + + # configuredDatasets = builtins.attrNames config.storage.zfs.datasets; + # datasetWarnings = + # lib.optional (config.storage.zfs.enable && (builtins.length configuredDatasets) > 0) + # "ZFS: Configured datasets: ${builtins.concatStringsSep ", " configuredDatasets}. Ensure these match your intended ZFS layout."; + # in + # diskWarnings ++ datasetWarnings; + + services.zfs = { + autoScrub.enable = true; + autoSnapshot.enable = true; + }; + + # # Configure disko for ZFS setup + disko.devices = { + disk = builtins.listToAttrs ( + builtins.map ( + drive: + lib.attrsets.nameValuePair (drive.name) { + type = "disk"; + device = "/dev/disk/by-id/${drive.value.device}"; + content = { + type = "gpt"; + partitions = { + ESP = lib.mkIf drive.value.boot { + size = config.storage.zfs.pool.bootPartitionSize; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "rpool"; + }; + }; + }; + }; + } + ) + allDevices + ); + + zpool = { + rpool = { + type = "zpool"; + mode = { + topology = { + type = "topology"; + vdev = + builtins.map (vdev: { + mode = diskoPoolMode; + members = builtins.map (device: hashDisk device.device) vdev; + }) + config.storage.zfs.pool.vdevs; + cache = builtins.map (device: hashDisk device.device) config.storage.zfs.pool.cache; + }; + }; + + options = { + ashift = "12"; + autotrim = "on"; + }; + + rootFsOptions = + { + canmount = "off"; + mountpoint = "none"; + xattr = "sa"; + acltype = "posixacl"; + relatime = "on"; + compression = "lz4"; + "com.sun:auto-snapshot" = "false"; + } + // (lib.attrsets.optionalAttrs config.storage.zfs.pool.encryption.enable { + encryption = "on"; + keyformat = config.storage.zfs.pool.encryption.keyformat; + keylocation = config.storage.zfs.pool.encryption.keylocation; + }); + + datasets = convertedDatasets; + }; + }; + }; + } + (lib.mkIf config.storage.zfs.notifications.enable { + programs.msmtp = { + enable = true; + setSendmail = true; + defaults = { + aliases = "/etc/aliases"; + port = config.storage.zfs.notifications.port; + tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; + tls = "on"; + auth = "login"; + tls_starttls = "off"; + }; + accounts = { + zfs_notifications = { + auth = true; + tls = true; + host = config.storage.zfs.notifications.host; + passwordeval = "cat ${config.storage.zfs.notifications.tokenFile}"; + user = config.storage.zfs.notifications.user; + from = config.storage.zfs.notifications.user; + }; + }; + }; + + services.zfs = { + zed = { + enableMail = true; + + settings = { + ZED_DEBUG_LOG = "/tmp/zed.debug.log"; + ZED_EMAIL_ADDR = [config.storage.zfs.notifications.to]; + ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; + ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; + + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = true; + + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = true; + }; + }; + }; + }) + ]); +} diff --git a/modules/nixos-modules/sync/default.nix b/modules/nixos-modules/sync/default.nix new file mode 100644 index 0000000..5640417 --- /dev/null +++ b/modules/nixos-modules/sync/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./sync.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/sync/storage.nix b/modules/nixos-modules/sync/storage.nix new file mode 100644 index 0000000..61bf855 --- /dev/null +++ b/modules/nixos-modules/sync/storage.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + options = { + services.syncthing.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.syncthing.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.syncthing.enable { + storage.datasets.replicate."system/root" = { + directories = { + "${mountDir}" = lib.mkIf config.services.syncthing.impermanence.enable { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + "${configDir}" = lib.mkIf config.services.syncthing.impermanence.enable { + enable = true; + owner.name = "syncthing"; + group.name = "syncthing"; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/sync/sync.nix b/modules/nixos-modules/sync/sync.nix new file mode 100644 index 0000000..28b6e38 --- /dev/null +++ b/modules/nixos-modules/sync/sync.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + syncthingConfiguration, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + config = lib.mkMerge [ + { + systemd = lib.mkIf config.services.syncthing.enable { + tmpfiles.rules = [ + "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" + "d ${mountDir} 2755 syncthing syncthing -" + "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" + "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" + ]; + }; + } + (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ + { + services.syncthing = { + user = "syncthing"; + group = "syncthing"; + dataDir = "${mountDir}/default"; + configDir = configDir; + overrideDevices = true; + overrideFolders = true; + configuration = syncthingConfiguration; + deviceName = config.networking.hostName; + }; + } + ])) + ]; +} diff --git a/modules/nixos-modules/system.nix b/modules/nixos-modules/system.nix new file mode 100644 index 0000000..b839067 --- /dev/null +++ b/modules/nixos-modules/system.nix @@ -0,0 +1,13 @@ +{...}: { + nix = { + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 7d"; + }; + optimise = { + automatic = true; + dates = ["weekly"]; + }; + }; +} diff --git a/modules/nixos-modules/tailscale/default.nix b/modules/nixos-modules/tailscale/default.nix new file mode 100644 index 0000000..7a283e8 --- /dev/null +++ b/modules/nixos-modules/tailscale/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./tailscale.nix + ./storage.nix + ]; +} diff --git a/modules/nixos-modules/tailscale/storage.nix b/modules/nixos-modules/tailscale/storage.nix new file mode 100644 index 0000000..7ac7e9a --- /dev/null +++ b/modules/nixos-modules/tailscale/storage.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: let + tailscale_data_directory = "/var/lib/tailscale"; +in { + options = { + services.tailscale.impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.tailscale.enable && config.storage.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.tailscale.enable { + storage.datasets.replicate."system/root" = { + directories."${tailscale_data_directory}" = lib.mkIf config.services.tailscale.impermanence.enable { + enable = true; + owner.name = "root"; + group.name = "root"; + }; + }; + }; +} diff --git a/modules/nixos-modules/tailscale/tailscale.nix b/modules/nixos-modules/tailscale/tailscale.nix new file mode 100644 index 0000000..06899b1 --- /dev/null +++ b/modules/nixos-modules/tailscale/tailscale.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + ... +}: { + options = { + host.tailscale = { + enable = lib.mkEnableOption "should tailscale be enabled on this computer"; + }; + }; + + config = lib.mkIf config.services.tailscale.enable ( + lib.mkMerge [ + { + # any configs we want shared between all machines + } + ] + ); +} diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix new file mode 100644 index 0000000..9cef952 --- /dev/null +++ b/modules/nixos-modules/users.nix @@ -0,0 +1,432 @@ +{ + lib, + config, + inputs, + ... +}: let + SOPS_AGE_KEY_DIRECTORY = import ../../const/sops_age_key_directory.nix; + + host = config.host; + + principleUsers = host.principleUsers; + terminalUsers = host.terminalUsers; + normalUsers = host.normalUsers; + + uids = { + leyla = 1000; + eve = 1002; + # ester = 1003; + # ivy = 1004; + jellyfin = 2000; + forgejo = 2002; + hass = 2004; + syncthing = 2007; + ollama = 2008; + git = 2009; + immich = 2010; + qbittorrent = 2011; + paperless = 2012; + actual = 2013; + radarr = 2014; + sonarr = 2015; + bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; + }; + + gids = { + leyla = 1000; + eve = 1002; + # ester = 1003 + # ivy = 1004; + users = 100; + jellyfin_media = 2001; + jellyfin = 2000; + forgejo = 2002; + hass = 2004; + syncthing = 2007; + ollama = 2008; + git = 2009; + immich = 2010; + qbittorrent = 2011; + paperless = 2012; + actual = 2013; + radarr = 2014; + sonarr = 2015; + bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; + }; + + users = config.users.users; + leyla = users.leyla.name; + eve = users.eve.name; +in { + config = lib.mkMerge [ + { + # principle users are by definition trusted + nix.settings.trusted-users = builtins.map (user: user.name) principleUsers; + + # we should only be able to ssh into principle users of a computer who are also set up for terminal access + services.openssh.settings.AllowUsers = builtins.map (user: user.name) (lib.lists.intersectLists terminalUsers principleUsers); + + # we need to set up env variables to nix can find keys to decrypt passwords on rebuild + environment = { + sessionVariables = { + SOPS_AGE_KEY_DIRECTORY = SOPS_AGE_KEY_DIRECTORY; + SOPS_AGE_KEY_FILE = "${SOPS_AGE_KEY_DIRECTORY}/key.txt"; + }; + }; + + # set up user passwords + sops = { + defaultSopsFormat = "yaml"; + gnupg.sshKeyPaths = []; + + age = { + keyFile = "/var/lib/sops-nix/key.txt"; + sshKeyPaths = []; + # generateKey = true; + }; + + secrets = { + "passwords/leyla" = { + neededForUsers = true; + sopsFile = "${inputs.secrets}/user-passwords.yaml"; + }; + "passwords/eve" = { + neededForUsers = true; + sopsFile = "${inputs.secrets}/user-passwords.yaml"; + }; + }; + }; + + users = { + mutableUsers = false; + users = { + leyla = { + uid = lib.mkForce uids.leyla; + name = lib.mkForce host.users.leyla.name; + description = "Leyla"; + extraGroups = + (lib.lists.optionals host.users.leyla.isNormalUser ["networkmanager"]) + ++ (lib.lists.optionals host.users.leyla.isPrincipleUser ["wheel" "dialout" "docker"]) + ++ (lib.lists.optionals host.users.leyla.isDesktopUser ["adbusers"]); + hashedPasswordFile = config.sops.secrets."passwords/leyla".path; + isNormalUser = host.users.leyla.isNormalUser; + isSystemUser = !host.users.leyla.isNormalUser; + group = config.users.users.leyla.name; + }; + + eve = { + uid = lib.mkForce uids.eve; + name = lib.mkForce host.users.eve.name; + description = "Eve"; + extraGroups = + lib.optionals host.users.eve.isNormalUser ["networkmanager"] + ++ (lib.lists.optionals host.users.eve.isPrincipleUser ["wheel"]); + hashedPasswordFile = config.sops.secrets."passwords/eve".path; + isNormalUser = host.users.eve.isNormalUser; + isSystemUser = !host.users.eve.isNormalUser; + group = config.users.users.eve.name; + }; + + jellyfin = { + uid = lib.mkForce uids.jellyfin; + isSystemUser = true; + group = config.users.users.jellyfin.name; + }; + + forgejo = { + uid = lib.mkForce uids.forgejo; + isSystemUser = true; + group = config.users.users.forgejo.name; + }; + + hass = { + uid = lib.mkForce uids.hass; + isSystemUser = true; + group = config.users.users.hass.name; + }; + + syncthing = { + uid = lib.mkForce uids.syncthing; + isSystemUser = true; + group = config.users.users.syncthing.name; + }; + + ollama = { + uid = lib.mkForce uids.ollama; + isSystemUser = true; + group = config.users.users.ollama.name; + }; + + git = { + uid = lib.mkForce uids.git; + isSystemUser = !config.services.forgejo.enable; + isNormalUser = config.services.forgejo.enable; + group = config.users.users.git.name; + }; + + immich = { + uid = lib.mkForce uids.immich; + isSystemUser = true; + group = config.users.users.immich.name; + }; + + qbittorrent = { + uid = lib.mkForce uids.qbittorrent; + isSystemUser = true; + group = config.users.users.qbittorrent.name; + }; + + paperless = { + uid = lib.mkForce uids.paperless; + isSystemUser = true; + group = config.users.users.paperless.name; + }; + + actual = { + uid = lib.mkForce uids.actual; + isSystemUser = true; + group = config.users.users.actual.name; + }; + + radarr = { + uid = lib.mkForce uids.radarr; + isSystemUser = true; + group = config.users.users.radarr.name; + }; + + sonarr = { + uid = lib.mkForce uids.sonarr; + isSystemUser = true; + group = config.users.users.sonarr.name; + }; + + bazarr = { + uid = lib.mkForce uids.bazarr; + isSystemUser = true; + group = config.users.users.bazarr.name; + }; + + lidarr = { + uid = lib.mkForce uids.lidarr; + isSystemUser = true; + group = config.users.users.lidarr.name; + }; + + crab-hole = { + uid = lib.mkForce uids.crab-hole; + isSystemUser = true; + group = config.users.users.crab-hole.name; + }; + }; + + groups = { + leyla = { + gid = lib.mkForce gids.leyla; + members = [ + leyla + ]; + }; + + eve = { + gid = lib.mkForce gids.eve; + members = [ + eve + ]; + }; + + users = { + gid = lib.mkForce gids.users; + members = [ + leyla + eve + ]; + }; + + jellyfin_media = { + gid = lib.mkForce gids.jellyfin_media; + members = [ + users.jellyfin.name + users.radarr.name + users.sonarr.name + users.bazarr.name + users.lidarr.name + leyla + eve + ]; + }; + + jellyfin = { + gid = lib.mkForce gids.jellyfin; + members = [ + users.jellyfin.name + # leyla + ]; + }; + + forgejo = { + gid = lib.mkForce gids.forgejo; + members = [ + users.forgejo.name + # leyla + ]; + }; + + hass = { + gid = lib.mkForce gids.hass; + members = [ + users.hass.name + # leyla + ]; + }; + + syncthing = { + gid = lib.mkForce gids.syncthing; + members = [ + users.syncthing.name + leyla + eve + ]; + }; + + ollama = { + gid = lib.mkForce gids.ollama; + members = [ + users.ollama.name + ]; + }; + + git = { + gid = lib.mkForce gids.git; + members = [ + users.git.name + ]; + }; + + immich = { + gid = lib.mkForce gids.immich; + members = [ + users.immich.name + # leyla + ]; + }; + + qbittorrent = { + gid = lib.mkForce gids.qbittorrent; + members = [ + users.qbittorrent.name + leyla + ]; + }; + + paperless = { + gid = lib.mkForce gids.paperless; + members = [ + users.paperless.name + ]; + }; + + actual = { + gid = lib.mkForce gids.actual; + members = [ + users.actual.name + ]; + }; + + radarr = { + gid = lib.mkForce gids.radarr; + members = [ + users.radarr.name + ]; + }; + + sonarr = { + gid = lib.mkForce gids.sonarr; + members = [ + users.sonarr.name + ]; + }; + + bazarr = { + gid = lib.mkForce gids.bazarr; + members = [ + users.bazarr.name + ]; + }; + + lidarr = { + gid = lib.mkForce gids.lidarr; + members = [ + users.lidarr.name + ]; + }; + + crab-hole = { + gid = lib.mkForce gids.crab-hole; + members = [ + users.crab-hole.name + ]; + }; + }; + }; + } + (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ + { + # sops age key needs to be available to pre persist for user generation + storage.datasets.local."system/sops" = { + type = "zfs_fs"; + mount = SOPS_AGE_KEY_DIRECTORY; + atime = "off"; + relatime = "off"; + impermanence.enable = false; + }; + } + (lib.mkIf (!config.storage.impermanence.enable) { + storage.datasets.replicate = lib.mkMerge ( + builtins.map (user: { + "home/${user.name}" = { + type = "zfs_fs"; + mount = "/home/${user.name}"; + snapshot.autoSnapshot = true; + }; + }) + normalUsers + ); + }) + (lib.mkIf config.storage.impermanence.enable { + storage.datasets.ephemeral = lib.mkMerge ( + builtins.map (user: { + "home/${user.name}" = { + type = "zfs_fs"; + mount = "/home/${user.name}"; + snapshot.blankSnapshot = true; + }; + }) + normalUsers + ); + + # Post resume commands to rollback user home datasets to blank snapshots + # Only add these when generateBase is true -- when false, the legacy + # storage config is responsible for providing rollback commands with + # the correct (old) dataset paths. + boot.initrd.postResumeCommands = lib.mkIf config.storage.generateBase (lib.mkAfter ( + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") + normalUsers) + )); + + # TODO: I don't think we need this anymore but I have not tested it + # Create persist home directories with proper permissions + # systemd = { + # tmpfiles.rules = + # builtins.map ( + # user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" + # ) + # normalUsers; + # }; + }) + ])) + ]; +} diff --git a/modules/nixos/desktop.nix b/modules/nixos/desktop.nix deleted file mode 100644 index 91b8ddc..0000000 --- a/modules/nixos/desktop.nix +++ /dev/null @@ -1,86 +0,0 @@ -{...}: { - flake.nixosModules.nixos-desktop = { - lib, - pkgs, - config, - ... - }: { - options.host.desktop.enable = lib.mkEnableOption "should desktop configuration be enabled"; - - config = lib.mkMerge [ - { - host.desktop.enable = lib.mkDefault true; - } - (lib.mkIf config.host.desktop.enable { - environment.gnome.excludePackages = with pkgs; [ - xterm # default terminal - atomix # puzzle game - cheese # webcam tool - epiphany # web browser - geary # email reader - gedit # text editor - decibels # audio player - gnome-characters # character set viewer - gnome-music # music player - gnome-photos # photo viewer - gnome-logs # log viewer - gnome-maps # map viewer - gnome-tour # welcome tour - hitori # sudoku game - iagno # go game - tali # poker game - yelp # help viewer - ]; - services = { - # Enable CUPS to print documents. - printing = { - enable = true; - drivers = [ - pkgs.hplip - pkgs.gutenprint - pkgs.gutenprintBin - ]; - }; - - xserver = { - # Enable the X11 windowing system. - enable = true; - - # Get rid of xTerm - desktopManager.xterm.enable = false; - excludePackages = with pkgs; [ - xterm - ]; - }; - - # Enable the GNOME Desktop Environment. - displayManager.gdm.enable = true; - desktopManager.gnome.enable = true; - - pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - - # If you want to use JACK applications, uncomment this - #jack.enable = true; - - # use the example session manager (no others are packaged yet so this is enabled by default, - # no need to redefine it in your config for now) - #media-session.enable = true; - }; - automatic-timezoned = { - enable = true; - }; - - # Enable sound with pipewire. - pulseaudio.enable = false; - }; - - # enable RealtimeKit for pulse audio - security.rtkit.enable = true; - }) - ]; - }; -} diff --git a/modules/nixos/hardware.nix b/modules/nixos/hardware.nix deleted file mode 100644 index f336b8c..0000000 --- a/modules/nixos/hardware.nix +++ /dev/null @@ -1,36 +0,0 @@ -{...}: { - flake.nixosModules.nixos-hardware = { - lib, - config, - pkgs, - ... - }: { - options.host.hardware = { - piperMouse = { - enable = lib.mkEnableOption "host has a piper mouse"; - }; - viaKeyboard = { - enable = lib.mkEnableOption "host has a via keyboard"; - }; - openRGB = { - enable = lib.mkEnableOption "host has open rgb hardware"; - }; - graphicsAcceleration = { - enable = lib.mkEnableOption "host has a gpu for graphical acceleration"; - }; - directAccess = { - enable = lib.mkEnableOption "can a host be used on its own"; - }; - }; - config = lib.mkMerge [ - (lib.mkIf config.host.hardware.piperMouse.enable { - services.ratbagd.enable = true; - }) - (lib.mkIf config.host.hardware.viaKeyboard.enable { - hardware.keyboard.qmk.enable = true; - - services.udev.packages = [pkgs.via]; - }) - ]; - }; -} diff --git a/modules/nixos/home-manager-adaptors/default.nix b/modules/nixos/home-manager-adaptors/default.nix deleted file mode 100644 index 1ff34a6..0000000 --- a/modules/nixos/home-manager-adaptors/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -# modules in this folder are to adapt home-manager modules configs to nixos-module configs -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.home-manager-adaptors = { - imports = [ - mod.home-manager-flipperzero - mod.home-manager-i18n - mod.home-manager-openssh - mod.home-manager-steam - ]; - }; -} diff --git a/modules/nixos/home-manager-adaptors/flipperzero.nix b/modules/nixos/home-manager-adaptors/flipperzero.nix deleted file mode 100644 index 228912b..0000000 --- a/modules/nixos/home-manager-adaptors/flipperzero.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.home-manager-flipperzero = { - lib, - config, - ... - }: let - home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; - in { - hardware.flipperzero.enable = lib.lists.any (home-user: home-user.hardware.flipperzero.enable) home-users; - }; -} diff --git a/modules/nixos/home-manager-adaptors/i18n.nix b/modules/nixos/home-manager-adaptors/i18n.nix deleted file mode 100644 index 8d3b66a..0000000 --- a/modules/nixos/home-manager-adaptors/i18n.nix +++ /dev/null @@ -1,28 +0,0 @@ -{...}: { - flake.nixosModules.home-manager-i18n = { - lib, - config, - ... - }: let - home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; - in { - config = { - i18n.supportedLocales = - lib.unique - (builtins.map (l: (lib.replaceStrings ["utf8" "utf-8" "UTF8"] ["UTF-8" "UTF-8" "UTF-8"] l) + "/UTF-8") ( - [ - "C.UTF-8" - "en_US.UTF-8" - config.i18n.defaultLocale - ] - ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings)) - ++ ( - map (user-config: user-config.i18n.defaultLocale) home-users - ) - ++ (lib.lists.flatten ( - map (user-config: lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") user-config.i18n.extraLocaleSettings)) home-users - )) - )); - }; - }; -} diff --git a/modules/nixos/home-manager-adaptors/openssh.nix b/modules/nixos/home-manager-adaptors/openssh.nix deleted file mode 100644 index ad78906..0000000 --- a/modules/nixos/home-manager-adaptors/openssh.nix +++ /dev/null @@ -1,13 +0,0 @@ -{...}: { - flake.nixosModules.home-manager-openssh = { - config, - lib, - ... - }: { - users.users = - lib.attrsets.mapAttrs (name: value: { - openssh.authorizedKeys.keys = value.programs.openssh.authorizedKeys; - }) - config.home-manager.users; - }; -} diff --git a/modules/nixos/home-manager-adaptors/steam.nix b/modules/nixos/home-manager-adaptors/steam.nix deleted file mode 100644 index f387bcf..0000000 --- a/modules/nixos/home-manager-adaptors/steam.nix +++ /dev/null @@ -1,20 +0,0 @@ -{...}: { - flake.nixosModules.home-manager-steam = { - lib, - config, - ... - }: let - setupSteam = - lib.lists.any - (value: value) - (lib.attrsets.mapAttrsToList (name: value: value.programs.steam.enable) config.home-manager.users); - in { - config = lib.mkIf setupSteam { - programs.steam = { - enable = true; - # TODO: figure out how to not install steam here - # package = lib.mkDefault pkgs.emptyFile; - }; - }; - }; -} diff --git a/modules/nixos/i18n.nix b/modules/nixos/i18n.nix deleted file mode 100644 index a8d67d7..0000000 --- a/modules/nixos/i18n.nix +++ /dev/null @@ -1,5 +0,0 @@ -{...}: { - flake.nixosModules.nixos-i18n = {...}: { - i18n.defaultLocale = "en_IE.UTF-8"; - }; -} diff --git a/modules/nixos/nixos-modules.nix b/modules/nixos/nixos-modules.nix deleted file mode 100644 index 6a5c403..0000000 --- a/modules/nixos/nixos-modules.nix +++ /dev/null @@ -1,21 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.nixos-modules-all = { - imports = [ - mod.nixos-system - mod.nixos-hardware - mod.nixos-users - mod.nixos-desktop - mod.nixos-ssh - mod.nixos-i18n - mod.storage - mod.home-manager-adaptors - mod.programs - ]; - - nixpkgs.config.permittedInsecurePackages = [ - "dotnet-sdk-6.0.428" - ]; - }; -} diff --git a/modules/nixos/overlays/default.nix b/modules/nixos/overlays/default.nix deleted file mode 100644 index 5cf6ceb..0000000 --- a/modules/nixos/overlays/default.nix +++ /dev/null @@ -1,42 +0,0 @@ -# External overlays from flake inputs -{inputs, ...}: { - flake.commonModules.overlays = { - nixpkgs.overlays = [ - inputs.nix-vscode-extensions.overlays.default - # Add noita_entangled_worlds from upstream flake to pkgs - (final: prev: { - noita_entangled_worlds = inputs.noita-entangled-worlds.packages.${prev.stdenv.hostPlatform.system}.noita-proxy; - }) - # Workaround: some extensions in nix-vscode-extensions have invalid semver - # engine versions (e.g. 1.112.01907 with leading zeros) that cause - # forVSCodeVersion to throw. This tries the version-filtered set per site - # and falls back to unfiltered only for sites with bad semver data. - (final: prev: { - nix-vscode-extensions = - prev.nix-vscode-extensions - // { - forVSCodeVersion = vscodeVersion: let - filtered = prev.nix-vscode-extensions.forVSCodeVersion vscodeVersion; - unfiltered = prev.nix-vscode-extensions; - safeSite = site: let - # builtins.attrNames forces the filter to run on all extensions, - # which triggers semver parsing. If any extension has an invalid - # version, this catches the error and falls back to the unfiltered set. - tried = builtins.tryEval ( - builtins.seq (builtins.length (builtins.attrNames filtered.${site})) filtered.${site} - ); - in - if tried.success - then tried.value - else unfiltered.${site}; - in - filtered - // { - open-vsx = safeSite "open-vsx"; - vscode-marketplace = safeSite "vscode-marketplace"; - }; - }; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/codium-extensions/default.nix b/modules/nixos/pkgs/codium-extensions/default.nix deleted file mode 100644 index a76ad9f..0000000 --- a/modules/nixos/pkgs/codium-extensions/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: { - flake.commonModules.codium-extensions = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - codium-extensions = {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/default.nix b/modules/nixos/pkgs/default.nix deleted file mode 100644 index e25058a..0000000 --- a/modules/nixos/pkgs/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -# Package modules providing custom packages via nixpkgs overlays -{config, ...}: let - pkg = config.flake.commonModules; -in { - flake.commonModules.pkgs = { - imports = [ - pkg.webtoon-dl - pkg.prostudiomasters - pkg.gdx-liftoff - pkg.e621-downloader - pkg.h3-c-lib - pkg.mapillary-uploader - pkg.panoramax - pkg.sgblur - pkg.codium-extensions - pkg.firefox-extensions - pkg.python - ]; - }; -} diff --git a/modules/nixos/pkgs/e621-downloader.nix b/modules/nixos/pkgs/e621-downloader.nix deleted file mode 100644 index 8f4e291..0000000 --- a/modules/nixos/pkgs/e621-downloader.nix +++ /dev/null @@ -1,46 +0,0 @@ -{...}: let - package = { - lib, - rustPlatform, - fetchFromGitHub, - pkg-config, - openssl, - ... - }: - rustPlatform.buildRustPackage rec { - pname = "e621-downloader"; - version = "1.7.2"; - - src = fetchFromGitHub { - owner = "McSib"; - repo = "e621_downloader"; - rev = version; - hash = "sha256-4z+PrCv8Mlp0VOJ5Akv1TXrJir1Ws/+45a6VCZGuCtk="; - }; - - cargoHash = "sha256-/yqNYjP7BuFQWilL2Ty+E5rd8qXj30twteptHx7cLRo="; - - nativeBuildInputs = [ - pkg-config - ]; - - buildInputs = [ - openssl - ]; - - meta = with lib; { - description = "E621 and E926 downloader made in Rust"; - homepage = "https://github.com/McSib/e621_downloader"; - license = licenses.asl20; - mainProgram = "e621_downloader"; - }; - }; -in { - flake.commonModules.e621-downloader = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - e621-downloader = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/firefox-extensions/default.nix b/modules/nixos/pkgs/firefox-extensions/default.nix deleted file mode 100644 index 5ea1528..0000000 --- a/modules/nixos/pkgs/firefox-extensions/default.nix +++ /dev/null @@ -1,25 +0,0 @@ -{config, ...}: { - flake.commonModules.firefox-extensions = { - pkgs, - inputs, - ... - }: let - inherit (inputs.firefox-addons.lib.${pkgs.stdenv.hostPlatform.system}) buildFirefoxXpiAddon; - in { - nixpkgs.overlays = [ - (final: prev: { - firefox-extensions = { - italiano-it-language-pack = pkgs.callPackage config.flake.commonModules.firefox-italiano-it-language-pack { - inherit buildFirefoxXpiAddon; - }; - dizionario-italiano = pkgs.callPackage config.flake.commonModules.firefox-dizionario-italiano { - inherit buildFirefoxXpiAddon; - }; - deutsch-de-language-pack = pkgs.callPackage config.flake.commonModules.firefox-deutsch-de-language-pack { - inherit buildFirefoxXpiAddon; - }; - }; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/firefox-extensions/deutsch-de-language-pack.nix b/modules/nixos/pkgs/firefox-extensions/deutsch-de-language-pack.nix deleted file mode 100644 index 4dfa740..0000000 --- a/modules/nixos/pkgs/firefox-extensions/deutsch-de-language-pack.nix +++ /dev/null @@ -1,22 +0,0 @@ -{...}: let - package = { - lib, - buildFirefoxXpiAddon, - ... - }: - buildFirefoxXpiAddon rec { - pname = "deutsch-de-language-pack"; - version = "145.0.20251106.194447"; - addonId = "langpack-de@firefox.mozilla.org"; - url = "https://addons.mozilla.org/firefox/downloads/file/4614311/deutsch_de_language_pack-${version}.xpi"; - sha256 = "aaaa95c29984fb3802a5e7edb6b7e5020c391d81f389b8a8133c163959ea4299"; - meta = with lib; { - description = "Firefox Language Pack for Deutsch (de) – German"; - license = licenses.mpl20; - mozPermissions = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.firefox-deutsch-de-language-pack = package; -} diff --git a/modules/nixos/pkgs/firefox-extensions/dizionario-italiano.nix b/modules/nixos/pkgs/firefox-extensions/dizionario-italiano.nix deleted file mode 100644 index 4a652d5..0000000 --- a/modules/nixos/pkgs/firefox-extensions/dizionario-italiano.nix +++ /dev/null @@ -1,22 +0,0 @@ -{...}: let - package = { - lib, - buildFirefoxXpiAddon, - ... - }: - buildFirefoxXpiAddon rec { - pname = "dizionario-italiano"; - version = "5.1"; - addonId = "it-IT@dictionaries.addons.mozilla.org"; - url = "https://addons.mozilla.org/firefox/downloads/file/3693497/dizionario_italiano-${version}.xpi"; - sha256 = "90b173ffdde34a77108152a5ff51879767b1dd84e0aa0dfb7b2bab94cd2e7f53"; - meta = with lib; { - description = "Add support for Italian to spellchecking"; - license = licenses.gpl3; - mozPermissions = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.firefox-dizionario-italiano = package; -} diff --git a/modules/nixos/pkgs/firefox-extensions/italiano-it-language-pack.nix b/modules/nixos/pkgs/firefox-extensions/italiano-it-language-pack.nix deleted file mode 100644 index fb8674b..0000000 --- a/modules/nixos/pkgs/firefox-extensions/italiano-it-language-pack.nix +++ /dev/null @@ -1,22 +0,0 @@ -{...}: let - package = { - lib, - buildFirefoxXpiAddon, - ... - }: - buildFirefoxXpiAddon rec { - pname = "italiano-it-language-pack"; - version = "145.0.20251106.194447"; - addonId = "langpack-it@firefox.mozilla.org"; - url = "https://addons.mozilla.org/firefox/downloads/file/4614309/italiano_it_language_pack-${version}.xpi"; - sha256 = "1eb271cedbf326543e222ba1b9a1da62fceef9d3c523ac02a098df296f155038"; - meta = with lib; { - description = "Firefox Language Pack for Italiano (it) – Italian"; - license = licenses.mpl20; - mozPermissions = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.firefox-italiano-it-language-pack = package; -} diff --git a/modules/nixos/pkgs/gdx-liftoff.nix b/modules/nixos/pkgs/gdx-liftoff.nix deleted file mode 100644 index 88206ea..0000000 --- a/modules/nixos/pkgs/gdx-liftoff.nix +++ /dev/null @@ -1,58 +0,0 @@ -{...}: let - package = { - stdenv, - fetchurl, - makeWrapper, - jdk, - lib, - libGL, - libx11, - libxcursor, - libxext, - libxrandr, - libxxf86vm, - ... - }: - stdenv.mkDerivation rec { - pname = "gdx-liftoff"; - version = "1.13.5.1"; - - src = fetchurl { - url = "https://github.com/libgdx/gdx-liftoff/releases/download/v${version}/gdx-liftoff-${version}.jar"; - hash = "sha256-9vCXGNGwI/P4VmcdIzTv2GPAX8bZb7nkfopaRAf6yMA="; - }; - - dontUnpack = true; - - nativeBuildInputs = [makeWrapper]; - - runtimeDependencies = lib.makeLibraryPath [ - # glfw - libGL - libx11 - libxcursor - libxext - libxrandr - libxxf86vm - ]; - - installPhase = '' - runHook preInstall - - install -Dm644 $src $out/lib/gdx-liftoff-${version}.jar - - makeWrapper ${lib.getExe jdk} $out/bin/gdx-liftoff-${version} \ - --append-flags "-jar $out/lib/gdx-liftoff-${version}.jar"\ - ${lib.optionalString stdenv.hostPlatform.isLinux "--prefix LD_LIBRARY_PATH : ${runtimeDependencies}"} - runHook postInstall - ''; - }; -in { - flake.commonModules.gdx-liftoff = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - gdx-liftoff = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/h3-c-lib.nix b/modules/nixos/pkgs/h3-c-lib.nix deleted file mode 100644 index 38f9eac..0000000 --- a/modules/nixos/pkgs/h3-c-lib.nix +++ /dev/null @@ -1,47 +0,0 @@ -{...}: let - package = { - lib, - stdenv, - fetchFromGitHub, - cmake, - doxygen, - }: - stdenv.mkDerivation rec { - pname = "h3"; - version = "4.3.0"; - - src = fetchFromGitHub { - owner = "uber"; - repo = "h3"; - rev = "v${version}"; - hash = "sha256-DUILKZ1QvML6qg+WdOxir6zRsgTvk+En6yjeFf6MQBg="; - }; - - nativeBuildInputs = [ - cmake - doxygen - ]; - - cmakeFlags = [ - "-DBUILD_SHARED_LIBS=ON" - "-DBUILD_TESTING=OFF" - ]; - - meta = with lib; { - homepage = "https://github.com/uber/h3"; - description = "Hexagonal hierarchical geospatial indexing system"; - license = licenses.asl20; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.h3-c-lib = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - # Override h3 C library to version 4.3.0 - h3 = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/mapillary-uploader.nix b/modules/nixos/pkgs/mapillary-uploader.nix deleted file mode 100644 index 6fe1105..0000000 --- a/modules/nixos/pkgs/mapillary-uploader.nix +++ /dev/null @@ -1,49 +0,0 @@ -{...}: let - package = { - lib, - fetchurl, - appimageTools, - }: let - pname = "mapillary-uploader"; - version = "4.7.2"; - - src = fetchurl { - url = "http://tools.mapillary.com/uploader/download/linux/${version}"; - name = "mapillary-uploader.AppImage"; - sha256 = "sha256-hpWdfeuhYylO+SFD3BsKI0s/xtObCDd5OcuJ6i/aEuI="; - }; - - appimageContents = appimageTools.extractType2 { - inherit pname version src; - }; - in - appimageTools.wrapType2 { - inherit pname version src; - - extraInstallCommands = '' - # Install desktop file - install -Dm644 ${appimageContents}/mapillary-desktop-uploader.desktop $out/share/applications/mapillary-uploader.desktop - - # Fix desktop file paths - substituteInPlace $out/share/applications/mapillary-uploader.desktop \ - --replace 'Exec=AppRun' 'Exec=${pname}' - ''; - - meta = with lib; { - description = "Mapillary Desktop Uploader - Upload street-level imagery to Mapillary"; - homepage = "https://www.mapillary.com/"; - license = licenses.unfree; # Mapillary's license terms - maintainers = []; - platforms = ["x86_64-linux"]; - sourceProvenance = with sourceTypes; [binaryNativeCode]; - }; - }; -in { - flake.commonModules.mapillary-uploader = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - mapillary-uploader = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/panoramax.nix b/modules/nixos/pkgs/panoramax.nix deleted file mode 100644 index 9e0886e..0000000 --- a/modules/nixos/pkgs/panoramax.nix +++ /dev/null @@ -1,115 +0,0 @@ -{...}: let - package = { - lib, - fetchFromGitLab, - buildPythonPackage, - flit-core, - flask, - pillow, - requests, - python-dotenv, - authlib, - sentry-sdk, - python-dateutil, - dateparser, - croniter, - pydantic, - flask-cors, - flask-compress, - flask-babel, - flasgger, - yoyo-migrations, - psycopg, - psycopg-pool, - tzdata, - email-validator, - pydantic-extra-types, - python-multipart, - fs, - fs-s3fs, - geopic-tag-reader, - pygeofilter, - pygeoif, - rfeed, - geojson-pydantic, - ... - }: let - pname = "geovisio"; - version = "2.10.0"; - repo = fetchFromGitLab { - owner = "panoramax"; - repo = "server/api"; - rev = version; - hash = "sha256-kCLcrOe7jJdIfmWWOmxQ5dOj8ZG2B7s0qFpHXs02B/E="; - }; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - - src = repo; - - build-system = [ - flit-core - ]; - - dependencies = [ - flask - pillow - requests - python-dotenv - authlib - sentry-sdk - python-dateutil - dateparser - croniter - pydantic - flask-cors - flask-compress - flask-babel - flasgger - yoyo-migrations - psycopg - psycopg-pool - tzdata - email-validator - pydantic-extra-types - python-multipart - fs - fs-s3fs - geopic-tag-reader - pygeofilter - pygeoif - rfeed - geojson-pydantic - # Missing from nixpkgs - may need custom packages: - # flask-executor - ]; - - # Skip tests as they may require network access or specific setup - doCheck = false; - - # Disable runtime dependencies check as many dependencies are not available in nixpkgs - dontCheckRuntimeDeps = true; - - # Disable imports check as many dependencies are not available in nixpkgs - pythonImportsCheck = []; - - meta = with lib; { - description = "Panoramax API client and tools for street-level imagery platform"; - homepage = "https://gitlab.com/panoramax/server/api"; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.panoramax = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - panoramax = pkgs.python3.pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/prostudiomasters.nix b/modules/nixos/pkgs/prostudiomasters.nix deleted file mode 100644 index 3d33350..0000000 --- a/modules/nixos/pkgs/prostudiomasters.nix +++ /dev/null @@ -1,43 +0,0 @@ -{...}: let - package = { - fetchurl, - appimageTools, - writeShellScript, - }: let - pname = "prostudiomasters"; - version = "2.5.6"; - src = fetchurl { - url = "https://download.prostudiomasters.com/linux/ProStudioMasters-${version}.AppImage"; - hash = "sha256-7owOwdcucFfl+JsVj+Seau2KOz0J4P/ep7WrBSNSmbs="; - }; - - # Create the base AppImage wrapper - baseApp = appimageTools.wrapType2 { - inherit pname version src; - }; - - # Create a wrapper script that automatically adds the --in-process-gpu flag - wrapper = writeShellScript "prostudiomasters-wrapper" '' - exec ${baseApp}/bin/prostudiomasters --in-process-gpu "$@" - ''; - in - # Override the base app to use our wrapper script - baseApp.overrideAttrs (oldAttrs: { - buildCommand = - oldAttrs.buildCommand - + '' - # Replace the original binary with our wrapper - rm $out/bin/prostudiomasters - cp ${wrapper} $out/bin/prostudiomasters - chmod +x $out/bin/prostudiomasters - ''; - }); -in { - flake.commonModules.prostudiomasters = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - prostudiomasters = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/python/default.nix b/modules/nixos/pkgs/python/default.nix deleted file mode 100644 index 61615f5..0000000 --- a/modules/nixos/pkgs/python/default.nix +++ /dev/null @@ -1,22 +0,0 @@ -{config, ...}: let - pkg = config.flake.commonModules; -in { - flake.commonModules.python = {...}: { - nixpkgs.overlays = [ - (final: prev: { - python3 = prev.python3.override { - packageOverrides = pythonPrev: pythonFinal: { - h3 = pythonPrev.callPackage pkg.python-h3 {h3 = final.h3;}; - pygeofilter = pythonPrev.callPackage pkg.python-pygeofilter {}; - pygeoif = pythonPrev.callPackage pkg.python-pygeoif {}; - rfeed = pythonPrev.callPackage pkg.python-rfeed {}; - pyexiv2 = pythonPrev.callPackage pkg.python-pyexiv2 {}; - geojson-pydantic = pythonPrev.callPackage pkg.python-geojson-pydantic {}; - geopic-tag-reader = pythonPrev.callPackage pkg.python-geopic-tag-reader {}; - }; - }; - python3Packages = final.python3.pkgs; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/python/geojson-pydantic.nix b/modules/nixos/pkgs/python/geojson-pydantic.nix deleted file mode 100644 index 4d570d3..0000000 --- a/modules/nixos/pkgs/python/geojson-pydantic.nix +++ /dev/null @@ -1,52 +0,0 @@ -{...}: let - package = { - lib, - fetchPypi, - buildPythonPackage, - flit-core, - pydantic, - geojson, - ... - }: let - pname = "geojson_pydantic"; - version = "2.0.0"; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - - src = fetchPypi { - inherit pname version; - hash = "sha256-ti6LRFAt0a1Ri19zkDWoGSSnb5gMvbOk6JFu+RO+JC4="; - }; - - build-system = [ - flit-core - ]; - - dependencies = [ - pydantic - geojson - ]; - - # Skip tests as they may require specific setup - doCheck = false; - - # Disable runtime dependencies check - dontCheckRuntimeDeps = true; - - # Basic imports check - pythonImportsCheck = ["geojson_pydantic"]; - - meta = with lib; { - description = "Pydantic models for GeoJSON objects"; - homepage = "https://github.com/developmentseed/geojson-pydantic"; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.python-geojson-pydantic = package; -} diff --git a/modules/nixos/pkgs/python/geopic-tag-reader.nix b/modules/nixos/pkgs/python/geopic-tag-reader.nix deleted file mode 100644 index 29eecc8..0000000 --- a/modules/nixos/pkgs/python/geopic-tag-reader.nix +++ /dev/null @@ -1,74 +0,0 @@ -{...}: let - package = { - lib, - fetchFromGitLab, - buildPythonPackage, - flit-core, - typer, - xmltodict, - timezonefinder, - pytz, - types-pytz, - types-python-dateutil, - rtree, - python-dateutil, - pyexiv2, - ... - }: let - pname = "geopic-tag-reader"; - version = "1.8.0"; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - - src = fetchFromGitLab { - owner = "panoramax"; - repo = "server/geo-picture-tag-reader"; - rev = version; - sha256 = "0lzf5xxxcdqmq28bpvgpkxf5jxmh2nawwa4rl4yg04bdsi16rf1j"; - }; - - build-system = [ - flit-core - ]; - - dependencies = [ - typer - xmltodict - pyexiv2 - timezonefinder - pytz - types-pytz - types-python-dateutil - rtree - ]; - - optional-dependencies = { - write-exif = [ - python-dateutil - types-python-dateutil - ]; - }; - - # Skip tests as they may require network access or specific setup - doCheck = false; - - # Disable runtime dependencies check as some dependencies might have issues - dontCheckRuntimeDeps = true; - - # Disable imports check initially to avoid dependency issues - pythonImportsCheck = []; - - meta = with lib; { - description = "GeoPic Tag Reader - Python library to read and write standardized metadata from geolocated pictures EXIF metadata"; - homepage = "https://gitlab.com/panoramax/server/geo-picture-tag-reader"; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.python-geopic-tag-reader = package; -} diff --git a/modules/nixos/pkgs/python/h3.nix b/modules/nixos/pkgs/python/h3.nix deleted file mode 100644 index cf4fe7b..0000000 --- a/modules/nixos/pkgs/python/h3.nix +++ /dev/null @@ -1,85 +0,0 @@ -{...}: let - package = { - autoPatchelfHook, - buildPythonPackage, - cmake, - cython, - fetchFromGitHub, - h3, - lib, - ninja, - numpy, - pytestCheckHook, - pytest-cov-stub, - scikit-build-core, - stdenv, - }: - buildPythonPackage rec { - pname = "h3"; - version = "4.3.1"; - pyproject = true; - - # pypi version does not include tests - src = fetchFromGitHub { - owner = "uber"; - repo = "h3-py"; - tag = "v${version}"; - hash = "sha256-zt7zbBgSp2P9q7mObZeQZpW9Szip62dAYdPZ2cGTmi4="; - }; - - dontConfigure = true; - - nativeCheckInputs = [ - pytestCheckHook - pytest-cov-stub - ]; - - build-system = - [ - scikit-build-core - cmake - cython - ninja - ] - ++ lib.optionals stdenv.hostPlatform.isLinux [ - # On Linux the .so files ends up referring to libh3.so instead of the full - # Nix store path. I'm not sure why this is happening! On Darwin it works - # fine. - autoPatchelfHook - ]; - - # This is not needed per-se, it's only added for autoPatchelfHook to work - # correctly. See the note above ^^ - buildInputs = lib.optionals stdenv.hostPlatform.isLinux [h3]; - - dependencies = [numpy]; - - # The following prePatch replaces the h3lib compilation with using the h3 packaged in nixpkgs. - # - # - Remove the h3lib submodule. - # - Patch CMakeLists to avoid building h3lib, and use h3 instead. - prePatch = let - cmakeCommands = '' - include_directories(${lib.getDev h3}/include/h3) - link_directories(${h3}/lib) - ''; - in '' - rm -r src/h3lib - substituteInPlace CMakeLists.txt \ - --replace-fail "add_subdirectory(src/h3lib)" "${cmakeCommands}" \ - --replace-fail "\''${CMAKE_CURRENT_BINARY_DIR}/src/h3lib/src/h3lib/include/h3api.h" "${lib.getDev h3}/include/h3/h3api.h" - ''; - - # Extra check to make sure we can import it from Python - pythonImportsCheck = ["h3"]; - - meta = { - homepage = "https://github.com/uber/h3-py"; - description = "Hierarchical hexagonal geospatial indexing system"; - license = lib.licenses.asl20; - maintainers = [lib.maintainers.kalbasit]; - }; - }; -in { - flake.commonModules.python-h3 = package; -} diff --git a/modules/nixos/pkgs/python/pyexiv2.nix b/modules/nixos/pkgs/python/pyexiv2.nix deleted file mode 100644 index 17b1838..0000000 --- a/modules/nixos/pkgs/python/pyexiv2.nix +++ /dev/null @@ -1,53 +0,0 @@ -{...}: let - package = { - lib, - fetchFromGitHub, - buildPythonPackage, - exiv2, - boost, - pybind11, - setuptools, - ... - }: let - pname = "pyexiv2"; - version = "2.15.3"; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - build-system = [setuptools]; - - src = fetchFromGitHub { - owner = "LeoHsiao1"; - repo = "pyexiv2"; - rev = "v${version}"; - sha256 = "sha256-83bFMaoXncvhRJNcCgkkC7B29wR5pjuLO/EdkQdqxxo="; - }; - - buildInputs = [ - exiv2 - boost - ]; - - nativeBuildInputs = [ - pybind11 - ]; - - # Skip tests as they may require specific test images - doCheck = false; - - # Disable runtime dependencies check initially - dontCheckRuntimeDeps = true; - - meta = with lib; { - description = "Python binding to the library exiv2"; - homepage = "https://github.com/LeoHsiao1/pyexiv2"; - license = licenses.gpl3Plus; - maintainers = []; - platforms = platforms.linux; - }; - }; -in { - flake.commonModules.python-pyexiv2 = package; -} diff --git a/modules/nixos/pkgs/python/pygeofilter.nix b/modules/nixos/pkgs/python/pygeofilter.nix deleted file mode 100644 index 0d90826..0000000 --- a/modules/nixos/pkgs/python/pygeofilter.nix +++ /dev/null @@ -1,56 +0,0 @@ -{...}: let - package = { - lib, - fetchPypi, - buildPythonPackage, - setuptools, - wheel, - lark, - python-dateutil, - shapely, - ... - }: let - pname = "pygeofilter"; - version = "0.3.1"; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - - src = fetchPypi { - inherit pname version; - hash = "sha256-+SvAYiCZ+H/os23nq92GBZ1hWontYIInNwgiI6V44VA="; - }; - - build-system = [ - setuptools - wheel - ]; - - dependencies = [ - lark - python-dateutil - shapely - ]; - - # Skip tests as they may require specific setup - doCheck = false; - - # Disable runtime dependencies check - dontCheckRuntimeDeps = true; - - # Basic imports check - pythonImportsCheck = ["pygeofilter"]; - - meta = with lib; { - description = "A pure Python parser implementation of OGC filtering standards"; - homepage = "https://github.com/geopython/pygeofilter"; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.python-pygeofilter = package; -} diff --git a/modules/nixos/pkgs/python/pygeoif.nix b/modules/nixos/pkgs/python/pygeoif.nix deleted file mode 100644 index 9dac79d..0000000 --- a/modules/nixos/pkgs/python/pygeoif.nix +++ /dev/null @@ -1,52 +0,0 @@ -{...}: let - package = { - lib, - fetchPypi, - buildPythonPackage, - setuptools, - wheel, - typing-extensions, - ... - }: let - pname = "pygeoif"; - version = "1.5.1"; - in - buildPythonPackage { - inherit pname version; - - pyproject = true; - - src = fetchPypi { - inherit pname version; - hash = "sha256-8nprah7Lh66swrUbzFnKeb5w7RKgEE3oYBR4shPdXYE="; - }; - - build-system = [ - setuptools - wheel - ]; - - dependencies = [ - typing-extensions - ]; - - # Skip tests as they may require specific setup - doCheck = false; - - # Disable runtime dependencies check - dontCheckRuntimeDeps = true; - - # Basic imports check - pythonImportsCheck = ["pygeoif"]; - - meta = with lib; { - description = "A basic implementation of the __geo_interface__"; - homepage = "https://github.com/cleder/pygeoif"; - license = licenses.lgpl21Plus; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.python-pygeoif = package; -} diff --git a/modules/nixos/pkgs/python/rfeed.nix b/modules/nixos/pkgs/python/rfeed.nix deleted file mode 100644 index bc415c9..0000000 --- a/modules/nixos/pkgs/python/rfeed.nix +++ /dev/null @@ -1,44 +0,0 @@ -{...}: let - package = { - lib, - fetchPypi, - buildPythonPackage, - setuptools, - python-dateutil, - }: - buildPythonPackage rec { - pname = "rfeed"; - version = "1.1.1"; - pyproject = true; - - src = fetchPypi { - inherit pname version; - hash = "sha256-qpUG8oZrdPWjItOUoUpjwZpoJcLZR1X/GdRt0eJDSBk="; - }; - - build-system = [ - setuptools - ]; - - dependencies = [ - python-dateutil - ]; - - # No tests available in the package - doCheck = false; - - pythonImportsCheck = [ - "rfeed" - ]; - - meta = with lib; { - description = "RSS feed generation library for Python"; - homepage = "https://pypi.org/project/rfeed/"; - license = licenses.mit; - maintainers = []; - platforms = platforms.all; - }; - }; -in { - flake.commonModules.python-rfeed = package; -} diff --git a/modules/nixos/pkgs/sgblur.nix b/modules/nixos/pkgs/sgblur.nix deleted file mode 100644 index d7e4ebc..0000000 --- a/modules/nixos/pkgs/sgblur.nix +++ /dev/null @@ -1,75 +0,0 @@ -{...}: let - package = { - lib, - python3Packages, - fetchFromGitHub, - pkg-config, - libjpeg_turbo, - exiftran ? libjpeg_turbo, - }: - python3Packages.buildPythonPackage { - pname = "sgblur"; - version = "1.0.0"; - - pyproject = true; - - src = fetchFromGitHub { - owner = "cquest"; - repo = "sgblur"; - rev = "master"; - hash = "sha256-17wpif2sa021kaa1pbkry4l1967la1qd7knhngvxblrvd7jqqz4y="; - }; - - nativeBuildInputs = [ - pkg-config - ]; - - buildInputs = [ - libjpeg_turbo - exiftran - ]; - - build-system = with python3Packages; [ - setuptools - wheel - ]; - - dependencies = with python3Packages; [ - # Core dependencies from pyproject.toml - ultralytics - # pyturbojpeg # May need special handling - pillow - # uuid # Built into Python - # exifread - python-multipart - fastapi - uvicorn - requests - # piexif - pydantic-settings - pydantic - ]; - - # Skip tests as they may require GPU or specific setup - doCheck = false; - - # The package may have import issues due to system dependencies - pythonImportsCheck = []; - - meta = with lib; { - description = "Panoramax Speedy Gonzales Blurring Algorithm - AI-powered face and license plate blurring API"; - homepage = "https://github.com/cquest/sgblur"; - license = licenses.mit; - maintainers = []; - platforms = platforms.unix; - }; - }; -in { - flake.commonModules.sgblur = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - sgblur = pkgs.python3.pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/pkgs/webtoon-dl.nix b/modules/nixos/pkgs/webtoon-dl.nix deleted file mode 100644 index 0702695..0000000 --- a/modules/nixos/pkgs/webtoon-dl.nix +++ /dev/null @@ -1,28 +0,0 @@ -{...}: let - package = { - buildGoModule, - fetchFromGitHub, - ... - }: - buildGoModule rec { - pname = "webtoon-dl"; - version = "0.0.10"; - - src = fetchFromGitHub { - owner = "robinovitch61"; - repo = "webtoon-dl"; - rev = "v${version}"; - hash = "sha256-geVb3LFPZxPQYARZnaqOr5sgaN6mqkEX5ZiLvg8mF5k="; - }; - - vendorHash = "sha256-NTqUygJ6b6kTnLUnJqxCo/URzaRouPLACEPi2Ob1s9w="; - }; -in { - flake.commonModules.webtoon-dl = {pkgs, ...}: { - nixpkgs.overlays = [ - (final: prev: { - webtoon-dl = pkgs.callPackage package {}; - }) - ]; - }; -} diff --git a/modules/nixos/programs/actual/actual.nix b/modules/nixos/programs/actual/actual.nix deleted file mode 100644 index b5e5d2a..0000000 --- a/modules/nixos/programs/actual/actual.nix +++ /dev/null @@ -1,25 +0,0 @@ -{...}: { - flake.nixosModules.actual-service = { - lib, - config, - ... - }: let - dataDirectory = "/var/lib/private/actual"; - in { - options.services.actual = { - port = lib.mkOption { - type = lib.types.port; - description = "The port to listen on"; - default = 5006; - }; - }; - config = lib.mkIf config.services.actual.enable { - services.actual = { - settings = { - port = config.services.actual.port; - dataDir = dataDirectory; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/actual/default.nix b/modules/nixos/programs/actual/default.nix deleted file mode 100644 index e2895fa..0000000 --- a/modules/nixos/programs/actual/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.actual = { - imports = [ - mod.actual-service - mod.actual-proxy - mod.actual-fail2ban - mod.actual-storage - ]; - }; -} diff --git a/modules/nixos/programs/actual/fail2ban.nix b/modules/nixos/programs/actual/fail2ban.nix deleted file mode 100644 index e4fd01e..0000000 --- a/modules/nixos/programs/actual/fail2ban.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.actual-fail2ban = { - lib, - config, - ... - }: { - config = lib.mkIf (config.services.actual.enable && config.services.fail2ban.enable) { - # TODO: configuration for fail2ban for actual - }; - }; -} diff --git a/modules/nixos/programs/actual/proxy.nix b/modules/nixos/programs/actual/proxy.nix deleted file mode 100644 index 7b385b0..0000000 --- a/modules/nixos/programs/actual/proxy.nix +++ /dev/null @@ -1,36 +0,0 @@ -{...}: { - flake.nixosModules.actual-proxy = { - lib, - config, - ... - }: { - options.services.actual = { - domain = lib.mkOption { - type = lib.types.str; - description = "domain that actual will be hosted at"; - default = "actual.arpa"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for actual"; - default = []; - }; - reverseProxy.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.actual.enable && config.services.reverseProxy.enable; - }; - }; - - config = lib.mkIf config.services.actual.reverseProxy.enable { - services.reverseProxy.services.actual = { - target = "http://localhost:${toString config.services.actual.settings.port}"; - domain = config.services.actual.domain; - extraDomains = config.services.actual.extraDomains; - - settings = { - forwardHeaders.enable = true; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/actual/storage.nix b/modules/nixos/programs/actual/storage.nix deleted file mode 100644 index 272647d..0000000 --- a/modules/nixos/programs/actual/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.actual-storage = { - lib, - config, - ... - }: let - dataDirectory = "/var/lib/private/actual"; - in { - options.services.actual.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.actual.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.actual.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDirectory}" = lib.mkIf config.services.actual.impermanence.enable { - owner.name = "actual"; - group.name = "actual"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/bazarr/default.nix b/modules/nixos/programs/bazarr/default.nix deleted file mode 100644 index 9e83706..0000000 --- a/modules/nixos/programs/bazarr/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.bazarr = { - imports = [ - mod.bazarr-storage - ]; - }; -} diff --git a/modules/nixos/programs/bazarr/storage.nix b/modules/nixos/programs/bazarr/storage.nix deleted file mode 100644 index 8431cd4..0000000 --- a/modules/nixos/programs/bazarr/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.bazarr-storage = { - lib, - config, - ... - }: let - bazarr_data_directory = "/var/lib/bazarr"; - in { - options.services.bazarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.bazarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.bazarr.enable { - storage.datasets.replicate."system/root" = { - directories."${bazarr_data_directory}" = lib.mkIf config.services.bazarr.impermanence.enable { - owner.name = "bazarr"; - group.name = "bazarr"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/crab-hole/crab-hole.nix b/modules/nixos/programs/crab-hole/crab-hole.nix deleted file mode 100644 index 0c25e97..0000000 --- a/modules/nixos/programs/crab-hole/crab-hole.nix +++ /dev/null @@ -1,195 +0,0 @@ -{...}: { - flake.nixosModules.crab-hole-service = { - config, - lib, - ... - }: let - cfg = config.services.crab-hole; - in { - options.services.crab-hole = { - port = lib.mkOption { - type = lib.types.port; - default = 8080; - description = "Port for the crab-hole API to listen on."; - }; - - openFirewall = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to open the firewall for the crab-hole API port."; - }; - - listen = lib.mkOption { - type = lib.types.str; - default = "0.0.0.0"; - description = "Address for the crab-hole API to listen on."; - }; - - show_doc = lib.mkEnableOption "OpenAPI documentation (loads content from third party websites)"; - - downstreams = { - host = { - enable = lib.mkEnableOption "host downstream DNS server accessible from network on all interfaces"; - port = lib.mkOption { - type = lib.types.port; - default = 53; - description = "Port for the host downstream DNS server to listen on."; - }; - openFirewall = lib.mkEnableOption "automatic port forwarding for the host downstream"; - disableSystemdResolved = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to automatically disable systemd-resolved when using port 53. Set to false if you want to handle the conflict manually."; - }; - }; - }; - - extraDownstreams = lib.mkOption { - type = lib.types.listOf (lib.types.submodule { - options = { - protocol = lib.mkOption { - type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"]; - description = "Protocol for the downstream server."; - }; - - listen = lib.mkOption { - type = lib.types.str; - description = "Address to listen on for downstream connections."; - }; - - port = lib.mkOption { - type = lib.types.port; - description = "Port to listen on for downstream connections."; - }; - }; - }); - default = []; - description = "List of additional downstream DNS server configurations."; - }; - - upstreams = { - cloudFlare = { - enable = lib.mkEnableOption "Cloudflare DNS over TLS upstream servers (1.1.1.1 and 1.0.0.1)"; - }; - }; - - extraUpstreams = lib.mkOption { - type = lib.types.listOf (lib.types.submodule { - options = { - socket_addr = lib.mkOption { - type = lib.types.str; - description = "Socket address of the upstream DNS server (e.g., \"1.1.1.1:853\" or \"[2606:4700:4700::1111]:853\")."; - }; - - protocol = lib.mkOption { - type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"]; - description = "Protocol to use for upstream DNS queries."; - }; - }; - }); - default = []; - description = "List of additional upstream DNS server configurations."; - }; - - blocklists = { - ad_malware = { - enable = lib.mkEnableOption "Host file for blocking ads and malware"; - url = lib.mkOption { - type = lib.types.str; - default = "http://sbc.io/hosts/hosts"; - description = "URL of the ad and malware blocklist host file"; - }; - }; - }; - - extraBlocklists = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Additional blocklist URLs to be added to the configuration"; - }; - }; - - config = lib.mkIf cfg.enable { - # Assertions for proper configuration - assertions = [ - { - assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && config.services.resolved.enable && cfg.downstreams.host.disableSystemdResolved); - message = "crab-hole host downstream cannot use port 53 while systemd-resolved is enabled. Either disable systemd-resolved or use a different port."; - } - { - assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && !cfg.downstreams.host.disableSystemdResolved && config.services.resolved.enable); - message = "crab-hole host downstream is configured to use port 53 but systemd-resolved is still enabled and disableSystemdResolved is false. Set disableSystemdResolved = true or manually disable systemd-resolved."; - } - ]; - - # Automatically disable systemd-resolved if using port 53 - services.resolved.enable = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkForce false); - - # Configure DNS nameservers when disabling systemd-resolved - networking.nameservers = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkDefault ["127.0.0.1" "1.1.1.1" "8.8.8.8"]); - - services.crab-hole.settings = lib.mkMerge [ - { - api = { - port = cfg.port; - listen = cfg.listen; - show_doc = cfg.show_doc; - }; - downstream = cfg.extraDownstreams; - upstream.name_servers = cfg.extraUpstreams; - blocklist.lists = cfg.extraBlocklists; - } - (lib.mkIf cfg.blocklists.ad_malware.enable { - blocklist.lists = [cfg.blocklists.ad_malware.url]; - }) - (lib.mkIf cfg.downstreams.host.enable { - downstream = [ - { - protocol = "udp"; - listen = "0.0.0.0"; - port = cfg.downstreams.host.port; - } - ]; - }) - (lib.mkIf cfg.upstreams.cloudFlare.enable { - upstream.name_servers = [ - { - socket_addr = "1.1.1.1:853"; - protocol = "tls"; - tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; - trust_nx_responses = false; - } - { - socket_addr = "1.0.0.1:853"; - protocol = "tls"; - tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; - trust_nx_responses = false; - } - { - socket_addr = "[2606:4700:4700::1111]:853"; - protocol = "tls"; - tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; - trust_nx_responses = false; - } - { - socket_addr = "[2606:4700:4700::1001]:853"; - protocol = "tls"; - tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; - trust_nx_responses = false; - } - ]; - }) - ]; - - # Open firewall if requested - networking.firewall = lib.mkMerge [ - (lib.mkIf cfg.openFirewall { - allowedTCPPorts = [cfg.port]; - }) - (lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.openFirewall) { - allowedUDPPorts = [cfg.downstreams.host.port]; - }) - ]; - }; - }; -} diff --git a/modules/nixos/programs/crab-hole/default.nix b/modules/nixos/programs/crab-hole/default.nix deleted file mode 100644 index 44c4cb0..0000000 --- a/modules/nixos/programs/crab-hole/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.crab-hole = { - imports = [ - mod.crab-hole-service - mod.crab-hole-storage - ]; - }; -} diff --git a/modules/nixos/programs/crab-hole/storage.nix b/modules/nixos/programs/crab-hole/storage.nix deleted file mode 100644 index 33a3c60..0000000 --- a/modules/nixos/programs/crab-hole/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.crab-hole-storage = { - lib, - config, - ... - }: let - workingDirectory = "/var/lib/private/crab-hole"; - in { - options.services.crab-hole.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.crab-hole.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.crab-hole.enable { - storage.datasets.replicate."system/root" = { - directories."${workingDirectory}" = lib.mkIf config.services.crab-hole.impermanence.enable { - owner.name = "crab-hole"; - group.name = "crab-hole"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/default.nix b/modules/nixos/programs/default.nix deleted file mode 100644 index 592911d..0000000 --- a/modules/nixos/programs/default.nix +++ /dev/null @@ -1,32 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.programs = { - imports = [ - mod.steam - mod.sync - mod.tailscale - mod.actual - mod.bazarr - mod.crab-hole - mod.fail2ban - mod.flaresolverr - mod.forgejo - mod.home-assistant - mod.immich - mod.jackett - mod.jellyfin - mod.lidarr - mod.network-storage - mod.panoramax - mod.paperless - mod.postgres - mod.qbittorent - mod.radarr - mod.reverse-proxy - mod.searx - mod.sonarr - mod.wyoming - ]; - }; -} diff --git a/modules/nixos/programs/fail2ban/default.nix b/modules/nixos/programs/fail2ban/default.nix deleted file mode 100644 index 1c81d85..0000000 --- a/modules/nixos/programs/fail2ban/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.fail2ban = { - imports = [ - mod.fail2ban-service - mod.fail2ban-storage - ]; - }; -} diff --git a/modules/nixos/programs/fail2ban/fail2ban.nix b/modules/nixos/programs/fail2ban/fail2ban.nix deleted file mode 100644 index 3d5d4a5..0000000 --- a/modules/nixos/programs/fail2ban/fail2ban.nix +++ /dev/null @@ -1,53 +0,0 @@ -{...}: { - flake.nixosModules.fail2ban-service = { - lib, - pkgs, - config, - ... - }: { - config = lib.mkIf config.services.fail2ban.enable { - environment.etc = { - "fail2ban/filter.d/nginx.local".text = lib.mkIf config.services.nginx.enable ( - pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [Definition] - failregex = "limiting requests, excess:.* by zone.*client: " - '') - ); - }; - - services.fail2ban = { - maxretry = 5; - ignoreIP = [ - # Whitelist local networks - "10.0.0.0/8" - "172.16.0.0/12" - "192.168.0.0/16" - - # tail scale tailnet - "100.64.0.0/10" - "fd7a:115c:a1e0::/48" - ]; - bantime = "24h"; # Ban IPs for one day on the first ban - bantime-increment = { - enable = true; # Enable increment of bantime after each violation - formula = "ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)"; - maxtime = "168h"; # Do not ban for more than 1 week - overalljails = true; # Calculate the ban time based on all the violations - }; - jails = { - nginx-iptables.settings = lib.mkIf config.services.nginx.enable { - enabled = true; - filter = "nginx"; - action = ''iptables-multiport[name=HTTP, port="http,https"]''; - backend = "auto"; - findtime = 600; - bantime = 600; - maxretry = 5; - }; - # TODO; figure out if there is any fail2ban things we can do on searx - # searx-iptables.settings = lib.mkIf config.services.searx.enable {}; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/fail2ban/storage.nix b/modules/nixos/programs/fail2ban/storage.nix deleted file mode 100644 index d20fad1..0000000 --- a/modules/nixos/programs/fail2ban/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.fail2ban-storage = { - lib, - config, - ... - }: let - dataFolder = "/var/lib/fail2ban"; - in { - options.services.fail2ban.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.fail2ban.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.fail2ban.enable { - storage.datasets.replicate."system/root" = { - directories."${dataFolder}" = lib.mkIf config.services.fail2ban.impermanence.enable { - owner.name = "fail2ban"; - group.name = "fail2ban"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/flaresolverr/default.nix b/modules/nixos/programs/flaresolverr/default.nix deleted file mode 100644 index 916d3e6..0000000 --- a/modules/nixos/programs/flaresolverr/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.flaresolverr = { - imports = [ - mod.flaresolverr-storage - ]; - }; -} diff --git a/modules/nixos/programs/flaresolverr/storage.nix b/modules/nixos/programs/flaresolverr/storage.nix deleted file mode 100644 index caed127..0000000 --- a/modules/nixos/programs/flaresolverr/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{...}: { - flake.nixosModules.flaresolverr-storage = { - lib, - config, - ... - }: { - options.services.flaresolverr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.flaresolverr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.flaresolverr.enable { - storage.datasets.replicate."system/root" = { - directories."/var/lib/flaresolverr" = lib.mkIf config.services.flaresolverr.impermanence.enable { - owner.name = "flaresolverr"; - group.name = "flaresolverr"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/forgejo/database.nix b/modules/nixos/programs/forgejo/database.nix deleted file mode 100644 index e4c6697..0000000 --- a/modules/nixos/programs/forgejo/database.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-database = { - lib, - config, - ... - }: let - usingPostgres = config.services.forgejo.database.type == "postgres"; - in { - config = lib.mkIf config.services.forgejo.enable { - assertions = [ - { - assertion = !usingPostgres || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when Forgejo database type is postgres"; - } - { - assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (db: db == "forgejo") config.services.postgresql.ensureDatabases); - message = "Forgejo built-in database creation failed - expected 'forgejo' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (user: user.name == "forgejo") config.services.postgresql.ensureUsers); - message = "Forgejo built-in user creation failed - expected user 'forgejo' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; - - services.forgejo.database.createDatabase = lib.mkDefault usingPostgres; - - systemd.services.forgejo = lib.mkIf usingPostgres { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; - }; -} diff --git a/modules/nixos/programs/forgejo/default.nix b/modules/nixos/programs/forgejo/default.nix deleted file mode 100644 index 1ddc3fc..0000000 --- a/modules/nixos/programs/forgejo/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.forgejo = { - imports = [ - mod.forgejo-service - mod.forgejo-database - mod.forgejo-proxy - mod.forgejo-fail2ban - mod.forgejo-storage - mod.forgejo-ssh - ]; - }; -} diff --git a/modules/nixos/programs/forgejo/fail2ban.nix b/modules/nixos/programs/forgejo/fail2ban.nix deleted file mode 100644 index e1ab7b7..0000000 --- a/modules/nixos/programs/forgejo/fail2ban.nix +++ /dev/null @@ -1,43 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-fail2ban = { - lib, - config, - pkgs, - ... - }: { - options.services.forgejo = { - fail2ban = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.forgejo.enable && config.services.fail2ban.enable; - }; - }; - }; - - config = lib.mkIf config.services.forgejo.fail2ban.enable { - environment.etc = { - "fail2ban/filter.d/forgejo.local".text = lib.mkIf config.services.forgejo.enable ( - pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [Definition] - failregex = ".*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from " - '') - ); - }; - - services.fail2ban = { - jails = { - forgejo-iptables.settings = lib.mkIf config.services.forgejo.enable { - enabled = true; - filter = "forgejo"; - action = ''iptables-multiport[name=HTTP, port="http,https"]''; - logpath = "${config.services.forgejo.settings.log.ROOT_PATH}/*.log"; - backend = "auto"; - findtime = 600; - bantime = 600; - maxretry = 5; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/forgejo/forgejo.nix b/modules/nixos/programs/forgejo/forgejo.nix deleted file mode 100644 index 4df02bd..0000000 --- a/modules/nixos/programs/forgejo/forgejo.nix +++ /dev/null @@ -1,48 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-service = { - lib, - config, - ... - }: let - httpPort = 8081; - sshPort = 22222; - db_user = "forgejo"; - in { - config = lib.mkIf config.services.forgejo.enable { - assertions = [ - { - assertion = config.services.forgejo.settings.server.BUILTIN_SSH_SERVER_USER == config.users.users.git.name; - message = "Forgejo BUILTIN_SSH_SERVER_USER hardcoded value does not match expected git user name"; - } - ]; - - services.forgejo = { - database = { - type = "postgres"; - socket = "/run/postgresql"; - }; - lfs.enable = true; - settings = { - server = { - DOMAIN = config.services.forgejo.reverseProxy.domain; - HTTP_PORT = httpPort; - START_SSH_SERVER = true; - SSH_LISTEN_PORT = sshPort; - SSH_PORT = 22; - BUILTIN_SSH_SERVER_USER = "git"; - ROOT_URL = "https://git.jan-leila.com"; - SSH_SERVER_KEY_EXCHANGES = "mlkem768x25519-sha256,curve25519-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha256"; - }; - service = { - DISABLE_REGISTRATION = true; - }; - database = { - DB_TYPE = "postgres"; - NAME = db_user; - USER = db_user; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/forgejo/proxy.nix b/modules/nixos/programs/forgejo/proxy.nix deleted file mode 100644 index af03409..0000000 --- a/modules/nixos/programs/forgejo/proxy.nix +++ /dev/null @@ -1,44 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-proxy = { - lib, - config, - ... - }: let - httpPort = 8081; - in { - options.services.forgejo = { - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.forgejo.enable && config.services.reverseProxy.enable; - }; - domain = lib.mkOption { - type = lib.types.str; - description = "domain that forgejo will be hosted at"; - default = "git.jan-leila.com"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for forgejo"; - default = []; - }; - }; - }; - - config = lib.mkIf config.services.forgejo.reverseProxy.enable { - services.reverseProxy.services.forgejo = { - target = "http://localhost:${toString httpPort}"; - domain = config.services.forgejo.reverseProxy.domain; - extraDomains = config.services.forgejo.reverseProxy.extraDomains; - - settings = { - forwardHeaders.enable = true; - }; - }; - - networking.firewall.allowedTCPPorts = [ - config.services.forgejo.settings.server.SSH_LISTEN_PORT - ]; - }; - }; -} diff --git a/modules/nixos/programs/forgejo/ssh.nix b/modules/nixos/programs/forgejo/ssh.nix deleted file mode 100644 index 351fbb3..0000000 --- a/modules/nixos/programs/forgejo/ssh.nix +++ /dev/null @@ -1,84 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-ssh = { - lib, - config, - pkgs, - ... - }: let - gitUser = config.services.forgejo.settings.server.BUILTIN_SSH_SERVER_USER; - forgejoUser = config.services.forgejo.user; - forgejo = config.services.forgejo.package; - stateDir = config.services.forgejo.stateDir; - forgejoExe = lib.getExe forgejo; - - separateUsers = gitUser != forgejoUser; - - # Run the AuthorizedKeysCommand as the forgejo user when users differ, - # so it can read app.ini without adding git to the forgejo group. - keysCommandUser = - if separateUsers - then forgejoUser - else gitUser; - - forgejoKeysCmd = "FORGEJO_WORK_DIR=${stateDir} ${forgejoExe} keys -e git -u \"$1\" -t \"$2\" -k \"$3\""; - - # When the SSH user differs from the forgejo service user, rewrite - # the command= wrapper to use sudo so forgejo serv runs as the user - # that owns the repository data. - forgejoKeysScript = pkgs.writeShellScript "forgejo-keys" ( - if separateUsers - then '' - ${forgejoKeysCmd} \ - | ${pkgs.gnused}/bin/sed 's|command="${forgejoExe}|command="/run/wrappers/bin/sudo -u ${forgejoUser} --preserve-env=SSH_ORIGINAL_COMMAND ${forgejoExe}|' - '' - else forgejoKeysCmd - ); - - forgejoKeysPath = "/run/forgejo-keys"; - in { - config = lib.mkIf config.services.forgejo.enable (lib.mkMerge [ - { - users.users.${gitUser}.shell = pkgs.bash; - - services.openssh.settings.AllowUsers = [gitUser]; - - # Copy the key lookup script to a root-owned path outside /nix/store. - # sshd StrictModes requires AuthorizedKeysCommand and all parent dirs - # to be owned by root with no group/world writes. /nix/store and /etc - # symlinks both fail this check. - system.activationScripts.forgejo-ssh-keys = lib.stringAfter ["etc"] '' - install -m 0755 -o root -g root ${forgejoKeysScript} ${forgejoKeysPath} - ''; - - services.openssh.extraConfig = '' - Match User ${gitUser} - AuthorizedKeysCommandUser ${keysCommandUser} - AuthorizedKeysCommand ${forgejoKeysPath} %u %t %k - AuthenticationMethods publickey - KbdInteractiveAuthentication no - PasswordAuthentication no - AllowAgentForwarding no - AllowTcpForwarding no - X11Forwarding no - PermitTTY no - ''; - } - - (lib.mkIf separateUsers { - # Allow the git user to run forgejo serv as the forgejo user - security.sudo.extraRules = [ - { - users = [gitUser]; - runAs = forgejoUser; - commands = [ - { - command = "${forgejoExe} --config=${stateDir}/custom/conf/app.ini serv *"; - options = ["NOPASSWD" "SETENV"]; - } - ]; - } - ]; - }) - ]); - }; -} diff --git a/modules/nixos/programs/forgejo/storage.nix b/modules/nixos/programs/forgejo/storage.nix deleted file mode 100644 index cff5500..0000000 --- a/modules/nixos/programs/forgejo/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.forgejo-storage = { - lib, - config, - ... - }: let - stateDir = "/var/lib/forgejo"; - in { - options.services.forgejo.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.forgejo.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.forgejo.enable { - storage.datasets.replicate."system/root" = { - directories."${stateDir}" = lib.mkIf config.services.forgejo.impermanence.enable { - owner.name = "forgejo"; - group.name = "forgejo"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/home-assistant/database.nix b/modules/nixos/programs/home-assistant/database.nix deleted file mode 100644 index 648df74..0000000 --- a/modules/nixos/programs/home-assistant/database.nix +++ /dev/null @@ -1,55 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-database = { - lib, - config, - ... - }: { - options.services.home-assistant = { - postgres = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Use PostgreSQL instead of SQLite"; - }; - user = lib.mkOption { - type = lib.types.str; - default = "hass"; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = "hass"; - description = "Database name"; - }; - }; - }; - - config = lib.mkIf config.services.home-assistant.enable { - assertions = [ - { - assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; - } - ]; - - services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - enable = true; - user = config.services.home-assistant.postgres.user; - database = config.services.home-assistant.postgres.database; - }; - - services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - extraPackages = python3Packages: - with python3Packages; [ - psycopg2 - ]; - }; - - systemd.services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; - }; -} diff --git a/modules/nixos/programs/home-assistant/default.nix b/modules/nixos/programs/home-assistant/default.nix deleted file mode 100644 index 9aca3f9..0000000 --- a/modules/nixos/programs/home-assistant/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.home-assistant = { - imports = [ - mod.home-assistant-service - mod.home-assistant-proxy - mod.home-assistant-database - mod.home-assistant-fail2ban - mod.home-assistant-storage - mod.home-assistant-extensions - ]; - }; -} diff --git a/modules/nixos/programs/home-assistant/extensions/default.nix b/modules/nixos/programs/home-assistant/extensions/default.nix deleted file mode 100644 index 43b62b8..0000000 --- a/modules/nixos/programs/home-assistant/extensions/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.home-assistant-extensions = { - imports = [ - mod.home-assistant-sonos - mod.home-assistant-jellyfin - mod.home-assistant-wyoming - ]; - }; -} diff --git a/modules/nixos/programs/home-assistant/extensions/jellyfin.nix b/modules/nixos/programs/home-assistant/extensions/jellyfin.nix deleted file mode 100644 index 4c18c3f..0000000 --- a/modules/nixos/programs/home-assistant/extensions/jellyfin.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-jellyfin = { - lib, - config, - ... - }: - lib.mkIf (config.services.home-assistant.extensions.jellyfin.enable) { - services.home-assistant.extraComponents = ["jellyfin"]; - # TODO: configure port, address, and login information here - }; -} diff --git a/modules/nixos/programs/home-assistant/extensions/sonos.nix b/modules/nixos/programs/home-assistant/extensions/sonos.nix deleted file mode 100644 index a265a79..0000000 --- a/modules/nixos/programs/home-assistant/extensions/sonos.nix +++ /dev/null @@ -1,13 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-sonos = { - lib, - config, - ... - }: - lib.mkIf (config.services.home-assistant.extensions.sonos.enable) { - services.home-assistant.extraComponents = ["sonos"]; - networking.firewall.allowedTCPPorts = [ - config.services.home-assistant.extensions.sonos.port - ]; - }; -} diff --git a/modules/nixos/programs/home-assistant/extensions/wyoming.nix b/modules/nixos/programs/home-assistant/extensions/wyoming.nix deleted file mode 100644 index 2664f75..0000000 --- a/modules/nixos/programs/home-assistant/extensions/wyoming.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-wyoming = { - lib, - config, - ... - }: - lib.mkIf (config.services.home-assistant.extensions.wyoming.enable) { - services.home-assistant.extraComponents = ["wyoming"]; - services.wyoming.enable = true; - }; -} diff --git a/modules/nixos/programs/home-assistant/fail2ban.nix b/modules/nixos/programs/home-assistant/fail2ban.nix deleted file mode 100644 index f44aefe..0000000 --- a/modules/nixos/programs/home-assistant/fail2ban.nix +++ /dev/null @@ -1,51 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-fail2ban = { - lib, - pkgs, - config, - ... - }: { - options.services.home-assistant = { - fail2ban = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.fail2ban.enable && config.services.home-assistant.enable; - }; - }; - }; - - config = lib.mkIf config.services.home-assistant.fail2ban.enable { - environment.etc = { - "fail2ban/filter.d/hass.local".text = ( - pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [INCLUDES] - before = common.conf - - [Definition] - failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from .*$ - - ignoreregex = - - [Init] - datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S - '') - ); - }; - - services.fail2ban = { - jails = { - home-assistant-iptables.settings = { - enabled = true; - filter = "hass"; - action = ''iptables-multiport[name=HTTP, port="http,https"]''; - logpath = "${config.services.home-assistant.configDir}/*.log"; - backend = "auto"; - findtime = 600; - bantime = 600; - maxretry = 5; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/home-assistant/home-assistant.nix b/modules/nixos/programs/home-assistant/home-assistant.nix deleted file mode 100644 index 5f1f807..0000000 --- a/modules/nixos/programs/home-assistant/home-assistant.nix +++ /dev/null @@ -1,106 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-service = { - lib, - config, - ... - }: { - options.services.home-assistant = { - database = lib.mkOption { - type = lib.types.enum [ - "builtin" - "postgres" - ]; - description = "what database do we want to use"; - default = "builtin"; - }; - - extensions = { - sonos = { - enable = lib.mkEnableOption "enable the sonos plugin"; - port = lib.mkOption { - type = lib.types.int; - default = 1400; - description = "what port to use for sonos discovery"; - }; - }; - jellyfin = { - enable = lib.mkEnableOption "enable the jellyfin plugin"; - }; - wyoming = { - enable = lib.mkEnableOption "enable wyoming"; - }; - }; - }; - - config = lib.mkIf config.services.home-assistant.enable (lib.mkMerge [ - { - services.home-assistant = { - configDir = "/var/lib/hass"; - extraComponents = [ - "default_config" - "esphome" - "met" - "radio_browser" - "isal" - "zha" - "webostv" - "tailscale" - "syncthing" - "analytics_insights" - "unifi" - "openweathermap" - "ollama" - "mobile_app" - "logbook" - "ssdp" - "usb" - "webhook" - "bluetooth" - "dhcp" - "energy" - "history" - "backup" - "assist_pipeline" - "conversation" - "sun" - "zeroconf" - "cpuspeed" - ]; - config = { - http = { - server_port = 8123; - use_x_forwarded_for = true; - trusted_proxies = ["127.0.0.1" "::1"]; - ip_ban_enabled = true; - login_attempts_threshold = 10; - }; - homeassistant = { - external_url = "https://${config.services.home-assistant.domain}"; - # internal_url = "http://192.168.1.2:8123"; - }; - recorder.db_url = "postgresql://@/${config.services.home-assistant.configDir}"; - "automation manual" = []; - "automation ui" = "!include automations.yaml"; - mobile_app = {}; - }; - extraPackages = python3Packages: - with python3Packages; [ - hassil - numpy - gtts - ]; - }; - - # TODO: configure /var/lib/hass/secrets.yaml via sops - - networking.firewall.allowedUDPPorts = [ - 1900 - ]; - - systemd.tmpfiles.rules = [ - "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" - ]; - } - ]); - }; -} diff --git a/modules/nixos/programs/home-assistant/proxy.nix b/modules/nixos/programs/home-assistant/proxy.nix deleted file mode 100644 index 358288f..0000000 --- a/modules/nixos/programs/home-assistant/proxy.nix +++ /dev/null @@ -1,45 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-proxy = { - lib, - config, - ... - }: { - options.services.home-assistant = { - domain = lib.mkOption { - type = lib.types.str; - description = "domain that home-assistant will be hosted at"; - default = "home-assistant.arpa"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for home-assistant"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.reverseProxy.enable && config.services.home-assistant.enable; - }; - }; - }; - - config = lib.mkIf config.services.home-assistant.reverseProxy.enable { - services.reverseProxy.services.home-assistant = { - target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; - domain = config.services.home-assistant.domain; - extraDomains = config.services.home-assistant.extraDomains; - - settings = { - proxyWebsockets.enable = true; - forwardHeaders.enable = true; - - # Custom timeout settings - proxyHeaders = { - enable = true; - timeout = 90; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/home-assistant/storage.nix b/modules/nixos/programs/home-assistant/storage.nix deleted file mode 100644 index 2cd779f..0000000 --- a/modules/nixos/programs/home-assistant/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.home-assistant-storage = { - lib, - config, - ... - }: let - configDir = "/var/lib/hass"; - in { - options.services.home-assistant.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.home-assistant.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.home-assistant.enable { - storage.datasets.replicate."system/root" = { - directories."${configDir}" = lib.mkIf config.services.home-assistant.impermanence.enable { - owner.name = "hass"; - group.name = "hass"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/immich/database.nix b/modules/nixos/programs/immich/database.nix deleted file mode 100644 index 60fe07e..0000000 --- a/modules/nixos/programs/immich/database.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.nixosModules.immich-database = { - lib, - config, - ... - }: { - config = lib.mkIf config.services.immich.enable { - assertions = [ - { - assertion = !config.services.immich.database.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Immich"; - } - { - assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (db: db == "immich") config.services.postgresql.ensureDatabases); - message = "Immich built-in database creation failed - expected 'immich' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers); - message = "Immich built-in user creation failed - expected user 'immich' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; - - # Note: Immich has built-in database creation via services.immich.database.createDB we only add the systemd dependency - - systemd.services.immich-server = lib.mkIf config.services.immich.database.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; - }; -} diff --git a/modules/nixos/programs/immich/default.nix b/modules/nixos/programs/immich/default.nix deleted file mode 100644 index d79af33..0000000 --- a/modules/nixos/programs/immich/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.immich = { - imports = [ - mod.immich-proxy - mod.immich-database - mod.immich-fail2ban - mod.immich-storage - ]; - }; -} diff --git a/modules/nixos/programs/immich/fail2ban.nix b/modules/nixos/programs/immich/fail2ban.nix deleted file mode 100644 index c5e6332..0000000 --- a/modules/nixos/programs/immich/fail2ban.nix +++ /dev/null @@ -1,37 +0,0 @@ -{...}: { - flake.nixosModules.immich-fail2ban = { - lib, - config, - pkgs, - ... - }: { - options.services.immich = { - fail2ban = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.fail2ban.enable && config.services.immich.enable; - }; - }; - }; - - config = lib.mkIf config.services.immich.fail2ban.enable { - environment.etc = { - "fail2ban/filter.d/immich.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [Definition] - failregex = immich-server.*Failed login attempt for user.+from ip address\s? - journalmatch = CONTAINER_TAG=immich-server - ''); - }; - - services.fail2ban = { - jails = { - immich-iptables.settings = { - enabled = true; - filter = "immich"; - backend = "systemd"; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/immich/proxy.nix b/modules/nixos/programs/immich/proxy.nix deleted file mode 100644 index 47b908c..0000000 --- a/modules/nixos/programs/immich/proxy.nix +++ /dev/null @@ -1,46 +0,0 @@ -{...}: { - flake.nixosModules.immich-proxy = { - lib, - config, - ... - }: { - options.services.immich = { - domain = lib.mkOption { - type = lib.types.str; - description = "domain that immich will be hosted at"; - default = "immich.arpa"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for immich"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.immich.enable && config.services.reverseProxy.enable; - }; - }; - }; - - config = lib.mkIf config.services.immich.reverseProxy.enable { - services.reverseProxy.services.immich = { - target = "http://localhost:${toString config.services.immich.port}"; - domain = config.services.immich.domain; - extraDomains = config.services.immich.extraDomains; - - settings = { - proxyWebsockets.enable = true; - forwardHeaders.enable = true; - maxBodySize = 50000; - - # Custom timeout settings - proxyHeaders = { - enable = true; - timeout = 600; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/immich/storage.nix b/modules/nixos/programs/immich/storage.nix deleted file mode 100644 index 93cc2c2..0000000 --- a/modules/nixos/programs/immich/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.immich-storage = { - lib, - config, - ... - }: let - mediaLocation = "/var/lib/immich"; - in { - options.services.immich.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.immich.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.immich.enable { - storage.datasets.replicate."system/root" = { - directories."${mediaLocation}" = lib.mkIf config.services.immich.impermanence.enable { - owner.name = "immich"; - group.name = "immich"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/jackett/default.nix b/modules/nixos/programs/jackett/default.nix deleted file mode 100644 index 6e371eb..0000000 --- a/modules/nixos/programs/jackett/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.jackett = { - imports = [ - mod.jackett-storage - ]; - - config = { - nixpkgs.overlays = [ - # Disable jackett tests due to date-related test failures - # (ParseDateTimeGoLangTest expects 2024-09-14 but gets 2025-09-14 due to year rollover logic) - (final: prev: { - jackett = prev.jackett.overrideAttrs (oldAttrs: { - doCheck = false; - }); - }) - ]; - }; - }; -} diff --git a/modules/nixos/programs/jackett/storage.nix b/modules/nixos/programs/jackett/storage.nix deleted file mode 100644 index a8977ca..0000000 --- a/modules/nixos/programs/jackett/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.jackett-storage = { - lib, - config, - ... - }: let - jackett_data_directory = "/var/lib/jackett/.config/Jackett"; - in { - options.services.jackett.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jackett.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.jackett.enable { - storage.datasets.replicate."system/root" = { - directories."${jackett_data_directory}" = lib.mkIf config.services.jackett.impermanence.enable { - owner.name = "jackett"; - group.name = "jackett"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/jellyfin/default.nix b/modules/nixos/programs/jellyfin/default.nix deleted file mode 100644 index e016ab6..0000000 --- a/modules/nixos/programs/jellyfin/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.jellyfin = { - imports = [ - mod.jellyfin-service - mod.jellyfin-proxy - mod.jellyfin-fail2ban - mod.jellyfin-storage - ]; - }; -} diff --git a/modules/nixos/programs/jellyfin/fail2ban.nix b/modules/nixos/programs/jellyfin/fail2ban.nix deleted file mode 100644 index 7461ba7..0000000 --- a/modules/nixos/programs/jellyfin/fail2ban.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - flake.nixosModules.jellyfin-fail2ban = { - lib, - pkgs, - config, - ... - }: { - config = lib.mkIf (config.services.jellyfin.enable && config.services.fail2ban.enable) { - environment.etc = { - "fail2ban/filter.d/jellyfin.local".text = ( - pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [Definition] - failregex = "^.*Authentication request for .* has been denied \\\\\\(IP: \\\"\\\"\\\\\\)\\\\\\." - '') - ); - }; - - services.fail2ban = { - jails = { - jellyfin-iptables.settings = { - enabled = true; - filter = "jellyfin"; - action = ''iptables-multiport[name=HTTP, port="http,https"]''; - logpath = "${config.services.jellyfin.dataDir}/log/*.log"; - backend = "auto"; - findtime = 600; - bantime = 600; - maxretry = 5; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/jellyfin/jellyfin.nix b/modules/nixos/programs/jellyfin/jellyfin.nix deleted file mode 100644 index 28d2f46..0000000 --- a/modules/nixos/programs/jellyfin/jellyfin.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - flake.nixosModules.jellyfin-service = { - lib, - pkgs, - config, - ... - }: let - jellyfinPort = 8096; - dlanPort = 1900; - in { - options.services.jellyfin = { - media_directory = lib.mkOption { - type = lib.types.str; - description = "directory jellyfin media will be hosted at"; - default = "/srv/jellyfin/media"; - }; - }; - - config = lib.mkIf config.services.jellyfin.enable { - environment.systemPackages = [ - pkgs.jellyfin - pkgs.jellyfin-web - pkgs.jellyfin-ffmpeg - ]; - - networking.firewall.allowedTCPPorts = [jellyfinPort dlanPort]; - - systemd.tmpfiles.rules = [ - "d ${config.services.jellyfin.media_directory} 2770 jellyfin jellyfin_media" - "A ${config.services.jellyfin.media_directory} - - - - u:jellyfin:rwX,g:jellyfin_media:rwX,o::-" - ]; - }; - }; -} diff --git a/modules/nixos/programs/jellyfin/proxy.nix b/modules/nixos/programs/jellyfin/proxy.nix deleted file mode 100644 index 56a12d1..0000000 --- a/modules/nixos/programs/jellyfin/proxy.nix +++ /dev/null @@ -1,43 +0,0 @@ -{...}: { - flake.nixosModules.jellyfin-proxy = { - lib, - config, - ... - }: let - jellyfinPort = 8096; - in { - options.services.jellyfin = { - domain = lib.mkOption { - type = lib.types.str; - description = "domain that jellyfin will be hosted at"; - default = "jellyfin.arpa"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for jellyfin"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jellyfin.enable && config.services.reverseProxy.enable; - }; - }; - }; - - config = lib.mkIf config.services.jellyfin.reverseProxy.enable { - services.reverseProxy.services.jellyfin = { - target = "http://localhost:${toString jellyfinPort}"; - domain = config.services.jellyfin.domain; - extraDomains = config.services.jellyfin.extraDomains; - - settings = { - forwardHeaders.enable = true; - maxBodySize = 20; - noSniff.enable = true; - proxyBuffering.enable = false; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/jellyfin/storage.nix b/modules/nixos/programs/jellyfin/storage.nix deleted file mode 100644 index 0802530..0000000 --- a/modules/nixos/programs/jellyfin/storage.nix +++ /dev/null @@ -1,58 +0,0 @@ -{...}: { - flake.nixosModules.jellyfin-storage = { - lib, - config, - ... - }: let - jellyfin_data_directory = "/var/lib/jellyfin"; - jellyfin_cache_directory = "/var/cache/jellyfin"; - in { - options.services.jellyfin.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.jellyfin.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.jellyfin.enable { - storage.datasets.replicate = { - "system/root" = { - directories = { - "${jellyfin_data_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - "${jellyfin_cache_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin"; - }; - }; - }; - "system/media" = { - mount = "/persist/replicate/system/media"; - - directories."${config.services.jellyfin.media_directory}" = lib.mkIf config.services.jellyfin.impermanence.enable { - enable = true; - owner.name = "jellyfin"; - group.name = "jellyfin_media"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = false; - write = false; - execute = false; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/lidarr/default.nix b/modules/nixos/programs/lidarr/default.nix deleted file mode 100644 index 81a9929..0000000 --- a/modules/nixos/programs/lidarr/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.lidarr = { - imports = [ - mod.lidarr-storage - ]; - }; -} diff --git a/modules/nixos/programs/lidarr/storage.nix b/modules/nixos/programs/lidarr/storage.nix deleted file mode 100644 index 72104c1..0000000 --- a/modules/nixos/programs/lidarr/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.lidarr-storage = { - lib, - config, - ... - }: let - lidarr_data_directory = "/var/lib/lidarr/.config/Lidarr"; - in { - options.services.lidarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.lidarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.lidarr.enable { - storage.datasets.replicate."system/root" = { - directories."${lidarr_data_directory}" = lib.mkIf config.services.lidarr.impermanence.enable { - owner.name = "lidarr"; - group.name = "lidarr"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/network_storage/default.nix b/modules/nixos/programs/network_storage/default.nix deleted file mode 100644 index eeaa705..0000000 --- a/modules/nixos/programs/network_storage/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.network-storage = { - imports = [ - mod.network-storage-service - mod.network-storage-nfs - ]; - }; -} diff --git a/modules/nixos/programs/network_storage/network_storage.nix b/modules/nixos/programs/network_storage/network_storage.nix deleted file mode 100644 index acdf61e..0000000 --- a/modules/nixos/programs/network_storage/network_storage.nix +++ /dev/null @@ -1,89 +0,0 @@ -{...}: { - flake.nixosModules.network-storage-service = { - config, - lib, - ... - }: let - export_directory = config.host.network_storage.export_directory; - in { - options = { - host.network_storage = { - enable = lib.mkEnableOption "is this machine going to export network storage"; - export_directory = lib.mkOption { - type = lib.types.path; - description = "what are exports going to be stored in"; - default = "/exports"; - }; - directories = lib.mkOption { - type = lib.types.listOf (lib.types.submodule ({config, ...}: { - options = { - folder = lib.mkOption { - type = lib.types.str; - description = "what is the name of this export directory"; - }; - bind = lib.mkOption { - type = lib.types.nullOr lib.types.path; - description = "is this directory bound to anywhere"; - default = null; - }; - user = lib.mkOption { - type = lib.types.str; - description = "what user owns this directory"; - default = "nouser"; - }; - group = lib.mkOption { - type = lib.types.str; - description = "what group owns this directory"; - default = "nogroup"; - }; - _directory = lib.mkOption { - internal = true; - readOnly = true; - type = lib.types.path; - default = "${export_directory}/${config.folder}"; - }; - }; - })); - description = "list of directory names to export"; - }; - }; - }; - - config = lib.mkIf config.host.network_storage.enable (lib.mkMerge [ - { - # create any folders that we need to have for our exports - systemd.tmpfiles.rules = - [ - "d ${config.host.network_storage.export_directory} 2775 nobody nogroup -" - ] - ++ ( - builtins.map ( - directory: "d ${directory._directory} 2770 ${directory.user} ${directory.group}" - ) - config.host.network_storage.directories - ); - - # set up any bind mounts that we need for our exports - fileSystems = builtins.listToAttrs ( - builtins.map (directory: - lib.attrsets.nameValuePair directory._directory { - device = directory.bind; - fsType = "none"; - options = ["bind"]; - }) ( - builtins.filter (directory: directory.bind != null) config.host.network_storage.directories - ) - ); - } - # (lib.mkIf config.host.impermanence.enable { - # environment.persistence."/persist/replicate/system/root" = { - # enable = true; - # hideMounts = true; - # directories = [ - # config.host.network_storage.export_directory - # ]; - # }; - # }) - ]); - }; -} diff --git a/modules/nixos/programs/network_storage/nfs.nix b/modules/nixos/programs/network_storage/nfs.nix deleted file mode 100644 index 2a2a63b..0000000 --- a/modules/nixos/programs/network_storage/nfs.nix +++ /dev/null @@ -1,109 +0,0 @@ -{...}: { - flake.nixosModules.network-storage-nfs = { - config, - lib, - ... - }: { - options = { - host.network_storage.nfs = { - enable = lib.mkEnableOption "is this server going to export network storage as nfs shares"; - port = lib.mkOption { - type = lib.types.int; - default = 2049; - description = "port that nfs will run on"; - }; - directories = lib.mkOption { - type = lib.types.listOf ( - lib.types.enum ( - builtins.map ( - directory: directory.folder - ) - config.host.network_storage.directories - ) - ); - description = "list of exported directories to be exported via nfs"; - }; - }; - }; - config = lib.mkMerge [ - { - assertions = [ - { - assertion = !(config.host.network_storage.nfs.enable && !config.host.network_storage.enable); - message = "nfs cant be enabled with network storage disabled"; - } - ]; - } - ( - lib.mkIf (config.host.network_storage.nfs.enable && config.host.network_storage.enable) { - services.nfs = { - settings = { - nfsd = { - threads = 32; - port = config.host.network_storage.nfs.port; - }; - }; - server = { - enable = true; - - lockdPort = 4001; - mountdPort = 4002; - statdPort = 4000; - - exports = lib.strings.concatLines ( - [ - "${config.host.network_storage.export_directory} 100.64.0.0/10(rw,fsid=0,no_subtree_check)" - ] - ++ ( - lib.lists.imap0 ( - i: directory: let - createOptions = fsid: "(rw,fsid=${toString fsid},nohide,insecure,no_subtree_check)"; - addresses = [ - # loopback - "127.0.0.1" - "::1" - # tailscale - "100.64.0.0/10" - "fd7a:115c:a1e0::/48" - ]; - options = lib.strings.concatStrings ( - lib.strings.intersperse " " ( - lib.lists.imap0 (index: address: "${address}${createOptions (1 + (i * (builtins.length addresses)) + index)}") addresses - ) - ); - in "${directory._directory} ${options}" - ) - ( - builtins.filter ( - directory: lib.lists.any (target: target == directory.folder) config.host.network_storage.nfs.directories - ) - config.host.network_storage.directories - ) - ) - ); - }; - }; - networking.firewall = let - ports = [ - 111 - config.host.network_storage.nfs.port - config.services.nfs.server.lockdPort - config.services.nfs.server.mountdPort - config.services.nfs.server.statdPort - 20048 - ]; - in { - # Allow NFS on Tailscale interface - interfaces.${config.services.tailscale.interfaceName} = { - allowedTCPPorts = ports; - allowedUDPPorts = ports; - }; - # Allow NFS on local network (assuming default interface) - allowedTCPPorts = ports; - allowedUDPPorts = ports; - }; - } - ) - ]; - }; -} diff --git a/modules/nixos/programs/panoramax/database.nix b/modules/nixos/programs/panoramax/database.nix deleted file mode 100644 index 5077352..0000000 --- a/modules/nixos/programs/panoramax/database.nix +++ /dev/null @@ -1,50 +0,0 @@ -{...}: { - flake.nixosModules.panoramax-database = { - lib, - config, - ... - }: { - options.services.panoramax = { - database = { - postgres = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Use PostgreSQL instead of SQLite"; - }; - user = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "Database name"; - }; - }; - }; - }; - - config = lib.mkIf config.services.panoramax.enable { - assertions = [ - { - assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using postgres database for Panoramax"; - } - ]; - - services.postgresql.databases.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { - enable = true; - user = config.services.panoramax.database.postgres.user; - database = config.services.panoramax.database.postgres.database; - }; - - systemd.services.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; - }; -} diff --git a/modules/nixos/programs/panoramax/default.nix b/modules/nixos/programs/panoramax/default.nix deleted file mode 100644 index 29954f5..0000000 --- a/modules/nixos/programs/panoramax/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.panoramax = { - imports = [ - mod.panoramax-service - mod.panoramax-database - mod.panoramax-proxy - mod.panoramax-fail2ban - mod.panoramax-storage - ]; - }; -} diff --git a/modules/nixos/programs/panoramax/fail2ban.nix b/modules/nixos/programs/panoramax/fail2ban.nix deleted file mode 100644 index 97b3a2a..0000000 --- a/modules/nixos/programs/panoramax/fail2ban.nix +++ /dev/null @@ -1,13 +0,0 @@ -{...}: { - flake.nixosModules.panoramax-fail2ban = { - lib, - config, - ... - }: { - config = lib.mkIf (config.services.panoramax.enable && config.services.fail2ban.enable) { - # TODO: configure options for fail2ban - # This is a placeholder - panoramax fail2ban configuration would need to be defined - # based on the specific log patterns and security requirements - }; - }; -} diff --git a/modules/nixos/programs/panoramax/panoramax.nix b/modules/nixos/programs/panoramax/panoramax.nix deleted file mode 100644 index c9d09b7..0000000 --- a/modules/nixos/programs/panoramax/panoramax.nix +++ /dev/null @@ -1,361 +0,0 @@ -{...}: { - flake.nixosModules.panoramax-service = { - config, - lib, - pkgs, - ... - }: { - options.services = { - panoramax = { - enable = lib.mkEnableOption "panoramax"; - - package = lib.mkOption { - type = lib.types.package; - default = pkgs.panoramax; - description = "The panoramax package to use"; - }; - - user = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "The user panoramax should run as."; - }; - - group = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "The group panoramax should run as."; - }; - - host = lib.mkOption { - type = lib.types.str; - default = "127.0.0.1"; - description = "Host to bind the panoramax service to"; - }; - - port = lib.mkOption { - type = lib.types.nullOr lib.types.port; - default = 5000; - description = "Port for the panoramax service"; - }; - - openFirewall = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to open the panoramax port in the firewall"; - }; - - settings = { - urlScheme = lib.mkOption { - type = lib.types.enum ["http" "https"]; - default = "https"; - description = "URL scheme for the application"; - }; - - storage = { - fsUrl = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = "/var/lib/panoramax/storage"; - description = "File system URL for storage"; - }; - }; - - infrastructure = { - nbProxies = lib.mkOption { - type = lib.types.nullOr lib.types.int; - default = 1; - description = "Number of proxies in front of the application"; - }; - }; - - flask = { - secretKey = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Flask secret key for session security"; - }; - - sessionCookieDomain = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Flask session cookie domain"; - }; - }; - - api = { - pictures = { - licenseSpdxId = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "SPDX license identifier for API pictures"; - }; - - licenseUrl = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "License URL for API pictures"; - }; - }; - }; - - extraEnvironment = lib.mkOption { - type = lib.types.attrsOf lib.types.str; - default = {}; - description = "Additional environment variables"; - example = { - CUSTOM_SETTING = "value"; - DEBUG = "true"; - }; - }; - }; - - database = { - createDB = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to automatically create the database and user"; - }; - - name = lib.mkOption { - type = lib.types.str; - default = "panoramax"; - description = "The name of the panoramax database"; - }; - - host = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = "/run/postgresql"; - description = "Hostname or address of the postgresql server. If an absolute path is given here, it will be interpreted as a unix socket path."; - }; - - port = lib.mkOption { - type = lib.types.nullOr lib.types.port; - default = 5432; - description = "Port of the postgresql server."; - }; - - user = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = "panoramax"; - description = "The database user for panoramax."; - }; - - # TODO: password file for external database - }; - - sgblur = { - # TODO: configs to bind to sgblur - }; - }; - sgblur = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to enable sgblur integration for face and license plate blurring"; - }; - - package = lib.mkOption { - type = lib.types.package; - default = pkgs.sgblur; - description = "The sgblur package to use"; - }; - - port = lib.mkOption { - type = lib.types.port; - default = 8080; - description = "Port for the sgblur service"; - }; - - host = lib.mkOption { - type = lib.types.str; - default = "127.0.0.1"; - description = "Host to bind the sgblur service to"; - }; - - url = lib.mkOption { - type = lib.types.str; - default = "http://127.0.0.1:8080"; - description = "URL where sgblur service is accessible"; - }; - }; - }; - - config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ - { - # Create panoramax user and group - users.users.${config.services.panoramax.user} = { - isSystemUser = true; - group = config.services.panoramax.group; - home = "/var/lib/panoramax"; - createHome = true; - }; - - users.groups.${config.services.panoramax.group} = {}; - - # Ensure storage directory exists with correct permissions - systemd.tmpfiles.rules = [ - "d '${config.services.panoramax.settings.storage.fsUrl}' 0755 ${config.services.panoramax.user} ${config.services.panoramax.group} - -" - ]; - - systemd.services.panoramax-api = { - description = "Panoramax API server (self hosted map street view)"; - after = ["network.target" "postgresql.service"]; - wantedBy = ["multi-user.target"]; - - environment = - { - # Core Flask configuration - FLASK_APP = "geovisio"; - - # Storage configuration - FS_URL = config.services.panoramax.settings.storage.fsUrl; - - # Infrastructure configuration - INFRA_NB_PROXIES = toString config.services.panoramax.settings.infrastructure.nbProxies; - - # Application configuration - PORT = toString config.services.panoramax.port; - - # Python path to include the panoramax package - PYTHONPATH = "${config.services.panoramax.package}/${pkgs.python3.sitePackages}"; - } - // ( - if config.services.panoramax.database.host == "/run/postgresql" - then { - DB_URL = "postgresql://${config.services.panoramax.database.user}@/${config.services.panoramax.database.name}?host=/run/postgresql"; - } - else { - DB_HOST = config.services.panoramax.database.host; - DB_PORT = toString config.services.panoramax.database.port; - DB_USERNAME = config.services.panoramax.database.user; - DB_NAME = config.services.panoramax.database.name; - } - ) - // (lib.optionalAttrs (config.services.panoramax.settings.flask.secretKey != null) { - FLASK_SECRET_KEY = config.services.panoramax.settings.flask.secretKey; - }) - // (lib.optionalAttrs (config.services.panoramax.settings.flask.sessionCookieDomain != null) { - FLASK_SESSION_COOKIE_DOMAIN = config.services.panoramax.settings.flask.sessionCookieDomain; - }) - // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseSpdxId != null) { - API_PICTURES_LICENSE_SPDX_ID = config.services.panoramax.settings.api.pictures.licenseSpdxId; - }) - // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseUrl != null) { - API_PICTURES_LICENSE_URL = config.services.panoramax.settings.api.pictures.licenseUrl; - }) - // (lib.optionalAttrs config.services.sgblur.enable { - SGBLUR_API_URL = config.services.sgblur.url; - }) - // config.services.panoramax.settings.extraEnvironment; - - path = with pkgs; [ - (python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])) - ]; - - serviceConfig = { - ExecStart = "${pkgs.python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])}/bin/waitress-serve --port ${toString config.services.panoramax.port} --call geovisio:create_app"; - User = config.services.panoramax.user; - Group = config.services.panoramax.group; - WorkingDirectory = "/var/lib/panoramax"; - Restart = "always"; - RestartSec = 5; - - # Security hardening - PrivateTmp = true; - ProtectSystem = "strict"; - ProtectHome = true; - ReadWritePaths = [ - "/var/lib/panoramax" - config.services.panoramax.settings.storage.fsUrl - ]; - NoNewPrivileges = true; - PrivateDevices = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectControlGroups = true; - RestrictSUIDSGID = true; - RestrictRealtime = true; - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - SystemCallArchitectures = "native"; - }; - }; - - # Open firewall if requested - networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ - config.services.panoramax.port - ]; - } - (lib.mkIf config.services.sgblur.enable { - # SGBlur service configuration - systemd.services.sgblur = { - description = "SGBlur face and license plate blurring service"; - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - - path = with pkgs; [ - config.services.sgblur.package - python3 - python3Packages.waitress - ]; - - serviceConfig = { - ExecStart = "${pkgs.python3Packages.waitress}/bin/waitress-serve --host ${config.services.sgblur.host} --port ${toString config.services.sgblur.port} src.detect.detect_api:app"; - WorkingDirectory = "${config.services.sgblur.package}"; - Restart = "always"; - RestartSec = 5; - - # Basic security hardening - PrivateTmp = true; - ProtectSystem = "strict"; - ProtectHome = true; - NoNewPrivileges = true; - PrivateDevices = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectControlGroups = true; - RestrictSUIDSGID = true; - RestrictRealtime = true; - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - SystemCallArchitectures = "native"; - }; - }; - - networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ - config.services.sgblur.port - ]; - }) - (lib.mkIf config.services.panoramax.database.createDB { - services.postgresql = { - enable = true; - ensureDatabases = lib.mkIf config.services.panoramax.database.createDB [config.services.panoramax.database.name]; - ensureUsers = lib.mkIf config.services.panoramax.database.createDB [ - { - name = config.services.panoramax.database.user; - ensureDBOwnership = true; - ensureClauses.login = true; - } - ]; - extensions = ps: with ps; [postgis]; - }; - systemd.services.postgresql.serviceConfig.ExecStartPost = let - sqlFile = pkgs.writeText "panoramax-postgis-setup.sql" '' - CREATE EXTENSION IF NOT EXISTS postgis; - - -- TODO: how can we ensure that this runs after the databases have been created - -- ALTER DATABASE ${config.services.panoramax.database.name} SET TIMEZONE TO 'UTC'; - - GRANT SET ON PARAMETER session_replication_role TO ${config.services.panoramax.database.user}; - ''; - in [ - '' - ${lib.getExe' config.services.postgresql.package "psql"} -d "${config.services.panoramax.database.user}" -f "${sqlFile}" - '' - ]; - }) - ]); - }; -} diff --git a/modules/nixos/programs/panoramax/proxy.nix b/modules/nixos/programs/panoramax/proxy.nix deleted file mode 100644 index 4112e90..0000000 --- a/modules/nixos/programs/panoramax/proxy.nix +++ /dev/null @@ -1,41 +0,0 @@ -{...}: { - flake.nixosModules.panoramax-proxy = { - lib, - config, - ... - }: { - options.services.panoramax = { - domain = lib.mkOption { - type = lib.types.str; - description = "domain that panoramax will be hosted at"; - default = "panoramax.arpa"; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for panoramax"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.panoramax.enable && config.services.reverseProxy.enable; - }; - }; - }; - - config = lib.mkIf config.services.panoramax.reverseProxy.enable { - services.reverseProxy.services.panoramax = { - target = "http://localhost:${toString config.services.panoramax.port}"; - domain = config.services.panoramax.domain; - extraDomains = config.services.panoramax.extraDomains; - - settings = { - proxyWebsockets.enable = true; - forwardHeaders.enable = true; - maxBodySize = 100000; - timeout = 300; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/panoramax/storage.nix b/modules/nixos/programs/panoramax/storage.nix deleted file mode 100644 index cbd35d4..0000000 --- a/modules/nixos/programs/panoramax/storage.nix +++ /dev/null @@ -1,21 +0,0 @@ -{...}: { - flake.nixosModules.panoramax-storage = { - lib, - config, - ... - }: { - options.services.panoramax.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.panoramax.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.panoramax.enable { - storage.datasets.replicate."system/root" = { - directories."/var/lib/panoramax" = lib.mkIf config.services.panoramax.impermanence.enable { - owner.name = "panoramax"; - group.name = "panoramax"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/paperless/database.nix b/modules/nixos/programs/paperless/database.nix deleted file mode 100644 index de88dc0..0000000 --- a/modules/nixos/programs/paperless/database.nix +++ /dev/null @@ -1,32 +0,0 @@ -{...}: { - flake.nixosModules.paperless-database = { - config, - lib, - ... - }: { - config = lib.mkIf config.services.paperless.enable { - assertions = [ - { - assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable; - message = "PostgreSQL must be enabled when using local postgres database for Paperless"; - } - { - assertion = !config.services.paperless.database.createLocally || (builtins.any (db: db == "paperless") config.services.postgresql.ensureDatabases); - message = "Paperless built-in database creation failed - expected 'paperless' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; - } - { - assertion = !config.services.paperless.database.createLocally || (builtins.any (user: user.name == "paperless") config.services.postgresql.ensureUsers); - message = "Paperless built-in user creation failed - expected user 'paperless' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; - } - ]; - - services.paperless.database.createLocally = lib.mkDefault true; - - systemd.services.paperless-scheduler = lib.mkIf config.services.paperless.database.createLocally { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }; - }; -} diff --git a/modules/nixos/programs/paperless/default.nix b/modules/nixos/programs/paperless/default.nix deleted file mode 100644 index 22df555..0000000 --- a/modules/nixos/programs/paperless/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.paperless = { - imports = [ - mod.paperless-service - mod.paperless-database - mod.paperless-proxy - mod.paperless-fail2ban - mod.paperless-storage - ]; - }; -} diff --git a/modules/nixos/programs/paperless/fail2ban.nix b/modules/nixos/programs/paperless/fail2ban.nix deleted file mode 100644 index c5c3294..0000000 --- a/modules/nixos/programs/paperless/fail2ban.nix +++ /dev/null @@ -1,36 +0,0 @@ -{...}: { - flake.nixosModules.paperless-fail2ban = { - config, - lib, - pkgs, - ... - }: { - config = lib.mkIf (config.services.paperless.enable && config.services.fail2ban.enable) { - environment.etc = { - "fail2ban/filter.d/paperless.local".text = ( - pkgs.lib.mkDefault (pkgs.lib.mkAfter '' - [Definition] - failregex = Login failed for user `.*` from (?:IP|private IP) ``\.$ - ignoreregex = - - '') - ); - }; - - services.fail2ban = { - jails = { - paperless.settings = { - enabled = true; - filter = "paperless"; - action = ''iptables-multiport[name=HTTP, port="http,https"]''; - logpath = "${config.services.paperless.dataDir}/log/*.log"; - backend = "auto"; - findtime = 600; - bantime = 600; - maxretry = 5; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/paperless/paperless.nix b/modules/nixos/programs/paperless/paperless.nix deleted file mode 100644 index de295ee..0000000 --- a/modules/nixos/programs/paperless/paperless.nix +++ /dev/null @@ -1,29 +0,0 @@ -{...}: { - flake.nixosModules.paperless-service = { - config, - lib, - ... - }: { - options.services.paperless = { - database = { - user = lib.mkOption { - type = lib.types.str; - description = "what is the user and database that we are going to use for paperless"; - default = "paperless"; - }; - }; - }; - - config = lib.mkIf config.services.paperless.enable { - services.paperless = { - configureTika = true; - settings = { - PAPERLESS_DBENGINE = "postgresql"; - PAPERLESS_DBHOST = "/run/postgresql"; - PAPERLESS_DBNAME = config.services.paperless.database.user; - PAPERLESS_DBUSER = config.services.paperless.database.user; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/paperless/proxy.nix b/modules/nixos/programs/paperless/proxy.nix deleted file mode 100644 index 95af0a7..0000000 --- a/modules/nixos/programs/paperless/proxy.nix +++ /dev/null @@ -1,35 +0,0 @@ -{...}: { - flake.nixosModules.paperless-proxy = { - config, - lib, - ... - }: { - options.services.paperless = { - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for paperless"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.paperless.enable && config.services.reverseProxy.enable; - }; - }; - }; - - config = lib.mkIf config.services.paperless.reverseProxy.enable { - services.reverseProxy.services.paperless = { - target = "http://${config.services.paperless.address}:${toString config.services.paperless.port}"; - domain = config.services.paperless.domain; - extraDomains = config.services.paperless.extraDomains; - - settings = { - proxyWebsockets.enable = true; - forwardHeaders.enable = true; - maxBodySize = 50000; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/paperless/storage.nix b/modules/nixos/programs/paperless/storage.nix deleted file mode 100644 index 969a1e1..0000000 --- a/modules/nixos/programs/paperless/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.paperless-storage = { - config, - lib, - ... - }: let - dataDir = "/var/lib/paperless"; - in { - options.services.paperless.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.paperless.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.paperless.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.paperless.impermanence.enable { - owner.name = "paperless"; - group.name = "paperless"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/postgres/default.nix b/modules/nixos/programs/postgres/default.nix deleted file mode 100644 index 4a13462..0000000 --- a/modules/nixos/programs/postgres/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.postgres = { - imports = [ - mod.postgres-service - mod.postgres-storage - ]; - }; -} diff --git a/modules/nixos/programs/postgres/postgres.nix b/modules/nixos/programs/postgres/postgres.nix deleted file mode 100644 index fecd7cd..0000000 --- a/modules/nixos/programs/postgres/postgres.nix +++ /dev/null @@ -1,124 +0,0 @@ -{...}: { - flake.nixosModules.postgres-service = { - config, - lib, - pkgs, - ... - }: let - enabledDatabases = lib.filterAttrs (_: db: db.enable) config.services.postgresql.databases; - extraDatabasesList = config.services.postgresql.extraDatabases; - - serviceDatabaseUsers = lib.mapAttrsToList (_: db: { - name = db.user; - ensureDBOwnership = true; - }) (lib.filterAttrs (_: db: db.ensureUser) enabledDatabases); - - extraDatabaseUsers = - builtins.map (dbName: { - name = dbName; - ensureDBOwnership = true; - }) - extraDatabasesList; - - serviceDatabases = lib.mapAttrsToList (_: db: db.database) enabledDatabases; - extraDatabaseNames = extraDatabasesList; - - serviceUserMappings = lib.mapAttrsToList (_: db: "user_map ${db.user} ${db.user}") enabledDatabases; - extraUserMappings = builtins.map (dbName: "user_map ${dbName} ${dbName}") extraDatabasesList; - - builtinServiceMappings = let - forgejoMapping = lib.optional (config.services.forgejo.enable && config.services.forgejo.database.type == "postgres") "user_map forgejo forgejo"; - immichMapping = lib.optional (config.services.immich.enable && config.services.immich.database.enable) "user_map immich immich"; - paperlessMapping = lib.optional (config.services.paperless.enable && config.services.paperless.database.createLocally) "user_map paperless paperless"; - in - forgejoMapping ++ immichMapping ++ paperlessMapping; - in { - options = { - services.postgresql = { - databases = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = "Whether to create this database and user"; - }; - user = lib.mkOption { - type = lib.types.str; - default = name; - description = "Database user name"; - }; - database = lib.mkOption { - type = lib.types.str; - default = name; - description = "Database name"; - }; - ensureUser = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to ensure the user exists"; - }; - }; - })); - default = {}; - description = "Databases to create for services"; - }; - - extraDatabases = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Additional databases to create (user name will match database name)"; - example = ["custom_db" "test_db"]; - }; - - adminUsers = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "System users who should have PostgreSQL superuser access"; - example = ["leyla" "admin"]; - }; - }; - }; - - config = lib.mkIf config.services.postgresql.enable { - services = { - postgresql = { - package = pkgs.postgresql_16; - - ensureUsers = - [ - {name = "postgres";} - ] - ++ serviceDatabaseUsers ++ extraDatabaseUsers; - - ensureDatabases = serviceDatabases ++ extraDatabaseNames; - - identMap = - '' - # ArbitraryMapName systemUser DBUser - - # Administration Users - superuser_map root postgres - superuser_map postgres postgres - '' - + ( - lib.strings.concatLines (builtins.map (user: "superuser_map ${user} postgres") config.services.postgresql.adminUsers) - ) - + '' - - # Client Users - '' - + ( - lib.strings.concatLines (serviceUserMappings ++ extraUserMappings ++ builtinServiceMappings) - ); - - authentication = pkgs.lib.mkOverride 10 '' - # type database DBuser origin-address auth-method optional_ident_map - local all postgres peer map=superuser_map - local sameuser all peer map=user_map - ''; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/postgres/storage.nix b/modules/nixos/programs/postgres/storage.nix deleted file mode 100644 index 056dc45..0000000 --- a/modules/nixos/programs/postgres/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.postgres-storage = { - config, - lib, - ... - }: let - dataDir = "/var/lib/postgresql/16"; - in { - options.services.postgresql.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.postgresql.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.postgresql.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.postgresql.impermanence.enable { - owner.name = "postgres"; - group.name = "postgres"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/qbittorent/default.nix b/modules/nixos/programs/qbittorent/default.nix deleted file mode 100644 index ea0f403..0000000 --- a/modules/nixos/programs/qbittorent/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.qbittorent = { - imports = [ - mod.qbittorent-service - mod.qbittorent-storage - ]; - }; -} diff --git a/modules/nixos/programs/qbittorent/qbittorent.nix b/modules/nixos/programs/qbittorent/qbittorent.nix deleted file mode 100644 index ac558fc..0000000 --- a/modules/nixos/programs/qbittorent/qbittorent.nix +++ /dev/null @@ -1,20 +0,0 @@ -{...}: { - flake.nixosModules.qbittorent-service = { - lib, - config, - ... - }: { - options.services.qbittorrent = { - mediaDir = lib.mkOption { - type = lib.types.path; - description = lib.mdDoc '' - The directory to create to store qbittorrent media. - ''; - }; - }; - - config = lib.mkIf config.services.qbittorrent.enable { - # Main qbittorrent configuration goes here if needed - }; - }; -} diff --git a/modules/nixos/programs/qbittorent/storage.nix b/modules/nixos/programs/qbittorent/storage.nix deleted file mode 100644 index 55b9866..0000000 --- a/modules/nixos/programs/qbittorent/storage.nix +++ /dev/null @@ -1,48 +0,0 @@ -{...}: { - flake.nixosModules.qbittorent-storage = { - lib, - config, - ... - }: let - qbittorent_profile_directory = "/var/lib/qBittorrent/"; - in { - options.services.qbittorrent.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.qbittorrent.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.qbittorrent.enable { - storage.datasets.replicate = { - "system/root" = { - directories."${qbittorent_profile_directory}" = lib.mkIf config.services.qbittorrent.impermanence.enable { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - }; - }; - "system/media" = { - mount = "/persist/replicate/system/media"; - - directories."${config.services.qbittorrent.mediaDir}" = lib.mkIf config.services.qbittorrent.impermanence.enable { - owner.name = "qbittorrent"; - group.name = "qbittorrent"; - owner.permissions = { - read = true; - write = true; - execute = true; - }; - group.permissions = { - read = true; - write = true; - execute = true; - }; - other.permissions = { - read = true; - write = false; - execute = true; - }; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/radarr/default.nix b/modules/nixos/programs/radarr/default.nix deleted file mode 100644 index e3ea4e4..0000000 --- a/modules/nixos/programs/radarr/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.radarr = { - imports = [ - mod.radarr-storage - ]; - }; -} diff --git a/modules/nixos/programs/radarr/storage.nix b/modules/nixos/programs/radarr/storage.nix deleted file mode 100644 index 8cb98ae..0000000 --- a/modules/nixos/programs/radarr/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.radarr-storage = { - lib, - config, - ... - }: let - radarr_data_directory = "/var/lib/radarr/.config/Radarr"; - in { - options.services.radarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.radarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.radarr.enable { - storage.datasets.replicate."system/root" = { - directories."${radarr_data_directory}" = lib.mkIf config.services.radarr.impermanence.enable { - owner.name = "radarr"; - group.name = "radarr"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/reverseProxy/default.nix b/modules/nixos/programs/reverseProxy/default.nix deleted file mode 100644 index 20af20c..0000000 --- a/modules/nixos/programs/reverseProxy/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.reverse-proxy = { - imports = [ - mod.reverse-proxy-service - mod.reverse-proxy-storage - ]; - }; -} diff --git a/modules/nixos/programs/reverseProxy/reverseProxy.nix b/modules/nixos/programs/reverseProxy/reverseProxy.nix deleted file mode 100644 index 9401bc6..0000000 --- a/modules/nixos/programs/reverseProxy/reverseProxy.nix +++ /dev/null @@ -1,178 +0,0 @@ -{...}: { - flake.nixosModules.reverse-proxy-service = { - lib, - config, - ... - }: { - options.services.reverseProxy = { - enable = lib.mkEnableOption "turn on the reverse proxy"; - openFirewall = lib.mkEnableOption "open the firewall"; - refuseUnmatchedDomains = lib.mkOption { - type = lib.types.bool; - description = "refuse connections for domains that don't match any configured virtual hosts"; - default = true; - }; - ports = { - http = lib.mkOption { - type = lib.types.port; - description = "HTTP port for the reverse proxy"; - default = 80; - }; - https = lib.mkOption { - type = lib.types.port; - description = "HTTPS port for the reverse proxy"; - default = 443; - }; - }; - acme = { - enable = lib.mkOption { - type = lib.types.bool; - description = "enable ACME certificate management"; - default = true; - }; - email = lib.mkOption { - type = lib.types.str; - description = "email address for ACME certificate registration"; - }; - }; - services = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - target = lib.mkOption { - type = lib.types.str; - description = "what url will all traffic to this application be forwarded to"; - }; - domain = lib.mkOption { - type = lib.types.str; - description = "what is the default subdomain to be used for this application to be used for"; - default = name; - }; - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for this domain"; - default = []; - }; - settings = { - certificateRenewal.enable = lib.mkOption { - type = lib.types.bool; - description = "auto renew certificates"; - default = true; - }; - forceSSL.enable = lib.mkOption { - type = lib.types.bool; - description = "auto renew certificates"; - default = true; - }; - proxyHeaders = { - enable = lib.mkEnableOption "should we proxy headers"; - timeout = lib.mkOption { - type = lib.types.int; - default = 60; - }; - }; - proxyWebsockets.enable = lib.mkEnableOption "should the default config proxy websockets"; - forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers"; - noSniff.enable = lib.mkEnableOption "should the no sniff flags be set"; - proxyBuffering.enable = lib.mkOption { - type = lib.types.bool; - description = "should proxy buffering be enabled"; - default = true; - }; - maxBodySize = lib.mkOption { - type = lib.types.nullOr lib.types.int; - description = ""; - default = null; - }; - }; - }; - })); - }; - }; - - config = let - httpPort = config.services.reverseProxy.ports.http; - httpsPort = config.services.reverseProxy.ports.https; - in - lib.mkIf config.services.reverseProxy.enable { - security.acme = lib.mkIf config.services.reverseProxy.acme.enable { - acceptTerms = true; - defaults.email = config.services.reverseProxy.acme.email; - }; - - services.nginx = { - enable = true; - virtualHosts = lib.mkMerge ( - (lib.optionals config.services.reverseProxy.refuseUnmatchedDomains [ - { - "_" = { - default = true; - serverName = "_"; - locations."/" = { - extraConfig = '' - return 444; - ''; - }; - }; - } - ]) - ++ lib.lists.flatten ( - lib.attrsets.mapAttrsToList ( - name: service: let - hostConfig = { - forceSSL = service.settings.forceSSL.enable; - enableACME = service.settings.certificateRenewal.enable; - locations = { - "/" = { - proxyPass = service.target; - proxyWebsockets = service.settings.proxyWebsockets.enable; - recommendedProxySettings = service.settings.forwardHeaders.enable; - extraConfig = let - # Client upload size configuration - maxBodySizeConfig = - lib.optionalString (service.settings.maxBodySize != null) - "client_max_body_size ${toString service.settings.maxBodySize}M;"; - - # Security header configuration - noSniffConfig = - lib.optionalString service.settings.noSniff.enable - "add_header X-Content-Type-Options nosniff;"; - - # Proxy buffering configuration - proxyBufferingConfig = - lib.optionalString (!service.settings.proxyBuffering.enable) - "proxy_buffering off;"; - - # Proxy timeout configuration - proxyTimeoutConfig = - lib.optionalString service.settings.proxyHeaders.enable - '' - proxy_read_timeout ${toString service.settings.proxyHeaders.timeout}s; - proxy_connect_timeout ${toString service.settings.proxyHeaders.timeout}s; - proxy_send_timeout ${toString service.settings.proxyHeaders.timeout}s; - ''; - in - maxBodySizeConfig + noSniffConfig + proxyBufferingConfig + proxyTimeoutConfig; - }; - }; - }; - in ( - [ - { - ${service.domain} = hostConfig; - } - ] - ++ builtins.map (domain: {${domain} = hostConfig;}) - service.extraDomains - ) - ) - config.services.reverseProxy.services - ) - ); - }; - networking.firewall.allowedTCPPorts = lib.mkIf config.services.reverseProxy.openFirewall [ - httpPort - httpsPort - ]; - }; - }; -} diff --git a/modules/nixos/programs/reverseProxy/storage.nix b/modules/nixos/programs/reverseProxy/storage.nix deleted file mode 100644 index 868534b..0000000 --- a/modules/nixos/programs/reverseProxy/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.reverse-proxy-storage = { - lib, - config, - ... - }: let - dataDir = "/var/lib/acme"; - in { - options.services.reverseProxy.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.reverseProxy.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.reverseProxy.enable { - storage.datasets.replicate."system/root" = { - directories."${dataDir}" = lib.mkIf config.services.reverseProxy.impermanence.enable { - owner.name = "acme"; - group.name = "acme"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/searx/default.nix b/modules/nixos/programs/searx/default.nix deleted file mode 100644 index d6ed868..0000000 --- a/modules/nixos/programs/searx/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.searx = { - imports = [ - mod.searx-service - mod.searx-proxy - ]; - }; -} diff --git a/modules/nixos/programs/searx/proxy.nix b/modules/nixos/programs/searx/proxy.nix deleted file mode 100644 index e0ca084..0000000 --- a/modules/nixos/programs/searx/proxy.nix +++ /dev/null @@ -1,33 +0,0 @@ -{...}: { - flake.nixosModules.searx-proxy = { - config, - lib, - ... - }: { - options.services.searx = { - extraDomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for searx"; - default = []; - }; - reverseProxy = { - enable = lib.mkOption { - type = lib.types.bool; - default = config.services.searx.enable && config.services.reverseProxy.enable; - }; - }; - }; - - config = lib.mkIf config.services.searx.reverseProxy.enable { - services.reverseProxy.services.searx = { - target = "http://localhost:${toString config.services.searx.settings.server.port}"; - domain = config.services.searx.domain; - extraDomains = config.services.searx.extraDomains; - - settings = { - forwardHeaders.enable = true; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/searx/searx.nix b/modules/nixos/programs/searx/searx.nix deleted file mode 100644 index 3ad9710..0000000 --- a/modules/nixos/programs/searx/searx.nix +++ /dev/null @@ -1,61 +0,0 @@ -{...}: { - flake.nixosModules.searx-service = { - config, - lib, - inputs, - ... - }: { - config = lib.mkIf config.services.searx.enable { - sops.secrets = { - "services/searx" = { - sopsFile = "${inputs.secrets}/defiant-services.yaml"; - }; - }; - - services.searx = { - environmentFile = config.sops.secrets."services/searx".path; - - # Rate limiting - limiterSettings = { - real_ip = { - x_for = 1; - ipv4_prefix = 32; - ipv6_prefix = 56; - }; - - botdetection = { - ip_limit = { - filter_link_local = true; - link_token = true; - }; - }; - }; - - settings = { - server = { - port = 8083; - secret_key = "@SEARXNG_SECRET@"; - }; - - # Search engine settings - search = { - safe_search = 2; - autocomplete_min = 2; - autocomplete = "duckduckgo"; - }; - - # Enabled plugins - enabled_plugins = [ - "Basic Calculator" - "Hash plugin" - "Tor check plugin" - "Open Access DOI rewrite" - "Hostnames plugin" - "Unit converter plugin" - "Tracker URL remover" - ]; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/sonarr/default.nix b/modules/nixos/programs/sonarr/default.nix deleted file mode 100644 index 681cd4b..0000000 --- a/modules/nixos/programs/sonarr/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.sonarr = { - imports = [ - mod.sonarr-storage - ]; - }; -} diff --git a/modules/nixos/programs/sonarr/storage.nix b/modules/nixos/programs/sonarr/storage.nix deleted file mode 100644 index 01aa891..0000000 --- a/modules/nixos/programs/sonarr/storage.nix +++ /dev/null @@ -1,23 +0,0 @@ -{...}: { - flake.nixosModules.sonarr-storage = { - lib, - config, - ... - }: let - sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; - in { - options.services.sonarr.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.sonarr.enable && config.storage.impermanence.enable; - }; - - config = lib.mkIf config.services.sonarr.enable { - storage.datasets.replicate."system/root" = { - directories."${sonarr_data_directory}" = lib.mkIf config.services.sonarr.impermanence.enable { - owner.name = "sonarr"; - group.name = "sonarr"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/steam.nix b/modules/nixos/programs/steam.nix deleted file mode 100644 index 5a20742..0000000 --- a/modules/nixos/programs/steam.nix +++ /dev/null @@ -1,11 +0,0 @@ -{...}: { - flake.nixosModules.steam = {...}: { - programs = { - steam = { - remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play - dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server - localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers - }; - }; - }; -} diff --git a/modules/nixos/programs/sync/default.nix b/modules/nixos/programs/sync/default.nix deleted file mode 100644 index 133aeaa..0000000 --- a/modules/nixos/programs/sync/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.sync = { - imports = [ - mod.sync-service - mod.sync-storage - ]; - }; -} diff --git a/modules/nixos/programs/sync/storage.nix b/modules/nixos/programs/sync/storage.nix deleted file mode 100644 index a3b71d7..0000000 --- a/modules/nixos/programs/sync/storage.nix +++ /dev/null @@ -1,34 +0,0 @@ -{...}: { - flake.nixosModules.sync-storage = { - config, - lib, - ... - }: let - mountDir = "/mnt/sync"; - configDir = "/etc/syncthing"; - in { - options = { - services.syncthing.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.syncthing.enable && config.storage.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.syncthing.enable { - storage.datasets.replicate."system/root" = { - directories = { - "${mountDir}" = lib.mkIf config.services.syncthing.impermanence.enable { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - "${configDir}" = lib.mkIf config.services.syncthing.impermanence.enable { - enable = true; - owner.name = "syncthing"; - group.name = "syncthing"; - }; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/sync/sync.nix b/modules/nixos/programs/sync/sync.nix deleted file mode 100644 index 935f401..0000000 --- a/modules/nixos/programs/sync/sync.nix +++ /dev/null @@ -1,38 +0,0 @@ -{...}: { - flake.nixosModules.sync-service = { - config, - lib, - syncthingConfiguration, - ... - }: let - mountDir = "/mnt/sync"; - configDir = "/etc/syncthing"; - in { - config = lib.mkMerge [ - { - systemd = lib.mkIf config.services.syncthing.enable { - tmpfiles.rules = [ - "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" - "d ${mountDir} 2755 syncthing syncthing -" - "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" - "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" - ]; - }; - } - (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ - { - services.syncthing = { - user = "syncthing"; - group = "syncthing"; - dataDir = "${mountDir}/default"; - configDir = configDir; - overrideDevices = true; - overrideFolders = true; - configuration = syncthingConfiguration; - deviceName = config.networking.hostName; - }; - } - ])) - ]; - }; -} diff --git a/modules/nixos/programs/tailscale/default.nix b/modules/nixos/programs/tailscale/default.nix deleted file mode 100644 index 0768379..0000000 --- a/modules/nixos/programs/tailscale/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{config, ...}: let - mod = config.flake.nixosModules; -in { - flake.nixosModules.tailscale = { - imports = [ - mod.tailscale-service - mod.tailscale-storage - ]; - }; -} diff --git a/modules/nixos/programs/tailscale/storage.nix b/modules/nixos/programs/tailscale/storage.nix deleted file mode 100644 index 4fc969a..0000000 --- a/modules/nixos/programs/tailscale/storage.nix +++ /dev/null @@ -1,26 +0,0 @@ -{...}: { - flake.nixosModules.tailscale-storage = { - config, - lib, - ... - }: let - tailscale_data_directory = "/var/lib/tailscale"; - in { - options = { - services.tailscale.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.tailscale.enable && config.storage.impermanence.enable; - }; - }; - - config = lib.mkIf config.services.tailscale.enable { - storage.datasets.replicate."system/root" = { - directories."${tailscale_data_directory}" = lib.mkIf config.services.tailscale.impermanence.enable { - enable = true; - owner.name = "root"; - group.name = "root"; - }; - }; - }; - }; -} diff --git a/modules/nixos/programs/tailscale/tailscale.nix b/modules/nixos/programs/tailscale/tailscale.nix deleted file mode 100644 index 37876ac..0000000 --- a/modules/nixos/programs/tailscale/tailscale.nix +++ /dev/null @@ -1,21 +0,0 @@ -{...}: { - flake.nixosModules.tailscale-service = { - config, - lib, - ... - }: { - options = { - host.tailscale = { - enable = lib.mkEnableOption "should tailscale be enabled on this computer"; - }; - }; - - config = lib.mkIf config.services.tailscale.enable ( - lib.mkMerge [ - { - # any configs we want shared between all machines - } - ] - ); - }; -} diff --git a/modules/nixos/programs/wyoming.nix b/modules/nixos/programs/wyoming.nix deleted file mode 100644 index 8f0af5d..0000000 --- a/modules/nixos/programs/wyoming.nix +++ /dev/null @@ -1,65 +0,0 @@ -{...}: { - flake.nixosModules.wyoming = { - lib, - config, - ... - }: { - options.services.wyoming.enable = lib.mkEnableOption "should wyoming be enabled on this device"; - config = lib.mkIf config.services.wyoming.enable (lib.mkMerge [ - { - services.wyoming = { - # Text to speech - piper = { - servers = { - "en" = { - enable = true; - # see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py - voice = "en-us-amy-low"; - uri = "tcp://0.0.0.0:10200"; - speaker = 0; - }; - }; - }; - - # Speech to text - faster-whisper = { - servers = { - "en" = { - enable = true; - # see https://github.com/rhasspy/rhasspy3/blob/master/programs/asr/faster-whisper/script/download.py - model = "tiny-int8"; - language = "en"; - uri = "tcp://0.0.0.0:10300"; - device = "cpu"; - }; - }; - }; - - openwakeword = { - enable = true; - uri = "tcp://0.0.0.0:10400"; - # preloadModels = [ - # "ok_nabu" - # ]; - # TODO: custom models - }; - }; - - # needs access to /proc/cpuinfo - systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all"; - } - (lib.mkIf config.host.impermanence.enable { - environment.persistence."/persist/replicate/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = "/var/lib/private/wyoming"; - mode = "0700"; - } - ]; - }; - }) - ]); - }; -} diff --git a/modules/nixos/ssh.nix b/modules/nixos/ssh.nix deleted file mode 100644 index b4f94bc..0000000 --- a/modules/nixos/ssh.nix +++ /dev/null @@ -1,46 +0,0 @@ -{...}: { - flake.nixosModules.nixos-ssh = { - lib, - config, - ... - }: { - options = { - services.openssh.impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = config.services.openssh.enable && config.storage.impermanence.enable; - }; - }; - - config = { - services = { - openssh = { - enable = true; - ports = [22]; - settings = { - PasswordAuthentication = false; - UseDns = true; - X11Forwarding = false; - }; - }; - }; - - storage.datasets.replicate."system/root" = { - files = lib.mkIf config.services.openssh.impermanence.enable (builtins.listToAttrs ( - lib.lists.flatten ( - builtins.map (hostKey: [ - { - name = hostKey.path; - value = {enable = true;}; - } - { - name = "${hostKey.path}.pub"; - value = {enable = true;}; - } - ]) - config.services.openssh.hostKeys - ) - )); - }; - }; - }; -} diff --git a/modules/nixos/storage/dataset.nix b/modules/nixos/storage/dataset.nix deleted file mode 100644 index 4ae1179..0000000 --- a/modules/nixos/storage/dataset.nix +++ /dev/null @@ -1,90 +0,0 @@ -{...}: let - submodule = {lib, ...}: {name, ...}: { - options = { - type = lib.mkOption { - type = lib.types.enum ["zfs_fs" "zfs_volume"]; - default = "zfs_fs"; - description = "Type of ZFS dataset (filesystem or volume)"; - }; - - acltype = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["off" "nfsv4" "posixacl"]); - default = null; - description = "Access control list type"; - }; - - relatime = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off"]); - default = null; - description = "Controls when access time is updated"; - }; - - atime = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off"]); - default = null; - description = "Controls whether access time is updated"; - }; - - xattr = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "sa" "dir"]); - default = null; - description = "Extended attribute storage method"; - }; - - compression = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["on" "off" "lz4" "gzip" "zstd" "lzjb" "zle"]); - default = null; - description = "Compression algorithm to use"; - }; - - sync = lib.mkOption { - type = lib.types.nullOr (lib.types.enum ["standard" "always" "disabled"]); - default = null; - description = "Synchronous write behavior"; - }; - - mount = lib.mkOption { - type = lib.types.nullOr lib.types.str; - description = "Controls the mount point used for this file system"; - default = null; - }; - - encryption = { - enable = lib.mkEnableOption "should encryption be enabled"; - type = lib.mkOption { - type = lib.types.enum ["aes-128-ccm" "aes-192-ccm" "aes-256-ccm" "aes-128-gcm" "aes-192-gcm" "aes-256-gcm"]; - description = "What encryption type to use"; - }; - keyformat = lib.mkOption { - type = lib.types.enum ["raw" "hex" "passphrase"]; - description = "Format of the encryption key"; - }; - keylocation = lib.mkOption { - type = lib.types.str; - description = "Location of the encryption key"; - }; - }; - - snapshot = { - # This option should set this option flag - autoSnapshot = lib.mkEnableOption "Enable automatic snapshots for this dataset"; - # Creates a blank snapshot in the post create hook for rollback purposes - blankSnapshot = lib.mkEnableOption "Should a blank snapshot be auto created in the post create hook"; - }; - - recordSize = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Suggested block size for files in the file system"; - }; - - postCreateHook = lib.mkOption { - type = lib.types.str; - default = ""; - description = "Script to run after dataset creation"; - }; - }; - }; -in { - flake.commonModules.storage-dataset-submodule = submodule; -} diff --git a/modules/nixos/storage/impermanence-dataset.nix b/modules/nixos/storage/impermanence-dataset.nix deleted file mode 100644 index 380e3f4..0000000 --- a/modules/nixos/storage/impermanence-dataset.nix +++ /dev/null @@ -1,60 +0,0 @@ -{config, ...}: let - datasetSubmodule = config.flake.commonModules.storage-dataset-submodule; - submodule = args @ {lib, ...}: {name, ...}: let - pathPermissions = { - read = lib.mkEnableOption "should the path have read permissions"; - write = lib.mkEnableOption "should the path have read permissions"; - execute = lib.mkEnableOption "should the path have read permissions"; - }; - pathTypeSubmodule = {name, ...}: { - options = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - owner = { - name = lib.mkOption { - type = lib.types.str; - default = "root"; - }; - permissions = pathPermissions; - }; - group = { - name = lib.mkOption { - type = lib.types.str; - default = "root"; - }; - permissions = pathPermissions; - }; - other = { - permissions = pathPermissions; - }; - }; - }; - in { - imports = [ - (datasetSubmodule args) - ]; - - options = { - files = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - directories = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule); - default = {}; - }; - impermanence.enable = lib.mkOption { - type = lib.types.bool; - default = true; - }; - }; - - config = { - mount = lib.mkDefault "/${name}"; - }; - }; -in { - flake.commonModules.storage-impermanence-dataset-submodule = submodule; -} diff --git a/modules/nixos/storage/impermanence.nix b/modules/nixos/storage/impermanence.nix deleted file mode 100644 index 1ce8e5d..0000000 --- a/modules/nixos/storage/impermanence.nix +++ /dev/null @@ -1,147 +0,0 @@ -{config, ...}: let - datasetSubmodule = config.flake.commonModules.storage-dataset-submodule; - impermanenceDatasetSubmodule = config.flake.commonModules.storage-impermanence-dataset-submodule; -in { - flake.nixosModules.storage-impermanence = args @ { - lib, - config, - ... - }: let - datasetSub = datasetSubmodule args; - impermanenceDatasetSub = impermanenceDatasetSubmodule args; - - permissionsToMode = permissions: let - permSetToDigit = permSet: - ( - if permSet.read - then 4 - else 0 - ) - + ( - if permSet.write - then 2 - else 0 - ) - + ( - if permSet.execute - then 1 - else 0 - ); - - ownerDigit = permSetToDigit permissions.owner.permissions; - groupDigit = permSetToDigit permissions.group.permissions; - otherDigit = permSetToDigit permissions.other.permissions; - in - toString ownerDigit + toString groupDigit + toString otherDigit; - - # Get the option names from both submodules to automatically determine which are impermanence-specific - regularDatasetEval = lib.evalModules { - modules = [datasetSub]; - specialArgs = args; - }; - impermanenceDatasetEval = lib.evalModules { - modules = [impermanenceDatasetSub]; - specialArgs = args; - }; - - regularDatasetOptions = builtins.attrNames regularDatasetEval.options; - impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; - - # Find options that are only in impermanence datasets (not in regular ZFS datasets) - impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; - in { - options.storage = { - impermanence = { - enable = lib.mkEnableOption "should impermanence be enabled for this system"; - - datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSub); - default = {}; - }; - }; - }; - - config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [ - { - assertions = [ - { - assertion = config.storage.zfs.enable; - message = "storage.impermanence can not be used without storage.zfs."; - } - ]; - - system.activationScripts = { - # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 - "createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; - - "var-lib-private-permissions" = lib.mkIf config.storage.generateBase { - deps = ["specialfs"]; - text = '' - mkdir -p /persist/replicate/system/root/var/lib/private - chmod 0700 /persist/replicate/system/root/var/lib/private - ''; - }; - }; - - programs.fuse.userAllowOther = true; - - # Suppress sudo lecture on every boot since impermanence wipes the lecture status file - security.sudo.extraConfig = "Defaults lecture=never"; - - fileSystems = - lib.mapAttrs' ( - datasetName: dataset: - lib.nameValuePair "/${datasetName}" { - device = "rpool/${datasetName}"; - fsType = "zfs"; - neededForBoot = true; - } - ) - (lib.filterAttrs ( - datasetName: dataset: dataset.impermanence.enable - ) - config.storage.impermanence.datasets); - - environment.persistence = - lib.mapAttrs (datasetName: dataset: { - enable = true; - hideMounts = true; - persistentStoragePath = "/${datasetName}"; - directories = lib.mapAttrsToList (path: dirConfig: { - directory = path; - user = dirConfig.owner.name; - group = dirConfig.group.name; - mode = permissionsToMode dirConfig; - }) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories); - files = lib.mapAttrsToList (path: fileConfig: { - file = path; - parentDirectory = { - user = fileConfig.owner.name; - group = fileConfig.group.name; - mode = permissionsToMode fileConfig; - }; - }) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files); - }) - (lib.filterAttrs ( - datasetName: dataset: let - enabledDirectories = lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories; - enabledFiles = lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files; - in - (enabledDirectories != {}) || (enabledFiles != {}) - ) - (lib.filterAttrs ( - datasetName: dataset: dataset.impermanence.enable - ) - config.storage.impermanence.datasets)); - } - (lib.mkIf config.storage.zfs.enable { - storage.zfs.datasets = - lib.mapAttrs ( - datasetName: dataset: - builtins.removeAttrs dataset impermanenceOnlyOptions - ) - config.storage.impermanence.datasets; - }) - ]); - }; -} diff --git a/modules/nixos/storage/storage.nix b/modules/nixos/storage/storage.nix deleted file mode 100644 index 5cc3201..0000000 --- a/modules/nixos/storage/storage.nix +++ /dev/null @@ -1,221 +0,0 @@ -{config, ...}: let - datasetSubmodule = config.flake.commonModules.storage-dataset-submodule; - impermanenceDatasetSubmodule = config.flake.commonModules.storage-impermanence-dataset-submodule; -in { - flake.nixosModules.storage-config = args @ { - lib, - config, - ... - }: let - datasetSub = datasetSubmodule args; - impermanenceDatasetSub = impermanenceDatasetSubmodule args; - - # Get the option names from both submodules to automatically determine which are impermanence-specific - regularDatasetEval = lib.evalModules { - modules = [datasetSub]; - specialArgs = args; - }; - impermanenceDatasetEval = lib.evalModules { - modules = [impermanenceDatasetSub]; - specialArgs = args; - }; - - regularDatasetOptions = builtins.attrNames regularDatasetEval.options; - impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options; - - # Find options that are only in impermanence datasets (not in regular ZFS datasets) - impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions; - in { - options.storage = { - generateBase = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - When enabled, enables automatic generation of base datasets (ephemeral, local, replicate roots). - This allows manual definition of datasets matching an existing system layout for migration purposes. - ''; - }; - datasets = { - ephemeral = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule datasetSub); - default = {}; - }; - local = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSub); - default = {}; - }; - replicate = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSub); - default = {}; - }; - }; - }; - - config = lib.mkMerge [ - (lib.mkIf (config.storage.zfs.enable && config.storage.generateBase) { - # Create ZFS datasets based on storage.datasets configuration - storage.datasets = { - local = { - "nix" = { - impermanence.enable = false; - type = "zfs_fs"; - mount = "/nix"; - snapshot = { - autoSnapshot = false; - }; - atime = "off"; - relatime = "off"; - }; - }; - }; - }) - (lib.mkIf (config.storage.zfs.enable && config.storage.impermanence.enable && config.storage.generateBase) { - storage.datasets = { - ephemeral = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "system/root" = { - type = "zfs_fs"; - mount = "/"; - snapshot = { - blankSnapshot = true; - }; - }; - }; - # TODO: can we auto set the mount points on these to just be `"/persist/local/${name}"` - local = { - "" = { - mount = "/persist/local"; - }; - }; - # TODO: can we auto set the mount points on these to just be `"/persist/replicate/${name}"` - replicate = { - "" = { - mount = "/persist/replicate"; - }; - "system/root" = { - mount = "/persist/replicate/system/root"; - snapshot = { - autoSnapshot = true; - }; - directories = { - "/var/lib/nixos".enable = true; - "/var/lib/systemd/coredump".enable = true; - }; - files = { - "/etc/machine-id".enable = true; - }; - }; - "home" = { - mount = "/persist/replicate/home"; - snapshot = { - autoSnapshot = true; - }; - }; - "system/var/log" = { - type = "zfs_fs"; - directories = { - "/var/log".enable = true; - }; - }; - }; - }; - - storage.zfs.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "ephemeral" - else "ephemeral/${name}"; - value = dataset; - }) - config.storage.datasets.ephemeral) - ]; - - boot.initrd.postResumeCommands = lib.mkAfter '' - zfs rollback -r rpool/ephemeral/system/root@blank - ''; - - storage.impermanence.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/local" - else "persist/local/${name}"; - value = dataset; - }) - config.storage.datasets.local) - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/replicate" - else "persist/replicate/${name}"; - value = dataset; - }) - config.storage.datasets.replicate) - ]; - }) - (lib.mkIf (config.storage.zfs.enable && !config.storage.impermanence.enable && config.storage.generateBase) { - storage.datasets = { - # Base organizational datasets (only needed when impermanence is disabled) - local = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "root" = { - type = "zfs_fs"; - mount = "/"; - compression = "lz4"; - acltype = "posixacl"; - relatime = "on"; - xattr = "sa"; - snapshot = { - autoSnapshot = true; - blankSnapshot = true; - }; - }; - }; - replicate = { - "" = { - type = "zfs_fs"; - mount = null; - }; - "system/var/log" = { - type = "zfs_fs"; - mount = "/var/log"; - }; - }; - }; - - storage.zfs.datasets = lib.mkMerge [ - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/local" - else "persist/local/${name}"; - value = builtins.removeAttrs dataset impermanenceOnlyOptions; - }) - config.storage.datasets.local) - (lib.mapAttrs' (name: dataset: { - name = - if name == "" - then "persist/replicate" - else "persist/replicate/${name}"; - value = builtins.removeAttrs dataset impermanenceOnlyOptions; - }) - config.storage.datasets.replicate) - ]; - }) - ]; - - # TODO: set up datasets for systemd services that want a dataset created - # TODO: home-manager.users..storage.impermanence.enable - # is false then persist the entire directory of the user - # if true persist home-manager.users..storage.impermanence.datasets - # TODO: systemd.services..storage.datasets persists - # TODO: configure other needed storage modes here - }; -} diff --git a/modules/nixos/storage/zfs.nix b/modules/nixos/storage/zfs.nix deleted file mode 100644 index 2817265..0000000 --- a/modules/nixos/storage/zfs.nix +++ /dev/null @@ -1,351 +0,0 @@ -{config, ...}: let - datasetSubmodule = config.flake.commonModules.storage-dataset-submodule; -in { - flake.nixosModules.storage-zfs = args @ { - lib, - pkgs, - config, - ... - }: let - datasetSub = datasetSubmodule args; - - # Hash function for disk names (max 27 chars to fit GPT limitations) - hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); - - # Map "stripe" to "" for disko compatibility (disko uses "" for stripe mode) - diskoPoolMode = - if config.storage.zfs.pool.mode == "stripe" - then "" - else config.storage.zfs.pool.mode; - - # Helper to flatten vdevs into list of devices with names - allVdevDevices = lib.lists.flatten (builtins.map ( - vdev: - builtins.map ( - device: - lib.attrsets.nameValuePair (hashDisk device.device) device - ) - vdev - ) - config.storage.zfs.pool.vdevs); - - # Cache devices with names - allCacheDevices = builtins.map ( - device: - lib.attrsets.nameValuePair (hashDisk device.device) device - ) (config.storage.zfs.pool.cache); - - # All devices (vdevs + cache) - allDevices = allVdevDevices ++ allCacheDevices; - - # Boot devices - filter devices that have boot = true - bootDevices = builtins.filter (device: device.value.boot) allDevices; - - # Helper function to convert dataset options to ZFS properties - datasetToZfsOptions = dataset: let - baseOptions = - (lib.attrsets.optionalAttrs (dataset.acltype != null) {acltype = dataset.acltype;}) - // (lib.attrsets.optionalAttrs (dataset.relatime != null) {relatime = dataset.relatime;}) - // (lib.attrsets.optionalAttrs (dataset.atime != null) {atime = dataset.atime;}) - // (lib.attrsets.optionalAttrs (dataset.xattr != null) {xattr = dataset.xattr;}) - // (lib.attrsets.optionalAttrs (dataset.compression != null) {compression = dataset.compression;}) - // (lib.attrsets.optionalAttrs (dataset.sync != null) {sync = dataset.sync;}) - // (lib.attrsets.optionalAttrs (dataset.recordSize != null) {recordSize = dataset.recordSize;}); - - encryptionOptions = lib.attrsets.optionalAttrs (dataset.encryption.enable) ( - (lib.attrsets.optionalAttrs (dataset.encryption ? type) {encryption = dataset.encryption.type;}) - // (lib.attrsets.optionalAttrs (dataset.encryption ? keyformat) {keyformat = dataset.encryption.keyformat;}) - // (lib.attrsets.optionalAttrs (dataset.encryption ? keylocation) {keylocation = dataset.encryption.keylocation;}) - ); - - mountOptions = lib.attrsets.optionalAttrs (dataset ? mount && dataset.mount ? enable) ( - if builtins.isBool dataset.mount.enable - then { - canmount = - if dataset.mount.enable - then "on" - else "off"; - } - else {canmount = dataset.mount.enable;} - ); - - snapshotOptions = lib.attrsets.optionalAttrs (dataset ? snapshot && dataset.snapshot ? autoSnapshot) { - "com.sun:auto-snapshot" = - if dataset.snapshot.autoSnapshot - then "true" - else "false"; - }; - in - baseOptions // encryptionOptions // mountOptions // snapshotOptions; - - # Helper to generate post create hooks - generatePostCreateHook = name: dataset: - dataset.postCreateHook - + (lib.optionalString dataset.snapshot.blankSnapshot '' - zfs snapshot rpool/${name}@blank - ''); - - # Convert datasets to disko format - convertedDatasets = builtins.listToAttrs ( - (lib.attrsets.mapAttrsToList ( - name: dataset: - lib.attrsets.nameValuePair name { - type = dataset.type; - options = datasetToZfsOptions dataset; - mountpoint = dataset.mount or null; - postCreateHook = generatePostCreateHook name dataset; - } - ) - config.storage.zfs.datasets) - ++ (lib.optional (config.storage.zfs.rootDataset != null) ( - lib.attrsets.nameValuePair "" { - type = config.storage.zfs.rootDataset.type; - options = datasetToZfsOptions config.storage.zfs.rootDataset; - mountpoint = config.storage.zfs.rootDataset.mount or null; - postCreateHook = generatePostCreateHook "" config.storage.zfs.rootDataset; - } - )) - ); - in { - options.storage = { - zfs = { - enable = lib.mkEnableOption "Should zfs be enabled on this system."; - - notifications = { - enable = lib.mkEnableOption "are notifications enabled"; - host = lib.mkOption { - type = lib.types.str; - description = "what is the host that we are going to send the email to"; - }; - port = lib.mkOption { - type = lib.types.port; - description = "what port is the host using to receive mail on"; - }; - to = lib.mkOption { - type = lib.types.str; - description = "what account is the email going to be sent to"; - }; - user = lib.mkOption { - type = lib.types.str; - description = "what user is the email going to be set from"; - }; - tokenFile = lib.mkOption { - type = lib.types.str; - description = "file containing the password to be used by msmtp for notifications"; - }; - }; - - pool = let - deviceType = - lib.types.coercedTo lib.types.str (device: { - device = device; - boot = false; - }) (lib.types.submodule { - options = { - device = lib.mkOption { - type = lib.types.str; - }; - boot = lib.mkEnableOption "should this device be a boot device"; - }; - }); - in { - encryption = { - enable = lib.mkEnableOption "Should encryption be enabled on this pool."; - keyformat = lib.mkOption { - type = lib.types.enum ["raw" "hex" "passphrase"]; - default = "hex"; - description = "Format of the encryption key"; - }; - keylocation = lib.mkOption { - type = lib.types.str; - default = "prompt"; - description = "Location of the encryption key"; - }; - }; - mode = lib.mkOption { - type = lib.types.enum ["stripe" "mirror" "raidz1" "raidz2" "raidz3"]; - default = "raidz2"; - description = "ZFS redundancy mode for the pool"; - }; - bootPartitionSize = lib.mkOption { - type = lib.types.str; - default = "2G"; - description = "Size of the boot partition on boot drives"; - }; - vdevs = lib.mkOption { - type = lib.types.listOf (lib.types.listOf deviceType); - default = []; - description = "List of vdevs, where each vdev is a list of devices"; - }; - cache = lib.mkOption { - type = lib.types.listOf deviceType; - default = []; - }; - }; - - rootDataset = lib.mkOption { - type = lib.types.nullOr (lib.types.submodule datasetSub); - description = "Root ZFS dataset to create"; - default = null; - }; - - datasets = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule datasetSub); - description = "Additional ZFS datasets to create"; - default = {}; - }; - }; - }; - - config = lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # Assertion that we have at least one boot device - assertions = [ - { - assertion = (builtins.length bootDevices) > 0; - message = "ZFS configuration requires at least one boot device. Set boot = true for at least one device in your vdevs or cache."; - } - ]; - - # # Warning about disk/dataset mismatches - these would be runtime checks - # warnings = let - # configuredDisks = builtins.map (device: device.device) (builtins.map (dev: dev.value) allDevices); - # diskWarnings = - # lib.optional (config.storage.zfs.enable) - # "ZFS: Please ensure the following disks are available on your system: ${builtins.concatStringsSep ", " configuredDisks}"; - - # configuredDatasets = builtins.attrNames config.storage.zfs.datasets; - # datasetWarnings = - # lib.optional (config.storage.zfs.enable && (builtins.length configuredDatasets) > 0) - # "ZFS: Configured datasets: ${builtins.concatStringsSep ", " configuredDatasets}. Ensure these match your intended ZFS layout."; - # in - # diskWarnings ++ datasetWarnings; - - services.zfs = { - autoScrub.enable = true; - autoSnapshot.enable = true; - }; - - # # Configure disko for ZFS setup - disko.devices = { - disk = builtins.listToAttrs ( - builtins.map ( - drive: - lib.attrsets.nameValuePair (drive.name) { - type = "disk"; - device = "/dev/disk/by-id/${drive.value.device}"; - content = { - type = "gpt"; - partitions = { - ESP = lib.mkIf drive.value.boot { - size = config.storage.zfs.pool.bootPartitionSize; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = ["umask=0077"]; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "rpool"; - }; - }; - }; - }; - } - ) - allDevices - ); - - zpool = { - rpool = { - type = "zpool"; - mode = { - topology = { - type = "topology"; - vdev = - builtins.map (vdev: { - mode = diskoPoolMode; - members = builtins.map (device: hashDisk device.device) vdev; - }) - config.storage.zfs.pool.vdevs; - cache = builtins.map (device: hashDisk device.device) config.storage.zfs.pool.cache; - }; - }; - - options = { - ashift = "12"; - autotrim = "on"; - }; - - rootFsOptions = - { - canmount = "off"; - mountpoint = "none"; - xattr = "sa"; - acltype = "posixacl"; - relatime = "on"; - compression = "lz4"; - "com.sun:auto-snapshot" = "false"; - } - // (lib.attrsets.optionalAttrs config.storage.zfs.pool.encryption.enable { - encryption = "on"; - keyformat = config.storage.zfs.pool.encryption.keyformat; - keylocation = config.storage.zfs.pool.encryption.keylocation; - }); - - datasets = convertedDatasets; - }; - }; - }; - } - (lib.mkIf config.storage.zfs.notifications.enable { - programs.msmtp = { - enable = true; - setSendmail = true; - defaults = { - aliases = "/etc/aliases"; - port = config.storage.zfs.notifications.port; - tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; - tls = "on"; - auth = "login"; - tls_starttls = "off"; - }; - accounts = { - zfs_notifications = { - auth = true; - tls = true; - host = config.storage.zfs.notifications.host; - passwordeval = "cat ${config.storage.zfs.notifications.tokenFile}"; - user = config.storage.zfs.notifications.user; - from = config.storage.zfs.notifications.user; - }; - }; - }; - - services.zfs = { - zed = { - enableMail = true; - - settings = { - ZED_DEBUG_LOG = "/tmp/zed.debug.log"; - ZED_EMAIL_ADDR = [config.storage.zfs.notifications.to]; - ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; - ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; - - ZED_NOTIFY_INTERVAL_SECS = 3600; - ZED_NOTIFY_VERBOSE = true; - - ZED_USE_ENCLOSURE_LEDS = true; - ZED_SCRUB_AFTER_RESILVER = true; - }; - }; - }; - }) - ]); - }; -} diff --git a/modules/nixos/system.nix b/modules/nixos/system.nix deleted file mode 100644 index 9d863bb..0000000 --- a/modules/nixos/system.nix +++ /dev/null @@ -1,15 +0,0 @@ -{...}: { - flake.nixosModules.nixos-system = {...}: { - nix = { - gc = { - automatic = true; - dates = "weekly"; - options = "--delete-older-than 7d"; - }; - optimise = { - automatic = true; - dates = ["weekly"]; - }; - }; - }; -} diff --git a/modules/nixos/users.nix b/modules/nixos/users.nix deleted file mode 100644 index 1871fba..0000000 --- a/modules/nixos/users.nix +++ /dev/null @@ -1,433 +0,0 @@ -{...}: { - flake.nixosModules.nixos-users = { - lib, - config, - inputs, - ... - }: let - SOPS_AGE_KEY_DIRECTORY = import ../../const/sops_age_key_directory.nix; - - host = config.host; - - principleUsers = host.principleUsers; - terminalUsers = host.terminalUsers; - normalUsers = host.normalUsers; - - uids = { - leyla = 1000; - eve = 1002; - # ester = 1003; - # ivy = 1004; - jellyfin = 2000; - forgejo = 2002; - hass = 2004; - syncthing = 2007; - ollama = 2008; - git = 2009; - immich = 2010; - qbittorrent = 2011; - paperless = 2012; - actual = 2013; - radarr = 2014; - sonarr = 2015; - bazarr = 2016; - lidarr = 2017; - crab-hole = 2018; - }; - - gids = { - leyla = 1000; - eve = 1002; - # ester = 1003 - # ivy = 1004; - users = 100; - jellyfin_media = 2001; - jellyfin = 2000; - forgejo = 2002; - hass = 2004; - syncthing = 2007; - ollama = 2008; - git = 2009; - immich = 2010; - qbittorrent = 2011; - paperless = 2012; - actual = 2013; - radarr = 2014; - sonarr = 2015; - bazarr = 2016; - lidarr = 2017; - crab-hole = 2018; - }; - - users = config.users.users; - leyla = users.leyla.name; - eve = users.eve.name; - in { - config = lib.mkMerge [ - { - # principle users are by definition trusted - nix.settings.trusted-users = builtins.map (user: user.name) principleUsers; - - # we should only be able to ssh into principle users of a computer who are also set up for terminal access - services.openssh.settings.AllowUsers = builtins.map (user: user.name) (lib.lists.intersectLists terminalUsers principleUsers); - - # we need to set up env variables to nix can find keys to decrypt passwords on rebuild - environment = { - sessionVariables = { - SOPS_AGE_KEY_DIRECTORY = SOPS_AGE_KEY_DIRECTORY; - SOPS_AGE_KEY_FILE = "${SOPS_AGE_KEY_DIRECTORY}/key.txt"; - }; - }; - - # set up user passwords - sops = { - defaultSopsFormat = "yaml"; - gnupg.sshKeyPaths = []; - - age = { - keyFile = "/var/lib/sops-nix/key.txt"; - sshKeyPaths = []; - # generateKey = true; - }; - - secrets = { - "passwords/leyla" = { - neededForUsers = true; - sopsFile = "${inputs.secrets}/user-passwords.yaml"; - }; - "passwords/eve" = { - neededForUsers = true; - sopsFile = "${inputs.secrets}/user-passwords.yaml"; - }; - }; - }; - - users = { - mutableUsers = false; - users = { - leyla = { - uid = lib.mkForce uids.leyla; - name = lib.mkForce host.users.leyla.name; - description = "Leyla"; - extraGroups = - (lib.lists.optionals host.users.leyla.isNormalUser ["networkmanager"]) - ++ (lib.lists.optionals host.users.leyla.isPrincipleUser ["wheel" "dialout" "docker"]) - ++ (lib.lists.optionals host.users.leyla.isDesktopUser ["adbusers"]); - hashedPasswordFile = config.sops.secrets."passwords/leyla".path; - isNormalUser = host.users.leyla.isNormalUser; - isSystemUser = !host.users.leyla.isNormalUser; - group = config.users.users.leyla.name; - }; - - eve = { - uid = lib.mkForce uids.eve; - name = lib.mkForce host.users.eve.name; - description = "Eve"; - extraGroups = - lib.optionals host.users.eve.isNormalUser ["networkmanager"] - ++ (lib.lists.optionals host.users.eve.isPrincipleUser ["wheel"]); - hashedPasswordFile = config.sops.secrets."passwords/eve".path; - isNormalUser = host.users.eve.isNormalUser; - isSystemUser = !host.users.eve.isNormalUser; - group = config.users.users.eve.name; - }; - - jellyfin = { - uid = lib.mkForce uids.jellyfin; - isSystemUser = true; - group = config.users.users.jellyfin.name; - }; - - forgejo = { - uid = lib.mkForce uids.forgejo; - isSystemUser = true; - group = config.users.users.forgejo.name; - }; - - hass = { - uid = lib.mkForce uids.hass; - isSystemUser = true; - group = config.users.users.hass.name; - }; - - syncthing = { - uid = lib.mkForce uids.syncthing; - isSystemUser = true; - group = config.users.users.syncthing.name; - }; - - ollama = { - uid = lib.mkForce uids.ollama; - isSystemUser = true; - group = config.users.users.ollama.name; - }; - - git = { - uid = lib.mkForce uids.git; - isSystemUser = true; - group = config.users.users.git.name; - }; - - immich = { - uid = lib.mkForce uids.immich; - isSystemUser = true; - group = config.users.users.immich.name; - }; - - qbittorrent = { - uid = lib.mkForce uids.qbittorrent; - isSystemUser = true; - group = config.users.users.qbittorrent.name; - }; - - paperless = { - uid = lib.mkForce uids.paperless; - isSystemUser = true; - group = config.users.users.paperless.name; - }; - - actual = { - uid = lib.mkForce uids.actual; - isSystemUser = true; - group = config.users.users.actual.name; - }; - - radarr = { - uid = lib.mkForce uids.radarr; - isSystemUser = true; - group = config.users.users.radarr.name; - }; - - sonarr = { - uid = lib.mkForce uids.sonarr; - isSystemUser = true; - group = config.users.users.sonarr.name; - }; - - bazarr = { - uid = lib.mkForce uids.bazarr; - isSystemUser = true; - group = config.users.users.bazarr.name; - }; - - lidarr = { - uid = lib.mkForce uids.lidarr; - isSystemUser = true; - group = config.users.users.lidarr.name; - }; - - crab-hole = { - uid = lib.mkForce uids.crab-hole; - isSystemUser = true; - group = config.users.users.crab-hole.name; - }; - }; - - groups = { - leyla = { - gid = lib.mkForce gids.leyla; - members = [ - leyla - ]; - }; - - eve = { - gid = lib.mkForce gids.eve; - members = [ - eve - ]; - }; - - users = { - gid = lib.mkForce gids.users; - members = [ - leyla - eve - ]; - }; - - jellyfin_media = { - gid = lib.mkForce gids.jellyfin_media; - members = [ - users.jellyfin.name - users.radarr.name - users.sonarr.name - users.bazarr.name - users.lidarr.name - leyla - eve - ]; - }; - - jellyfin = { - gid = lib.mkForce gids.jellyfin; - members = [ - users.jellyfin.name - # leyla - ]; - }; - - forgejo = { - gid = lib.mkForce gids.forgejo; - members = [ - users.forgejo.name - # leyla - ]; - }; - - hass = { - gid = lib.mkForce gids.hass; - members = [ - users.hass.name - # leyla - ]; - }; - - syncthing = { - gid = lib.mkForce gids.syncthing; - members = [ - users.syncthing.name - leyla - eve - ]; - }; - - ollama = { - gid = lib.mkForce gids.ollama; - members = [ - users.ollama.name - ]; - }; - - git = { - gid = lib.mkForce gids.git; - members = [ - users.git.name - ]; - }; - - immich = { - gid = lib.mkForce gids.immich; - members = [ - users.immich.name - # leyla - ]; - }; - - qbittorrent = { - gid = lib.mkForce gids.qbittorrent; - members = [ - users.qbittorrent.name - leyla - ]; - }; - - paperless = { - gid = lib.mkForce gids.paperless; - members = [ - users.paperless.name - ]; - }; - - actual = { - gid = lib.mkForce gids.actual; - members = [ - users.actual.name - ]; - }; - - radarr = { - gid = lib.mkForce gids.radarr; - members = [ - users.radarr.name - ]; - }; - - sonarr = { - gid = lib.mkForce gids.sonarr; - members = [ - users.sonarr.name - ]; - }; - - bazarr = { - gid = lib.mkForce gids.bazarr; - members = [ - users.bazarr.name - ]; - }; - - lidarr = { - gid = lib.mkForce gids.lidarr; - members = [ - users.lidarr.name - ]; - }; - - crab-hole = { - gid = lib.mkForce gids.crab-hole; - members = [ - users.crab-hole.name - ]; - }; - }; - }; - } - (lib.mkIf config.storage.zfs.enable (lib.mkMerge [ - { - # sops age key needs to be available to pre persist for user generation - storage.datasets.local."system/sops" = { - type = "zfs_fs"; - mount = SOPS_AGE_KEY_DIRECTORY; - atime = "off"; - relatime = "off"; - impermanence.enable = false; - }; - } - (lib.mkIf (!config.storage.impermanence.enable) { - storage.datasets.replicate = lib.mkMerge ( - builtins.map (user: { - "home/${user.name}" = { - type = "zfs_fs"; - mount = "/home/${user.name}"; - snapshot.autoSnapshot = true; - }; - }) - normalUsers - ); - }) - (lib.mkIf config.storage.impermanence.enable { - storage.datasets.ephemeral = lib.mkMerge ( - builtins.map (user: { - "home/${user.name}" = { - type = "zfs_fs"; - mount = "/home/${user.name}"; - snapshot.blankSnapshot = true; - }; - }) - normalUsers - ); - - # Post resume commands to rollback user home datasets to blank snapshots - # Only add these when generateBase is true -- when false, the legacy - # storage config is responsible for providing rollback commands with - # the correct (old) dataset paths. - boot.initrd.postResumeCommands = lib.mkIf config.storage.generateBase (lib.mkAfter ( - lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/ephemeral/home/${user.name}@blank") - normalUsers) - )); - - # TODO: I don't think we need this anymore but I have not tested it - # Create persist home directories with proper permissions - # systemd = { - # tmpfiles.rules = - # builtins.map ( - # user: "d /persist/replicate/home/${user.name} 700 ${user.name} ${user.name} -" - # ) - # normalUsers; - # }; - }) - ])) - ]; - }; -} diff --git a/modules/parts.nix b/modules/parts.nix deleted file mode 100644 index e928985..0000000 --- a/modules/parts.nix +++ /dev/null @@ -1,120 +0,0 @@ -{ - inputs, - config, - ... -}: let - home-manager = inputs.home-manager; - sops-nix = inputs.sops-nix; - nix-syncthing = inputs.nix-syncthing; - disko = inputs.disko; - impermanence = inputs.impermanence; - - common-modules = [ - config.flake.commonModules.pkgs - config.flake.commonModules.overlays - ]; - - home-manager-modules = - common-modules - ++ [ - sops-nix.homeManagerModules.sops - config.flake.homeModules.home-manager-modules-all - ]; - - home-manager-base = _: { - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "backup"; - home-manager.extraSpecialArgs = { - inherit inputs; - }; - home-manager.sharedModules = home-manager-modules; - }; - - system-modules = - common-modules - ++ [ - home-manager-base - config.flake.commonModules.system-modules-all - ]; -in { - systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; - - flake.homeModules.homeModules = {imports = home-manager-modules;}; - - flake.nixosModules.nixosModules = { - imports = - system-modules - ++ [ - sops-nix.nixosModules.sops - nix-syncthing.nixosModules.syncthing - impermanence.nixosModules.impermanence - home-manager.nixosModules.home-manager - disko.nixosModules.disko - # lix-module.nixosModules.default - config.flake.nixosModules.nixos-modules-all - ({ - lib, - config, - ... - }: { - home-manager.users = { - leyla = lib.mkIf config.host.users.leyla.isNormalUser inputs.self.homeModules.leylaConfiguration; - eve = lib.mkIf config.host.users.eve.isNormalUser inputs.self.homeModules.eveConfiguration; - }; - }) - ]; - }; - - flake.darwinModules.darwinModules = { - imports = - system-modules - ++ [ - sops-nix.darwinModules.sops - home-manager.darwinModules.home-manager - config.flake.darwinModules.darwin-modules-all - ({ - lib, - config, - ... - }: { - home-manager.users = { - leyla = lib.mkIf config.host.users.leyla.isNormalUser inputs.self.homeModules.leylaConfiguration; - eve = lib.mkIf config.host.users.eve.isNormalUser inputs.self.homeModules.eveConfiguration; - }; - }) - ]; - }; - - perSystem = { - pkgs, - system, - ... - }: { - formatter = pkgs.alejandra; - - devShells.default = pkgs.mkShell { - packages = with pkgs; [ - # for version controlling this repo - git - # for formatting code in this repo - alejandra - # for editing secrets in the secrets repo - sops - # for viewing configuration options defined in this repo - nix-inspect - # for installing flakes from this repo onto other systems - nixos-anywhere - # for updating disko configurations - pkgs.disko - # for viewing dconf entries - dconf-editor - ]; - - SOPS_AGE_KEY_DIRECTORY = import ../const/sops_age_key_directory.nix; - - shellHook = '' - git config core.hooksPath .hooks - ''; - }; - }; -} diff --git a/modules/syncthing/default.nix b/modules/syncthing/default.nix deleted file mode 100644 index 02bd38b..0000000 --- a/modules/syncthing/default.nix +++ /dev/null @@ -1,125 +0,0 @@ -{inputs, ...}: { - flake.syncthingConfiguration = inputs.nix-syncthing.lib.syncthingConfiguration { - modules = [ - ({config, ...}: { - folders = { - leyla_documents = { - id = "hvrj0-9bm1p"; - }; - leyla_calendar = { - id = "8oatl-1rv6w"; - }; - leyla_supernote_notes = { - id = "dwbuv-zffnf"; - }; - eve_records = { - id = "by6at-d4h9n"; - }; - share = { - id = "73ot0-cxmkx"; - }; - }; - devices = { - defiant = { - id = "3R6E6Y4-2F7MF2I-IGB4WE6-A3SQSMV-LIBYSAM-2OXHHU2-KJ6CGIV-QNMCPAR"; - folders = { - leyla_documents = { - folder = config.folders.leyla_documents; - path = "/mnt/sync/leyla/documents"; - }; - leyla_calendar = { - folder = config.folders.leyla_calendar; - path = "/mnt/sync/leyla/calendar"; - }; - leyla_supernote_notes = { - folder = config.folders.leyla_supernote_notes; - path = "/mnt/sync/leyla/notes"; - }; - eve_records = { - folder = config.folders.eve_records; - path = "/mnt/sync/eve/records"; - }; - share = { - folder = config.folders.share; - path = "/mnt/sync/default/share"; - }; - }; - }; - twilight = { - id = "UDIYL7V-OAZ2BI3-EJRAWFB-GZYVDWR-JNUYW3F-FFQ35MU-XBTGWEF-QD6K6QN"; - folders = { - leyla_documents = { - folder = config.folders.leyla_documents; - path = "/mnt/sync/leyla/documents"; - }; - share = { - folder = config.folders.share; - path = "/mnt/sync/default/share"; - }; - }; - }; - horizon = { - id = "OGPAEU6-5UR56VL-SP7YC4Y-IMVCRTO-XFD4CYN-Z6T5TZO-PFZNAT6-4MKWPQS"; - folders = { - leyla_documents = { - folder = config.folders.leyla_documents; - path = "/mnt/sync/leyla/documents"; - }; - share = { - folder = config.folders.share; - path = "/mnt/sync/default/share"; - }; - }; - }; - coven = { - id = "QGU7NN6-OMXTWVA-YCZ73S5-2O7ECTS-MUCTN4M-YH6WLEL-U4U577I-7PBNCA5"; - folders = { - leyla_documents = { - folder = config.folders.leyla_documents; - }; - share = { - folder = config.folders.share; - }; - }; - }; - ceder = { - id = "MGXUJBS-7AENXHB-7YQRNWG-QILKEJD-5462U2E-WAQW4R4-I2TVK5H-SMK6LAA"; - folders = { - share = { - folder = config.folders.share; - }; - leyla_documents = { - folder = config.folders.leyla_documents; - }; - leyla_calendar = { - folder = config.folders.leyla_calendar; - }; - leyla_notes = { - folder = config.folders.leyla_supernote_notes; - }; - }; - }; - emergent = { - id = "6MIDMKJ-7IFHXVX-FIR3YTB-KVE75LN-PA6IOTN-I257LWR-MMC4K6C-5H4SHQN"; - folders = { - eve_records = { - folder = config.folders.eve_records; - }; - share = { - folder = config.folders.share; - }; - }; - }; - shale = { - id = "AOAXEVD-QJ2IVRA-6G44Q7Q-TGUPXU2-FWWKOBH-DPKWC5N-LBAEHWJ-7EQF4AM"; - folders = { - share = { - folder = config.folders.share; - }; - }; - }; - }; - }) - ]; - }; -} diff --git a/modules/system-modules/default.nix b/modules/system-modules/default.nix new file mode 100644 index 0000000..637b6b5 --- /dev/null +++ b/modules/system-modules/default.nix @@ -0,0 +1,9 @@ +# this folder container modules that are for nixos and darwin +{...}: { + imports = [ + ./home-manager + ./system.nix + ./nix-development.nix + ./users.nix + ]; +} diff --git a/modules/system-modules/home-manager/default.nix b/modules/system-modules/home-manager/default.nix new file mode 100644 index 0000000..3745b8f --- /dev/null +++ b/modules/system-modules/home-manager/default.nix @@ -0,0 +1,2 @@ +# modules in this folder are to adapt home-manager modules configs to system-module configs +{...}: {} diff --git a/modules/system-modules/nix-development.nix b/modules/system-modules/nix-development.nix new file mode 100644 index 0000000..6eeddc4 --- /dev/null +++ b/modules/system-modules/nix-development.nix @@ -0,0 +1,26 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: { + options.host.nix-development.enable = lib.mkEnableOption "should desktop configuration be enabled"; + + config = lib.mkMerge [ + { + host.nix-development.enable = lib.mkDefault true; + } + (lib.mkIf config.host.nix-development.enable { + nix = { + nixPath = ["nixpkgs=${inputs.nixpkgs}"]; + }; + environment.systemPackages = with pkgs; [ + # nix language server + nil + # nix formatter + alejandra + ]; + }) + ]; +} diff --git a/modules/system-modules/system.nix b/modules/system-modules/system.nix new file mode 100644 index 0000000..f464835 --- /dev/null +++ b/modules/system-modules/system.nix @@ -0,0 +1,7 @@ +{...}: { + nix = { + settings = { + experimental-features = ["nix-command" "flakes"]; + }; + }; +} diff --git a/modules/system-modules/users.nix b/modules/system-modules/users.nix new file mode 100644 index 0000000..cd9c900 --- /dev/null +++ b/modules/system-modules/users.nix @@ -0,0 +1,113 @@ +{ + lib, + config, + ... +}: let + host = config.host; + + hostUsers = host.hostUsers; + principleUsers = host.principleUsers; +in { + options.host = { + users = lib.mkOption { + default = {}; + type = lib.types.attrsOf (lib.types.submodule ({ + config, + name, + ... + }: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + description = '' + What should this users name on the system be + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.name"; + }; + isPrincipleUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should be configured as root and have ssh access + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isPrincipleUser"; + }; + isDesktopUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should install their desktop applications + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isDesktopUser"; + }; + isTerminalUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should install their terminal applications + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isTerminalUser"; + }; + isNormalUser = lib.mkOption { + type = lib.types.bool; + default = config.isDesktopUser || config.isTerminalUser; + description = '' + User should install their applications and can log in + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isNormalUser"; + }; + }; + })); + }; + hostUsers = lib.mkOption { + default = lib.attrsets.mapAttrsToList (_: user: user) host.users; + }; + principleUsers = lib.mkOption { + default = lib.lists.filter (user: user.isPrincipleUser) hostUsers; + }; + normalUsers = lib.mkOption { + default = lib.lists.filter (user: user.isNormalUser) hostUsers; + }; + desktopUsers = lib.mkOption { + default = lib.lists.filter (user: user.isDesktopUser) hostUsers; + }; + terminalUsers = lib.mkOption { + default = lib.lists.filter (user: user.isTerminalUser) hostUsers; + }; + }; + + config = { + host.users = { + leyla = { + isPrincipleUser = lib.mkDefault false; + isDesktopUser = lib.mkDefault false; + isTerminalUser = lib.mkDefault false; + }; + eve = { + isPrincipleUser = lib.mkDefault false; + isDesktopUser = lib.mkDefault false; + isTerminalUser = lib.mkDefault false; + }; + }; + + assertions = + ( + builtins.map (user: { + assertion = !(user.isPrincipleUser && !user.isNormalUser); + message = '' + Non normal user ${user.name} can not be a principle user. + ''; + }) + hostUsers + ) + ++ [ + { + assertion = (builtins.length principleUsers) > 0; + message = '' + At least one user must be a principle user. + ''; + } + ]; + }; +} diff --git a/modules/system/default.nix b/modules/system/default.nix deleted file mode 100644 index 4d74f98..0000000 --- a/modules/system/default.nix +++ /dev/null @@ -1,11 +0,0 @@ -{config, ...}: let - mod = config.flake.commonModules; -in { - flake.commonModules.system-modules-all = { - imports = [ - mod.system-config - mod.nix-development - mod.system-users - ]; - }; -} diff --git a/modules/system/nix-development.nix b/modules/system/nix-development.nix deleted file mode 100644 index c2107b5..0000000 --- a/modules/system/nix-development.nix +++ /dev/null @@ -1,28 +0,0 @@ -{...}: { - flake.commonModules.nix-development = { - lib, - pkgs, - config, - inputs, - ... - }: { - options.host.nix-development.enable = lib.mkEnableOption "should desktop configuration be enabled"; - - config = lib.mkMerge [ - { - host.nix-development.enable = lib.mkDefault true; - } - (lib.mkIf config.host.nix-development.enable { - nix = { - nixPath = ["nixpkgs=${inputs.nixpkgs}"]; - }; - environment.systemPackages = with pkgs; [ - # nix language server - nil - # nix formatter - alejandra - ]; - }) - ]; - }; -} diff --git a/modules/system/system.nix b/modules/system/system.nix deleted file mode 100644 index f6d5604..0000000 --- a/modules/system/system.nix +++ /dev/null @@ -1,9 +0,0 @@ -{...}: { - flake.commonModules.system-config = { - nix = { - settings = { - experimental-features = ["nix-command" "flakes"]; - }; - }; - }; -} diff --git a/modules/system/users.nix b/modules/system/users.nix deleted file mode 100644 index fe2e1d1..0000000 --- a/modules/system/users.nix +++ /dev/null @@ -1,115 +0,0 @@ -{...}: { - flake.commonModules.system-users = { - lib, - config, - ... - }: let - host = config.host; - - hostUsers = host.hostUsers; - principleUsers = host.principleUsers; - in { - options.host = { - users = lib.mkOption { - default = {}; - type = lib.types.attrsOf (lib.types.submodule ({ - config, - name, - ... - }: { - options = { - name = lib.mkOption { - type = lib.types.str; - default = name; - description = '' - What should this users name on the system be - ''; - defaultText = lib.literalExpression "config.host.users.\${name}.name"; - }; - isPrincipleUser = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - User should be configured as root and have ssh access - ''; - defaultText = lib.literalExpression "config.host.users.\${name}.isPrincipleUser"; - }; - isDesktopUser = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - User should install their desktop applications - ''; - defaultText = lib.literalExpression "config.host.users.\${name}.isDesktopUser"; - }; - isTerminalUser = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - User should install their terminal applications - ''; - defaultText = lib.literalExpression "config.host.users.\${name}.isTerminalUser"; - }; - isNormalUser = lib.mkOption { - type = lib.types.bool; - default = config.isDesktopUser || config.isTerminalUser; - description = '' - User should install their applications and can log in - ''; - defaultText = lib.literalExpression "config.host.users.\${name}.isNormalUser"; - }; - }; - })); - }; - hostUsers = lib.mkOption { - default = lib.attrsets.mapAttrsToList (_: user: user) host.users; - }; - principleUsers = lib.mkOption { - default = lib.lists.filter (user: user.isPrincipleUser) hostUsers; - }; - normalUsers = lib.mkOption { - default = lib.lists.filter (user: user.isNormalUser) hostUsers; - }; - desktopUsers = lib.mkOption { - default = lib.lists.filter (user: user.isDesktopUser) hostUsers; - }; - terminalUsers = lib.mkOption { - default = lib.lists.filter (user: user.isTerminalUser) hostUsers; - }; - }; - - config = { - host.users = { - leyla = { - isPrincipleUser = lib.mkDefault false; - isDesktopUser = lib.mkDefault false; - isTerminalUser = lib.mkDefault false; - }; - eve = { - isPrincipleUser = lib.mkDefault false; - isDesktopUser = lib.mkDefault false; - isTerminalUser = lib.mkDefault false; - }; - }; - - assertions = - ( - builtins.map (user: { - assertion = !(user.isPrincipleUser && !user.isNormalUser); - message = '' - Non normal user ${user.name} can not be a principle user. - ''; - }) - hostUsers - ) - ++ [ - { - assertion = (builtins.length principleUsers) > 0; - message = '' - At least one user must be a principle user. - ''; - } - ]; - }; - }; -} diff --git a/util/default.nix b/util/default.nix new file mode 100644 index 0000000..d72d00d --- /dev/null +++ b/util/default.nix @@ -0,0 +1,117 @@ +{inputs}: let + util = (import ./default.nix) {inherit inputs;}; + outputs = inputs.self.outputs; + + lib = inputs.lib; + nixpkgs = inputs.nixpkgs; + home-manager = inputs.home-manager; + nix-darwin = inputs.nix-darwin; + sops-nix = inputs.sops-nix; + nix-syncthing = inputs.nix-syncthing; + disko = inputs.disko; + impermanence = inputs.impermanence; + # lix-module = inputs.lix-module; + + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + forEachSystem = nixpkgs.lib.genAttrs systems; + pkgsFor = system: nixpkgs.legacyPackages.${system}; + + common-modules = [ + ../modules/common-modules + ]; + + home-manager-modules = + common-modules + ++ [ + sops-nix.homeManagerModules.sops + ../modules/home-manager-modules + ]; + + home-manager-config = nixpkgs: { + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "backup"; + home-manager.extraSpecialArgs = { + inherit inputs outputs util; + }; + home-manager.users = import ../configurations/home-manager (nixpkgs + // { + osConfig = nixpkgs.config; + }); + home-manager.sharedModules = home-manager-modules; + }; + + system-modules = + common-modules + ++ [ + home-manager-config + ../modules/system-modules + ]; + + syncthingConfiguration = nix-syncthing.lib.syncthingConfiguration { + modules = [ + (import ../configurations/syncthing) + ]; + }; +in { + forEachPkgs = lambda: forEachSystem (system: lambda system (pkgsFor system)); + + mkUnless = condition: yes: (lib.mkIf (!condition) yes); + mkIfElse = condition: yes: no: + lib.mkMerge [ + (lib.mkIf condition yes) + (lib.mkUnless condition no) + ]; + + mkNixosSystem = host: + nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs outputs util syncthingConfiguration;}; + modules = + system-modules + ++ [ + sops-nix.nixosModules.sops + nix-syncthing.nixosModules.syncthing + impermanence.nixosModules.impermanence + home-manager.nixosModules.home-manager + disko.nixosModules.disko + # lix-module.nixosModules.default + ../modules/nixos-modules + ../configurations/nixos/${host} + ]; + }; + + mkDarwinSystem = host: + nix-darwin.lib.darwinSystem { + specialArgs = {inherit inputs outputs util;}; + modules = + system-modules + ++ [ + sops-nix.darwinModules.sops + home-manager.darwinModules.home-manager + ../modules/darwin-modules + ../configurations/darwin/${host} + ]; + }; + + mkHome = { + user, + host, + system, + osConfig, + }: + home-manager.lib.homeManagerConfiguration { + pkgs = pkgsFor system; + extraSpecialArgs = { + inherit inputs util outputs osConfig; + }; + modules = + home-manager-modules + ++ [ + ../configurations/home-manager/${user} + ]; + }; +}