diff --git a/features/bootloader.nix b/features/bootloader.nix new file mode 100644 index 0000000..ae145e8 --- /dev/null +++ b/features/bootloader.nix @@ -0,0 +1,40 @@ +{ + nixos = + { config, lib, inputs, ... }: + let + cfg = config.features.bootloader; + in + { + imports = [ inputs.lanzaboote.nixosModules.lanzaboote ]; + + options.features.bootloader = { + enable = lib.mkEnableOption "bootloader"; + + mode = lib.mkOption { + type = lib.types.enum [ + "systemd-boot" + "lanzaboote" + ]; + default = "systemd-boot"; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + boot.loader.efi.canTouchEfiVariables = true; + } + + (lib.mkIf (cfg.mode == "systemd-boot") { + boot.loader.systemd-boot.enable = true; + }) + + (lib.mkIf (cfg.mode == "lanzaboote") { + boot.loader.systemd-boot.enable = lib.mkForce false; + boot.lanzaboote = { + enable = true; + pkiBundle = "/var/lib/sbctl"; + }; + }) + ]); + }; +} diff --git a/features/calibre.nix b/features/calibre.nix deleted file mode 100644 index e310fa7..0000000 --- a/features/calibre.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - nixos = - { pkgs, ... }: - { - environment.systemPackages = [ pkgs.calibre ]; - - # udev rules for kindle and mtp device access - # NOTE:(@janezicmatej) uses services.udev.packages instead of extraRules - # because extraRules writes to 99-local.rules which is too late for uaccess - # see https://github.com/NixOS/nixpkgs/issues/308681 - services.udev.packages = [ - pkgs.libmtp - (pkgs.writeTextFile { - name = "kindle-udev-rules"; - text = '' - ACTION!="remove", SUBSYSTEM=="usb", ATTRS{idVendor}=="1949", TAG+="uaccess" - ''; - destination = "/etc/udev/rules.d/70-kindle.rules"; - }) - ]; - }; -} diff --git a/features/claude.nix b/features/claude.nix index 6f5fd2a..23a5c7b 100644 --- a/features/claude.nix +++ b/features/claude.nix @@ -1,10 +1,21 @@ { - home = - { pkgs, ... }: + nixos = + { lib, ... }: { - home.packages = [ - pkgs.claude-code - pkgs.mcp-nixos - ]; + options.features.claude.enable = lib.mkEnableOption "claude"; + }; + + home = + { pkgs, lib, osConfig, ... }: + let + cfg = osConfig.features.claude; + in + { + config = lib.mkIf cfg.enable { + home.packages = [ + pkgs.claude-code + pkgs.mcp-nixos + ]; + }; }; } diff --git a/features/desktop-minimal.nix b/features/desktop-minimal.nix deleted file mode 100644 index 73ff718..0000000 --- a/features/desktop-minimal.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ - nixos = - { pkgs, inputs, ... }: - { - imports = [ inputs.stylix.nixosModules.stylix ]; - - # audio - services.pipewire = { - enable = true; - pulse.enable = true; - }; - - security.polkit.enable = true; - services.dbus.enable = true; - services.playerctld.enable = true; - - xdg.portal = { - enable = true; - xdgOpenUsePortal = true; - extraPortals = [ - pkgs.xdg-desktop-portal-wlr - pkgs.xdg-desktop-portal-gtk - ]; - }; - - fonts.packages = with pkgs; [ - font-awesome - nerd-fonts.jetbrains-mono - ]; - - # theming - stylix = { - enable = true; - polarity = "dark"; - image = "${inputs.assets}/wallpaper.png"; - base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-material-dark-medium.yaml"; - }; - }; - - home = - { inputs, ... }: - { - home.file.".assets".source = inputs.assets; - }; -} diff --git a/features/desktop.nix b/features/desktop.nix index f2e06af..3caf908 100644 --- a/features/desktop.nix +++ b/features/desktop.nix @@ -1,85 +1,152 @@ { nixos = - { pkgs, inputs, ... }: + { config, lib, pkgs, inputs, ... }: + let + cfg = config.features.desktop; + in { - imports = [ inputs.stylix.nixosModules.stylix ]; + options.features.desktop = { + enable = lib.mkEnableOption "desktop environment"; - # audio - services.pipewire = { - enable = true; - pulse.enable = true; + audio.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + bluetooth.enable = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + apps.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; + + theme = { + wallpaper = lib.mkOption { + type = lib.types.path; + default = "${inputs.assets}/wallpaper.png"; + }; + + scheme = lib.mkOption { + type = lib.types.str; + default = "gruvbox-material-dark-medium"; + }; + + polarity = lib.mkOption { + type = lib.types.enum [ + "dark" + "light" + ]; + default = "dark"; + }; + }; + + internalCA.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; }; - # bluetooth - hardware.bluetooth.enable = true; - services.blueman.enable = true; + config = lib.mkIf cfg.enable (lib.mkMerge [ + # base desktop + { + security.polkit.enable = true; + services.dbus.enable = true; + services.playerctld.enable = true; - security.polkit.enable = true; - services.dbus.enable = true; - services.playerctld.enable = true; + xdg.portal = { + enable = true; + xdgOpenUsePortal = true; + extraPortals = with pkgs; [ + xdg-desktop-portal-wlr + xdg-desktop-portal-gtk + ]; + }; - xdg.portal = { - enable = true; - xdgOpenUsePortal = true; - extraPortals = [ - pkgs.xdg-desktop-portal-wlr - pkgs.xdg-desktop-portal-gtk - ]; - }; + fonts.packages = with pkgs; [ + font-awesome + nerd-fonts.jetbrains-mono + ]; - fonts.packages = with pkgs; [ - font-awesome - nerd-fonts.jetbrains-mono - ]; + stylix = { + enable = true; + polarity = cfg.theme.polarity; + image = cfg.theme.wallpaper; + base16Scheme = "${pkgs.base16-schemes}/share/themes/${cfg.theme.scheme}.yaml"; + }; + } - # theming - stylix = { - enable = true; - polarity = "dark"; - image = "${inputs.assets}/wallpaper.png"; - base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-material-dark-medium.yaml"; - }; + # audio + (lib.mkIf cfg.audio.enable { + services.pipewire = { + enable = true; + pulse.enable = true; + }; + environment.systemPackages = with pkgs; [ + pavucontrol + easyeffects + ]; + }) - programs.thunderbird.enable = true; - programs._1password.enable = true; - programs._1password-gui.enable = true; + # bluetooth + (lib.mkIf cfg.bluetooth.enable { + hardware.bluetooth.enable = true; + services.blueman.enable = true; + }) - environment.systemPackages = with pkgs; [ - easyeffects - ghostty - google-chrome - zathura - pavucontrol - bolt-launcher - libnotify - bibata-cursors - vesktop - rocketchat-desktop - telegram-desktop - slack - jellyfin-media-player - cider-2 - mpv - ffmpeg - wf-recorder - wl-mirror - protonmail-bridge - ledger-live-desktop - ]; + # apps + (lib.mkIf cfg.apps.enable { + programs.thunderbird.enable = true; - # internal CA - security.pki.certificateFiles = [ - inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system}.ca-matheo-si - ]; + environment.systemPackages = with pkgs; [ + ghostty + google-chrome + zathura + calibre + bolt-launcher + libnotify + bibata-cursors + vesktop + rocketchat-desktop + telegram-desktop + slack + jellyfin-media-player + cider-2 + mpv + ffmpeg + wf-recorder + wl-mirror + protonmail-bridge + ledger-live-desktop + ]; - xdg.mime.defaultApplications = { - "application/pdf" = "org.pwmt.zathura.desktop"; - }; + xdg.mime.defaultApplications = { + "application/pdf" = "org.pwmt.zathura.desktop"; + }; + + # kindle udev rules for calibre + features.udev.kindle.enable = lib.mkDefault true; + }) + + # internal CA + (lib.mkIf cfg.internalCA.enable { + security.pki.certificateFiles = [ + inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system}.ca-matheo-si + ]; + }) + ]); }; home = - { inputs, ... }: + { lib, inputs, osConfig, ... }: + let + cfg = osConfig.features.desktop; + in { - home.file.".assets".source = inputs.assets; + config = lib.mkIf cfg.enable { + home.file.".assets".source = inputs.assets; + }; }; } diff --git a/features/dev.nix b/features/dev.nix index fe5dce1..98d423f 100644 --- a/features/dev.nix +++ b/features/dev.nix @@ -1,25 +1,29 @@ { + nixos = + { lib, ... }: + { + options.features.dev.enable = lib.mkEnableOption "development tools"; + }; + home = - { pkgs, inputs, ... }: + { pkgs, lib, inputs, osConfig, ... }: let + cfg = osConfig.features.dev; packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system}; in { - home.packages = [ - pkgs.git - packages.git-linearize - packages.ggman + config = lib.mkIf cfg.enable { + home.packages = [ + pkgs.python3 + pkgs.osc - pkgs.python3 - pkgs.osc - - pkgs.google-cloud-sdk - pkgs.google-cloud-sql-proxy - - packages.ahab - pkgs.just - pkgs.presenterm - ]; + pkgs.google-cloud-sdk + pkgs.google-cloud-sql-proxy + packages.ahab + pkgs.just + pkgs.presenterm + ]; + }; }; } diff --git a/features/direnv/default.nix b/features/direnv/default.nix index a50c083..06af9d3 100644 --- a/features/direnv/default.nix +++ b/features/direnv/default.nix @@ -1,26 +1,40 @@ { nixos = - { inputs, ... }: + { config, lib, inputs, ... }: + let + cfg = config.features.direnv; + in { - nix.registry.dev = { - from = { - type = "indirect"; - id = "dev"; - }; - to = { - type = "path"; - path = inputs.self.outPath; + options.features.direnv.enable = lib.mkEnableOption "direnv"; + + config = lib.mkIf cfg.enable { + nix.registry.dev = { + from = { + type = "indirect"; + id = "dev"; + }; + to = { + type = "path"; + path = inputs.self.outPath; + }; }; }; }; - home = _: { - programs.direnv = { - enable = true; - nix-direnv.enable = true; - config.global.hide_env_diff = true; - }; + home = + { lib, osConfig, ... }: + let + cfg = osConfig.features.direnv; + in + { + config = lib.mkIf cfg.enable { + programs.direnv = { + enable = true; + nix-direnv.enable = true; + config.global.hide_env_diff = true; + }; - xdg.configFile."direnv/lib/use_dev.sh".source = ./use_dev.sh; - }; + xdg.configFile."direnv/lib/use_dev.sh".source = ./use_dev.sh; + }; + }; } diff --git a/features/docker.nix b/features/docker.nix index 16440ee..fd7b7f3 100644 --- a/features/docker.nix +++ b/features/docker.nix @@ -1,12 +1,19 @@ { nixos = - { user, ... }: + { config, lib, user, ... }: + let + cfg = config.features.docker; + in { - virtualisation.docker = { - enable = true; - logDriver = "json-file"; - }; + options.features.docker.enable = lib.mkEnableOption "docker"; - users.users.${user}.extraGroups = [ "docker" ]; + config = lib.mkIf cfg.enable { + virtualisation.docker = { + enable = true; + logDriver = "json-file"; + }; + + users.users.${user}.extraGroups = [ "docker" ]; + }; }; } diff --git a/features/filedrop.nix b/features/filedrop.nix index 4cebf26..fe867b4 100644 --- a/features/filedrop.nix +++ b/features/filedrop.nix @@ -1,42 +1,56 @@ { nixos = - { config, userKeys, ... }: + { config, lib, userKeys, ... }: + let + cfg = config.features.filedrop; + in { - sops.secrets.filedrop-authorized-keys = { - sopsFile = ../secrets/floo.yaml; - mode = "0444"; + options.features.filedrop = { + enable = lib.mkEnableOption "filedrop sftp service"; + + sopsFile = lib.mkOption { + type = lib.types.path; + + }; }; - users.groups.filedrop = { - members = [ "matej" ]; + config = lib.mkIf cfg.enable { + sops.secrets.filedrop-authorized-keys = { + inherit (cfg) sopsFile; + mode = "0444"; + }; + + users.groups.filedrop = { + members = [ "matej" ]; + }; + + users.users.filedrop = { + isSystemUser = true; + group = "filedrop"; + home = "/home/filedrop"; + shell = "/run/current-system/sw/bin/nologin"; + openssh.authorizedKeys.keys = userKeys.sshAuthorizedKeys; + }; + + # chroot dir must be root-owned; incoming is writable by filedrop + systemd.tmpfiles.rules = [ + "d /home/filedrop 0755 root root -" + "d /home/filedrop/incoming 2775 filedrop filedrop -" + "a+ /home/filedrop/incoming - - - - group:filedrop:rwx" + "a+ /home/filedrop/incoming - - - - default:group:filedrop:rwx" + "a+ /home/filedrop/incoming - - - - default:mask::rwx" + "L /home/matej/filedrop - - - - /home/filedrop/incoming" + ]; + + # relaxed umask so default acl takes full effect + services.openssh.extraConfig = '' + Match User filedrop + ForceCommand internal-sftp -u 0002 + ChrootDirectory /home/filedrop + AuthorizedKeysFile /etc/ssh/authorized_keys.d/filedrop %h/.ssh/authorized_keys ${config.sops.secrets.filedrop-authorized-keys.path} + AllowTcpForwarding no + X11Forwarding no + ''; }; - - users.users.filedrop = { - isSystemUser = true; - group = "filedrop"; - home = "/home/filedrop"; - shell = "/run/current-system/sw/bin/nologin"; - openssh.authorizedKeys.keys = userKeys.sshAuthorizedKeys; - }; - - # chroot dir must be root-owned; incoming is writable by filedrop - systemd.tmpfiles.rules = [ - "d /home/filedrop 0755 root root -" - "d /home/filedrop/incoming 2775 filedrop filedrop -" - "a+ /home/filedrop/incoming - - - - group:filedrop:rwx" - "a+ /home/filedrop/incoming - - - - default:group:filedrop:rwx" - "a+ /home/filedrop/incoming - - - - default:mask::rwx" - "L /home/matej/filedrop - - - - /home/filedrop/incoming" - ]; - - # relaxed umask so default acl takes full effect - services.openssh.extraConfig = '' - Match User filedrop - ForceCommand internal-sftp -u 0002 - ChrootDirectory /home/filedrop - AuthorizedKeysFile /etc/ssh/authorized_keys.d/filedrop %h/.ssh/authorized_keys ${config.sops.secrets.filedrop-authorized-keys.path} - AllowTcpForwarding no - X11Forwarding no - ''; }; } diff --git a/features/gaming.nix b/features/gaming.nix index feaf7e7..b4e07c3 100644 --- a/features/gaming.nix +++ b/features/gaming.nix @@ -1,14 +1,21 @@ { nixos = - { pkgs, ... }: + { config, lib, pkgs, ... }: + let + cfg = config.features.gaming; + in { - programs.steam = { - enable = true; - remotePlay.openFirewall = true; - dedicatedServer.openFirewall = true; - localNetworkGameTransfers.openFirewall = true; - }; + options.features.gaming.enable = lib.mkEnableOption "gaming"; - environment.systemPackages = [ pkgs.prismlauncher ]; + config = lib.mkIf cfg.enable { + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + dedicatedServer.openFirewall = true; + localNetworkGameTransfers.openFirewall = true; + }; + + environment.systemPackages = [ pkgs.prismlauncher ]; + }; }; } diff --git a/features/git.nix b/features/git.nix new file mode 100644 index 0000000..764eff3 --- /dev/null +++ b/features/git.nix @@ -0,0 +1,23 @@ +{ + nixos = + { lib, ... }: + { + options.features.git.enable = lib.mkEnableOption "git"; + }; + + home = + { pkgs, lib, inputs, osConfig, ... }: + let + cfg = osConfig.features.git; + packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system}; + in + { + config = lib.mkIf cfg.enable { + home.packages = [ + pkgs.git + packages.git-linearize + packages.ggman + ]; + }; + }; +} diff --git a/features/gnupg.nix b/features/gnupg.nix index e39f156..8647bc4 100644 --- a/features/gnupg.nix +++ b/features/gnupg.nix @@ -1,9 +1,36 @@ { - nixos = _: { - programs.gnupg.agent = { - enable = true; - enableSSHSupport = true; - enableExtraSocket = true; + nixos = + { config, lib, pkgs, ... }: + let + cfg = config.features.gnupg; + in + { + options.features.gnupg = { + enable = lib.mkEnableOption "gnupg"; + + yubikey.enable = lib.mkOption { + type = lib.types.bool; + default = false; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + enableExtraSocket = true; + }; + } + + (lib.mkIf cfg.yubikey.enable { + environment.systemPackages = with pkgs; [ + yubikey-personalization + yubikey-manager + ]; + + services.pcscd.enable = true; + }) + ]); }; - }; } diff --git a/features/greeter.nix b/features/greeter.nix deleted file mode 100644 index 54bddb6..0000000 --- a/features/greeter.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - nixos = - { lib, inputs, ... }: - { - programs.regreet = { - enable = true; - # single output to avoid stretching across monitors - cageArgs = [ - "-s" - "-m" - "last" - ]; - font = { - name = lib.mkForce "JetBrainsMono Nerd Font"; - size = lib.mkForce 14; - }; - settings = { - background = { - path = lib.mkForce "${inputs.assets}/wallpaper.png"; - fit = lib.mkForce "Cover"; - }; - GTK = { - application_prefer_dark_theme = lib.mkForce true; - }; - }; - }; - }; -} diff --git a/features/harmonia/default.nix b/features/harmonia/default.nix index 3d842f4..a29a0b5 100644 --- a/features/harmonia/default.nix +++ b/features/harmonia/default.nix @@ -1,12 +1,14 @@ { nixos = { - pkgs, config, + lib, + pkgs, inputs, ... }: let + cfg = config.features.harmonia; hosts = [ "fw16" "tower" @@ -17,34 +19,38 @@ flakeRef = inputs.self.outPath; in { - services.harmonia.cache = { - enable = true; - signKeyPaths = [ config.sops.secrets.nix-signing-key.path ]; - }; + options.features.harmonia.enable = lib.mkEnableOption "harmonia"; - networking.firewall.interfaces."tailscale0".allowedTCPPorts = [ 5000 ]; - - systemd.services.cache-builder = { - description = "Build all host closures for binary cache"; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.bash}/bin/bash ${./cache-builder.sh}"; + config = lib.mkIf cfg.enable { + services.harmonia.cache = { + enable = true; + signKeyPaths = [ config.sops.secrets.nix-signing-key.path ]; }; - environment = { - FLAKE_REF = flakeRef; - HOSTS = builtins.concatStringsSep " " hosts; - GC_ROOT_DIR = "/nix/var/nix/gcroots/cache-builder"; - }; - path = [ config.nix.package ]; - }; - systemd.timers.cache-builder = { - description = "Periodically build all host closures"; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnUnitActiveSec = "15min"; - OnBootSec = "5min"; - Persistent = true; + networking.firewall.interfaces."tailscale0".allowedTCPPorts = [ 5000 ]; + + systemd.services.cache-builder = { + description = "Build all host closures for binary cache"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.bash}/bin/bash ${./cache-builder.sh}"; + }; + environment = { + FLAKE_REF = flakeRef; + HOSTS = builtins.concatStringsSep " " hosts; + GC_ROOT_DIR = "/nix/var/nix/gcroots/cache-builder"; + }; + path = [ config.nix.package ]; + }; + + systemd.timers.cache-builder = { + description = "Periodically build all host closures"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnUnitActiveSec = "15min"; + OnBootSec = "5min"; + Persistent = true; + }; }; }; }; diff --git a/features/initrd-ssh.nix b/features/initrd-ssh.nix index 23e7056..10b65a0 100644 --- a/features/initrd-ssh.nix +++ b/features/initrd-ssh.nix @@ -2,6 +2,7 @@ nixos = { lib, config, ... }: let + cfg = config.features.initrd-ssh; keyDir = "/etc/secrets/initrd"; mkIpString = @@ -15,44 +16,45 @@ "${address}::${gateway}:${netmask}::${interface}:none"; in { - options = { - initrd-ssh = { - ip = { - enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)"; + options.features.initrd-ssh = { + enable = lib.mkEnableOption "initrd ssh"; - address = lib.mkOption { - type = lib.types.str; - }; + ip = { + enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)"; - gateway = lib.mkOption { - type = lib.types.str; - }; - - netmask = lib.mkOption { - type = lib.types.str; - default = "255.255.255.0"; - }; - - interface = lib.mkOption { - type = lib.types.str; - }; + address = lib.mkOption { + type = lib.types.str; }; - authorizedKeys = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ ]; + gateway = lib.mkOption { + type = lib.types.str; }; - networkModule = lib.mkOption { + netmask = lib.mkOption { + type = lib.types.str; + default = "255.255.255.0"; + }; + + interface = lib.mkOption { type = lib.types.str; }; }; + + authorizedKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + + networkModule = lib.mkOption { + type = lib.types.str; + }; }; - config = { - boot.initrd.kernelModules = [ config.initrd-ssh.networkModule ]; - boot.kernelParams = lib.mkIf config.initrd-ssh.ip.enable [ - "ip=${mkIpString config.initrd-ssh.ip}" + config = lib.mkIf cfg.enable { + boot.initrd.availableKernelModules = [ cfg.networkModule ]; + boot.initrd.kernelModules = [ cfg.networkModule ]; + boot.kernelParams = lib.mkIf cfg.ip.enable [ + "ip=${mkIpString cfg.ip}" ]; boot.initrd.network = { @@ -64,7 +66,7 @@ "${keyDir}/ssh_host_rsa_key" "${keyDir}/ssh_host_ed25519_key" ]; - inherit (config.initrd-ssh) authorizedKeys; + inherit (cfg) authorizedKeys; }; postCommands = '' echo 'cryptsetup-askpass' >> /root/.profile diff --git a/features/localisation.nix b/features/localisation.nix index df07cdc..ec5f649 100644 --- a/features/localisation.nix +++ b/features/localisation.nix @@ -1,25 +1,30 @@ { nixos = { lib, config, ... }: + let + cfg = config.features.localisation; + in { - options = { - localisation = { - timeZone = lib.mkOption { - type = lib.types.str; - }; + options.features.localisation = { + enable = lib.mkEnableOption "localisation"; - defaultLocale = lib.mkOption { - type = lib.types.str; - }; + timeZone = lib.mkOption { + type = lib.types.str; + default = "Europe/Ljubljana"; + }; + + defaultLocale = lib.mkOption { + type = lib.types.str; + default = "en_US.UTF-8"; }; }; - config = { - time.timeZone = config.localisation.timeZone; - i18n.defaultLocale = config.localisation.defaultLocale; + config = lib.mkIf cfg.enable { + time.timeZone = cfg.timeZone; + i18n.defaultLocale = cfg.defaultLocale; # NOTE:(@janezicmatej) some apps (e.g. java) need TZ env var explicitly - environment.variables.TZ = config.localisation.timeZone; + environment.variables.TZ = cfg.timeZone; }; }; } diff --git a/features/neovim.nix b/features/neovim.nix index fb72a39..a348e2f 100644 --- a/features/neovim.nix +++ b/features/neovim.nix @@ -1,4 +1,17 @@ { + nixos = + { lib, ... }: + { + options.features.neovim = { + enable = lib.mkEnableOption "neovim"; + + dotfiles = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + }; + }; + }; + home = { config, @@ -6,24 +19,21 @@ lib, pkgs, inputs, + osConfig, ... }: + let + cfg = osConfig.features.neovim; + in { - options = { - neovim.dotfiles = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - }; - }; - - config = lib.mkMerge [ + config = lib.mkIf cfg.enable (lib.mkMerge [ (lib.optionalAttrs (options ? stylix) { - # disable stylix neovim target when stylix is present (loaded by desktop feature) + # disable stylix neovim target when stylix is present stylix.targets.neovim.enable = false; }) { - xdg.configFile."nvim" = lib.mkIf (config.neovim.dotfiles != null) { - source = config.neovim.dotfiles; + xdg.configFile."nvim" = lib.mkIf (cfg.dotfiles != null) { + source = cfg.dotfiles; }; programs.neovim = { @@ -64,6 +74,6 @@ ]; }; } - ]; + ]); }; } diff --git a/features/networkmanager.nix b/features/networkmanager.nix index 05a10c2..dc62e1c 100644 --- a/features/networkmanager.nix +++ b/features/networkmanager.nix @@ -1,9 +1,18 @@ { - nixos = _: { - networking.networkmanager.enable = true; - networking.nameservers = [ - "1.1.1.1" - "8.8.8.8" - ]; - }; + nixos = + { config, lib, ... }: + let + cfg = config.features.networkmanager; + in + { + options.features.networkmanager.enable = lib.mkEnableOption "networkmanager"; + + config = lib.mkIf cfg.enable { + networking.networkmanager.enable = true; + networking.nameservers = [ + "1.1.1.1" + "8.8.8.8" + ]; + }; + }; } diff --git a/features/nix-ld.nix b/features/nix-ld.nix index e81219c..7c9940a 100644 --- a/features/nix-ld.nix +++ b/features/nix-ld.nix @@ -1,5 +1,14 @@ { - nixos = _: { - programs.nix-ld.enable = true; - }; + nixos = + { config, lib, ... }: + let + cfg = config.features.nix-ld; + in + { + options.features.nix-ld.enable = lib.mkEnableOption "nix-ld"; + + config = lib.mkIf cfg.enable { + programs.nix-ld.enable = true; + }; + }; } diff --git a/features/nix-settings.nix b/features/nix-settings.nix new file mode 100644 index 0000000..06246d1 --- /dev/null +++ b/features/nix-settings.nix @@ -0,0 +1,71 @@ +{ + nixos = + { config, lib, ... }: + let + cfg = config.features.nix-settings; + in + { + options.features.nix-settings = { + enable = lib.mkEnableOption "nix settings"; + + towerCache.enable = lib.mkOption { + type = lib.types.bool; + default = true; + + }; + + gc = { + dates = lib.mkOption { + type = lib.types.str; + default = "monthly"; + }; + + olderThan = lib.mkOption { + type = lib.types.str; + default = "30d"; + }; + }; + + optimise.dates = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "monthly" ]; + }; + }; + + config = lib.mkIf cfg.enable { + nix = { + settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + download-buffer-size = 2 * 1024 * 1024 * 1024; + warn-dirty = false; + substituters = + [ + "https://cache.nixos.org" + "https://nix-community.cachix.org?priority=45" + ] + ++ lib.optional cfg.towerCache.enable "http://tower:5000?priority=50"; + trusted-public-keys = + [ + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + ] + ++ lib.optional cfg.towerCache.enable "matej.nix-1:TdbemLVYblvAxqJcwb3mVKmmr3cfzXbMcZHE5ILnZDE="; + }; + + gc = { + automatic = true; + dates = cfg.gc.dates; + options = "--delete-older-than ${cfg.gc.olderThan}"; + }; + + optimise = { + automatic = true; + dates = cfg.optimise.dates; + }; + }; + }; + }; +} diff --git a/features/onepassword.nix b/features/onepassword.nix new file mode 100644 index 0000000..d1e5b50 --- /dev/null +++ b/features/onepassword.nix @@ -0,0 +1,18 @@ +{ + nixos = + { config, lib, user, ... }: + let + cfg = config.features.onepassword; + in + { + options.features.onepassword.enable = lib.mkEnableOption "1password"; + + config = lib.mkIf cfg.enable { + programs._1password.enable = true; + programs._1password-gui = { + enable = true; + polkitPolicyOwners = [ user ]; + }; + }; + }; +} diff --git a/features/openssh.nix b/features/openssh.nix index 7a03aef..9a0fd58 100644 --- a/features/openssh.nix +++ b/features/openssh.nix @@ -1,18 +1,23 @@ { nixos = { lib, config, ... }: + let + cfg = config.features.openssh; + in { - options = { - openssh.port = lib.mkOption { + options.features.openssh = { + enable = lib.mkEnableOption "openssh"; + + port = lib.mkOption { type = lib.types.port; default = 22; }; }; - config = { + config = lib.mkIf cfg.enable { services.openssh = { enable = true; - ports = [ config.openssh.port ]; + ports = [ cfg.port ]; settings = { PasswordAuthentication = false; AllowUsers = null; diff --git a/features/power.nix b/features/power.nix new file mode 100644 index 0000000..bd580e1 --- /dev/null +++ b/features/power.nix @@ -0,0 +1,57 @@ +{ + nixos = + { config, lib, ... }: + let + cfg = config.features.power; + in + { + options.features.power = { + enable = lib.mkEnableOption "laptop power management"; + + resumeDevice = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + + lidSwitch = lib.mkOption { + type = lib.types.str; + default = "suspend-then-hibernate"; + }; + + powerKey = lib.mkOption { + type = lib.types.str; + default = "suspend-then-hibernate"; + }; + + idleAction = lib.mkOption { + type = lib.types.str; + default = "suspend-then-hibernate"; + }; + + idleActionSec = lib.mkOption { + type = lib.types.str; + default = "15min"; + }; + + hibernateDelaySec = lib.mkOption { + type = lib.types.str; + default = "30min"; + }; + }; + + config = lib.mkIf cfg.enable { + boot.resumeDevice = lib.mkIf (cfg.resumeDevice != null) cfg.resumeDevice; + + services.logind.settings.Login = { + HandleLidSwitch = cfg.lidSwitch; + HandlePowerKey = cfg.powerKey; + IdleAction = cfg.idleAction; + IdleActionSec = cfg.idleActionSec; + }; + + systemd.sleep.settings.Sleep = { + HibernateDelaySec = cfg.hibernateDelaySec; + }; + }; + }; +} diff --git a/features/printing.nix b/features/printing.nix index a564157..81c6762 100644 --- a/features/printing.nix +++ b/features/printing.nix @@ -1,10 +1,19 @@ { - nixos = _: { - services.printing.enable = true; - services.avahi = { - enable = true; - nssmdns4 = true; - openFirewall = true; + nixos = + { config, lib, ... }: + let + cfg = config.features.printing; + in + { + options.features.printing.enable = lib.mkEnableOption "printing"; + + config = lib.mkIf cfg.enable { + services.printing.enable = true; + services.avahi = { + enable = true; + nssmdns4 = true; + openFirewall = true; + }; + }; }; - }; } diff --git a/features/remote-base.nix b/features/remote-base.nix index cde28d2..18c4d22 100644 --- a/features/remote-base.nix +++ b/features/remote-base.nix @@ -1,13 +1,20 @@ { nixos = - { config, user, ... }: + { config, lib, user, ... }: + let + cfg = config.features.remote-base; + in { - sops.secrets.user-password = { - sopsFile = ../secrets/common.yaml; - neededForUsers = true; - }; + options.features.remote-base.enable = lib.mkEnableOption "remote-base"; - users.mutableUsers = false; - users.users.${user}.hashedPasswordFile = config.sops.secrets.user-password.path; + config = lib.mkIf cfg.enable { + sops.secrets.user-password = { + sopsFile = ../secrets/common.yaml; + neededForUsers = true; + }; + + users.mutableUsers = false; + users.users.${user}.hashedPasswordFile = config.sops.secrets.user-password.path; + }; }; } diff --git a/features/shell-minimal.nix b/features/shell-minimal.nix deleted file mode 100644 index 5ecde47..0000000 --- a/features/shell-minimal.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - nixos = _: { - programs.zsh.enable = true; - environment.etc."zshenv".text = '' - export ZDOTDIR=$HOME/.config/zsh - ''; - }; - - home = - { pkgs, ... }: - { - home.packages = with pkgs; [ - starship - ]; - }; -} diff --git a/features/shell.nix b/features/shell.nix index 23c2ce1..43f41cd 100644 --- a/features/shell.nix +++ b/features/shell.nix @@ -1,25 +1,28 @@ { - nixos = _: { - programs.zsh.enable = true; - environment.etc."zshenv".text = '' - export ZDOTDIR=$HOME/.config/zsh - ''; - }; + nixos = + { lib, ... }: + { + options.features.shell.enable = lib.mkEnableOption "shell extras"; + }; home = - { pkgs, ... }: + { pkgs, lib, osConfig, ... }: + let + cfg = osConfig.features.shell; + in { - home.packages = with pkgs; [ - starship - fzf - htop - jc - jq - openssl - pv - ripgrep - fd - tmux - ]; + config = lib.mkIf cfg.enable { + home.packages = with pkgs; [ + fzf + htop + jc + jq + openssl + pv + ripgrep + fd + tmux + ]; + }; }; } diff --git a/features/sway.nix b/features/sway.nix index 6c8a94c..1bfae7f 100644 --- a/features/sway.nix +++ b/features/sway.nix @@ -1,38 +1,91 @@ { nixos = - { pkgs, ... }: + { config, lib, pkgs, ... }: + let + cfg = config.features.sway; + desktopCfg = config.features.desktop; + in { - programs.sway = { - enable = true; - package = pkgs.swayfx; - wrapperFeatures.gtk = true; - extraSessionCommands = '' - # fix for java awt apps not rendering - export _JAVA_AWT_WM_NONREPARENTING=1 - ''; + options.features.sway = { + enable = lib.mkEnableOption "sway window manager"; + + greeter.enable = lib.mkOption { + type = lib.types.bool; + default = true; + }; }; - environment.systemPackages = with pkgs; [ - waybar - mako - wob - playerctl - brightnessctl - foot - grim - pulseaudio - swayidle - swaylock-effects - jq - slurp - wl-clipboard - pamixer - wlsunset - satty - wayland-pipewire-idle-inhibit - fuzzel - cliphist - zenity - ]; + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + # soft dependency + features.desktop.enable = lib.mkDefault true; + + # hard dependency + assertions = [ + { + assertion = desktopCfg.enable; + message = "features.sway requires features.desktop"; + } + ]; + + programs.sway = { + enable = true; + package = pkgs.swayfx; + wrapperFeatures.gtk = true; + extraSessionCommands = '' + # fix for java awt apps not rendering + export _JAVA_AWT_WM_NONREPARENTING=1 + ''; + }; + + environment.systemPackages = with pkgs; [ + waybar + mako + wob + playerctl + brightnessctl + foot + grim + pulseaudio + swayidle + swaylock-effects + jq + slurp + wl-clipboard + pamixer + wlsunset + satty + wayland-pipewire-idle-inhibit + fuzzel + cliphist + zenity + ]; + } + + # greeter + (lib.mkIf cfg.greeter.enable { + programs.regreet = { + enable = true; + cageArgs = [ + "-s" + "-m" + "last" + ]; + font = { + name = lib.mkForce "JetBrainsMono Nerd Font"; + size = lib.mkForce 14; + }; + settings = { + background = { + path = lib.mkForce (toString desktopCfg.theme.wallpaper); + fit = lib.mkForce "Cover"; + }; + GTK = { + application_prefer_dark_theme = lib.mkForce true; + }; + }; + }; + }) + ]); }; } diff --git a/features/tailscale.nix b/features/tailscale.nix index a6916ad..69f543d 100644 --- a/features/tailscale.nix +++ b/features/tailscale.nix @@ -1,8 +1,17 @@ { - nixos = _: { - services.tailscale = { - enable = true; - useRoutingFeatures = "both"; + nixos = + { config, lib, ... }: + let + cfg = config.features.tailscale; + in + { + options.features.tailscale.enable = lib.mkEnableOption "tailscale"; + + config = lib.mkIf cfg.enable { + services.tailscale = { + enable = true; + useRoutingFeatures = "both"; + }; + }; }; - }; } diff --git a/features/udev.nix b/features/udev.nix new file mode 100644 index 0000000..5e3764e --- /dev/null +++ b/features/udev.nix @@ -0,0 +1,52 @@ +{ + nixos = + { config, lib, pkgs, ... }: + let + cfg = config.features.udev; + in + { + options.features.udev = { + enable = lib.mkEnableOption "custom udev rules"; + + kindle.enable = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + ledger.enable = lib.mkOption { + type = lib.types.bool; + default = false; + }; + + keyboard-zsa.enable = lib.mkOption { + type = lib.types.bool; + default = false; + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + (lib.mkIf cfg.kindle.enable { + # NOTE:(@janezicmatej) uses services.udev.packages instead of extraRules + # because extraRules writes to 99-local.rules which is too late for uaccess + services.udev.packages = [ + pkgs.libmtp + (pkgs.writeTextFile { + name = "kindle-udev-rules"; + text = '' + ACTION!="remove", SUBSYSTEM=="usb", ATTRS{idVendor}=="1949", TAG+="uaccess" + ''; + destination = "/etc/udev/rules.d/70-kindle.rules"; + }) + ]; + }) + + (lib.mkIf cfg.ledger.enable { + hardware.ledger.enable = true; + }) + + (lib.mkIf cfg.keyboard-zsa.enable { + hardware.keyboard.zsa.enable = true; + }) + ]); + }; +} diff --git a/features/user-matej.nix b/features/user-matej.nix index c47213d..50d3086 100644 --- a/features/user-matej.nix +++ b/features/user-matej.nix @@ -10,15 +10,12 @@ in }; nixos = - { pkgs, ... }: + { ... }: { - programs.zsh.enable = true; - users.users.matej = { uid = 1000; isNormalUser = true; home = "/home/matej"; - shell = pkgs.zsh; extraGroups = [ "wheel" ]; openssh.authorizedKeys.keys = sshKeys; }; diff --git a/features/vm-9p-automount.nix b/features/vm-9p-automount.nix deleted file mode 100644 index bfc9751..0000000 --- a/features/vm-9p-automount.nix +++ /dev/null @@ -1,72 +0,0 @@ -{ - nixos = - { - pkgs, - lib, - config, - ... - }: - let - inherit (config.vm-9p-automount) user; - inherit (config.users.users.${user}) home group; - in - { - options = { - vm-9p-automount = { - user = lib.mkOption { - type = lib.types.str; - }; - - prefix = lib.mkOption { - type = lib.types.str; - default = "m_"; - }; - - basePath = lib.mkOption { - type = lib.types.str; - default = "${home}/mnt"; - }; - }; - }; - - config = { - systemd.services.vm-9p-automount = { - description = "Auto-discover and mount 9p shares"; - after = [ - "local-fs.target" - "nss-user-lookup.target" - "systemd-modules-load.service" - ]; - wants = [ "systemd-modules-load.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = pkgs.writeShellScript "vm-9p-automount" '' - BASE="${config.vm-9p-automount.basePath}" - PREFIX="${config.vm-9p-automount.prefix}" - mkdir -p "$BASE" - chown ${user}:${group} "$BASE" - - for tagfile in $(find /sys/devices -name mount_tag 2>/dev/null); do - [ -f "$tagfile" ] || continue - tag=$(tr -d '\0' < "$tagfile") - - case "$tag" in - "$PREFIX"*) ;; - *) continue ;; - esac - - name="''${tag#"$PREFIX"}" - target="$BASE/$name" - - mkdir -p "$target" - ${pkgs.util-linux}/bin/mount -t 9p "$tag" "$target" \ - -o trans=virtio,version=9p2000.L || continue - done - ''; - }; - }; - }; - }; -} diff --git a/features/vm-guest.nix b/features/vm-guest.nix index 0d6e7af..8014528 100644 --- a/features/vm-guest.nix +++ b/features/vm-guest.nix @@ -6,42 +6,110 @@ config, ... }: + let + cfg = config.features.vm-guest; + autoUser = cfg.automount.user; + autoHome = config.users.users.${autoUser}.home; + autoGroup = config.users.users.${autoUser}.group; + in { - options = { - vm-guest.headless = lib.mkOption { + options.features.vm-guest = { + enable = lib.mkEnableOption "qemu vm guest"; + + headless = lib.mkOption { type = lib.types.bool; default = false; }; - }; - config = { - services.qemuGuest.enable = true; - services.spice-vdagentd.enable = lib.mkIf (!config.vm-guest.headless) true; + automount = { + enable = lib.mkEnableOption "9p share automount"; - boot.kernelParams = lib.mkIf config.vm-guest.headless [ "console=ttyS0,115200" ]; + user = lib.mkOption { + type = lib.types.str; + }; - boot.initrd.availableKernelModules = [ - "9p" - "9pnet_virtio" - ]; - boot.kernelModules = [ - "9p" - "9pnet_virtio" - ]; + prefix = lib.mkOption { + type = lib.types.str; + default = "m_"; + }; - networking = { - useDHCP = true; - firewall.allowedTCPPorts = [ 22 ]; + basePath = lib.mkOption { + type = lib.types.str; + default = "${autoHome}/mnt"; + }; }; - - security.sudo.wheelNeedsPassword = false; - - environment.systemPackages = with pkgs; [ - curl - wget - htop - sshfs - ]; }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + services.qemuGuest.enable = true; + services.spice-vdagentd.enable = lib.mkIf (!cfg.headless) true; + + boot.kernelParams = lib.mkIf cfg.headless [ "console=ttyS0,115200" ]; + + boot.initrd.availableKernelModules = [ + "9p" + "9pnet_virtio" + ]; + boot.kernelModules = [ + "9p" + "9pnet_virtio" + ]; + + networking = { + useDHCP = true; + firewall.allowedTCPPorts = [ 22 ]; + }; + + security.sudo.wheelNeedsPassword = false; + + environment.systemPackages = with pkgs; [ + curl + wget + htop + sshfs + ]; + } + + (lib.mkIf cfg.automount.enable { + systemd.services.vm-9p-automount = { + description = "Auto-discover and mount 9p shares"; + after = [ + "local-fs.target" + "nss-user-lookup.target" + "systemd-modules-load.service" + ]; + wants = [ "systemd-modules-load.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = pkgs.writeShellScript "vm-9p-automount" '' + BASE="${cfg.automount.basePath}" + PREFIX="${cfg.automount.prefix}" + mkdir -p "$BASE" + chown ${autoUser}:${autoGroup} "$BASE" + + for tagfile in $(find /sys/devices -name mount_tag 2>/dev/null); do + [ -f "$tagfile" ] || continue + tag=$(tr -d '\0' < "$tagfile") + + case "$tag" in + "$PREFIX"*) ;; + *) continue ;; + esac + + name="''${tag#"$PREFIX"}" + target="$BASE/$name" + + mkdir -p "$target" + ${pkgs.util-linux}/bin/mount -t 9p "$tag" "$target" \ + -o trans=virtio,version=9p2000.L || continue + done + ''; + }; + }; + }) + ]); }; } diff --git a/features/yubikey.nix b/features/yubikey.nix deleted file mode 100644 index 966a1aa..0000000 --- a/features/yubikey.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ - nixos = - { pkgs, ... }: - { - environment.systemPackages = with pkgs; [ - yubikey-personalization - yubikey-manager - ]; - - services.pcscd.enable = true; - }; -} diff --git a/features/zsh.nix b/features/zsh.nix new file mode 100644 index 0000000..646b62e --- /dev/null +++ b/features/zsh.nix @@ -0,0 +1,42 @@ +{ + nixos = + { config, lib, pkgs, user, ... }: + let + cfg = config.features.zsh; + in + { + options.features.zsh = { + enable = lib.mkEnableOption "zsh"; + + loginShell.enable = lib.mkOption { + type = lib.types.bool; + default = true; + + }; + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + programs.zsh.enable = true; + environment.etc."zshenv".text = '' + export ZDOTDIR=$HOME/.config/zsh + ''; + } + + (lib.mkIf cfg.loginShell.enable { + users.users.${user}.shell = pkgs.zsh; + }) + ]); + }; + + home = + { pkgs, lib, osConfig, ... }: + let + cfg = osConfig.features.zsh; + in + { + config = lib.mkIf cfg.enable { + home.packages = [ pkgs.starship ]; + }; + }; +} diff --git a/flake/hosts.nix b/flake/hosts.nix index a0ec121..d66a6a0 100644 --- a/flake/hosts.nix +++ b/flake/hosts.nix @@ -15,25 +15,29 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "openssh" - "localisation" - "gnupg" - "shell" - "desktop" - "sway" - "greeter" - "printing" - "networkmanager" - "docker" - "tailscale" - "nix-ld" - "yubikey" - "calibre" - "gaming" - "direnv" - "neovim" - "dev" + "bootloader" "claude" + "desktop" + "dev" + "direnv" + "docker" + "gaming" + "git" + "gnupg" + "localisation" + "neovim" + "networkmanager" + "nix-ld" + "nix-settings" + "onepassword" + "openssh" + "power" + "printing" + "shell" + "sway" + "tailscale" + "udev" + "zsh" ]; }; @@ -41,27 +45,30 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "openssh" - "localisation" - "gnupg" - "shell" - "desktop" - "sway" - "greeter" - "printing" - "networkmanager" - "docker" - "tailscale" - "nix-ld" - "yubikey" - "calibre" - "gaming" - "initrd-ssh" - "direnv" - "neovim" - "dev" + "bootloader" "claude" + "desktop" + "dev" + "direnv" + "docker" + "gaming" + "git" + "gnupg" "harmonia" + "initrd-ssh" + "localisation" + "neovim" + "networkmanager" + "nix-ld" + "nix-settings" + "onepassword" + "openssh" + "printing" + "shell" + "sway" + "tailscale" + "udev" + "zsh" ]; }; @@ -70,7 +77,9 @@ in system = "x86_64-linux"; user = "matej"; features = [ + "nix-settings" "openssh" + "zsh" ]; }; @@ -78,11 +87,14 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "openssh" + "bootloader" "localisation" + "nix-settings" + "openssh" + "remote-base" "shell" "tailscale" - "remote-base" + "zsh" ]; }; @@ -91,12 +103,14 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "openssh" + "filedrop" "localisation" + "nix-settings" + "openssh" + "remote-base" "shell" "tailscale" - "remote-base" - "filedrop" + "zsh" ]; }; @@ -104,14 +118,15 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "localisation" + "bootloader" + "desktop" "gnupg" - "shell-minimal" - "desktop-minimal" - "sway" - "greeter" + "localisation" "networkmanager" - "yubikey" + "nix-settings" + "sway" + "udev" + "zsh" ]; }; @@ -119,16 +134,18 @@ in system = "x86_64-linux"; user = "matej"; features = [ - "openssh" - "localisation" - "gnupg" - "shell" - "vm-guest" - "vm-9p-automount" - "docker" - "neovim" "claude" "dev" + "docker" + "git" + "gnupg" + "localisation" + "neovim" + "nix-settings" + "openssh" + "shell" + "vm-guest" + "zsh" ]; }; }; diff --git a/hosts/cube/configuration.nix b/hosts/cube/configuration.nix index 98d9f67..e985332 100644 --- a/hosts/cube/configuration.nix +++ b/hosts/cube/configuration.nix @@ -2,9 +2,6 @@ { imports = [ inputs.disko.nixosModules.disko ]; - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; - disko.devices.disk.main = { type = "disk"; device = "/dev/nvme0n1"; @@ -32,10 +29,5 @@ }; }; - localisation = { - timeZone = "Europe/Ljubljana"; - defaultLocale = "en_US.UTF-8"; - }; - system.stateVersion = "25.11"; } diff --git a/hosts/ephvm/configuration.nix b/hosts/ephvm/configuration.nix index 775d276..a5ece44 100644 --- a/hosts/ephvm/configuration.nix +++ b/hosts/ephvm/configuration.nix @@ -5,6 +5,7 @@ ... }: { + features.nix-settings.towerCache.enable = false; # no hardware firmware needed in a VM hardware.enableRedistributableFirmware = lib.mkForce false; hardware.wirelessRegulatoryDatabase = lib.mkForce false; @@ -28,18 +29,12 @@ ); }; - vm-guest.headless = true; - - vm-9p-automount.user = "matej"; - - localisation = { - timeZone = "UTC"; - defaultLocale = "en_US.UTF-8"; - }; - - home-manager.users.matej = { - neovim.dotfiles = inputs.nvim; + features.vm-guest.headless = true; + features.vm-guest.automount = { + enable = true; + user = "matej"; }; + features.neovim.dotfiles = inputs.nvim; # ensure .config exists with correct ownership before automount systemd.tmpfiles.rules = [ "d /home/matej/.config 0755 matej users -" ]; diff --git a/hosts/floo/configuration.nix b/hosts/floo/configuration.nix index dde1d29..ae82b71 100644 --- a/hosts/floo/configuration.nix +++ b/hosts/floo/configuration.nix @@ -2,6 +2,8 @@ { imports = [ inputs.disko.nixosModules.disko ]; + features.filedrop.sopsFile = ../../secrets/floo.yaml; + boot.loader.grub.enable = true; disko.devices.disk.main = { @@ -26,10 +28,5 @@ }; }; - localisation = { - timeZone = "Europe/Ljubljana"; - defaultLocale = "en_US.UTF-8"; - }; - system.stateVersion = "25.11"; } diff --git a/hosts/fortress/configuration.nix b/hosts/fortress/configuration.nix index c5e1195..9558331 100644 --- a/hosts/fortress/configuration.nix +++ b/hosts/fortress/configuration.nix @@ -10,13 +10,11 @@ inputs.nixos-hardware.nixosModules.framework-16-amd-ai-300-series ]; - localisation = { - timeZone = "Europe/Ljubljana"; - defaultLocale = "en_US.UTF-8"; + features.desktop = { + apps.enable = false; + internalCA.enable = false; }; - - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; + features.gnupg.yubikey.enable = true; disko.devices.disk.main = { type = "disk"; diff --git a/hosts/fw16/configuration.nix b/hosts/fw16/configuration.nix index f19b3a8..88c6c40 100644 --- a/hosts/fw16/configuration.nix +++ b/hosts/fw16/configuration.nix @@ -1,8 +1,7 @@ { lib, - pkgs, - inputs, options, + inputs, ... }: @@ -11,37 +10,22 @@ inputs.nixos-hardware.nixosModules.framework-16-amd-ai-300-series ]; - localisation = { - timeZone = "Europe/Ljubljana"; - defaultLocale = "en_US.UTF-8"; + features.desktop.bluetooth.enable = true; + features.gnupg.yubikey.enable = true; + features.udev = { + ledger.enable = true; + keyboard-zsa.enable = true; }; - - boot.loader.systemd-boot.enable = true; - boot.loader.efi.canTouchEfiVariables = true; + features.power.resumeDevice = "/dev/disk/by-uuid/ff4750e7-3a9f-42c2-bb68-c458a6560540"; boot.kernelParams = [ "pcie_aspm.policy=powersupersave" ]; - boot.resumeDevice = "/dev/disk/by-uuid/ff4750e7-3a9f-42c2-bb68-c458a6560540"; - - services.logind.settings.Login = { - HandleLidSwitch = "suspend-then-hibernate"; - HandlePowerKey = "suspend-then-hibernate"; - IdleAction = "suspend-then-hibernate"; - IdleActionSec = "15min"; - }; - - systemd.sleep.settings.Sleep = { - HibernateDelaySec = "30min"; - }; - programs.nix-ld.libraries = options.programs.nix-ld.libraries.default; services.gnome.gnome-keyring.enable = true; services.teamviewer.enable = true; services.hardware.bolt.enable = true; - hardware.keyboard.zsa.enable = true; - hardware.ledger.enable = true; hardware.bluetooth.powerOnBoot = true; hardware.inputmodule.enable = true; diff --git a/hosts/iso/configuration.nix b/hosts/iso/configuration.nix index 6a7b733..d306305 100644 --- a/hosts/iso/configuration.nix +++ b/hosts/iso/configuration.nix @@ -1,15 +1,13 @@ -{ lib, ... }: +{ lib, userKeys, ... }: { + features.nix-settings.towerCache.enable = false; image.modules.iso-installer = { isoImage.squashfsCompression = "zstd -Xcompression-level 6"; }; # live iso: passwordless login and sudo users.users.matej.initialHashedPassword = ""; - users.users.root.openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICQGLdINKzs+sEy62Pefng0bcedgU396+OryFgeH99/c janezicmatej" - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDk00+Km03epQXQs+xEwwH3zcurACzkEH+kDOPBw6RQe openpgp:0xB095D449" - ]; + users.users.root.openssh.authorizedKeys.keys = userKeys.sshAuthorizedKeys; services.openssh.settings.PermitRootLogin = lib.mkForce "prohibit-password"; security.sudo.wheelNeedsPassword = false; diff --git a/hosts/tower/configuration.nix b/hosts/tower/configuration.nix index 617831c..6c8e7b5 100644 --- a/hosts/tower/configuration.nix +++ b/hosts/tower/configuration.nix @@ -1,38 +1,28 @@ { config, - lib, - inputs, userKeys, ... }: { - imports = [ - inputs.lanzaboote.nixosModules.lanzaboote - ]; + features.nix-settings.towerCache.enable = false; + features.bootloader.mode = "lanzaboote"; + features.desktop.bluetooth.enable = true; + features.gnupg.yubikey.enable = true; + features.udev = { + ledger.enable = true; + keyboard-zsa.enable = true; + }; + features.initrd-ssh = { + networkModule = "r8169"; + authorizedKeys = userKeys.sshAuthorizedKeys; + }; # nix store signing sops.secrets.nix-signing-key.sopsFile = ../../secrets/tower.yaml; nix.settings.secret-key-files = [ config.sops.secrets.nix-signing-key.path ]; - localisation = { - timeZone = "Europe/Ljubljana"; - defaultLocale = "en_US.UTF-8"; - }; - - initrd-ssh = { - networkModule = "r8169"; - authorizedKeys = userKeys.sshAuthorizedKeys; - }; - - # lanzaboote secure boot boot.kernelParams = [ "btusb.reset=1" ]; - boot.loader.efi.canTouchEfiVariables = true; - boot.loader.systemd-boot.enable = lib.mkForce false; - boot.lanzaboote = { - enable = true; - pkiBundle = "/var/lib/sbctl"; - }; services.udisks2.enable = true; diff --git a/lib/mkHost.nix b/lib/mkHost.nix index 0d5bd44..d6517fd 100644 --- a/lib/mkHost.nix +++ b/lib/mkHost.nix @@ -27,7 +27,19 @@ let hostConfig = ../hosts/${name}/configuration.nix; hostHWConfig = ../hosts/${name}/hardware-configuration.nix; - # load feature with path check + # auto-discover all features, excluding user-* and default.nix + featureDir = builtins.readDir ../features; + allFeatureNames = lib.pipe featureDir [ + (lib.filterAttrs ( + n: t: + (t == "regular" && lib.hasSuffix ".nix" n && n != "default.nix" && !lib.hasPrefix "user-" n) + || (t == "directory" && builtins.pathExists ../features/${n}/default.nix) + )) + builtins.attrNames + (map (n: lib.removeSuffix ".nix" n)) + ]; + + # load all features unconditionally loadFeature = f: assert @@ -35,7 +47,7 @@ let || throw "feature '${f}' not found at ${toString (featurePath f)}"; import (featurePath f); - loadedFeatures = map loadFeature features; + loadedFeatures = map loadFeature allFeatureNames; # load user feature with path check userFeature = @@ -55,31 +67,42 @@ let # collect nixos and home modules from all features nixosMods = map (f: f.nixos) (builtins.filter (f: f ? nixos) allFeatures); homeMods = map (f: f.home) (builtins.filter (f: f ? home) allFeatures); + + # translate features list to enable flags + featureEnableModule = + { lib, ... }: + { + config.features = lib.genAttrs features (_: { + enable = true; + }); + }; in nixpkgs.lib.nixosSystem { inherit system; - modules = [ - ../nix.nix - inputs.sops-nix.nixosModules.sops + modules = + [ + inputs.sops-nix.nixosModules.sops + inputs.stylix.nixosModules.stylix - { nixpkgs.overlays = overlays; } - { nixpkgs.config.allowUnfree = true; } - { networking.hostName = name; } + { nixpkgs.overlays = overlays; } + { nixpkgs.config.allowUnfree = true; } + { networking.hostName = name; } - hostConfig - ] - ++ lib.optional (builtins.pathExists hostHWConfig) hostHWConfig - ++ nixosMods - ++ lib.optionals hasUser [ - inputs.home-manager.nixosModules.home-manager - { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "backup"; - home-manager.users.${user}.imports = homeMods; - home-manager.extraSpecialArgs = { inherit inputs; }; - } - ]; + featureEnableModule + hostConfig + ] + ++ lib.optional (builtins.pathExists hostHWConfig) hostHWConfig + ++ nixosMods + ++ lib.optionals hasUser [ + inputs.home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "backup"; + home-manager.users.${user}.imports = homeMods; + home-manager.extraSpecialArgs = { inherit inputs; }; + } + ]; specialArgs = { inherit inputs userKeys user; };