diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..8392d159 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 00000000..78d2a4ad --- /dev/null +++ b/.gitconfig @@ -0,0 +1,2 @@ +[core] + hooksPath = .hooks diff --git a/.gitignore b/.gitignore index e2f5dd2e..ce2538fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -result \ No newline at end of file +result +.direnv +.vscode/* +!.vscode/settings.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..dcfaddd4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "secrets"] + path = secrets + url = git@git.jan-leila.com:jan-leila/nix-config-secrets.git diff --git a/.hooks/post-commit b/.hooks/post-commit new file mode 100755 index 00000000..03a160da --- /dev/null +++ b/.hooks/post-commit @@ -0,0 +1,14 @@ +#!/usr/bin/env nix-shell +#! nix-shell -i bash ../shell.nix + +echo "restoring stashed changes" + +# 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 new file mode 100755 index 00000000..74cbc64a --- /dev/null +++ b/.hooks/pre-commit @@ -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) + +echo "stashing all uncommitted changes with named stash (excluding hooks)" +git stash push -q --keep-index -m "pre-commit-stash-$(date +%s)" -- ':!.hooks/' + +# 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" +alejandra -q . + +RESULT=$? + +echo "adding lint changes to commit" +git add -u + +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/.sops.yaml b/.sops.yaml index 0ac5664b..a6e6f4f4 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,7 +1,19 @@ keys: - &leyla age15ga3jmn2mqtlgwwtdcdh6l5vdx6um9aftrkexxfyue6xvcqapqusle75jh creation_rules: - - path_regex: secrets/secrets.yaml$ + - path_regex: secrets/user-passwords.yaml$ key_groups: - age: - *leyla + - path_regex: secrets/defiant-services.yaml$ + key_groups: + - age: + - *leyla + - path_regex: secrets/vpn-keys.yaml$ + key_groups: + - age: + - *leyla + - path_regex: secrets/application-keys.yaml$ + key_groups: + - age: + - *leyla \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..8d6717e2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "cSpell.words": [ + "attrsets", + "bitwarden", + "forgejo", + "gids", + "headscale", + "hesperium", + "jellyfin", + "macvlan", + "nextcloud", + "nixos", + "nixpkgs", + "pihole", + "pkgs", + "rpool", + "searx", + "ublock", + "uids" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index b8bed400..883e1437 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,107 @@ +# nix-config + +https://git.jan-leila.com/jan-leila/nix-config + +nix multi user, multi system, configuration with `sops` secret management, `home-manager`, and `nixos-anywhere` setup via `disko` with `zfs` + `impermanence` + # Hosts ## Host Map -| Hostname | Device Description | Primary User | Role | -| :---------: | :------------------------: | :--------------: | :-------: | -| `twilight` | Desktop Computer | Leyla | Desktop | -| `horizon` | 13 inch Framework Laptop | Leyla | Laptop | -| `defiant` | NAS Server | Leyla | Service | -| `emergent` | Desktop Computer | Eve | Laptop | -| `threshold` | Laptop | Eve | Desktop | +| Hostname | Device Description | Primary User | Role | Provisioned | Using Nix | +| :---------: | :------------------------: | :--------------: | :-------: | :---------: | :-------: | +| `twilight` | Desktop Computer | Leyla | Desktop | ✅ | ✅ | +| `horizon` | 13 inch Framework Laptop | Leyla | Laptop | ✅ | ✅ | +| `defiant` | NAS Server | Leyla | Server | ✅ | ✅ | +| `hesperium` | Mac | ????? | Mac | ❌ | ❌ | +| `emergent` | Desktop Computer | Eve | Desktop | ✅ | ✅ | +| `threshold` | Laptop | Eve | Laptop | ❌ | ❌ | +| `wolfram` | Steam Deck | House | Handheld | ✅ | ❌ | +| `ceder` | A5 Tablet | Leyla | Tablet | ✅ | ❌ | +| `skate` | A6 Tablet | Leyla | Tablet | ❌ | ❌ | +| `shale` | A6 Tablet | Eve | Tablet | ✅ | ❌ | +| `coven` | Pixel 8 | Leyla | Android | ✅ | ❌ | - -### Rebuild current machine to match target host: -`sudo nixos-rebuild switch --flake .#hostname` - -### Rebuild current machine maintaining current target +# Tooling +## Rebuilding `./rebuild.sh` -# New machine setup -keys for decrypting password secrets for each users located at `/var/lib/sops-nix/key.txt` - -updating passwords: `sops secrets/secrets.yaml` +## Updating +`nix flake update` +## New host setup `./install.sh --target 192.168.1.130 --flake hostname` -> how the current config was set up https://www.youtube.com/watch?v=G5f6GC7SnhU +## Updating Secrets +`sops secrets/secrets_file_here.yaml` -> something about ssh keys for remotes +## Inspecting a configuration +`nix-inspect -p .` # Notes: -- Look into this for fixing nixos-anywhere `https://github.com/lucidph3nx/nixos-config/tree/main` -- Look into this for rotating sops keys `https://technotim.live/posts/rotate-sops-encryption-keys/` -- Look into this for openssh known configurations https://search.nixos.org/options?channel=unstable&from=0&size=15&sort=alpha_asc&type=packages&query=services.openssh -- Look into this for flake templates https://nix.dev/manual/nix/2.22/command-ref/new-cli/nix3-flake-init -- Look into this for headscale https://carlosvaz.com/posts/setting-up-headscale-on-nixos/ -# Updating -`nix flake update` +## Research topics +- 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 -- allowUnfree should be enabled user side not host side (this isn't enabled at all right now for some reason???) -- Move configs for pipe mouse, open rgb, and via keyboard to hardware config and install users side from those configs -- have nfs binds and exports defined by same code -- move services from defiant into own flake -- made base domain in nas services configurable -- vscode extensions should be in own flake (make sure to add the nixpkgs.overlays in it too) -## New Features -- GNOME default monitors per hardware configuration? -- 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) -- Flake templates -- Docker parity with existing NAS on defiant -- NFS on defiant -- firefox declarative??? -- figure out steam vr things? -- Open GL? -- util functions -- openssh known hosts -- limit boot configurations to 2 on defiant -- rotate sops encryption keys periodically (and somehow sync between devices?) -- zfs email after scrubbing -- headscale server -- mastodon server -- tail scale clients -- wake on LAN \ No newline at end of file +- [ ] 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 + +## 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/build-installer.sh b/build-installer.sh new file mode 100644 index 00000000..e124091d --- /dev/null +++ b/build-installer.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +while [ $# -gt 0 ]; do + case "$1" in + --flake*|-f*) + if [[ "$1" != *=* ]]; then shift; fi + flake="${1#*=}" + ;; + # --user*|-u*) + # if [[ "$1" != *=* ]]; then shift; fi + # user="${1#*=}" + # ;; + --help|-h) + echo "--help -h: print this message" + echo "--flake -f: set the flake to build an installer for" + # echo "--user -u: set the user to install flake as on the target system" + exit 0 + ;; + *) + echo "Error: Invalid argument $1" + exit 1 + ;; + esac + shift +done + +flake=${flake:-"basic"} +user=${user:-$USER} + +nix build .#installerConfigurations.$flake.config.system.build.isoImage \ No newline at end of file diff --git a/configurations/darwin/hesperium/configuration.nix b/configurations/darwin/hesperium/configuration.nix new file mode 100644 index 00000000..f8af5c8b --- /dev/null +++ b/configurations/darwin/hesperium/configuration.nix @@ -0,0 +1,16 @@ +{...}: { + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isNormalUser = false; + }; + }; + + system.stateVersion = 5; + + nixpkgs.hostPlatform = "aarch64-darwin"; +} diff --git a/configurations/darwin/hesperium/default.nix b/configurations/darwin/hesperium/default.nix new file mode 100644 index 00000000..220a6fb7 --- /dev/null +++ b/configurations/darwin/hesperium/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./configuration.nix + ]; +} diff --git a/configurations/home-manager/default.nix b/configurations/home-manager/default.nix new file mode 100644 index 00000000..3f884814 --- /dev/null +++ b/configurations/home-manager/default.nix @@ -0,0 +1,13 @@ +{ + lib, + config, + osConfig, + ... +}: let + users = config.host.users; +in { + leyla = lib.mkIf users.leyla.isNormalUser (import ./leyla); + eve = lib.mkIf users.eve.isNormalUser (import ./eve); + ivy = lib.mkIf users.ivy.isNormalUser (import ./ivy); + git = lib.mkIf (osConfig.services.forgejo.enable or false) (import ./git); +} diff --git a/configurations/home-manager/eve/default.nix b/configurations/home-manager/eve/default.nix new file mode 100644 index 00000000..192c9807 --- /dev/null +++ b/configurations/home-manager/eve/default.nix @@ -0,0 +1,56 @@ +{osConfig, ...}: let + userConfig = osConfig.host.users.eve; +in { + imports = [ + ./packages.nix + ./gnomeconf.nix + ]; + + home = { + username = userConfig.name; + homeDirectory = osConfig.users.users.eve.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + + # Home Manager is pretty good at managing dotfiles. The primary way to manage + # plain files is through 'home.file'. + file = { + # # Building this configuration will create a copy of 'dotfiles/screenrc' in + # # the Nix store. Activating the configuration will then make '~/.screenrc' a + # # symlink to the Nix store copy. + # ".screenrc".source = dotfiles/screenrc; + + # # You can also set the file content immediately. + # ".gradle/gradle.properties".text = '' + # org.gradle.console=verbose + # org.gradle.daemon.idletimeout=3600000 + # ''; + }; + + # Home Manager can also manage your environment variables through + # 'home.sessionVariables'. If you don't want to manage your shell through Home + # Manager then you have to manually source 'hm-session-vars.sh' located at + # either + # + # ~/.nix-profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # /etc/profiles/per-user/leyla/etc/profile.d/hm-session-vars.sh + # + sessionVariables = { + # EDITOR = "emacs"; + }; + }; +} diff --git a/configurations/home-manager/eve/gnomeconf.nix b/configurations/home-manager/eve/gnomeconf.nix new file mode 100644 index 00000000..7cd3863d --- /dev/null +++ b/configurations/home-manager/eve/gnomeconf.nix @@ -0,0 +1,39 @@ +{ + osConfig, + lib, + ... +}: { + config = { + gnome = lib.mkMerge [ + { + colorScheme = "prefer-dark"; + accentColor = "slate"; + clockFormat = "24h"; + nightLight = { + enable = true; + automatic = false; + fromTime = 12.0; + toTime = 11.999999999999; + temperature = 2700; + }; + extraWindowControls = true; + extensions = { + dash-to-panel = { + enable = true; + }; + }; + } + + (lib.mkIf (osConfig.networking.hostName == "horizon") { + displayScaling = 125; + experimentalFeatures = { + scaleMonitorFramebuffer = true; + }; + }) + ]; + + dconf = { + enable = true; + }; + }; +} diff --git a/configurations/home-manager/eve/packages.nix b/configurations/home-manager/eve/packages.nix new file mode 100644 index 00000000..fb8d8a47 --- /dev/null +++ b/configurations/home-manager/eve/packages.nix @@ -0,0 +1,86 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + userConfig = osConfig.host.users.eve; + hardware = osConfig.host.hardware; +in { + config = { + nixpkgs.config = { + allowUnfree = true; + }; + + # Packages that can be installed without any extra configuration + # See https://search.nixos.org/packages for all options + home.packages = lib.lists.optionals userConfig.isDesktopUser ( + with pkgs; [ + gnomeExtensions.dash-to-panel + claude-code + ] + ); + + # Packages that need to be installed with some extra configuration + # See https://home-manager-options.extranix.com/ for all options + programs = lib.mkMerge [ + { + # Let Home Manager install and manage itself. + home-manager.enable = true; + } + (lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) { + git = { + enable = true; + settings = { + user.name = "Eve"; + user.email = "evesnrobins@gmail.com"; + init.defaultBranch = "main"; + }; + }; + + openssh = { + enable = true; + hostKeys = [ + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + } + ]; + }; + }) + (lib.mkIf config.user.isDesktopUser { + vscode = { + enable = true; + package = pkgs.vscodium; + }; + + firefox.enable = true; + bitwarden.enable = true; + discord.enable = true; + makemkv.enable = true; + signal-desktop-bin.enable = true; + steam.enable = true; + piper.enable = hardware.piperMouse.enable; + krita.enable = true; + ungoogled-chromium.enable = true; + + inkscape.enable = true; + obsidian.enable = true; + obs-studio.enable = true; + kdenlive.enable = true; + tor-browser.enable = true; + olympus.enable = true; + libreoffice.enable = true; + + claude-code.enable = osConfig.host.ai.enable; + + # Windows applications that we need to figure out how to install + guild-wars-2.enable = false; + vortex.enable = false; + dungeon-draft.enable = false; + vmware-workstation.enable = true; + }) + ]; + }; +} diff --git a/configurations/home-manager/git/default.nix b/configurations/home-manager/git/default.nix new file mode 100644 index 00000000..1ea29cc3 --- /dev/null +++ b/configurations/home-manager/git/default.nix @@ -0,0 +1,22 @@ +{osConfig, ...}: { + impermanence.fallbackPersistence.enable = false; + + home = { + username = osConfig.users.users.git.name; + homeDirectory = osConfig.users.users.git.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + }; + + programs.ssh.extraConfig = '' + AuthorizedKeysFile + /var/lib/forgejo/.ssh/authorized_keys + ''; +} diff --git a/configurations/home-manager/ivy/default.nix b/configurations/home-manager/ivy/default.nix new file mode 100644 index 00000000..48a3cae1 --- /dev/null +++ b/configurations/home-manager/ivy/default.nix @@ -0,0 +1,55 @@ +{osConfig, ...}: let + userConfig = osConfig.host.users.ivy; +in { + imports = [ + ./packages.nix + ]; + + home = { + username = userConfig.name; + homeDirectory = osConfig.users.users.ivy.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + + # Home Manager is pretty good at managing dotfiles. The primary way to manage + # plain files is through 'home.file'. + file = { + # # Building this configuration will create a copy of 'dotfiles/screenrc' in + # # the Nix store. Activating the configuration will then make '~/.screenrc' a + # # symlink to the Nix store copy. + # ".screenrc".source = dotfiles/screenrc; + + # # You can also set the file content immediately. + # ".gradle/gradle.properties".text = '' + # org.gradle.console=verbose + # org.gradle.daemon.idletimeout=3600000 + # ''; + }; + + # Home Manager can also manage your environment variables through + # 'home.sessionVariables'. If you don't want to manage your shell through Home + # Manager then you have to manually source 'hm-session-vars.sh' located at + # either + # + # ~/.nix-profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # /etc/profiles/per-user/ivy/etc/profile.d/hm-session-vars.sh + # + sessionVariables = { + # EDITOR = "emacs"; + }; + }; +} diff --git a/configurations/home-manager/ivy/packages.nix b/configurations/home-manager/ivy/packages.nix new file mode 100644 index 00000000..3c2a3d9e --- /dev/null +++ b/configurations/home-manager/ivy/packages.nix @@ -0,0 +1,73 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: { + config = { + nixpkgs.config = { + allowUnfree = true; + }; + + # Programs that need to be installed with some extra configuration + programs = lib.mkMerge [ + { + # Let Home Manager install and manage itself. + home-manager.enable = true; + } + (lib.mkIf (config.user.isDesktopUser || config.user.isTerminalUser) { + # git = { + # enable = true; + # userName = "Ivy"; + # userEmail = "ivy@example.com"; # Update this with actual email + # extraConfig.init.defaultBranch = "main"; + # }; + + openssh = { + enable = true; + hostKeys = [ + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + } + ]; + }; + }) + (lib.mkIf config.user.isDesktopUser { + vscode = { + enable = true; + package = pkgs.vscodium; + mutableExtensionsDir = false; + + profiles.default = { + enableUpdateCheck = false; + enableExtensionUpdateCheck = false; + + extraExtensions = { + # Cline extension (Claude AI assistant) + claudeDev.enable = true; + # Auto Rename Tag + autoRenameTag.enable = true; + # Live Server + liveServer.enable = true; + }; + + extensions = let + extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + in ( + with extension-pkgs.open-vsx; [ + streetsidesoftware.code-spell-checker + ] + ); + }; + }; + + firefox.enable = true; + discord.enable = true; + signal-desktop-bin.enable = true; + claude-code.enable = true; + }) + ]; + }; +} diff --git a/configurations/home-manager/leyla/dconf.nix b/configurations/home-manager/leyla/dconf.nix new file mode 100644 index 00000000..9aa61f7c --- /dev/null +++ b/configurations/home-manager/leyla/dconf.nix @@ -0,0 +1,101 @@ +{...}: { + config = { + gnome = { + extraWindowControls = true; + colorScheme = "prefer-dark"; + clockFormat = "24h"; + nightLight = { + enable = true; + automatic = false; + fromTime = 12.0; + toTime = 11.999999999999; + temperature = 2700; + }; + extensions = { + dash-to-dock = { + enable = true; + options = { + "dock-position" = "LEFT"; + "intellihide-mode" = "ALL_WINDOWS"; + "show-trash" = false; + "require-pressure-to-show" = false; + "show-mounts" = false; + }; + }; + }; + hotkeys = { + "Open Terminal" = { + binding = "t"; + command = "kgx"; + }; + "Open Firefox" = { + binding = "f"; + command = "firefox"; + }; + }; + }; + + dconf = { + enable = true; + settings = { + "org/gnome/shell" = { + favorite-apps = ["org.gnome.Nautilus.desktop" "firefox.desktop" "codium.desktop" "steam.desktop" "org.gnome.Console.desktop"]; + # app-picker-layout = + # builtins.map ( + # applications: + # lib.hm.gvariant (builtins.listToAttrs (lib.lists.imap0 (i: v: lib.attrsets.nameValuePair v (lib.hm.gvariant.mkVariant "{'position': <${i}>}")) applications)) + # ) [ + # [ + # "org.gnome.Nautilus.desktop" + # "bitwarden.desktop" + # "firefox.desktop" + # "torbrowser.desktop" + # "chromium-browser.desktop" + # "codium.desktop" + # "idea-community.desktop" + # "org.gnome.TextEditor.desktop" + # "dbeaver.desktop" + # "bruno.desktop" + # "anki.desktop" + # "obsidian.desktop" + # "signal-desktop.desktop" + # "discord.desktop" + # "gimp.desktop" + # "org.inkscape.Inkscape.desktop" + # "org.kde.krita.desktop" + # "davinci-resolve.desktop" + # "com.obsproject.Studio.desktop" + # "org.freecad.FreeCAD.desktop" + # "makemkv.desktop" + # "easytag.desktop" + # "transmission-gtk.desktop" + # ] + # [ + # "SteamVR.desktop" + # "Beat Saber.desktop" + # "Noun Town.desktop" + # "WEBFISHING.desktop" + # "Factorio.desktop" + # ] + # [ + # "org.gnome.Settings.desktop" + # "org.gnome.SystemMonitor.desktop" + # "org.gnome.Snapshot.desktop" + # "org.gnome.Usage.desktop" + # "org.gnome.DiskUtility.desktop" + # "org.gnome.Evince.desktop" + # "org.gnome.fonts.desktop" + # "noisetorch.desktop" + # "nvidia-settings.desktop" + # "OpnRGB.desktop" + # "org.freedesktop.Piper.desktop" + # "via-nativia.desktop" + # "protonvpn-app.desktop" + # "simple-scan.desktop" + # ] + # ]; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/default.nix b/configurations/home-manager/leyla/default.nix new file mode 100644 index 00000000..8a377549 --- /dev/null +++ b/configurations/home-manager/leyla/default.nix @@ -0,0 +1,95 @@ +{ + pkgs, + config, + osConfig, + ... +}: { + imports = [ + ./packages + ./i18n.nix + ./impermanence.nix + ./dconf.nix + ]; + + config = { + impermanence.enable = osConfig.host.impermanence.enable; + + # Home Manager needs a bit of information about you and the paths it should + # manage. + home = { + username = osConfig.host.users.leyla.name; + homeDirectory = osConfig.users.users.leyla.home; + + # This value determines the Home Manager release that your configuration is + # compatible with. This helps avoid breakage when a new Home Manager release + # introduces backwards incompatible changes. + # + # You should not change this value, even if you update Home Manager. If you do + # want to update the value, then make sure to first check the Home Manager + # release notes. + stateVersion = "23.11"; # Please read the comment before changing. + + # Home Manager is pretty good at managing dotfiles. The primary way to manage + # plain files is through 'home.file'. + file = { + # # Building this configuration will create a copy of 'dotfiles/screenrc' in + # # the Nix store. Activating the configuration will then make '~/.screenrc' a + # # symlink to the Nix store copy. + # ".screenrc".source = dotfiles/screenrc; + + # # You can also set the file content immediately. + # ".gradle/gradle.properties".text = '' + # org.gradle.console=verbose + # org.gradle.daemon.idletimeout=3600000 + # ''; + "${config.xdg.configHome}/user-dirs.dirs" = { + force = true; + text = '' + # This file is written by xdg-user-dirs-update + # If you want to change or add directories, just edit the line you're + # interested in. All local changes will be retained on the next run. + # Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped + # homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an + # absolute path. No other format is supported. + # + XDG_DESKTOP_DIR="$HOME/desktop" + XDG_DOWNLOAD_DIR="$HOME/downloads" + XDG_DOCUMENTS_DIR="$HOME/documents" + XDG_TEMPLATES_DIR="$HOME/documents/templates" + XDG_MUSIC_DIR="$HOME/documents/music" + XDG_PICTURES_DIR="$HOME/documents/photos" + XDG_VIDEOS_DIR="$HOME/documents/videos" + XDG_PUBLICSHARE_DIR="$HOME/documents/public" + ''; + }; + }; + + keyboard.layout = "us,it,de"; + + # Home Manager can also manage your environment variables through + # 'home.sessionVariables'. If you don't want to manage your shell through Home + # Manager then you have to manually source 'hm-session-vars.sh' located at + # either + # + # ~/.nix-profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh + # + # or + # + # /etc/profiles/per-user/leyla/etc/profile.d/hm-session-vars.sh + # + sessionVariables = { + # EDITOR = "emacs"; + }; + }; + + # TODO: move this into a fonts module + home.packages = with pkgs; [ + aileron + ]; + fonts.fontconfig.enable = true; + }; +} diff --git a/configurations/home-manager/leyla/i18n.nix b/configurations/home-manager/leyla/i18n.nix new file mode 100644 index 00000000..f12cd95a --- /dev/null +++ b/configurations/home-manager/leyla/i18n.nix @@ -0,0 +1,12 @@ +{...}: { + i18n = { + defaultLocale = "en_IE.UTF-8"; + + extraLocaleSettings = { + # LC_ADDRESS = "en_IE.UTF-8"; # lets just get used to this one now + # LC_TELEPHONE = "en_IE.UTF-8"; # lets just get used to this one now + LC_MONETARY = "en_US.UTF-8"; # to be changed once I move + LC_PAPER = "en_US.UTF-8"; # convenient for american printers until I move + }; + }; +} diff --git a/configurations/home-manager/leyla/impermanence.nix b/configurations/home-manager/leyla/impermanence.nix new file mode 100644 index 00000000..ce81c818 --- /dev/null +++ b/configurations/home-manager/leyla/impermanence.nix @@ -0,0 +1,20 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.impermanence.enable) { + home.persistence."/persist/home/leyla" = { + directories = [ + "desktop" + "downloads" + "documents" + ]; + files = [ + ".bash_history" # keep shell history around + "${config.xdg.dataHome}/recently-used.xbel" # gnome recently viewed files + ]; + allowOther = true; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/default.nix b/configurations/home-manager/leyla/packages/default.nix new file mode 100644 index 00000000..50cc175c --- /dev/null +++ b/configurations/home-manager/leyla/packages/default.nix @@ -0,0 +1,93 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + hardware = osConfig.host.hardware; +in { + imports = [ + ./vscode + ./firefox + ./direnv.nix + ./openssh.nix + ./git.nix + ./makemkv.nix + ]; + + config = lib.mkMerge [ + { + programs = lib.mkMerge [ + { + # Let Home Manager install and manage itself. + home-manager.enable = true; + } + (lib.mkIf (config.user.isTerminalUser || config.user.isDesktopUser) { + bash.enable = true; + git.enable = true; + openssh.enable = true; + }) + (lib.mkIf config.user.isDesktopUser { + bitwarden.enable = true; + obs-studio.enable = hardware.graphicsAcceleration.enable; + qbittorrent.enable = true; + prostudiomasters.enable = true; + protonvpn-gui.enable = true; + dbeaver-bin.enable = true; + bruno.enable = true; + piper.enable = hardware.piperMouse.enable; + proxmark3.enable = true; + openrgb.enable = hardware.openRGB.enable; + via.enable = hardware.viaKeyboard.enable; + claude-code.enable = osConfig.host.ai.enable; + davinci-resolve.enable = hardware.graphicsAcceleration.enable; + mfoc.enable = true; + }) + (lib.mkIf (hardware.directAccess.enable && config.user.isDesktopUser) { + anki.enable = true; + makemkv.enable = true; + discord.enable = true; + signal-desktop-bin.enable = true; + calibre.enable = true; + obsidian.enable = true; + jetbrains.idea-community.enable = true; + vscode.enable = true; + firefox.enable = true; + steam.enable = true; + krita.enable = true; + ungoogled-chromium.enable = true; + libreoffice.enable = true; + mapillary-uploader.enable = true; + inkscape.enable = true; + gimp.enable = true; + freecad.enable = true; + onionshare.enable = true; + pdfarranger.enable = true; + picard.enable = true; + qflipper.enable = true; + openvpn.enable = true; + noisetorch.enable = true; + tor-browser.enable = true; + gdx-liftoff.enable = true; + # polycule package is now working with Flutter 3.29 + polycule.enable = true; + }) + ]; + } + (lib.mkIf config.user.isTerminalUser { + home.packages = with pkgs; [ + # command line tools + sox + yt-dlp + ffmpeg + imagemagick + ]; + }) + (lib.mkIf config.user.isDesktopUser { + nixpkgs.config = { + allowUnfree = true; + }; + }) + ]; +} diff --git a/configurations/home-manager/leyla/packages/direnv.nix b/configurations/home-manager/leyla/packages/direnv.nix new file mode 100644 index 00000000..038c1499 --- /dev/null +++ b/configurations/home-manager/leyla/packages/direnv.nix @@ -0,0 +1,22 @@ +{ + lib, + config, + osConfig, + ... +}: let + userConfig = osConfig.host.users.leyla; +in { + config = lib.mkIf userConfig.isDesktopUser { + programs = { + direnv = { + enable = true; + enableBashIntegration = true; + nix-direnv.enable = true; + config = { + global.hide_env_diff = true; + whitelist.exact = ["${config.home.homeDirectory}/documents/code/nix-config"]; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/firefox/bookmarks.nix b/configurations/home-manager/leyla/packages/firefox/bookmarks.nix new file mode 100644 index 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 new file mode 100644 index 00000000..499e37b1 --- /dev/null +++ b/configurations/home-manager/leyla/packages/git.nix @@ -0,0 +1,13 @@ +{...}: { + config = { + programs = { + git = { + settings = { + user.name = "Leyla Becker"; + user.email = "git@jan-leila.com"; + init.defaultBranch = "main"; + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/makemkv.nix b/configurations/home-manager/leyla/packages/makemkv.nix new file mode 100644 index 00000000..ee719554 --- /dev/null +++ b/configurations/home-manager/leyla/packages/makemkv.nix @@ -0,0 +1,17 @@ +{ + config, + inputs, + ... +}: { + config = { + sops.secrets = { + "application-keys/makemkv" = { + sopsFile = "${inputs.secrets}/application-keys.yaml"; + }; + }; + programs.makemkv = { + appKeyFile = config.sops.placeholder."application-keys/makemkv"; + destinationDir = "/home/leyla/downloads/makemkv"; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/openssh.nix b/configurations/home-manager/leyla/packages/openssh.nix new file mode 100644 index 00000000..91aec11d --- /dev/null +++ b/configurations/home-manager/leyla/packages/openssh.nix @@ -0,0 +1,23 @@ +{ + config, + osConfig, + ... +}: { + config = { + programs = { + openssh = { + authorizedKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHeItmt8TRW43uNcOC+eIurYC7Eunc0V3LGocQqLaYj leyla@horizon" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIILimFIW2exEH/Xo7LtXkqgE04qusvnPNpPWSCeNrFkP leyla@defiant" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKBiZkg1c2aaNHiieBX4cEziqvJVj9pcDfzUrKU/mO0I leyla@twilight" + ]; + hostKeys = [ + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + } + ]; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/vscode/default.nix b/configurations/home-manager/leyla/packages/vscode/default.nix new file mode 100644 index 00000000..36168b20 --- /dev/null +++ b/configurations/home-manager/leyla/packages/vscode/default.nix @@ -0,0 +1,136 @@ +{ + lib, + pkgs, + config, + osConfig, + ... +}: let + nix-development-enabled = osConfig.host.nix-development.enable; + ai-tooling-enabled = osConfig.host.ai.enable; +in { + imports = [ + ./user-words.nix + ]; + + config = lib.mkIf config.user.isDesktopUser { + programs = { + bash.shellAliases = { + code = "codium"; + }; + + vscode = { + package = pkgs.vscodium; + + mutableExtensionsDir = false; + + profiles.default = { + enableUpdateCheck = false; + enableExtensionUpdateCheck = false; + + userSettings = lib.mkMerge [ + { + "javascript.updateImportsOnFileMove.enabled" = "always"; + "editor.tabSize" = 2; + "editor.insertSpaces" = false; + # "terminal.integrated.fontFamily" = "'Droid Sans Mono', 'monospace', monospace"; + } + ]; + + extraExtensions = { + # vs code feel + oneDark.enable = true; + atomKeybindings.enable = true; + openRemoteSsh.enable = true; + # openDyslexicFont.enable = false; + + # html development + autoRenameTag.enable = true; + liveServer.enable = true; + + # js development + es7ReactJsSnippets.enable = true; + tauriVscode.enable = true; + vscodeEslint.enable = true; + vscodeJest.enable = true; + vitest.enable = true; + vscodeStandard.enable = true; + vscodeStylelint.enable = true; + + nearley.enable = true; + + # astro development + vscodeMdx.enable = true; + astroVscode.enable = true; + + # nix development + alejandra.enable = nix-development-enabled; + nixIde.enable = nix-development-enabled; + + # go development + go.enable = true; + + # rust development + rustAnalyzer.enable = true; + + # claude development + claudeDev = lib.mkIf ai-tooling-enabled { + enable = true; + mcp = { + nixos = { + enable = true; + autoApprove = { + nixos_search = true; + nixos_info = true; + home_manager_search = true; + home_manager_info = true; + darwin_search = true; + darwin_info = true; + nixos_flakes_search = true; + }; + }; + eslint = { + enable = true; + autoApprove = { + lint-files = true; + }; + }; + vitest = { + enable = true; + autoApprove = { + list_tests = true; + run_tests = true; + analyze_coverage = true; + set_project_root = true; + }; + }; + sleep = { + enable = true; + timeout = 18000; # 5 hours to match claude codes timeout + autoApprove = { + sleep = true; + }; + }; + }; + }; + + # misc extensions + evenBetterToml.enable = true; + direnv.enable = config.programs.direnv.enable; + conventionalCommits.enable = true; + }; + + extensions = let + extension-pkgs = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + in ( + with extension-pkgs.open-vsx; [ + # vs code feel extensions + streetsidesoftware.code-spell-checker + streetsidesoftware.code-spell-checker-german + streetsidesoftware.code-spell-checker-italian + ] + ); + }; + }; + }; + }; +} diff --git a/configurations/home-manager/leyla/packages/vscode/user-words.nix b/configurations/home-manager/leyla/packages/vscode/user-words.nix new file mode 100644 index 00000000..bb99bbca --- /dev/null +++ b/configurations/home-manager/leyla/packages/vscode/user-words.nix @@ -0,0 +1,126 @@ +{ + pkgs, + lib, + ... +}: { + config.programs.vscode.profiles.default.userSettings = { + "cSpell.userWords" = [ + "leyla" + ]; + + "cSpell.languageSettings" = [ + { + "languageId" = "nix"; + "locale" = "*"; + "dictionaries" = [ + "applications" + "ai-words" + "nix-words" + + # We need to include all other dictionaries in the nix language settings because they exist in this file + # TODO: see if there is a way to make this only apply for this file + "js-words" + ]; + } + { + "languageId" = "javascript,typescript,js,ts"; + "locale" = "*"; + "dictionaries" = [ + "js-words" + ]; + } + ]; + + "cSpell.customDictionaries" = { + applications = { + name = "applications"; + description = "application names"; + path = pkgs.writeText "applications.txt" (lib.strings.concatLines [ + "ollama" + "syncthing" + "immich" + "sonos" + "makemkv" + "hass" + "qbittorent" + "prostudiomasters" + "protonmail" + "pulseaudio" + ]); + }; + + ai-words = { + name = "ai-words"; + description = "common words used for ai development"; + path = pkgs.writeText "ai-words.txt" (lib.strings.concatLines [ + "ollama" + "deepseek" + "qwen" + ]); + }; + + nix-words = { + name = "nix-words"; + description = "words used in nix configurations"; + path = pkgs.writeText "nix-words.txt" (lib.strings.concatLines [ + "pname" + "direnv" + "tmpfiles" + "Networkd" + "networkmanager" + "dialout" + "adbusers" + "authkey" + "netdevs" + "atomix" + "geary" + "gedit" + "hitori" + "iagno" + "alsa" + "timezoned" + "pipewire" + "rtkit" + "disko" + "ashift" + "autotrim" + "canmount" + "mountpoint" + "xattr" + "acltype" + "relatime" + "keyformat" + "keylocation" + "vdevs" + + # codium extensions + "akamud" + "onedark" + "jeanp" + "dsznajder" + "dbaeumer" + "orta" + "tauri" + "unifiedjs" + "tamasfe" + "pinage" + "jnoortheen" + "kamadorueda" + "karyfoundation" + "nearley" + + # nix.optimise is spelled wrong + "optimise" + ]); + }; + + js-words = { + name = "js-words"; + description = "words used in js development"; + path = pkgs.writeText "js-words.txt" (lib.strings.concatLines [ + "webdav" + ]); + }; + }; + }; +} diff --git a/configurations/installer/basic/configuration.nix b/configurations/installer/basic/configuration.nix new file mode 100644 index 00000000..4e63727d --- /dev/null +++ b/configurations/installer/basic/configuration.nix @@ -0,0 +1,19 @@ +{ + lib, + pkgs, + modulesPath, + ... +}: { + imports = [(modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix")]; + + systemd.services.sshd.wantedBy = pkgs.lib.mkForce ["multi-user.target"]; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AaAeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee username@host" + ]; + + isoImage.squashfsCompression = "gzip -Xcompression-level 1"; + + networking.hostName = "installer"; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/configurations/installer/basic/default.nix b/configurations/installer/basic/default.nix new file mode 100644 index 00000000..220a6fb7 --- /dev/null +++ b/configurations/installer/basic/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./configuration.nix + ]; +} diff --git a/configurations/nixos/defiant/configuration.nix b/configurations/nixos/defiant/configuration.nix new file mode 100644 index 00000000..e2f9401f --- /dev/null +++ b/configurations/nixos/defiant/configuration.nix @@ -0,0 +1,413 @@ +# server nas +{ + inputs, + config, + ... +}: { + sops.secrets = { + "vpn-keys/tailscale-authkey/defiant" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + "vpn-keys/proton-wireguard/defiant-p2p" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + mode = "0640"; + owner = "root"; + group = "systemd-network"; + }; + "services/zfs_smtp_token" = { + sopsFile = "${inputs.secrets}/defiant-services.yaml"; + }; + "services/paperless_password" = { + sopsFile = "${inputs.secrets}/defiant-services.yaml"; + mode = "0700"; + owner = "paperless"; + group = "paperless"; + }; + }; + + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + }; + impermanence.enable = true; + storage = { + enable = true; + encryption = true; + notifications = { + enable = true; + host = "smtp.protonmail.ch"; + port = 587; + to = "leyla@jan-leila.com"; + user = "noreply@jan-leila.com"; + tokenFile = config.sops.secrets."services/zfs_smtp_token".path; + }; + pool = { + # We are having to boot off of the nvm cache drive because I cant figure out how to boot via the HBA + bootDrives = ["nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F"]; + vdevs = [ + [ + "ata-ST18000NE000-3G6101_ZVTCXVEB" + "ata-ST18000NE000-3G6101_ZVTCXWSC" + "ata-ST18000NE000-3G6101_ZVTD10EH" + "ata-ST18000NT001-3NF101_ZVTE0S3Q" + "ata-ST18000NT001-3NF101_ZVTEF27J" + "ata-ST18000NE000-3G6101_ZVTJ7359" + ] + [ + "ata-ST4000NE001-2MA101_WS2275P3" + "ata-ST4000NE001-2MA101_WS227B9F" + "ata-ST4000NE001-2MA101_WS227CEW" + "ata-ST4000NE001-2MA101_WS227CYN" + "ata-ST4000NE001-2MA101_WS23TBWV" + "ata-ST4000NE001-2MA101_WS23TC5F" + ] + ]; + cache = [ + "nvme-Samsung_SSD_990_PRO_4TB_S7KGNU0X907881F" + ]; + }; + }; + network_storage = { + enable = true; + directories = [ + { + folder = "leyla_documents"; + user = "leyla"; + group = "leyla"; + bind = "/home/leyla/documents"; + } + { + folder = "eve_documents"; + user = "eve"; + group = "eve"; + } + { + folder = "users_documents"; + user = "root"; + group = "users"; + } + { + folder = "media"; + user = "jellyfin"; + group = "jellyfin_media"; + bind = config.services.jellyfin.media_directory; + } + ]; + nfs = { + enable = true; + directories = ["leyla_documents" "eve_documents" "users_documents" "media"]; + }; + }; + }; + + systemd.network = { + enable = true; + + netdevs = { + "10-bond0" = { + netdevConfig = { + Kind = "bond"; + Name = "bond0"; + }; + bondConfig = { + Mode = "802.3ad"; + TransmitHashPolicy = "layer3+4"; + }; + }; + + "20-wg0" = { + netdevConfig = { + Kind = "wireguard"; + Name = "wg0"; + }; + wireguardConfig = { + PrivateKeyFile = config.sops.secrets."vpn-keys/proton-wireguard/defiant-p2p".path; + ListenPort = 51820; + }; + wireguardPeers = [ + { + PublicKey = "rRO6yJim++Ezz6scCLMaizI+taDjU1pzR2nfW6qKbW0="; + Endpoint = "185.230.126.146:51820"; + # Allow all traffic but use policy routing to prevent system-wide VPN + AllowedIPs = ["0.0.0.0/0"]; + } + ]; + }; + }; + networks = { + "40-bond0" = { + matchConfig.Name = "bond0"; + linkConfig = { + RequiredForOnline = "degraded-carrier"; + RequiredFamilyForOnline = "any"; + }; + networkConfig.DHCP = "yes"; + + address = [ + "192.168.1.10/32" + ]; + + # Set lower priority for default gateway to allow WireGuard interface binding + routes = [ + { + Destination = "0.0.0.0/0"; + Gateway = "192.168.1.1"; + Metric = 100; + } + ]; + dns = ["192.168.1.1"]; + }; + + "50-wg0" = { + matchConfig.Name = "wg0"; + networkConfig = { + DHCP = "no"; + }; + address = [ + "10.2.0.2/32" + ]; + # Configure routing for application binding + routingPolicyRules = [ + { + # Route traffic from VPN interface through VPN table + From = "10.2.0.2/32"; + Table = 200; + Priority = 100; + } + ]; + routes = [ + { + # Direct route to VPN gateway + Destination = "10.2.0.1/32"; + Scope = "link"; + } + { + # Route VPN subnet through VPN gateway in custom table + Destination = "10.2.0.0/16"; + Gateway = "10.2.0.1"; + Table = 200; + } + { + # Route all traffic through VPN gateway in custom table + Destination = "0.0.0.0/0"; + Gateway = "10.2.0.1"; + Table = 200; + } + ]; + }; + }; + }; + + # limit arc usage to 50gb because ollama doesn't play nice with zfs using up all of the memory + boot.kernelParams = ["zfs.zfs_arc_max=53687091200"]; + + # Enable policy routing and source routing for application-specific VPN binding + boot.kernel.sysctl = { + "net.ipv4.conf.all.rp_filter" = 2; + "net.ipv4.conf.default.rp_filter" = 2; + "net.ipv4.conf.wg0.rp_filter" = 2; + }; + + services = { + # PostgreSQL database server + postgresql = { + enable = true; + adminUsers = ["leyla"]; + }; + + # temp enable desktop environment for setup + # Enable the X11 windowing system. + xserver.enable = true; + + # Enable the GNOME Desktop Environment. + displayManager = { + gdm.enable = true; + }; + desktopManager = { + gnome.enable = true; + }; + + # Enable new reverse proxy system + reverseProxy = { + enable = true; + openFirewall = true; + acme = { + enable = true; + email = "jan-leila@protonmail.com"; + }; + }; + + ollama = { + enable = true; + exposePort = true; + + acceleration = false; + + environmentVariables = { + OLLAMA_KEEP_ALIVE = "24h"; + }; + + loadModels = [ + # conversation models + "llama3.1:8b" + "deepseek-r1:8b" + "deepseek-r1:32b" + "deepseek-r1:70b" + + # auto complete models + "qwen2.5-coder:1.5b-base" + "qwen2.5-coder:7b" + "deepseek-coder:6.7b" + "deepseek-coder:33b" + + # agent models + "qwen3:8b" + "qwen3:32b" + "qwen3:235b-a22b" + + "qwen3-coder:30b" + "qwen3-coder:30b-a3b-fp16" + + # embedding models + "nomic-embed-text:latest" + ]; + }; + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/defiant".path; + useRoutingFeatures = "server"; + extraUpFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + "--accept-dns=false" + ]; + extraSetFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + "--accept-dns=false" + ]; + }; + + syncthing.enable = true; + + fail2ban.enable = true; + + jellyfin = { + enable = true; + domain = "media.jan-leila.com"; + extraDomains = ["jellyfin.jan-leila.com"]; + }; + + immich = { + enable = true; + domain = "photos.jan-leila.com"; + }; + + forgejo = { + enable = true; + reverseProxy.domain = "git.jan-leila.com"; + }; + + searx = { + enable = true; + domain = "search.jan-leila.com"; + }; + + actual = { + enable = true; + domain = "budget.jan-leila.com"; + }; + + home-assistant = { + enable = true; + domain = "home.jan-leila.com"; + openFirewall = true; + postgres.enable = true; + + extensions = { + sonos.enable = true; + jellyfin.enable = true; + wyoming.enable = false; # Temporarily disabled due to dependency conflict in wyoming-piper + }; + }; + + paperless = { + enable = true; + domain = "documents.jan-leila.com"; + passwordFile = config.sops.secrets."services/paperless_password".path; + }; + + panoramax = { + enable = false; + 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"; + openFirewall = true; + webuiPort = 8084; + }; + + sonarr = { + enable = true; + openFirewall = true; + }; + radarr = { + enable = true; + openFirewall = true; + }; + bazarr = { + enable = true; + openFirewall = true; + }; + lidarr = { + enable = true; + openFirewall = true; + }; + jackett = { + enable = true; + openFirewall = true; + }; + flaresolverr = { + enable = true; + openFirewall = true; + }; + }; + + # disable computer sleeping + systemd.targets = { + sleep.enable = false; + suspend.enable = false; + hibernate.enable = false; + hybrid-sleep.enable = false; + }; + services.displayManager.gdm.autoSuspend = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/defiant/default.nix b/configurations/nixos/defiant/default.nix new file mode 100644 index 00000000..30139466 --- /dev/null +++ b/configurations/nixos/defiant/default.nix @@ -0,0 +1,8 @@ +# server nas +{...}: { + imports = [ + ./hardware-configuration.nix + ./configuration.nix + ./packages.nix + ]; +} diff --git a/configurations/nixos/defiant/hardware-configuration.nix b/configurations/nixos/defiant/hardware-configuration.nix new file mode 100644 index 00000000..d4a638b0 --- /dev/null +++ b/configurations/nixos/defiant/hardware-configuration.nix @@ -0,0 +1,63 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot = { + initrd = { + availableKernelModules = ["xhci_pci" "aacraid" "ahci" "usbhid" "nvme" "usb_storage" "sd_mod"]; + kernelModules = []; + }; + kernelModules = ["kvm-amd"]; + extraModulePackages = []; + + # Bootloader. + loader = { + systemd-boot.enable = true; + efi = { + canTouchEfiVariables = true; + efiSysMountPoint = "/boot"; + }; + }; + supportedFilesystems = ["zfs"]; + + zfs.extraPools = ["rpool"]; + }; + + networking = { + hostName = "defiant"; # Define your hostname. + hostId = "c51763d6"; + useNetworkd = true; + }; + + systemd.network = { + enable = true; + + networks = { + "30-eno1" = { + matchConfig.Name = "eno1"; + networkConfig.Bond = "bond0"; + }; + "30-eno2" = { + matchConfig.Name = "eno2"; + networkConfig.Bond = "bond0"; + }; + }; + }; + + networking.networkmanager.enable = true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware = { + # TODO: hardware graphics + cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + }; +} diff --git a/configurations/nixos/defiant/packages.nix b/configurations/nixos/defiant/packages.nix new file mode 100644 index 00000000..45780b02 --- /dev/null +++ b/configurations/nixos/defiant/packages.nix @@ -0,0 +1,9 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [ + ffsubsync + sox + yt-dlp + ffmpeg + imagemagick + ]; +} diff --git a/configurations/nixos/emergent/configuration.nix b/configurations/nixos/emergent/configuration.nix new file mode 100644 index 00000000..6121069f --- /dev/null +++ b/configurations/nixos/emergent/configuration.nix @@ -0,0 +1,167 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page, on +# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). +{ + lib, + pkgs, + ... +}: { + imports = [ + ./nvidia-drivers.nix + ]; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # networking.hostName = "nixos"; # Define your hostname. + # Pick only one of the below networking options. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + # networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. + + # Set your time zone. + # time.timeZone = "Europe/Amsterdam"; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + # Select internationalisation properties. + # i18n.defaultLocale = "en_US.UTF-8"; + # console = { + # font = "Lat2-Terminus16"; + # keyMap = "us"; + # useXkbConfig = true; # use xkb.options in tty. + # }; + + # Enable the X11 windowing system. + services.xserver.enable = true; + # Enable wacom touchscreen device + services.xserver.wacom.enable = true; + + # installed opentabletdriver + hardware.opentabletdriver.enable = true; + hardware.keyboard.qmk.enable = true; + + # Enable the GNOME Desktop Environment. + services.displayManager.gdm.enable = true; + services.desktopManager.gnome.enable = true; + + host = { + ai.enable = true; + users = { + eve = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + }; + hardware = { + piperMouse.enable = true; + }; + + storage = { + enable = true; + pool = { + mode = ""; + drives = ["wwn-0x5000039fd0cf05eb"]; + }; + }; + }; + + services.tailscale.enable = true; + # We were having weird build errors so this is disabled right now + # error: The option `devices.emergent.folders.eve_records.path' was accessed but has no value defined. Try setting the option + services.syncthing.enable = false; + + # Configure keymap in X11 + # services.xserver.xkb.layout = "us"; + # services.xserver.xkb.options = "eurosign:e,caps:escape"; + + # Enable CUPS to print documents. + # services.printing.enable = true; + + # Enable sound. + # services.pulseaudio.enable = true; + # OR + # services.pipewire = { + # enable = true; + # pulse.enable = true; + # }; + + # Enable touchpad support (enabled default in most desktopManager). + # services.libinput.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + # users.users.alice = { + # isNormalUser = true; + # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + # packages = with pkgs; [ + # tree + # ]; + # }; + + # programs.firefox.enable = true; + + nixpkgs.config.allowUnfree = true; + + # Packages that can be installed without any extra configuration + # See https://search.nixos.org/packages for all options + environment.systemPackages = with pkgs; [ + wget + ]; + + # Packages that need to be installed with some extra configuration + # See https://search.nixos.org/options for all options + programs = {}; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + # programs.mtr.enable = true; + # programs.gnupg.agent = { + # enable = true; + # enableSSHSupport = true; + # }; + + # List services that you want to enable: + + # Enable the OpenSSH daemon. + # services.openssh.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + networking = { + networkmanager.enable = true; + useDHCP = lib.mkDefault true; + hostId = "7e35eb97"; # arbitrary id number generated via this command: `head -c4 /dev/urandom | od -A none -t x4` + hostName = "emergent"; # Define your hostname. + }; + + # Copy the NixOS configuration file and link it from the resulting system + # (/run/current-system/configuration.nix). This is useful in case you + # accidentally delete configuration.nix. + # system.copySystemConfiguration = true; + + # This option defines the first version of NixOS you have installed on this particular machine, + # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. + # + # Most users should NEVER change this value after the initial install, for any reason, + # even if you've upgraded your system to a new NixOS release. + # + # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, + # so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how + # to actually do that. + # + # This value being lower than the current NixOS release does NOT mean your system is + # out of date, out of support, or vulnerable. + # + # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, + # and migrated your data accordingly. + # + # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . + system.stateVersion = "25.05"; # Did you read the comment? +} diff --git a/configurations/nixos/emergent/default.nix b/configurations/nixos/emergent/default.nix new file mode 100644 index 00000000..452334a2 --- /dev/null +++ b/configurations/nixos/emergent/default.nix @@ -0,0 +1,7 @@ +# evs desktop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + ]; +} diff --git a/configurations/nixos/emergent/hardware-configuration.nix b/configurations/nixos/emergent/hardware-configuration.nix new file mode 100644 index 00000000..4e131499 --- /dev/null +++ b/configurations/nixos/emergent/hardware-configuration.nix @@ -0,0 +1,32 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + pkgs, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = []; + boot.extraModulePackages = []; + + swapDevices = []; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp42s0.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp4s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/emergent/nvidia-drivers.nix b/configurations/nixos/emergent/nvidia-drivers.nix new file mode 100644 index 00000000..b532446d --- /dev/null +++ b/configurations/nixos/emergent/nvidia-drivers.nix @@ -0,0 +1,51 @@ +{ + config, + lib, + pkgs, + ... +}: { + # Enable OpenGL + hardware.graphics = { + enable = true; + }; + + # Load nvidia driver for Xorg and Wayland + services = { + xserver = { + # Load nvidia driver for Xorg and Wayland + videoDrivers = ["nvidia"]; + }; + # Use X instead of wayland + displayManager.gdm.wayland = false; + }; + + hardware.nvidia = { + # Modesetting is required. + modesetting.enable = true; + + # Nvidia power management. Experimental, and can cause sleep/suspend to fail. + # Enable this if you have graphical corruption issues or application crashes after waking + # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead + # of just the bare essentials. + powerManagement.enable = true; + + # Fine-grained power management. Turns off GPU when not in use. + # Experimental and only works on modern Nvidia GPUs (Turing or newer). + powerManagement.finegrained = false; + + # Use the NVidia open source kernel module (not to be confused with the + # independent third-party "nouveau" open source driver). + # Support is limited to the Turing and later architectures. Full list of + # supported GPUs is at: + # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus + # Only available from driver 515.43.04+ + open = true; + + # Enable the Nvidia settings menu, + # accessible via `nvidia-settings`. + nvidiaSettings = true; + + # Optionally, you may need to select the appropriate driver version for your specific GPU. + package = config.boot.kernelPackages.nvidiaPackages.stable; + }; +} diff --git a/configurations/nixos/horizon/configuration.nix b/configurations/nixos/horizon/configuration.nix new file mode 100644 index 00000000..0e86fe7c --- /dev/null +++ b/configurations/nixos/horizon/configuration.nix @@ -0,0 +1,158 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: { + imports = [ + inputs.nixos-hardware.nixosModules.framework-11th-gen-intel + ]; + + nixpkgs.config.allowUnfree = true; + + boot = { + initrd = { + availableKernelModules = ["usb_storage" "sd_mod"]; + }; + kernelModules = ["sg"]; + + # Bootloader. + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + }; + + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isDesktopUser = true; + ivy.isDesktopUser = true; + }; + + hardware = { + directAccess.enable = true; + }; + + ai = { + enable = true; + models = { + "Llama 3.1 8B" = { + model = "llama3.1:8b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + "Deepseek Coder:6.7B" = { + model = "deepseek-coder:6.7b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + "Deepseek Coder:33B" = { + model = "deepseek-coder:33b"; + roles = ["chat" "edit" "apply"]; + apiBase = "http://defiant:11434"; + }; + + "Deepseek r1:8B" = { + model = "deepseek-r1:8b"; + roles = ["chat"]; + apiBase = "http://defiant:11434"; + }; + + "Deepseek r1:32B" = { + model = "deepseek-r1:32b"; + roles = ["chat"]; + apiBase = "http://defiant:11434"; + }; + + "qwen2.5-coder:1.5b-base" = { + model = "qwen2.5-coder:1.5b-base"; + roles = ["autocomplete"]; + apiBase = "http://defiant:11434"; + }; + + "nomic-embed-text:latest" = { + model = "nomic-embed-text:latest"; + roles = ["embed"]; + apiBase = "http://defiant:11434"; + }; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + cachefilesd + webtoon-dl + ]; + services.cachefilesd.enable = true; + + programs = { + adb.enable = true; + }; + + networking = { + networkmanager.enable = true; + hostName = "horizon"; # Define your hostname. + }; + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + + hardware = { + graphics.enable = true; + }; + + sops.secrets = { + "vpn-keys/tailscale-authkey/horizon" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + }; + + services = { + # sudo fprintd-enroll + fprintd = { + enable = true; + }; + # firmware update tool + fwupd = { + enable = true; + }; + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/horizon".path; + useRoutingFeatures = "client"; + }; + + syncthing.enable = true; + + ollama = { + enable = true; + loadModels = [ + "llama3.1:8b" + ]; + }; + }; + + # Enable network-online.target for better network dependency handling + systemd.services.NetworkManager-wait-online.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + # services.xserver.libinput.enable = true; + + # Open ports in the firewall. + # networking.firewall.allowedTCPPorts = [ ... ]; + # networking.firewall.allowedUDPPorts = [ ... ]; + # Or disable the firewall altogether. + # networking.firewall.enable = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/horizon/default.nix b/configurations/nixos/horizon/default.nix new file mode 100644 index 00000000..b916d828 --- /dev/null +++ b/configurations/nixos/horizon/default.nix @@ -0,0 +1,8 @@ +# leyla laptop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + # ./network-mount.nix + ]; +} diff --git a/configurations/nixos/horizon/hardware-configuration.nix b/configurations/nixos/horizon/hardware-configuration.nix new file mode 100644 index 00000000..cec49141 --- /dev/null +++ b/configurations/nixos/horizon/hardware-configuration.nix @@ -0,0 +1,45 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["xhci_pci" "thunderbolt" "nvme"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-intel"]; + boot.extraModulePackages = []; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/866d422b-f816-4ad9-9846-791839cb9337"; + fsType = "ext4"; + }; + + "/boot" = { + device = "/dev/disk/by-uuid/E138-65B5"; + fsType = "vfat"; + }; + }; + + swapDevices = [ + {device = "/dev/disk/by-uuid/be98e952-a072-4c3a-8c12-69500b5a2fff";} + ]; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.tailscale0.useDHCP = lib.mkDefault true; + # networking.interfaces.wlp170s0.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/horizon/network-mount.nix b/configurations/nixos/horizon/network-mount.nix new file mode 100644 index 00000000..fde16f5c --- /dev/null +++ b/configurations/nixos/horizon/network-mount.nix @@ -0,0 +1,76 @@ +{...}: { + boot.supportedFilesystems = ["nfs"]; + + fileSystems = { + "/mnt/leyla_documents" = { + device = "defiant:/exports/leyla_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" # Allow interruption of NFS calls + "timeo=30" # 3 second timeout (30 deciseconds) + "retrans=2" # Only 2 retries before giving up + "x-systemd.idle-timeout=300" # 5 minute idle timeout for mobile + "x-systemd.device-timeout=15" # 15 second device timeout + "bg" # Background mount - don't block boot + "fsc" # Enable caching + "_netdev" # Network device - wait for network + "x-systemd.requires=network-online.target" # Require network to be online + "x-systemd.after=network-online.target" # Start after network is online + "x-systemd.mount-timeout=30" # 30 second mount timeout + ]; + }; + + "/mnt/users_documents" = { + device = "defiant:/exports/users_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "nofail" + "soft" + "intr" + "timeo=30" + "retrans=2" + "x-systemd.idle-timeout=300" + "x-systemd.device-timeout=15" + "bg" + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=30" + ]; + }; + + "/mnt/media" = { + device = "defiant:/exports/media"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" + "timeo=30" + "retrans=2" + "x-systemd.idle-timeout=300" + "x-systemd.device-timeout=15" + "bg" + # Mobile-optimized read settings + "rsize=8192" # Smaller read size for mobile + "wsize=8192" # Smaller write size for mobile + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + "x-systemd.mount-timeout=30" + ]; + }; + }; +} diff --git a/configurations/nixos/twilight/configuration.nix b/configurations/nixos/twilight/configuration.nix new file mode 100644 index 00000000..477c5178 --- /dev/null +++ b/configurations/nixos/twilight/configuration.nix @@ -0,0 +1,160 @@ +{ + inputs, + config, + pkgs, + ... +}: { + imports = [ + ./monitors.nix + ]; + + nixpkgs.config.allowUnfree = true; + + boot.initrd.availableKernelModules = ["usb_storage"]; + boot.kernelModules = ["sg"]; + + boot.loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + + sops.secrets = { + "vpn-keys/tailscale-authkey/twilight" = { + sopsFile = "${inputs.secrets}/vpn-keys.yaml"; + }; + }; + host = { + users = { + leyla = { + isDesktopUser = true; + isTerminalUser = true; + isPrincipleUser = true; + }; + eve.isDesktopUser = true; + }; + hardware = { + piperMouse.enable = true; + viaKeyboard.enable = true; + openRGB.enable = true; + graphicsAcceleration.enable = true; + directAccess.enable = true; + }; + ai = { + enable = true; + # TODO: benchmark twilight against defiant and prune this list of models that are faster on defiant + models = { + # conversation models + "Llama 3.1 8B" = { + model = "lamma3.1:8b"; + roles = ["chat" "edit" "apply"]; + }; + "deepseek-r1:8b" = { + model = "deepseek-r1:8b"; + roles = ["chat" "edit" "apply"]; + }; + "deepseek-r1:32b" = { + model = "deepseek-r1:32b"; + roles = ["chat" "edit" "apply"]; + }; + + # auto complete models + "qwen2.5-coder:1.5b-base" = { + model = "qwen2.5-coder:1.5b-base"; + roles = ["autocomplete"]; + }; + "qwen2.5-coder:7b" = { + model = "qwen2.5-coder:7b"; + roles = ["autocomplete"]; + }; + "deepseek-coder:6.7b" = { + model = "deepseek-coder:6.7b"; + roles = ["autocomplete"]; + }; + "deepseek-coder:33b" = { + model = "deepseek-coder:33b"; + roles = ["autocomplete"]; + }; + + # agent models + "qwen3:32b" = { + model = "qwen3:32b"; + roles = ["chat" "edit" "apply"]; + }; + + # embedding models + "nomic-embed-text:latest" = { + model = "nomic-embed-text:latest"; + roles = ["embed"]; + }; + }; + }; + }; + services = { + ollama = { + enable = true; + exposePort = true; + + loadModels = [ + # conversation models + "llama3.1:8b" + "deepseek-r1:8b" + "deepseek-r1:32b" + + # auto complete models + "qwen2.5-coder:1.5b-base" + "qwen2.5-coder:7b" + "deepseek-coder:6.7b" + "deepseek-coder:33b" + + # agent models + "qwen3:32b" + + # embedding models + "nomic-embed-text:latest" + ]; + }; + + tailscale = { + enable = true; + authKeyFile = config.sops.secrets."vpn-keys/tailscale-authkey/twilight".path; + useRoutingFeatures = "both"; + extraUpFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + ]; + extraSetFlags = [ + "--advertise-exit-node" + "--advertise-routes=192.168.0.0/24" + ]; + }; + + syncthing.enable = true; + }; + + # Enable network-online.target for better network dependency handling + systemd.services.NetworkManager-wait-online.enable = true; + + environment.systemPackages = with pkgs; [ + cachefilesd + ]; + hardware.steam-hardware.enable = true; # Provides udev rules for controller, HTC vive, and Valve Index + + networking = { + networkmanager.enable = true; + hostName = "twilight"; # Define your hostname. + }; + + # enabled virtualisation for docker + # virtualisation.docker.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + # services.xserver.libinput.enable = true; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It's perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/configurations/nixos/twilight/default.nix b/configurations/nixos/twilight/default.nix new file mode 100644 index 00000000..aa841f80 --- /dev/null +++ b/configurations/nixos/twilight/default.nix @@ -0,0 +1,9 @@ +# leyla desktop +{...}: { + imports = [ + ./configuration.nix + ./hardware-configuration.nix + ./nvidia-drivers.nix + # ./network-mount.nix + ]; +} diff --git a/configurations/nixos/twilight/hardware-configuration.nix b/configurations/nixos/twilight/hardware-configuration.nix new file mode 100644 index 00000000..1389cafe --- /dev/null +++ b/configurations/nixos/twilight/hardware-configuration.nix @@ -0,0 +1,42 @@ +# Do not modify this file! It was generated by ‘nixos-generate-config’ +# and may be overwritten by future invocations. Please make changes +# to /etc/nixos/configuration.nix instead. +{ + config, + lib, + modulesPath, + ... +}: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "ahci" "usbhid" "sd_mod"]; + boot.initrd.kernelModules = []; + boot.kernelModules = ["kvm-amd"]; + boot.extraModulePackages = []; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/8be49c65-2b57-48f1-b74d-244d26061adb"; + fsType = "ext4"; + }; + + "/boot" = { + device = "/dev/disk/by-uuid/3006-3867"; + fsType = "vfat"; + options = ["fmask=0022" "dmask=0022"]; + }; + }; + + swapDevices = []; + + # Enables DHCP on each ethernet and wireless interface. In case of scripted networking + # (the default) this is the recommended approach. When using systemd-networkd it's + # still possible to use this option, but it's recommended to use it in conjunction + # with explicit per-interface declarations with `networking.interfaces..useDHCP`. + networking.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/configurations/nixos/twilight/monitors.nix b/configurations/nixos/twilight/monitors.nix new file mode 100644 index 00000000..1308f501 --- /dev/null +++ b/configurations/nixos/twilight/monitors.nix @@ -0,0 +1,199 @@ +{pkgs, ...}: { + systemd.tmpfiles.rules = [ + "L+ /run/gdm/.config/monitors.xml - - - - ${pkgs.writeText "gdm-monitors.xml" '' + + + + 0 + 156 + 1 + + + DP-4 + DEL + DELL U2719D + 8RGXNS2 + + + 2560 + 1440 + 59.951 + + + + + 2560 + 324 + 1 + yes + + + DP-2 + GSM + LG ULTRAGEAR + 0x00068c96 + + + 1920 + 1080 + 240.001 + + + + + 4480 + 0 + 1 + + left + no + + + + HDMI-0 + HWP + HP w2207 + CND7332S88 + + + 1600 + 1000 + 59.999 + + + + + + + 0 + 0 + 1 + yes + + + DP-1 + DEL + DELL U2719D + 8RGXNS2 + + + 2560 + 1440 + 59.951 + + + + + 4480 + 226 + 1 + + left + no + + + + HDMI-1 + HWP + HP w2207 + CND7332S88 + + + 1680 + 1050 + 59.954 + + + + + 2560 + 226 + 1 + + + DP-2 + GSM + LG ULTRAGEAR + 0x00068c96 + + + 1920 + 1080 + 240.001 + + + + + + + 2560 + 228 + 1 + yes + + + DP-2 + GSM + LG ULTRAGEAR + 0x00068c96 + + + 1920 + 1080 + 240.001 + + + + + 4480 + 69 + 1 + + left + no + + + + HDMI-1 + HWP + HP w2207 + CND7332S88 + + + 1680 + 1050 + 59.954 + + + + + 0 + 0 + 1 + + + DP-3 + DEL + DELL U2719D + 8RGXNS2 + + + 2560 + 1440 + 59.951 + + + + + + None-1 + unknown + unknown + unknown + + + + + ''}" + ]; +} diff --git a/configurations/nixos/twilight/network-mount.nix b/configurations/nixos/twilight/network-mount.nix new file mode 100644 index 00000000..9f84b040 --- /dev/null +++ b/configurations/nixos/twilight/network-mount.nix @@ -0,0 +1,72 @@ +{...}: { + boot.supportedFilesystems = ["nfs"]; + + fileSystems = { + "/mnt/leyla_documents" = { + device = "defiant:/exports/leyla_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" # Allow interruption of NFS calls + "timeo=50" # 5 second timeout (50 deciseconds) - longer than mobile + "retrans=3" # 3 retries for desktop + "x-systemd.idle-timeout=600" # 10 minute idle timeout for desktop + "x-systemd.device-timeout=30" # 30 second device timeout + "bg" # Background mount - don't block boot + "fsc" # Enable caching + "_netdev" # Network device - wait for network + "x-systemd.requires=network-online.target" # Require network to be online + "x-systemd.after=network-online.target" # Start after network is online + ]; + }; + + "/mnt/users_documents" = { + device = "defiant:/exports/users_documents"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "nofail" + "soft" + "intr" + "timeo=50" + "retrans=3" + "x-systemd.idle-timeout=600" + "bg" + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + ]; + }; + + "/mnt/media" = { + device = "defiant:/exports/media"; + fsType = "nfs"; + options = [ + "x-systemd.automount" + "noauto" + "noatime" + "nofail" + "soft" + "intr" + "timeo=50" + "retrans=3" + "x-systemd.idle-timeout=600" + "x-systemd.device-timeout=30" + "bg" + # Desktop-optimized read settings + "rsize=32768" # Larger read size for desktop + "wsize=32768" # Larger write size for desktop + "fsc" + "_netdev" + "x-systemd.requires=network-online.target" + "x-systemd.after=network-online.target" + ]; + }; + }; +} diff --git a/configurations/nixos/twilight/nvidia-drivers.nix b/configurations/nixos/twilight/nvidia-drivers.nix new file mode 100644 index 00000000..d875e37d --- /dev/null +++ b/configurations/nixos/twilight/nvidia-drivers.nix @@ -0,0 +1,47 @@ +{config, ...}: { + services = { + xserver = { + # Load nvidia driver for Xorg and Wayland + videoDrivers = ["nvidia"]; + }; + # Use X instead of wayland for gaming reasons + displayManager.gdm.wayland = false; + }; + + hardware = { + # Enable OpenGL + graphics.enable = true; + + # install graphics drivers + nvidia = { + # Modesetting is required. + modesetting.enable = true; + + # Nvidia power management. Experimental, and can cause sleep/suspend to fail. + # Enable this if you have graphical corruption issues or application crashes after waking + # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead + # of just the bare essentials. + powerManagement.enable = true; + + # Fine-grained power management. Turns off GPU when not in use. + # Experimental and only works on modern Nvidia GPUs (Turing or newer). + powerManagement.finegrained = false; + + # Use the NVidia open source kernel module (not to be confused with the + # independent third-party "nouveau" open source driver). + # Support is limited to the Turing and later architectures. Full list of + # supported GPUs is at: + # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus + # Only available from driver 515.43.04+ + # Currently alpha-quality/buggy, so false is currently the recommended setting. + open = true; + + # Enable the Nvidia settings menu, + # accessible via `nvidia-settings`. + nvidiaSettings = true; + + # Optionally, you may need to select the appropriate driver version for your specific GPU. + package = config.boot.kernelPackages.nvidiaPackages.production; + }; + }; +} diff --git a/configurations/syncthing/default.nix b/configurations/syncthing/default.nix new file mode 100644 index 00000000..397f678c --- /dev/null +++ b/configurations/syncthing/default.nix @@ -0,0 +1,119 @@ +{config, ...}: { + folders = { + leyla_documents = { + id = "hvrj0-9bm1p"; + }; + leyla_calendar = { + id = "8oatl-1rv6w"; + }; + leyla_supernote_notes = { + id = "dwbuv-zffnf"; + }; + eve_records = { + id = "by6at-d4h9n"; + }; + share = { + id = "73ot0-cxmkx"; + }; + }; + devices = { + defiant = { + id = "3R6E6Y4-2F7MF2I-IGB4WE6-A3SQSMV-LIBYSAM-2OXHHU2-KJ6CGIV-QNMCPAR"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + leyla_calendar = { + folder = config.folders.leyla_calendar; + path = "/mnt/sync/leyla/calendar"; + }; + leyla_supernote_notes = { + folder = config.folders.leyla_supernote_notes; + path = "/mnt/sync/leyla/notes"; + }; + eve_records = { + folder = config.folders.eve_records; + path = "/mnt/sync/eve/records"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + twilight = { + id = "UDIYL7V-OAZ2BI3-EJRAWFB-GZYVDWR-JNUYW3F-FFQ35MU-XBTGWEF-QD6K6QN"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + horizon = { + id = "OGPAEU6-5UR56VL-SP7YC4Y-IMVCRTO-XFD4CYN-Z6T5TZO-PFZNAT6-4MKWPQS"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + path = "/mnt/sync/leyla/documents"; + }; + share = { + folder = config.folders.share; + path = "/mnt/sync/default/share"; + }; + }; + }; + coven = { + id = "QGU7NN6-OMXTWVA-YCZ73S5-2O7ECTS-MUCTN4M-YH6WLEL-U4U577I-7PBNCA5"; + folders = { + leyla_documents = { + folder = config.folders.leyla_documents; + }; + share = { + folder = config.folders.share; + }; + }; + }; + ceder = { + id = "MGXUJBS-7AENXHB-7YQRNWG-QILKEJD-5462U2E-WAQW4R4-I2TVK5H-SMK6LAA"; + folders = { + share = { + folder = config.folders.share; + }; + leyla_documents = { + folder = config.folders.leyla_documents; + }; + leyla_calendar = { + folder = config.folders.leyla_calendar; + }; + leyla_notes = { + folder = config.folders.leyla_supernote_notes; + }; + }; + }; + emergent = { + id = "6MIDMKJ-7IFHXVX-FIR3YTB-KVE75LN-PA6IOTN-I257LWR-MMC4K6C-5H4SHQN"; + folders = { + eve_records = { + folder = config.folders.eve_records; + }; + share = { + folder = config.folders.share; + }; + }; + }; + shale = { + id = "AOAXEVD-QJ2IVRA-6G44Q7Q-TGUPXU2-FWWKOBH-DPKWC5N-LBAEHWJ-7EQF4AM"; + folders = { + share = { + folder = config.folders.share; + }; + }; + }; + }; +} diff --git a/const/sops_age_key_directory.nix b/const/sops_age_key_directory.nix new file mode 100644 index 00000000..cf948df6 --- /dev/null +++ b/const/sops_age_key_directory.nix @@ -0,0 +1 @@ +"/var/lib/sops-nix" diff --git a/enviroments/client/default.nix b/enviroments/client/default.nix deleted file mode 100644 index 555305f8..00000000 --- a/enviroments/client/default.nix +++ /dev/null @@ -1,60 +0,0 @@ -{ pkgs, ... }: -{ - imports = [ - ../common - ]; - - services = { - - # Enable CUPS to print documents. - printing.enable = true; - - xserver = { - # Enable the X11 windowing system. - enable = true; - - # Enable the GNOME Desktop Environment. - displayManager.gdm.enable = true; - desktopManager = { - gnome.enable = true; - xterm.enable = false; - }; - - # Get rid of xTerm - excludePackages = [ pkgs.xterm ]; - - # Configure keymap in X11 - xkb = { - layout = "us,it,de"; - variant = ""; - }; - }; - - pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - # If you want to use JACK applications, uncomment this - #jack.enable = true; - - # use the example session manager (no others are packaged yet so this is enabled by default, - # no need to redefine it in your config for now) - #media-session.enable = true; - }; - }; - - - # Enable sound with pipewire. - hardware.pulseaudio.enable = false; - security.rtkit.enable = true; - - environment.systemPackages = with pkgs; [ - # helvetica font - aileron - - cachefilesd - - gnomeExtensions.dash-to-dock - ]; -} \ No newline at end of file diff --git a/enviroments/common/default.nix b/enviroments/common/default.nix deleted file mode 100644 index 99f3e5ae..00000000 --- a/enviroments/common/default.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ pkgs, ... }: -{ - imports = [ - ../../users - ]; - - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - nix.settings.trusted-users = [ "leyla" ]; - - # Enable networking - networking.networkmanager.enable = true; - - # Set your time zone. - time.timeZone = "America/Chicago"; - - i18n.defaultLocale = "en_US.UTF-8"; - - i18n.extraLocaleSettings = { - LC_ADDRESS = "en_US.UTF-8"; - LC_IDENTIFICATION = "en_US.UTF-8"; - LC_MEASUREMENT = "en_US.UTF-8"; - LC_MONETARY = "en_US.UTF-8"; - LC_NAME = "en_US.UTF-8"; - LC_NUMERIC = "en_US.UTF-8"; - LC_PAPER = "en_US.UTF-8"; - LC_TELEPHONE = "en_US.UTF-8"; - LC_TIME = "en_US.UTF-8"; - }; - - users.groups.users = {}; - - services = { - openssh = { - enable = true; - ports = [ 22 ]; - settings = { - PasswordAuthentication = false; - AllowUsers = [ "leyla" ]; # Allows all users by default. Can be [ "user1" "user2" ] - UseDns = true; - X11Forwarding = false; - }; - }; - }; - - sops = { - defaultSopsFile = ../../secrets/secrets.yaml; - defaultSopsFormat = "yaml"; - gnupg.sshKeyPaths = []; - - age ={ - keyFile = "/var/lib/sops-nix/key.txt"; - sshKeyPaths = []; - # generateKey = true; - }; - }; - environment.sessionVariables = { - AGE_KEY_FILE_LOCATION = "/var/lib/sops-nix/"; - }; - - # List packages installed in system profile. - environment.systemPackages = with pkgs; [ - wget - - # version control - git - - # system debuging tools - iputils - dnsutils - ]; -} \ No newline at end of file diff --git a/enviroments/server/default.nix b/enviroments/server/default.nix deleted file mode 100644 index d3e9d638..00000000 --- a/enviroments/server/default.nix +++ /dev/null @@ -1,63 +0,0 @@ -{ config, ... }: -{ - imports = [ - ../common - ]; - - services = let - headscaleDomain = "headscale.jan-leila.com"; - in { - nfs.server = { - enable = true; - exports = '' - /home/leyla 192.168.1.0/22(rw,sync,no_subtree_check,crossmnt) - /home/eve 192.168.1.0/22(rw,sync,no_subtree_check,crossmnt) - /home/ester 192.168.1.0/22(rw,sync,no_subtree_check,crossmnt) - /home/users 192.168.1.0/22(rw,sync,no_subtree_check,crossmnt) - ''; - }; - - headscale = { - enable = true; - address = "0.0.0.0"; - port = 8080; - settings = { - server_url = "https://${headscaleDomain}"; - dns_config.base_domain = "jan-leila.com"; - logtail.enabled = false; - }; - }; - - nginx = { - enable = false; # TODO: enable this when you want to test all the configs - virtualHosts = { - ${headscaleDomain} = { - forceSSL = true; - enableACME = true; - locations."/" = { - proxyPass = - "http://localhost:${toString config.services.headscale.port}"; - proxyWebsockets = true; - }; - }; - }; - }; - }; - - security.acme = { - acceptTerms = true; - defaults.email = "jan-leila@protonmail.com"; - }; - - # disable computer sleeping - systemd.targets = { - sleep.enable = false; - suspend.enable = false; - hibernate.enable = false; - hybrid-sleep.enable = false; - }; - - networking.firewall.allowedTCPPorts = [ 2049 ]; - - environment.systemPackages = [ config.services.headscale.package ]; -} \ No newline at end of file diff --git a/flake.lock b/flake.lock index 3f277b2d..ae03c48c 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,23 @@ { "nodes": { + "devshell": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1741473158, + "narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=", + "owner": "numtide", + "repo": "devshell", + "rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, "disko": { "inputs": { "nixpkgs": [ @@ -7,11 +25,11 @@ ] }, "locked": { - "lastModified": 1725377834, - "narHash": "sha256-tqoAO8oT6zEUDXte98cvA1saU9+1dLJQe3pMKLXv8ps=", + "lastModified": 1760701190, + "narHash": "sha256-y7UhnWlER8r776JsySqsbTUh2Txf7K30smfHlqdaIQw=", "owner": "nix-community", "repo": "disko", - "rev": "e55f9a8678adc02024a4877c2a403e3f6daf24fe", + "rev": "3a9450b26e69dcb6f8de6e2b07b3fc1c288d85f5", "type": "github" }, "original": { @@ -20,14 +38,35 @@ "type": "github" } }, - "flake-compat": { - "flake": false, + "firefox-addons": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "dir": "pkgs/firefox-addons", + "lastModified": 1761797037, + "narHash": "sha256-OqwAGit+3cdsG02K6+8WJniA2q0rqUVc6zbT5N9C1us=", + "owner": "rycee", + "repo": "nur-expressions", + "rev": "3d9f4de0988bcfa57e45e16e1ef9326c56bdf891", + "type": "gitlab" + }, + "original": { + "dir": "pkgs/firefox-addons", + "owner": "rycee", + "repo": "nur-expressions", + "type": "gitlab" + } + }, + "flake-compat": { + "locked": { + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { @@ -41,11 +80,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -54,6 +93,39 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "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, + "narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=", + "owner": "lf-", + "repo": "flakey-profile", + "rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d", + "type": "github" + }, + "original": { + "owner": "lf-", + "repo": "flakey-profile", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -61,11 +133,11 @@ ] }, "locked": { - "lastModified": 1725948275, - "narHash": "sha256-4QOPemDQ9VRLQaAdWuvdDBhh+lEUOAnSMHhdr4nS1mk=", + "lastModified": 1761845621, + "narHash": "sha256-d+R4MHsGmdebvSMsYUFWONsZSlUbOo8Zq/wjMdMiIac=", "owner": "nix-community", "repo": "home-manager", - "rev": "e5fa72bad0c6f533e8d558182529ee2acc9454fe", + "rev": "97e3022a8d2c09313fa49847f6da4d76abcfc72d", "type": "github" }, "original": { @@ -74,20 +146,133 @@ "type": "github" } }, - "nix-vscode-extensions": { + "impermanence": { + "locked": { + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "lix": { + "flake": false, + "locked": { + "lastModified": 1755787066, + "narHash": "sha256-X2UwkUEban08GRSPXRr+kz8fckHqebr3P77qSvjoeOw=", + "rev": "ac9721a92e8138d29707824dbedb484c76948493", + "type": "tarball", + "url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/ac9721a92e8138d29707824dbedb484c76948493.tar.gz?rev=ac9721a92e8138d29707824dbedb484c76948493" + }, + "original": { + "type": "tarball", + "url": "https://git.lix.systems/lix-project/lix/archive/main.tar.gz" + } + }, + "lix-module": { "inputs": { - "flake-compat": "flake-compat", "flake-utils": "flake-utils", + "flakey-profile": "flakey-profile", + "lix": "lix", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1726623336, - "narHash": "sha256-mslZtr0SPdHDLUM5VRV0ipQQ4G0Piv2Kk15490w4JXM=", + "lastModified": 1759851320, + "narHash": "sha256-n5dRAIC3/78drQtFxmQRrBLd6TKfotUnX7GWu0mAcSg=", + "ref": "refs/heads/main", + "rev": "7c31a18259b8358ac196cf803a26967c0fa1d3e4", + "revCount": 163, + "type": "git", + "url": "https://git.lix.systems/lix-project/nixos-module.git" + }, + "original": { + "type": "git", + "url": "https://git.lix.systems/lix-project/nixos-module.git" + } + }, + "mcp-nixos": { + "inputs": { + "devshell": "devshell", + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1760821194, + "narHash": "sha256-UCsJ8eDuHL14u2GFIYEY/drtZ6jht5zN/G/6QNlEy2g=", + "owner": "utensils", + "repo": "mcp-nixos", + "rev": "0ae453f38d0f088c31d4678da3a12b183165986f", + "type": "github" + }, + "original": { + "owner": "utensils", + "repo": "mcp-nixos", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1761339987, + "narHash": "sha256-IUaawVwItZKi64IA6kF6wQCLCzpXbk2R46dHn8sHkig=", + "owner": "LnL7", + "repo": "nix-darwin", + "rev": "7cd9aac79ee2924a85c211d21fafd394b06a38de", + "type": "github" + }, + "original": { + "owner": "LnL7", + "repo": "nix-darwin", + "type": "github" + } + }, + "nix-syncthing": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741849924, + "narHash": "sha256-5vyb1H6HtW24QVqfI56P4QVQP6vHh1jS9ULwnunCO94=", + "ref": "main", + "rev": "86bcb200c83b6a5d13b3583126b9d8dc6770613a", + "revCount": 6, + "type": "git", + "url": "https://git.jan-leila.com/jan-leila/nix-syncthing" + }, + "original": { + "ref": "main", + "type": "git", + "url": "https://git.jan-leila.com/jan-leila/nix-syncthing" + } + }, + "nix-vscode-extensions": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1761789484, + "narHash": "sha256-17gDUWloFXQlavqHRey/urQe6sQ3yP5hsQyYmcNOZyU=", "owner": "nix-community", "repo": "nix-vscode-extensions", - "rev": "b23683fef09032c85bb8b20f8ec72fb2f70075ff", + "rev": "c47e683d236fa6e4c27dbda2af3468cb9aceb813", "type": "github" }, "original": { @@ -98,11 +283,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1725885300, - "narHash": "sha256-5RLEnou1/GJQl+Wd+Bxaj7QY7FFQ9wjnFq1VNEaxTmc=", + "lastModified": 1761827175, + "narHash": "sha256-XdPVSYyIBK4/ruoqujaQmmSGg3J2/EenexV9IEXhr6o=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "166dee4f88a7e3ba1b7a243edb1aca822f00680e", + "rev": "43ffe9ac82567512abb83187cb673de1091bdfa8", "type": "github" }, "original": { @@ -114,43 +299,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1725634671, - "narHash": "sha256-v3rIhsJBOMLR8e/RNWxr828tB+WywYIoajrZKFM+0Gg=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1725762081, - "narHash": "sha256-vNv+aJUW5/YurRy1ocfvs4q/48yVESwlC/yHzjkZSP8=", + "lastModified": 1722073938, + "narHash": "sha256-OpX0StkL8vpXyWOGUD6G+MA26wAXK6SpT94kLJXo6B4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dc454045f5b5d814e5862a6d057e7bb5c29edc05", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1725534445, - "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39", + "rev": "e36e9f57337d0ff0cf77aceb58af4c805472bfae", "type": "github" }, "original": { @@ -160,27 +313,68 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1761672384, + "narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "disko": "disko", + "firefox-addons": "firefox-addons", + "flake-compat": "flake-compat", "home-manager": "home-manager", + "impermanence": "impermanence", + "lix-module": "lix-module", + "mcp-nixos": "mcp-nixos", + "nix-darwin": "nix-darwin", + "nix-syncthing": "nix-syncthing", "nix-vscode-extensions": "nix-vscode-extensions", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", + "secrets": "secrets", "sops-nix": "sops-nix" } }, + "secrets": { + "flake": false, + "locked": { + "lastModified": 1759945215, + "narHash": "sha256-xmUzOuhJl6FtTjR5++OQvSoAnXe7/VA5QFCZDyFwBXo=", + "ref": "refs/heads/main", + "rev": "444229a105445339fb028d15a8d866063c5f8141", + "revCount": 21, + "type": "git", + "url": "ssh://git@git.jan-leila.com/jan-leila/nix-config-secrets.git" + }, + "original": { + "type": "git", + "url": "ssh://git@git.jan-leila.com/jan-leila/nix-config-secrets.git" + } + }, "sops-nix": { "inputs": { - "nixpkgs": "nixpkgs_2", - "nixpkgs-stable": "nixpkgs-stable" + "nixpkgs": [ + "nixpkgs" + ] }, "locked": { - "lastModified": 1725922448, - "narHash": "sha256-ruvh8tlEflRPifs5tlpa0gkttzq4UtgXkJQS7FusgFE=", + "lastModified": 1760998189, + "narHash": "sha256-ee2e1/AeGL5X8oy/HXsZQvZnae6XfEVdstGopKucYLY=", "owner": "Mic92", "repo": "sops-nix", - "rev": "cede1a08039178ac12957733e97ab1006c6b6892", + "rev": "5a7d18b5c55642df5c432aadb757140edfeb70b3", "type": "github" }, "original": { @@ -203,6 +397,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "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/flake.nix b/flake.nix index 033e2c25..ddf92ce9 100644 --- a/flake.nix +++ b/flake.nix @@ -5,71 +5,179 @@ # base packages nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - # encrypt files that contain secreats that I would like to not encrypt - sops-nix.url = "github:Mic92/sops-nix"; + lix-module = { + url = "git+https://git.lix.systems/lix-project/nixos-module.git"; + inputs.nixpkgs.follows = "nixpkgs"; + }; - # declairtive disk configuration + # secret encryption + sops-nix = { + url = "github:Mic92/sops-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # self hosted repo of secrets file to further protect files in case of future encryption vulnerabilities + secrets = { + url = "git+ssh://git@git.jan-leila.com/jan-leila/nix-config-secrets.git"; + flake = false; + }; + + # common config for syncthing + nix-syncthing = { + url = "git+https://git.jan-leila.com/jan-leila/nix-syncthing?ref=main"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # disk configurations disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; - # managment per user + # delete your darlings + impermanence = { + url = "github:nix-community/impermanence"; + }; + + nix-darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # users home directories home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; - # repo of hardware configs for prebuilt systems - nixos-hardware.url = "github:NixOS/nixos-hardware/master"; + # firefox extensions + firefox-addons = { + url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; + inputs.nixpkgs.follows = "nixpkgs"; + }; - # vscode extensions + # vscode extensions nix-vscode-extensions = { url = "github:nix-community/nix-vscode-extensions"; inputs.nixpkgs.follows = "nixpkgs"; }; + + # pregenerated hardware configurations + nixos-hardware = { + url = "github:NixOS/nixos-hardware/master"; + }; + + # this is just here so that we have a lock on it for our dev shells + flake-compat = { + url = "github:edolstra/flake-compat"; + }; + + # MCP NixOS server for Claude Dev + mcp-nixos = { + url = "github:utensils/mcp-nixos"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, disko, nixos-hardware, ... }@inputs: - let - forEachSystem = nixpkgs.lib.genAttrs [ - "aarch64-darwin" - "aarch64-linux" - "x86_64-darwin" - "x86_64-linux" - ]; - forEachPkgs = lambda: forEachSystem (system: lambda nixpkgs.legacyPackages.${system}); - in - { - packages = forEachPkgs (pkgs: import ./pkgs { inherit pkgs; }); + outputs = { + self, + nixpkgs, + sops-nix, + nix-syncthing, + home-manager, + impermanence, + ... + } @ inputs: let + util = import ./util {inherit inputs;}; + forEachPkgs = util.forEachPkgs; - nixosConfigurations = { - # Leyla Laptop - horizon = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - ./hosts/horizon/configuration.nix - inputs.home-manager.nixosModules.default - nixos-hardware.nixosModules.framework-11th-gen-intel - ]; - }; - # Leyla Desktop - twilight = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - ./hosts/twilight/configuration.nix - inputs.home-manager.nixosModules.default - ]; - }; - # NAS Service - defiant = nixpkgs.lib.nixosSystem { - specialArgs = { inherit inputs; }; - modules = [ - disko.nixosModules.disko - ./hosts/defiant/disko-config.nix - ./hosts/defiant/configuration.nix - ]; - }; - }; + mkNixosInstaller = util.mkNixosInstaller; + mkNixosSystem = util.mkNixosSystem; + mkDarwinSystem = util.mkDarwinSystem; + mkHome = util.mkHome; + syncthingConfiguration = util.syncthingConfiguration; + + installerSystems = { + basic = mkNixosInstaller "basic" []; }; + + nixosSystems = { + horizon = mkNixosSystem "horizon"; + twilight = mkNixosSystem "twilight"; + defiant = mkNixosSystem "defiant"; + emergent = mkNixosSystem "emergent"; + }; + + darwinSystems = { + hesperium = mkDarwinSystem "hesperium"; + }; + + homeSystems = { + # stand alone home manager configurations here: + # name = mkHome "name" + }; + + systemsHomes = nixpkgs.lib.attrsets.mergeAttrsList ( + nixpkgs.lib.attrsets.mapAttrsToList (hostname: system: ( + nixpkgs.lib.attrsets.mapAttrs' (user: _: { + name = "${user}@${hostname}"; + value = mkHome { + user = user; + host = hostname; + system = system.pkgs.hostPlatform.system; + osConfig = system.config; + }; + }) + system.config.home-manager.users + )) + (nixosSystems // darwinSystems) + ); + + homeConfigurations = + systemsHomes + // homeSystems; + in { + formatter = forEachPkgs (system: pkgs: pkgs.alejandra); + + # templates = import ./templates; + + devShells = forEachPkgs (system: pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ + # for version controlling this repo + git + # for formatting code in this repo + alejandra + # for editing secrets in the secrets repo + sops + # for viewing configuration options defined in this repo + nix-inspect + # for installing flakes from this repo onto other systems + nixos-anywhere + # for updating disko configurations + disko + # for viewing dconf entries + dconf-editor + # for MCP NixOS server support in development + inputs.mcp-nixos.packages.${system}.default + ]; + + SOPS_AGE_KEY_DIRECTORY = import ./const/sops_age_key_directory.nix; + + shellHook = '' + git config core.hooksPath .hooks + ''; + }; + }); + + installerConfigurations = installerSystems; + + nixosConfigurations = nixosSystems; + + darwinConfigurations = darwinSystems; + + homeConfigurations = homeConfigurations; + + syncthingConfiguration = syncthingConfiguration; + }; } diff --git a/hosts/defiant/configuration.nix b/hosts/defiant/configuration.nix deleted file mode 100644 index d2b13483..00000000 --- a/hosts/defiant/configuration.nix +++ /dev/null @@ -1,57 +0,0 @@ -# server nas -{ config, pkgs, inputs, ... }: -{ - imports = - [ - inputs.home-manager.nixosModules.default - inputs.sops-nix.nixosModules.sops - - ./hardware-configuration.nix - - ../../enviroments/server - ]; - - users.leyla.isThinUser = true; - - boot.loader.grub = { - enable = true; - zfsSupport = true; - efiSupport = true; - efiInstallAsRemovable = true; - }; - - nixpkgs.config.allowUnfree = true; - - services = { - zfs = { - autoScrub.enable = true; - autoSnapshot.enable = true; - }; - - # temp enable desktop enviroment for setup - # Enable the X11 windowing system. - xserver = { - enable = true; - - # Enable the GNOME Desktop Environment. - displayManager = { - gdm.enable = true; - }; - desktopManager = { - gnome.enable = true; - xterm.enable = false; - }; - - # Get rid of xTerm - excludePackages = [ pkgs.xterm ]; - }; - }; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? -} \ No newline at end of file diff --git a/hosts/defiant/disko-config.nix b/hosts/defiant/disko-config.nix deleted file mode 100644 index 653f29f6..00000000 --- a/hosts/defiant/disko-config.nix +++ /dev/null @@ -1,136 +0,0 @@ -{ lib, ... }: -let - bootDisk = devicePath: { - type = "disk"; - device = devicePath; - content = { - type = "gpt"; - - partitions = { - boot = { - size = "1M"; - type = "EF02"; # for grub MBR - }; - ESP = { - size = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - }; - }; - }; - }; - }; - zfsDisk = devicePath: { - type = "disk"; - device = devicePath; - content = { - type = "gpt"; - partitions = { - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; - }; - }; - }; - }; - cacheDisk = devicePath: swapSize: { - type = "disk"; - device = devicePath; - content = { - type = "gpt"; - partitions = { - encryptedSwap = { - size = swapSize; - content = { - type = "swap"; - randomEncryption = true; - discardPolicy = "both"; - resumeDevice = true; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; - }; - }; - }; - }; -in { - disko.devices = { - disk = { - boot = bootDisk "/dev/disk/by-path/pci-0000:23:00.3-usb-0:1:1.0-scsi-0:0:0:0"; - - hd_13_tb_a = zfsDisk "/dev/disk/by-id/ata-ST18000NE000-3G6101_ZVTCXVEB"; - hd_13_tb_b = zfsDisk "/dev/disk/by-id/ata-ST18000NE000-3G6101_ZVTCXWSC"; - hd_13_tb_c = zfsDisk "/dev/disk/by-id/ata-ST18000NE000-3G6101_ZVTD10EH"; - - # ssd_2_tb_a = cacheDisk "64G" "/dev/disk/by-id/XXX"; - }; - zpool = { - zroot = { - type = "zpool"; - mode = { - topology = { - type = "topology"; - vdev = [ - { - # should this only mirror for this inital config with 3 drives we will used raidz2 for future configs??? - mode = "mirror"; - members = [ - "hd_13_tb_a" "hd_13_tb_b" "hd_13_tb_c" - ]; - } - ]; - cache = [ ]; - # cache = [ "ssd_2_tb_a" ]; - }; - }; - - options = { - ashift = "12"; - }; - - rootFsOptions = { - encryption = "on"; - keyformat = "hex"; - keylocation = "prompt"; - compression = "lz4"; - xattr = "sa"; - acltype = "posixacl"; - "com.sun:auto-snapshot" = "false"; - }; - - mountpoint = "/"; - postCreateHook = "zfs list -t snapshot -H -o name | grep -E '^zroot@blank$' || zfs snapshot zroot@blank"; - - datasets = { - "nix" = { - type = "zfs_fs"; - mountpoint = "/nix"; - }; - "home" = { - type = "zfs_fs"; - mountpoint = "/mnt/home"; - options = { - "com.sun:auto-snapshot" = "true"; - }; - }; - "var" = { - type = "zfs_fs"; - mountpoint = "/var"; - }; - }; - }; - }; - }; -} - diff --git a/hosts/defiant/hardware-configuration.nix b/hosts/defiant/hardware-configuration.nix deleted file mode 100644 index 3ba63d00..00000000 --- a/hosts/defiant/hardware-configuration.nix +++ /dev/null @@ -1,45 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot = { - initrd = { - availableKernelModules = [ "xhci_pci" "aacraid" "ahci" "usbhid" "usb_storage" "sd_mod" ]; - kernelModules = [ ]; - }; - kernelModules = [ "kvm-amd" ]; - extraModulePackages = [ ]; - - supportedFilesystems = [ "zfs" ]; - - zfs.extraPools = [ "zroot" ]; - }; - - # fileSystems."/" = - # { device = "/dev/disk/by-uuid/dc6a9664-80f2-4988-afd7-fee5bd3ee2ca"; - # fsType = "ext4"; - # }; - - swapDevices = [ ]; - - networking = { - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - useDHCP = lib.mkDefault true; - # networking.interfaces.eno1.useDHCP = lib.mkDefault true; - # networking.interfaces.eno2.useDHCP = lib.mkDefault true; - hostId = "c51763d6"; - hostName = "defiant"; # Define your hostname. - }; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} \ No newline at end of file diff --git a/hosts/horizon/configuration.nix b/hosts/horizon/configuration.nix deleted file mode 100644 index f1c3bee4..00000000 --- a/hosts/horizon/configuration.nix +++ /dev/null @@ -1,49 +0,0 @@ -# leyla laptop -{ config, pkgs, inputs, ... }: -{ - imports = - [ - inputs.home-manager.nixosModules.default - inputs.sops-nix.nixosModules.sops - - ./hardware-configuration.nix - - ../../enviroments/client - ]; - - users = { - leyla.isFullUser = true; - ester.isFullUser = true; - eve.isFullUser = true; - }; - - # enabled virtualisation for docker - virtualisation.docker = { - enable = true; - rootless = { - enable = true; - setSocketVariable = true; - }; - }; - users.extraGroups.docker.members = [ "leyla" ]; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Allow unfree packages - nixpkgs.config.allowUnfree = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? -} diff --git a/hosts/horizon/hardware-configuration.nix b/hosts/horizon/hardware-configuration.nix deleted file mode 100644 index 59a900d4..00000000 --- a/hosts/horizon/hardware-configuration.nix +++ /dev/null @@ -1,104 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot = { - initrd = { - availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" ]; - kernelModules = [ ]; - }; - kernelModules = [ "kvm-intel" "sg" ]; - extraModulePackages = [ ]; - - # Bootloader. - loader = { - systemd-boot.enable = true; - efi.canTouchEfiVariables = true; - }; - }; - - - hardware.graphics.enable = true; - - fileSystems = { - "/" = - { device = "/dev/disk/by-uuid/866d422b-f816-4ad9-9846-791839cb9337"; - fsType = "ext4"; - }; - - "/boot" = - { device = "/dev/disk/by-uuid/E138-65B5"; - fsType = "vfat"; - }; - - "/mnt/leyla_home" = - { - device = "defiant:/home/leyla"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "noatime" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - "/mnt/eve_home" = - { - device = "defiant:/home/eve"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - "/mnt/ester_home" = - { - device = "defiant:/home/ester"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - "/mnt/users_home" = - { - device = "defiant:/home/users"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - # "/mnt/legacy_leyla_home" = - # { - # device = "server.arpa:/home/leyla"; - # fsType = "nfs"; - # options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - # }; - - # "/mnt/legacy_share_home" = - # { - # device = "server.arpa:/home/share"; - # fsType = "nfs"; - # options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - # }; - - # "/mnt/legacy_docker_home" = - # { - # device = "server.arpa:/home/docker"; - # fsType = "nfs"; - # options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ]; - # }; - }; - - services.cachefilesd.enable = true; - - swapDevices = - [ { device = "/dev/disk/by-uuid/be98e952-a072-4c3a-8c12-69500b5a2fff"; } - ]; - - networking = { - useDHCP = lib.mkDefault true; - hostName = "horizon"; # Define your hostname. - }; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} diff --git a/hosts/twilight/configuration.nix b/hosts/twilight/configuration.nix deleted file mode 100644 index 1352c8bb..00000000 --- a/hosts/twilight/configuration.nix +++ /dev/null @@ -1,42 +0,0 @@ -# leyla laptop -{ config, pkgs, inputs, ... }: -{ - imports = - [ - inputs.home-manager.nixosModules.default - inputs.sops-nix.nixosModules.sops - - ./hardware-configuration.nix - - ../../enviroments/client - ]; - - users = { - leyla = { - isFullUser = true; - hasPiperMouse = true; - hasOpenRGBHardware = true; - hasViaKeyboard = true; - hasGPU = true; - }; - ester.isFullUser = true; - eve.isFullUser = true; - }; - - # enabled virtualisation for docker - # virtualisation.docker.enable = true; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Allow unfree packages - nixpkgs.config.allowUnfree = true; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "23.05"; # Did you read the comment? -} diff --git a/hosts/twilight/hardware-configuration.nix b/hosts/twilight/hardware-configuration.nix deleted file mode 100644 index ab24b97b..00000000 --- a/hosts/twilight/hardware-configuration.nix +++ /dev/null @@ -1,119 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot = { - initrd = { - availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ]; - kernelModules = [ ]; - }; - kernelModules = [ "kvm-amd" "sg" ]; - extraModulePackages = [ ]; - - # Bootloader. - loader = { - systemd-boot.enable = true; - efi.canTouchEfiVariables = true; - }; - }; - - services.xserver = { - # Load nvidia driver for Xorg and Wayland - videoDrivers = ["nvidia"]; - - # Use X instead of wayland for gaming reasons - displayManager.gdm.wayland = false; - }; - - hardware = { - # Enable OpenGL - graphics.enable = true; - - # install graphics drivers - nvidia = { - # Modesetting is required. - modesetting.enable = true; - - # Nvidia power management. Experimental, and can cause sleep/suspend to fail. - # Enable this if you have graphical corruption issues or application crashes after waking - # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead - # of just the bare essentials. - powerManagement.enable = false; - - # Fine-grained power management. Turns off GPU when not in use. - # Experimental and only works on modern Nvidia GPUs (Turing or newer). - powerManagement.finegrained = false; - - # Use the NVidia open source kernel module (not to be confused with the - # independent third-party "nouveau" open source driver). - # Support is limited to the Turing and later architectures. Full list of - # supported GPUs is at: - # https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus - # Only available from driver 515.43.04+ - # Currently alpha-quality/buggy, so false is currently the recommended setting. - open = false; - - # Enable the Nvidia settings menu, - # accessible via `nvidia-settings`. - nvidiaSettings = true; - - # Optionally, you may need to select the appropriate driver version for your specific GPU. - package = config.boot.kernelPackages.nvidiaPackages.production; - }; - }; - - fileSystems = { - "/" = - { device = "/dev/disk/by-uuid/8be49c65-2b57-48f1-b74d-244d26061adb"; - fsType = "ext4"; - }; - - "/boot" = - { device = "/dev/disk/by-uuid/3006-3867"; - fsType = "vfat"; - options = [ "fmask=0022" "dmask=0022" ]; - }; - - "/mnt/leyla_home" = - { - device = "server.arpa:/home/leyla"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - "/mnt/share_home" = - { - device = "server.arpa:/home/share"; - fsType = "nfs"; - options = [ "x-systemd.automount" "user" "nofail" "soft" "x-systemd.idle-timeout=600" "fsc" ]; - }; - - "/mnt/docker_home" = - { - device = "server.arpa:/home/docker"; - fsType = "nfs"; - options = [ "x-systemd.automount" "noauto" "x-systemd.idle-timeout=600" ]; - }; - }; - - swapDevices = [ ]; - - networking = { - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - useDHCP = lib.mkDefault true; - hostName = "twilight"; # Define your hostname. - }; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} - diff --git a/install.sh b/install.sh index 07189989..c77d7482 100755 --- a/install.sh +++ b/install.sh @@ -39,6 +39,7 @@ if [ -z ${flake} ]; then exit 1; fi +# TODO: we might not need to copy the key over here anymore? temp=$(mktemp -d) # Function to cleanup temporary directory on exit cleanup() { @@ -47,8 +48,8 @@ cleanup() { trap cleanup EXIT # copy key file to temp folder to copy over to target -mkdir -p $temp$AGE_KEY_FILE_LOCATION -cp -r $AGE_KEY_FILE_LOCATION/* $temp$AGE_KEY_FILE_LOCATION +mkdir -p $temp$SOPS_AGE_KEY_DIRECTORY +cp -r $SOPS_AGE_KEY_DIRECTORY/* $temp$SOPS_AGE_KEY_DIRECTORY # commit number in this is because the main branch of nixos-anywhere is broken right now -nix run github:nix-community/nixos-anywhere/b3b6bfebba35d55fba485ceda588984dec74c54f -- --extra-files $temp --flake ".#$flake" ${user:-nixos}@$target +nixos-anywhere --extra-files $temp --flake ".#$flake" ${user:-nixos}@$target diff --git a/modules/common-modules/default.nix b/modules/common-modules/default.nix new file mode 100644 index 00000000..3dd19232 --- /dev/null +++ b/modules/common-modules/default.nix @@ -0,0 +1,7 @@ +# this folder is for modules that are common between nixos, home-manager, and darwin +{...}: { + imports = [ + ./overlays + ./pkgs + ]; +} diff --git a/modules/common-modules/overlays/default.nix b/modules/common-modules/overlays/default.nix new file mode 100644 index 00000000..2c0f712d --- /dev/null +++ b/modules/common-modules/overlays/default.nix @@ -0,0 +1,6 @@ +# this folder is for derivation overlays +{inputs, ...}: { + nixpkgs.overlays = [ + inputs.nix-vscode-extensions.overlays.default + ]; +} diff --git a/modules/common-modules/pkgs/codium-extensions/ai-code.nix b/modules/common-modules/pkgs/codium-extensions/ai-code.nix new file mode 100644 index 00000000..9c9efe3e --- /dev/null +++ b/modules/common-modules/pkgs/codium-extensions/ai-code.nix @@ -0,0 +1,42 @@ +{ + buildNpmPackage, + vscode-utils, + pkgs, + ... +}: let + version = "0.0.1"; + pname = "ai-code"; + publisher = "jan-leila"; + vsix = buildNpmPackage { + inherit version pname; + + src = builtins.fetchGit { + url = "ssh://git@git.jan-leila.com/jan-leila/ai-code.git"; + rev = "d48e01713021dbb30de0ebbee2cfaf99e4e9b5a6"; + }; + + npmDepsHash = "sha256-kjMyEnT3dz0yH5Ydh+aGoFDocKpBYGRmfnwbEdvvgpY="; + + nativeBuildInputs = with pkgs; [ + vsce + ]; + + buildPhase = '' + ${pkgs.vsce}/bin/vsce package -o ${pname}.zip + ''; + + installPhase = '' + mkdir -p $out + mv ${pname}.zip $out/${pname}.zip + ''; + }; +in + vscode-utils.buildVscodeExtension { + inherit pname version; + + src = "${vsix}/${pname}.zip"; + + vscodeExtUniqueId = "${publisher}.${pname}"; + vscodeExtPublisher = publisher; + vscodeExtName = pname; + } diff --git a/modules/common-modules/pkgs/codium-extensions/default.nix b/modules/common-modules/pkgs/codium-extensions/default.nix new file mode 100644 index 00000000..a60e8a03 --- /dev/null +++ b/modules/common-modules/pkgs/codium-extensions/default.nix @@ -0,0 +1,3 @@ +{pkgs, ...}: { + ai-code = pkgs.callPackage ./ai-code.nix {}; +} diff --git a/modules/common-modules/pkgs/default.nix b/modules/common-modules/pkgs/default.nix new file mode 100644 index 00000000..a2f61b18 --- /dev/null +++ b/modules/common-modules/pkgs/default.nix @@ -0,0 +1,45 @@ +{pkgs, ...}: { + imports = [ + ./python + ]; + + nixpkgs.overlays = [ + (final: prev: { + webtoon-dl = + pkgs.callPackage + ./webtoon-dl.nix + {}; + }) + (final: prev: { + prostudiomasters = + pkgs.callPackage + ./prostudiomasters.nix + {}; + }) + (final: prev: { + noita_entangled_worlds = pkgs.callPackage ./noita-entangled-worlds.nix {}; + }) + (final: prev: { + gdx-liftoff = pkgs.callPackage ./gdx-liftoff.nix {}; + }) + (final: prev: { + codium-extensions = pkgs.callPackage ./codium-extensions {}; + }) + (final: prev: { + mapillary-uploader = pkgs.callPackage ./mapillary-uploader.nix {}; + }) + (final: prev: { + panoramax = pkgs.python3.pkgs.callPackage ./panoramax.nix {}; + }) + (final: prev: { + sgblur = pkgs.python3.pkgs.callPackage ./sgblur.nix {}; + }) + (final: prev: { + # Override h3 C library to version 4.3.0 + h3 = pkgs.callPackage ./h3-c-lib.nix {}; + }) + (final: prev: { + polycule = pkgs.callPackage ./polycule {}; + }) + ]; +} diff --git a/modules/common-modules/pkgs/gdx-liftoff.nix b/modules/common-modules/pkgs/gdx-liftoff.nix new file mode 100644 index 00000000..d2e94241 --- /dev/null +++ b/modules/common-modules/pkgs/gdx-liftoff.nix @@ -0,0 +1,44 @@ +{ + stdenv, + fetchurl, + makeWrapper, + jdk, + lib, + xorg, + libGL, + ... +}: +stdenv.mkDerivation rec { + pname = "gdx-liftoff"; + version = "1.13.5.1"; + + src = fetchurl { + url = "https://github.com/libgdx/gdx-liftoff/releases/download/v${version}/gdx-liftoff-${version}.jar"; + hash = "sha256-9vCXGNGwI/P4VmcdIzTv2GPAX8bZb7nkfopaRAf6yMA="; + }; + + dontUnpack = true; + + nativeBuildInputs = [makeWrapper]; + + runtimeDependencies = lib.makeLibraryPath [ + # glfw + libGL + xorg.libX11 + xorg.libXcursor + xorg.libXext + xorg.libXrandr + xorg.libXxf86vm + ]; + + installPhase = '' + runHook preInstall + + install -Dm644 $src $out/lib/gdx-liftoff-${version}.jar + + makeWrapper ${lib.getExe jdk} $out/bin/gdx-liftoff-${version} \ + --append-flags "-jar $out/lib/gdx-liftoff-${version}.jar"\ + ${lib.optionalString stdenv.hostPlatform.isLinux "--prefix LD_LIBRARY_PATH : ${runtimeDependencies}"} + runHook postInstall + ''; +} diff --git a/modules/common-modules/pkgs/h3-c-lib.nix b/modules/common-modules/pkgs/h3-c-lib.nix new file mode 100644 index 00000000..2615d3cb --- /dev/null +++ b/modules/common-modules/pkgs/h3-c-lib.nix @@ -0,0 +1,36 @@ +{ + lib, + stdenv, + fetchFromGitHub, + cmake, + doxygen, +}: +stdenv.mkDerivation rec { + pname = "h3"; + version = "4.3.0"; + + src = fetchFromGitHub { + owner = "uber"; + repo = "h3"; + rev = "v${version}"; + hash = "sha256-DUILKZ1QvML6qg+WdOxir6zRsgTvk+En6yjeFf6MQBg="; + }; + + nativeBuildInputs = [ + cmake + doxygen + ]; + + cmakeFlags = [ + "-DBUILD_SHARED_LIBS=ON" + "-DBUILD_TESTING=OFF" + ]; + + meta = with lib; { + homepage = "https://github.com/uber/h3"; + description = "Hexagonal hierarchical geospatial indexing system"; + license = licenses.asl20; + maintainers = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/mapillary-uploader.nix b/modules/common-modules/pkgs/mapillary-uploader.nix new file mode 100644 index 00000000..acff7723 --- /dev/null +++ b/modules/common-modules/pkgs/mapillary-uploader.nix @@ -0,0 +1,39 @@ +{ + lib, + fetchurl, + appimageTools, +}: let + pname = "mapillary-uploader"; + version = "4.7.2"; + + src = fetchurl { + url = "http://tools.mapillary.com/uploader/download/linux/${version}"; + name = "mapillary-uploader.AppImage"; + sha256 = "sha256-hpWdfeuhYylO+SFD3BsKI0s/xtObCDd5OcuJ6i/aEuI="; + }; + + appimageContents = appimageTools.extractType2 { + inherit pname version src; + }; +in + appimageTools.wrapType2 { + inherit pname version src; + + extraInstallCommands = '' + # Install desktop file + install -Dm644 ${appimageContents}/mapillary-desktop-uploader.desktop $out/share/applications/mapillary-uploader.desktop + + # Fix desktop file paths + substituteInPlace $out/share/applications/mapillary-uploader.desktop \ + --replace 'Exec=AppRun' 'Exec=${pname}' + ''; + + meta = with lib; { + description = "Mapillary Desktop Uploader - Upload street-level imagery to Mapillary"; + homepage = "https://www.mapillary.com/"; + license = licenses.unfree; # Mapillary's license terms + maintainers = []; + platforms = ["x86_64-linux"]; + sourceProvenance = with sourceTypes; [binaryNativeCode]; + }; + } diff --git a/modules/common-modules/pkgs/noita-entangled-worlds.nix b/modules/common-modules/pkgs/noita-entangled-worlds.nix new file mode 100644 index 00000000..322ce418 --- /dev/null +++ b/modules/common-modules/pkgs/noita-entangled-worlds.nix @@ -0,0 +1,46 @@ +# not working yet +{ + pkgs, + rustPlatform, + fetchFromGitHub, + ... +}: let + version = "1.5.3"; + repo = fetchFromGitHub { + owner = "IntQuant"; + repo = "noita_entangled_worlds"; + rev = "v${version}"; + hash = "sha256-frrpD0aWTeDbZYtp15R+quUUAZf7OvHlbSLtGJJtAqk="; + }; +in + rustPlatform.buildRustPackage { + name = "noita-proxy-${version}"; + src = repo + "/noita-proxy"; + prePatch = '' + substituteInPlace Cargo.toml \ + --replace "path = \"../shared\"" "path = \"${repo + "/shared"}\"" + ''; + nativeBuildInputs = with pkgs; [ + pkg-config + python3 + cmake + ]; + buildInputs = with pkgs; [ + openssl + openssl.dev + libpulseaudio + libjack2 + alsa-lib + xorg.libxcb + xorg.libxcb.dev + libopus + ]; + propagatedBuildInputs = with pkgs; [ + steamworks-sdk-redist + ]; + runtimeDependencies = with pkgs; [ + steamworks-sdk-redist + ]; + doCheck = false; + cargoHash = "sha256-TzUS6d6PopgGf2i1yVaXaXdzNrvfSz+Gv67BAtxYmb4="; + } diff --git a/modules/common-modules/pkgs/panoramax.nix b/modules/common-modules/pkgs/panoramax.nix new file mode 100644 index 00000000..75b5e0e7 --- /dev/null +++ b/modules/common-modules/pkgs/panoramax.nix @@ -0,0 +1,105 @@ +{ + lib, + fetchFromGitLab, + buildPythonPackage, + flit-core, + flask, + pillow, + requests, + python-dotenv, + authlib, + sentry-sdk, + python-dateutil, + dateparser, + croniter, + pydantic, + flask-cors, + flask-compress, + flask-babel, + flasgger, + yoyo-migrations, + psycopg, + psycopg-pool, + tzdata, + email-validator, + pydantic-extra-types, + python-multipart, + fs, + fs-s3fs, + geopic-tag-reader, + pygeofilter, + pygeoif, + rfeed, + geojson-pydantic, + ... +}: let + pname = "geovisio"; + version = "2.10.0"; + repo = fetchFromGitLab { + owner = "panoramax"; + repo = "server/api"; + rev = version; + hash = "sha256-kCLcrOe7jJdIfmWWOmxQ5dOj8ZG2B7s0qFpHXs02B/E="; + }; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = repo; + + build-system = [ + flit-core + ]; + + dependencies = [ + flask + pillow + requests + python-dotenv + authlib + sentry-sdk + python-dateutil + dateparser + croniter + pydantic + flask-cors + flask-compress + flask-babel + flasgger + yoyo-migrations + psycopg + psycopg-pool + tzdata + email-validator + pydantic-extra-types + python-multipart + fs + fs-s3fs + geopic-tag-reader + pygeofilter + pygeoif + rfeed + geojson-pydantic + # Missing from nixpkgs - may need custom packages: + # flask-executor + ]; + + # Skip tests as they may require network access or specific setup + doCheck = false; + + # Disable runtime dependencies check as many dependencies are not available in nixpkgs + dontCheckRuntimeDeps = true; + + # Disable imports check as many dependencies are not available in nixpkgs + pythonImportsCheck = []; + + meta = with lib; { + description = "Panoramax API client and tools for street-level imagery platform"; + homepage = "https://gitlab.com/panoramax/server/api"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/polycule/default.nix b/modules/common-modules/pkgs/polycule/default.nix new file mode 100644 index 00000000..e9841fed --- /dev/null +++ b/modules/common-modules/pkgs/polycule/default.nix @@ -0,0 +1,149 @@ +{ + lib, + flutter332, + fetchFromGitLab, + pkg-config, + wrapGAppsHook3, + gtk3, + glib, + glib-networking, + webkitgtk_4_1, + libsecret, + libnotify, + dbus, + sqlcipher, + openssl, + mpv, + alsa-lib, + libass, + ffmpeg-full, + libplacebo, + libunwind, + shaderc, + vulkan-headers, + vulkan-loader, + lcms2, + libdovi, + libdvdnav, + libdvdread, + mujs, + libbluray, + lua, + rubberband, + libuchardet, + zimg, + openal, + pipewire, + libpulseaudio, + libcaca, + libdrm, + libdisplay-info, + libgbm, + xorg, + nv-codec-headers-11, + libva, + libvdpau, +}: +flutter332.buildFlutterApplication rec { + pname = "polycule"; + version = "0.3.4"; + + src = fetchFromGitLab { + owner = "polycule_client"; + repo = "polycule"; + rev = "v${version}"; + hash = "sha256-RUu8DKuX2NUU5Ce5WLHtDaORkn7CSrgTj3KhM/z+yHc="; + }; + + pubspecLock = lib.importJSON ./polycule-pubspec.lock.json; + + gitHashes = { + matrix = "sha256-w/QB5nYJ9Lh77TcYKEN/DnNQjWfp+9NX0dwQ9GOzWE8="; + media_kit = "sha256-1sVX+aHFLFJBtrNZrR6tWkb80vFELW2N9EejyQKlBPg="; + media_kit_libs_android_video = "sha256-N6QoktM8u9NYF8MAXLsxM9RlV8nICM4NbnmABHTRkZg="; + }; + + nativeBuildInputs = [ + pkg-config + wrapGAppsHook3 + ]; + + buildInputs = [ + gtk3 + glib + glib-networking + webkitgtk_4_1 + libsecret + libnotify + dbus + sqlcipher + openssl + mpv + alsa-lib + libass + ffmpeg-full + libplacebo + libunwind + shaderc + vulkan-headers + vulkan-loader + lcms2 + libdovi + libdvdnav + libdvdread + mujs + libbluray + lua + rubberband + libuchardet + zimg + openal + pipewire + libpulseaudio + libcaca + libdrm + libdisplay-info + libgbm + xorg.libXScrnSaver + xorg.libXpresent + nv-codec-headers-11 + libva + libvdpau + ]; + + flutterBuildFlags = [ + "--release" + "--target" + "lib/main.dart" + "--dart-define=POLYCULE_VERSION=v${version}" + "--dart-define=POLYCULE_IS_STABLE=true" + "--no-tree-shake-icons" + ]; + + postInstall = '' + # Install desktop files and icons from the source + install -Dm644 linux/business.braid.polycule.desktop $out/share/applications/polycule.desktop + install -Dm644 assets/logo/logo-circle.png $out/share/pixmaps/polycule.png + + # Update desktop file to use correct executable name + substituteInPlace $out/share/applications/polycule.desktop \ + --replace 'Exec=business.braid.polycule' 'Exec=polycule' + + # Create a symlink with the expected name + ln -sf $out/bin/polycule $out/bin/business.braid.polycule + ''; + + meta = with lib; { + description = "A geeky and efficient [matrix] client for power users"; + longDescription = '' + Polycule is a modern Matrix client built with Flutter, designed for power users + who want a fast, efficient, and feature-rich Matrix experience. + ''; + homepage = "https://polycule.im/"; + license = licenses.eupl12; + maintainers = []; + platforms = ["x86_64-linux" "aarch64-linux"]; + sourceProvenance = with sourceTypes; [fromSource]; + mainProgram = "polycule"; + }; +} diff --git a/modules/common-modules/pkgs/polycule/polycule-pubspec.lock.json b/modules/common-modules/pkgs/polycule/polycule-pubspec.lock.json new file mode 100644 index 00000000..e119fa29 --- /dev/null +++ b/modules/common-modules/pkgs/polycule/polycule-pubspec.lock.json @@ -0,0 +1,2459 @@ +{ + "packages": { + "_fe_analyzer_shared": { + "dependency": "transitive", + "description": { + "name": "_fe_analyzer_shared", + "sha256": "da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "85.0.0" + }, + "analyzer": { + "dependency": "transitive", + "description": { + "name": "analyzer", + "sha256": "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "7.7.1" + }, + "animations": { + "dependency": "direct main", + "description": { + "name": "animations", + "sha256": "d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.11" + }, + "app_links": { + "dependency": "direct main", + "description": { + "name": "app_links", + "sha256": "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.4.0" + }, + "app_links_linux": { + "dependency": "transitive", + "description": { + "name": "app_links_linux", + "sha256": "f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.3" + }, + "app_links_platform_interface": { + "dependency": "transitive", + "description": { + "name": "app_links_platform_interface", + "sha256": "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.2" + }, + "app_links_web": { + "dependency": "transitive", + "description": { + "name": "app_links_web", + "sha256": "af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.4" + }, + "archive": { + "dependency": "transitive", + "description": { + "name": "archive", + "sha256": "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.0.7" + }, + "args": { + "dependency": "transitive", + "description": { + "name": "args", + "sha256": "d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.7.0" + }, + "async": { + "dependency": "direct main", + "description": { + "name": "async", + "sha256": "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.13.0" + }, + "audio_session": { + "dependency": "transitive", + "description": { + "name": "audio_session", + "sha256": "8f96a7fecbb718cb093070f868b4cdcb8a9b1053dce342ff8ab2fde10eb9afb7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.2" + }, + "barcode": { + "dependency": "transitive", + "description": { + "name": "barcode", + "sha256": "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.9" + }, + "barcode_widget": { + "dependency": "direct main", + "description": { + "name": "barcode_widget", + "sha256": "6f2c5b08659b1a5f4d88d183e6007133ea2f96e50e7b8bb628f03266c3931427", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.4" + }, + "base58check": { + "dependency": "transitive", + "description": { + "name": "base58check", + "sha256": "6c300dfc33e598d2fe26319e13f6243fea81eaf8204cb4c6b69ef20a625319a5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "blurhash_dart": { + "dependency": "direct main", + "description": { + "name": "blurhash_dart", + "sha256": "43955b6c2e30a7d440028d1af0fa185852f3534b795cc6eb81fbf397b464409f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.1" + }, + "boolean_selector": { + "dependency": "transitive", + "description": { + "name": "boolean_selector", + "sha256": "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.2" + }, + "build_cli_annotations": { + "dependency": "transitive", + "description": { + "name": "build_cli_annotations", + "sha256": "b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.0" + }, + "camera": { + "dependency": "transitive", + "description": { + "name": "camera", + "sha256": "d6ec2cbdbe2fa8f5e0d07d8c06368fe4effa985a4a5ddade9cc58a8cd849557d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.11.2" + }, + "camera_android_camerax": { + "dependency": "transitive", + "description": { + "name": "camera_android_camerax", + "sha256": "58b8fe843a3c83fd1273c00cb35f5a8ae507f6cc9b2029bcf7e2abba499e28d8", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.6.19+1" + }, + "camera_avfoundation": { + "dependency": "transitive", + "description": { + "name": "camera_avfoundation", + "sha256": "e4aca5bccaf897b70cac87e5fdd789393310985202442837922fd40325e2733b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.21+1" + }, + "camera_platform_interface": { + "dependency": "transitive", + "description": { + "name": "camera_platform_interface", + "sha256": "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.10.0" + }, + "camera_web": { + "dependency": "transitive", + "description": { + "name": "camera_web", + "sha256": "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.5" + }, + "canonical_json": { + "dependency": "transitive", + "description": { + "name": "canonical_json", + "sha256": "d6be1dd66b420c6ac9f42e3693e09edf4ff6edfee26cb4c28c1c019fdb8c0c15", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.2" + }, + "characters": { + "dependency": "transitive", + "description": { + "name": "characters", + "sha256": "f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.4.0" + }, + "checked_yaml": { + "dependency": "transitive", + "description": { + "name": "checked_yaml", + "sha256": "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.4" + }, + "cli_config": { + "dependency": "transitive", + "description": { + "name": "cli_config", + "sha256": "ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.0" + }, + "cli_util": { + "dependency": "transitive", + "description": { + "name": "cli_util", + "sha256": "ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.4.2" + }, + "clock": { + "dependency": "transitive", + "description": { + "name": "clock", + "sha256": "fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.2" + }, + "collection": { + "dependency": "direct main", + "description": { + "name": "collection", + "sha256": "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.19.1" + }, + "convert": { + "dependency": "transitive", + "description": { + "name": "convert", + "sha256": "b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.2" + }, + "coverage": { + "dependency": "transitive", + "description": { + "name": "coverage", + "sha256": "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.15.0" + }, + "cross_file": { + "dependency": "direct main", + "description": { + "name": "cross_file", + "sha256": "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.4+2" + }, + "crypto": { + "dependency": "transitive", + "description": { + "name": "crypto", + "sha256": "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.6" + }, + "csslib": { + "dependency": "direct main", + "description": { + "name": "csslib", + "sha256": "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.2" + }, + "cupertino_http": { + "dependency": "direct main", + "description": { + "name": "cupertino_http", + "sha256": "72187f715837290a63479a5b0ae709f4fedad0ed6bd0441c275eceaa02d5abae", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.0" + }, + "cupertino_icons": { + "dependency": "direct main", + "description": { + "name": "cupertino_icons", + "sha256": "ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.8" + }, + "dart_animated_emoji": { + "dependency": "direct main", + "description": { + "name": "dart_animated_emoji", + "sha256": "0e0865f1b56e2f2979e8caa09a7d693e30133050c5c677de301e6ca4d8da945e", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.1.2" + }, + "dbus": { + "dependency": "direct main", + "description": { + "name": "dbus", + "sha256": "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.7.11" + }, + "diacritic": { + "dependency": "direct main", + "description": { + "name": "diacritic", + "sha256": "12981945ec38931748836cd76f2b38773118d0baef3c68404bdfde9566147876", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.1.6" + }, + "diffutil_dart": { + "dependency": "direct main", + "description": { + "name": "diffutil_dart", + "sha256": "5e74883aedf87f3b703cb85e815bdc1ed9208b33501556e4a8a5572af9845c81", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.0.1" + }, + "dynamic_color": { + "dependency": "direct main", + "description": { + "name": "dynamic_color", + "sha256": "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.8.1" + }, + "emoji_extension": { + "dependency": "direct main", + "description": { + "name": "emoji_extension", + "sha256": "7678a3e3fca4f2dfbce02cf8d439a81e130ce303fdc1ad90f484f57fd5ce4ba1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.0" + }, + "enhanced_enum": { + "dependency": "transitive", + "description": { + "name": "enhanced_enum", + "sha256": "074c5a8b9664799ca91e1e8b68003b8694cb19998671cbafd9c7779c13fcdecf", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.4" + }, + "equatable": { + "dependency": "transitive", + "description": { + "name": "equatable", + "sha256": "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.7" + }, + "fake_async": { + "dependency": "transitive", + "description": { + "name": "fake_async", + "sha256": "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.3.3" + }, + "fetch_api": { + "dependency": "transitive", + "description": { + "name": "fetch_api", + "sha256": "24cbd5616f3d4008c335c197bb90bfa0eb43b9e55c6de5c60d1f805092636034", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.1" + }, + "fetch_client": { + "dependency": "direct main", + "description": { + "name": "fetch_client", + "sha256": "375253f4efe64303c793fb17fe90771c591320b2ae11fb29cb5b406cc8533c00", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.4" + }, + "ffi": { + "dependency": "transitive", + "description": { + "name": "ffi", + "sha256": "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.4" + }, + "file": { + "dependency": "transitive", + "description": { + "name": "file", + "sha256": "a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "7.0.1" + }, + "file_selector": { + "dependency": "direct main", + "description": { + "name": "file_selector", + "sha256": "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.3" + }, + "file_selector_android": { + "dependency": "transitive", + "description": { + "name": "file_selector_android", + "sha256": "3015702ab73987000e7ff2df5ddc99666d2bcd65cdb243f59da35729d3be6cff", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.5.1+15" + }, + "file_selector_ios": { + "dependency": "transitive", + "description": { + "name": "file_selector_ios", + "sha256": "94b98ad950b8d40d96fee8fa88640c2e4bd8afcdd4817993bd04e20310f45420", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.5.3+1" + }, + "file_selector_linux": { + "dependency": "transitive", + "description": { + "name": "file_selector_linux", + "sha256": "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.3+2" + }, + "file_selector_macos": { + "dependency": "transitive", + "description": { + "name": "file_selector_macos", + "sha256": "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.4+3" + }, + "file_selector_platform_interface": { + "dependency": "transitive", + "description": { + "name": "file_selector_platform_interface", + "sha256": "a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.6.2" + }, + "file_selector_web": { + "dependency": "transitive", + "description": { + "name": "file_selector_web", + "sha256": "c4c0ea4224d97a60a7067eca0c8fd419e708ff830e0c83b11a48faf566cec3e7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.4+2" + }, + "file_selector_windows": { + "dependency": "transitive", + "description": { + "name": "file_selector_windows", + "sha256": "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.3+4" + }, + "fixnum": { + "dependency": "transitive", + "description": { + "name": "fixnum", + "sha256": "b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.1" + }, + "flutter": { + "dependency": "direct main", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "flutter_adaptive_scaffold": { + "dependency": "direct main", + "description": { + "name": "flutter_adaptive_scaffold", + "sha256": "5eb1d1d174304a4e67c4bb402ed38cb4a5ebdac95ce54099e91460accb33d295", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.3+1" + }, + "flutter_confetti": { + "dependency": "direct main", + "description": { + "name": "flutter_confetti", + "sha256": "7e46b82ea0adc456afc91037652bbfbd52a951804fde0708822fad5d68be6398", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.5.1" + }, + "flutter_driver": { + "dependency": "direct dev", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "flutter_highlighting": { + "dependency": "direct main", + "description": { + "name": "flutter_highlighting", + "sha256": "426770b1453e8302f8cc58455ebcaad33e3049e73ca18f9d3c83554552bf3baf", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.0+11.8.0" + }, + "flutter_html": { + "dependency": "direct main", + "description": { + "name": "flutter_html", + "sha256": "38a2fd702ffdf3243fb7441ab58aa1bc7e6922d95a50db76534de8260638558d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.0" + }, + "flutter_html_svg": { + "dependency": "direct main", + "description": { + "name": "flutter_html_svg", + "sha256": "76f59c238571333d95271817c3d94688b3c4dca2735552e481e49039d3efdb13", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.0" + }, + "flutter_html_table": { + "dependency": "direct main", + "description": { + "name": "flutter_html_table", + "sha256": "de15300b1f6d8014e1702e7edfdf3411f362c8fb753e89bac4c99215ea94a4d8", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.0" + }, + "flutter_keyboard_visibility": { + "dependency": "direct main", + "description": { + "name": "flutter_keyboard_visibility", + "sha256": "98664be7be0e3ffca00de50f7f6a287ab62c763fc8c762e0a21584584a3ff4f8", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.0" + }, + "flutter_keyboard_visibility_linux": { + "dependency": "transitive", + "description": { + "name": "flutter_keyboard_visibility_linux", + "sha256": "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.0" + }, + "flutter_keyboard_visibility_macos": { + "dependency": "transitive", + "description": { + "name": "flutter_keyboard_visibility_macos", + "sha256": "c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.0" + }, + "flutter_keyboard_visibility_platform_interface": { + "dependency": "transitive", + "description": { + "name": "flutter_keyboard_visibility_platform_interface", + "sha256": "e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "flutter_keyboard_visibility_web": { + "dependency": "transitive", + "description": { + "name": "flutter_keyboard_visibility_web", + "sha256": "d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "flutter_keyboard_visibility_windows": { + "dependency": "transitive", + "description": { + "name": "flutter_keyboard_visibility_windows", + "sha256": "fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.0" + }, + "flutter_launcher_icons": { + "dependency": "direct dev", + "description": { + "name": "flutter_launcher_icons", + "sha256": "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.14.4" + }, + "flutter_layout_grid": { + "dependency": "transitive", + "description": { + "name": "flutter_layout_grid", + "sha256": "739e568db97af031d528dfd8a80d333df0e5a310a126e087690fa42cd61dfb5f", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.8" + }, + "flutter_lints": { + "dependency": "direct dev", + "description": { + "name": "flutter_lints", + "sha256": "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.0" + }, + "flutter_local_notifications": { + "dependency": "direct main", + "description": { + "name": "flutter_local_notifications", + "sha256": "20ca0a9c82ce0c855ac62a2e580ab867f3fbea82680a90647f7953832d0850ae", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "19.4.0" + }, + "flutter_local_notifications_linux": { + "dependency": "transitive", + "description": { + "name": "flutter_local_notifications_linux", + "sha256": "e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.0" + }, + "flutter_local_notifications_platform_interface": { + "dependency": "transitive", + "description": { + "name": "flutter_local_notifications_platform_interface", + "sha256": "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "9.1.0" + }, + "flutter_local_notifications_windows": { + "dependency": "transitive", + "description": { + "name": "flutter_local_notifications_windows", + "sha256": "ed46d7ae4ec9d19e4c8fa2badac5fe27ba87a3fe387343ce726f927af074ec98", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.2" + }, + "flutter_localizations": { + "dependency": "direct main", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "flutter_openssl_crypto": { + "dependency": "direct main", + "description": { + "name": "flutter_openssl_crypto", + "sha256": "293b4fcda13ab0710645a16e82f3d5b7de19bfc0ab2d06bcdb87637222eda5e1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.5.0" + }, + "flutter_plugin_android_lifecycle": { + "dependency": "transitive", + "description": { + "name": "flutter_plugin_android_lifecycle", + "sha256": "6382ce712ff69b0f719640ce957559dde459e55ecd433c767e06d139ddf16cab", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.29" + }, + "flutter_rust_bridge": { + "dependency": "transitive", + "description": { + "name": "flutter_rust_bridge", + "sha256": "b416ff56002789e636244fb4cc449f587656eff995e5a7169457eb0593fcaddb", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.10.0" + }, + "flutter_secure_storage": { + "dependency": "direct main", + "description": { + "name": "flutter_secure_storage", + "sha256": "f7eceb0bc6f4fd0441e29d43cab9ac2a1c5ffd7ea7b64075136b718c46954874", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "10.0.0-beta.4" + }, + "flutter_secure_storage_darwin": { + "dependency": "transitive", + "description": { + "name": "flutter_secure_storage_darwin", + "sha256": "f226f2a572bed96bc6542198ebaec227150786e34311d455a7e2d3d06d951845", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.1.0" + }, + "flutter_secure_storage_linux": { + "dependency": "transitive", + "description": { + "name": "flutter_secure_storage_linux", + "sha256": "9b4b73127e857cd3117d43a70fa3dddadb6e0b253be62e6a6ab85caa0742182c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.1" + }, + "flutter_secure_storage_platform_interface": { + "dependency": "transitive", + "description": { + "name": "flutter_secure_storage_platform_interface", + "sha256": "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.1" + }, + "flutter_secure_storage_web": { + "dependency": "transitive", + "description": { + "name": "flutter_secure_storage_web", + "sha256": "4c3f233e739545c6cb09286eeec1cc4744138372b985113acc904f7263bef517", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "flutter_secure_storage_windows": { + "dependency": "transitive", + "description": { + "name": "flutter_secure_storage_windows", + "sha256": "ff32af20f70a8d0e59b2938fc92de35b54a74671041c814275afd80e27df9f21", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.0.0" + }, + "flutter_svg": { + "dependency": "direct main", + "description": { + "name": "flutter_svg", + "sha256": "cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.0" + }, + "flutter_test": { + "dependency": "direct dev", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "flutter_typeahead": { + "dependency": "direct main", + "description": { + "name": "flutter_typeahead", + "sha256": "d64712c65db240b1057559b952398ebb6e498077baeebf9b0731dade62438a6d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "5.2.0" + }, + "flutter_vodozemac": { + "dependency": "direct main", + "description": { + "name": "flutter_vodozemac", + "sha256": "2405ca121b84d1cd83200a14021022e1691b123a23bcefc36adc7740cefbc1f9", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.2" + }, + "flutter_web_plugins": { + "dependency": "transitive", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "flutter_zxing": { + "dependency": "direct main", + "description": { + "name": "flutter_zxing", + "sha256": "dbcd89da2c9aa84f48d7d7e1ba436825f8656a69b142abb7bcdb7c2d9c22d48c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.1" + }, + "frontend_server_client": { + "dependency": "transitive", + "description": { + "name": "frontend_server_client", + "sha256": "f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.0.0" + }, + "fuchsia_remote_debug_protocol": { + "dependency": "transitive", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "glob": { + "dependency": "transitive", + "description": { + "name": "glob", + "sha256": "c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.3" + }, + "go_router": { + "dependency": "direct main", + "description": { + "name": "go_router", + "sha256": "8b1f37dfaf6e958c6b872322db06f946509433bec3de753c3491a42ae9ec2b48", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "16.1.0" + }, + "gtk": { + "dependency": "transitive", + "description": { + "name": "gtk", + "sha256": "e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.0" + }, + "highlighting": { + "dependency": "direct main", + "description": { + "name": "highlighting", + "sha256": "196005ed9c98ee559939fcecd466fa941b9e99b3a93394691b86780ad4da50f3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.9.0+11.8.0" + }, + "html": { + "dependency": "direct main", + "description": { + "name": "html", + "sha256": "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.15.6" + }, + "html_unescape": { + "dependency": "transitive", + "description": { + "name": "html_unescape", + "sha256": "15362d7a18f19d7b742ef8dcb811f5fd2a2df98db9f80ea393c075189e0b61e3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "http": { + "dependency": "direct main", + "description": { + "name": "http", + "sha256": "bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.5.0" + }, + "http_parser": { + "dependency": "transitive", + "description": { + "name": "http_parser", + "sha256": "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.1.2" + }, + "http_profile": { + "dependency": "transitive", + "description": { + "name": "http_profile", + "sha256": "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.1.0" + }, + "image": { + "dependency": "direct main", + "description": { + "name": "image", + "sha256": "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.5.4" + }, + "image_picker": { + "dependency": "direct main", + "description": { + "name": "image_picker", + "sha256": "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.2" + }, + "image_picker_android": { + "dependency": "transitive", + "description": { + "name": "image_picker_android", + "sha256": "b08e9a04d0f8d91f4a6e767a745b9871bfbc585410205c311d0492de20a7ccd6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.8.12+25" + }, + "image_picker_for_web": { + "dependency": "transitive", + "description": { + "name": "image_picker_for_web", + "sha256": "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.6" + }, + "image_picker_ios": { + "dependency": "transitive", + "description": { + "name": "image_picker_ios", + "sha256": "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.8.12+2" + }, + "image_picker_linux": { + "dependency": "transitive", + "description": { + "name": "image_picker_linux", + "sha256": "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.1+2" + }, + "image_picker_macos": { + "dependency": "transitive", + "description": { + "name": "image_picker_macos", + "sha256": "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.1+2" + }, + "image_picker_platform_interface": { + "dependency": "transitive", + "description": { + "name": "image_picker_platform_interface", + "sha256": "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.10.1" + }, + "image_picker_windows": { + "dependency": "transitive", + "description": { + "name": "image_picker_windows", + "sha256": "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.1+1" + }, + "import_sorter": { + "dependency": "direct main", + "description": { + "name": "import_sorter", + "sha256": "eb15738ccead84e62c31e0208ea4e3104415efcd4972b86906ca64a1187d0836", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.6.0" + }, + "integration_test": { + "dependency": "direct dev", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "intl": { + "dependency": "direct main", + "description": { + "name": "intl", + "sha256": "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.20.2" + }, + "io": { + "dependency": "transitive", + "description": { + "name": "io", + "sha256": "dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.5" + }, + "js": { + "dependency": "transitive", + "description": { + "name": "js", + "sha256": "f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.6.7" + }, + "json_annotation": { + "dependency": "transitive", + "description": { + "name": "json_annotation", + "sha256": "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.9.0" + }, + "just_audio": { + "dependency": "direct main", + "description": { + "name": "just_audio", + "sha256": "679637a3ec5b6e00f36472f5a3663667df00ee4822cbf5dafca0f568c710960a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.4" + }, + "just_audio_media_kit": { + "dependency": "direct main", + "description": { + "name": "just_audio_media_kit", + "sha256": "f3cf04c3a50339709e87e90b4e841eef4364ab4be2bdbac0c54cc48679f84d23", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.0" + }, + "just_audio_platform_interface": { + "dependency": "transitive", + "description": { + "name": "just_audio_platform_interface", + "sha256": "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.6.0" + }, + "just_audio_web": { + "dependency": "transitive", + "description": { + "name": "just_audio_web", + "sha256": "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.4.16" + }, + "just_waveform": { + "dependency": "direct main", + "description": { + "name": "just_waveform", + "sha256": "8c65acd24f13b866e3377f07f8869e823f3f2d8b734938f4e6688075af40b4f2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.0.7" + }, + "leak_tracker": { + "dependency": "transitive", + "description": { + "name": "leak_tracker", + "sha256": "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "10.0.9" + }, + "leak_tracker_flutter_testing": { + "dependency": "transitive", + "description": { + "name": "leak_tracker_flutter_testing", + "sha256": "f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.9" + }, + "leak_tracker_testing": { + "dependency": "transitive", + "description": { + "name": "leak_tracker_testing", + "sha256": "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.1" + }, + "linkify": { + "dependency": "direct main", + "description": { + "name": "linkify", + "sha256": "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "5.0.0" + }, + "lints": { + "dependency": "transitive", + "description": { + "name": "lints", + "sha256": "a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.0" + }, + "list_counter": { + "dependency": "transitive", + "description": { + "name": "list_counter", + "sha256": "c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.2" + }, + "locale_names": { + "dependency": "direct main", + "description": { + "name": "locale_names", + "sha256": "7a89ca54072f4f13d0f5df5a9ba69337554bf2fd057d1dd2a238898f3f159374", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.1" + }, + "logging": { + "dependency": "transitive", + "description": { + "name": "logging", + "sha256": "c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.3.0" + }, + "lottie": { + "dependency": "direct main", + "description": { + "name": "lottie", + "sha256": "c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.3.1" + }, + "markdown": { + "dependency": "transitive", + "description": { + "name": "markdown", + "sha256": "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "7.3.0" + }, + "matcher": { + "dependency": "transitive", + "description": { + "name": "matcher", + "sha256": "dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.12.17" + }, + "material_color_utilities": { + "dependency": "transitive", + "description": { + "name": "material_color_utilities", + "sha256": "f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.11.1" + }, + "matrix": { + "dependency": "direct main", + "description": { + "path": ".", + "ref": "braid/msc3861-native-oidc", + "resolved-ref": "82ad90573e0e5e1ccb2cf1e669a5861bd6db351c", + "url": "https://github.com/TheOneWithTheBraid/matrix-dart-sdk.git" + }, + "source": "git", + "version": "1.1.0" + }, + "matrix_homeserver_recommendations": { + "dependency": "direct main", + "description": { + "name": "matrix_homeserver_recommendations", + "sha256": "48cd67146dd80b925c1cce1604da4712e7963b490d31801bad70b51ff8e30cd2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.4.1" + }, + "media_kit": { + "dependency": "direct main", + "description": { + "path": "media_kit", + "ref": "braid/stub-template", + "resolved-ref": "215972e56ceb6036b51d1dc8803d5e0ab489bfe1", + "url": "https://github.com/TheOneWithTheBraid/media-kit.git" + }, + "source": "git", + "version": "1.2.0" + }, + "media_kit_libs_android_video": { + "dependency": "direct overridden", + "description": { + "path": "libs/android/media_kit_libs_android_video", + "ref": "main", + "resolved-ref": "ad84c59faa2b871926cb31516bdeec65d7676884", + "url": "https://github.com/Predidit/media-kit.git" + }, + "source": "git", + "version": "1.3.6" + }, + "media_kit_libs_ios_video": { + "dependency": "transitive", + "description": { + "name": "media_kit_libs_ios_video", + "sha256": "b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.4" + }, + "media_kit_libs_linux": { + "dependency": "transitive", + "description": { + "name": "media_kit_libs_linux", + "sha256": "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.1" + }, + "media_kit_libs_macos_video": { + "dependency": "transitive", + "description": { + "name": "media_kit_libs_macos_video", + "sha256": "f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.4" + }, + "media_kit_libs_video": { + "dependency": "direct main", + "description": { + "name": "media_kit_libs_video", + "sha256": "958cc55e7065d9d01f52a2842dab2a0812a92add18489f1006d864fb5e42a3ef", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.6" + }, + "media_kit_libs_windows_video": { + "dependency": "transitive", + "description": { + "name": "media_kit_libs_windows_video", + "sha256": "dff76da2778729ab650229e6b4ec6ec111eb5151431002cbd7ea304ff1f112ab", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.11" + }, + "media_kit_video": { + "dependency": "direct main", + "description": { + "name": "media_kit_video", + "sha256": "a656a9463298c1adc64c57f2d012874f7f2900f0c614d9545a3e7b8bb9e2137b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.3.0" + }, + "media_store_plus": { + "dependency": "direct main", + "description": { + "name": "media_store_plus", + "sha256": "4b4971365e00a4ed9fde14abf40d7c27475b66b8bba9bf43478ae2ecb449df20", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.1.3" + }, + "meta": { + "dependency": "transitive", + "description": { + "name": "meta", + "sha256": "e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.16.0" + }, + "mime": { + "dependency": "direct main", + "description": { + "name": "mime", + "sha256": "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "objective_c": { + "dependency": "transitive", + "description": { + "name": "objective_c", + "sha256": "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "7.1.0" + }, + "package_config": { + "dependency": "transitive", + "description": { + "name": "package_config", + "sha256": "f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.0" + }, + "package_info_plus": { + "dependency": "transitive", + "description": { + "name": "package_info_plus", + "sha256": "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "8.3.1" + }, + "package_info_plus_platform_interface": { + "dependency": "transitive", + "description": { + "name": "package_info_plus_platform_interface", + "sha256": "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.2.1" + }, + "path": { + "dependency": "transitive", + "description": { + "name": "path", + "sha256": "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.9.1" + }, + "path_parsing": { + "dependency": "transitive", + "description": { + "name": "path_parsing", + "sha256": "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.0" + }, + "path_provider": { + "dependency": "direct main", + "description": { + "name": "path_provider", + "sha256": "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.5" + }, + "path_provider_android": { + "dependency": "transitive", + "description": { + "name": "path_provider_android", + "sha256": "d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.17" + }, + "path_provider_foundation": { + "dependency": "transitive", + "description": { + "name": "path_provider_foundation", + "sha256": "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.1" + }, + "path_provider_linux": { + "dependency": "transitive", + "description": { + "name": "path_provider_linux", + "sha256": "f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.1" + }, + "path_provider_platform_interface": { + "dependency": "transitive", + "description": { + "name": "path_provider_platform_interface", + "sha256": "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.2" + }, + "path_provider_windows": { + "dependency": "transitive", + "description": { + "name": "path_provider_windows", + "sha256": "bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.0" + }, + "petitparser": { + "dependency": "transitive", + "description": { + "name": "petitparser", + "sha256": "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.1.0" + }, + "platform": { + "dependency": "transitive", + "description": { + "name": "platform", + "sha256": "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.6" + }, + "plugin_platform_interface": { + "dependency": "transitive", + "description": { + "name": "plugin_platform_interface", + "sha256": "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.8" + }, + "pointer_interceptor": { + "dependency": "transitive", + "description": { + "name": "pointer_interceptor", + "sha256": "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.1+2" + }, + "pointer_interceptor_ios": { + "dependency": "transitive", + "description": { + "name": "pointer_interceptor_ios", + "sha256": "a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.1" + }, + "pointer_interceptor_platform_interface": { + "dependency": "transitive", + "description": { + "name": "pointer_interceptor_platform_interface", + "sha256": "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.0+1" + }, + "pointer_interceptor_web": { + "dependency": "transitive", + "description": { + "name": "pointer_interceptor_web", + "sha256": "460b600e71de6fcea2b3d5f662c92293c049c4319e27f0829310e5a953b3ee2a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.3" + }, + "pool": { + "dependency": "transitive", + "description": { + "name": "pool", + "sha256": "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.5.1" + }, + "posix": { + "dependency": "transitive", + "description": { + "name": "posix", + "sha256": "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.3" + }, + "process": { + "dependency": "transitive", + "description": { + "name": "process", + "sha256": "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "5.0.3" + }, + "pub_semver": { + "dependency": "transitive", + "description": { + "name": "pub_semver", + "sha256": "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.2.0" + }, + "qr": { + "dependency": "transitive", + "description": { + "name": "qr", + "sha256": "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.2" + }, + "quiver": { + "dependency": "transitive", + "description": { + "name": "quiver", + "sha256": "ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.2.2" + }, + "random_string": { + "dependency": "transitive", + "description": { + "name": "random_string", + "sha256": "03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.1" + }, + "receive_sharing_intent": { + "dependency": "direct main", + "description": { + "name": "receive_sharing_intent", + "sha256": "ec76056e4d258ad708e76d85591d933678625318e411564dcb9059048ca3a593", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.8.1" + }, + "rxdart": { + "dependency": "transitive", + "description": { + "name": "rxdart", + "sha256": "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.28.0" + }, + "safe_local_storage": { + "dependency": "transitive", + "description": { + "name": "safe_local_storage", + "sha256": "e9a21b6fec7a8aa62cc2585ff4c1b127df42f3185adbd2aca66b47abe2e80236", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.1" + }, + "screen_brightness_android": { + "dependency": "transitive", + "description": { + "name": "screen_brightness_android", + "sha256": "fb5fa43cb89d0c9b8534556c427db1e97e46594ac5d66ebdcf16063b773d54ed", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.2" + }, + "screen_brightness_platform_interface": { + "dependency": "transitive", + "description": { + "name": "screen_brightness_platform_interface", + "sha256": "737bd47b57746bc4291cab1b8a5843ee881af499514881b0247ec77447ee769c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.0" + }, + "sdp_transform": { + "dependency": "transitive", + "description": { + "name": "sdp_transform", + "sha256": "73e412a5279a5c2de74001535208e20fff88f225c9a4571af0f7146202755e45", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.2" + }, + "sentry": { + "dependency": "direct main", + "description": { + "name": "sentry", + "sha256": "d9f3dcf1ecdd600cf9ce134f622383adde5423ecfdaf0ca9b20fbc1c44849337", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "9.6.0" + }, + "share_plus": { + "dependency": "direct main", + "description": { + "name": "share_plus", + "sha256": "d7dc0630a923883c6328ca31b89aa682bacbf2f8304162d29f7c6aaff03a27a1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "11.1.0" + }, + "share_plus_platform_interface": { + "dependency": "transitive", + "description": { + "name": "share_plus_platform_interface", + "sha256": "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.1.0" + }, + "sky_engine": { + "dependency": "transitive", + "description": "flutter", + "source": "sdk", + "version": "0.0.0" + }, + "slugify": { + "dependency": "transitive", + "description": { + "name": "slugify", + "sha256": "b272501565cb28050cac2d96b7bf28a2d24c8dae359280361d124f3093d337c3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.0" + }, + "source_map_stack_trace": { + "dependency": "transitive", + "description": { + "name": "source_map_stack_trace", + "sha256": "c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.2" + }, + "source_maps": { + "dependency": "transitive", + "description": { + "name": "source_maps", + "sha256": "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.13" + }, + "source_span": { + "dependency": "transitive", + "description": { + "name": "source_span", + "sha256": "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.10.1" + }, + "sprintf": { + "dependency": "transitive", + "description": { + "name": "sprintf", + "sha256": "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "7.0.0" + }, + "sqflite": { + "dependency": "direct main", + "description": { + "name": "sqflite", + "sha256": "e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.2" + }, + "sqflite_android": { + "dependency": "transitive", + "description": { + "name": "sqflite_android", + "sha256": "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.1" + }, + "sqflite_common": { + "dependency": "transitive", + "description": { + "name": "sqflite_common", + "sha256": "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.5.6" + }, + "sqflite_common_ffi": { + "dependency": "direct main", + "description": { + "name": "sqflite_common_ffi", + "sha256": "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.6" + }, + "sqflite_darwin": { + "dependency": "transitive", + "description": { + "name": "sqflite_darwin", + "sha256": "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.2" + }, + "sqflite_platform_interface": { + "dependency": "transitive", + "description": { + "name": "sqflite_platform_interface", + "sha256": "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.0" + }, + "sqlcipher_flutter_libs": { + "dependency": "direct main", + "description": { + "name": "sqlcipher_flutter_libs", + "sha256": "dd1fcc74d5baf3c36ad53e2652b2d06c9f8747494a3ccde0076e88b159dfe622", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.6.8" + }, + "sqlite3": { + "dependency": "transitive", + "description": { + "name": "sqlite3", + "sha256": "f393d92c71bdcc118d6203d07c991b9be0f84b1a6f89dd4f7eed348131329924", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.9.0" + }, + "stack_trace": { + "dependency": "transitive", + "description": { + "name": "stack_trace", + "sha256": "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.12.1" + }, + "stream_channel": { + "dependency": "transitive", + "description": { + "name": "stream_channel", + "sha256": "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.4" + }, + "stream_transform": { + "dependency": "transitive", + "description": { + "name": "stream_transform", + "sha256": "ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.1" + }, + "string_scanner": { + "dependency": "transitive", + "description": { + "name": "string_scanner", + "sha256": "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.4.1" + }, + "sync_http": { + "dependency": "transitive", + "description": { + "name": "sync_http", + "sha256": "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.1" + }, + "synchronized": { + "dependency": "transitive", + "description": { + "name": "synchronized", + "sha256": "c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.4.0" + }, + "term_glyph": { + "dependency": "transitive", + "description": { + "name": "term_glyph", + "sha256": "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.2" + }, + "test_api": { + "dependency": "transitive", + "description": { + "name": "test_api", + "sha256": "fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.7.4" + }, + "test_core": { + "dependency": "transitive", + "description": { + "name": "test_core", + "sha256": "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.6.8" + }, + "timezone": { + "dependency": "transitive", + "description": { + "name": "timezone", + "sha256": "dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.10.1" + }, + "tint": { + "dependency": "transitive", + "description": { + "name": "tint", + "sha256": "9652d9a589f4536d5e392cf790263d120474f15da3cf1bee7f1fdb31b4de5f46", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.1" + }, + "tuple": { + "dependency": "transitive", + "description": { + "name": "tuple", + "sha256": "a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.0.2" + }, + "typed_data": { + "dependency": "transitive", + "description": { + "name": "typed_data", + "sha256": "f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.4.0" + }, + "unifiedpush": { + "dependency": "direct main", + "description": { + "name": "unifiedpush", + "sha256": "1418375efb580af9640de4eaf4209cb6481f9a48792648ced3051f30e67d9568", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.0.2" + }, + "unifiedpush_android": { + "dependency": "transitive", + "description": { + "name": "unifiedpush_android", + "sha256": "2f25db8eb2fc3183bf2e43db89fff20b2587adc1c361e1d1e06b223a0d45b50a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.1" + }, + "unifiedpush_platform_interface": { + "dependency": "transitive", + "description": { + "name": "unifiedpush_platform_interface", + "sha256": "bb49d2748211520e35e0374ab816faa8a2c635267e71909d334ad868d532eba5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.1" + }, + "universal_platform": { + "dependency": "transitive", + "description": { + "name": "universal_platform", + "sha256": "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.0" + }, + "unorm_dart": { + "dependency": "direct main", + "description": { + "name": "unorm_dart", + "sha256": "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.0" + }, + "uri_parser": { + "dependency": "transitive", + "description": { + "name": "uri_parser", + "sha256": "ff4d2c720aca3f4f7d5445e23b11b2d15ef8af5ddce5164643f38ff962dcb270", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.0.0" + }, + "url_launcher": { + "dependency": "direct main", + "description": { + "name": "url_launcher", + "sha256": "f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.3.2" + }, + "url_launcher_android": { + "dependency": "transitive", + "description": { + "name": "url_launcher_android", + "sha256": "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.3.17" + }, + "url_launcher_ios": { + "dependency": "transitive", + "description": { + "name": "url_launcher_ios", + "sha256": "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.3.3" + }, + "url_launcher_linux": { + "dependency": "transitive", + "description": { + "name": "url_launcher_linux", + "sha256": "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.2.1" + }, + "url_launcher_macos": { + "dependency": "transitive", + "description": { + "name": "url_launcher_macos", + "sha256": "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.2.2" + }, + "url_launcher_platform_interface": { + "dependency": "transitive", + "description": { + "name": "url_launcher_platform_interface", + "sha256": "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.3.2" + }, + "url_launcher_web": { + "dependency": "transitive", + "description": { + "name": "url_launcher_web", + "sha256": "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.4.1" + }, + "url_launcher_windows": { + "dependency": "transitive", + "description": { + "name": "url_launcher_windows", + "sha256": "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.4" + }, + "uuid": { + "dependency": "transitive", + "description": { + "name": "uuid", + "sha256": "a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "4.5.1" + }, + "vector_graphics": { + "dependency": "transitive", + "description": { + "name": "vector_graphics", + "sha256": "a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.19" + }, + "vector_graphics_codec": { + "dependency": "transitive", + "description": { + "name": "vector_graphics_codec", + "sha256": "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.13" + }, + "vector_graphics_compiler": { + "dependency": "transitive", + "description": { + "name": "vector_graphics_compiler", + "sha256": "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.17" + }, + "vector_math": { + "dependency": "transitive", + "description": { + "name": "vector_math", + "sha256": "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "2.1.4" + }, + "visibility_detector": { + "dependency": "direct main", + "description": { + "name": "visibility_detector", + "sha256": "dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.4.0+2" + }, + "vm_service": { + "dependency": "transitive", + "description": { + "name": "vm_service", + "sha256": "ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "15.0.0" + }, + "vodozemac": { + "dependency": "direct main", + "description": { + "name": "vodozemac", + "sha256": "dba14017e042748fb22d270e8ab1d3e46965b89788dd3857dba938ec07571968", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.2.0" + }, + "volume_controller": { + "dependency": "transitive", + "description": { + "name": "volume_controller", + "sha256": "d75039e69c0d90e7810bfd47e3eedf29ff8543ea7a10392792e81f9bded7edf5", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.4.0" + }, + "wakelock_plus": { + "dependency": "transitive", + "description": { + "name": "wakelock_plus", + "sha256": "a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.3.2" + }, + "wakelock_plus_platform_interface": { + "dependency": "transitive", + "description": { + "name": "wakelock_plus_platform_interface", + "sha256": "e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.3" + }, + "watcher": { + "dependency": "transitive", + "description": { + "name": "watcher", + "sha256": "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.2" + }, + "web": { + "dependency": "direct main", + "description": { + "name": "web", + "sha256": "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.1" + }, + "web_multiple_tab_detector": { + "dependency": "direct main", + "description": { + "name": "web_multiple_tab_detector", + "sha256": "a40d485720ea88b4e25311421d435906ba202ac33e35435403dc1c49c5ed7c4e", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "0.3.0" + }, + "web_socket": { + "dependency": "transitive", + "description": { + "name": "web_socket", + "sha256": "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.0.1" + }, + "webdriver": { + "dependency": "transitive", + "description": { + "name": "webdriver", + "sha256": "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.0" + }, + "webrtc_interface": { + "dependency": "transitive", + "description": { + "name": "webrtc_interface", + "sha256": "86fe3afc81a08481dfb25cf14a5a94e27062ecef25544783f352c914e0bbc1ca", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.2.2+hotfix.2" + }, + "win32": { + "dependency": "transitive", + "description": { + "name": "win32", + "sha256": "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "5.14.0" + }, + "xdg_directories": { + "dependency": "transitive", + "description": { + "name": "xdg_directories", + "sha256": "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "1.1.0" + }, + "xml": { + "dependency": "transitive", + "description": { + "name": "xml", + "sha256": "b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "6.5.0" + }, + "yaml": { + "dependency": "transitive", + "description": { + "name": "yaml", + "sha256": "b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce", + "url": "https://pub.dev" + }, + "source": "hosted", + "version": "3.1.3" + } + }, + "sdks": { + "dart": ">=3.8.0 <4.0.0", + "flutter": ">=3.29.0" + } +} diff --git a/modules/common-modules/pkgs/prostudiomasters.nix b/modules/common-modules/pkgs/prostudiomasters.nix new file mode 100644 index 00000000..1a3ad015 --- /dev/null +++ b/modules/common-modules/pkgs/prostudiomasters.nix @@ -0,0 +1,33 @@ +{ + fetchurl, + appimageTools, + writeShellScript, +}: let + pname = "prostudiomasters"; + version = "2.5.6"; + src = fetchurl { + url = "https://download.prostudiomasters.com/linux/ProStudioMasters-${version}.AppImage"; + hash = "sha256-7owOwdcucFfl+JsVj+Seau2KOz0J4P/ep7WrBSNSmbs="; + }; + + # Create the base AppImage wrapper + baseApp = appimageTools.wrapType2 { + inherit pname version src; + }; + + # Create a wrapper script that automatically adds the --in-process-gpu flag + wrapper = writeShellScript "prostudiomasters-wrapper" '' + exec ${baseApp}/bin/prostudiomasters --in-process-gpu "$@" + ''; +in + # Override the base app to use our wrapper script + baseApp.overrideAttrs (oldAttrs: { + buildCommand = + oldAttrs.buildCommand + + '' + # Replace the original binary with our wrapper + rm $out/bin/prostudiomasters + cp ${wrapper} $out/bin/prostudiomasters + chmod +x $out/bin/prostudiomasters + ''; + }) diff --git a/modules/common-modules/pkgs/python/default.nix b/modules/common-modules/pkgs/python/default.nix new file mode 100644 index 00000000..f69c5122 --- /dev/null +++ b/modules/common-modules/pkgs/python/default.nix @@ -0,0 +1,18 @@ +{...}: { + nixpkgs.overlays = [ + (final: prev: { + python3 = prev.python3.override { + packageOverrides = pythonPrev: pythonFinal: { + h3 = pythonPrev.callPackage ./h3.nix {h3 = final.h3;}; + pygeofilter = pythonPrev.callPackage ./pygeofilter.nix {}; + pygeoif = pythonPrev.callPackage ./pygeoif.nix {}; + rfeed = pythonPrev.callPackage ./rfeed.nix {}; + pyexiv2 = pythonPrev.callPackage ./pyexiv2.nix {}; + geojson-pydantic = pythonPrev.callPackage ./geojson-pydantic.nix {}; + geopic-tag-reader = pythonPrev.callPackage ./geopic-tag-reader.nix {}; + }; + }; + python3Packages = final.python3.pkgs; + }) + ]; +} diff --git a/modules/common-modules/pkgs/python/geojson-pydantic.nix b/modules/common-modules/pkgs/python/geojson-pydantic.nix new file mode 100644 index 00000000..96ec6b5e --- /dev/null +++ b/modules/common-modules/pkgs/python/geojson-pydantic.nix @@ -0,0 +1,48 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + flit-core, + pydantic, + geojson, + ... +}: let + pname = "geojson_pydantic"; + version = "2.0.0"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-ti6LRFAt0a1Ri19zkDWoGSSnb5gMvbOk6JFu+RO+JC4="; + }; + + build-system = [ + flit-core + ]; + + dependencies = [ + pydantic + geojson + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["geojson_pydantic"]; + + meta = with lib; { + description = "Pydantic models for GeoJSON objects"; + homepage = "https://github.com/developmentseed/geojson-pydantic"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/geopic-tag-reader.nix b/modules/common-modules/pkgs/python/geopic-tag-reader.nix new file mode 100644 index 00000000..bd8451fb --- /dev/null +++ b/modules/common-modules/pkgs/python/geopic-tag-reader.nix @@ -0,0 +1,70 @@ +{ + lib, + fetchFromGitLab, + buildPythonPackage, + flit-core, + typer, + xmltodict, + timezonefinder, + pytz, + types-pytz, + types-python-dateutil, + rtree, + python-dateutil, + pyexiv2, + ... +}: let + pname = "geopic-tag-reader"; + version = "1.8.0"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchFromGitLab { + owner = "panoramax"; + repo = "server/geo-picture-tag-reader"; + rev = version; + sha256 = "0lzf5xxxcdqmq28bpvgpkxf5jxmh2nawwa4rl4yg04bdsi16rf1j"; + }; + + build-system = [ + flit-core + ]; + + dependencies = [ + typer + xmltodict + pyexiv2 + timezonefinder + pytz + types-pytz + types-python-dateutil + rtree + ]; + + optional-dependencies = { + write-exif = [ + python-dateutil + types-python-dateutil + ]; + }; + + # Skip tests as they may require network access or specific setup + doCheck = false; + + # Disable runtime dependencies check as some dependencies might have issues + dontCheckRuntimeDeps = true; + + # Disable imports check initially to avoid dependency issues + pythonImportsCheck = []; + + meta = with lib; { + description = "GeoPic Tag Reader - Python library to read and write standardized metadata from geolocated pictures EXIF metadata"; + homepage = "https://gitlab.com/panoramax/server/geo-picture-tag-reader"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/h3.nix b/modules/common-modules/pkgs/python/h3.nix new file mode 100644 index 00000000..2dc3d26e --- /dev/null +++ b/modules/common-modules/pkgs/python/h3.nix @@ -0,0 +1,81 @@ +{ + autoPatchelfHook, + buildPythonPackage, + cmake, + cython, + fetchFromGitHub, + h3, + lib, + ninja, + numpy, + pytestCheckHook, + pytest-cov-stub, + scikit-build-core, + stdenv, +}: +buildPythonPackage rec { + pname = "h3"; + version = "4.3.1"; + pyproject = true; + + # pypi version does not include tests + src = fetchFromGitHub { + owner = "uber"; + repo = "h3-py"; + tag = "v${version}"; + hash = "sha256-zt7zbBgSp2P9q7mObZeQZpW9Szip62dAYdPZ2cGTmi4="; + }; + + dontConfigure = true; + + nativeCheckInputs = [ + pytestCheckHook + pytest-cov-stub + ]; + + build-system = + [ + scikit-build-core + cmake + cython + ninja + ] + ++ lib.optionals stdenv.hostPlatform.isLinux [ + # On Linux the .so files ends up referring to libh3.so instead of the full + # Nix store path. I'm not sure why this is happening! On Darwin it works + # fine. + autoPatchelfHook + ]; + + # This is not needed per-se, it's only added for autoPatchelfHook to work + # correctly. See the note above ^^ + buildInputs = lib.optionals stdenv.hostPlatform.isLinux [h3]; + + dependencies = [numpy]; + + # The following prePatch replaces the h3lib compilation with using the h3 packaged in nixpkgs. + # + # - Remove the h3lib submodule. + # - Patch CMakeLists to avoid building h3lib, and use h3 instead. + prePatch = let + cmakeCommands = '' + include_directories(${lib.getDev h3}/include/h3) + link_directories(${h3}/lib) + ''; + in '' + rm -r src/h3lib + substituteInPlace CMakeLists.txt \ + --replace-fail "add_subdirectory(src/h3lib)" "${cmakeCommands}" \ + --replace-fail "\''${CMAKE_CURRENT_BINARY_DIR}/src/h3lib/src/h3lib/include/h3api.h" "${lib.getDev h3}/include/h3/h3api.h" + ''; + + # Extra check to make sure we can import it from Python + pythonImportsCheck = ["h3"]; + + meta = { + homepage = "https://github.com/uber/h3-py"; + description = "Hierarchical hexagonal geospatial indexing system"; + license = lib.licenses.asl20; + maintainers = [lib.maintainers.kalbasit]; + }; +} diff --git a/modules/common-modules/pkgs/python/pyexiv2.nix b/modules/common-modules/pkgs/python/pyexiv2.nix new file mode 100644 index 00000000..69fa5376 --- /dev/null +++ b/modules/common-modules/pkgs/python/pyexiv2.nix @@ -0,0 +1,49 @@ +{ + lib, + fetchFromGitHub, + buildPythonPackage, + exiv2, + boost, + pybind11, + setuptools, + ... +}: let + pname = "pyexiv2"; + version = "2.15.3"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + build-system = [setuptools]; + + src = fetchFromGitHub { + owner = "LeoHsiao1"; + repo = "pyexiv2"; + rev = "v${version}"; + sha256 = "sha256-83bFMaoXncvhRJNcCgkkC7B29wR5pjuLO/EdkQdqxxo="; + }; + + buildInputs = [ + exiv2 + boost + ]; + + nativeBuildInputs = [ + pybind11 + ]; + + # Skip tests as they may require specific test images + doCheck = false; + + # Disable runtime dependencies check initially + dontCheckRuntimeDeps = true; + + meta = with lib; { + description = "Python binding to the library exiv2"; + homepage = "https://github.com/LeoHsiao1/pyexiv2"; + license = licenses.gpl3Plus; + maintainers = []; + platforms = platforms.linux; + }; + } diff --git a/modules/common-modules/pkgs/python/pygeofilter.nix b/modules/common-modules/pkgs/python/pygeofilter.nix new file mode 100644 index 00000000..aa310f98 --- /dev/null +++ b/modules/common-modules/pkgs/python/pygeofilter.nix @@ -0,0 +1,52 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + wheel, + lark, + python-dateutil, + shapely, + ... +}: let + pname = "pygeofilter"; + version = "0.3.1"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-+SvAYiCZ+H/os23nq92GBZ1hWontYIInNwgiI6V44VA="; + }; + + build-system = [ + setuptools + wheel + ]; + + dependencies = [ + lark + python-dateutil + shapely + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["pygeofilter"]; + + meta = with lib; { + description = "A pure Python parser implementation of OGC filtering standards"; + homepage = "https://github.com/geopython/pygeofilter"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/pygeoif.nix b/modules/common-modules/pkgs/python/pygeoif.nix new file mode 100644 index 00000000..12b8b122 --- /dev/null +++ b/modules/common-modules/pkgs/python/pygeoif.nix @@ -0,0 +1,48 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + wheel, + typing-extensions, + ... +}: let + pname = "pygeoif"; + version = "1.5.1"; +in + buildPythonPackage { + inherit pname version; + + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-8nprah7Lh66swrUbzFnKeb5w7RKgEE3oYBR4shPdXYE="; + }; + + build-system = [ + setuptools + wheel + ]; + + dependencies = [ + typing-extensions + ]; + + # Skip tests as they may require specific setup + doCheck = false; + + # Disable runtime dependencies check + dontCheckRuntimeDeps = true; + + # Basic imports check + pythonImportsCheck = ["pygeoif"]; + + meta = with lib; { + description = "A basic implementation of the __geo_interface__"; + homepage = "https://github.com/cleder/pygeoif"; + license = licenses.lgpl21Plus; + maintainers = []; + platforms = platforms.all; + }; + } diff --git a/modules/common-modules/pkgs/python/rfeed.nix b/modules/common-modules/pkgs/python/rfeed.nix new file mode 100644 index 00000000..0be8ab99 --- /dev/null +++ b/modules/common-modules/pkgs/python/rfeed.nix @@ -0,0 +1,40 @@ +{ + lib, + fetchPypi, + buildPythonPackage, + setuptools, + python-dateutil, +}: +buildPythonPackage rec { + pname = "rfeed"; + version = "1.1.1"; + pyproject = true; + + src = fetchPypi { + inherit pname version; + hash = "sha256-qpUG8oZrdPWjItOUoUpjwZpoJcLZR1X/GdRt0eJDSBk="; + }; + + build-system = [ + setuptools + ]; + + dependencies = [ + python-dateutil + ]; + + # No tests available in the package + doCheck = false; + + pythonImportsCheck = [ + "rfeed" + ]; + + meta = with lib; { + description = "RSS feed generation library for Python"; + homepage = "https://pypi.org/project/rfeed/"; + license = licenses.mit; + maintainers = []; + platforms = platforms.all; + }; +} diff --git a/modules/common-modules/pkgs/sgblur.nix b/modules/common-modules/pkgs/sgblur.nix new file mode 100644 index 00000000..d007b4e7 --- /dev/null +++ b/modules/common-modules/pkgs/sgblur.nix @@ -0,0 +1,65 @@ +{ + lib, + python3Packages, + fetchFromGitHub, + pkg-config, + libjpeg_turbo, + exiftran ? libjpeg_turbo, +}: +python3Packages.buildPythonPackage { + pname = "sgblur"; + version = "1.0.0"; + + pyproject = true; + + src = fetchFromGitHub { + owner = "cquest"; + repo = "sgblur"; + rev = "master"; + hash = "sha256-17wpif2sa021kaa1pbkry4l1967la1qd7knhngvxblrvd7jqqz4y="; + }; + + nativeBuildInputs = [ + pkg-config + ]; + + buildInputs = [ + libjpeg_turbo + exiftran + ]; + + build-system = with python3Packages; [ + setuptools + wheel + ]; + + dependencies = with python3Packages; [ + # Core dependencies from pyproject.toml + ultralytics + # pyturbojpeg # May need special handling + pillow + # uuid # Built into Python + # exifread + python-multipart + fastapi + uvicorn + requests + # piexif + pydantic-settings + pydantic + ]; + + # Skip tests as they may require GPU or specific setup + doCheck = false; + + # The package may have import issues due to system dependencies + pythonImportsCheck = []; + + meta = with lib; { + description = "Panoramax Speedy Gonzales Blurring Algorithm - AI-powered face and license plate blurring API"; + homepage = "https://github.com/cquest/sgblur"; + license = licenses.mit; + maintainers = []; + platforms = platforms.unix; + }; +} diff --git a/modules/common-modules/pkgs/webtoon-dl.nix b/modules/common-modules/pkgs/webtoon-dl.nix new file mode 100644 index 00000000..43410989 --- /dev/null +++ b/modules/common-modules/pkgs/webtoon-dl.nix @@ -0,0 +1,18 @@ +{ + buildGoModule, + fetchFromGitHub, + ... +}: +buildGoModule rec { + pname = "webtoon-dl"; + version = "0.0.10"; + + src = fetchFromGitHub { + owner = "robinovitch61"; + repo = "webtoon-dl"; + rev = "v${version}"; + hash = "sha256-geVb3LFPZxPQYARZnaqOr5sgaN6mqkEX5ZiLvg8mF5k="; + }; + + vendorHash = "sha256-NTqUygJ6b6kTnLUnJqxCo/URzaRouPLACEPi2Ob1s9w="; +} diff --git a/modules/darwin-modules/default.nix b/modules/darwin-modules/default.nix new file mode 100644 index 00000000..5f4447b9 --- /dev/null +++ b/modules/darwin-modules/default.nix @@ -0,0 +1,8 @@ +# this folder container modules that are for darwin only +{...}: { + imports = [ + ./home-manager + ./users.nix + ./system.nix + ]; +} diff --git a/modules/darwin-modules/home-manager/default.nix b/modules/darwin-modules/home-manager/default.nix new file mode 100644 index 00000000..1ebec5fe --- /dev/null +++ b/modules/darwin-modules/home-manager/default.nix @@ -0,0 +1,2 @@ +# modules in this folder are to adapt home-manager modules configs to darwin-module configs +{...}: {} diff --git a/modules/darwin-modules/system.nix b/modules/darwin-modules/system.nix new file mode 100644 index 00000000..ee56162b --- /dev/null +++ b/modules/darwin-modules/system.nix @@ -0,0 +1,27 @@ +{self, ...}: { + system.configurationRevision = self.rev or self.dirtyRev or null; + + nix = { + gc = { + automatic = true; + interval = [ + { + Hour = 4; + Minute = 15; + Weekday = 7; + } + ]; + options = "--delete-older-than 7d"; + }; + optimise = { + automatic = true; + interval = [ + { + Hour = 4; + Minute = 15; + Weekday = 7; + } + ]; + }; + }; +} diff --git a/modules/darwin-modules/users.nix b/modules/darwin-modules/users.nix new file mode 100644 index 00000000..72fd1b1c --- /dev/null +++ b/modules/darwin-modules/users.nix @@ -0,0 +1,16 @@ +{ + lib, + config, + ... +}: let + host = config.host; +in { + users = { + users = { + leyla = { + name = lib.mkForce host.users.leyla.name; + home = lib.mkForce "/home/${host.users.leyla.name}"; + }; + }; + }; +} diff --git a/modules/home-manager-modules/default.nix b/modules/home-manager-modules/default.nix new file mode 100644 index 00000000..29d34148 --- /dev/null +++ b/modules/home-manager-modules/default.nix @@ -0,0 +1,13 @@ +# this folder container modules that are for home manager only +{...}: { + imports = [ + ./sops.nix + ./user.nix + ./flipperzero.nix + ./i18n.nix + ./impermanence.nix + ./openssh.nix + ./gnome.nix + ./programs + ]; +} diff --git a/modules/home-manager-modules/flipperzero.nix b/modules/home-manager-modules/flipperzero.nix new file mode 100644 index 00000000..6354bc0f --- /dev/null +++ b/modules/home-manager-modules/flipperzero.nix @@ -0,0 +1,3 @@ +{lib, ...}: { + options.hardware.flipperzero.enable = lib.mkEnableOption "enable flipperzero hardware"; +} diff --git a/modules/home-manager-modules/gnome.nix b/modules/home-manager-modules/gnome.nix new file mode 100644 index 00000000..ab56189b --- /dev/null +++ b/modules/home-manager-modules/gnome.nix @@ -0,0 +1,203 @@ +{ + lib, + config, + pkgs, + ... +}: let + enabledExtensions = + [] + ++ lib.optional config.gnome.extensions.dash-to-dock.enable pkgs.gnomeExtensions.dash-to-dock + ++ lib.optional config.gnome.extensions.dash-to-panel.enable pkgs.gnomeExtensions.dash-to-panel; + + extensions = config.gnome.extraExtensions ++ enabledExtensions; +in { + options.gnome = { + extraWindowControls = lib.mkEnableOption "Should we add back in the minimize and maximize window controls?"; + clockFormat = lib.mkOption { + type = lib.types.enum [ + "12h" + "24h" + ]; + default = "24h"; + }; + colorScheme = lib.mkOption { + type = lib.types.enum [ + "default" + "prefer-dark" + "prefer-light" + ]; + default = "default"; + }; + accentColor = lib.mkOption { + type = lib.types.enum [ + "blue" + "teal" + "green" + "yellow" + "orange" + "red" + "pink" + "purple" + "slate" + ]; + default = "blue"; + }; + extraExtensions = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = []; + description = "The set of extensions to install and enable in the user environment."; + }; + hotkeys = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + key = lib.mkOption { + type = lib.types.strMatching "[a-zA-Z0-9-]+"; + default = builtins.replaceStrings [" " "/" "_"] ["-" "-" "-"] name; + }; + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + binding = lib.mkOption { + type = lib.types.str; + }; + command = lib.mkOption { + type = lib.types.str; + }; + }; + })); + default = {}; + }; + displayScaling = lib.mkOption { + type = lib.types.nullOr (lib.types.enum [100 125 150 175 200]); + default = null; + description = "Display scaling percentage for GNOME"; + }; + experimentalFeatures = lib.mkOption { + type = lib.types.submodule { + options = { + scaleMonitorFramebuffer = lib.mkEnableOption "scale-monitor-framebuffer experimental feature"; + }; + }; + default = {}; + description = "GNOME experimental features to enable"; + }; + + nightLight = lib.mkOption { + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "night light (blue light filter)"; + automatic = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically schedule night light based on sunset/sunrise"; + }; + fromTime = lib.mkOption { + type = lib.types.float; + default = 20.0; + description = "Start time for night light in 24-hour format (e.g., 20.0 for 8:00 PM)"; + }; + toTime = lib.mkOption { + type = lib.types.float; + default = 6.0; + description = "End time for night light in 24-hour format (e.g., 6.0 for 6:00 AM)"; + }; + temperature = lib.mkOption { + type = lib.types.int; + default = 4000; + description = "Color temperature for night light (1000-10000K, lower is warmer)"; + }; + }; + }; + default = {}; + description = "Night light configuration"; + }; + + extensions = { + dash-to-dock = { + enable = lib.mkEnableOption "Dash to Dock extension"; + options = lib.mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; + description = "Dash to Dock configuration options. If null, no custom configuration will be applied."; + }; + }; + + dash-to-panel = { + enable = lib.mkEnableOption "Dash to Panel extension"; + options = lib.mkOption { + type = lib.types.nullOr lib.types.attrs; + default = null; + description = "Dash to Panel configuration options. If null, no custom configuration will be applied."; + }; + }; + }; + }; + + config = { + home.packages = extensions; + dconf = { + settings = lib.mkMerge [ + { + "org/gnome/shell" = { + disable-user-extensions = false; # enables user extensions + enabled-extensions = builtins.map (extension: extension.extensionUuid) extensions; + }; + + "org/gnome/desktop/wm/preferences".button-layout = lib.mkIf config.gnome.extraWindowControls ":minimize,maximize,close"; + + "org/gnome/desktop/interface".color-scheme = config.gnome.colorScheme; + "org/gnome/desktop/interface".accent-color = config.gnome.accentColor; + "org/gnome/desktop/interface".clock-format = config.gnome.clockFormat; + "org/gnome/desktop/interface".text-scaling-factor = lib.mkIf (config.gnome.displayScaling != null) (config.gnome.displayScaling / 100.0); + + "org/gnome/mutter".experimental-features = lib.mkIf (builtins.any (x: x) (builtins.attrValues config.gnome.experimentalFeatures)) ( + lib.optional config.gnome.experimentalFeatures.scaleMonitorFramebuffer "scale-monitor-framebuffer" + ); + } + + # Night light configuration + (lib.mkIf config.gnome.nightLight.enable { + "org/gnome/settings-daemon/plugins/color" = { + night-light-enabled = true; + night-light-schedule-automatic = config.gnome.nightLight.automatic; + night-light-schedule-from = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.fromTime; + night-light-schedule-to = lib.mkIf (!config.gnome.nightLight.automatic) config.gnome.nightLight.toTime; + night-light-temperature = config.gnome.nightLight.temperature; + }; + }) + ( + lib.mkMerge ( + builtins.map (value: let + entry = "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${value.key}"; + in { + ${entry} = { + binding = value.binding; + command = value.command; + name = value.name; + }; + + "org/gnome/settings-daemon/plugins/media-keys" = { + custom-keybindings = [ + "/${entry}/" + ]; + }; + }) + ( + lib.attrsets.mapAttrsToList (_: value: value) config.gnome.hotkeys + ) + ) + ) + + # Extension configurations + (lib.mkIf (config.gnome.extensions.dash-to-dock.enable && config.gnome.extensions.dash-to-dock.options != null) { + "org/gnome/shell/extensions/dash-to-dock" = config.gnome.extensions.dash-to-dock.options; + }) + + (lib.mkIf (config.gnome.extensions.dash-to-panel.enable && config.gnome.extensions.dash-to-panel.options != null) { + "org/gnome/shell/extensions/dash-to-panel" = config.gnome.extensions.dash-to-panel.options; + }) + ]; + }; + }; +} diff --git a/modules/home-manager-modules/i18n.nix b/modules/home-manager-modules/i18n.nix new file mode 100644 index 00000000..2c93e597 --- /dev/null +++ b/modules/home-manager-modules/i18n.nix @@ -0,0 +1,42 @@ +{ + lib, + config, + ... +}: { + options = { + i18n = { + defaultLocale = lib.mkOption { + type = lib.types.str; + default = "en_US.UTF-8"; + example = "nl_NL.UTF-8"; + description = '' + The default locale. It determines the language for program + messages, the format for dates and times, sort order, and so on. + It also determines the character set, such as UTF-8. + ''; + }; + + extraLocaleSettings = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + example = { + LC_MESSAGES = "en_US.UTF-8"; + LC_TIME = "de_DE.UTF-8"; + }; + description = '' + A set of additional system-wide locale settings other than + `LANG` which can be configured with + {option}`i18n.defaultLocale`. + ''; + }; + }; + }; + + config = { + home.sessionVariables = + { + LANG = config.i18n.defaultLocale; + } + // config.i18n.extraLocaleSettings; + }; +} diff --git a/modules/home-manager-modules/impermanence.nix b/modules/home-manager-modules/impermanence.nix new file mode 100644 index 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 new file mode 100644 index 00000000..afc98dd2 --- /dev/null +++ b/modules/home-manager-modules/openssh.nix @@ -0,0 +1,107 @@ +{ + pkgs, + config, + osConfig, + lib, + ... +}: { + options.programs.openssh = { + enable = lib.mkEnableOption "should we enable openssh"; + authorizedKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = []; + }; + hostKeys = lib.mkOption { + type = lib.types.listOf lib.types.attrs; + default = []; + example = [ + { + type = "rsa"; + bits = 4096; + path = "${config.home.username}_${osConfig.networking.hostName}_rsa"; + rounds = 100; + openSSHFormat = true; + } + { + type = "ed25519"; + path = "${config.home.username}_${osConfig.networking.hostName}_ed25519"; + rounds = 100; + comment = "key comment"; + } + ]; + description = '' + NixOS can automatically generate SSH host keys. This option + specifies the path, type and size of each key. See + {manpage}`ssh-keygen(1)` for supported types + and sizes. Paths are relative to home directory + ''; + }; + }; + + config = lib.mkIf config.programs.openssh.enable ( + lib.mkMerge [ + ( + lib.mkIf ((builtins.length config.programs.openssh.hostKeys) != 0) { + services.ssh-agent.enable = true; + programs.ssh = { + enable = true; + enableDefaultConfig = false; + matchBlocks = { + "*" = { + compression = true; + addKeysToAgent = "confirm"; + }; + }; + extraConfig = lib.strings.concatLines ( + builtins.map (hostKey: "IdentityFile ~/.ssh/${hostKey.path}") config.programs.openssh.hostKeys + ); + }; + + systemd.user.services = builtins.listToAttrs ( + builtins.map (hostKey: + lib.attrsets.nameValuePair "ssh-gen-keys-${hostKey.path}" { + Install = { + WantedBy = ["default.target"]; + }; + Service = let + path = "${config.home.homeDirectory}/.ssh/${hostKey.path}"; + in { + Restart = "always"; + Type = "simple"; + ExecStart = "${ + pkgs.writeShellScript "ssh-gen-keys" '' + if ! [ -s "${path}" ]; then + if ! [ -h "${path}" ]; then + rm -f "${path}" + fi + mkdir -p "$(dirname '${path}')" + chmod 0755 "$(dirname '${path}')" + ${pkgs.openssh}/bin/ssh-keygen \ + -t "${hostKey.type}" \ + ${lib.optionalString (hostKey ? bits) "-b ${toString hostKey.bits}"} \ + ${lib.optionalString (hostKey ? rounds) "-a ${toString hostKey.rounds}"} \ + ${lib.optionalString (hostKey ? comment) "-C '${hostKey.comment}'"} \ + ${lib.optionalString (hostKey ? openSSHFormat && hostKey.openSSHFormat) "-o"} \ + -f "${path}" \ + -N "" + chown ${config.home.username} ${path}* + chgrp ${config.home.username} ${path}* + fi + '' + }"; + }; + }) + config.programs.openssh.hostKeys + ); + } + ) + (lib.mkIf config.impermanence.enable { + home.persistence."/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/anki.nix b/modules/home-manager-modules/programs/anki.nix new file mode 100644 index 00000000..c2f93ea0 --- /dev/null +++ b/modules/home-manager-modules/programs/anki.nix @@ -0,0 +1,15 @@ +{ + lib, + config, + osConfig, + ... +}: { + config = lib.mkIf (config.programs.anki.enable && osConfig.host.impermanence.enable) { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.dataHome}/Anki2/" + ]; + allowOther = true; + }; + }; +} diff --git a/modules/home-manager-modules/programs/bitwarden.nix b/modules/home-manager-modules/programs/bitwarden.nix new file mode 100644 index 00000000..e305b6c6 --- /dev/null +++ b/modules/home-manager-modules/programs/bitwarden.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.bitwarden = { + enable = lib.mkEnableOption "enable bitwarden"; + }; + + config = lib.mkIf config.programs.bitwarden.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + bitwarden-desktop + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/Bitwarden" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/bruno.nix b/modules/home-manager-modules/programs/bruno.nix new file mode 100644 index 00000000..8ad5e634 --- /dev/null +++ b/modules/home-manager-modules/programs/bruno.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.bruno = { + enable = lib.mkEnableOption "enable bruno"; + }; + + config = lib.mkIf config.programs.bruno.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + bruno + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/bruno/" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/calibre.nix b/modules/home-manager-modules/programs/calibre.nix new file mode 100644 index 00000000..dbe6e2b6 --- /dev/null +++ b/modules/home-manager-modules/programs/calibre.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.calibre = { + enable = lib.mkEnableOption "enable calibre"; + }; + + config = lib.mkIf config.programs.calibre.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + calibre + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/calibre" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/davinci-resolve.nix b/modules/home-manager-modules/programs/davinci-resolve.nix new file mode 100644 index 00000000..6c4526fa --- /dev/null +++ b/modules/home-manager-modules/programs/davinci-resolve.nix @@ -0,0 +1,29 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.davinci-resolve = { + enable = lib.mkEnableOption "enable davinci-resolve"; + }; + + config = lib.mkIf config.programs.davinci-resolve.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + davinci-resolve + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.dataHome}/DaVinciResolve" + "${config.xdg.configHome}/blackmagic" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/dbeaver.nix b/modules/home-manager-modules/programs/dbeaver.nix new file mode 100644 index 00000000..8b6c41ad --- /dev/null +++ b/modules/home-manager-modules/programs/dbeaver.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.dbeaver-bin = { + enable = lib.mkEnableOption "enable dbeaver"; + }; + + config = lib.mkIf config.programs.dbeaver-bin.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + dbeaver-bin + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.dataHome}/DBeaverData/" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/default.nix b/modules/home-manager-modules/programs/default.nix new file mode 100644 index 00000000..3fff4895 --- /dev/null +++ b/modules/home-manager-modules/programs/default.nix @@ -0,0 +1,49 @@ +{...}: { + imports = [ + ./firefox.nix + ./signal.nix + ./bitwarden.nix + ./makemkv.nix + ./obs.nix + ./anki.nix + ./piper.nix + ./qbittorrent.nix + ./discord.nix + ./obsidian.nix + ./prostudiomasters.nix + ./idea.nix + ./kdenlive.nix + ./krita.nix + ./protonvpn.nix + ./calibre.nix + ./bruno.nix + ./dbeaver.nix + ./dungeon-draft.nix + ./steam.nix + ./vscode + ./ungoogled-chromium.nix + ./libreoffice.nix + ./mapillary-uploader.nix + ./inkscape.nix + ./gimp.nix + ./guild-wars-2.nix + ./proxmark3.nix + ./freecad.nix + ./onionshare.nix + ./mfoc.nix + ./pdfarranger.nix + ./picard.nix + ./qflipper.nix + ./openvpn.nix + ./noisetorch.nix + ./olympus.nix + ./openrgb.nix + ./via.nix + ./vortex.nix + ./davinci-resolve.nix + ./gdx-liftoff.nix + ./tor-browser.nix + ./polycule.nix + ./vmware-workstation.nix + ]; +} diff --git a/modules/home-manager-modules/programs/discord.nix b/modules/home-manager-modules/programs/discord.nix new file mode 100644 index 00000000..d5d7192a --- /dev/null +++ b/modules/home-manager-modules/programs/discord.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.discord = { + enable = lib.mkEnableOption "enable discord"; + }; + + config = lib.mkIf config.programs.discord.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + discord + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/discord/" + ]; + allowOther = true; + }; + } + ) + ]); +} 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 new file mode 100644 index 00000000..8841887d --- /dev/null +++ b/modules/home-manager-modules/programs/firefox.nix @@ -0,0 +1,42 @@ +{ + lib, + config, + ... +}: let + buildProfilePersistence = profile: { + directories = [ + ".mozilla/firefox/${profile}/extensions" + ]; + files = [ + ".mozilla/firefox/${profile}/cookies.sqlite" + ".mozilla/firefox/${profile}/favicons.sqlite" + # Permissions and ${profileName} levels for each site + ".mozilla/firefox/${profile}/permissions.sqlite" + ".mozilla/firefox/${profile}/content-prefs.sqlite" + # Browser history and bookmarks + ".mozilla/firefox/${profile}/places.sqlite" + # I guess this is useful? + # https://bugzilla.mozilla.org/show_bug.cgi?id=1511384 + # https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria + ".mozilla/firefox/${profile}/storage.sqlite" + # Extension configuration + ".mozilla/firefox/${profile}/extension-settings.json" + ]; + allowOther = true; + }; +in { + config = lib.mkIf (config.programs.firefox.enable && config.impermanence.enable) { + home.persistence."/persist${config.home.homeDirectory}" = lib.mkMerge ( + ( + lib.attrsets.mapAttrsToList + (profile: _: buildProfilePersistence profile) + config.programs.firefox.profiles + ) + ++ ( + lib.lists.optional + ((builtins.length (lib.attrsets.mapAttrsToList (key: value: value) config.programs.firefox.profiles)) == 0) + (buildProfilePersistence "default") + ) + ); + }; +} diff --git a/modules/home-manager-modules/programs/freecad.nix b/modules/home-manager-modules/programs/freecad.nix new file mode 100644 index 00000000..89668dec --- /dev/null +++ b/modules/home-manager-modules/programs/freecad.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.freecad = { + enable = lib.mkEnableOption "enable freecad"; + }; + + config = lib.mkIf config.programs.freecad.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + freecad + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/FreeCAD" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/gdx-liftoff.nix b/modules/home-manager-modules/programs/gdx-liftoff.nix new file mode 100644 index 00000000..44408312 --- /dev/null +++ b/modules/home-manager-modules/programs/gdx-liftoff.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.gdx-liftoff = { + enable = lib.mkEnableOption "enable gdx-liftoff"; + }; + + config = lib.mkIf config.programs.gdx-liftoff.enable { + home.packages = with pkgs; [ + gdx-liftoff + ]; + }; +} diff --git a/modules/home-manager-modules/programs/gimp.nix b/modules/home-manager-modules/programs/gimp.nix new file mode 100644 index 00000000..925a2d9f --- /dev/null +++ b/modules/home-manager-modules/programs/gimp.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.gimp = { + enable = lib.mkEnableOption "enable gimp"; + }; + + config = lib.mkIf config.programs.gimp.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + gimp + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/GIMP" + ]; + allowOther = true; + }; + } + ) + ]); +} 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 new file mode 100644 index 00000000..e59e7b21 --- /dev/null +++ b/modules/home-manager-modules/programs/idea.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.jetbrains.idea-community = { + enable = lib.mkEnableOption "enable idea-community"; + }; + + config = lib.mkIf config.programs.jetbrains.idea-community.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + jetbrains.idea-community + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + # configuration + "${config.xdg.configHome}/JetBrains/" + # plugins + "${config.xdg.dataHome}/JetBrains/" + # System and Logs + "${config.xdg.cacheHome}/JetBrains/" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/inkscape.nix b/modules/home-manager-modules/programs/inkscape.nix new file mode 100644 index 00000000..a26ddecc --- /dev/null +++ b/modules/home-manager-modules/programs/inkscape.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.inkscape = { + enable = lib.mkEnableOption "enable inkscape"; + }; + + config = lib.mkIf config.programs.inkscape.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + inkscape + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/inkscape" + ]; + allowOther = true; + }; + } + ) + ]); +} 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 new file mode 100644 index 00000000..3ba5560c --- /dev/null +++ b/modules/home-manager-modules/programs/krita.nix @@ -0,0 +1,29 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.krita = { + enable = lib.mkEnableOption "enable krita"; + }; + + config = lib.mkIf config.programs.krita.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + krita + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/kritarc" + "${config.xdg.dataHome}/krita" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/libreoffice.nix b/modules/home-manager-modules/programs/libreoffice.nix new file mode 100644 index 00000000..93163e79 --- /dev/null +++ b/modules/home-manager-modules/programs/libreoffice.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.libreoffice = { + enable = lib.mkEnableOption "enable libreoffice"; + }; + + config = lib.mkIf config.programs.libreoffice.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + libreoffice + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/libreoffice" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/makemkv.nix b/modules/home-manager-modules/programs/makemkv.nix new file mode 100644 index 00000000..e92c3d37 --- /dev/null +++ b/modules/home-manager-modules/programs/makemkv.nix @@ -0,0 +1,41 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.makemkv = { + enable = lib.mkEnableOption "enable makemkv"; + appKeyFile = lib.mkOption { + type = lib.types.str; + }; + destinationDir = lib.mkOption { + type = lib.types.str; + }; + }; + + config = lib.mkIf config.programs.makemkv.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + makemkv + ]; + + sops.templates."MakeMKV.settings.conf".content = '' + app_DestinationDir = "${config.programs.makemkv.destinationDir}" + app_DestinationType = "2" + app_Key = "${config.programs.makemkv.appKeyFile}" + ''; + + home.file.".MakeMKV/settings.conf".source = config.lib.file.mkOutOfStoreSymlink config.sops.templates."MakeMKV.settings.conf".path; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..df1f0937 --- /dev/null +++ b/modules/home-manager-modules/programs/mapillary-uploader.nix @@ -0,0 +1,30 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.programs.mapillary-uploader; +in { + options.programs.mapillary-uploader = { + enable = mkEnableOption "Mapillary Desktop Uploader"; + }; + + config = mkIf cfg.enable (mkMerge [ + { + home.packages = [pkgs.mapillary-uploader]; + } + ( + mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..6006c9ba --- /dev/null +++ b/modules/home-manager-modules/programs/mfoc.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.mfoc = { + enable = lib.mkEnableOption "enable mfoc"; + }; + + config = lib.mkIf config.programs.mfoc.enable { + home.packages = with pkgs; [ + mfoc + ]; + }; +} diff --git a/modules/home-manager-modules/programs/noisetorch.nix b/modules/home-manager-modules/programs/noisetorch.nix new file mode 100644 index 00000000..4b426387 --- /dev/null +++ b/modules/home-manager-modules/programs/noisetorch.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.noisetorch = { + enable = lib.mkEnableOption "enable noisetorch"; + }; + + config = lib.mkIf config.programs.noisetorch.enable { + home.packages = with pkgs; [ + noisetorch + ]; + }; +} diff --git a/modules/home-manager-modules/programs/obs.nix b/modules/home-manager-modules/programs/obs.nix new file mode 100644 index 00000000..bfdba908 --- /dev/null +++ b/modules/home-manager-modules/programs/obs.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.programs.obs-studio.enable (lib.mkMerge [ + ( + 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 new file mode 100644 index 00000000..824563d3 --- /dev/null +++ b/modules/home-manager-modules/programs/obsidian.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.programs.obsidian.enable (lib.mkMerge [ + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..475f993a --- /dev/null +++ b/modules/home-manager-modules/programs/onionshare.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.onionshare = { + enable = lib.mkEnableOption "enable onionshare"; + }; + + config = lib.mkIf config.programs.onionshare.enable { + home.packages = with pkgs; [ + onionshare + ]; + }; +} diff --git a/modules/home-manager-modules/programs/openrgb.nix b/modules/home-manager-modules/programs/openrgb.nix new file mode 100644 index 00000000..c9d5e141 --- /dev/null +++ b/modules/home-manager-modules/programs/openrgb.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.openrgb = { + enable = lib.mkEnableOption "enable openrgb"; + }; + + config = lib.mkIf config.programs.openrgb.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + openrgb + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..dcd499ce --- /dev/null +++ b/modules/home-manager-modules/programs/openvpn.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.openvpn = { + enable = lib.mkEnableOption "enable openvpn"; + }; + + config = lib.mkIf config.programs.openvpn.enable { + home.packages = with pkgs; [ + openvpn + ]; + }; +} diff --git a/modules/home-manager-modules/programs/pdfarranger.nix b/modules/home-manager-modules/programs/pdfarranger.nix new file mode 100644 index 00000000..9246efd4 --- /dev/null +++ b/modules/home-manager-modules/programs/pdfarranger.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.pdfarranger = { + enable = lib.mkEnableOption "enable pdfarranger"; + }; + + config = lib.mkIf config.programs.pdfarranger.enable { + home.packages = with pkgs; [ + pdfarranger + ]; + }; +} diff --git a/modules/home-manager-modules/programs/picard.nix b/modules/home-manager-modules/programs/picard.nix new file mode 100644 index 00000000..bc37b865 --- /dev/null +++ b/modules/home-manager-modules/programs/picard.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.picard = { + enable = lib.mkEnableOption "enable picard"; + }; + + config = lib.mkIf config.programs.picard.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + picard + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/MusicBrainz" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/piper.nix b/modules/home-manager-modules/programs/piper.nix new file mode 100644 index 00000000..3ed25fd8 --- /dev/null +++ b/modules/home-manager-modules/programs/piper.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.piper = { + enable = lib.mkEnableOption "enable piper"; + }; + + config = lib.mkIf config.programs.piper.enable { + home.packages = with pkgs; [ + piper + ]; + }; +} diff --git a/modules/home-manager-modules/programs/polycule.nix b/modules/home-manager-modules/programs/polycule.nix new file mode 100644 index 00000000..d0aea2ae --- /dev/null +++ b/modules/home-manager-modules/programs/polycule.nix @@ -0,0 +1,31 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.polycule = { + enable = lib.mkEnableOption "enable polycule matrix client"; + package = lib.mkPackageOption pkgs "polycule" {}; + }; + + config = lib.mkIf config.programs.polycule.enable (lib.mkMerge [ + { + home.packages = [ + config.programs.polycule.package + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + # TODO: check that these are actually the correct folders + # directories = [ + # "${config.xdg.configHome}/polycule" + # "${config.xdg.dataHome}/polycule" + # "${config.xdg.cacheHome}/polycule" + # ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/prostudiomasters.nix b/modules/home-manager-modules/programs/prostudiomasters.nix new file mode 100644 index 00000000..5345169f --- /dev/null +++ b/modules/home-manager-modules/programs/prostudiomasters.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.prostudiomasters = { + enable = lib.mkEnableOption "enable prostudiomasters"; + }; + + config = lib.mkIf config.programs.prostudiomasters.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + prostudiomasters + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..513a6103 --- /dev/null +++ b/modules/home-manager-modules/programs/protonvpn.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.protonvpn-gui = { + enable = lib.mkEnableOption "enable protonvpn"; + }; + + config = lib.mkIf config.programs.protonvpn-gui.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + protonvpn-gui + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/protonvpn" + "${config.xdg.configHome}/Proton" + ]; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/proxmark3.nix b/modules/home-manager-modules/programs/proxmark3.nix new file mode 100644 index 00000000..656be193 --- /dev/null +++ b/modules/home-manager-modules/programs/proxmark3.nix @@ -0,0 +1,16 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.proxmark3 = { + enable = lib.mkEnableOption "enable proxmark3"; + }; + + config = lib.mkIf config.programs.proxmark3.enable { + home.packages = with pkgs; [ + proxmark3 + ]; + }; +} diff --git a/modules/home-manager-modules/programs/qbittorrent.nix b/modules/home-manager-modules/programs/qbittorrent.nix new file mode 100644 index 00000000..61d13c02 --- /dev/null +++ b/modules/home-manager-modules/programs/qbittorrent.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.qbittorrent = { + enable = lib.mkEnableOption "enable qbittorrent"; + }; + + config = lib.mkIf config.programs.qbittorrent.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + qbittorrent + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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 new file mode 100644 index 00000000..8b427667 --- /dev/null +++ b/modules/home-manager-modules/programs/qflipper.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.qflipper = { + enable = lib.mkEnableOption "enable qflipper"; + }; + + config = lib.mkIf config.programs.qflipper.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + qFlipper + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/qFlipper" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/signal.nix b/modules/home-manager-modules/programs/signal.nix new file mode 100644 index 00000000..7db23a7a --- /dev/null +++ b/modules/home-manager-modules/programs/signal.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.signal-desktop-bin = { + enable = lib.mkEnableOption "enable signal"; + }; + + config = lib.mkIf config.programs.signal-desktop-bin.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + signal-desktop-bin + ]; + } + ( + 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 new file mode 100644 index 00000000..fd98cb6a --- /dev/null +++ b/modules/home-manager-modules/programs/steam.nix @@ -0,0 +1,36 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.steam = { + enable = lib.mkEnableOption "enable steam"; + }; + + config = lib.mkIf config.programs.steam.enable ( + lib.mkMerge [ + { + home.packages = with pkgs; [ + steam + steam.run + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + { + directory = "${config.xdg.dataHome}/Steam"; + method = "symlink"; + } + ]; + allowOther = true; + }; + } + ) + ] + ); + + # TODO: bind impermanence config +} diff --git a/modules/home-manager-modules/programs/tor-browser.nix b/modules/home-manager-modules/programs/tor-browser.nix new file mode 100644 index 00000000..c3b085da --- /dev/null +++ b/modules/home-manager-modules/programs/tor-browser.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.tor-browser = { + enable = lib.mkEnableOption "enable tor-browser"; + }; + + config = lib.mkIf config.programs.tor-browser.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + tor-browser + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.dataHome}/torbrowser" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/ungoogled-chromium.nix b/modules/home-manager-modules/programs/ungoogled-chromium.nix new file mode 100644 index 00000000..ef6a8818 --- /dev/null +++ b/modules/home-manager-modules/programs/ungoogled-chromium.nix @@ -0,0 +1,28 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.ungoogled-chromium = { + enable = lib.mkEnableOption "enable ungoogled-chromium"; + }; + + config = lib.mkIf config.programs.ungoogled-chromium.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + ungoogled-chromium + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/persist${config.home.homeDirectory}" = { + directories = [ + "${config.xdg.configHome}/chromium" + ]; + allowOther = true; + }; + } + ) + ]); +} diff --git a/modules/home-manager-modules/programs/via.nix b/modules/home-manager-modules/programs/via.nix new file mode 100644 index 00000000..0aa58e4e --- /dev/null +++ b/modules/home-manager-modules/programs/via.nix @@ -0,0 +1,29 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.programs.via = { + enable = lib.mkEnableOption "enable via"; + }; + + config = lib.mkIf config.programs.via.enable (lib.mkMerge [ + { + home.packages = with pkgs; [ + via + ]; + } + ( + lib.mkIf config.impermanence.enable { + home.persistence."/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/aiCode.nix b/modules/home-manager-modules/programs/vscode/aiCode.nix new file mode 100644 index 00000000..838a4399 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/aiCode.nix @@ -0,0 +1,45 @@ +{ + lib, + pkgs, + ... +}: let + pkgsRepository = pkgs.codium-extensions; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.aiCode = { + enable = lib.mkEnableOption "should the ai code extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "ai-code" {}; + ollamaHost = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "what host should be used for ollama"; + default = null; + }; + inlineCompletion = { + enable = lib.mkOption { + type = lib.types.bool; + description = "should inline completion be enabled"; + default = true; + }; + model = lib.mkOption { + type = lib.types.nullOr lib.types.str; + description = "what model should be used for ollama"; + default = null; + }; + }; + }; + }; + config = lib.mkIf config.extraExtensions.aiCode.enable { + extensions = [ + config.extraExtensions.aiCode.extension + ]; + userSettings = { + "aiCode.ollamaHost" = lib.mkIf (config.extraExtensions.aiCode.ollamaHost != null) config.extraExtensions.aiCode.ollamaHost; + "aiCode.inlineCompletion.enable" = config.extraExtensions.aiCode.inlineCompletion.enable; + "aiCode.inlineCompletion.model" = lib.mkIf (config.extraExtensions.aiCode.inlineCompletion.model != null) config.extraExtensions.aiCode.inlineCompletion.model; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/alejandra.nix b/modules/home-manager-modules/programs/vscode/alejandra.nix new file mode 100644 index 00000000..ffeaf961 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/alejandra.nix @@ -0,0 +1,34 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.alejandra = { + enable = lib.mkEnableOption "Enable Alejandra extension for Nix formatting"; + extension = lib.mkPackageOption pkgsRepository "alejandra" { + default = ["kamadorueda" "alejandra"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.alejandra.enable { + extensions = [config.extraExtensions.alejandra.extension]; + userSettings = { + "[nix]" = { + "editor.defaultFormatter" = "kamadorueda.alejandra"; + "editor.formatOnPaste" = true; + "editor.formatOnSave" = true; + "editor.formatOnType" = true; + }; + "alejandra.program" = "alejandra"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/astroVscode.nix b/modules/home-manager-modules/programs/vscode/astroVscode.nix new file mode 100644 index 00000000..4bae34a4 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/astroVscode.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.astroVscode = { + enable = lib.mkEnableOption "should the astro-vscode extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "astro-vscode" { + default = ["astro-build" "astro-vscode"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.astroVscode.enable { + extensions = [ + config.extraExtensions.astroVscode.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/atomKeybindings.nix b/modules/home-manager-modules/programs/vscode/atomKeybindings.nix new file mode 100644 index 00000000..95cd9284 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/atomKeybindings.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.atomKeybindings = { + enable = lib.mkEnableOption "should the atom keybindings extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "atom-keybindings" { + default = ["ms-vscode" "atom-keybindings"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.atomKeybindings.enable { + extensions = [ + config.extraExtensions.atomKeybindings.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/autoRenameTag.nix b/modules/home-manager-modules/programs/vscode/autoRenameTag.nix new file mode 100644 index 00000000..5f24a329 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/autoRenameTag.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.autoRenameTag = { + enable = lib.mkEnableOption "should the auto-rename-tag extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "auto-rename-tag" { + default = ["formulahendry" "auto-rename-tag"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.autoRenameTag.enable { + extensions = [ + config.extraExtensions.autoRenameTag.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/claudeDev.nix b/modules/home-manager-modules/programs/vscode/claudeDev.nix new file mode 100644 index 00000000..ffeaff3d --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/claudeDev.nix @@ -0,0 +1,207 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; + + mcp-nixos = inputs.mcp-nixos.packages.${pkgs.stdenv.hostPlatform.system}.default; + + anyProfileHasMcpNixos = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.nixos.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpEslint = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.eslint.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpVitest = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.vitest.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcpSleep = lib.any ( + profile: + profile.extraExtensions.claudeDev.enable + && profile.extraExtensions.claudeDev.mcp.sleep.enable + ) (lib.attrValues config.programs.vscode.profiles); + + anyProfileHasMcp = anyProfileHasMcpNixos || anyProfileHasMcpEslint || anyProfileHasMcpVitest || anyProfileHasMcpSleep; + + getMcpTimeout = serverName: + lib.findFirst (timeout: timeout != null) null (map ( + profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then profile.extraExtensions.claudeDev.mcp.${serverName}.timeout + else null + ) (lib.attrValues config.programs.vscode.profiles)); + + getMcpAutoApprove = serverName: + lib.foldl' ( + acc: profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then acc // profile.extraExtensions.claudeDev.mcp.${serverName}.autoApprove + else acc + ) {} (lib.attrValues config.programs.vscode.profiles); + + getMcpPackage = serverName: + lib.findFirst (package: package != null) null (map ( + profile: + if profile.extraExtensions.claudeDev.enable && profile.extraExtensions.claudeDev.mcp.${serverName}.enable + then profile.extraExtensions.claudeDev.mcp.${serverName}.package + else null + ) (lib.attrValues config.programs.vscode.profiles)); +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.claudeDev = { + enable = lib.mkEnableOption "should the claude-dev extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "claude-dev" { + default = ["saoudrizwan" "claude-dev"]; + }; + + mcp = { + nixos = { + enable = lib.mkEnableOption "enable NixOS MCP server for Claude Dev"; + autoApprove = { + nixos_search = lib.mkEnableOption "should the nixos_search tool be auto approved for the nixos MCP server"; + nixos_info = lib.mkEnableOption "should the nixos_info tool be auto approved for the nixos MCP server"; + home_manager_search = lib.mkEnableOption "should the home_manager_search tool be auto approved for the nixos MCP server"; + home_manager_info = lib.mkEnableOption "should the home_manager_info tool be auto approved for the nixos MCP server"; + darwin_search = lib.mkEnableOption "should the darwin_search tool be auto approved for the nixos MCP server"; + darwin_info = lib.mkEnableOption "should the darwin_info tool be auto approved for the nixos MCP server"; + nixos_flakes_search = lib.mkEnableOption "should the nixos_flakes_search tool be auto approved for the nixos MCP server"; + }; + }; + eslint = { + enable = lib.mkEnableOption "enable ESLint MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "@eslint/mcp@latest"; + description = "NPM package to use for ESLint MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for ESLint MCP server operations"; + }; + autoApprove = { + lint-files = lib.mkEnableOption "Should the lint-files tool be auto approved for ESLint MCP server"; + }; + }; + vitest = { + enable = lib.mkEnableOption "enable Vitest MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "@djankies/vitest-mcp"; + description = "NPM package to use for Vitest MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for Vitest MCP server operations"; + }; + autoApprove = { + list_tests = lib.mkEnableOption "Should the list_tests tool be auto approved for Vitest MCP server"; + run_tests = lib.mkEnableOption "Should the run_tests tool be auto approved for Vitest MCP server"; + analyze_coverage = lib.mkEnableOption "Should the analyze_coverage tool be auto approved for Vitest MCP server"; + set_project_root = lib.mkEnableOption "Should the set_project_root tool be auto approved for Vitest MCP server"; + }; + }; + sleep = { + enable = lib.mkEnableOption "enable Sleep MCP server for Claude Dev"; + package = lib.mkOption { + type = lib.types.str; + default = "sleep-mcp"; + description = "NPM package to use for Sleep MCP server"; + }; + timeout = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = "Timeout in seconds for Sleep MCP server operations"; + }; + autoApprove = { + sleep = lib.mkEnableOption "Should the sleep tool be auto approved for Sleep MCP server"; + }; + }; + }; + }; + }; + config = lib.mkIf config.extraExtensions.claudeDev.enable { + extensions = [ + config.extraExtensions.claudeDev.extension + ]; + }; + })); + }; + + config = lib.mkMerge [ + (lib.mkIf anyProfileHasMcpNixos { + home.packages = [ + mcp-nixos + ]; + }) + + (lib.mkIf anyProfileHasMcp { + home.file."${config.xdg.configHome}/VSCodium/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json" = { + text = builtins.toJSON { + mcpServers = + (lib.optionalAttrs anyProfileHasMcpNixos { + nixos = { + command = "${mcp-nixos}/bin/mcp-nixos"; + }; + }) + // (lib.optionalAttrs anyProfileHasMcpEslint { + eslint = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "eslint")]; + } + // (lib.optionalAttrs ((getMcpTimeout "eslint") != null) { + timeout = getMcpTimeout "eslint"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "eslint") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "eslint")); + }); + }) + // (lib.optionalAttrs anyProfileHasMcpVitest { + vitest = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "vitest")]; + } + // (lib.optionalAttrs ((getMcpTimeout "vitest") != null) { + timeout = getMcpTimeout "vitest"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "vitest") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "vitest")); + }); + }) + // (lib.optionalAttrs anyProfileHasMcpSleep { + sleep-mcp = + { + command = "${pkgs.nodejs}/bin/npx"; + args = ["-y" (getMcpPackage "sleep")]; + } + // (lib.optionalAttrs ((getMcpTimeout "sleep") != null) { + timeout = getMcpTimeout "sleep"; + }) + // (lib.optionalAttrs ((getMcpAutoApprove "sleep") != {}) { + autoApprove = builtins.attrNames (lib.filterAttrs (_: v: v) (getMcpAutoApprove "sleep")); + }); + }); + }; + force = true; + }; + }) + ]; +} diff --git a/modules/home-manager-modules/programs/vscode/conventionalCommits.nix b/modules/home-manager-modules/programs/vscode/conventionalCommits.nix new file mode 100644 index 00000000..5bc81246 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/conventionalCommits.nix @@ -0,0 +1,34 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.conventionalCommits = { + enable = lib.mkEnableOption "Enable VSCode Conventional Commits extension"; + extension = lib.mkPackageOption pkgsRepository "conventional-commits" { + default = ["vivaxy" "vscode-conventional-commits"]; + }; + + gitmoji = lib.mkEnableOption "should emoji be prompted for as a part of the commit message./"; + + promptScopes = lib.mkEnableOption "prompting for scopes in conventional commits"; + }; + }; + config = lib.mkIf config.extraExtensions.conventionalCommits.enable { + extensions = [config.extraExtensions.conventionalCommits.extension]; + + userSettings = { + "conventionalCommits.gitmoji" = config.extraExtensions.conventionalCommits.gitmoji; + "conventionalCommits.promptScopes" = config.extraExtensions.conventionalCommits.promptScopes; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/default.nix b/modules/home-manager-modules/programs/vscode/default.nix new file mode 100644 index 00000000..f9d83dc7 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/default.nix @@ -0,0 +1,29 @@ +{...}: { + imports = [ + ./oneDark.nix + ./atomKeybindings.nix + ./aiCode.nix + ./alejandra.nix + ./nixIde.nix + ./autoRenameTag.nix + ./es7ReactJsSnippets.nix + ./liveServer.nix + ./tauriVscode.nix + ./vscodeEslint.nix + ./vscodeJest.nix + ./vscodeStandard.nix + ./vscodeStylelint.nix + ./go.nix + ./evenBetterToml.nix + ./openRemoteSsh.nix + ./rustAnalyzer.nix + ./astroVscode.nix + ./vscodeMdx.nix + ./claudeDev.nix + ./nearley.nix + ./vitest.nix + ./direnv.nix + ./conventionalCommits.nix + ./openDyslexicFont.nix + ]; +} diff --git a/modules/home-manager-modules/programs/vscode/direnv.nix b/modules/home-manager-modules/programs/vscode/direnv.nix new file mode 100644 index 00000000..231ea177 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/direnv.nix @@ -0,0 +1,25 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.direnv = { + enable = lib.mkEnableOption "Enable direnv extension"; + extension = lib.mkPackageOption pkgsRepository "direnv" { + default = ["mkhl" "direnv"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.direnv.enable { + extensions = [config.extraExtensions.direnv.extension]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix b/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix new file mode 100644 index 00000000..09e6da3e --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/es7ReactJsSnippets.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.es7ReactJsSnippets = { + enable = lib.mkEnableOption "should the es7-react-js-snippets extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "es7-react-js-snippets" { + default = ["dsznajder" "es7-react-js-snippets"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.es7ReactJsSnippets.enable { + extensions = [ + config.extraExtensions.es7ReactJsSnippets.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/evenBetterToml.nix b/modules/home-manager-modules/programs/vscode/evenBetterToml.nix new file mode 100644 index 00000000..9813ee1e --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/evenBetterToml.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.evenBetterToml = { + enable = lib.mkEnableOption "should the even-better-toml extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "even-better-toml" { + default = ["tamasfe" "even-better-toml"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.evenBetterToml.enable { + extensions = [ + config.extraExtensions.evenBetterToml.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/go.nix b/modules/home-manager-modules/programs/vscode/go.nix new file mode 100644 index 00000000..02ffe5da --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/go.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.go = { + enable = lib.mkEnableOption "should the go extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "go" { + default = ["golang" "go"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.go.enable { + extensions = [ + config.extraExtensions.go.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/liveServer.nix b/modules/home-manager-modules/programs/vscode/liveServer.nix new file mode 100644 index 00000000..3f53ca30 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/liveServer.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.liveServer = { + enable = lib.mkEnableOption "should the live-server extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "live-server" { + default = ["ms-vscode" "live-server"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.liveServer.enable { + extensions = [ + config.extraExtensions.liveServer.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/nearley.nix b/modules/home-manager-modules/programs/vscode/nearley.nix new file mode 100644 index 00000000..3020a9ec --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/nearley.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.vscode-marketplace; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.nearley = { + enable = lib.mkEnableOption "should the nearley extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "nearley" { + default = ["karyfoundation" "nearley"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.nearley.enable { + extensions = [ + config.extraExtensions.nearley.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/nixIde.nix b/modules/home-manager-modules/programs/vscode/nixIde.nix new file mode 100644 index 00000000..bc79b693 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/nixIde.nix @@ -0,0 +1,29 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.nixIde = { + enable = lib.mkEnableOption "Enable Nix IDE extension"; + extension = lib.mkPackageOption pkgsRepository "nix-ide" { + default = ["jnoortheen" "nix-ide"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.nixIde.enable { + extensions = [config.extraExtensions.nixIde.extension]; + userSettings = { + "nix.enableLanguageServer" = true; + "nix.serverPath" = "nil"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/oneDark.nix b/modules/home-manager-modules/programs/vscode/oneDark.nix new file mode 100644 index 00000000..5ed43f40 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/oneDark.nix @@ -0,0 +1,30 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.oneDark = { + enable = lib.mkEnableOption "should the one dark theme for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "onedark" { + default = ["akamud" "vscode-theme-onedark"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.oneDark.enable { + extensions = [ + config.extraExtensions.oneDark.extension + ]; + userSettings = { + "workbench.colorTheme" = "Atom One Dark"; + }; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/openDyslexicFont.nix b/modules/home-manager-modules/programs/vscode/openDyslexicFont.nix new file mode 100644 index 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/openRemoteSsh.nix b/modules/home-manager-modules/programs/vscode/openRemoteSsh.nix new file mode 100644 index 00000000..c1b6daa0 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/openRemoteSsh.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.openRemoteSsh = { + enable = lib.mkEnableOption "should the open-remote-ssh extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "open-remote-ssh" { + default = ["jeanp413" "open-remote-ssh"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.openRemoteSsh.enable { + extensions = [ + config.extraExtensions.openRemoteSsh.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/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/home-manager-modules/programs/vscode/tauriVscode.nix b/modules/home-manager-modules/programs/vscode/tauriVscode.nix new file mode 100644 index 00000000..9185fb3a --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/tauriVscode.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.tauriVscode = { + enable = lib.mkEnableOption "should the tauri-vscode extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "tauri-vscode" { + default = ["tauri-apps" "tauri-vscode"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.tauriVscode.enable { + extensions = [ + config.extraExtensions.tauriVscode.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vitest.nix b/modules/home-manager-modules/programs/vscode/vitest.nix new file mode 100644 index 00000000..446d25bc --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vitest.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vitest = { + enable = lib.mkEnableOption "should the vitest extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vitest" { + default = ["vitest" "explorer"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vitest.enable { + extensions = [ + config.extraExtensions.vitest.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeEslint.nix b/modules/home-manager-modules/programs/vscode/vscodeEslint.nix new file mode 100644 index 00000000..64d979fa --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeEslint.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeEslint = { + enable = lib.mkEnableOption "should the vscode-eslint extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-eslint" { + default = ["dbaeumer" "vscode-eslint"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeEslint.enable { + extensions = [ + config.extraExtensions.vscodeEslint.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeJest.nix b/modules/home-manager-modules/programs/vscode/vscodeJest.nix new file mode 100644 index 00000000..7c24f2ac --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeJest.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeJest = { + enable = lib.mkEnableOption "should the vscode-jest extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-jest" { + default = ["orta" "vscode-jest"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeJest.enable { + extensions = [ + config.extraExtensions.vscodeJest.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeMdx.nix b/modules/home-manager-modules/programs/vscode/vscodeMdx.nix new file mode 100644 index 00000000..c49fe51c --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeMdx.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeMdx = { + enable = lib.mkEnableOption "should the vscode-mdx extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-mdx" { + default = ["unifiedjs" "vscode-mdx"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeMdx.enable { + extensions = [ + config.extraExtensions.vscodeMdx.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeStandard.nix b/modules/home-manager-modules/programs/vscode/vscodeStandard.nix new file mode 100644 index 00000000..31c8ad0b --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeStandard.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeStandard = { + enable = lib.mkEnableOption "should the vscode-standard extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-standard" { + default = ["standard" "vscode-standard"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeStandard.enable { + extensions = [ + config.extraExtensions.vscodeStandard.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix b/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix new file mode 100644 index 00000000..0d43b296 --- /dev/null +++ b/modules/home-manager-modules/programs/vscode/vscodeStylelint.nix @@ -0,0 +1,27 @@ +{ + lib, + pkgs, + config, + ... +}: let + pkgsRepositories = pkgs.nix-vscode-extensions.forVSCodeVersion config.programs.vscode.package.version; + pkgsRepository = pkgsRepositories.open-vsx; +in { + options.programs.vscode.profiles = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options = { + extraExtensions.vscodeStylelint = { + enable = lib.mkEnableOption "should the vscode-stylelint extension for vscode be enabled"; + extension = lib.mkPackageOption pkgsRepository "vscode-stylelint" { + default = ["stylelint" "vscode-stylelint"]; + }; + }; + }; + config = lib.mkIf config.extraExtensions.vscodeStylelint.enable { + extensions = [ + config.extraExtensions.vscodeStylelint.extension + ]; + }; + })); + }; +} diff --git a/modules/home-manager-modules/sops.nix b/modules/home-manager-modules/sops.nix new file mode 100644 index 00000000..910fbb6f --- /dev/null +++ b/modules/home-manager-modules/sops.nix @@ -0,0 +1,7 @@ +{...}: { + config = { + sops = { + age.keyFile = "/var/lib/sops-nix/key.txt"; + }; + }; +} diff --git a/modules/home-manager-modules/user.nix b/modules/home-manager-modules/user.nix new file mode 100644 index 00000000..efce22de --- /dev/null +++ b/modules/home-manager-modules/user.nix @@ -0,0 +1,17 @@ +{ + lib, + config, + osConfig, + ... +}: { + options.user = { + isDesktopUser = lib.mkOption { + type = lib.types.bool; + default = osConfig.host.users.${config.home.username}.isDesktopUser; + }; + isTerminalUser = lib.mkOption { + type = lib.types.bool; + default = osConfig.host.users.${config.home.username}.isTerminalUser; + }; + }; +} diff --git a/modules/nixos-modules/ai.nix b/modules/nixos-modules/ai.nix new file mode 100644 index 00000000..d8cd63d9 --- /dev/null +++ b/modules/nixos-modules/ai.nix @@ -0,0 +1,46 @@ +{lib, ...}: { + options.host = { + ai = { + enable = lib.mkEnableOption "should we use AI on this machine"; + models = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({name, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + }; + model = lib.mkOption { + type = lib.types.str; + }; + provider = lib.mkOption { + type = lib.types.str; + default = "ollama"; + }; + apiBase = lib.mkOption { + type = lib.types.str; + default = "http://localhost:11434"; + }; + roles = lib.mkOption { + type = lib.types.listOf (lib.types.enum [ + "chat" + "autocomplete" + "embed" + "rerank" + "edit" + "apply" + "summarize" + ]); + default = []; + }; + }; + })); + }; + default = {}; + }; + }; + + config = { + # TODO: configure ollama to download any modules listed in options.host.ai.models.{name}.model if options.host.ai.models.{name}.apiBase is localhost + # TODO: if we have any models that have a non localhost options.host.ai.models.{name}.apiBase then set services.ollama.enable to a lib.mkAfter true + }; +} diff --git a/modules/nixos-modules/default.nix b/modules/nixos-modules/default.nix new file mode 100644 index 00000000..2ba1a587 --- /dev/null +++ b/modules/nixos-modules/default.nix @@ -0,0 +1,24 @@ +# this folder container modules that are for nixos only +{...}: { + imports = [ + ./home-manager + ./system.nix + ./hardware.nix + ./users.nix + ./desktop.nix + ./ssh.nix + ./i18n.nix + ./sync.nix + ./impermanence.nix + ./disko.nix + ./ollama.nix + ./ai.nix + ./tailscale.nix + ./steam.nix + ./server + ]; + + nixpkgs.config.permittedInsecurePackages = [ + "dotnet-sdk-6.0.428" + ]; +} diff --git a/modules/nixos-modules/desktop.nix b/modules/nixos-modules/desktop.nix new file mode 100644 index 00000000..6686ee33 --- /dev/null +++ b/modules/nixos-modules/desktop.nix @@ -0,0 +1,81 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.host.desktop.enable = lib.mkEnableOption "should desktop configuration be enabled"; + + config = lib.mkMerge [ + { + host.desktop.enable = lib.mkDefault true; + } + (lib.mkIf config.host.desktop.enable { + environment.gnome.excludePackages = with pkgs; [ + xterm # default terminal + atomix # puzzle game + cheese # webcam tool + epiphany # web browser + geary # email reader + gedit # text editor + decibels # audio player + gnome-characters # character set viewer + gnome-music # music player + gnome-photos # photo viewer + gnome-logs # log viewer + gnome-maps # map viewer + gnome-tour # welcome tour + hitori # sudoku game + iagno # go game + tali # poker game + yelp # help viewer + ]; + services = { + # Enable CUPS to print documents. + printing = { + enable = true; + drivers = [ + pkgs.hplip + pkgs.gutenprint + pkgs.gutenprintBin + ]; + }; + + xserver = { + # Enable the X11 windowing system. + enable = true; + + # Get rid of xTerm + desktopManager.xterm.enable = false; + }; + + # Enable the GNOME Desktop Environment. + displayManager.gdm.enable = true; + desktopManager.gnome.enable = true; + + pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + + # If you want to use JACK applications, uncomment this + #jack.enable = true; + + # use the example session manager (no others are packaged yet so this is enabled by default, + # no need to redefine it in your config for now) + #media-session.enable = true; + }; + automatic-timezoned = { + enable = true; + }; + + # Enable sound with pipewire. + pulseaudio.enable = false; + }; + + # enable RealtimeKit for pulse audio + security.rtkit.enable = true; + }) + ]; +} diff --git a/modules/nixos-modules/disko.nix b/modules/nixos-modules/disko.nix new file mode 100644 index 00000000..a962689e --- /dev/null +++ b/modules/nixos-modules/disko.nix @@ -0,0 +1,267 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: let + # there currently is a bug with disko that causes long disk names to be generated improperly this hash function should alleviate it when used for disk names instead of what we are defaulting to + # max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that + hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive)); + + vdevs = + builtins.map ( + disks: + builtins.map (disk: lib.attrsets.nameValuePair (hashDisk disk) disk) disks + ) + config.host.storage.pool.vdevs; + cache = + builtins.map ( + disk: lib.attrsets.nameValuePair (hashDisk disk) disk + ) + config.host.storage.pool.cache; + + datasets = config.host.storage.pool.datasets // config.host.storage.pool.extraDatasets; +in { + options.host.storage = { + enable = lib.mkEnableOption "are we going create zfs disks with disko on this device"; + encryption = lib.mkEnableOption "is the vdev going to be encrypted"; + notifications = { + enable = lib.mkEnableOption "are notifications enabled"; + host = lib.mkOption { + type = lib.types.str; + description = "what is the host that we are going to send the email to"; + }; + port = lib.mkOption { + type = lib.types.port; + description = "what port is the host using to receive mail on"; + }; + to = lib.mkOption { + type = lib.types.str; + description = "what account is the email going to be sent to"; + }; + user = lib.mkOption { + type = lib.types.str; + description = "what user is the email going to be set from"; + }; + tokenFile = lib.mkOption { + type = lib.types.str; + description = "file containing the password to be used by msmtp for notifications"; + }; + }; + pool = { + mode = lib.mkOption { + type = lib.types.str; + default = "raidz2"; + description = "what level of redundancy should this pool have"; + }; + # list of drives in pool that will have a boot partition put onto them + bootDrives = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of disks that are going to have a boot partition installed on them"; + default = lib.lists.flatten config.host.storage.pool.vdevs; + }; + # shorthand for vdevs if you only have 1 vdev + drives = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of drives that are going to be in the vdev"; + default = []; + }; + # list of all drives in each vdev + vdevs = lib.mkOption { + type = lib.types.listOf (lib.types.listOf lib.types.str); + description = "list of disks that are going to be in"; + default = [config.host.storage.pool.drives]; + }; + # list of cache drives for pool + cache = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "list of drives that are going to be used as cache"; + default = []; + }; + # Default datasets that are needed to make a functioning system + datasets = lib.mkOption { + type = lib.types.attrsOf (inputs.disko.lib.subType { + types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; + }); + default = { + "local" = { + type = "zfs_fs"; + options.canmount = "off"; + }; + # nix directory needs to be available pre persist and doesn't need to be snapshotted or backed up + "local/system/nix" = { + type = "zfs_fs"; + mountpoint = "/nix"; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + # dataset for root that gets rolled back on every boot + "local/system/root" = { + type = "zfs_fs"; + mountpoint = "/"; + options = { + canmount = "on"; + }; + postCreateHook = '' + zfs snapshot rpool/local/system/root@blank + ''; + }; + }; + }; + extraDatasets = lib.mkOption { + type = lib.types.attrsOf (inputs.disko.lib.subType { + types = {inherit (inputs.disko.lib.types) zfs_fs zfs_volume;}; + }); + description = "List of datasets to define"; + default = {}; + }; + }; + }; + + config = lib.mkIf config.host.storage.enable { + programs.msmtp = lib.mkIf config.host.storage.notifications.enable { + enable = true; + setSendmail = true; + defaults = { + aliases = "/etc/aliases"; + port = config.host.storage.notifications.port; + tls_trust_file = "/etc/ssl/certs/ca-certificates.crt"; + tls = "on"; + auth = "login"; + tls_starttls = "off"; + }; + accounts = { + zfs_notifications = { + auth = true; + tls = true; + host = config.host.storage.notifications.host; + passwordeval = "cat ${config.host.storage.notifications.tokenFile}"; + user = config.host.storage.notifications.user; + from = config.host.storage.notifications.user; + }; + }; + }; + + services.zfs = { + autoScrub.enable = true; + autoSnapshot.enable = true; + + zed = lib.mkIf config.host.storage.notifications.enable { + enableMail = true; + + settings = { + ZED_DEBUG_LOG = "/tmp/zed.debug.log"; + ZED_EMAIL_ADDR = [config.host.storage.notifications.to]; + ZED_EMAIL_PROG = "${pkgs.msmtp}/bin/msmtp"; + ZED_EMAIL_OPTS = "-a zfs_notifications @ADDRESS@"; + + ZED_NOTIFY_INTERVAL_SECS = 3600; + ZED_NOTIFY_VERBOSE = true; + + ZED_USE_ENCLOSURE_LEDS = true; + ZED_SCRUB_AFTER_RESILVER = true; + }; + }; + }; + + disko.devices = { + disk = ( + builtins.listToAttrs ( + builtins.map + (drive: + lib.attrsets.nameValuePair (drive.name) { + type = "disk"; + device = "/dev/disk/by-id/${drive.value}"; + content = { + type = "gpt"; + partitions = { + ESP = lib.mkIf (builtins.elem drive.value config.host.storage.pool.bootDrives) { + # The 2GB here for the boot partition might be a bit overkill we probably only need like 1/4th of that but storage is cheap + size = "2G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + zfs = { + size = "100%"; + content = { + type = "zfs"; + pool = "rpool"; + }; + }; + }; + }; + }) + ( + (lib.lists.flatten vdevs) ++ cache + ) + ) + ); + zpool = { + rpool = { + type = "zpool"; + mode = { + topology = { + type = "topology"; + vdev = ( + builtins.map (disks: { + mode = config.host.storage.pool.mode; + members = + builtins.map (disk: disk.name) disks; + }) + vdevs + ); + cache = builtins.map (disk: disk.name) cache; + }; + }; + + options = { + ashift = "12"; + autotrim = "on"; + }; + + rootFsOptions = + { + canmount = "off"; + mountpoint = "none"; + + xattr = "sa"; + acltype = "posixacl"; + relatime = "on"; + + compression = "lz4"; + + "com.sun:auto-snapshot" = "false"; + } + // ( + lib.attrsets.optionalAttrs config.host.storage.encryption { + encryption = "on"; + keyformat = "hex"; + keylocation = "prompt"; + } + ); + + datasets = lib.mkMerge [ + ( + lib.attrsets.mapAttrs (name: value: { + type = value.type; + options = value.options; + mountpoint = value.mountpoint; + postCreateHook = value.postCreateHook; + }) + datasets + ) + ]; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/hardware.nix b/modules/nixos-modules/hardware.nix new file mode 100644 index 00000000..07e6fa8c --- /dev/null +++ b/modules/nixos-modules/hardware.nix @@ -0,0 +1,34 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.host.hardware = { + piperMouse = { + enable = lib.mkEnableOption "host has a piper mouse"; + }; + viaKeyboard = { + enable = lib.mkEnableOption "host has a via keyboard"; + }; + openRGB = { + enable = lib.mkEnableOption "host has open rgb hardware"; + }; + graphicsAcceleration = { + enable = lib.mkEnableOption "host has a gpu for graphical acceleration"; + }; + directAccess = { + enable = lib.mkEnableOption "can a host be used on its own"; + }; + }; + config = lib.mkMerge [ + (lib.mkIf config.host.hardware.piperMouse.enable { + services.ratbagd.enable = true; + }) + (lib.mkIf config.host.hardware.viaKeyboard.enable { + hardware.keyboard.qmk.enable = true; + + services.udev.packages = [pkgs.via]; + }) + ]; +} diff --git a/modules/nixos-modules/home-manager/default.nix b/modules/nixos-modules/home-manager/default.nix new file mode 100644 index 00000000..10f86c7d --- /dev/null +++ b/modules/nixos-modules/home-manager/default.nix @@ -0,0 +1,9 @@ +# modules in this folder are to adapt home-manager modules configs to nixos-module configs +{...}: { + imports = [ + ./flipperzero.nix + ./i18n.nix + ./openssh.nix + ./steam.nix + ]; +} diff --git a/modules/nixos-modules/home-manager/flipperzero.nix b/modules/nixos-modules/home-manager/flipperzero.nix new file mode 100644 index 00000000..6c947730 --- /dev/null +++ b/modules/nixos-modules/home-manager/flipperzero.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: let + home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; +in { + hardware.flipperzero.enable = lib.lists.any (home-user: home-user.hardware.flipperzero.enable) home-users; +} diff --git a/modules/nixos-modules/home-manager/i18n.nix b/modules/nixos-modules/home-manager/i18n.nix new file mode 100644 index 00000000..78b86faa --- /dev/null +++ b/modules/nixos-modules/home-manager/i18n.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: let + home-users = lib.attrsets.mapAttrsToList (_: user: user) config.home-manager.users; +in { + config = { + i18n.supportedLocales = + lib.unique + (builtins.map (l: (lib.replaceStrings ["utf8" "utf-8" "UTF8"] ["UTF-8" "UTF-8" "UTF-8"] l) + "/UTF-8") ( + [ + "C.UTF-8" + "en_US.UTF-8" + config.i18n.defaultLocale + ] + ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings)) + ++ ( + map (user-config: user-config.i18n.defaultLocale) home-users + ) + ++ (lib.lists.flatten ( + map (user-config: lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") user-config.i18n.extraLocaleSettings)) home-users + )) + )); + }; +} diff --git a/modules/nixos-modules/home-manager/openssh.nix b/modules/nixos-modules/home-manager/openssh.nix new file mode 100644 index 00000000..31a785f9 --- /dev/null +++ b/modules/nixos-modules/home-manager/openssh.nix @@ -0,0 +1,11 @@ +{ + config, + lib, + ... +}: { + users.users = + lib.attrsets.mapAttrs (name: value: { + openssh.authorizedKeys.keys = value.programs.openssh.authorizedKeys; + }) + config.home-manager.users; +} diff --git a/modules/nixos-modules/home-manager/steam.nix b/modules/nixos-modules/home-manager/steam.nix new file mode 100644 index 00000000..d151bca6 --- /dev/null +++ b/modules/nixos-modules/home-manager/steam.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + ... +}: let + setupSteam = + lib.lists.any + (value: value) + (lib.attrsets.mapAttrsToList (name: value: value.programs.steam.enable) config.home-manager.users); +in { + config = lib.mkIf setupSteam { + programs.steam = { + enable = true; + # TODO: figure out how to not install steam here + # package = lib.mkDefault pkgs.emptyFile; + }; + }; +} diff --git a/modules/nixos-modules/i18n.nix b/modules/nixos-modules/i18n.nix new file mode 100644 index 00000000..eada12c0 --- /dev/null +++ b/modules/nixos-modules/i18n.nix @@ -0,0 +1,3 @@ +{...}: { + i18n.defaultLocale = "en_IE.UTF-8"; +} diff --git a/modules/nixos-modules/impermanence.nix b/modules/nixos-modules/impermanence.nix new file mode 100644 index 00000000..7735e97e --- /dev/null +++ b/modules/nixos-modules/impermanence.nix @@ -0,0 +1,100 @@ +{ + config, + lib, + ... +}: { + options.host.impermanence.enable = lib.mkEnableOption "are we going to use impermanence on this device"; + + config = lib.mkMerge [ + { + assertions = [ + { + assertion = !(config.host.impermanence.enable && !config.host.storage.enable); + message = '' + Disko storage must be enabled to use impermanence. + ''; + } + ]; + } + ( + lib.mkIf config.host.impermanence.enable { + assertions = [ + { + assertion = config.host.impermanence.enable && config.host.storage.enable; + message = "Impermanence can not be used without managed host storage."; + } + ]; + + # fixes issues with /var/lib/private not having the correct permissions https://github.com/nix-community/impermanence/issues/254 + system.activationScripts."createPersistentStorageDirs".deps = ["var-lib-private-permissions" "users" "groups"]; + system.activationScripts = { + "var-lib-private-permissions" = { + deps = ["specialfs"]; + text = '' + mkdir -p /persist/system/root/var/lib/private + chmod 0700 /persist/system/root/var/lib/private + ''; + }; + }; + + programs.fuse.userAllowOther = true; + + boot.initrd.postResumeCommands = lib.mkAfter '' + zfs rollback -r rpool/local/system/root@blank + ''; + + fileSystems = { + "/".neededForBoot = true; + "/persist/system/root".neededForBoot = true; + "/persist/system/var/log".neededForBoot = true; + }; + + host.storage.pool.extraDatasets = { + # persist datasets are datasets that contain information that we would like to keep around + "persist" = { + type = "zfs_fs"; + options.canmount = "off"; + options = { + "com.sun:auto-snapshot" = "true"; + }; + }; + # this is where root data actually lives + "persist/system/root" = { + type = "zfs_fs"; + mountpoint = "/persist/system/root"; + }; + "persist/system/var/log" = { + type = "zfs_fs"; + mountpoint = "/persist/system/var/log"; + # logs should be append only so we shouldn't need to snapshot them + options = { + "com.sun:auto-snapshot" = "false"; + }; + }; + }; + + environment.persistence."/persist/system/var/log" = { + enable = true; + hideMounts = true; + directories = [ + "/var/log" + ]; + }; + + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + "/var/lib/nixos" + "/var/lib/systemd/coredump" + ]; + files = [ + "/etc/machine-id" + ]; + }; + + security.sudo.extraConfig = "Defaults lecture=never"; + } + ) + ]; +} diff --git a/modules/nixos-modules/ollama.nix b/modules/nixos-modules/ollama.nix new file mode 100644 index 00000000..99819bf5 --- /dev/null +++ b/modules/nixos-modules/ollama.nix @@ -0,0 +1,46 @@ +{ + config, + lib, + ... +}: { + options = { + services.ollama.exposePort = lib.mkEnableOption "should we expose ollama on tailscale"; + }; + + config = lib.mkIf config.services.ollama.enable ( + lib.mkMerge [ + { + services.ollama = { + # TODO: these should match whats set in the users file + group = "ollama"; + user = "ollama"; + }; + } + (lib.mkIf config.services.ollama.exposePort (let + ports = [ + config.services.ollama.port + ]; + in { + services.ollama.host = "0.0.0.0"; + networking.firewall.interfaces.${config.services.tailscale.interfaceName} = { + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + })) + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = "/var/lib/private/ollama"; + user = config.services.ollama.user; + group = config.services.ollama.group; + mode = "0700"; + } + ]; + }; + }) + ] + ); +} 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 new file mode 100644 index 00000000..14b715e9 --- /dev/null +++ b/modules/nixos-modules/server/actual/const.nix @@ -0,0 +1,3 @@ +{ + dataDirectory = "/var/lib/private/actual"; +} diff --git a/modules/nixos-modules/server/actual/default.nix b/modules/nixos-modules/server/actual/default.nix new file mode 100644 index 00000000..b59517b8 --- /dev/null +++ b/modules/nixos-modules/server/actual/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./actual.nix + ./proxy.nix + ./fail2ban.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/actual/fail2ban.nix b/modules/nixos-modules/server/actual/fail2ban.nix new file mode 100644 index 00000000..3ad754e8 --- /dev/null +++ b/modules/nixos-modules/server/actual/fail2ban.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.services.actual.enable && config.services.fail2ban.enable) { + # TODO: configuration for fail2ban for actual + }; +} diff --git a/modules/nixos-modules/server/actual/impermanence.nix b/modules/nixos-modules/server/actual/impermanence.nix new file mode 100644 index 00000000..d870789d --- /dev/null +++ b/modules/nixos-modules/server/actual/impermanence.nix @@ -0,0 +1,37 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + dataDirectory = const.dataDirectory; +in { + options.services.actual = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.actual.impermanence.enable { + assertions = [ + { + 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" = { + directories = [ + { + directory = dataDirectory; + user = "actual"; + group = "actual"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/actual/proxy.nix b/modules/nixos-modules/server/actual/proxy.nix new file mode 100644 index 00000000..9d375745 --- /dev/null +++ b/modules/nixos-modules/server/actual/proxy.nix @@ -0,0 +1,34 @@ +{ + lib, + config, + ... +}: { + options.services.actual = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that actual will be hosted at"; + default = "actual.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for actual"; + default = []; + }; + reverseProxy.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.actual.enable && config.services.reverseProxy.enable; + }; + }; + + config = lib.mkIf config.services.actual.reverseProxy.enable { + services.reverseProxy.services.actual = { + target = "http://localhost:${toString config.services.actual.settings.port}"; + domain = config.services.actual.domain; + extraDomains = config.services.actual.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/bazarr/default.nix b/modules/nixos-modules/server/bazarr/default.nix new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/bazarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/bazarr/impermanence.nix b/modules/nixos-modules/server/bazarr/impermanence.nix new file mode 100644 index 00000000..70a45d13 --- /dev/null +++ b/modules/nixos-modules/server/bazarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + bazarr_data_directory = "/var/lib/bazarr"; +in { + options.services.bazarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.bazarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.bazarr.impermanence.enable { + assertions = [ + { + assertion = config.services.bazarr.dataDir == bazarr_data_directory; + message = "bazarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = bazarr_data_directory; + user = "bazarr"; + group = "bazarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/crab-hole/crab-hole.nix b/modules/nixos-modules/server/crab-hole/crab-hole.nix new file mode 100644 index 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 new file mode 100644 index 00000000..2b330893 --- /dev/null +++ b/modules/nixos-modules/server/default.nix @@ -0,0 +1,26 @@ +{...}: { + imports = [ + ./reverseProxy + ./fail2ban + ./postgres + ./network_storage + + ./actual + ./bazarr + ./crab-hole + ./flaresolverr + ./forgejo + ./home-assistant + ./immich + ./jackett + ./jellyfin + ./lidarr + ./panoramax + ./paperless + ./qbittorent + ./radarr + ./searx + ./sonarr + ./wyoming.nix + ]; +} diff --git a/modules/nixos-modules/server/fail2ban/default.nix b/modules/nixos-modules/server/fail2ban/default.nix new file mode 100644 index 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/const.nix b/modules/nixos-modules/server/forgejo/const.nix new file mode 100644 index 00000000..10e39740 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/const.nix @@ -0,0 +1,4 @@ +{ + httpPort = 8081; + sshPort = 22222; +} diff --git a/modules/nixos-modules/server/forgejo/database.nix b/modules/nixos-modules/server/forgejo/database.nix new file mode 100644 index 00000000..bb8781c4 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/database.nix @@ -0,0 +1,32 @@ +{ + lib, + config, + ... +}: let + usingPostgres = config.services.forgejo.database.type == "postgres"; +in { + config = lib.mkIf config.services.forgejo.enable { + assertions = [ + { + assertion = !usingPostgres || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when Forgejo database type is postgres"; + } + { + assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (db: db == "forgejo") config.services.postgresql.ensureDatabases); + message = "Forgejo built-in database creation failed - expected 'forgejo' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !(usingPostgres && config.services.forgejo.database.createDatabase) || (builtins.any (user: user.name == "forgejo") config.services.postgresql.ensureUsers); + message = "Forgejo built-in user creation failed - expected user 'forgejo' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + services.forgejo.database.createDatabase = lib.mkDefault usingPostgres; + + systemd.services.forgejo = lib.mkIf usingPostgres { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/default.nix b/modules/nixos-modules/server/forgejo/default.nix new file mode 100644 index 00000000..4333f69a --- /dev/null +++ b/modules/nixos-modules/server/forgejo/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./forgejo.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/forgejo/fail2ban.nix b/modules/nixos-modules/server/forgejo/fail2ban.nix new file mode 100644 index 00000000..dfe221a5 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/fail2ban.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.services.forgejo = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.services.fail2ban.enable; + }; + }; + }; + + config = lib.mkIf config.services.forgejo.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/forgejo.local".text = lib.mkIf config.services.forgejo.enable ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = ".*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from " + '') + ); + }; + + services.fail2ban = { + jails = { + forgejo-iptables.settings = lib.mkIf config.services.forgejo.enable { + enabled = true; + filter = "forgejo"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.forgejo.settings.log.ROOT_PATH}/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/forgejo.nix b/modules/nixos-modules/server/forgejo/forgejo.nix new file mode 100644 index 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 new file mode 100644 index 00000000..6fe3de8b --- /dev/null +++ b/modules/nixos-modules/server/forgejo/impermanence.nix @@ -0,0 +1,35 @@ +{ + lib, + config, + ... +}: let + stateDir = "/var/lib/forgejo"; +in { + options.services.forgejo = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.forgejo.impermanence.enable { + assertions = [ + { + assertion = config.services.forgejo.stateDir == stateDir; + message = "forgejo state directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = stateDir; + user = "forgejo"; + group = "forgejo"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/forgejo/proxy.nix b/modules/nixos-modules/server/forgejo/proxy.nix new file mode 100644 index 00000000..c2d31319 --- /dev/null +++ b/modules/nixos-modules/server/forgejo/proxy.nix @@ -0,0 +1,43 @@ +{ + lib, + config, + ... +}: let + const = import ./const.nix; + httpPort = const.httpPort; +in { + options.services.forgejo = { + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.forgejo.enable && config.services.reverseProxy.enable; + }; + domain = lib.mkOption { + type = lib.types.str; + description = "domain that forgejo will be hosted at"; + default = "git.jan-leila.com"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for forgejo"; + default = []; + }; + }; + }; + + config = lib.mkIf config.services.forgejo.reverseProxy.enable { + services.reverseProxy.services.forgejo = { + target = "http://localhost:${toString httpPort}"; + domain = config.services.forgejo.reverseProxy.domain; + extraDomains = config.services.forgejo.reverseProxy.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + + networking.firewall.allowedTCPPorts = [ + config.services.forgejo.settings.server.SSH_LISTEN_PORT + ]; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/database.nix b/modules/nixos-modules/server/home-assistant/database.nix new file mode 100644 index 00000000..f1927ed9 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/database.nix @@ -0,0 +1,53 @@ +{ + lib, + config, + ... +}: { + options.services.home-assistant = { + postgres = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use PostgreSQL instead of SQLite"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "hass"; + description = "Database user name"; + }; + database = lib.mkOption { + type = lib.types.str; + default = "hass"; + description = "Database name"; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.enable { + assertions = [ + { + assertion = !config.services.home-assistant.postgres.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Home Assistant"; + } + ]; + + services.postgresql.databases.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + enable = true; + user = config.services.home-assistant.postgres.user; + database = config.services.home-assistant.postgres.database; + }; + + services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + extraPackages = python3Packages: + with python3Packages; [ + psycopg2 + ]; + }; + + systemd.services.home-assistant = lib.mkIf config.services.home-assistant.postgres.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/default.nix b/modules/nixos-modules/server/home-assistant/default.nix new file mode 100644 index 00000000..b6f9356d --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/default.nix @@ -0,0 +1,10 @@ +{ + imports = [ + ./home-assistant.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./impermanence.nix + ./extensions + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/default.nix b/modules/nixos-modules/server/home-assistant/extensions/default.nix new file mode 100644 index 00000000..9ef84a3f --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/default.nix @@ -0,0 +1,12 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + ./sonos.nix + ./jellyfin.nix + ./wyoming.nix + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix b/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix new file mode 100644 index 00000000..29af274b --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/jellyfin.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.jellyfin.enable) { + services.home-assistant.extraComponents = ["jellyfin"]; + # TODO: configure port, address, and login information here +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/sonos.nix b/modules/nixos-modules/server/home-assistant/extensions/sonos.nix new file mode 100644 index 00000000..c70649fb --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/sonos.nix @@ -0,0 +1,11 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.sonos.enable) { + services.home-assistant.extraComponents = ["sonos"]; + networking.firewall.allowedTCPPorts = [ + config.services.home-assistant.extensions.sonos.port + ]; +} diff --git a/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix b/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix new file mode 100644 index 00000000..840d360b --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/extensions/wyoming.nix @@ -0,0 +1,9 @@ +{ + lib, + config, + ... +}: +lib.mkIf (config.services.home-assistant.extensions.wyoming.enable) { + services.home-assistant.extraComponents = ["wyoming"]; + services.wyoming.enable = true; +} diff --git a/modules/nixos-modules/server/home-assistant/fail2ban.nix b/modules/nixos-modules/server/home-assistant/fail2ban.nix new file mode 100644 index 00000000..25194efe --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/fail2ban.nix @@ -0,0 +1,49 @@ +{ + lib, + pkgs, + config, + ... +}: { + options.services.home-assistant = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.services.home-assistant.enable; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/hass.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [INCLUDES] + before = common.conf + + [Definition] + failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from .*$ + + ignoreregex = + + [Init] + datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S + '') + ); + }; + + services.fail2ban = { + jails = { + home-assistant-iptables.settings = { + enabled = true; + filter = "hass"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.home-assistant.configDir}/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/home-assistant/home-assistant.nix b/modules/nixos-modules/server/home-assistant/home-assistant.nix new file mode 100644 index 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/impermanence.nix b/modules/nixos-modules/server/home-assistant/impermanence.nix new file mode 100644 index 00000000..8c056a1c --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/impermanence.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: let + configDir = "/var/lib/hass"; +in + lib.mkIf (config.host.impermanence.enable && config.services.home-assistant.enable) { + assertions = [ + { + assertion = config.services.home-assistant.configDir == configDir; + message = "home assistant config directory does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = configDir; + user = "hass"; + group = "hass"; + } + ]; + }; + } diff --git a/modules/nixos-modules/server/home-assistant/proxy.nix b/modules/nixos-modules/server/home-assistant/proxy.nix new file mode 100644 index 00000000..b7564599 --- /dev/null +++ b/modules/nixos-modules/server/home-assistant/proxy.nix @@ -0,0 +1,43 @@ +{ + lib, + config, + ... +}: { + options.services.home-assistant = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that home-assistant will be hosted at"; + default = "home-assistant.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for home-assistant"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.reverseProxy.enable && config.services.home-assistant.enable; + }; + }; + }; + + config = lib.mkIf config.services.home-assistant.reverseProxy.enable { + services.reverseProxy.services.home-assistant = { + target = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; + domain = config.services.home-assistant.domain; + extraDomains = config.services.home-assistant.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 90; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/database.nix b/modules/nixos-modules/server/immich/database.nix new file mode 100644 index 00000000..52af51eb --- /dev/null +++ b/modules/nixos-modules/server/immich/database.nix @@ -0,0 +1,30 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf config.services.immich.enable { + assertions = [ + { + assertion = !config.services.immich.database.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Immich"; + } + { + assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (db: db == "immich") config.services.postgresql.ensureDatabases); + message = "Immich built-in database creation failed - expected 'immich' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !(config.services.immich.database.enable && config.services.immich.database.createDB) || (builtins.any (user: user.name == "immich") config.services.postgresql.ensureUsers); + message = "Immich built-in user creation failed - expected user 'immich' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + # Note: Immich has built-in database creation via services.immich.database.createDB we only add the systemd dependency + + systemd.services.immich-server = lib.mkIf config.services.immich.database.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/default.nix b/modules/nixos-modules/server/immich/default.nix new file mode 100644 index 00000000..4d93c0b6 --- /dev/null +++ b/modules/nixos-modules/server/immich/default.nix @@ -0,0 +1,20 @@ +{...}: { + imports = [ + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./impermanence.nix + ]; + + # NOTE: This shouldn't be needed now that we are out of testing + # config = lib.mkIf config.services.immich.enable { + # networking.firewall.interfaces.${config.services.tailscale.interfaceName} = { + # allowedUDPPorts = [ + # config.services.immich.port + # ]; + # allowedTCPPorts = [ + # config.services.immich.port + # ]; + # }; + # }; +} diff --git a/modules/nixos-modules/server/immich/fail2ban.nix b/modules/nixos-modules/server/immich/fail2ban.nix new file mode 100644 index 00000000..21593e7b --- /dev/null +++ b/modules/nixos-modules/server/immich/fail2ban.nix @@ -0,0 +1,35 @@ +{ + lib, + config, + pkgs, + ... +}: { + options.services.immich = { + fail2ban = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.fail2ban.enable && config.services.immich.enable; + }; + }; + }; + + config = lib.mkIf config.services.immich.fail2ban.enable { + environment.etc = { + "fail2ban/filter.d/immich.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = immich-server.*Failed login attempt for user.+from ip address\s? + journalmatch = CONTAINER_TAG=immich-server + ''); + }; + + services.fail2ban = { + jails = { + immich-iptables.settings = { + enabled = true; + filter = "immich"; + backend = "systemd"; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/impermanence.nix b/modules/nixos-modules/server/immich/impermanence.nix new file mode 100644 index 00000000..56e51d06 --- /dev/null +++ b/modules/nixos-modules/server/immich/impermanence.nix @@ -0,0 +1,32 @@ +{ + lib, + config, + ... +}: let + mediaLocation = "/var/lib/immich"; +in { + options.services.immich = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.immich.impermanence.enable { + assertions = [ + { + assertion = config.services.immich.mediaLocation == mediaLocation; + message = "immich media location does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = mediaLocation; + user = "immich"; + group = "immich"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/immich/proxy.nix b/modules/nixos-modules/server/immich/proxy.nix new file mode 100644 index 00000000..9c8c165a --- /dev/null +++ b/modules/nixos-modules/server/immich/proxy.nix @@ -0,0 +1,44 @@ +{ + lib, + config, + ... +}: { + options.services.immich = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that immich will be hosted at"; + default = "immich.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for immich"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.immich.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.immich.reverseProxy.enable { + services.reverseProxy.services.immich = { + target = "http://localhost:${toString config.services.immich.port}"; + domain = config.services.immich.domain; + extraDomains = config.services.immich.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 50000; + + # Custom timeout settings + proxyHeaders = { + enable = true; + timeout = 600; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/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 new file mode 100644 index 00000000..2dbdcfd2 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./jellyfin.nix + ./proxy.nix + ./fail2ban.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/jellyfin/fail2ban.nix b/modules/nixos-modules/server/jellyfin/fail2ban.nix new file mode 100644 index 00000000..ba8d8bac --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/fail2ban.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: { + config = lib.mkIf (config.services.jellyfin.enable && config.services.fail2ban.enable) { + environment.etc = { + "fail2ban/filter.d/jellyfin.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = "^.*Authentication request for .* has been denied \\\\\\(IP: \\\"\\\"\\\\\\)\\\\\\." + '') + ); + }; + + services.fail2ban = { + jails = { + jellyfin-iptables.settings = { + enabled = true; + filter = "jellyfin"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.jellyfin.dataDir}/log/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/jellyfin/impermanence.nix b/modules/nixos-modules/server/jellyfin/impermanence.nix new file mode 100644 index 00000000..cbcb54f8 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/impermanence.nix @@ -0,0 +1,73 @@ +{ + lib, + config, + ... +}: let + jellyfin_data_directory = "/var/lib/jellyfin"; + jellyfin_cache_directory = "/var/cache/jellyfin"; +in { + options.services.jellyfin = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.jellyfin.impermanence.enable { + fileSystems."/persist/system/jellyfin".neededForBoot = true; + + host.storage.pool.extraDatasets = { + # sops age key needs to be available to pre persist for user generation + "persist/system/jellyfin" = { + type = "zfs_fs"; + mountpoint = "/persist/system/jellyfin"; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + }; + + assertions = [ + { + assertion = config.services.jellyfin.dataDir == jellyfin_data_directory; + message = "jellyfin data directory does not match persistence"; + } + { + assertion = config.services.jellyfin.cacheDir == jellyfin_cache_directory; + message = "jellyfin cache directory does not match persistence"; + } + ]; + + environment.persistence = { + "/persist/system/root" = { + directories = [ + { + directory = jellyfin_data_directory; + user = "jellyfin"; + group = "jellyfin"; + } + { + directory = jellyfin_cache_directory; + user = "jellyfin"; + group = "jellyfin"; + } + ]; + }; + + "/persist/system/jellyfin" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = config.services.jellyfin.media_directory; + user = "jellyfin"; + group = "jellyfin_media"; + mode = "1770"; + } + ]; + }; + }; + }; +} 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 new file mode 100644 index 00000000..35289e76 --- /dev/null +++ b/modules/nixos-modules/server/jellyfin/proxy.nix @@ -0,0 +1,41 @@ +{ + lib, + config, + ... +}: let + jellyfinPort = 8096; +in { + options.services.jellyfin = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that jellyfin will be hosted at"; + default = "jellyfin.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for jellyfin"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.jellyfin.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.jellyfin.reverseProxy.enable { + services.reverseProxy.services.jellyfin = { + target = "http://localhost:${toString jellyfinPort}"; + domain = config.services.jellyfin.domain; + extraDomains = config.services.jellyfin.extraDomains; + + settings = { + forwardHeaders.enable = true; + maxBodySize = 20; + noSniff.enable = true; + proxyBuffering.enable = false; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/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 new file mode 100644 index 00000000..cd100abc --- /dev/null +++ b/modules/nixos-modules/server/network_storage/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./network_storage.nix + ./nfs.nix + ]; +} diff --git a/modules/nixos-modules/server/network_storage/network_storage.nix b/modules/nixos-modules/server/network_storage/network_storage.nix new file mode 100644 index 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/network_storage/nfs.nix b/modules/nixos-modules/server/network_storage/nfs.nix new file mode 100644 index 00000000..297dc1a4 --- /dev/null +++ b/modules/nixos-modules/server/network_storage/nfs.nix @@ -0,0 +1,107 @@ +{ + config, + lib, + ... +}: { + options = { + host.network_storage.nfs = { + enable = lib.mkEnableOption "is this server going to export network storage as nfs shares"; + port = lib.mkOption { + type = lib.types.int; + default = 2049; + description = "port that nfs will run on"; + }; + directories = lib.mkOption { + type = lib.types.listOf ( + lib.types.enum ( + builtins.map ( + directory: directory.folder + ) + config.host.network_storage.directories + ) + ); + description = "list of exported directories to be exported via nfs"; + }; + }; + }; + config = lib.mkMerge [ + { + assertions = [ + { + assertion = !(config.host.network_storage.nfs.enable && !config.host.network_storage.enable); + message = "nfs cant be enabled with network storage disabled"; + } + ]; + } + ( + lib.mkIf (config.host.network_storage.nfs.enable && config.host.network_storage.enable) { + services.nfs = { + settings = { + nfsd = { + threads = 32; + port = config.host.network_storage.nfs.port; + }; + }; + server = { + enable = true; + + lockdPort = 4001; + mountdPort = 4002; + statdPort = 4000; + + exports = lib.strings.concatLines ( + [ + "${config.host.network_storage.export_directory} 100.64.0.0/10(rw,fsid=0,no_subtree_check)" + ] + ++ ( + lib.lists.imap0 ( + i: directory: let + createOptions = fsid: "(rw,fsid=${toString fsid},nohide,insecure,no_subtree_check)"; + addresses = [ + # loopback + "127.0.0.1" + "::1" + # tailscale + "100.64.0.0/10" + "fd7a:115c:a1e0::/48" + ]; + options = lib.strings.concatStrings ( + lib.strings.intersperse " " ( + lib.lists.imap0 (index: address: "${address}${createOptions (1 + (i * (builtins.length addresses)) + index)}") addresses + ) + ); + in "${directory._directory} ${options}" + ) + ( + builtins.filter ( + directory: lib.lists.any (target: target == directory.folder) config.host.network_storage.nfs.directories + ) + config.host.network_storage.directories + ) + ) + ); + }; + }; + networking.firewall = let + ports = [ + 111 + config.host.network_storage.nfs.port + config.services.nfs.server.lockdPort + config.services.nfs.server.mountdPort + config.services.nfs.server.statdPort + 20048 + ]; + in { + # Allow NFS on Tailscale interface + interfaces.${config.services.tailscale.interfaceName} = { + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + # Allow NFS on local network (assuming default interface) + allowedTCPPorts = ports; + allowedUDPPorts = ports; + }; + } + ) + ]; +} diff --git a/modules/nixos-modules/server/panoramax/database.nix b/modules/nixos-modules/server/panoramax/database.nix new file mode 100644 index 00000000..17217263 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/database.nix @@ -0,0 +1,48 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax = { + database = { + postgres = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Use PostgreSQL instead of SQLite"; + }; + user = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "Database user name"; + }; + database = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "Database name"; + }; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.enable { + assertions = [ + { + assertion = !config.services.panoramax.database.postgres.enable || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using postgres database for Panoramax"; + } + ]; + + services.postgresql.databases.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { + enable = true; + user = config.services.panoramax.database.postgres.user; + database = config.services.panoramax.database.postgres.database; + }; + + systemd.services.panoramax = lib.mkIf config.services.panoramax.database.postgres.enable { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/panoramax/default.nix b/modules/nixos-modules/server/panoramax/default.nix new file mode 100644 index 00000000..4c6b9ea0 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/default.nix @@ -0,0 +1,9 @@ +{...}: { + imports = [ + ./proxy.nix + ./fail2ban.nix + ./impermanence.nix + ./panoramax.nix + ./database.nix + ]; +} diff --git a/modules/nixos-modules/server/panoramax/fail2ban.nix b/modules/nixos-modules/server/panoramax/fail2ban.nix new file mode 100644 index 00000000..649b53a6 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/fail2ban.nix @@ -0,0 +1,11 @@ +{ + lib, + config, + ... +}: { + config = lib.mkIf (config.services.panoramax.enable && config.services.fail2ban.enable) { + # TODO: configure options for fail2ban + # This is a placeholder - panoramax fail2ban configuration would need to be defined + # based on the specific log patterns and security requirements + }; +} diff --git a/modules/nixos-modules/server/panoramax/impermanence.nix b/modules/nixos-modules/server/panoramax/impermanence.nix new file mode 100644 index 00000000..e25ef926 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/impermanence.nix @@ -0,0 +1,20 @@ +{ + lib, + config, + ... +}: { + 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 + # - panoramax storage directories + # - any cache or temporary directories that need to persist + }; +} diff --git a/modules/nixos-modules/server/panoramax/panoramax.nix b/modules/nixos-modules/server/panoramax/panoramax.nix new file mode 100644 index 00000000..fd77db7c --- /dev/null +++ b/modules/nixos-modules/server/panoramax/panoramax.nix @@ -0,0 +1,359 @@ +{ + config, + lib, + pkgs, + ... +}: { + options.services = { + panoramax = { + enable = lib.mkEnableOption "panoramax"; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.panoramax; + description = "The panoramax package to use"; + }; + + user = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The user panoramax should run as."; + }; + + group = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The group panoramax should run as."; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "Host to bind the panoramax service to"; + }; + + port = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = 5000; + description = "Port for the panoramax service"; + }; + + openFirewall = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to open the panoramax port in the firewall"; + }; + + settings = { + urlScheme = lib.mkOption { + type = lib.types.enum ["http" "https"]; + default = "https"; + description = "URL scheme for the application"; + }; + + storage = { + fsUrl = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "/var/lib/panoramax/storage"; + description = "File system URL for storage"; + }; + }; + + infrastructure = { + nbProxies = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = 1; + description = "Number of proxies in front of the application"; + }; + }; + + flask = { + secretKey = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Flask secret key for session security"; + }; + + sessionCookieDomain = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Flask session cookie domain"; + }; + }; + + api = { + pictures = { + licenseSpdxId = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "SPDX license identifier for API pictures"; + }; + + licenseUrl = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "License URL for API pictures"; + }; + }; + }; + + extraEnvironment = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = {}; + description = "Additional environment variables"; + example = { + CUSTOM_SETTING = "value"; + DEBUG = "true"; + }; + }; + }; + + database = { + createDB = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically create the database and user"; + }; + + name = lib.mkOption { + type = lib.types.str; + default = "panoramax"; + description = "The name of the panoramax database"; + }; + + host = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "/run/postgresql"; + description = "Hostname or address of the postgresql server. If an absolute path is given here, it will be interpreted as a unix socket path."; + }; + + port = lib.mkOption { + type = lib.types.nullOr lib.types.port; + default = 5432; + description = "Port of the postgresql server."; + }; + + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = "panoramax"; + description = "The database user for panoramax."; + }; + + # TODO: password file for external database + }; + + sgblur = { + # TODO: configs to bind to sgblur + }; + }; + sgblur = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether to enable sgblur integration for face and license plate blurring"; + }; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.sgblur; + description = "The sgblur package to use"; + }; + + port = lib.mkOption { + type = lib.types.port; + default = 8080; + description = "Port for the sgblur service"; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "Host to bind the sgblur service to"; + }; + + url = lib.mkOption { + type = lib.types.str; + default = "http://127.0.0.1:8080"; + description = "URL where sgblur service is accessible"; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.enable (lib.mkMerge [ + { + # Create panoramax user and group + users.users.${config.services.panoramax.user} = { + isSystemUser = true; + group = config.services.panoramax.group; + home = "/var/lib/panoramax"; + createHome = true; + }; + + users.groups.${config.services.panoramax.group} = {}; + + # Ensure storage directory exists with correct permissions + systemd.tmpfiles.rules = [ + "d '${config.services.panoramax.settings.storage.fsUrl}' 0755 ${config.services.panoramax.user} ${config.services.panoramax.group} - -" + ]; + + systemd.services.panoramax-api = { + description = "Panoramax API server (self hosted map street view)"; + after = ["network.target" "postgresql.service"]; + wantedBy = ["multi-user.target"]; + + environment = + { + # Core Flask configuration + FLASK_APP = "geovisio"; + + # Storage configuration + FS_URL = config.services.panoramax.settings.storage.fsUrl; + + # Infrastructure configuration + INFRA_NB_PROXIES = toString config.services.panoramax.settings.infrastructure.nbProxies; + + # Application configuration + PORT = toString config.services.panoramax.port; + + # Python path to include the panoramax package + PYTHONPATH = "${config.services.panoramax.package}/${pkgs.python3.sitePackages}"; + } + // ( + if config.services.panoramax.database.host == "/run/postgresql" + then { + DB_URL = "postgresql://${config.services.panoramax.database.user}@/${config.services.panoramax.database.name}?host=/run/postgresql"; + } + else { + DB_HOST = config.services.panoramax.database.host; + DB_PORT = toString config.services.panoramax.database.port; + DB_USERNAME = config.services.panoramax.database.user; + DB_NAME = config.services.panoramax.database.name; + } + ) + // (lib.optionalAttrs (config.services.panoramax.settings.flask.secretKey != null) { + FLASK_SECRET_KEY = config.services.panoramax.settings.flask.secretKey; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.flask.sessionCookieDomain != null) { + FLASK_SESSION_COOKIE_DOMAIN = config.services.panoramax.settings.flask.sessionCookieDomain; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseSpdxId != null) { + API_PICTURES_LICENSE_SPDX_ID = config.services.panoramax.settings.api.pictures.licenseSpdxId; + }) + // (lib.optionalAttrs (config.services.panoramax.settings.api.pictures.licenseUrl != null) { + API_PICTURES_LICENSE_URL = config.services.panoramax.settings.api.pictures.licenseUrl; + }) + // (lib.optionalAttrs config.services.sgblur.enable { + SGBLUR_API_URL = config.services.sgblur.url; + }) + // config.services.panoramax.settings.extraEnvironment; + + path = with pkgs; [ + (python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])) + ]; + + serviceConfig = { + ExecStart = "${pkgs.python3.withPackages (ps: with ps; [config.services.panoramax.package waitress])}/bin/waitress-serve --port ${toString config.services.panoramax.port} --call geovisio:create_app"; + User = config.services.panoramax.user; + Group = config.services.panoramax.group; + WorkingDirectory = "/var/lib/panoramax"; + Restart = "always"; + RestartSec = 5; + + # Security hardening + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + ReadWritePaths = [ + "/var/lib/panoramax" + config.services.panoramax.settings.storage.fsUrl + ]; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + }; + }; + + # Open firewall if requested + networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ + config.services.panoramax.port + ]; + } + (lib.mkIf config.services.sgblur.enable { + # SGBlur service configuration + systemd.services.sgblur = { + description = "SGBlur face and license plate blurring service"; + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + + path = with pkgs; [ + config.services.sgblur.package + python3 + python3Packages.waitress + ]; + + serviceConfig = { + ExecStart = "${pkgs.python3Packages.waitress}/bin/waitress-serve --host ${config.services.sgblur.host} --port ${toString config.services.sgblur.port} src.detect.detect_api:app"; + WorkingDirectory = "${config.services.sgblur.package}"; + Restart = "always"; + RestartSec = 5; + + # Basic security hardening + PrivateTmp = true; + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + SystemCallArchitectures = "native"; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf config.services.panoramax.openFirewall [ + config.services.sgblur.port + ]; + }) + (lib.mkIf config.services.panoramax.database.createDB { + services.postgresql = { + enable = true; + ensureDatabases = lib.mkIf config.services.panoramax.database.createDB [config.services.panoramax.database.name]; + ensureUsers = lib.mkIf config.services.panoramax.database.createDB [ + { + name = config.services.panoramax.database.user; + ensureDBOwnership = true; + ensureClauses.login = true; + } + ]; + extensions = ps: with ps; [postgis]; + }; + systemd.services.postgresql.serviceConfig.ExecStartPost = let + sqlFile = pkgs.writeText "panoramax-postgis-setup.sql" '' + CREATE EXTENSION IF NOT EXISTS postgis; + + -- TODO: how can we ensure that this runs after the databases have been created + -- ALTER DATABASE ${config.services.panoramax.database.name} SET TIMEZONE TO 'UTC'; + + GRANT SET ON PARAMETER session_replication_role TO ${config.services.panoramax.database.user}; + ''; + in [ + '' + ${lib.getExe' config.services.postgresql.package "psql"} -d "${config.services.panoramax.database.user}" -f "${sqlFile}" + '' + ]; + }) + ]); +} diff --git a/modules/nixos-modules/server/panoramax/proxy.nix b/modules/nixos-modules/server/panoramax/proxy.nix new file mode 100644 index 00000000..7cd71113 --- /dev/null +++ b/modules/nixos-modules/server/panoramax/proxy.nix @@ -0,0 +1,39 @@ +{ + lib, + config, + ... +}: { + options.services.panoramax = { + domain = lib.mkOption { + type = lib.types.str; + description = "domain that panoramax will be hosted at"; + default = "panoramax.arpa"; + }; + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for panoramax"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.panoramax.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.panoramax.reverseProxy.enable { + services.reverseProxy.services.panoramax = { + target = "http://localhost:${toString config.services.panoramax.port}"; + domain = config.services.panoramax.domain; + extraDomains = config.services.panoramax.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 100000; + timeout = 300; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/database.nix b/modules/nixos-modules/server/paperless/database.nix new file mode 100644 index 00000000..c63e59d9 --- /dev/null +++ b/modules/nixos-modules/server/paperless/database.nix @@ -0,0 +1,30 @@ +{ + config, + lib, + ... +}: { + config = lib.mkIf config.services.paperless.enable { + assertions = [ + { + assertion = !config.services.paperless.database.createLocally || config.services.postgresql.enable; + message = "PostgreSQL must be enabled when using local postgres database for Paperless"; + } + { + assertion = !config.services.paperless.database.createLocally || (builtins.any (db: db == "paperless") config.services.postgresql.ensureDatabases); + message = "Paperless built-in database creation failed - expected 'paperless' in ensureDatabases but got: ${builtins.toString config.services.postgresql.ensureDatabases}"; + } + { + assertion = !config.services.paperless.database.createLocally || (builtins.any (user: user.name == "paperless") config.services.postgresql.ensureUsers); + message = "Paperless built-in user creation failed - expected user 'paperless' in ensureUsers but got: ${builtins.toString (builtins.map (u: u.name) config.services.postgresql.ensureUsers)}"; + } + ]; + + services.paperless.database.createLocally = lib.mkDefault true; + + systemd.services.paperless-scheduler = lib.mkIf config.services.paperless.database.createLocally { + requires = [ + config.systemd.services.postgresql.name + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/default.nix b/modules/nixos-modules/server/paperless/default.nix new file mode 100644 index 00000000..7e5e16be --- /dev/null +++ b/modules/nixos-modules/server/paperless/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./paperless.nix + ./proxy.nix + ./database.nix + ./fail2ban.nix + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/paperless/fail2ban.nix b/modules/nixos-modules/server/paperless/fail2ban.nix new file mode 100644 index 00000000..e1a70f9f --- /dev/null +++ b/modules/nixos-modules/server/paperless/fail2ban.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + pkgs, + ... +}: { + config = lib.mkIf (config.services.paperless.enable && config.services.fail2ban.enable) { + environment.etc = { + "fail2ban/filter.d/paperless.local".text = ( + pkgs.lib.mkDefault (pkgs.lib.mkAfter '' + [Definition] + failregex = Login failed for user `.*` from (?:IP|private IP) ``\.$ + ignoreregex = + + '') + ); + }; + + services.fail2ban = { + jails = { + paperless.settings = { + enabled = true; + filter = "paperless"; + action = ''iptables-multiport[name=HTTP, port="http,https"]''; + logpath = "${config.services.paperless.dataDir}/log/*.log"; + backend = "auto"; + findtime = 600; + bantime = 600; + maxretry = 5; + }; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/paperless/impermanence.nix b/modules/nixos-modules/server/paperless/impermanence.nix new file mode 100644 index 00000000..fc87ea7d --- /dev/null +++ b/modules/nixos-modules/server/paperless/impermanence.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: let + dataDir = "/var/lib/paperless"; +in { + options.services.paperless = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.paperless.impermanence.enable { + assertions = [ + { + assertion = config.services.paperless.dataDir == dataDir; + message = "paperless data location does not match persistence"; + } + ]; + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = dataDir; + user = "paperless"; + group = "paperless"; + } + ]; + }; + }; +} 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 new file mode 100644 index 00000000..9d152c9c --- /dev/null +++ b/modules/nixos-modules/server/paperless/proxy.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + ... +}: { + options.services.paperless = { + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for paperless"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.paperless.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.paperless.reverseProxy.enable { + services.reverseProxy.services.paperless = { + target = "http://${config.services.paperless.address}:${toString config.services.paperless.port}"; + domain = config.services.paperless.domain; + extraDomains = config.services.paperless.extraDomains; + + settings = { + proxyWebsockets.enable = true; + forwardHeaders.enable = true; + maxBodySize = 50000; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/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/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 new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/radarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/radarr/impermanence.nix b/modules/nixos-modules/server/radarr/impermanence.nix new file mode 100644 index 00000000..c948e3a5 --- /dev/null +++ b/modules/nixos-modules/server/radarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + radarr_data_directory = "/var/lib/radarr/.config/Radarr"; +in { + options.services.radarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.radarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.radarr.impermanence.enable { + assertions = [ + { + assertion = config.services.radarr.dataDir == radarr_data_directory; + message = "radarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = radarr_data_directory; + user = "radarr"; + group = "radarr"; + } + ]; + }; + }; +} 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/searx/default.nix b/modules/nixos-modules/server/searx/default.nix new file mode 100644 index 00000000..54263802 --- /dev/null +++ b/modules/nixos-modules/server/searx/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./searx.nix + ./proxy.nix + ]; +} diff --git a/modules/nixos-modules/server/searx/proxy.nix b/modules/nixos-modules/server/searx/proxy.nix new file mode 100644 index 00000000..e994e4a1 --- /dev/null +++ b/modules/nixos-modules/server/searx/proxy.nix @@ -0,0 +1,31 @@ +{ + config, + lib, + ... +}: { + options.services.searx = { + extraDomains = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "extra domains that should be configured for searx"; + default = []; + }; + reverseProxy = { + enable = lib.mkOption { + type = lib.types.bool; + default = config.services.searx.enable && config.services.reverseProxy.enable; + }; + }; + }; + + config = lib.mkIf config.services.searx.reverseProxy.enable { + services.reverseProxy.services.searx = { + target = "http://localhost:${toString config.services.searx.settings.server.port}"; + domain = config.services.searx.domain; + extraDomains = config.services.searx.extraDomains; + + settings = { + forwardHeaders.enable = true; + }; + }; + }; +} diff --git a/modules/nixos-modules/server/searx/searx.nix b/modules/nixos-modules/server/searx/searx.nix new file mode 100644 index 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 new file mode 100644 index 00000000..86dbb4b1 --- /dev/null +++ b/modules/nixos-modules/server/sonarr/default.nix @@ -0,0 +1,5 @@ +{...}: { + imports = [ + ./impermanence.nix + ]; +} diff --git a/modules/nixos-modules/server/sonarr/impermanence.nix b/modules/nixos-modules/server/sonarr/impermanence.nix new file mode 100644 index 00000000..5b90ee9d --- /dev/null +++ b/modules/nixos-modules/server/sonarr/impermanence.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + sonarr_data_directory = "/var/lib/sonarr/.config/NzbDrone"; +in { + options.services.sonarr = { + impermanence.enable = lib.mkOption { + type = lib.types.bool; + default = config.services.sonarr.enable && config.host.impermanence.enable; + }; + }; + + config = lib.mkIf config.services.sonarr.impermanence.enable { + assertions = [ + { + assertion = config.services.sonarr.dataDir == sonarr_data_directory; + message = "sonarr data directory does not match persistence"; + } + ]; + + environment.persistence."/persist/system/root" = { + directories = [ + { + directory = sonarr_data_directory; + user = "sonarr"; + group = "sonarr"; + } + ]; + }; + }; +} diff --git a/modules/nixos-modules/server/wyoming.nix b/modules/nixos-modules/server/wyoming.nix new file mode 100644 index 00000000..c9a14741 --- /dev/null +++ b/modules/nixos-modules/server/wyoming.nix @@ -0,0 +1,63 @@ +{ + lib, + config, + ... +}: { + options.services.wyoming.enable = lib.mkEnableOption "should wyoming be enabled on this device"; + config = lib.mkIf config.services.wyoming.enable (lib.mkMerge [ + { + services.wyoming = { + # Text to speech + piper = { + servers = { + "en" = { + enable = true; + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/tts/piper/script/download.py + voice = "en-us-amy-low"; + uri = "tcp://0.0.0.0:10200"; + speaker = 0; + }; + }; + }; + + # Speech to text + faster-whisper = { + servers = { + "en" = { + enable = true; + # see https://github.com/rhasspy/rhasspy3/blob/master/programs/asr/faster-whisper/script/download.py + model = "tiny-int8"; + language = "en"; + uri = "tcp://0.0.0.0:10300"; + device = "cpu"; + }; + }; + }; + + openwakeword = { + enable = true; + uri = "tcp://0.0.0.0:10400"; + # preloadModels = [ + # "ok_nabu" + # ]; + # TODO: custom models + }; + }; + + # needs access to /proc/cpuinfo + systemd.services."wyoming-faster-whisper-en".serviceConfig.ProcSubset = lib.mkForce "all"; + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = "/var/lib/private/wyoming"; + mode = "0700"; + } + ]; + }; + }) + ]); +} diff --git a/modules/nixos-modules/ssh.nix b/modules/nixos-modules/ssh.nix new file mode 100644 index 00000000..6f5fac18 --- /dev/null +++ b/modules/nixos-modules/ssh.nix @@ -0,0 +1,28 @@ +{ + lib, + config, + ... +}: { + config = lib.mkMerge [ + { + services = { + openssh = { + enable = true; + ports = [22]; + settings = { + PasswordAuthentication = false; + UseDns = true; + X11Forwarding = false; + }; + }; + }; + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence."/persist/system/root" = { + files = lib.lists.flatten ( + builtins.map (hostKey: [hostKey.path "${hostKey.path}.pub"]) config.services.openssh.hostKeys + ); + }; + }) + ]; +} diff --git a/modules/nixos-modules/steam.nix b/modules/nixos-modules/steam.nix new file mode 100644 index 00000000..20c09789 --- /dev/null +++ b/modules/nixos-modules/steam.nix @@ -0,0 +1,9 @@ +{...}: { + programs = { + steam = { + remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play + dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server + localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers + }; + }; +} diff --git a/modules/nixos-modules/sync.nix b/modules/nixos-modules/sync.nix new file mode 100644 index 00000000..bf430413 --- /dev/null +++ b/modules/nixos-modules/sync.nix @@ -0,0 +1,69 @@ +{ + config, + lib, + outputs, + ... +}: let + mountDir = "/mnt/sync"; + configDir = "/etc/syncthing"; +in { + config = lib.mkMerge [ + { + systemd = lib.mkIf config.services.syncthing.enable { + tmpfiles.rules = [ + "A ${mountDir} - - - - u:syncthing:rwX,g:syncthing:rwX,o::-" + "d ${mountDir} 2755 syncthing syncthing -" + "d ${config.services.syncthing.dataDir} 775 syncthing syncthing -" + "d ${config.services.syncthing.configDir} 755 syncthing syncthing -" + ]; + }; + } + (lib.mkIf config.services.syncthing.enable (lib.mkMerge [ + { + services.syncthing = { + user = "syncthing"; + group = "syncthing"; + dataDir = "${mountDir}/default"; + configDir = configDir; + overrideDevices = true; + overrideFolders = true; + configuration = outputs.syncthingConfiguration; + deviceName = config.networking.hostName; + }; + } + + (lib.mkIf config.host.impermanence.enable { + assertions = + [ + { + assertion = config.services.syncthing.configDir == configDir; + message = "syncthing config dir does not match persistence"; + } + ] + ++ lib.attrsets.mapAttrsToList (_: folder: { + assertion = lib.strings.hasPrefix mountDir folder.path; + message = "syncthing folder ${folder.label} is stored at ${folder.path} which not under the persisted path of ${mountDir}"; + }) + config.services.syncthing.settings.folders; + environment.persistence = { + "/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = mountDir; + user = "syncthing"; + group = "syncthing"; + } + { + directory = configDir; + user = "syncthing"; + group = "syncthing"; + } + ]; + }; + }; + }) + ])) + ]; +} diff --git a/modules/nixos-modules/system.nix b/modules/nixos-modules/system.nix new file mode 100644 index 00000000..b8390679 --- /dev/null +++ b/modules/nixos-modules/system.nix @@ -0,0 +1,13 @@ +{...}: { + nix = { + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 7d"; + }; + optimise = { + automatic = true; + dates = ["weekly"]; + }; + }; +} diff --git a/modules/nixos-modules/tailscale.nix b/modules/nixos-modules/tailscale.nix new file mode 100644 index 00000000..db664e81 --- /dev/null +++ b/modules/nixos-modules/tailscale.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + ... +}: let + tailscale_data_directory = "/var/lib/tailscale"; +in { + options.host.tailscale = { + enable = lib.mkEnableOption "should tailscale be enabled on this computer"; + }; + + config = lib.mkIf config.services.tailscale.enable ( + lib.mkMerge [ + { + # any configs we want shared between all machines + } + (lib.mkIf config.host.impermanence.enable { + environment.persistence = { + "/persist/system/root" = { + enable = true; + hideMounts = true; + directories = [ + { + directory = tailscale_data_directory; + user = "root"; + group = "root"; + } + ]; + }; + }; + }) + ] + ); +} diff --git a/modules/nixos-modules/users.nix b/modules/nixos-modules/users.nix new file mode 100644 index 00000000..987e080e --- /dev/null +++ b/modules/nixos-modules/users.nix @@ -0,0 +1,477 @@ +{ + lib, + config, + inputs, + ... +}: let + SOPS_AGE_KEY_DIRECTORY = import ../../const/sops_age_key_directory.nix; + + host = config.host; + + principleUsers = host.principleUsers; + terminalUsers = host.terminalUsers; + normalUsers = host.normalUsers; + + uids = { + leyla = 1000; + eve = 1002; + ivy = 1004; + jellyfin = 2000; + forgejo = 2002; + hass = 2004; + syncthing = 2007; + ollama = 2008; + git = 2009; + immich = 2010; + qbittorrent = 2011; + paperless = 2012; + actual = 2013; + radarr = 2014; + sonarr = 2015; + bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; + }; + + gids = { + leyla = 1000; + eve = 1002; + ivy = 1004; + users = 100; + jellyfin_media = 2001; + jellyfin = 2000; + forgejo = 2002; + hass = 2004; + syncthing = 2007; + ollama = 2008; + git = 2009; + immich = 2010; + qbittorrent = 2011; + paperless = 2012; + actual = 2013; + radarr = 2014; + sonarr = 2015; + bazarr = 2016; + lidarr = 2017; + crab-hole = 2018; + }; + + users = config.users.users; + leyla = users.leyla.name; + eve = users.eve.name; + ivy = users.ivy.name; +in { + config = lib.mkMerge [ + { + # principle users are by definition trusted + nix.settings.trusted-users = builtins.map (user: user.name) principleUsers; + + # we should only be able to ssh into principle users of a computer who are also set up for terminal access + services.openssh.settings.AllowUsers = builtins.map (user: user.name) (lib.lists.intersectLists terminalUsers principleUsers); + + # we need to set up env variables to nix can find keys to decrypt passwords on rebuild + environment = { + sessionVariables = { + SOPS_AGE_KEY_DIRECTORY = SOPS_AGE_KEY_DIRECTORY; + SOPS_AGE_KEY_FILE = "${SOPS_AGE_KEY_DIRECTORY}/key.txt"; + }; + }; + + # set up user passwords + sops = { + defaultSopsFormat = "yaml"; + gnupg.sshKeyPaths = []; + + age = { + keyFile = "/var/lib/sops-nix/key.txt"; + sshKeyPaths = []; + # generateKey = true; + }; + + secrets = { + "passwords/leyla" = { + neededForUsers = true; + sopsFile = "${inputs.secrets}/user-passwords.yaml"; + }; + "passwords/eve" = { + neededForUsers = true; + sopsFile = "${inputs.secrets}/user-passwords.yaml"; + }; + "passwords/ivy" = { + neededForUsers = true; + sopsFile = "${inputs.secrets}/user-passwords.yaml"; + }; + }; + }; + + users = { + mutableUsers = false; + users = { + leyla = { + uid = lib.mkForce uids.leyla; + name = lib.mkForce host.users.leyla.name; + description = "Leyla"; + extraGroups = + (lib.lists.optionals host.users.leyla.isNormalUser ["networkmanager"]) + ++ (lib.lists.optionals host.users.leyla.isPrincipleUser ["wheel" "dialout"]) + ++ (lib.lists.optionals host.users.leyla.isDesktopUser ["adbusers"]); + hashedPasswordFile = config.sops.secrets."passwords/leyla".path; + isNormalUser = host.users.leyla.isNormalUser; + isSystemUser = !host.users.leyla.isNormalUser; + group = config.users.users.leyla.name; + }; + + eve = { + uid = lib.mkForce uids.eve; + name = lib.mkForce host.users.eve.name; + description = "Eve"; + extraGroups = + lib.optionals host.users.eve.isNormalUser ["networkmanager"] + ++ (lib.lists.optionals host.users.eve.isPrincipleUser ["wheel"]); + hashedPasswordFile = config.sops.secrets."passwords/eve".path; + isNormalUser = host.users.eve.isNormalUser; + isSystemUser = !host.users.eve.isNormalUser; + group = config.users.users.eve.name; + }; + + ivy = { + uid = lib.mkForce uids.ivy; + name = lib.mkForce host.users.ivy.name; + description = "Ivy"; + extraGroups = + lib.optionals host.users.ivy.isNormalUser ["networkmanager"] + ++ (lib.lists.optionals host.users.ivy.isPrincipleUser ["wheel"]); + hashedPasswordFile = config.sops.secrets."passwords/ivy".path; + isNormalUser = host.users.ivy.isNormalUser; + isSystemUser = !host.users.ivy.isNormalUser; + group = config.users.users.ivy.name; + }; + + jellyfin = { + uid = lib.mkForce uids.jellyfin; + isSystemUser = true; + group = config.users.users.jellyfin.name; + }; + + forgejo = { + uid = lib.mkForce uids.forgejo; + isSystemUser = true; + group = config.users.users.forgejo.name; + }; + + hass = { + uid = lib.mkForce uids.hass; + isSystemUser = true; + group = config.users.users.hass.name; + }; + + syncthing = { + uid = lib.mkForce uids.syncthing; + isSystemUser = true; + group = config.users.users.syncthing.name; + }; + + ollama = { + uid = lib.mkForce uids.ollama; + isSystemUser = true; + group = config.users.users.ollama.name; + }; + + git = { + uid = lib.mkForce uids.git; + isSystemUser = !config.services.forgejo.enable; + isNormalUser = config.services.forgejo.enable; + group = config.users.users.git.name; + }; + + immich = { + uid = lib.mkForce uids.immich; + isSystemUser = true; + group = config.users.users.immich.name; + }; + + qbittorrent = { + uid = lib.mkForce uids.qbittorrent; + isSystemUser = true; + group = config.users.users.qbittorrent.name; + }; + + paperless = { + uid = lib.mkForce uids.paperless; + isSystemUser = true; + group = config.users.users.paperless.name; + }; + + actual = { + uid = lib.mkForce uids.actual; + isSystemUser = true; + group = config.users.users.actual.name; + }; + + radarr = { + uid = lib.mkForce uids.radarr; + isSystemUser = true; + group = config.users.users.radarr.name; + }; + + sonarr = { + uid = lib.mkForce uids.sonarr; + isSystemUser = true; + group = config.users.users.sonarr.name; + }; + + bazarr = { + uid = lib.mkForce uids.bazarr; + isSystemUser = true; + group = config.users.users.bazarr.name; + }; + + lidarr = { + uid = lib.mkForce uids.lidarr; + isSystemUser = true; + group = config.users.users.lidarr.name; + }; + + crab-hole = { + uid = lib.mkForce uids.crab-hole; + isSystemUser = true; + group = config.users.users.crab-hole.name; + }; + }; + + groups = { + leyla = { + gid = lib.mkForce gids.leyla; + members = [ + leyla + ]; + }; + + eve = { + gid = lib.mkForce gids.eve; + members = [ + eve + ]; + }; + + ivy = { + gid = lib.mkForce gids.ivy; + members = [ + ivy + ]; + }; + + users = { + gid = lib.mkForce gids.users; + members = [ + leyla + eve + ivy + ]; + }; + + jellyfin_media = { + gid = lib.mkForce gids.jellyfin_media; + members = [ + users.jellyfin.name + users.radarr.name + users.sonarr.name + users.bazarr.name + users.lidarr.name + leyla + eve + ivy + ]; + }; + + jellyfin = { + gid = lib.mkForce gids.jellyfin; + members = [ + users.jellyfin.name + # leyla + ]; + }; + + forgejo = { + gid = lib.mkForce gids.forgejo; + members = [ + users.forgejo.name + # leyla + ]; + }; + + hass = { + gid = lib.mkForce gids.hass; + members = [ + users.hass.name + # leyla + ]; + }; + + syncthing = { + gid = lib.mkForce gids.syncthing; + members = [ + users.syncthing.name + leyla + eve + ivy + ]; + }; + + ollama = { + gid = lib.mkForce gids.ollama; + members = [ + users.ollama.name + ]; + }; + + git = { + gid = lib.mkForce gids.git; + members = [ + users.git.name + ]; + }; + + immich = { + gid = lib.mkForce gids.immich; + members = [ + users.immich.name + # leyla + ]; + }; + + qbittorrent = { + gid = lib.mkForce gids.qbittorrent; + members = [ + users.qbittorrent.name + leyla + ]; + }; + + paperless = { + gid = lib.mkForce gids.paperless; + members = [ + users.paperless.name + ]; + }; + + actual = { + gid = lib.mkForce gids.actual; + members = [ + users.actual.name + ]; + }; + + radarr = { + gid = lib.mkForce gids.radarr; + members = [ + users.radarr.name + ]; + }; + + sonarr = { + gid = lib.mkForce gids.sonarr; + members = [ + users.sonarr.name + ]; + }; + + bazarr = { + gid = lib.mkForce gids.bazarr; + members = [ + users.bazarr.name + ]; + }; + + lidarr = { + gid = lib.mkForce gids.lidarr; + members = [ + users.lidarr.name + ]; + }; + + crab-hole = { + gid = lib.mkForce gids.crab-hole; + members = [ + users.crab-hole.name + ]; + }; + }; + }; + } + (lib.mkIf config.host.impermanence.enable { + boot.initrd.postResumeCommands = lib.mkAfter ( + lib.strings.concatLines (builtins.map (user: "zfs rollback -r rpool/local/home/${user.name}@blank") + normalUsers) + ); + + systemd = { + tmpfiles.rules = + builtins.map ( + user: "d /persist/home/${user.name} 700 ${user.name} ${user.name} -" + ) + normalUsers; + }; + + fileSystems = lib.mkMerge [ + { + ${SOPS_AGE_KEY_DIRECTORY}.neededForBoot = true; + } + ( + builtins.listToAttrs ( + builtins.map (user: + lib.attrsets.nameValuePair "/persist/home/${user.name}" { + neededForBoot = true; + }) + normalUsers + ) + ) + ( + builtins.listToAttrs ( + builtins.map (user: + lib.attrsets.nameValuePair "/home/${user.name}" { + neededForBoot = true; + }) + normalUsers + ) + ) + ]; + + host.storage.pool.extraDatasets = lib.mkMerge ( + [ + { + # sops age key needs to be available to pre persist for user generation + "local/system/sops" = { + type = "zfs_fs"; + mountpoint = SOPS_AGE_KEY_DIRECTORY; + options = { + atime = "off"; + relatime = "off"; + canmount = "on"; + }; + }; + } + ] + ++ ( + builtins.map (user: { + "local/home/${user.name}" = { + type = "zfs_fs"; + mountpoint = "/home/${user.name}"; + options = { + canmount = "on"; + }; + postCreateHook = '' + zfs snapshot rpool/local/home/${user.name}@blank + ''; + }; + "persist/home/${user.name}" = { + type = "zfs_fs"; + mountpoint = "/persist/home/${user.name}"; + }; + }) + normalUsers + ) + ); + }) + ]; +} diff --git a/modules/system-modules/default.nix b/modules/system-modules/default.nix new file mode 100644 index 00000000..637b6b53 --- /dev/null +++ b/modules/system-modules/default.nix @@ -0,0 +1,9 @@ +# this folder container modules that are for nixos and darwin +{...}: { + imports = [ + ./home-manager + ./system.nix + ./nix-development.nix + ./users.nix + ]; +} diff --git a/modules/system-modules/home-manager/default.nix b/modules/system-modules/home-manager/default.nix new file mode 100644 index 00000000..3745b8fe --- /dev/null +++ b/modules/system-modules/home-manager/default.nix @@ -0,0 +1,2 @@ +# modules in this folder are to adapt home-manager modules configs to system-module configs +{...}: {} diff --git a/modules/system-modules/nix-development.nix b/modules/system-modules/nix-development.nix new file mode 100644 index 00000000..6eeddc4a --- /dev/null +++ b/modules/system-modules/nix-development.nix @@ -0,0 +1,26 @@ +{ + lib, + pkgs, + config, + inputs, + ... +}: { + options.host.nix-development.enable = lib.mkEnableOption "should desktop configuration be enabled"; + + config = lib.mkMerge [ + { + host.nix-development.enable = lib.mkDefault true; + } + (lib.mkIf config.host.nix-development.enable { + nix = { + nixPath = ["nixpkgs=${inputs.nixpkgs}"]; + }; + environment.systemPackages = with pkgs; [ + # nix language server + nil + # nix formatter + alejandra + ]; + }) + ]; +} diff --git a/modules/system-modules/system.nix b/modules/system-modules/system.nix new file mode 100644 index 00000000..f464835d --- /dev/null +++ b/modules/system-modules/system.nix @@ -0,0 +1,7 @@ +{...}: { + nix = { + settings = { + experimental-features = ["nix-command" "flakes"]; + }; + }; +} diff --git a/modules/system-modules/users.nix b/modules/system-modules/users.nix new file mode 100644 index 00000000..dda9ed37 --- /dev/null +++ b/modules/system-modules/users.nix @@ -0,0 +1,118 @@ +{ + lib, + config, + ... +}: let + host = config.host; + + hostUsers = host.hostUsers; + principleUsers = host.principleUsers; +in { + options.host = { + users = lib.mkOption { + default = {}; + type = lib.types.attrsOf (lib.types.submodule ({ + config, + name, + ... + }: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + description = '' + What should this users name on the system be + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.name"; + }; + isPrincipleUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should be configured as root and have ssh access + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isPrincipleUser"; + }; + isDesktopUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should install their desktop applications + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isDesktopUser"; + }; + isTerminalUser = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + User should install their terminal applications + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isTerminalUser"; + }; + isNormalUser = lib.mkOption { + type = lib.types.bool; + default = config.isDesktopUser || config.isTerminalUser; + description = '' + User should install their applications and can log in + ''; + defaultText = lib.literalExpression "config.host.users.\${name}.isNormalUser"; + }; + }; + })); + }; + hostUsers = lib.mkOption { + default = lib.attrsets.mapAttrsToList (_: user: user) host.users; + }; + principleUsers = lib.mkOption { + default = lib.lists.filter (user: user.isPrincipleUser) hostUsers; + }; + normalUsers = lib.mkOption { + default = lib.lists.filter (user: user.isNormalUser) hostUsers; + }; + desktopUsers = lib.mkOption { + default = lib.lists.filter (user: user.isDesktopUser) hostUsers; + }; + terminalUsers = lib.mkOption { + default = lib.lists.filter (user: user.isTerminalUser) hostUsers; + }; + }; + + config = { + host.users = { + leyla = { + isPrincipleUser = lib.mkDefault false; + isDesktopUser = lib.mkDefault false; + isTerminalUser = lib.mkDefault false; + }; + eve = { + isPrincipleUser = lib.mkDefault false; + isDesktopUser = lib.mkDefault false; + isTerminalUser = lib.mkDefault false; + }; + ivy = { + isPrincipleUser = lib.mkDefault false; + isDesktopUser = lib.mkDefault false; + isTerminalUser = lib.mkDefault false; + }; + }; + + assertions = + ( + builtins.map (user: { + assertion = !(user.isPrincipleUser && !user.isNormalUser); + message = '' + Non normal user ${user.name} can not be a principle user. + ''; + }) + hostUsers + ) + ++ [ + { + assertion = (builtins.length principleUsers) > 0; + message = '' + At least one user must be a principle user. + ''; + } + ]; + }; +} diff --git a/nix-config-secrets b/nix-config-secrets new file mode 160000 index 00000000..444229a1 --- /dev/null +++ b/nix-config-secrets @@ -0,0 +1 @@ +Subproject commit 444229a105445339fb028d15a8d866063c5f8141 diff --git a/overlays/intellij.nix b/overlays/intellij.nix deleted file mode 100644 index d83bd153..00000000 --- a/overlays/intellij.nix +++ /dev/null @@ -1,19 +0,0 @@ -_: -{ - # nixpkgs.overlays = [ - # (self: super: { - # # idea is too out of date for android gradle things - # jetbrains = { - # jdk = super.jdk17; - # idea-community = super.jetbrains.idea-community.overrideAttrs (oldAttrs: rec { - # version = "2023.3.3"; - # name = "idea-community-${version}"; - # src = super.fetchurl { - # sha256 = "sha256-3BI97Tx+3onnzT1NXkb62pa4dj9kjNDNvFt9biYgP9I="; - # url = "https://download.jetbrains.com/idea/ideaIC-${version}.tar.gz"; - # }; - # }); - # }; - # }) - # ]; -} \ No newline at end of file diff --git a/overlays/vscodium.nix b/overlays/vscodium.nix deleted file mode 100644 index 618af196..00000000 --- a/overlays/vscodium.nix +++ /dev/null @@ -1,15 +0,0 @@ -_: -{ - # nixpkgs.overlays = [ - # (self: super: { - # # ui is broken on 1.84 - # vscodium = super.vscodium.overrideAttrs (oldAttrs: rec { - # version = "1.85.2.24019"; - # src = super.fetchurl { - # sha256 = "sha256-OBGFXOSN+Oq9uj/5O6tF0Kp7rxTY1AzNbhLK8G+EqVk="; - # url = "https://github.com/VSCodium/vscodium/releases/download/${version}/VSCodium-linux-x64-${version}.tar.gz"; - # }; - # }); - # }) - # ]; -} \ No newline at end of file diff --git a/pkgs/default.nix b/pkgs/default.nix deleted file mode 100644 index 9a81f3b6..00000000 --- a/pkgs/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -pkgs: { - -} \ No newline at end of file diff --git a/rebuild.sh b/rebuild.sh index b37be136..67504502 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -1,12 +1,31 @@ #!/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 +else + preserve_result=false +fi + +show_trace=false + while [ $# -gt 0 ]; do case "$1" in --target*|-t*) if [[ "$1" != *=* ]]; then shift; fi # Value is next arg if no `=` target="${1#*=}" ;; - --flake*|-h*) + --flake*|-f*) if [[ "$1" != *=* ]]; then shift; fi flake="${1#*=}" ;; @@ -18,12 +37,44 @@ while [ $# -gt 0 ]; do if [[ "$1" != *=* ]]; then shift; fi user="${1#*=}" ;; + --host*) + if [[ "$1" != *=* ]]; then shift; fi + host="${1#*=}" + ;; + --preserve-result) + preserve_result=true + ;; + --no-preserve-result) + preserve_result=false + ;; + --show-trace) + show_trace=true + ;; --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 "--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 ;; *) @@ -34,14 +85,46 @@ 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} -if [[ "$target" == "$(hostname)" ]] -then - nixos-rebuild $mode --use-remote-sudo --flake .#$flake -else - nixos-rebuild $mode --use-remote-sudo --target-host $user@$target --flake .#$flake +# 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" + +if [[ $host ]]; +then + command="$command --build-host $host" +fi + +if [[ "$target" != "$(hostname)" ]]; +then + command="$command --target-host $user@$target" +fi + +if [[ "$show_trace" = true ]]; +then + command="$command --show-trace" +fi + +echo $command +$command + +if [ -d "result" ]; +then + if [[ "$preserve_result" == "false" ]]; + then + rm -r result + fi fi diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml deleted file mode 100644 index 1c7579ef..00000000 --- a/secrets/secrets.yaml +++ /dev/null @@ -1,24 +0,0 @@ -passwords: - leyla: ENC[AES256_GCM,data:c69e5uF40ACxVI0zXizydaqMVk6MXVJ13HwptHKeYIJ9H6bCgZRK0HCoTYw366mIpe7zt2V/OVdNr6hdzGfLa90/iOAMaCGqgw==,iv:esVvjfJm3RvO8RdXPvrnT/+At7VFl9Vt6077I5Ks89Q=,tag:fHfIFBRVH3y/V16rHYsT2g==,type:str] - ester: ENC[AES256_GCM,data:Cz3oXNOVz35Uino3HLUNcao4YbG1QwmZn6ulWafGpa6Z3U+X+92f+PpHNx6L+q9ToIDabx0vNGs0Pfsrs4y9k/nmhWB1i66PzA==,iv:pY3aVbxmILYXHG06+XJWM6nHA8FbmsNBssh5LXplCOM=,tag:D09d2Bv4SAO7v4JeHVM+tw==,type:str] - eve: ENC[AES256_GCM,data:XvJjFNIujwk9ttYLTbAE+PEMUpWzLXrJeJJ0aEqWBwx+gjOwX4XVg0J/B75ByJxflh9RSwB0oAGfC+6coAHoMTXPyym52zAYBw==,iv:lVbZ8uC6IKn3Bew0LHmwl47nFfBuNqslltNBiv6cx7I=,tag:lgE0N6JKDcOPqynwtXJKzQ==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age15ga3jmn2mqtlgwwtdcdh6l5vdx6um9aftrkexxfyue6xvcqapqusle75jh - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXd3BCR2RmMms4ZkNlbTdy - SzRKQ1NqZmFmOHJIS0oxZ3BMSnUyWXAyUUVrCk9tNjNNb0xEcnkvamJpSFF5UlhU - ejF4ZHFlZzJoemxpWXd0clN3cFZvMlkKLS0tIDdoK1oxc2doQTh3QlVyc3dhUE1W - VFBiZm5ZK2kwZjJPd3dCai9QUlpLaFEKFuwGgcdleN69voM5mpsa4J/ulmzZo7q+ - Q7KHOOidDH9C4xKjztYMuJSyviOYiIgILhljMXbNlmZnRs867gmmbw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-09-03T02:19:43Z" - mac: ENC[AES256_GCM,data:Wc8nCiXVj6/+FANq82T+KsObOgwKUJTfkEnrK5MRU5gbLF3Skn0BY/alskV4aI9Kgi1cwh5ZBhHNzvyeIujuRB55QYyoocY0Pq7vLH5dgnA58DKEzrb09SAayiiH9hzRSTkdhtxj8FgCAdA6dWVkHEAO351ee67QNkG0nSwDdK0=,iv:vwUO50SKvzAPwACV1xhh7r+Am/OdlkNEN1pMimEVfC8=,tag:yF2CK41sLHLQqIISlQGAGg==,type:str] - pgp: [] - unencrypted_suffix: _unencrypted - version: 3.9.0 diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..d7c46b9e --- /dev/null +++ b/shell.nix @@ -0,0 +1,14 @@ +( + import + ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } + ) + {src = ./.;} +) +.shellNix diff --git a/templates/default.nix b/templates/default.nix deleted file mode 100644 index f9d63b0f..00000000 --- a/templates/default.nix +++ /dev/null @@ -1,2 +0,0 @@ -_: -{} \ No newline at end of file diff --git a/users/default.nix b/users/default.nix deleted file mode 100644 index 57effe39..00000000 --- a/users/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ inputs, ... }: -{ - imports = [ ./leyla ./ester ./eve ]; - - users.mutableUsers = false; - - home-manager.extraSpecialArgs = { inherit inputs; }; -} \ No newline at end of file diff --git a/users/ester/default.nix b/users/ester/default.nix deleted file mode 100644 index 156716f3..00000000 --- a/users/ester/default.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ lib, config, pkgs, ... }: -let - cfg = config.users.ester; -in -{ - options.users.ester = { - isFullUser = lib.mkEnableOption "ester"; - }; - - config = { - sops.secrets = lib.mkIf cfg.isFullUser { - "passwords/ester" = { - neededForUsers = true; - # sopsFile = ../secrets.yaml; - }; - }; - - users.groups.ester = {}; - - users.users.ester = lib.mkMerge [ - { - uid = 1001; - description = "Ester"; - group = "ester"; - } - - ( - if cfg.isFullUser then { - isNormalUser = true; - extraGroups = [ "networkmanager" "users" ]; - - hashedPasswordFile = config.sops.secrets."passwords/ester".path; - - packages = with pkgs; [ - firefox - bitwarden - discord - ]; - } else { - isSystemUser = true; - } - ) - ]; - }; -} \ No newline at end of file diff --git a/users/eve/default.nix b/users/eve/default.nix deleted file mode 100644 index 4ed06a8e..00000000 --- a/users/eve/default.nix +++ /dev/null @@ -1,47 +0,0 @@ -{ lib, config, pkgs, ... }: -let - cfg = config.users.eve; -in -{ - options.users.eve = { - isFullUser = lib.mkEnableOption "eve"; - }; - - config = { - sops.secrets = lib.mkIf cfg.isFullUser { - "passwords/eve" = { - neededForUsers = true; - # sopsFile = ../secrets.yaml; - }; - }; - - users.groups.eve = {}; - - users.users.eve = lib.mkMerge [ - { - uid = 1002; - description = "Eve"; - group = "eve"; - } - - ( - if cfg.isFullUser then { - isNormalUser = true; - extraGroups = [ "networkmanager" "users" ]; - - hashedPasswordFile = config.sops.secrets."passwords/eve".path; - - packages = with pkgs; [ - firefox - bitwarden - discord - makemkv - signal-desktop - ]; - } else { - isSystemUser = true; - } - ) - ]; - }; -} \ No newline at end of file diff --git a/users/leyla/default.nix b/users/leyla/default.nix deleted file mode 100644 index 7a8dc545..00000000 --- a/users/leyla/default.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ lib, config, pkgs, ... }: -let - cfg = config.users.leyla; -in -{ - imports =[ - ./packages.nix - ]; - - options.users.leyla = { - isFullUser = lib.mkEnableOption "create usable leyla user"; - isThinUser = lib.mkEnableOption "create usable user but witohut user applications"; - hasPiperMouse = lib.mkEnableOption "install programs for managing piper supported mouses"; - hasOpenRGBHardware = lib.mkEnableOption "install programs for managing openRGB supported hardware"; - hasViaKeyboard = lib.mkEnableOption "install programs for managing via supported keyboards"; - hasGPU = lib.mkEnableOption "installs gpu intensive programs"; - }; - - config = { - sops.secrets = lib.mkIf (cfg.isFullUser || cfg.isThinUser) { - "passwords/leyla" = { - neededForUsers = true; - # sopsFile = ../secrets.yaml; - }; - }; - - users.groups.leyla = {}; - - users.users.leyla = lib.mkMerge [ - { - uid = 1000; - description = "Leyla"; - group = "leyla"; - } - - ( - if (cfg.isFullUser || cfg.isThinUser) then { - isNormalUser = true; - extraGroups = lib.mkMerge [ - ["networkmanager" "wheel" "users"] - ( - lib.mkIf (!cfg.isThinUser) [ "adbusers" ] - ) - ]; - - hashedPasswordFile = config.sops.secrets."passwords/leyla".path; - - openssh = { - authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJHeItmt8TRW43uNcOC+eIurYC7Eunc0V3LGocQqLaYj leyla@horizon" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKBiZkg1c2aaNHiieBX4cEziqvJVj9pcDfzUrKU/mO0I leyla@twilight" - ]; - }; - } else { - isSystemUser = true; - } - ) - ]; - - # TODO: this should reference the home directory from the user config - services.openssh.hostKeys = [ - { - comment = "leyla@" + config.networking.hostName; - path = "/home/leyla/.ssh/leyla_" + config.networking.hostName + "_ed25519"; - rounds = 100; - type = "ed25519"; - } - ]; - - home-manager.users.leyla = lib.mkIf (cfg.isFullUser || cfg.isThinUser) (import ./home.nix); - }; -} \ No newline at end of file diff --git a/users/leyla/home.nix b/users/leyla/home.nix deleted file mode 100644 index 40a6926b..00000000 --- a/users/leyla/home.nix +++ /dev/null @@ -1,125 +0,0 @@ -{ config, pkgs, ... }: - -{ - # Home Manager needs a bit of information about you and the paths it should - # manage. - home = { - username = "leyla"; - homeDirectory = "/home/leyla"; - - # This value determines the Home Manager release that your configuration is - # compatible with. This helps avoid breakage when a new Home Manager release - # introduces backwards incompatible changes. - # - # You should not change this value, even if you update Home Manager. If you do - # want to update the value, then make sure to first check the Home Manager - # release notes. - stateVersion = "23.11"; # Please read the comment before changing. - - # The home.packages option allows you to install Nix packages into your - # environment. - packages = [ - # # Adds the 'hello' command to your environment. It prints a friendly - # # "Hello, world!" when run. - # pkgs.hello - - # # It is sometimes useful to fine-tune packages, for example, by applying - # # overrides. You can do that directly here, just don't forget the - # # parentheses. Maybe you want to install Nerd Fonts with a limited number of - # # fonts? - # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; }) - - # # You can also create simple shell scripts directly inside your - # # configuration. For example, this adds a command 'my-hello' to your - # # environment: - # (pkgs.writeShellScriptBin "my-hello" '' - # echo "Hello, ${config.home.username}!" - # '') - ]; - - # Home Manager is pretty good at managing dotfiles. The primary way to manage - # plain files is through 'home.file'. - file = { - # # Building this configuration will create a copy of 'dotfiles/screenrc' in - # # the Nix store. Activating the configuration will then make '~/.screenrc' a - # # symlink to the Nix store copy. - # ".screenrc".source = dotfiles/screenrc; - - # # You can also set the file content immediately. - # ".gradle/gradle.properties".text = '' - # org.gradle.console=verbose - # org.gradle.daemon.idletimeout=3600000 - # ''; - }; - - # Home Manager can also manage your environment variables through - # 'home.sessionVariables'. If you don't want to manage your shell through Home - # Manager then you have to manually source 'hm-session-vars.sh' located at - # either - # - # ~/.nix-profile/etc/profile.d/hm-session-vars.sh - # - # or - # - # ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh - # - # or - # - # /etc/profiles/per-user/leyla/etc/profile.d/hm-session-vars.sh - # - sessionVariables = { - # EDITOR = "emacs"; - }; - }; - - programs = { - # Let Home Manager install and manage itself. - home-manager.enable = true; - git = { - enable = true; - userName = "Leyla Becker"; - userEmail = "git@jan-leila.com"; - extraConfig.init.defaultBranch = "main"; - }; - }; - - dconf = { - enable = true; - settings = { - "org/gnome/desktop/interface".color-scheme = "prefer-dark"; - - "org/gnome/shell" = { - disable-user-extensions = false; # enables user extensions - enabled-extensions = [ - # Put UUIDs of extensions that you want to enable here. - # If the extension you want to enable is packaged in nixpkgs, - # you can easily get its UUID by accessing its extensionUuid - # field (look at the following example). - pkgs.gnomeExtensions.dash-to-dock.extensionUuid - - # Alternatively, you can manually pass UUID as a string. - # "dash-to-dock@micxgx.gmail.com" - ]; - }; - - "org/gnome/shell/extensions/dash-to-dock" = { - "dock-position" = "LEFT"; - "intellihide-mode" = "ALL_WINDOWS"; - "show-trash" = false; - "require-pressure-to-show" = true; - "show-mounts" = false; - }; - - "org/gnome/settings-daemon/plugins/media-keys" = { - custom-keybindings = [ - "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/" - ]; - }; - "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0" = { - binding = "t"; - command = "kgx"; - name = "Open Terminal"; - }; - }; - }; -} diff --git a/users/leyla/packages.nix b/users/leyla/packages.nix deleted file mode 100644 index 223f5685..00000000 --- a/users/leyla/packages.nix +++ /dev/null @@ -1,129 +0,0 @@ -{ lib, config, pkgs, inputs, ... }: -let - cfg = config.users.leyla; -in -{ - imports = [ - ../../overlays/intellij.nix - ../../overlays/vscodium.nix - ]; - - nixpkgs = { - overlays = [ - inputs.nix-vscode-extensions.overlays.default - ]; - }; - - programs = { - bash.shellAliases = lib.mkIf cfg.isFullUser { - code = "codium"; - }; - - steam = lib.mkIf cfg.isFullUser { - enable = true; - remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play - dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server - }; - - noisetorch.enable = cfg.isFullUser; - - adb.enable = cfg.isFullUser; - }; - - users.users.leyla.packages = lib.mkIf (cfg.isFullUser || cfg.isThinUser) ( - lib.mkMerge [ - ( - with pkgs; [ - # comand line tools - yt-dlp - ffmpeg - imagemagick - ] - ) - ( - lib.mkIf (!cfg.isThinUser) ( - with pkgs; [ - #foss platforms - signal-desktop - bitwarden - firefox - ungoogled-chromium - libreoffice - inkscape - gimp - krita - freecad - # cura - kicad-small - makemkv - transmission_4-gtk - onionshare - easytag - # rhythmbox - (lib.mkIf cfg.hasGPU obs-studio) - # wireshark - # rpi-imager - # fritzing - - # proprietary platforms - discord - obsidian - steam - (lib.mkIf cfg.hasGPU davinci-resolve) - - # development tools - (vscode-with-extensions.override { - vscode = vscodium; - vscodeExtensions = with open-vsx; [ - # vs code feel extensions - ms-vscode.atom-keybindings - akamud.vscode-theme-onedark - streetsidesoftware.code-spell-checker - streetsidesoftware.code-spell-checker-german - streetsidesoftware.code-spell-checker-italian - jeanp413.open-remote-ssh - - # nix extensions - pinage404.nix-extension-pack - jnoortheen.nix-ide - - # html extensions - formulahendry.auto-rename-tag - ms-vscode.live-server - - # js extensions - dsznajder.es7-react-js-snippets - dbaeumer.vscode-eslint - standard.vscode-standard - firsttris.vscode-jest-runner - stylelint.vscode-stylelint - tauri-apps.tauri-vscode - - # misc extensions - bungcip.better-toml - ] ++ (with vscode-marketplace; [ - # js extensions - karyfoundation.nearley - ]); - }) - androidStudioPackages.canary - jetbrains.idea-community - dbeaver-bin - bruno - - # system tools - protonvpn-gui - openvpn - nextcloud-client - noisetorch - - # hardware managment tools - (lib.mkIf cfg.hasPiperMouse piper) - (lib.mkIf cfg.hasOpenRGBHardware openrgb) - (lib.mkIf cfg.hasViaKeyboard via) - ] - ) - ) - ] - ); -} \ No newline at end of file diff --git a/util/default.nix b/util/default.nix index a4fab1ea..fb2f83d1 100644 --- a/util/default.nix +++ b/util/default.nix @@ -1,8 +1,128 @@ -_: -{ - # mkUnless = condition: then: (mkIf (!condition) then); - # mkIfElse = condition: then: else: lib.mkMerge [ - # (mkIf condition then) - # (mkUnless condition else) - # ]; -} \ No newline at end of file +{inputs}: let + util = (import ./default.nix) {inherit inputs;}; + outputs = inputs.self.outputs; + + lib = inputs.lib; + nixpkgs = inputs.nixpkgs; + home-manager = inputs.home-manager; + nix-darwin = inputs.nix-darwin; + sops-nix = inputs.sops-nix; + nix-syncthing = inputs.nix-syncthing; + disko = inputs.disko; + impermanence = inputs.impermanence; + lix-module = inputs.lix-module; + + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + forEachSystem = nixpkgs.lib.genAttrs systems; + pkgsFor = system: nixpkgs.legacyPackages.${system}; + + common-modules = [ + ../modules/common-modules + ]; + + home-manager-modules = + common-modules + ++ [ + sops-nix.homeManagerModules.sops + impermanence.homeManagerModules.impermanence + ../modules/home-manager-modules + ]; + + home-manager-config = nixpkgs: { + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "backup"; + home-manager.extraSpecialArgs = { + inherit inputs outputs util; + }; + home-manager.users = import ../configurations/home-manager (nixpkgs + // { + osConfig = nixpkgs.config; + }); + home-manager.sharedModules = home-manager-modules; + }; + + system-modules = + common-modules + ++ [ + home-manager-config + ../modules/system-modules + ]; +in { + forEachPkgs = lambda: forEachSystem (system: lambda system (pkgsFor system)); + + mkUnless = condition: yes: (lib.mkIf (!condition) yes); + mkIfElse = condition: yes: no: + lib.mkMerge [ + (lib.mkIf condition yes) + (lib.mkUnless condition no) + ]; + + mkNixosInstaller = host: userKeys: + nixpkgs.lib.nixosSystem { + modules = [ + { + # TODO: authorized keys for all users and hosts + } + ../configurations/nixos/${host} + ]; + }; + + mkNixosSystem = host: + nixpkgs.lib.nixosSystem { + specialArgs = {inherit inputs outputs util;}; + modules = + system-modules + ++ [ + sops-nix.nixosModules.sops + nix-syncthing.nixosModules.syncthing + impermanence.nixosModules.impermanence + home-manager.nixosModules.home-manager + disko.nixosModules.disko + lix-module.nixosModules.default + ../modules/nixos-modules + ../configurations/nixos/${host} + ]; + }; + + mkDarwinSystem = host: + nix-darwin.lib.darwinSystem { + specialArgs = {inherit inputs outputs util;}; + modules = + system-modules + ++ [ + sops-nix.darwinModules.sops + home-manager.darwinModules.home-manager + ../modules/darwin-modules + ../configurations/darwin/${host} + ]; + }; + + mkHome = { + user, + host, + system, + osConfig, + }: + home-manager.lib.homeManagerConfiguration { + pkgs = pkgsFor system; + extraSpecialArgs = { + inherit inputs util outputs osConfig; + }; + modules = + home-manager-modules + ++ [ + ../configurations/home-manager/${user} + ]; + }; + + syncthingConfiguration = nix-syncthing.lib.syncthingConfiguration { + modules = [ + (import ../configurations/syncthing) + ]; + }; +}