From d022103d822fff51db2389d5f9743947be72a6c6 Mon Sep 17 00:00:00 2001 From: botalex Date: Fri, 15 Aug 2025 21:53:02 +0200 Subject: [PATCH] I guess i forgot to add networking --- aliases.nix | 29 ++++++++ configuration.nix | 93 ++++++------------------- docker.nix | 5 ++ flake.lock | 21 ++++++ flake.nix | 8 +++ hardware-configuration.nix | 26 +++++++ modules/de.nix | 31 +++++++++ modules/displayOff.nix | 44 ++++++++++++ modules/drivers/nvidia.nix | 66 ++++++++++++++++++ modules/fishShell.nix | 6 ++ modules/lowGPU.nix | 44 ++++++++++++ modules/nodejs.nix | 8 +++ modules/nvim.nix | 17 +++-- modules/python.nix | 13 ++++ networking/caddy.nix | 135 ++++++++++++++++++++++++++++++++++++ networking/networkSetup.nix | 8 +++ programs.nix | 18 +++++ users.nix | 33 +++++++++ 18 files changed, 528 insertions(+), 77 deletions(-) create mode 100644 aliases.nix create mode 100644 docker.nix create mode 100644 modules/de.nix create mode 100644 modules/displayOff.nix create mode 100644 modules/drivers/nvidia.nix create mode 100644 modules/fishShell.nix create mode 100644 modules/lowGPU.nix create mode 100644 modules/nodejs.nix create mode 100644 modules/python.nix create mode 100644 networking/caddy.nix create mode 100644 networking/networkSetup.nix create mode 100644 programs.nix create mode 100644 users.nix diff --git a/aliases.nix b/aliases.nix new file mode 100644 index 0000000..88141e2 --- /dev/null +++ b/aliases.nix @@ -0,0 +1,29 @@ +{pkgs, ...} : { + programs.fish = { + enable = true; + + shellAliases = { + nrb = "sudo nixos-rebuild switch --flake /etc/nixos --impure"; + ni = "nvim /etc/nixos/configuration.nix"; + bat="upower -i /org/freedesktop/UPower/devices/battery_BAT0| grep -E 'state|percentage'"; + gpu="nvidia-smi -q | grep -i 'draw.*W'"; + wifi="sudo nmtui"; + all="sudo chmod -R a+rwx ./*"; + ng="cd /etc/nginx/ && sudo nvim ."; + copy="xclip -sel clip"; + pubkey="cat ~/.ssh/id_ed25519.pub | copy"; + up="docker compose up -d"; + down="docker compose down"; + }; + + interactiveShellInit = '' + function enter + if test (count $argv) -lt 1 + echo "usage: enter " + return 1 + end + docker exec -it $argv[1] sh + end + ''; + }; + } diff --git a/configuration.nix b/configuration.nix index 6d4a5d8..c6bc194 100755 --- a/configuration.nix +++ b/configuration.nix @@ -14,11 +14,27 @@ imports = [ # Include the results of the hardware scan. ./hardware-configuration.nix + ./aliases.nix + ./docker.nix + ./modules/drivers/nvidia.nix + + ./modules/python.nix + ./programs.nix + ./modules/nodejs.nix + + ./modules/fishShell.nix + + ./users.nix + ./networking/caddy.nix + + ./modules/de.nix + # ./modules/displayOff.nix ]; # Bootloader. boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; + boot.loader.timeout = 2; networking.hostName = "nixos"; # Define your hostname. # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. @@ -72,9 +88,6 @@ enable = true; viAlias = true; vimAlias = true; - # extraPackages = with pkgs ; [ - # ripgrep fd fzf git unzip gcc - # ]; }; home-manager = { @@ -84,17 +97,11 @@ }; }; - # environment.etc."/nvim".source = builtins.fetchGit { - # url = "https://github.com/MagicBOTAlex/NVimConfigs.git"; - # ref = "master"; - # }; - - # Enable the X11 windowing system. - services.xserver.enable = true; - - # Enable the XFCE Desktop Environment. - services.xserver.displayManager.lightdm.enable = true; - services.xserver.desktopManager.xfce.enable = true; + # Root uses the exact same module + home-manager.users.root = { pkgs, ... }: { + home.stateVersion = "24.05"; + imports = [ ./modules/nvim.nix ]; + }; # Configure keymap in X11 services.xserver.xkb = { @@ -108,67 +115,9 @@ # Enable CUPS to print documents. services.printing.enable = true; - # Enable sound with pipewire. - services.pulseaudio.enable = false; - security.rtkit.enable = true; - services.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 touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Define a user account. Don't forget to set a password with ‘passwd’. - users.users.botserver = { - isNormalUser = true; - description = "botserver"; - extraGroups = [ - "networkmanager" - "wheel" - ]; - packages = with pkgs; [ - # thunderbird - ]; - - openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAhiPhFbCi64NduuV794omgS8mctBLXtqxbaEJyUo6lg botalex@DESKTOPSKTOP-ENDVV0V" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFhTExbc9m4dCK6676wGiA8zPjE0l/9Fz2yf0IKvUvg snorre@archlinux" - ]; - }; - - # Enable automatic login for the user. - services.displayManager.autoLogin.enable = true; - services.displayManager.autoLogin.user = "botserver"; - - # Install firefox. - programs.firefox.enable = true; - # Allow unfree packages nixpkgs.config.allowUnfree = true; - # List packages installed in system profile. To search, run: - # $ nix search wget - # environment.systemPackages = with pkgs; [ - # vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. - # wget - # ]; - environment.systemPackages = with pkgs; [ - neovim - wget - iproute2 - curl - fastfetch - ]; - programs.git = { enable = true; config = { diff --git a/docker.nix b/docker.nix new file mode 100644 index 0000000..0774ced --- /dev/null +++ b/docker.nix @@ -0,0 +1,5 @@ +{pkgs, ...} : { + virtualisation.docker.enable = true; + + hardware.nvidia-container-toolkit.enable = true; +} diff --git a/flake.lock b/flake.lock index b288a71..9823463 100755 --- a/flake.lock +++ b/flake.lock @@ -20,6 +20,26 @@ "type": "github" } }, + "nix-index-database": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1754800038, + "narHash": "sha256-UbLO8/0pVBXLJuyRizYOJigtzQAj8Z2bTnbKSec/wN0=", + "owner": "nix-community", + "repo": "nix-index-database", + "rev": "b65f8d80656f9fcbd1fecc4b7f0730f468333142", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-index-database", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1754725699, @@ -39,6 +59,7 @@ "root": { "inputs": { "home-manager": "home-manager", + "nix-index-database": "nix-index-database", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index b5833bc..6fcd18e 100755 --- a/flake.nix +++ b/flake.nix @@ -9,6 +9,10 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + nix-index-database = { + url = "github:nix-community/nix-index-database"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, nixpkgs, ... }@inputs: @@ -23,6 +27,10 @@ # home manager part 2 inputs.home-manager.nixosModules.default + + inputs.nix-index-database.nixosModules.nix-index + + { programs.nix-index-database.comma.enable = true; } ]; }; }; diff --git a/hardware-configuration.nix b/hardware-configuration.nix index 7c65a5d..26b7910 100755 --- a/hardware-configuration.nix +++ b/hardware-configuration.nix @@ -40,6 +40,32 @@ ]; }; + + + + + + fileSystems."/mnt/stolenFromSister" = { + device = "/dev/disk/by-uuid/8f2c41c0-84bb-40ee-a3f8-b1bbd378d5d7"; + fsType = "ext4"; # or "btrfs", "xfs", etc. + options = [ "nofail" "x-systemd.automount"]; # don't block boot if the disk is missing + }; + + fileSystems."/mnt/OtherStolenDriveFromSister" = { + device = "/dev/disk/by-uuid/fc16759c-24fc-46d6-99fe-865068605f46"; + fsType = "ext4"; # or "btrfs", "xfs", etc. + options = [ "nofail" "x-systemd.automount"]; # don't block boot if the disk is missing + }; + + systemd.tmpfiles.rules = [ + "d /mnt/stolenFromSister/jelly/ 0755 starr starr -" + "d /mnt/OtherStolenDriveFromSister/downloads/ 0755 starr starr -" + ]; + + + + + swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking diff --git a/modules/de.nix b/modules/de.nix new file mode 100644 index 0000000..5e636c3 --- /dev/null +++ b/modules/de.nix @@ -0,0 +1,31 @@ +{pkgs, ...}:{ + # Enable the X11 windowing system. + services.xserver.enable = true; + + # Enable the XFCE Desktop Environment. + services.xserver.displayManager.lightdm.enable = true; + services.xserver.desktopManager.xfce.enable = true; + + # Enable touchpad support (enabled default in most desktopManager). + services.xserver.libinput.enable = true; + + # Enable automatic login for the user. + services.displayManager.autoLogin.enable = true; + services.displayManager.autoLogin.user = "botserver"; + + # Enable sound with pipewire. + services.pulseaudio.enable = false; + security.rtkit.enable = true; + services.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; + }; + } diff --git a/modules/displayOff.nix b/modules/displayOff.nix new file mode 100644 index 0000000..ed4f0d4 --- /dev/null +++ b/modules/displayOff.nix @@ -0,0 +1,44 @@ +{ config, pkgs, ... }: + +{ + systemd.services.blank-tty1 = { + description = "Blank the active console (defaults to tty1)"; + wants = [ "getty@tty1.service" ]; + after = [ "getty@tty1.service" ]; + unitConfig.ConditionPathExists = "/dev/tty1"; + + # Put `setterm` in PATH + path = [ pkgs.kbd ]; + + serviceConfig = { + Type = "oneshot"; + }; + + # Do exactly what worked for you, with a small fallback + script = '' + set -e + + # Prefer the active VT if available; otherwise use tty1 + TTY="$(cat /sys/class/tty/tty0/active 2>/dev/null || echo tty1)" + + # Try setterm on that TTY (same redirections as your manual command) + if ! setterm --term linux --blank force /dev/"$TTY" 2>/dev/null; then + # Fallback: ask the framebuffer to blank (driver-dependent) + for fb in /sys/class/graphics/fb*/blank; do + [ -w "$fb" ] && echo 1 > "$fb" && break + done + fi + ''; + }; + + systemd.timers.blank-tty1 = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnActiveSec = "30s"; # 30s after the timer is activated at boot + AccuracySec = "1s"; + Persistent = true; + Unit = "blank-tty1.service"; + }; + }; +} + diff --git a/modules/drivers/nvidia.nix b/modules/drivers/nvidia.nix new file mode 100644 index 0000000..c77892d --- /dev/null +++ b/modules/drivers/nvidia.nix @@ -0,0 +1,66 @@ +{ + config, + pkgs, + lib, + ... +}: { + nixpkgs.config.nvidia.acceptLicense = true; + services.xserver.videoDrivers = ["nvidia"]; + + boot.kernelParams = ["nvidia.NVreg_PreserveVideoMemoryAllocations=1"]; + + hardware.graphics = { + enable = true; + enable32Bit = true; + extraPackages = with pkgs; [ + nvidia-vaapi-driver + ]; + }; + + hardware.nvidia = { + package = config.boot.kernelPackages.nvidiaPackages.stable; + modesetting.enable = true; + open = false; + nvidiaSettings = true; + dynamicBoost.enable = false; + + powerManagement = { + enable = true; + finegrained = true; + }; + + prime = { + sync.enable = false; + intelBusId = "PCI:0:2:0"; + nvidiaBusId = "PCI:1:0:0"; + + offload = { + enable = true; + enableOffloadCmd = true; + }; + }; + }; + + specialisation = { + Battery.configuration = { + system.nixos.tags = ["Battery"]; + boot.extraModprobeConfig = '' + blacklist nouveau + options nouveau modeset=0 + ''; + + services.udev.extraRules = '' + # Remove NVIDIA USB xHCI Host Controller devices, if present + ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{power/control}="auto", ATTR{remove}="1" + # Remove NVIDIA USB Type-C UCSI devices, if present + ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{power/control}="auto", ATTR{remove}="1" + # Remove NVIDIA Audio devices, if present + ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{power/control}="auto", ATTR{remove}="1" + # Remove NVIDIA VGA/3D controller devices + ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x03[0-9]*", ATTR{power/control}="auto", ATTR{remove}="1" + ''; + boot.blacklistedKernelModules = ["nouveau" "nvidia" "nvidia_drm" "nvidia_modeset"]; + }; + }; +} + diff --git a/modules/fishShell.nix b/modules/fishShell.nix new file mode 100644 index 0000000..87518b5 --- /dev/null +++ b/modules/fishShell.nix @@ -0,0 +1,6 @@ +{pkgs, ...} : { + programs.fish.enable = true; + documentation.man.generateCaches = false; + + users.users."botserver".shell = pkgs.fish; +} diff --git a/modules/lowGPU.nix b/modules/lowGPU.nix new file mode 100644 index 0000000..ed4f0d4 --- /dev/null +++ b/modules/lowGPU.nix @@ -0,0 +1,44 @@ +{ config, pkgs, ... }: + +{ + systemd.services.blank-tty1 = { + description = "Blank the active console (defaults to tty1)"; + wants = [ "getty@tty1.service" ]; + after = [ "getty@tty1.service" ]; + unitConfig.ConditionPathExists = "/dev/tty1"; + + # Put `setterm` in PATH + path = [ pkgs.kbd ]; + + serviceConfig = { + Type = "oneshot"; + }; + + # Do exactly what worked for you, with a small fallback + script = '' + set -e + + # Prefer the active VT if available; otherwise use tty1 + TTY="$(cat /sys/class/tty/tty0/active 2>/dev/null || echo tty1)" + + # Try setterm on that TTY (same redirections as your manual command) + if ! setterm --term linux --blank force /dev/"$TTY" 2>/dev/null; then + # Fallback: ask the framebuffer to blank (driver-dependent) + for fb in /sys/class/graphics/fb*/blank; do + [ -w "$fb" ] && echo 1 > "$fb" && break + done + fi + ''; + }; + + systemd.timers.blank-tty1 = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnActiveSec = "30s"; # 30s after the timer is activated at boot + AccuracySec = "1s"; + Persistent = true; + Unit = "blank-tty1.service"; + }; + }; +} + diff --git a/modules/nodejs.nix b/modules/nodejs.nix new file mode 100644 index 0000000..0a10edb --- /dev/null +++ b/modules/nodejs.nix @@ -0,0 +1,8 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + nodejs_22 + nodePackages.live-server + nodePackages.serve + ]; +} diff --git a/modules/nvim.nix b/modules/nvim.nix index 1155b37..d07bcf0 100644 --- a/modules/nvim.nix +++ b/modules/nvim.nix @@ -97,10 +97,17 @@ in extraLuaPackages = ls: with ls; [ luarocks ]; }; - xdg.configFile."nvim".source = pkgs.fetchFromGitHub { - owner = "MagicBOTAlex"; - repo = "NVimConfigs"; - rev = "2927ce8e62e47b0b542ae18623cb6dbee6c32add"; - hash = "sha256-f45NJYaiBLAQ9RmjzEPzI6LBrlj/vdA+ONkdRAzAIjQ="; + # Screw declarative here + xdg.configFile."nvim".source = builtins.fetchGit { + url = "https://github.com/MagicBOTAlex/NVimConfigs"; + ref = "master"; # change if the default branch is different + # submodules = true; # uncomment if needed }; + + # xdg.configFile."nvim".source = pkgs.fetchFromGitHub { + # owner = "MagicBOTAlex"; + # repo = "NVimConfigs"; + # rev = "2927ce8e62e47b0b542ae18623cb6dbee6c32add"; + # hash = "sha256-f45NJYaiBLAQ9RmjzEPzI6LBrlj/vdA+ONkdRAzAIjQ="; + # }; } diff --git a/modules/python.nix b/modules/python.nix new file mode 100644 index 0000000..51eb420 --- /dev/null +++ b/modules/python.nix @@ -0,0 +1,13 @@ +{pkgs, ...}: { + environment.systemPackages = with pkgs; [ +# ... + (python3.withPackages (python-pkgs: with python-pkgs; [ + pandas + requests + spotipy + python-dotenv + fastapi + uvicorn + ])) + ]; +} diff --git a/networking/caddy.nix b/networking/caddy.nix new file mode 100644 index 0000000..a0baf0d --- /dev/null +++ b/networking/caddy.nix @@ -0,0 +1,135 @@ +{pkgs, ... } : { + imports = [ + ./networkSetup.nix + ]; + + services.caddy.virtualHosts."immich.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:2283 + ''; + }; + + + services.caddy.virtualHosts."ha.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:8123 + ''; + }; + + services.caddy.virtualHosts."jelly.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:8096 + ''; + }; + + + services.caddy.virtualHosts."pocket.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:5500 + ''; + }; + + services.caddy.virtualHosts."seer.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:5055 + ''; + }; + + services.caddy.virtualHosts."development.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:5550 + ''; + }; + +services.caddy.virtualHosts."spotify.api.deprived.dev" = { + extraConfig = '' + encode zstd gzip + + # --- CORS: preflight (OPTIONS) --- + @preflight { + method OPTIONS + header Origin * + header Access-Control-Request-Method * + } + handle @preflight { + header { + Access-Control-Allow-Origin "{http.request.header.Origin}" + Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" + Access-Control-Allow-Headers "{http.request.header.Access-Control-Request-Headers}" + Access-Control-Allow-Credentials "true" + Access-Control-Max-Age "600" + Vary "Origin" + } + respond 204 + } + + # --- Auth: protect everything except OPTIONS --- + @protected { + not method OPTIONS + } + basicauth @protected { + alice $2a$14$GbqQnETcOz5fNEbS06Y0E.HxRIIgPKAK7OMijT1Bv63h3V6S/gwRG + } + + # --- Reverse proxy: strip upstream CORS so we don't end up with duplicates --- + reverse_proxy 127.0.0.1:6666 { + header_down -Access-Control-Allow-Origin + header_down -Access-Control-Allow-Methods + header_down -Access-Control-Allow-Headers + header_down -Access-Control-Allow-Credentials + header_down -Vary + } + + # --- CORS: set headers on actual responses (only when Origin is present) --- + @cors header Origin * + header @cors { + Access-Control-Allow-Origin "{http.request.header.Origin}" + Access-Control-Allow-Credentials "true" + # Optionally expose any headers your frontend needs to read: + # Access-Control-Expose-Headers "Content-Type, Content-Length, Date" + Vary "Origin" + } + ''; +}; + + +services.caddy.virtualHosts."spotify.playing.deprived.dev" = { + extraConfig = '' + encode zstd gzip + + @preflight method OPTIONS + handle @preflight { + header { + Access-Control-Allow-Origin "{http.request.header.Origin}" + Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" + Access-Control-Allow-Headers "{http.request.header.Access-Control-Request-Headers}" + Access-Control-Allow-Credentials "true" + Access-Control-Max-Age "600" + Vary "Origin" + } + respond 204 + } + + @protected not method OPTIONS + basicauth @protected { + alice $2a$14$GbqQnETcOz5fNEbS06Y0E.HxRIIgPKAK7OMijT1Bv63h3V6S/gwRG + } + + reverse_proxy 127.0.0.1:8800 + + header { + Access-Control-Allow-Origin "{http.request.header.Origin}" + Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" + Access-Control-Allow-Headers "{http.request.header.Access-Control-Request-Headers}" + Access-Control-Allow-Credentials "true" + Vary "Origin" + } + ''; +}; + + services.caddy.virtualHosts."lyrics.deprived.dev" = { + extraConfig = '' + reverse_proxy * 127.0.0.1:7444 + ''; + }; +} diff --git a/networking/networkSetup.nix b/networking/networkSetup.nix new file mode 100644 index 0000000..03eeedc --- /dev/null +++ b/networking/networkSetup.nix @@ -0,0 +1,8 @@ +{pkgs, ... } : { + services.caddy.enable = true; + + security.acme.acceptTerms = true; + security.acme.defaults.email = "zhen@deprived.dev"; + networking.firewall.enable = false; + +} diff --git a/programs.nix b/programs.nix new file mode 100644 index 0000000..537bf05 --- /dev/null +++ b/programs.nix @@ -0,0 +1,18 @@ +{pkgs,...} : { + environment.systemPackages = with pkgs; [ + neovim + wget + iproute2 + curl + fastfetch + tree + btop + pigz + ncdu + screen + nixfmt-tree + ffmpeg-full + ]; + + programs.starship.enable = true; +} diff --git a/users.nix b/users.nix new file mode 100644 index 0000000..8c2b830 --- /dev/null +++ b/users.nix @@ -0,0 +1,33 @@ +{pkgs, ...}: { + users.users.botserver = { + isNormalUser = true; + description = "botserver"; + extraGroups = [ + "networkmanager" + "wheel" + "docker" + "starr" + ]; + packages = with pkgs; [ + # thunderbird + ]; + + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAhiPhFbCi64NduuV794omgS8mctBLXtqxbaEJyUo6lg botalex@DESKTOPSKTOP-ENDVV0V" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFhTExbc9m4dCK6676wGiA8zPjE0l/9Fz2yf0IKvUvg snorre@archlinux" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxUPAsPkri0B+xkO3sCHJZfKgAbgPcepP8J4WW4yyLj u0_a167@localhost" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfQLOKUnOARUAs8X1EL1GRHoCQ0oMun0vzL7Z78yOsM nixos@nixos" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJw1ckvXz78ITeqANrWSkJl6PJo2AMA4myNrRMBAB7xW zhentao2004@gmail.com" + ]; + }; + + users.users.starr = { + isNormalUser = true; + description = "For jellyfin"; + extraGroups = [ + "starr" + ]; + }; + + users.groups."starr" = {}; +}