diff --git a/.hooks/post-commit b/.hooks/post-commit index 56c439da..03a160da 100755 --- a/.hooks/post-commit +++ b/.hooks/post-commit @@ -3,4 +3,12 @@ echo "restoring stashed changes" -git stash pop -q +# Find the most recent pre-commit stash and restore it +recent_stash=$(git stash list | grep "pre-commit-stash-" | head -n 1 | cut -d: -f1) + +if [ -n "$recent_stash" ]; then + echo "Found recent pre-commit stash: $recent_stash" + git stash pop -q "$recent_stash" +else + echo "No pre-commit stash found to restore" +fi diff --git a/.hooks/post-merge b/.hooks/post-merge new file mode 100755 index 00000000..06fabc33 --- /dev/null +++ b/.hooks/post-merge @@ -0,0 +1,32 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash ../shell.nix + +# Get current branch name +current_branch=$(git branch --show-current) + +# Only perform actions if we're on main branch and a merge just completed +if [ "$current_branch" = "main" ]; then + echo "Post-merge on main branch - running nix flake check" + + # Run nix flake check after merge into main + nix flake check + + if [ ! $? -eq 0 ]; then + echo "Warning: nix flake check failed after merge into main" + echo "Please fix the issues as soon as possible" + else + echo "nix flake check passed after merge" + fi + + # Check if there are any pre-commit stashes to restore + recent_stash=$(git stash list | grep "pre-commit-stash-" | head -n 1 | cut -d: -f1) + + if [ -n "$recent_stash" ]; then + echo "Post-merge: restoring pre-commit stash on main branch" + git stash pop -q "$recent_stash" + else + echo "Post-merge: no pre-commit stash to restore on main branch" + fi +else + echo "Post-merge: no action needed on branch '$current_branch'" +fi diff --git a/.hooks/pre-commit b/.hooks/pre-commit index f98c64f8..74cbc64a 100755 --- a/.hooks/pre-commit +++ b/.hooks/pre-commit @@ -1,14 +1,24 @@ #!/usr/bin/env nix-shell #! nix-shell -i bash ../shell.nix -echo "stashing all uncommitted changes" -git stash -q --keep-index +# Get current branch name +current_branch=$(git branch --show-current) -echo "checking flakes all compile" -nix flake check +echo "stashing all uncommitted changes with named stash (excluding hooks)" +git stash push -q --keep-index -m "pre-commit-stash-$(date +%s)" -- ':!.hooks/' -if [ ! $? -eq 0 ]; then - exit 1 +# Only run nix flake check if we're on main branch +if [ "$current_branch" = "main" ]; then + echo "On main branch - checking flakes all compile" + nix flake check + + if [ ! $? -eq 0 ]; then + echo "Error: nix flake check failed on main branch" + exit 1 + fi + echo "nix flake check passed" +else + echo "Not on main branch - skipping nix flake check" fi echo "running linter" @@ -19,4 +29,4 @@ RESULT=$? echo "adding lint changes to commit" git add -u -exit $RESULT \ No newline at end of file +exit $RESULT diff --git a/.hooks/pre-merge-commit b/.hooks/pre-merge-commit new file mode 100755 index 00000000..9b7b41d4 --- /dev/null +++ b/.hooks/pre-merge-commit @@ -0,0 +1,37 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash ../shell.nix + +# Get the target branch (the branch being merged into) +target_branch="" + +# Check if we're in the middle of a merge +if [ -f .git/MERGE_HEAD ]; then + # We're in a merge, check if the current branch is main + current_branch=$(git branch --show-current) + if [ "$current_branch" = "main" ]; then + target_branch="main" + fi +fi + +# If we're merging into main, run nix flake check +if [ "$target_branch" = "main" ]; then + echo "Merging into main branch - running nix flake check..." + + echo "stashing all uncommitted changes with named stash (excluding hooks)" + git stash push -q --keep-index -m "pre-merge-stash-$(date +%s)" -- ':!.hooks/' + + echo "checking flakes all compile" + nix flake check + + if [ ! $? -eq 0 ]; then + echo "Error: nix flake check failed. Merge aborted." + echo "Please fix the issues and try merging again." + exit 1 + fi + + echo "nix flake check passed. Merge can proceed." +else + echo "Not merging into main branch, skipping nix flake check." +fi + +exit 0 diff --git a/README.md b/README.md index acaa6e76..dc12d352 100644 --- a/README.md +++ b/README.md @@ -43,39 +43,66 @@ nix multi user, multi system, configuration with `sops` secret management, `home - Look into this for auto rotating sops keys `https://technotim.live/posts/rotate-sops-encryption-keys/` - Look into this for npins https://jade.fyi/blog/pinning-nixos-with-npins/ - https://nixos-and-flakes.thiscute.world/ +- proton mail now has an smtp server we could use that for our zfs and SMART test emails # Tasks: +## Chores: +- [ ] test out crab hole service + ## Tech Debt -- monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/) -- nfs export should be backed by the same values for server and client -## New Features -- crab-hole -- figure out why syncthing and jellyfins permissions don't propagate downwards -- figure out steam vr things? -- auto turn off on power loss - nut -- zfs email after scrubbing # TODO: test this -- SMART test with email results -- samba mounts -- offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs) -- Create Tor guard/relay server -- migrate away from flakes and move to npins -- whisper -- zfs encryption FIDO2 2fa (look into shavee) -- Secure Boot - https://github.com/nix-community/lanzaboote -- rotate sops encryption keys periodically (and somehow sync between devices?) -- wake on LAN for updates -- remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html -- ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix -- panoramax instance -- mastodon instance -- rework the reverse_proxy.nix file so that it is a normally named service. Then also change it so that we can hook into it with both a base domain and a subdomain to make migrating to vpn accessible services easier -- move searx, home-assistant, actual, jellyfin, paperless, and immich to only be accessible via vpn -- make radarr, sonarr, and bazarr accessible over vpn -- create some sort of service that allows uploading files to jellyfin - - auto sort files into where they should go with some combination of filebot cli and picard cli -- graphana accessible though tailscale -- fix panoramax package -- actual instance -- intergrade radarr, sonarr, and bazarr -- claude code MCP servers should bundle node with them so they work in all environments +- [ ] monitor configuration in `~/.config/monitors.xml` should be sym linked to `/run/gdm/.config/monitors.xml` (https://www.reddit.com/r/NixOS/comments/u09cz9/home_manager_create_my_own_symlinks_automatically/) +- [ ] migrate away from flakes and move to npins + +## Broken things +- [ ] figure out steam vr things? +- [ ] whisper was having issues + +## Data Integrity +- [ ] zfs email after scrubbing # TODO: test this +- [ ] SMART test with email results +- [ ] zfs encryption FIDO2 2fa (look into shavee) +- [ ] rotate sops encryption keys periodically (and somehow sync between devices?) +- [ ] Secure Boot - https://github.com/nix-community/lanzaboote +- [ ] auto turn off on power loss - nut +- [ ] secondary server with data sync. Maybe a Pi with a usb hdd enclosure and use rtcwake to only turn on once a week to sync data over tailscale with connection initiated from pi's side. We could probably put this at LZ. Hoping for it to draw only like $1 of power a month. Initial sync should probably be done here before we move it over because that will take a while. Data should be encrypted so that devices doesn't have access to it. Project will prob cost like $1800 + +## Data Access +- [ ] nfs export should be backed by the same values for server and client +- [ ] samba mounts +- [ ] offline access for nfs mounts (overlay with rsync might be a good option here? https://www.spinics.net/lists/linux-unionfs/msg07105.html note about nfs4 and overlay fs) +- [ ] figure out why syncthing and jellyfins permissions don't propagate downwards +- [ ] make radarr, sonarr, and bazarr accessible over vpn +- [ ] move searx, home-assistant, actual, vikunja, jellyfin, paperless, and immich to only be accessible via vpn + +## Services +- [ ] vikunja service for project management +- [ ] Penpot services (need to make this custom) +- [ ] minecraft server with old world file +- [ ] Create Tor guard/relay server +- [ ] mastodon instance +- [ ] screeps server +- [ ] storj server + +## DevOps +- [ ] wake on LAN for updates +- [ ] remote distributed builds - https://nix.dev/tutorials/nixos/distributed-builds-setup.html +- [ ] ISO target that contains authorized keys for nixos-anywhere https://github.com/diegofariasm/yggdrasil/blob/4acc43ebc7bcbf2e41376d14268e382007e94d78/hosts/bootstrap/default.nix +- [ ] fix panoramax package +- [ ] claude code MCP servers should bundle node with them so they work in all environments + +## Observability +- [ ] graphana for dashboards +- [ ] prometheus and loki for metric and log collection + - [ ] zfs storage usage + - [ ] zfs drive health status + - [ ] service version lag + - [ ] network/cpu/ram utilization + - [ ] http latency + - [ ] postgres db load + - [ ] nginx queries +- [ ] ntfy.sh for push notifications +- [ ] kuma for uptime visualization + +## Packages +- [ ] Custom private fork of MultiMC \ No newline at end of file diff --git a/configurations/home-manager/eve/gnomeconf.nix b/configurations/home-manager/eve/gnomeconf.nix index fbad391d..7cd3863d 100644 --- a/configurations/home-manager/eve/gnomeconf.nix +++ b/configurations/home-manager/eve/gnomeconf.nix @@ -1,12 +1,39 @@ -{pkgs, ...}: { +{ + 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; - settings = { - "org/gnome/shell".enabled-extensions = [ - pkgs.gnomeExtensions.dash-to-panel.extensionUuid - ]; - }; }; }; } diff --git a/configurations/home-manager/eve/packages.nix b/configurations/home-manager/eve/packages.nix index f738fe29..fb8d8a47 100644 --- a/configurations/home-manager/eve/packages.nix +++ b/configurations/home-manager/eve/packages.nix @@ -18,6 +18,7 @@ in { home.packages = lib.lists.optionals userConfig.isDesktopUser ( with pkgs; [ gnomeExtensions.dash-to-panel + claude-code ] ); @@ -31,9 +32,11 @@ in { (lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) { git = { enable = true; - userName = "Eve"; - userEmail = "evesnrobins@gmail.com"; - extraConfig.init.defaultBranch = "main"; + settings = { + user.name = "Eve"; + user.email = "evesnrobins@gmail.com"; + init.defaultBranch = "main"; + }; }; openssh = { @@ -61,6 +64,22 @@ in { 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; + + 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 index 2276e7a3..1ea29cc3 100644 --- a/configurations/home-manager/git/default.nix +++ b/configurations/home-manager/git/default.nix @@ -1,4 +1,6 @@ {osConfig, ...}: { + impermanence.fallbackPersistence.enable = false; + home = { username = osConfig.users.users.git.name; homeDirectory = osConfig.users.users.git.home; diff --git a/configurations/home-manager/leyla/dconf.nix b/configurations/home-manager/leyla/dconf.nix index ef75db68..9aa61f7c 100644 --- a/configurations/home-manager/leyla/dconf.nix +++ b/configurations/home-manager/leyla/dconf.nix @@ -1,31 +1,43 @@ -{pkgs, ...}: { +{...}: { config = { gnome = { extraWindowControls = true; colorScheme = "prefer-dark"; clockFormat = "24h"; - extensions = [ - pkgs.gnomeExtensions.dash-to-dock - ]; + 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/extensions/dash-to-dock" = { - "dock-position" = "LEFT"; - "intellihide-mode" = "ALL_WINDOWS"; - "show-trash" = false; - "require-pressure-to-show" = false; - "show-mounts" = false; - }; - "org/gnome/shell" = { favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"]; # app-picker-layout = diff --git a/configurations/home-manager/leyla/default.nix b/configurations/home-manager/leyla/default.nix index 6d759c5c..8a377549 100644 --- a/configurations/home-manager/leyla/default.nix +++ b/configurations/home-manager/leyla/default.nix @@ -12,6 +12,8 @@ ]; config = { + impermanence.enable = osConfig.host.impermanence.enable; + # Home Manager needs a bit of information about you and the paths it should # manage. home = { @@ -87,7 +89,6 @@ # TODO: move this into a fonts module home.packages = with pkgs; [ aileron - nerd-fonts.open-dyslexic ]; fonts.fontconfig.enable = true; }; diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix index 041bff8d..ce81c818 100644 --- a/configurations/home-manager/leyla/impermanence.nix +++ b/configurations/home-manager/leyla/impermanence.nix @@ -1,10 +1,9 @@ { lib, config, - osConfig, ... }: { - config = lib.mkIf osConfig.host.impermanence.enable { + config = lib.mkIf (config.impermanence.enable) { home.persistence."/persist/home/leyla" = { directories = [ "desktop" diff --git a/configurations/home-manager/leyla/packages/default.nix b/configurations/home-manager/leyla/packages/default.nix index 5bccad3e..50cc175c 100644 --- a/configurations/home-manager/leyla/packages/default.nix +++ b/configurations/home-manager/leyla/packages/default.nix @@ -9,7 +9,7 @@ in { imports = [ ./vscode - ./firefox.nix + ./firefox ./direnv.nix ./openssh.nix ./git.nix diff --git a/configurations/home-manager/leyla/packages/firefox.nix b/configurations/home-manager/leyla/packages/firefox.nix deleted file mode 100644 index d166eb47..00000000 --- a/configurations/home-manager/leyla/packages/firefox.nix +++ /dev/null @@ -1,344 +0,0 @@ -{ - lib, - pkgs, - inputs, - ... -}: { - config = { - 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.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 - - deutsch-de-language-pack - dictionary-german - - tab-session-manager - - # ( - # buildFirefoxXpiAddon rec { - # pname = "italiano-it-language-pack"; - # version = "132.0.20241110.231641"; - # addonId = "langpack-it@firefox.mozilla.org"; - # url = "https://addons.mozilla.org/firefox/downloads/file/4392453/italiano_it_language_pack-${version}.xpi"; - # sha256 = ""; - # meta = with lib; - # { - # description = "Firefox Language Pack for Italiano (it) – Italian"; - # license = licenses.mpl20; - # mozPermissions = []; - # platforms = platforms.all; - # }; - # } - # ) - # ( - # buildFirefoxXpiAddon rec { - # pname = "dizionario-italiano"; - # version = "5.1"; - # addonId = "it-IT@dictionaries.addons.mozilla.org"; - # url = "https://addons.mozilla.org/firefox/downloads/file/1163874/dizionario_italiano-${version}.xpi"; - # sha256 = ""; - # meta = with lib; - # { - # description = "Add support for Italian to spellchecking"; - # license = licenses.gpl3; - # mozPermissions = []; - # platforms = platforms.all; - # }; - # } - # ) - ]; - - 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; - - # Security - "privacy.trackingprotection.enabled" = true; - "dom.security.https_only_mode" = true; - - "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" = ""; - "dom.security.https_only_mode_pbm" = true; - "dom.security.https_only_mode_error_page_user_suggestions" = true; - - # Disable telemetry - "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; - }; - - bookmarks = { - force = true; - settings = [ - { - 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 = "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 = [""]; - } - # Template - # { - # name = ""; - # url = ""; - # keyword = ""; - # tags = [""]; - # } - ]; - }; - }; - }; - }; -} 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 00000000..8435d45d --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/bookmarks.nix @@ -0,0 +1,149 @@ +{...}: { + 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 = [""]; + } + # 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 00000000..4246c68e --- /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 00000000..16783532 --- /dev/null +++ b/configurations/home-manager/leyla/packages/firefox/firefox.nix @@ -0,0 +1,221 @@ +{ + 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.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 + + deutsch-de-language-pack + dictionary-german + + tab-session-manager + + # (\ + # buildFirefoxXpiAddon rec {\ + # pname = "italiano-it-language-pack";\ + # version = "132.0.20241110.231641";\ + # addonId = "langpack-it@firefox.mozilla.org";\ + # url = "https://addons.mozilla.org/firefox/downloads/file/4392453/italiano_it_language_pack-${version}.xpi";\ + # sha256 = "";\ + # meta = with lib;\ + # {\ + # description = "Firefox Language Pack for Italiano (it) – Italian";\ + # license = licenses.mpl20;\ + # mozPermissions = [];\ + # platforms = platforms.all;\ + # };\ + # }\ + # )\ + # (\ + # buildFirefoxXpiAddon rec {\ + # pname = "dizionario-italiano";\ + # version = "5.1";\ + # addonId = "it-IT@dictionaries.addons.mozilla.org";\ + # url = "https://addons.mozilla.org/firefox/downloads/file/1163874/dizionario_italiano-${version}.xpi";\ + # sha256 = "";\ + # meta = with lib;\ + # {\ + # description = "Add support for Italian to spellchecking";\ + # license = licenses.gpl3;\ + # mozPermissions = [];\ + # platforms = platforms.all;\ + # };\ + # }\ + # )\ + ]; + + 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 00000000..66310c22 --- /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 index 568cd7ab..499e37b1 100644 --- a/configurations/home-manager/leyla/packages/git.nix +++ b/configurations/home-manager/leyla/packages/git.nix @@ -2,9 +2,11 @@ config = { programs = { git = { - userName = "Leyla Becker"; - userEmail = "git@jan-leila.com"; - extraConfig.init.defaultBranch = "main"; + settings = { + user.name = "Leyla Becker"; + user.email = "git@jan-leila.com"; + init.defaultBranch = "main"; + }; }; }; }; diff --git a/configurations/home-manager/leyla/packages/vscode/default.nix b/configurations/home-manager/leyla/packages/vscode/default.nix index ba9e48a3..36168b20 100644 --- a/configurations/home-manager/leyla/packages/vscode/default.nix +++ b/configurations/home-manager/leyla/packages/vscode/default.nix @@ -32,6 +32,7 @@ in { "javascript.updateImportsOnFileMove.enabled" = "always"; "editor.tabSize" = 2; "editor.insertSpaces" = false; + # "terminal.integrated.fontFamily" = "'Droid Sans Mono', 'monospace', monospace"; } ]; @@ -40,6 +41,7 @@ in { oneDark.enable = true; atomKeybindings.enable = true; openRemoteSsh.enable = true; + # openDyslexicFont.enable = false; # html development autoRenameTag.enable = true; @@ -67,6 +69,9 @@ in { # go development go.enable = true; + # rust development + rustAnalyzer.enable = true; + # claude development claudeDev = lib.mkIf ai-tooling-enabled { enable = true; diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix index 9fbdee68..e2f9401f 100644 --- a/configurations/nixos/defiant/configuration.nix +++ b/configurations/nixos/defiant/configuration.nix @@ -57,7 +57,6 @@ "ata-ST18000NT001-3NF101_ZVTEF27J" "ata-ST18000NE000-3G6101_ZVTJ7359" ] - # TODO: this needs to be configured manually [ "ata-ST4000NE001-2MA101_WS2275P3" "ata-ST4000NE001-2MA101_WS227B9F" @@ -103,18 +102,6 @@ directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; }; }; - reverse_proxy = { - enable = true; - enableACME = true; - hostname = "jan-leila.com"; - }; - postgres = { - extraUsers = { - leyla = { - isAdmin = true; - }; - }; - }; }; systemd.network = { @@ -226,6 +213,12 @@ }; services = { + # PostgreSQL database server + postgresql = { + enable = true; + adminUsers = ["leyla"]; + }; + # temp enable desktop environment for setup # Enable the X11 windowing system. xserver.enable = true; @@ -238,6 +231,16 @@ gnome.enable = true; }; + # Enable new reverse proxy system + reverseProxy = { + enable = true; + openFirewall = true; + acme = { + enable = true; + email = "jan-leila@protonmail.com"; + }; + }; + ollama = { enable = true; exposePort = true; @@ -295,35 +298,35 @@ jellyfin = { enable = true; - subdomain = "media"; - extraSubdomains = ["jellyfin"]; + domain = "media.jan-leila.com"; + extraDomains = ["jellyfin.jan-leila.com"]; }; immich = { enable = true; - subdomain = "photos"; + domain = "photos.jan-leila.com"; }; forgejo = { enable = true; - subdomain = "git"; + reverseProxy.domain = "git.jan-leila.com"; }; searx = { enable = true; - subdomain = "search"; + domain = "search.jan-leila.com"; }; actual = { - enable = false; - subdomain = "budget"; + enable = true; + domain = "budget.jan-leila.com"; }; home-assistant = { enable = true; - subdomain = "home"; + domain = "home.jan-leila.com"; openFirewall = true; - database = "postgres"; + postgres.enable = true; extensions = { sonos.enable = true; @@ -334,7 +337,7 @@ paperless = { enable = true; - subdomain = "documents"; + domain = "documents.jan-leila.com"; passwordFile = config.sops.secrets."services/paperless_password".path; }; @@ -343,6 +346,21 @@ openFirewall = true; }; + crab-hole = { + enable = true; + port = 8085; + openFirewall = true; + show_doc = true; + downstreams = { + host = { + enable = true; + openFirewall = true; + }; + }; + upstreams.cloudFlare.enable = true; + blocklists.ad_malware.enable = true; + }; + qbittorrent = { enable = true; mediaDir = "/srv/qbittorent"; @@ -350,21 +368,28 @@ webuiPort = 8084; }; - filebot-cleanup = { - enable = true; - licenseFile = "/srv/jellyfin/filebot_license.psm"; - }; - sonarr = { - enable = false; + enable = true; openFirewall = true; }; radarr = { - enable = false; + enable = true; openFirewall = true; }; bazarr = { - enable = false; + enable = true; + openFirewall = true; + }; + lidarr = { + enable = true; + openFirewall = true; + }; + jackett = { + enable = true; + openFirewall = true; + }; + flaresolverr = { + enable = true; openFirewall = true; }; }; diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix index 05975a12..30139466 100644 --- a/configurations/nixos/defiant/default.nix +++ b/configurations/nixos/defiant/default.nix @@ -4,6 +4,5 @@ ./hardware-configuration.nix ./configuration.nix ./packages.nix - ./filebot.nix ]; } diff --git a/configurations/nixos/defiant/filebot.nix b/configurations/nixos/defiant/filebot.nix deleted file mode 100644 index aaf247db..00000000 --- a/configurations/nixos/defiant/filebot.nix +++ /dev/null @@ -1,82 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -with lib; let - cfg = config.services.filebot-cleanup; -in { - options.services.filebot-cleanup = { - enable = mkEnableOption "Filebot cleanup service"; - - licenseFile = mkOption { - type = types.nullOr types.path; - default = null; - description = "Path to the Filebot license file"; - }; - - cleanupDirectory = mkOption { - type = types.str; - default = "/srv/jellyfin/filebot_cleanup"; - description = "Directory where cleaned up media files are stored"; - }; - }; - - config = mkIf cfg.enable { - users.groups.filebot_cleanup = {}; - users.users.filebot_cleanup = { - isSystemUser = true; - group = "filebot_cleanup"; - extraGroups = ["jellyfin_media"]; - home = cfg.cleanupDirectory; - createHome = true; - }; - - nixpkgs.config.allowUnfreePredicate = pkg: - builtins.elem (lib.getName pkg) [ - "filebot" - ]; - - environment.systemPackages = with pkgs; [ - filebot - ]; - - systemd.services.filebot-cleanup = { - description = "Filebot media cleanup service"; - serviceConfig = { - Type = "simple"; - User = "filebot_cleanup"; - Group = "filebot_cleanup"; - ExecStart = pkgs.writeShellScript "filebot-cleanup" '' - ${optionalString (cfg.licenseFile != null) '' - ${pkgs.filebot}/bin/filebot --license "${cfg.licenseFile}" - ''} - ${pkgs.filebot}/bin/filebot -rename -r "/srv/jellyfin/media/Movies/" --output "${cfg.cleanupDirectory}/" --format "{jellyfin}" -non-strict --action duplicate - ${pkgs.filebot}/bin/filebot -rename -r "/srv/jellyfin/media/Shows/" --output "${cfg.cleanupDirectory}/" --format "{jellyfin}" -non-strict --action duplicate - ''; - StandardOutput = "journal"; - StandardError = "journal"; - }; - wantedBy = ["multi-user.target"]; - }; - - environment.persistence = lib.mkIf config.host.impermanence.enable { - "/persist/system/jellyfin" = { - enable = true; - hideMounts = true; - files = [ - cfg.licenseFile - ]; - directories = [ - { - directory = cfg.cleanupDirectory; - user = "filebot_cleanup"; - group = "filebot_cleanup"; - mode = "1770"; - } - ]; - }; - }; - }; -} diff --git a/configurations/nixos/emergent/configuration.nix b/configurations/nixos/emergent/configuration.nix index bb671f73..6121069f 100644 --- a/configurations/nixos/emergent/configuration.nix +++ b/configurations/nixos/emergent/configuration.nix @@ -41,12 +41,14 @@ # 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; @@ -68,6 +70,9 @@ }; 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"; diff --git a/flake.lock b/flake.lock index 93091053..ae03c48c 100644 --- a/flake.lock +++ b/flake.lock @@ -25,11 +25,11 @@ ] }, "locked": { - "lastModified": 1758287904, - "narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=", + "lastModified": 1760701190, + "narHash": "sha256-y7UhnWlER8r776JsySqsbTUh2Txf7K30smfHlqdaIQw=", "owner": "nix-community", "repo": "disko", - "rev": "67ff9807dd148e704baadbd4fd783b54282ca627", + "rev": "3a9450b26e69dcb6f8de6e2b07b3fc1c288d85f5", "type": "github" }, "original": { @@ -46,11 +46,11 @@ }, "locked": { "dir": "pkgs/firefox-addons", - "lastModified": 1759403080, - "narHash": "sha256-EteyL8KyG9R5xzqyOBzyag4n2cSemu61VFrl3opJSqE=", + "lastModified": 1761797037, + "narHash": "sha256-OqwAGit+3cdsG02K6+8WJniA2q0rqUVc6zbT5N9C1us=", "owner": "rycee", "repo": "nur-expressions", - "rev": "8af6dfcbcbf1115a4f5aeed77ff0db5d3c02caf0", + "rev": "3d9f4de0988bcfa57e45e16e1ef9326c56bdf891", "type": "gitlab" }, "original": { @@ -62,11 +62,11 @@ }, "flake-compat": { "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -111,24 +111,6 @@ "type": "github" } }, - "flake-utils_3": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "flakey-profile": { "locked": { "lastModified": 1712898590, @@ -151,11 +133,11 @@ ] }, "locked": { - "lastModified": 1759337100, - "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=", + "lastModified": 1761845621, + "narHash": "sha256-d+R4MHsGmdebvSMsYUFWONsZSlUbOo8Zq/wjMdMiIac=", "owner": "nix-community", "repo": "home-manager", - "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3", + "rev": "97e3022a8d2c09313fa49847f6da4d76abcfc72d", "type": "github" }, "original": { @@ -182,11 +164,11 @@ "lix": { "flake": false, "locked": { - "lastModified": 1759624822, - "narHash": "sha256-cf40qfsfpxJU/BnQ9PEj027LdPINNSsJqm+C6Ug93BA=", - "rev": "57333a0e600c5e096a609410a2f1059b97194b1e", + "lastModified": 1755787066, + "narHash": "sha256-X2UwkUEban08GRSPXRr+kz8fckHqebr3P77qSvjoeOw=", + "rev": "ac9721a92e8138d29707824dbedb484c76948493", "type": "tarball", - "url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/57333a0e600c5e096a609410a2f1059b97194b1e.tar.gz" + "url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/ac9721a92e8138d29707824dbedb484c76948493.tar.gz?rev=ac9721a92e8138d29707824dbedb484c76948493" }, "original": { "type": "tarball", @@ -203,11 +185,11 @@ ] }, "locked": { - "lastModified": 1756511062, - "narHash": "sha256-IgD1JR7scSEwlK/YAbmrcTWpAYT30LPldCUHdzXkaMs=", + "lastModified": 1759851320, + "narHash": "sha256-n5dRAIC3/78drQtFxmQRrBLd6TKfotUnX7GWu0mAcSg=", "ref": "refs/heads/main", - "rev": "3f09a5eb772e02d98bb8878ab687d5b721f00d16", - "revCount": 162, + "rev": "7c31a18259b8358ac196cf803a26967c0fa1d3e4", + "revCount": 163, "type": "git", "url": "https://git.lix.systems/lix-project/nixos-module.git" }, @@ -225,11 +207,11 @@ ] }, "locked": { - "lastModified": 1759342933, - "narHash": "sha256-mdlUFcrOfvT0Pm+Hko/6aR3xf1ao5JA2iem4KsEVjP4=", + "lastModified": 1760821194, + "narHash": "sha256-UCsJ8eDuHL14u2GFIYEY/drtZ6jht5zN/G/6QNlEy2g=", "owner": "utensils", "repo": "mcp-nixos", - "rev": "50b02bcba32b941d2ec48fedef68641702ca5b0f", + "rev": "0ae453f38d0f088c31d4678da3a12b183165986f", "type": "github" }, "original": { @@ -245,11 +227,11 @@ ] }, "locked": { - "lastModified": 1758805352, - "narHash": "sha256-BHdc43Lkayd+72W/NXRKHzX5AZ+28F3xaUs3a88/Uew=", + "lastModified": 1761339987, + "narHash": "sha256-IUaawVwItZKi64IA6kF6wQCLCzpXbk2R46dHn8sHkig=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "c48e963a5558eb1c3827d59d21c5193622a1477c", + "rev": "7cd9aac79ee2924a85c211d21fafd394b06a38de", "type": "github" }, "original": { @@ -281,17 +263,16 @@ }, "nix-vscode-extensions": { "inputs": { - "flake-utils": "flake-utils_3", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1759369908, - "narHash": "sha256-IIhaE6jAge64z+fIyi/8Vtu0JdTtapbp4CvwiuIkZ1E=", + "lastModified": 1761789484, + "narHash": "sha256-17gDUWloFXQlavqHRey/urQe6sQ3yP5hsQyYmcNOZyU=", "owner": "nix-community", "repo": "nix-vscode-extensions", - "rev": "a66ad2141b1440a838ead278c6edfe8a4ce75e6c", + "rev": "c47e683d236fa6e4c27dbda2af3468cb9aceb813", "type": "github" }, "original": { @@ -302,11 +283,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1759261527, - "narHash": "sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g=", + "lastModified": 1761827175, + "narHash": "sha256-XdPVSYyIBK4/ruoqujaQmmSGg3J2/EenexV9IEXhr6o=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "e087756cf4abbe1a34f3544c480fc1034d68742f", + "rev": "43ffe9ac82567512abb83187cb673de1091bdfa8", "type": "github" }, "original": { @@ -334,11 +315,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1759381078, - "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", + "lastModified": 1761672384, + "narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", + "rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c", "type": "github" }, "original": { @@ -389,11 +370,11 @@ ] }, "locked": { - "lastModified": 1759188042, - "narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=", + "lastModified": 1760998189, + "narHash": "sha256-ee2e1/AeGL5X8oy/HXsZQvZnae6XfEVdstGopKucYLY=", "owner": "Mic92", "repo": "sops-nix", - "rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d", + "rev": "5a7d18b5c55642df5c432aadb757140edfeb70b3", "type": "github" }, "original": { @@ -431,21 +412,6 @@ "repo": "default", "type": "github" } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/modules/common-modules/pkgs/mapillary-uploader.nix b/modules/common-modules/pkgs/mapillary-uploader.nix index 7ce24f22..acff7723 100644 --- a/modules/common-modules/pkgs/mapillary-uploader.nix +++ b/modules/common-modules/pkgs/mapillary-uploader.nix @@ -9,7 +9,7 @@ src = fetchurl { url = "http://tools.mapillary.com/uploader/download/linux/${version}"; name = "mapillary-uploader.AppImage"; - sha256 = "sha256-Oyx7AIdA/2mwBaq7UzXOoyq/z2SU2sViMN40sY2RCQw="; + sha256 = "sha256-hpWdfeuhYylO+SFD3BsKI0s/xtObCDd5OcuJ6i/aEuI="; }; appimageContents = appimageTools.extractType2 { @@ -23,9 +23,6 @@ in # Install desktop file install -Dm644 ${appimageContents}/mapillary-desktop-uploader.desktop $out/share/applications/mapillary-uploader.desktop - # Install icon - install -Dm644 ${appimageContents}/usr/share/icons/hicolor/0x0/apps/mapillary-desktop-uploader.png $out/share/pixmaps/mapillary-uploader.png - # Fix desktop file paths substituteInPlace $out/share/applications/mapillary-uploader.desktop \ --replace 'Exec=AppRun' 'Exec=${pname}' diff --git a/modules/common-modules/pkgs/polycule/default.nix b/modules/common-modules/pkgs/polycule/default.nix index b463cc5c..e9841fed 100644 --- a/modules/common-modules/pkgs/polycule/default.nix +++ b/modules/common-modules/pkgs/polycule/default.nix @@ -3,7 +3,7 @@ flutter332, fetchFromGitLab, pkg-config, - wrapGAppsHook, + wrapGAppsHook3, gtk3, glib, glib-networking, @@ -65,7 +65,7 @@ flutter332.buildFlutterApplication rec { nativeBuildInputs = [ pkg-config - wrapGAppsHook + wrapGAppsHook3 ]; buildInputs = [ diff --git a/modules/home-manager-modules/default.nix b/modules/home-manager-modules/default.nix index 4c085a54..29d34148 100644 --- a/modules/home-manager-modules/default.nix +++ b/modules/home-manager-modules/default.nix @@ -5,6 +5,7 @@ ./user.nix ./flipperzero.nix ./i18n.nix + ./impermanence.nix ./openssh.nix ./gnome.nix ./programs diff --git a/modules/home-manager-modules/gnome.nix b/modules/home-manager-modules/gnome.nix index 8c70cf6c..ab56189b 100644 --- a/modules/home-manager-modules/gnome.nix +++ b/modules/home-manager-modules/gnome.nix @@ -1,8 +1,16 @@ { 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 { @@ -34,7 +42,7 @@ ]; default = "blue"; }; - extensions = lib.mkOption { + extraExtensions = lib.mkOption { type = lib.types.listOf lib.types.package; default = []; description = "The set of extensions to install and enable in the user environment."; @@ -60,16 +68,80 @@ })); 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 = config.gnome.extensions; + home.packages = extensions; dconf = { settings = lib.mkMerge [ { "org/gnome/shell" = { disable-user-extensions = false; # enables user extensions - enabled-extensions = builtins.map (extension: extension.extensionUuid) config.gnome.extensions; + enabled-extensions = builtins.map (extension: extension.extensionUuid) extensions; }; "org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close"; @@ -77,7 +149,23 @@ "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 @@ -100,6 +188,15 @@ ) ) ) + + # 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/impermanence.nix b/modules/home-manager-modules/impermanence.nix new file mode 100644 index 00000000..6c75edd1 --- /dev/null +++ b/modules/home-manager-modules/impermanence.nix @@ -0,0 +1,35 @@ +{ + 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; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf config.impermanence.enable { + assertions = [ + { + assertion = osConfig.host.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.host.impermanence.enable && !cfg.enable && cfg.fallbackPersistence.enable) { + home.persistence."/persist/home/${config.home.username}" = { + directories = ["."]; + allowOther = true; + }; + }) + ]; +} diff --git a/modules/home-manager-modules/openssh.nix b/modules/home-manager-modules/openssh.nix index 9d77d106..afc98dd2 100644 --- a/modules/home-manager-modules/openssh.nix +++ b/modules/home-manager-modules/openssh.nix @@ -95,7 +95,7 @@ ); } ) - (lib.mkIf osConfig.host.impermanence.enable { + (lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix index b9b91c41..e305b6c6 100644 --- a/modules/home-manager-modules/programs/bitwarden.nix +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.bitwarden = { @@ -12,11 +11,11 @@ config = lib.mkIf config.programs.bitwarden.enable (lib.mkMerge [ { home.packages = with pkgs; [ - bitwarden + bitwarden-desktop ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/Bitwarden" diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix index 00b248f5..8ad5e634 100644 --- a/modules/home-manager-modules/programs/bruno.nix +++ b/modules/home-manager-modules/programs/bruno.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.bruno = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/bruno/" diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix index 9e5f34e3..dbe6e2b6 100644 --- a/modules/home-manager-modules/programs/calibre.nix +++ b/modules/home-manager-modules/programs/calibre.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.calibre = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 index 00ba525b..6c4526fa 100644 --- a/modules/home-manager-modules/programs/davinci-resolve.nix +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.davinci-resolve = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/DaVinciResolve" diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix index a9624594..8b6c41ad 100644 --- a/modules/home-manager-modules/programs/dbeaver.nix +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.dbeaver-bin = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.dataHome}/DBeaverData/" diff --git a/modules/home-manager-modules/programs/default.nix b/modules/home-manager-modules/programs/default.nix index 68e5c717..3fff4895 100644 --- a/modules/home-manager-modules/programs/default.nix +++ b/modules/home-manager-modules/programs/default.nix @@ -12,11 +12,13 @@ ./obsidian.nix ./prostudiomasters.nix ./idea.nix + ./kdenlive.nix ./krita.nix ./protonvpn.nix ./calibre.nix ./bruno.nix ./dbeaver.nix + ./dungeon-draft.nix ./steam.nix ./vscode ./ungoogled-chromium.nix @@ -24,6 +26,7 @@ ./mapillary-uploader.nix ./inkscape.nix ./gimp.nix + ./guild-wars-2.nix ./proxmark3.nix ./freecad.nix ./onionshare.nix @@ -33,11 +36,14 @@ ./qflipper.nix ./openvpn.nix ./noisetorch.nix + ./olympus.nix ./openrgb.nix ./via.nix + ./vortex.nix ./davinci-resolve.nix ./gdx-liftoff.nix ./tor-browser.nix ./polycule.nix + ./vmware-workstation.nix ]; } diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix index e8605a51..d5d7192a 100644 --- a/modules/home-manager-modules/programs/discord.nix +++ b/modules/home-manager-modules/programs/discord.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.discord = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 00000000..faa69c69 --- /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 index 907b619b..8841887d 100644 --- a/modules/home-manager-modules/programs/firefox.nix +++ b/modules/home-manager-modules/programs/firefox.nix @@ -1,7 +1,6 @@ { lib, config, - osConfig, ... }: let buildProfilePersistence = profile: { @@ -26,7 +25,7 @@ allowOther = true; }; in { - config = lib.mkIf (config.programs.firefox.enable && osConfig.host.impermanence.enable) { + config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge ( ( lib.attrsets.mapAttrsToList diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix index ec172057..89668dec 100644 --- a/modules/home-manager-modules/programs/freecad.nix +++ b/modules/home-manager-modules/programs/freecad.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.freecad = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 index b29230d3..44408312 100644 --- a/modules/home-manager-modules/programs/gdx-liftoff.nix +++ b/modules/home-manager-modules/programs/gdx-liftoff.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.gdx-liftoff = { diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix index 428068ea..925a2d9f 100644 --- a/modules/home-manager-modules/programs/gimp.nix +++ b/modules/home-manager-modules/programs/gimp.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.gimp = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 00000000..3f68ec68 --- /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 index f0a928cd..e59e7b21 100644 --- a/modules/home-manager-modules/programs/idea.nix +++ b/modules/home-manager-modules/programs/idea.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.jetbrains.idea-community = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ # configuration diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix index facb08f5..a26ddecc 100644 --- a/modules/home-manager-modules/programs/inkscape.nix +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.inkscape = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 00000000..05327d17 --- /dev/null +++ b/modules/home-manager-modules/programs/kdenlive.nix @@ -0,0 +1,36 @@ +{ + 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."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/kdenliverc" + "${config.xdg.dataHome}/kdenlive" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/krita.nix b/modules/home-manager-modules/programs/krita.nix index d6622510..3ba5560c 100644 --- a/modules/home-manager-modules/programs/krita.nix +++ b/modules/home-manager-modules/programs/krita.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.krita = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/kritarc" diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix index b61ea58c..93163e79 100644 --- a/modules/home-manager-modules/programs/libreoffice.nix +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.libreoffice = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/libreoffice" diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix index eca059dd..e92c3d37 100644 --- a/modules/home-manager-modules/programs/makemkv.nix +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.makemkv = { @@ -30,7 +29,7 @@ home.file.".MakeMKV/settings.conf".source = config.lib.file.mkOutOfStoreSymlink config.sops.templates."MakeMKV.settings.conf".path; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ ".MakeMKV" diff --git a/modules/home-manager-modules/programs/mapillary-uploader.nix b/modules/home-manager-modules/programs/mapillary-uploader.nix index 38c11448..df1f0937 100644 --- a/modules/home-manager-modules/programs/mapillary-uploader.nix +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -11,7 +11,20 @@ in { enable = mkEnableOption "Mapillary Desktop Uploader"; }; - config = mkIf cfg.enable { - home.packages = [pkgs.mapillary-uploader]; - }; + config = mkIf cfg.enable (mkMerge [ + { + home.packages = [pkgs.mapillary-uploader]; + } + ( + mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/mapillary-uploader" + "${config.xdg.dataHome}/mapillary-uploader" + ]; + allowOther = true; + }; + } + ) + ]); } diff --git a/modules/home-manager-modules/programs/mfoc.nix b/modules/home-manager-modules/programs/mfoc.nix index 7b92007f..6006c9ba 100644 --- a/modules/home-manager-modules/programs/mfoc.nix +++ b/modules/home-manager-modules/programs/mfoc.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.mfoc = { diff --git a/modules/home-manager-modules/programs/noisetorch.nix b/modules/home-manager-modules/programs/noisetorch.nix index c53e3a91..4b426387 100644 --- a/modules/home-manager-modules/programs/noisetorch.nix +++ b/modules/home-manager-modules/programs/noisetorch.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.noisetorch = { diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix index 98c4feac..bfdba908 100644 --- a/modules/home-manager-modules/programs/obs.nix +++ b/modules/home-manager-modules/programs/obs.nix @@ -1,13 +1,17 @@ { lib, config, - osConfig, ... }: { config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ ( - lib.mkIf osConfig.host.impermanence.enable { - # TODO: map impermanence for obs + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/obs-studio" + ]; + allowOther = true; + }; } ) ]); diff --git a/modules/home-manager-modules/programs/obsidian.nix b/modules/home-manager-modules/programs/obsidian.nix index 4d28b3e7..824563d3 100644 --- a/modules/home-manager-modules/programs/obsidian.nix +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -1,12 +1,11 @@ { lib, config, - osConfig, ... }: { config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 00000000..0e38eecc --- /dev/null +++ b/modules/home-manager-modules/programs/olympus.nix @@ -0,0 +1,36 @@ +{ + 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."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/olympus" + "${config.xdg.dataHome}/olympus" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/onionshare.nix b/modules/home-manager-modules/programs/onionshare.nix index ed1903de..475f993a 100644 --- a/modules/home-manager-modules/programs/onionshare.nix +++ b/modules/home-manager-modules/programs/onionshare.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.onionshare = { diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix index 0260c91a..c9d5e141 100644 --- a/modules/home-manager-modules/programs/openrgb.nix +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -2,16 +2,27 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.openrgb = { enable = lib.mkEnableOption "enable openrgb"; }; - config = lib.mkIf config.programs.openrgb.enable { - home.packages = with pkgs; [ - openrgb - ]; - }; + config = lib.mkIf config.programs.openrgb.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + openrgb + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/OpenRGB" + ]; + allowOther = true; + }; + } + ) + ]); } diff --git a/modules/home-manager-modules/programs/openvpn.nix b/modules/home-manager-modules/programs/openvpn.nix index 814c16da..dcd499ce 100644 --- a/modules/home-manager-modules/programs/openvpn.nix +++ b/modules/home-manager-modules/programs/openvpn.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.openvpn = { diff --git a/modules/home-manager-modules/programs/pdfarranger.nix b/modules/home-manager-modules/programs/pdfarranger.nix index d4e33b5c..9246efd4 100644 --- a/modules/home-manager-modules/programs/pdfarranger.nix +++ b/modules/home-manager-modules/programs/pdfarranger.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.pdfarranger = { diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix index d2c1fe22..bc37b865 100644 --- a/modules/home-manager-modules/programs/picard.nix +++ b/modules/home-manager-modules/programs/picard.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.picard = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/MusicBrainz" diff --git a/modules/home-manager-modules/programs/piper.nix b/modules/home-manager-modules/programs/piper.nix index ec0d887d..3ed25fd8 100644 --- a/modules/home-manager-modules/programs/piper.nix +++ b/modules/home-manager-modules/programs/piper.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.piper = { diff --git a/modules/home-manager-modules/programs/polycule.nix b/modules/home-manager-modules/programs/polycule.nix index a7004bd1..d0aea2ae 100644 --- a/modules/home-manager-modules/programs/polycule.nix +++ b/modules/home-manager-modules/programs/polycule.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.polycule = { @@ -17,7 +16,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { # TODO: check that these are actually the correct folders # directories = [ diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix index 9e6088f0..5345169f 100644 --- a/modules/home-manager-modules/programs/prostudiomasters.nix +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.prostudiomasters = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/ProStudioMasters" diff --git a/modules/home-manager-modules/programs/protonvpn.nix b/modules/home-manager-modules/programs/protonvpn.nix index dd11aae7..513a6103 100644 --- a/modules/home-manager-modules/programs/protonvpn.nix +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.protonvpn-gui = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/protonvpn" diff --git a/modules/home-manager-modules/programs/proxmark3.nix b/modules/home-manager-modules/programs/proxmark3.nix index ad1e2989..656be193 100644 --- a/modules/home-manager-modules/programs/proxmark3.nix +++ b/modules/home-manager-modules/programs/proxmark3.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.proxmark3 = { diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix index 02e23df8..61d13c02 100644 --- a/modules/home-manager-modules/programs/qbittorrent.nix +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.qbittorrent = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/qBittorrent" diff --git a/modules/home-manager-modules/programs/qflipper.nix b/modules/home-manager-modules/programs/qflipper.nix index abc24424..8b427667 100644 --- a/modules/home-manager-modules/programs/qflipper.nix +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.qflipper = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/qFlipper" diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix index fdf0af9e..7db23a7a 100644 --- a/modules/home-manager-modules/programs/signal.nix +++ b/modules/home-manager-modules/programs/signal.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.signal-desktop-bin = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/Signal" diff --git a/modules/home-manager-modules/programs/steam.nix b/modules/home-manager-modules/programs/steam.nix index 46611514..fd98cb6a 100644 --- a/modules/home-manager-modules/programs/steam.nix +++ b/modules/home-manager-modules/programs/steam.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.steam = { @@ -18,7 +17,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ { diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix index 2c585788..c3b085da 100644 --- a/modules/home-manager-modules/programs/tor-browser.nix +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.tor-browser = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { 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 index 5b52cd67..ef6a8818 100644 --- a/modules/home-manager-modules/programs/ungoogled-chromium.nix +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -2,7 +2,6 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.ungoogled-chromium = { @@ -16,7 +15,7 @@ ]; } ( - lib.mkIf osConfig.host.impermanence.enable { + lib.mkIf config.impermanence.enable { home.persistence."/persist${config.home.homeDirectory}" = { directories = [ "${config.xdg.configHome}/chromium" diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix index 0b794526..0aa58e4e 100644 --- a/modules/home-manager-modules/programs/via.nix +++ b/modules/home-manager-modules/programs/via.nix @@ -2,16 +2,28 @@ lib, pkgs, config, - osConfig, ... }: { options.programs.via = { enable = lib.mkEnableOption "enable via"; }; - config = lib.mkIf config.programs.via.enable { - home.packages = with pkgs; [ - via - ]; - }; + config = lib.mkIf config.programs.via.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + via + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/via" + "${config.xdg.dataHome}/via" + ]; + allowOther = true; + }; + } + ) + ]); } 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 00000000..8e9d406e --- /dev/null +++ b/modules/home-manager-modules/programs/vmware-workstation.nix @@ -0,0 +1,37 @@ +{ + 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."/persist${config.home.homeDirectory}" = { + directories = [ + { + directory = ".vmware"; + method = "symlink"; + } + { + directory = "vmware"; + method = "symlink"; + } + ]; + allowOther = true; + }; + } + ) + ] + ); +} diff --git a/modules/home-manager-modules/programs/vortex.nix b/modules/home-manager-modules/programs/vortex.nix new file mode 100644 index 00000000..cb865262 --- /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/default.nix b/modules/home-manager-modules/programs/vscode/default.nix index 85f4a623..f9d83dc7 100644 --- a/modules/home-manager-modules/programs/vscode/default.nix +++ b/modules/home-manager-modules/programs/vscode/default.nix @@ -16,6 +16,7 @@ ./go.nix ./evenBetterToml.nix ./openRemoteSsh.nix + ./rustAnalyzer.nix ./astroVscode.nix ./vscodeMdx.nix ./claudeDev.nix @@ -23,5 +24,6 @@ ./vitest.nix ./direnv.nix ./conventionalCommits.nix + ./openDyslexicFont.nix ]; } 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 00000000..f1f6215b --- /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/rustAnalyzer.nix b/modules/home-manager-modules/programs/vscode/rustAnalyzer.nix new file mode 100644 index 00000000..66e9ebe2 --- /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/nixos-modules/server/actual/actual.nix b/modules/nixos-modules/server/actual/actual.nix new file mode 100644 index 00000000..4cca4491 --- /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 index 13b068e8..14b715e9 100644 --- a/modules/nixos-modules/server/actual/const.nix +++ b/modules/nixos-modules/server/actual/const.nix @@ -1,3 +1,3 @@ { - dataDirectory = "/var/lib/actual/"; + dataDirectory = "/var/lib/private/actual"; } diff --git a/modules/nixos-modules/server/actual/default.nix b/modules/nixos-modules/server/actual/default.nix index 546240e1..b59517b8 100644 --- a/modules/nixos-modules/server/actual/default.nix +++ b/modules/nixos-modules/server/actual/default.nix @@ -1,26 +1,8 @@ { - lib, - config, - ... -}: let - const = import ./const.nix; - dataDirectory = const.dataDirectory; -in { imports = [ + ./actual.nix ./proxy.nix ./fail2ban.nix ./impermanence.nix ]; - - config = lib.mkIf config.services.actual.enable { - systemd.tmpfiles.rules = [ - "d ${dataDirectory} 2770 actual actual" - ]; - - services.actual = { - settings = { - ACTUAL_DATA_DIR = dataDirectory; - }; - }; - }; } diff --git a/modules/nixos-modules/server/actual/impermanence.nix b/modules/nixos-modules/server/actual/impermanence.nix index 5eee95ac..d870789d 100644 --- a/modules/nixos-modules/server/actual/impermanence.nix +++ b/modules/nixos-modules/server/actual/impermanence.nix @@ -6,11 +6,22 @@ const = import ./const.nix; dataDirectory = const.dataDirectory; in { - config = lib.mkIf (config.services.actual.enable && config.host.impermanence.enable) { + options.services.actual = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.actual.impermanence.enable { assertions = [ { - assertion = config.services.actual.settings.ACTUAL_DATA_DIR == dataDirectory; - message = "actual data location does not match persistence"; + assertion = config.services.actual.settings.dataDir == dataDirectory; + message = "actual data location does not match persistence\nconfig directory: ${config.services.actual.settings.dataDir}\npersistence directory: ${dataDirectory}"; + } + { + assertion = config.systemd.services.actual.serviceConfig.DynamicUser or false; + message = "actual systemd service must have DynamicUser enabled to use private directory"; } ]; environment.persistence."/persist/system/root" = { diff --git a/modules/nixos-modules/server/actual/proxy.nix b/modules/nixos-modules/server/actual/proxy.nix index 6ca51e49..9d375745 100644 --- a/modules/nixos-modules/server/actual/proxy.nix +++ b/modules/nixos-modules/server/actual/proxy.nix @@ -4,17 +4,30 @@ ... }: { options.services.actual = { - subdomain = lib.mkOption { + domain = lib.mkOption { type = lib.types.str; - default = "actual"; - description = "subdomain of base domain that actual will be hosted at"; + 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.enable && config.host.reverse_proxy.enable) { - host = { - reverse_proxy.subdomains.${config.services.actual.subdomain} = { - target = "http://localhost:${toString config.services.actual.settings.port}"; + 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/bazarr/default.nix b/modules/nixos-modules/server/bazarr/default.nix index f39d9400..86dbb4b1 100644 --- a/modules/nixos-modules/server/bazarr/default.nix +++ b/modules/nixos-modules/server/bazarr/default.nix @@ -1,6 +1,5 @@ {...}: { imports = [ - ./proxy.nix ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/bazarr/impermanence.nix b/modules/nixos-modules/server/bazarr/impermanence.nix index 22fb0e63..70a45d13 100644 --- a/modules/nixos-modules/server/bazarr/impermanence.nix +++ b/modules/nixos-modules/server/bazarr/impermanence.nix @@ -5,7 +5,14 @@ }: let bazarr_data_directory = "/var/lib/bazarr"; in { - config = lib.mkIf (config.services.bazarr.enable && config.host.impermanence.enable) { + options.services.bazarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.bazarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.bazarr.impermanence.enable { assertions = [ { assertion = config.services.bazarr.dataDir == bazarr_data_directory; diff --git a/modules/nixos-modules/server/bazarr/proxy.nix b/modules/nixos-modules/server/bazarr/proxy.nix deleted file mode 100644 index fe310d81..00000000 --- a/modules/nixos-modules/server/bazarr/proxy.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.bazarr = { - subdomain = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Subdomain for reverse proxy. If null, service will be local only."; - }; - extraSubdomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Extra subdomains for reverse proxy."; - }; - }; - - config = lib.mkIf (config.services.bazarr.enable && config.services.bazarr.subdomain != null) { - host.reverse_proxy.subdomains.bazarr = { - subdomain = config.services.bazarr.subdomain; - extraSubdomains = config.services.bazarr.extraSubdomains; - target = "http://127.0.0.1:6767"; - websockets.enable = true; - forwardHeaders.enable = true; - }; - }; -} 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 00000000..d76323ae --- /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 00000000..158a8513 --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./crab-hole.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/crab-hole/impermanence.nix b/modules/nixos-modules/server/crab-hole/impermanence.nix new file mode 100644 index 00000000..51efc0cf --- /dev/null +++ b/modules/nixos-modules/server/crab-hole/impermanence.nix @@ -0,0 +1,33 @@ +{ + 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.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.crab-hole.impermanence.enable { + assertions = [ + { + assertion = + config.systemd.services.crab-hole.serviceConfig.WorkingDirectory == (builtins.replaceStrings ["/private"] [""] workingDirectory); + message = "crab-hole working directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = workingDirectory; + user = "crab-hole"; + group = "crab-hole"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/default.nix b/modules/nixos-modules/server/default.nix index e550123d..2b330893 100644 --- a/modules/nixos-modules/server/default.nix +++ b/modules/nixos-modules/server/default.nix @@ -1,20 +1,23 @@ {...}: { imports = [ - ./reverse_proxy.nix - ./fail2ban.nix - ./postgres.nix + ./reverseProxy + ./fail2ban + ./postgres ./network_storage - ./podman.nix ./actual ./bazarr + ./crab-hole + ./flaresolverr ./forgejo ./home-assistant ./immich + ./jackett ./jellyfin + ./lidarr ./panoramax ./paperless - ./qbittorent.nix + ./qbittorent ./radarr ./searx ./sonarr diff --git a/modules/nixos-modules/server/fail2ban.nix b/modules/nixos-modules/server/fail2ban.nix deleted file mode 100644 index d19aeebd..00000000 --- a/modules/nixos-modules/server/fail2ban.nix +++ /dev/null @@ -1,74 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: let - dataFolder = "/var/lib/fail2ban"; - dataFile = "fail2ban.sqlite3"; -in { - config = lib.mkIf config.services.fail2ban.enable (lib.mkMerge [ - { - 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 {}; - }; - }; - } - (lib.mkIf config.host.impermanence.enable { - assertions = [ - { - assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; - message = "fail2ban data file does not match persistence"; - } - ]; - - environment.persistence."/persist/system/root" = { - directories = [ - { - directory = dataFolder; - user = "fail2ban"; - group = "fail2ban"; - } - ]; - }; - }) - ]); -} diff --git a/modules/nixos-modules/server/fail2ban/default.nix b/modules/nixos-modules/server/fail2ban/default.nix new file mode 100644 index 00000000..30fca99b --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./fail2ban.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/fail2ban/fail2ban.nix b/modules/nixos-modules/server/fail2ban/fail2ban.nix new file mode 100644 index 00000000..261c68fa --- /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/impermanence.nix b/modules/nixos-modules/server/fail2ban/impermanence.nix new file mode 100644 index 00000000..6e214b36 --- /dev/null +++ b/modules/nixos-modules/server/fail2ban/impermanence.nix @@ -0,0 +1,34 @@ +{ + 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.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.fail2ban.impermanence.enable { + assertions = [ + { + assertion = config.services.fail2ban.daemonSettings.Definition.dbfile == "${dataFolder}/${dataFile}"; + message = "fail2ban data file does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = dataFolder; + user = "fail2ban"; + group = "fail2ban"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/flaresolverr/default.nix b/modules/nixos-modules/server/flaresolverr/default.nix new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/flaresolverr/impermanence.nix b/modules/nixos-modules/server/flaresolverr/impermanence.nix new file mode 100644 index 00000000..4544e750 --- /dev/null +++ b/modules/nixos-modules/server/flaresolverr/impermanence.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: { + options.services.flaresolverr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.flaresolverr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.flaresolverr.impermanence.enable { + # FlareSolverr typically doesn't need persistent storage as it's a proxy service + # but we'll add basic structure in case it's needed for logs or configuration + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = "/var/lib/flaresolverr"; + user = "flaresolverr"; + group = "flaresolverr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/database.nix b/modules/nixos-modules/server/forgejo/database.nix index 0417aabd..bb8781c4 100644 --- a/modules/nixos-modules/server/forgejo/database.nix +++ b/modules/nixos-modules/server/forgejo/database.nix @@ -2,40 +2,31 @@ lib, config, ... -}: { - config = lib.mkIf config.services.forgejo.enable ( - lib.mkMerge [ +}: let + usingPostgres = config.services.forgejo.database.type == "postgres"; +in { + config = lib.mkIf config.services.forgejo.enable { + assertions = [ { - host = { - postgres = { - enable = true; - }; - }; - - assertions = [ - { - assertion = config.services.forgejo.settings.database.DB_TYPE == "postgres"; - message = "Forgejo database type must be postgres"; - } - ]; + assertion = !usingPostgres || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when Forgejo database type is postgres"; } - (lib.mkIf config.host.postgres.enable { - host = { - postgres = { - extraUsers = { - forgejo = { - isClient = true; - createUser = true; - }; - }; - extraDatabases = { - forgejo = { - name = "forgejo"; - }; - }; - }; - }; - }) - ] - ); + { + 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 index 1fdc8d9a..4333f69a 100644 --- a/modules/nixos-modules/server/forgejo/default.nix +++ b/modules/nixos-modules/server/forgejo/default.nix @@ -1,53 +1,9 @@ { - lib, - config, - ... -}: let - const = import ./const.nix; - httpPort = const.httpPort; - sshPort = const.sshPort; - db_user = "forgejo"; -in { imports = [ + ./forgejo.nix ./proxy.nix ./database.nix ./fail2ban.nix ./impermanence.nix ]; - - 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.subdomain}.${config.host.reverse_proxy.hostname}"; - 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/fail2ban.nix b/modules/nixos-modules/server/forgejo/fail2ban.nix index 213c804a..dfe221a5 100644 --- a/modules/nixos-modules/server/forgejo/fail2ban.nix +++ b/modules/nixos-modules/server/forgejo/fail2ban.nix @@ -4,7 +4,16 @@ pkgs, ... }: { - config = lib.mkIf (config.services.forgejo.enable && config.services.fail2ban.enable) { + 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 '' diff --git a/modules/nixos-modules/server/forgejo/forgejo.nix b/modules/nixos-modules/server/forgejo/forgejo.nix new file mode 100644 index 00000000..70d30877 --- /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/impermanence.nix b/modules/nixos-modules/server/forgejo/impermanence.nix index 04f21a5e..6fe3de8b 100644 --- a/modules/nixos-modules/server/forgejo/impermanence.nix +++ b/modules/nixos-modules/server/forgejo/impermanence.nix @@ -5,7 +5,14 @@ }: let stateDir = "/var/lib/forgejo"; in { - config = lib.mkIf (config.services.forgejo.enable && config.host.impermanence.enable) { + options.services.forgejo = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.forgejo.impermanence.enable { assertions = [ { assertion = config.services.forgejo.stateDir == stateDir; diff --git a/modules/nixos-modules/server/forgejo/proxy.nix b/modules/nixos-modules/server/forgejo/proxy.nix index 51f769d1..c2d31319 100644 --- a/modules/nixos-modules/server/forgejo/proxy.nix +++ b/modules/nixos-modules/server/forgejo/proxy.nix @@ -7,16 +7,33 @@ httpPort = const.httpPort; in { options.services.forgejo = { - subdomain = lib.mkOption { - type = lib.types.str; - description = "subdomain of base domain that forgejo will be hosted at"; - default = "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.enable && config.host.reverse_proxy.enable) { - host.reverse_proxy.subdomains.${config.services.forgejo.subdomain} = { + 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 = [ diff --git a/modules/nixos-modules/server/home-assistant/database.nix b/modules/nixos-modules/server/home-assistant/database.nix index 0ac8002f..f1927ed9 100644 --- a/modules/nixos-modules/server/home-assistant/database.nix +++ b/modules/nixos-modules/server/home-assistant/database.nix @@ -2,55 +2,52 @@ lib, config, ... -}: let - dbUser = "hass"; -in { - config = lib.mkIf config.services.home-assistant.enable ( - lib.mkMerge [ +}: { + 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 = [ { - host = { - postgres = { - enable = true; - }; - }; - - assertions = [ - { - assertion = config.services.home-assistant.database == "postgres"; - message = "Home Assistant database type must be postgres"; - } - ]; + assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; } - (lib.mkIf config.host.postgres.enable { - host = { - postgres = { - extraUsers = { - ${dbUser} = { - isClient = true; - createUser = true; - }; - }; - extraDatabases = { - ${dbUser} = { - name = dbUser; - }; - }; - }; - }; + ]; - services.home-assistant = { - extraPackages = python3Packages: - with python3Packages; [ - psycopg2 - ]; - }; + 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; + }; - systemd.services.home-assistant = { - requires = [ - config.systemd.services.postgresql.name - ]; - }; - }) - ] - ); + 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 index 83d8ba76..b6f9356d 100644 --- a/modules/nixos-modules/server/home-assistant/default.nix +++ b/modules/nixos-modules/server/home-assistant/default.nix @@ -1,112 +1,10 @@ { - lib, - config, - ... -}: { imports = [ + ./home-assistant.nix ./proxy.nix ./database.nix ./fail2ban.nix ./impermanence.nix ./extensions ]; - - 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.subdomain}.${config.host.reverse_proxy.hostname}"; - # 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/fail2ban.nix b/modules/nixos-modules/server/home-assistant/fail2ban.nix index 6ac5900f..25194efe 100644 --- a/modules/nixos-modules/server/home-assistant/fail2ban.nix +++ b/modules/nixos-modules/server/home-assistant/fail2ban.nix @@ -3,36 +3,46 @@ pkgs, config, ... -}: -lib.mkIf (config.services.fail2ban.enable && config.services.home-assistant.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 - '') - ); +}: { + options.services.home-assistant = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.services.home-assistant.enable; + }; + }; }; - 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; + 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 00000000..fa58d5ee --- /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 index ba8f20da..b7564599 100644 --- a/modules/nixos-modules/server/home-assistant/proxy.nix +++ b/modules/nixos-modules/server/home-assistant/proxy.nix @@ -4,29 +4,39 @@ ... }: { options.services.home-assistant = { - subdomain = lib.mkOption { + domain = lib.mkOption { type = lib.types.str; - description = "subdomain of base domain that home-assistant will be hosted at"; - default = "home-assistant"; + 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.host.reverse_proxy.enable && config.services.home-assistant.enable) { - host = { - reverse_proxy.subdomains.${config.services.home-assistant.subdomain} = { - target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; + 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; - websockets.enable = true; + settings = { + proxyWebsockets.enable = true; forwardHeaders.enable = true; - extraConfig = '' - add_header Upgrade $http_upgrade; - add_header Connection \"upgrade\"; - - proxy_buffering off; - - proxy_read_timeout 90; - ''; + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 90; + }; }; }; }; diff --git a/modules/nixos-modules/server/immich/database.nix b/modules/nixos-modules/server/immich/database.nix index 74b1aaa9..52af51eb 100644 --- a/modules/nixos-modules/server/immich/database.nix +++ b/modules/nixos-modules/server/immich/database.nix @@ -3,24 +3,28 @@ config, ... }: { - config = lib.mkIf config.services.immich.enable (lib.mkMerge [ - { - host = { - postgres = { - enable = true; - }; - }; - } - (lib.mkIf config.host.postgres.enable { - host = { - postgres = { - extraUsers = { - ${config.services.immich.database.user} = { - isClient = true; - }; - }; - }; - }; - }) - ]); + 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/fail2ban.nix b/modules/nixos-modules/server/immich/fail2ban.nix index c9ec87bf..21593e7b 100644 --- a/modules/nixos-modules/server/immich/fail2ban.nix +++ b/modules/nixos-modules/server/immich/fail2ban.nix @@ -4,7 +4,16 @@ pkgs, ... }: { - config = lib.mkIf (config.services.fail2ban.enable && config.services.immich.enable) { + 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] diff --git a/modules/nixos-modules/server/immich/impermanence.nix b/modules/nixos-modules/server/immich/impermanence.nix index f63d178f..56e51d06 100644 --- a/modules/nixos-modules/server/immich/impermanence.nix +++ b/modules/nixos-modules/server/immich/impermanence.nix @@ -5,7 +5,14 @@ }: let mediaLocation = "/var/lib/immich"; in { - config = lib.mkIf (config.services.immich.enable && config.host.impermanence.enable) { + options.services.immich = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.immich.impermanence.enable { assertions = [ { assertion = config.services.immich.mediaLocation == mediaLocation; diff --git a/modules/nixos-modules/server/immich/proxy.nix b/modules/nixos-modules/server/immich/proxy.nix index dae2420b..9c8c165a 100644 --- a/modules/nixos-modules/server/immich/proxy.nix +++ b/modules/nixos-modules/server/immich/proxy.nix @@ -4,31 +4,40 @@ ... }: { options.services.immich = { - subdomain = lib.mkOption { + domain = lib.mkOption { type = lib.types.str; - description = "subdomain of base domain that immich will be hosted at"; - default = "immich"; + 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.enable && config.host.reverse_proxy.enable) { - host = { - reverse_proxy.subdomains.${config.services.immich.subdomain} = { - target = "http://localhost:${toString config.services.immich.port}"; + 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; - websockets.enable = true; + settings = { + proxyWebsockets.enable = true; forwardHeaders.enable = true; + maxBodySize = 50000; - extraConfig = '' - # allow large file uploads - client_max_body_size 50000M; - - # set timeout - proxy_read_timeout 600s; - proxy_send_timeout 600s; - send_timeout 600s; - proxy_redirect off; - ''; + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 600; + }; }; }; }; diff --git a/modules/nixos-modules/server/jackett/default.nix b/modules/nixos-modules/server/jackett/default.nix new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/jackett/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/jackett/impermanence.nix b/modules/nixos-modules/server/jackett/impermanence.nix new file mode 100644 index 00000000..24fc5e63 --- /dev/null +++ b/modules/nixos-modules/server/jackett/impermanence.nix @@ -0,0 +1,33 @@ +{ + 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.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.jackett.impermanence.enable { + assertions = [ + { + assertion = config.services.jackett.dataDir == jackett_data_directory; + message = "jackett data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = jackett_data_directory; + user = "jackett"; + group = "jackett"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/default.nix b/modules/nixos-modules/server/jellyfin/default.nix index 0d884818..2dbdcfd2 100644 --- a/modules/nixos-modules/server/jellyfin/default.nix +++ b/modules/nixos-modules/server/jellyfin/default.nix @@ -1,38 +1,8 @@ { - lib, - pkgs, - config, - ... -}: let - jellyfinPort = 8096; - dlanPort = 1900; -in { imports = [ + ./jellyfin.nix ./proxy.nix ./fail2ban.nix ./impermanence.nix ]; - - 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/impermanence.nix b/modules/nixos-modules/server/jellyfin/impermanence.nix index e0b3b5d5..cbcb54f8 100644 --- a/modules/nixos-modules/server/jellyfin/impermanence.nix +++ b/modules/nixos-modules/server/jellyfin/impermanence.nix @@ -6,7 +6,14 @@ jellyfin_data_directory = "/var/lib/jellyfin"; jellyfin_cache_directory = "/var/cache/jellyfin"; in { - config = lib.mkIf (config.services.jellyfin.enable && config.host.impermanence.enable) { + options.services.jellyfin = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.jellyfin.impermanence.enable { fileSystems."/persist/system/jellyfin".neededForBoot = true; host.storage.pool.extraDatasets = { diff --git a/modules/nixos-modules/server/jellyfin/jellyfin.nix b/modules/nixos-modules/server/jellyfin/jellyfin.nix new file mode 100644 index 00000000..9bfa9217 --- /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 index 1020a19e..35289e76 100644 --- a/modules/nixos-modules/server/jellyfin/proxy.nix +++ b/modules/nixos-modules/server/jellyfin/proxy.nix @@ -6,33 +6,36 @@ jellyfinPort = 8096; in { options.services.jellyfin = { - subdomain = lib.mkOption { + domain = lib.mkOption { type = lib.types.str; - description = "subdomain of base domain that jellyfin will be hosted at"; - default = "jellyfin"; + description = "domain that jellyfin will be hosted at"; + default = "jellyfin.arpa"; }; - extraSubdomains = lib.mkOption { + extraDomains = lib.mkOption { type = lib.types.listOf lib.types.str; - description = "ex subdomain of base domain that jellyfin will be hosted at"; + 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.enable && config.host.reverse_proxy.enable) { - host.reverse_proxy.subdomains.jellyfin = { + 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; - subdomain = config.services.jellyfin.subdomain; - extraSubdomains = config.services.jellyfin.extraSubdomains; - - forwardHeaders.enable = true; - - extraConfig = '' - client_max_body_size 20M; - add_header X-Content-Type-Options "nosniff"; - - proxy_buffering off; - ''; + settings = { + forwardHeaders.enable = true; + maxBodySize = 20; + noSniff.enable = true; + proxyBuffering.enable = false; + }; }; }; } diff --git a/modules/nixos-modules/server/lidarr/default.nix b/modules/nixos-modules/server/lidarr/default.nix new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/lidarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/lidarr/impermanence.nix b/modules/nixos-modules/server/lidarr/impermanence.nix new file mode 100644 index 00000000..5d3aa3ff --- /dev/null +++ b/modules/nixos-modules/server/lidarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + 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.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.lidarr.impermanence.enable { + assertions = [ + { + assertion = config.services.lidarr.dataDir == lidarr_data_directory; + message = "lidarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = lidarr_data_directory; + user = "lidarr"; + group = "lidarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/network_storage/default.nix b/modules/nixos-modules/server/network_storage/default.nix index eaac7fe1..cd100abc 100644 --- a/modules/nixos-modules/server/network_storage/default.nix +++ b/modules/nixos-modules/server/network_storage/default.nix @@ -1,90 +1,6 @@ { - config, - lib, - ... -}: let - export_directory = config.host.network_storage.export_directory; -in { imports = [ + ./network_storage.nix ./nfs.nix ]; - - 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/system/root" = { - # enable = true; - # hideMounts = true; - # directories = [ - # config.host.network_storage.export_directory - # ]; - # }; - # }) - ]); } 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 00000000..ebc3bee7 --- /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/system/root" = { + # enable = true; + # hideMounts = true; + # directories = [ + # config.host.network_storage.export_directory + # ]; + # }; + # }) + ]); +} diff --git a/modules/nixos-modules/server/panoramax/database.nix b/modules/nixos-modules/server/panoramax/database.nix index 8679f9a4..17217263 100644 --- a/modules/nixos-modules/server/panoramax/database.nix +++ b/modules/nixos-modules/server/panoramax/database.nix @@ -3,32 +3,46 @@ config, ... }: { - config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ - { - host = { - postgres = { - enable = true; + 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"; }; }; - } - ( - lib.mkIf config.host.postgres.enable { - host = { - postgres = { - extraUsers = { - ${config.services.panoramax.database.user} = { - isClient = true; - createUser = true; - }; - }; - extraDatabases = { - ${config.services.panoramax.database.name} = { - name = config.services.panoramax.database.user; - }; - }; - }; - }; + }; + }; + + 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/impermanence.nix b/modules/nixos-modules/server/panoramax/impermanence.nix index 011c3223..e25ef926 100644 --- a/modules/nixos-modules/server/panoramax/impermanence.nix +++ b/modules/nixos-modules/server/panoramax/impermanence.nix @@ -1,10 +1,16 @@ { lib, config, - osConfig, ... }: { - config = lib.mkIf (config.services.panoramax.enable && osConfig.host.impermanence.enable) { + options.services.panoramax = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.panoramax.impermanence.enable { # TODO: configure impermanence for panoramax data # This would typically include directories like: # - /var/lib/panoramax diff --git a/modules/nixos-modules/server/panoramax/proxy.nix b/modules/nixos-modules/server/panoramax/proxy.nix index 79f93260..7cd71113 100644 --- a/modules/nixos-modules/server/panoramax/proxy.nix +++ b/modules/nixos-modules/server/panoramax/proxy.nix @@ -4,31 +4,35 @@ ... }: { options.services.panoramax = { - subdomain = lib.mkOption { + domain = lib.mkOption { type = lib.types.str; - description = "subdomain of base domain that panoramax will be hosted at"; - default = "panoramax"; + 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.enable && config.host.reverse_proxy.enable) { - host = { - reverse_proxy.subdomains.${config.services.panoramax.subdomain} = { - target = "http://localhost:${toString config.services.panoramax.port}"; + 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; - websockets.enable = true; + settings = { + proxyWebsockets.enable = true; forwardHeaders.enable = true; - - extraConfig = '' - # allow large file uploads for panoramic images - client_max_body_size 100M; - - # set timeout for image processing - proxy_read_timeout 300s; - proxy_send_timeout 300s; - send_timeout 300s; - proxy_redirect off; - ''; + maxBodySize = 100000; + timeout = 300; }; }; }; diff --git a/modules/nixos-modules/server/paperless/database.nix b/modules/nixos-modules/server/paperless/database.nix index 6f4ce514..c63e59d9 100644 --- a/modules/nixos-modules/server/paperless/database.nix +++ b/modules/nixos-modules/server/paperless/database.nix @@ -3,32 +3,28 @@ lib, ... }: { - config = lib.mkIf config.services.paperless.enable (lib.mkMerge [ - { - host = { - postgres = { - enable = true; - }; - }; - } - ( - lib.mkIf config.host.postgres.enable { - host = { - postgres = { - extraUsers = { - ${config.services.paperless.database.user} = { - isClient = true; - createUser = true; - }; - }; - extraDatabases = { - ${config.services.paperless.database.user} = { - name = config.services.paperless.database.user; - }; - }; - }; - }; + 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 index a6878eb0..7e5e16be 100644 --- a/modules/nixos-modules/server/paperless/default.nix +++ b/modules/nixos-modules/server/paperless/default.nix @@ -1,35 +1,9 @@ { - config, - lib, - ... -}: { imports = [ + ./paperless.nix ./proxy.nix ./database.nix ./fail2ban.nix ./impermanence.nix ]; - - 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 = { - domain = "${config.services.paperless.subdomain}.${config.host.reverse_proxy.hostname}"; - 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/impermanence.nix b/modules/nixos-modules/server/paperless/impermanence.nix index d9e17bdb..fc87ea7d 100644 --- a/modules/nixos-modules/server/paperless/impermanence.nix +++ b/modules/nixos-modules/server/paperless/impermanence.nix @@ -5,7 +5,14 @@ }: let dataDir = "/var/lib/paperless"; in { - config = lib.mkIf (config.services.paperless.enable && config.host.impermanence.enable) { + options.services.paperless = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.paperless.impermanence.enable { assertions = [ { assertion = config.services.paperless.dataDir == dataDir; diff --git a/modules/nixos-modules/server/paperless/paperless.nix b/modules/nixos-modules/server/paperless/paperless.nix new file mode 100644 index 00000000..5bcbfeda --- /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 index 2910f079..9d152c9c 100644 --- a/modules/nixos-modules/server/paperless/proxy.nix +++ b/modules/nixos-modules/server/paperless/proxy.nix @@ -4,25 +4,29 @@ ... }: { options.services.paperless = { - subdomain = lib.mkOption { - type = lib.types.str; - description = "subdomain of base domain that paperless will be hosted at"; - default = "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.enable && config.host.reverse_proxy.enable) { - host = { - reverse_proxy.subdomains.${config.services.paperless.subdomain} = { - target = "http://${config.services.paperless.address}:${toString config.services.paperless.port}"; + 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; - websockets.enable = true; + settings = { + proxyWebsockets.enable = true; forwardHeaders.enable = true; - - extraConfig = '' - # allow large file uploads - client_max_body_size 50000M; - ''; + maxBodySize = 50000; }; }; }; diff --git a/modules/nixos-modules/server/podman.nix b/modules/nixos-modules/server/podman.nix deleted file mode 100644 index 9301140f..00000000 --- a/modules/nixos-modules/server/podman.nix +++ /dev/null @@ -1,73 +0,0 @@ -{ - lib, - config, - ... -}: { - options.host.podman = { - enable = lib.mkEnableOption "should podman be enabled on this computer"; - macvlan = { - subnet = lib.mkOption { - type = lib.types.str; - description = "Subnet for macvlan address range"; - }; - gateway = lib.mkOption { - type = lib.types.str; - description = "Gateway for macvlan"; - # TODO: see if we can default this to systemd network gateway - }; - networkInterface = lib.mkOption { - type = lib.types.str; - description = "Parent network interface for macvlan"; - # TODO: see if we can default this some interface? - }; - }; - }; - config = lib.mkIf config.host.podman.enable { - systemd = { - services = { - # "podman-network-macvlan" = { - # path = [pkgs.podman]; - # serviceConfig = { - # Type = "oneshot"; - # RemainAfterExit = true; - # ExecStop = "podman network rm -f macvlan"; - # }; - # script = '' - # podman network inspect macvlan || podman network create --driver macvlan --subnet ${config.host.podman.macvlan.subnet} --gateway ${config.host.podman.macvlan.gateway} --opt parent=${config.host.podman.macvlan.networkInterface} macvlan - # ''; - # partOf = ["podman-compose-root.target"]; - # wantedBy = ["podman-compose-root.target"]; - # }; - }; - # disable computer sleeping - targets = { - # Root service - # When started, this will automatically create all resources and start - # the containers. When stopped, this will teardown all resources. - "podman-compose-root" = { - unitConfig = { - Description = "Root target for podman targets."; - }; - wantedBy = ["multi-user.target"]; - }; - }; - }; - - virtualisation = { - # Runtime - podman = { - enable = true; - autoPrune.enable = true; - dockerCompat = true; - # defaultNetwork.settings = { - # # Required for container networking to be able to use names. - # dns_enabled = true; - # }; - }; - - oci-containers = { - backend = "podman"; - }; - }; - }; -} diff --git a/modules/nixos-modules/server/postgres.nix b/modules/nixos-modules/server/postgres.nix deleted file mode 100644 index 71ce44c5..00000000 --- a/modules/nixos-modules/server/postgres.nix +++ /dev/null @@ -1,121 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: let - dataDir = "/var/lib/postgresql/16"; - adminUsers = lib.lists.filter (user: user.isAdmin) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); - clientUsers = lib.lists.filter (user: user.isClient) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); - createUsers = lib.lists.filter (user: user.createUser) (lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraUsers); - createDatabases = lib.attrsets.mapAttrsToList (_: user: user) config.host.postgres.extraDatabases; -in { - options = { - host.postgres = { - enable = lib.mkEnableOption "enable postgres"; - extraUsers = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - name = lib.mkOption { - type = lib.types.str; - default = name; - }; - isAdmin = lib.mkOption { - type = lib.types.bool; - default = false; - }; - isClient = lib.mkOption { - type = lib.types.bool; - default = false; - }; - createUser = lib.mkOption { - type = lib.types.bool; - default = false; - }; - }; - })); - default = {}; - }; - extraDatabases = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - name = lib.mkOption { - type = lib.types.str; - default = name; - }; - }; - })); - default = {}; - }; - }; - }; - - config = lib.mkIf config.host.postgres.enable (lib.mkMerge [ - { - services = { - postgresql = { - enable = true; - package = pkgs.postgresql_16; - ensureUsers = - [ - { - name = "postgres"; - } - ] - ++ ( - builtins.map (user: { - name = user.name; - ensureDBOwnership = true; - }) - createUsers - ); - ensureDatabases = builtins.map (database: database.name) createDatabases; - identMap = - '' - # ArbitraryMapName systemUser DBUser - - # Administration Users - superuser_map root postgres - superuser_map postgres postgres - '' - + ( - lib.strings.concatLines (builtins.map (user: "superuser_map ${user.name} postgres") adminUsers) - ) - + '' - - # Client Users - '' - + ( - lib.strings.concatLines (builtins.map (user: "user_map ${user.name} ${user.name}") clientUsers) - ); - # configuration here lets users access the db that matches their name and lets user postgres access everything - 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 - ''; - }; - }; - } - - (lib.mkIf config.host.impermanence.enable { - assertions = [ - { - assertion = config.services.postgresql.dataDir == dataDir; - message = "postgres data directory does not match persistence"; - } - ]; - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = dataDir; - user = "postgres"; - group = "postgres"; - } - ]; - }; - }) - ]); -} diff --git a/modules/nixos-modules/server/postgres/default.nix b/modules/nixos-modules/server/postgres/default.nix new file mode 100644 index 00000000..abf4ade8 --- /dev/null +++ b/modules/nixos-modules/server/postgres/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./postgres.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/postgres/impermanence.nix b/modules/nixos-modules/server/postgres/impermanence.nix new file mode 100644 index 00000000..a67fb1a3 --- /dev/null +++ b/modules/nixos-modules/server/postgres/impermanence.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/postgresql/16"; +in { + config = lib.mkIf (config.services.postgresql.enable && config.host.impermanence.enable) { + assertions = [ + { + assertion = config.services.postgresql.dataDir == dataDir; + message = "postgres data directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = dataDir; + user = "postgres"; + group = "postgres"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/postgres/postgres.nix b/modules/nixos-modules/server/postgres/postgres.nix new file mode 100644 index 00000000..af7d1b48 --- /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/qbittorent.nix b/modules/nixos-modules/server/qbittorent.nix deleted file mode 100644 index 2d545875..00000000 --- a/modules/nixos-modules/server/qbittorent.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ - lib, - config, - ... -}: let - qbittorent_profile_directory = "/var/lib/qBittorrent/"; -in { - 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 (lib.mkMerge [ - (lib.mkIf config.host.impermanence.enable { - fileSystems."/persist/system/qbittorrent".neededForBoot = true; - - host.storage.pool.extraDatasets = { - # sops age key needs to be available to pre persist for user generation - "persist/system/qbittorrent" = { - type = "zfs_fs"; - mountpoint = "/persist/system/qbittorrent"; - options = { - canmount = "on"; - }; - }; - }; - - assertions = [ - { - assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; - message = "qbittorrent data directory does not match persistence"; - } - ]; - - environment.persistence = { - "/persist/system/root" = { - directories = [ - { - directory = qbittorent_profile_directory; - user = "qbittorrent"; - group = "qbittorrent"; - } - ]; - }; - - "/persist/system/qbittorrent" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = config.services.qbittorrent.mediaDir; - user = "qbittorrent"; - group = "qbittorrent"; - mode = "1775"; - } - ]; - }; - }; - }) - ]); -} diff --git a/modules/nixos-modules/server/qbittorent/default.nix b/modules/nixos-modules/server/qbittorent/default.nix new file mode 100644 index 00000000..f7511e62 --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./qbittorent.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/qbittorent/impermanence.nix b/modules/nixos-modules/server/qbittorent/impermanence.nix new file mode 100644 index 00000000..1489e7dc --- /dev/null +++ b/modules/nixos-modules/server/qbittorent/impermanence.nix @@ -0,0 +1,61 @@ +{ + 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.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.qbittorrent.impermanence.enable { + fileSystems."/persist/system/qbittorrent".neededForBoot = true; + + host.storage.pool.extraDatasets = { + # sops age key needs to be available to pre persist for user generation + "persist/system/qbittorrent" = { + type = "zfs_fs"; + mountpoint = "/persist/system/qbittorrent"; + options = { + canmount = "on"; + }; + }; + }; + + assertions = [ + { + assertion = config.services.qbittorrent.profileDir == qbittorent_profile_directory; + message = "qbittorrent data directory does not match persistence"; + } + ]; + + environment.persistence = { + "/persist/system/root" = { + directories = [ + { + directory = qbittorent_profile_directory; + user = "qbittorrent"; + group = "qbittorrent"; + } + ]; + }; + + "/persist/system/qbittorrent" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.qbittorrent.mediaDir; + user = "qbittorrent"; + group = "qbittorrent"; + mode = "1775"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/qbittorent/qbittorent.nix b/modules/nixos-modules/server/qbittorent/qbittorent.nix new file mode 100644 index 00000000..44603c8a --- /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/radarr/default.nix b/modules/nixos-modules/server/radarr/default.nix index f39d9400..86dbb4b1 100644 --- a/modules/nixos-modules/server/radarr/default.nix +++ b/modules/nixos-modules/server/radarr/default.nix @@ -1,6 +1,5 @@ {...}: { imports = [ - ./proxy.nix ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/radarr/impermanence.nix b/modules/nixos-modules/server/radarr/impermanence.nix index 4a3242c9..c948e3a5 100644 --- a/modules/nixos-modules/server/radarr/impermanence.nix +++ b/modules/nixos-modules/server/radarr/impermanence.nix @@ -5,7 +5,14 @@ }: let radarr_data_directory = "/var/lib/radarr/.config/Radarr"; in { - config = lib.mkIf (config.services.radarr.enable && config.host.impermanence.enable) { + options.services.radarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.radarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.radarr.impermanence.enable { assertions = [ { assertion = config.services.radarr.dataDir == radarr_data_directory; diff --git a/modules/nixos-modules/server/radarr/proxy.nix b/modules/nixos-modules/server/radarr/proxy.nix deleted file mode 100644 index ec5f575f..00000000 --- a/modules/nixos-modules/server/radarr/proxy.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.radarr = { - subdomain = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Subdomain for reverse proxy. If null, service will be local only."; - }; - extraSubdomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Extra subdomains for reverse proxy."; - }; - }; - - config = lib.mkIf (config.services.radarr.enable && config.services.radarr.subdomain != null) { - host.reverse_proxy.subdomains.radarr = { - subdomain = config.services.radarr.subdomain; - extraSubdomains = config.services.radarr.extraSubdomains; - target = "http://127.0.0.1:7878"; - websockets.enable = true; - forwardHeaders.enable = true; - }; - }; -} diff --git a/modules/nixos-modules/server/reverseProxy/default.nix b/modules/nixos-modules/server/reverseProxy/default.nix new file mode 100644 index 00000000..5d571753 --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/default.nix @@ -0,0 +1,6 @@ +{...}: { + imports = [ + ./reverseProxy.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/reverseProxy/impermanence.nix b/modules/nixos-modules/server/reverseProxy/impermanence.nix new file mode 100644 index 00000000..7af55df2 --- /dev/null +++ b/modules/nixos-modules/server/reverseProxy/impermanence.nix @@ -0,0 +1,21 @@ +{ + lib, + config, + ... +}: let + dataDir = "/var/lib/acme"; +in { + config = lib.mkIf (config.host.impermanence.enable && config.services.reverseProxy.enable) { + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = dataDir; + user = "acme"; + group = "acme"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/reverseProxy/reverseProxy.nix b/modules/nixos-modules/server/reverseProxy/reverseProxy.nix new file mode 100644 index 00000000..eecc9bf7 --- /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/reverse_proxy.nix b/modules/nixos-modules/server/reverse_proxy.nix deleted file mode 100644 index 26b43749..00000000 --- a/modules/nixos-modules/server/reverse_proxy.nix +++ /dev/null @@ -1,128 +0,0 @@ -{ - lib, - config, - ... -}: let - dataDir = "/var/lib/acme"; - httpPort = 80; - httpsPort = 443; -in { - options.host.reverse_proxy = { - enable = lib.mkEnableOption "turn on the reverse proxy"; - hostname = lib.mkOption { - type = lib.types.str; - description = "what host name are we going to be proxying from"; - }; - forceSSL = lib.mkOption { - type = lib.types.bool; - description = "force connections to use https"; - default = config.host.reverse_proxy.enableACME; - }; - enableACME = lib.mkOption { - type = lib.types.bool; - description = "auto renew certificates"; - default = true; - }; - subdomains = lib.mkOption { - type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { - options = { - subdomain = lib.mkOption { - type = lib.types.str; - description = "what is the default subdomain to be used for this application to be used for"; - default = name; - }; - extraSubdomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - description = "extra domains that should be configured for this domain"; - default = []; - }; - - target = lib.mkOption { - type = lib.types.str; - description = "what url will all traffic to this application be forwarded to"; - }; - - websockets.enable = lib.mkEnableOption "should the default config proxy websockets"; - - forwardHeaders.enable = lib.mkEnableOption "should the default config contain forward headers"; - - extraConfig = lib.mkOption { - type = lib.types.lines; - default = ""; - description = '' - These lines go to the end of the upstream verbatim. - ''; - }; - }; - })); - }; - }; - - config = lib.mkIf config.host.reverse_proxy.enable (lib.mkMerge [ - { - security.acme = lib.mkIf config.host.reverse_proxy.enableACME { - acceptTerms = true; - defaults.email = "jan-leila@protonmail.com"; - }; - - services.nginx = { - enable = true; - virtualHosts = lib.mkMerge ( - lib.lists.flatten ( - lib.attrsets.mapAttrsToList ( - name: value: let - hostConfig = { - forceSSL = config.host.reverse_proxy.forceSSL; - enableACME = config.host.reverse_proxy.enableACME; - locations = { - "/" = { - proxyPass = value.target; - proxyWebsockets = value.websockets.enable; - recommendedProxySettings = value.forwardHeaders.enable; - extraConfig = - value.extraConfig; - }; - }; - }; - in ( - [ - { - ${"${value.subdomain}.${config.host.reverse_proxy.hostname}"} = hostConfig; - } - ] - ++ builtins.map (subdomain: {${"${subdomain}.${config.host.reverse_proxy.hostname}"} = hostConfig;}) - value.extraSubdomains - ) - ) - config.host.reverse_proxy.subdomains - ) - ); - }; - - networking.firewall.allowedTCPPorts = [ - httpPort - httpsPort - ]; - } - (lib.mkIf config.host.impermanence.enable { - # TODO: figure out how to write an assertion for this - # assertions = [ - # { - # assertion = security.acme.certs..directory == dataDir; - # message = "postgres data directory does not match persistence"; - # } - # ]; - environment.persistence."/persist/system/root" = { - enable = true; - hideMounts = true; - directories = [ - { - directory = dataDir; - user = "acme"; - group = "acme"; - } - ]; - }; - }) - ]); -} diff --git a/modules/nixos-modules/server/searx/default.nix b/modules/nixos-modules/server/searx/default.nix index ac84c1dd..54263802 100644 --- a/modules/nixos-modules/server/searx/default.nix +++ b/modules/nixos-modules/server/searx/default.nix @@ -1,63 +1,6 @@ { - config, - lib, - inputs, - ... -}: { imports = [ + ./searx.nix ./proxy.nix ]; - - 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/searx/proxy.nix b/modules/nixos-modules/server/searx/proxy.nix index 0c1eae15..e994e4a1 100644 --- a/modules/nixos-modules/server/searx/proxy.nix +++ b/modules/nixos-modules/server/searx/proxy.nix @@ -4,18 +4,27 @@ ... }: { options.services.searx = { - subdomain = lib.mkOption { - type = lib.types.str; - description = "subdomain of base domain that searx will be hosted at"; - default = "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.enable && config.host.reverse_proxy.enable) { - host = { - reverse_proxy.subdomains.searx = { - subdomain = config.services.searx.subdomain; - target = "http://localhost:${toString config.services.searx.settings.server.port}"; + 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 00000000..d4d4012c --- /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 index f39d9400..86dbb4b1 100644 --- a/modules/nixos-modules/server/sonarr/default.nix +++ b/modules/nixos-modules/server/sonarr/default.nix @@ -1,6 +1,5 @@ {...}: { imports = [ - ./proxy.nix ./impermanence.nix ]; } diff --git a/modules/nixos-modules/server/sonarr/impermanence.nix b/modules/nixos-modules/server/sonarr/impermanence.nix index abc843c5..5b90ee9d 100644 --- a/modules/nixos-modules/server/sonarr/impermanence.nix +++ b/modules/nixos-modules/server/sonarr/impermanence.nix @@ -5,7 +5,14 @@ }: let sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; in { - config = lib.mkIf (config.services.sonarr.enable && config.host.impermanence.enable) { + options.services.sonarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.sonarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.sonarr.impermanence.enable { assertions = [ { assertion = config.services.sonarr.dataDir == sonarr_data_directory; diff --git a/modules/nixos-modules/server/sonarr/proxy.nix b/modules/nixos-modules/server/sonarr/proxy.nix deleted file mode 100644 index 22b90a62..00000000 --- a/modules/nixos-modules/server/sonarr/proxy.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - lib, - config, - ... -}: { - options.services.sonarr = { - subdomain = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Subdomain for reverse proxy. If null, service will be local only."; - }; - extraSubdomains = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = []; - description = "Extra subdomains for reverse proxy."; - }; - }; - - config = lib.mkIf (config.services.sonarr.enable && config.services.sonarr.subdomain != null) { - host.reverse_proxy.subdomains.sonarr = { - subdomain = config.services.sonarr.subdomain; - extraSubdomains = config.services.sonarr.extraSubdomains; - target = "http://127.0.0.1:8989"; - websockets.enable = true; - forwardHeaders.enable = true; - }; - }; -} diff --git a/modules/nixos-modules/server/wyoming.nix b/modules/nixos-modules/server/wyoming.nix index 4894dd43..c9a14741 100644 --- a/modules/nixos-modules/server/wyoming.nix +++ b/modules/nixos-modules/server/wyoming.nix @@ -37,9 +37,9 @@ openwakeword = { enable = true; uri = "tcp://0.0.0.0:10400"; - preloadModels = [ - "ok_nabu" - ]; + # preloadModels = [ + # "ok_nabu" + # ]; # TODO: custom models }; }; diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix index 137ae4bd..987e080e 100644 --- a/modules/nixos-modules/users.nix +++ b/modules/nixos-modules/users.nix @@ -29,6 +29,8 @@ radarr = 2014; sonarr = 2015; bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; }; gids = { @@ -50,6 +52,8 @@ radarr = 2014; sonarr = 2015; bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; }; users = config.users.users; @@ -221,6 +225,18 @@ in { 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 = { @@ -261,6 +277,7 @@ in { users.radarr.name users.sonarr.name users.bazarr.name + users.lidarr.name leyla eve ivy @@ -365,6 +382,20 @@ in { 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 + ]; + }; }; }; } diff --git a/rebuild.sh b/rebuild.sh index 36a12012..67504502 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -1,5 +1,15 @@ #!/usr/bin/env bash +# Get current branch and git status for branch-aware behavior +current_branch=$(git branch --show-current 2>/dev/null || echo "unknown") +git_status=$(git status --porcelain 2>/dev/null || echo "") + +# Default values +default_target=$(hostname) +default_user="$USER" +default_host=$(hostname) +default_mode=$(if [[ "$current_branch" != "main" ]]; then echo "test"; else echo "switch"; fi) + if [ -d "result" ]; then preserve_result=true @@ -42,14 +52,29 @@ while [ $# -gt 0 ]; do ;; --help|-h) echo "--help -h: print this message" - echo "--target -t: set the target system to rebuild on" - echo "--flake -f: set the flake to rebuild on the target system" - echo "--mode -m: set the mode to rebuild flake as on the target system" - echo "--user -u: set the user to rebuild flake as on the target system" - echo "--host: set the host that the flake will be rebuilt on (unset for current machine)" + echo "--target -t: defaults to the current system" + echo " currently: $default_target" + echo "--flake -f: defaults to same as target" + echo " currently: ${target:-$default_target}" + echo "--mode -m: defaults to 'switch', but 'test' on non-main branches" + echo " currently would be: $default_mode" + echo "--user -u: defaults to the current user" + echo " currently: $default_user" + echo "--host: defaults to building on the current machine" + echo " currently: $default_host" echo "--preserve-result: do not remove the generated result folder after building" echo "--no-preserve-result: remove any result folder after building" echo "--show-trace: show trace on builds" + echo "" + echo "Branch-aware behavior:" + echo " - On non-main branches: defaults to test mode with warning" + echo " - On main with uncommitted changes: shows warning about creating a branch" + echo " - Current branch: $current_branch" + if [[ -n "$git_status" ]]; then + echo " - Git status: uncommitted changes detected" + else + echo " - Git status: clean working tree" + fi exit 0 ;; *) @@ -60,10 +85,21 @@ while [ $# -gt 0 ]; do shift done -target=${target:-$(hostname)} +target=${target:-$default_target} flake=${flake:-$target} -mode=${mode:-switch} -user=${user:-$USER} +mode=${mode:-$default_mode} +user=${user:-$default_user} + +# Branch-aware warnings and behavior +if [[ "$current_branch" != "main" ]] && [[ "$mode" == "test" ]]; then + echo "⚠️ WARNING: You are on branch '$current_branch' (not main)" + echo " Defaulting to test mode to prevent accidental system changes" + echo " Specify --mode=switch explicitly if you want to apply changes" +elif [[ "$current_branch" == "main" ]] && [[ -n "$git_status" ]] && [[ "$mode" != "test" ]]; then + echo "⚠️ WARNING: You are on main branch with uncommitted changes" + echo " Consider creating a feature branch for development:" + echo " git checkout -b feature/your-feature-name" +fi command="nixos-rebuild $mode --sudo --ask-sudo-password --flake .#$flake" @@ -91,4 +127,4 @@ then then rm -r result fi -fi \ No newline at end of file +fi