diff --git a/glue/boot.nix b/glue/boot.nix new file mode 100644 index 0000000..8e81434 --- /dev/null +++ b/glue/boot.nix @@ -0,0 +1,137 @@ +# ============================================================================ +# Boot & Storage Configuration +# ============================================================================ +# This module defines: +# - Disko partition layout (EFI, swap, root) +# - Bootloader configuration (systemd-boot with Plymouth) +# - Filesystem options (device, swap size) +# - Build method options (used by installer/artifacts.nix) +# - Convenience options (forUser, useHostPrefix) + +{ config, lib, ... }: + +{ + options.athenix = { + host = { + useHostPrefix = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to prepend the host prefix to the hostname (used in inventory and hosts/default.nix)."; + }; + filesystem = { + device = lib.mkOption { + type = lib.types.str; + description = "The main disk device to use for installation."; + }; + useSwap = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to create and use a swap partition."; + }; + swapSize = lib.mkOption { + type = lib.types.str; + description = "The size of the swap partition."; + }; + }; + }; + }; + + config = { + # ========== Disk Partitioning (Disko) ========== + disko.enableConfig = lib.mkDefault true; + + disko.devices = { + disk.main = { + type = "disk"; + device = config.athenix.host.filesystem.device; + content = { + type = "gpt"; + partitions = { + # EFI System Partition + ESP = { + name = "ESP"; + label = "BOOT"; + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "umask=0077" ]; + extraArgs = [ + "-n" + "BOOT" + ]; + }; + }; + + # Swap Partition (size configurable per host) + swap = lib.mkIf config.athenix.host.filesystem.useSwap { + name = "swap"; + label = "swap"; + size = config.athenix.host.filesystem.swapSize; + content = { + type = "swap"; + }; + }; + + # Root Partition (takes remaining space) + root = { + name = "root"; + label = "root"; + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + extraArgs = [ + "-L" + "ROOT" + ]; + }; + }; + }; + }; + }; + }; + + # Bootloader Configuration + boot = { + loader.systemd-boot.enable = true; + loader.efi.canTouchEfiVariables = true; + plymouth.enable = true; + + # Enable "Silent boot" + consoleLogLevel = 3; + initrd.verbose = false; + + # Hide the OS choice for bootloaders. + # It's still possible to open the bootloader list by pressing any key + # It will just not appear on screen unless a key is pressed + loader.timeout = lib.mkDefault 0; + }; + + # Set your time zone. + time.timeZone = "America/New_York"; + + # Select internationalisation properties. + 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"; + }; + + systemd.sleep.extraConfig = '' + SuspendState=freeze + HibernateDelaySec=2h + ''; + }; +} diff --git a/glue/common.nix b/glue/common.nix new file mode 100644 index 0000000..c7ad4ae --- /dev/null +++ b/glue/common.nix @@ -0,0 +1,99 @@ +# ============================================================================ +# Common Host Module +# ============================================================================ +# This module contains all the common configuration shared by all host types. +# It is automatically imported by the fleet generator for every host. + +{ inputs }: +{ + config, + lib, + ... +}: +{ + imports = [ + ./boot.nix + ./user-config.nix + ../sw + ../users.nix + inputs.home-manager.nixosModules.home-manager + inputs.agenix.nixosModules.default + inputs.disko.nixosModules.disko + ]; + + # Define garbage collection options here since they're consumed in this module + options.athenix = { + forUser = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Convenience option to configure a host for a specific user. + Automatically enables the user (sets athenix.users.username.enable = true). + Value should be a username from athenix.users.accounts. + ''; + }; + + system.gc = { + enable = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to enable automatic garbage collection."; + }; + frequency = lib.mkOption { + type = lib.types.str; + default = "weekly"; + description = "How often to run garbage collection (systemd timer format)."; + }; + retentionDays = lib.mkOption { + type = lib.types.int; + default = 30; + description = "Number of days to keep old generations before deletion."; + }; + optimise = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to automatically optimize the Nix store."; + }; + }; + + host.buildMethods = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "installer-iso" ]; + description = '' + List of allowed build methods for this host (used by installer/artifacts.nix). + Supported methods: + - "installer-iso": Generates an auto-install ISO that installs this configuration to disk. + - "iso": Generates a live ISO (using nixos-generators). + - "ipxe": Generates iPXE netboot artifacts (kernel, initrd, script). + - "lxc": Generates an LXC container tarball. + - "proxmox": Generates a Proxmox VMA archive. + ''; + }; + }; + + config = lib.mkMerge [ + # Enable forUser if specified + (lib.mkIf (config.athenix.forUser != null) { + athenix.users.${config.athenix.forUser}.enable = true; + }) + + { + system.stateVersion = "25.11"; + + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + + # Automatic Garbage Collection + nix.gc = lib.mkIf config.athenix.system.gc.enable { + automatic = true; + dates = config.athenix.system.gc.frequency; + options = "--delete-older-than ${toString config.athenix.system.gc.retentionDays}d"; + }; + + # Optimize storage + nix.optimise.automatic = config.athenix.system.gc.optimise; + } + ]; +} diff --git a/glue/fleet.nix b/glue/fleet.nix new file mode 100644 index 0000000..e0714b2 --- /dev/null +++ b/glue/fleet.nix @@ -0,0 +1,278 @@ +{ + inputs, + fleet ? import ../inventory.nix, + ... +}: + +# ============================================================================ +# Fleet Generator +# ============================================================================ +# This file contains the logic to generate NixOS configurations for all hosts +# defined in inventory.nix. It supports both hostname-based and count-based +# configurations with flexible type associations. + +let + nixpkgs = inputs.nixpkgs; + lib = nixpkgs.lib; + + # Load all available host types from hosts/ + hostTypes = import ../variants { inherit inputs; }; + + # Helper to create a single NixOS system configuration + mkHost = + { + hostName, + system ? "x86_64-linux", + hostType, + configOverrides ? { }, + externalModuleThunk ? null, + }: + let + # Lazy evaluation: only fetch external module when building this host + externalModulePath = + if externalModuleThunk != null then + let + # Force evaluation of the thunk (fetchGit, fetchTarball, etc.) + fetchedPath = externalModuleThunk; + # Extract outPath from fetchGit/fetchTarball results + extractedPath = + if builtins.isAttrs fetchedPath && fetchedPath ? outPath then fetchedPath.outPath else fetchedPath; + in + if builtins.isPath extractedPath then + extractedPath + "/default.nix" + else if lib.isDerivation extractedPath then + extractedPath + "/default.nix" + else + extractedPath + "/default.nix" + else + null; + + # Load users.nix to find external user modules + pkgs = nixpkgs.legacyPackages.${system}; + usersData = import ../users.nix { inherit pkgs; }; + accounts = usersData.athenix.users or { }; + + # Build a map of user names to their nixos module paths (if they exist) + # We'll use this to conditionally import modules based on user.enable + userNixosModulePaths = lib.filterAttrs (_: v: v != null) ( + lib.mapAttrs ( + name: user: + if (user ? external && user.external != null) then + let + externalPath = + if builtins.isAttrs user.external && user.external ? outPath then + user.external.outPath + else + user.external; + nixosModulePath = externalPath + "/nixos.nix"; + in + if + (builtins.isPath externalPath || (builtins.isString externalPath && lib.hasPrefix "/" externalPath)) + && builtins.pathExists nixosModulePath + then + nixosModulePath + else + null + else + null + ) accounts + ); + + # Create conditional wrapper modules for each user's nixos.nix + # Each wrapper checks if the user is enabled before applying the module content + userNixosModules = lib.mapAttrsToList ( + name: modulePath: + { + config, + lib, + pkgs, + ... + }@args: + let + # Import the user's nixos module - it returns a function or attrset + importedModuleFunc = import modulePath { inherit inputs; }; + # If it's a function, call it with the module args; otherwise use as-is + importedModule = + if lib.isFunction importedModuleFunc then importedModuleFunc args else importedModuleFunc; + in + { + config = lib.mkIf (config.athenix.users.${name}.enable or false) importedModule; + } + ) userNixosModulePaths; + + # Get the host type module from the hostTypes attribute set + typeModule = + hostTypes.${hostType} + or (throw "Host type '${hostType}' not found. Available types: ${lib.concatStringsSep ", " (lib.attrNames hostTypes)}"); + + # External module from fetchGit/fetchurl + externalPathModule = + if externalModulePath != null then import externalModulePath { inherit inputs; } else { }; + + # Config override module - translate special keys to athenix options + overrideModule = + { ... }: + let + cleanConfig = lib.removeAttrs configOverrides [ + "type" + "count" + "devices" + "overrides" + "defaultCount" + "buildMethods" + ]; + specialConfig = lib.optionalAttrs (configOverrides ? buildMethods) { + athenix.host.buildMethods = configOverrides.buildMethods; + }; + in + { + config = lib.mkMerge [ + cleanConfig + specialConfig + ]; + }; + + allModules = + userNixosModules + ++ [ + (import ./common.nix { inherit inputs; }) + typeModule + overrideModule + { networking.hostName = hostName; } + ] + ++ lib.optional (externalModulePath != null) externalPathModule; + in + { + system = lib.nixosSystem { + inherit system; + specialArgs = { inherit inputs; }; + modules = allModules; + }; + modules = allModules; + }; + + # Process inventory entries - top-level keys are always prefixes + processInventory = lib.mapAttrs ( + prefix: config: + let + hostType = config.type or prefix; + system = config.system or "x86_64-linux"; + devices = config.devices or { }; + hasCount = config ? count; + + # Helper to generate hostname from prefix and suffix + # Numbers get no dash: "nix-surface1", "nix-surface2" + # Letters get dash: "nix-surface-alpha", "nix-surface-beta" + mkHostName = + prefix: suffix: usePrefix: + if !usePrefix then + suffix + else if lib.match "[0-9]+" suffix != null then + "${prefix}${suffix}" # numeric: no dash + else + "${prefix}-${suffix}"; # non-numeric: add dash + + # Extract common overrides and default count + overrides = config.overrides or { }; + defaultCount = config.defaultCount or 0; + + # If devices is a number, treat it as count + devicesValue = config.devices or { }; + actualDevices = if lib.isInt devicesValue then { } else devicesValue; + actualCount = if lib.isInt devicesValue then devicesValue else (config.count or 0); + + # Clean base config - remove inventory management keys + baseConfig = lib.removeAttrs config [ + "type" + "system" + "count" + "devices" + "overrides" + "defaultCount" + ]; + + # Generate hosts from explicit device definitions + deviceHosts = lib.listToAttrs ( + lib.mapAttrsToList ( + deviceKey: deviceConfig: + let + # Check if deviceConfig has an 'external' field for lazy evaluation + hasExternalField = builtins.isAttrs deviceConfig && deviceConfig ? external; + + # Extract external module thunk if present (don't evaluate yet!) + externalModuleThunk = if hasExternalField then deviceConfig.external else null; + + # Remove 'external' from config to avoid conflicts + cleanDeviceConfig = + if hasExternalField then lib.removeAttrs deviceConfig [ "external" ] else deviceConfig; + + # Merge: base config -> overrides -> device-specific config + mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) cleanDeviceConfig; + + # Check useHostPrefix from the merged config + usePrefix = mergedConfig.athenix.host.useHostPrefix or true; + hostName = mkHostName prefix deviceKey usePrefix; + in + { + name = hostName; + value = mkHost { + inherit + hostName + system + hostType + externalModuleThunk + ; + configOverrides = mergedConfig; + }; + } + ) actualDevices + ); + + # Generate numbered hosts from count or defaultCount + # If devices are specified, defaultCount fills in the gaps + countToUse = if actualCount > 0 then actualCount else defaultCount; + + # Get which numbered keys are already defined in devices + existingNumbers = lib.filter (k: lib.match "[0-9]+" k != null) (lib.attrNames actualDevices); + + countHosts = + if countToUse > 0 then + lib.listToAttrs ( + lib.filter (x: x != null) ( + map ( + i: + let + deviceKey = toString i; + # Skip if this number is already in explicit devices + alreadyDefined = lib.elem deviceKey existingNumbers; + in + if alreadyDefined then + null + else + let + hostName = mkHostName prefix deviceKey true; + mergedConfig = lib.recursiveUpdate baseConfig overrides; + in + { + name = hostName; + value = mkHost { + inherit hostName system hostType; + configOverrides = mergedConfig; + }; + } + ) (lib.range 1 countToUse) + ) + ) + else + { }; + in + lib.recursiveUpdate deviceHosts countHosts + ) fleet; + + # Flatten the nested structure + allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues processInventory); +in +{ + nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts; + modules = lib.mapAttrs (n: v: v.modules) allHosts; +} diff --git a/glue/user-config.nix b/glue/user-config.nix new file mode 100644 index 0000000..299acef --- /dev/null +++ b/glue/user-config.nix @@ -0,0 +1,276 @@ +{ + pkgs, + config, + lib, + inputs, + ... +}: + +# ============================================================================ +# User Configuration Module +# ============================================================================ +# This module defines the schema for user accounts and handles their creation. +# It bridges the gap between the data in 'users.nix' and the actual NixOS +# and Home Manager configuration. + +let + # Load users.nix to get account definitions + usersData = import ../users.nix { inherit pkgs; }; + accounts = usersData.athenix.users or { }; + + # Helper: Resolve external module path from fetchGit/fetchTarball/path + resolveExternalPath = + external: + if external == null then + null + else if builtins.isAttrs external && external ? outPath then + external.outPath + else + external; + + # Helper: Check if path exists and is valid + isValidPath = + path: + path != null + && (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path)) + && builtins.pathExists path; + + # Extract athenix.users options from external user.nix modules + # First, build a cache of options per user from their external user.nix (if any). + externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) ( + name: + let + user = accounts.${name}; + externalPath = resolveExternalPath (user.external or null); + userNixPath = if externalPath != null then externalPath + "/user.nix" else null; + in + if isValidPath userNixPath then + let + # Import and evaluate the module with minimal args + outerModule = import userNixPath { inherit inputs; }; + evaluatedModule = outerModule { + config = { }; + inherit lib pkgs; + osConfig = null; + }; + # Extract just the athenix.users. options + athenixUsers = evaluatedModule.athenix.users or { }; + in + athenixUsers.${name} or { } + else + { } + ); + + # externalUserOptions only contains users that actually have options defined + externalUserOptions = lib.filterAttrs ( + _: moduleOptions: moduleOptions != { } + ) externalUserModuleOptions; + + # Submodule defining the structure of a user account + userSubmodule = lib.types.submodule { + options = { + isNormalUser = lib.mkOption { + type = lib.types.bool; + default = true; + }; + description = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + extraGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + }; + hashedPassword = lib.mkOption { + type = lib.types.str; + default = "!"; + }; + extraPackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + excludePackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + homePackages = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + }; + extraImports = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = [ ]; + }; + external = lib.mkOption { + type = lib.types.nullOr ( + lib.types.oneOf [ + lib.types.path + lib.types.package + lib.types.attrs + ] + ); + default = null; + description = '' + External user configuration module. Can be: + - A path to a local module directory + - A fetchGit/fetchTarball result pointing to a repository + + The external module can contain: + - user.nix (optional): Sets athenix.users. options AND home-manager config + - nixos.nix (optional): System-level NixOS configuration + + Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; } + ''; + }; + opensshKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "List of SSH public keys for the user."; + }; + shell = lib.mkOption { + type = lib.types.nullOr lib.types.package; + default = null; + description = "The shell for this user."; + }; + editor = lib.mkOption { + type = lib.types.nullOr lib.types.package; + default = null; + description = "The default editor for this user."; + }; + useZshTheme = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to apply the system Zsh theme."; + }; + useNvimPlugins = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to apply the system Neovim configuration."; + }; + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether this user account is enabled on this system."; + }; + }; + }; +in +{ + + options.athenix.users = lib.mkOption { + type = lib.types.attrsOf userSubmodule; + default = { }; + description = "User accounts configuration. Set enable=true for users that should exist on this system."; + }; + + config = { + # Merge user definitions from users.nix with options from external user.nix modules + # External options take precedence over users.nix (which uses lib.mkDefault) + athenix.users = lib.mapAttrs ( + name: user: + user + // { + description = lib.mkDefault (user.description or null); + shell = lib.mkDefault (user.shell or null); + extraGroups = lib.mkDefault (user.extraGroups or [ ]); + } + // (externalUserOptions.${name} or { }) + ) accounts; + + # Generate NixOS users + users.users = + let + enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users; + in + lib.mapAttrs ( + name: user: + let + isPlasma6 = config.services.desktopManager.plasma6.enable; + defaultPackages = lib.optionals (isPlasma6 && name != "root") [ pkgs.kdePackages.kate ]; + finalPackages = lib.subtractLists user.excludePackages (defaultPackages ++ user.extraPackages); + in + { + inherit (user) isNormalUser extraGroups hashedPassword; + description = if user.description != null then user.description else lib.mkDefault ""; + openssh.authorizedKeys.keys = user.opensshKeys; + packages = finalPackages; + shell = if user.shell != null then user.shell else pkgs.bash; + } + ) enabledAccounts; + + # Home Manager configs per user + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + extraSpecialArgs = { + osConfig = config; + inherit inputs; + }; + + users = + let + enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users; + in + lib.mapAttrs ( + name: user: + let + # Resolve external module paths + hasExternal = user.external != null; + externalPath = resolveExternalPath user.external; + userNixPath = if externalPath != null then externalPath + "/user.nix" else null; + hasExternalUser = isValidPath userNixPath; + + # Import external user.nix for home-manager (filter out athenix.* options) + externalUserModule = + if hasExternalUser then + let + fullModule = import userNixPath { inherit inputs; }; + in + # Only pass through non-athenix options to home-manager + { + config, + lib, + pkgs, + osConfig, + ... + }: + let + evaluated = fullModule { + inherit + config + lib + pkgs + osConfig + ; + }; + in + lib.filterAttrs (attrName: _: attrName != "athenix") evaluated + else + { }; + + # Common imports based on user flags + commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [ + (import ../sw/nvim.nix { inherit user; }) + ]; + + # Build imports list + allImports = user.extraImports ++ commonImports ++ lib.optional hasExternalUser externalUserModule; + in + lib.mkMerge [ + { + imports = allImports; + + # Always set these required options + home.username = name; + home.homeDirectory = if name == "root" then "/root" else "/home/${name}"; + home.stateVersion = "25.11"; + } + (lib.mkIf (!hasExternal) { + # For local users only, add their packages + home.packages = user.homePackages; + }) + ] + ) enabledAccounts; + }; + }; +} diff --git a/variants/default.nix b/variants/default.nix new file mode 100644 index 0000000..8c4fcc7 --- /dev/null +++ b/variants/default.nix @@ -0,0 +1,29 @@ +# ============================================================================ +# Host Types Module +# ============================================================================ +# This module exports all available host types as an attribute set. +# Each type is a NixOS module function that takes { inputs } and returns +# a module configuration. + +{ inputs }: +let + inherit (builtins) readDir attrNames; + lib = inputs.nixpkgs.lib; + inherit (lib) filterAttrs removeSuffix genAttrs; + + files = readDir ./.; + + # Keep only regular *.nix files except default.nix + nixFiles = + filterAttrs + (name: type: + type == "regular" + && lib.hasSuffix ".nix" name + && name != "default.nix") + files; + + moduleNames = map (name: removeSuffix ".nix" name) (attrNames nixFiles); +in +genAttrs moduleNames + (name: + import ./${name}.nix { inherit inputs; }) diff --git a/variants/nix-desktop.nix b/variants/nix-desktop.nix new file mode 100644 index 0000000..2455eba --- /dev/null +++ b/variants/nix-desktop.nix @@ -0,0 +1,51 @@ +# ============================================================================ +# Desktop Configuration +# ============================================================================ +# Hardware and boot configuration for standard desktop workstations. +# Includes Intel CPU support and NVMe storage. + +{ inputs, ... }: +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe SSD support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.swapSize = lib.mkDefault "16G"; + athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +} diff --git a/variants/nix-ephemeral.nix b/variants/nix-ephemeral.nix new file mode 100644 index 0000000..51e46c0 --- /dev/null +++ b/variants/nix-ephemeral.nix @@ -0,0 +1,67 @@ +# ============================================================================ +# Ephemeral/Diskless System Configuration +# ============================================================================ +# Configuration for systems that run entirely from RAM without persistent storage. +# Suitable for kiosks, netboot clients, and stateless workstations. +# All data is lost on reboot. + +{ inputs, ... }: +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Ephemeral Configuration ========== + # No persistent storage - everything runs from RAM + athenix.host.filesystem.swapSize = lib.mkForce "0G"; + athenix.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device + athenix.host.buildMethods = lib.mkDefault [ + "iso" # Live ISO image + "ipxe" # Network boot + ]; + + # Disable disk management for RAM-only systems + disko.enableConfig = lib.mkForce false; + + # Define tmpfs root filesystem + fileSystems."/" = { + device = "none"; + fsType = "tmpfs"; + options = [ + "defaults" + "size=50%" + "mode=755" + ]; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "stateless-kiosk"; +} diff --git a/variants/nix-laptop.nix b/variants/nix-laptop.nix new file mode 100644 index 0000000..6d9ed5c --- /dev/null +++ b/variants/nix-laptop.nix @@ -0,0 +1,64 @@ +# ============================================================================ +# Laptop Configuration +# ============================================================================ +# Hardware and boot configuration for laptop systems with mobile features. +# Includes power management, lid switch handling, and Intel graphics fixes. + +{ inputs, ... }: +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "thunderbolt" # Thunderbolt support + "nvme" # NVMe SSD support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + "i915.enable_psr=0" # Disable Panel Self Refresh (stability) + "i915.enable_dc=0" # Disable display power saving + "i915.enable_fbc=0" # Disable framebuffer compression + ]; + + # ========== Hardware Configuration ========== + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + athenix.host.filesystem.swapSize = lib.mkDefault "34G"; # Larger swap for hibernation + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + + # ========== Power Management ========== + services.upower.enable = lib.mkDefault true; + services.logind.settings = { + Login = { + HandleLidSwitch = "suspend"; + HandleLidSwitchExternalPower = "suspend"; + HandleLidSwitchDocked = "ignore"; + }; + }; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +} diff --git a/variants/nix-lxc.nix b/variants/nix-lxc.nix new file mode 100644 index 0000000..cecb409 --- /dev/null +++ b/variants/nix-lxc.nix @@ -0,0 +1,61 @@ +# ============================================================================ +# Proxmox LXC Container Configuration +# ============================================================================ +# Configuration for lightweight Linux containers running in Proxmox. +# Disables boot/disk management and enables remote development support. + +{ inputs, ... }: +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + inputs.vscode-server.nixosModules.default + "${modulesPath}/virtualisation/proxmox-lxc.nix" + ]; + + # ========== Nix Configuration ========== + nix.settings.trusted-users = [ + "root" + "engr-ugaif" + ]; + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + + # ========== Container-Specific Configuration ========== + boot.isContainer = true; + boot.loader.systemd-boot.enable = lib.mkForce false; # No bootloader in container + disko.enableConfig = lib.mkForce false; # No disk management in container + console.enable = true; + + # Allow getty to work in containers + systemd.services."getty@".unitConfig.ConditionPathExists = [ + "" + "/dev/%I" + ]; + + # Suppress unnecessary systemd units for containers + systemd.suppressedSystemUnits = [ + "dev-mqueue.mount" + "sys-kernel-debug.mount" + "sys-fs-fuse-connections.mount" + ]; + + # ========== Remote Development ========== + services.vscode-server.enable = true; + + # ========== System Configuration ========== + system.stateVersion = "25.11"; + athenix.host.buildMethods = lib.mkDefault [ + "lxc" # LXC container tarball + "proxmox" # Proxmox VMA archive + ]; + + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "headless"; +} diff --git a/variants/nix-surface.nix b/variants/nix-surface.nix new file mode 100644 index 0000000..53f74c4 --- /dev/null +++ b/variants/nix-surface.nix @@ -0,0 +1,69 @@ +# ============================================================================ +# Microsoft Surface Tablet Configuration +# ============================================================================ +# Hardware configuration for Surface Go tablets in kiosk mode. +# Uses nixos-hardware module and older kernel for Surface-specific drivers. + +{ inputs, ... }: +{ + config, + lib, + pkgs, + modulesPath, + ... +}: +let + # Use older kernel version for better Surface Go compatibility + refSystem = inputs.nixpkgs-old-kernel.lib.nixosSystem { + system = pkgs.stdenv.hostPlatform.system; + modules = [ inputs.nixos-hardware.nixosModules.microsoft-surface-go ]; + }; + refKernelPackages = refSystem.config.boot.kernelPackages; +in +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + inputs.nixos-hardware.nixosModules.microsoft-surface-go + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "nvme" # NVMe support (though Surface uses eMMC) + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + "intel_ipu3_imgu" # Intel camera image processing + "intel_ipu3_isys" # Intel camera sensor interface + "fbcon=map:1" # Framebuffer console mapping + "i915.enable_psr=0" # Disable Panel Self Refresh (breaks resume) + "i915.enable_dc=0" # Disable display power saving + ]; + + # Use older kernel for better Surface hardware support + boot.kernelPackages = lib.mkForce refKernelPackages; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.swapSize = lib.mkDefault "8G"; + athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; # eMMC storage # eMMC storage + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "tablet-kiosk"; # Touch-optimized kiosk mode +} diff --git a/variants/nix-wsl.nix b/variants/nix-wsl.nix new file mode 100644 index 0000000..f13787a --- /dev/null +++ b/variants/nix-wsl.nix @@ -0,0 +1,53 @@ +# ============================================================================ +# Windows Subsystem for Linux (WSL) Configuration +# ============================================================================ +# Configuration for NixOS running in WSL2 on Windows. +# Integrates with nixos-wsl for WSL-specific functionality. + +{ inputs, ... }: +{ + lib, + config, + ... +}: +{ + imports = [ + inputs.nixos-wsl.nixosModules.default + inputs.vscode-server.nixosModules.default + ]; + + # ========== Options ========== + options.athenix.host.wsl.user = lib.mkOption { + type = lib.types.str; + default = "engr-ugaif"; + description = "The default user to log in as in WSL."; + }; + + config = { + # ========== WSL Configuration ========== + wsl.enable = true; + # Use forUser if set, otherwise fall back to wsl.user option + wsl.defaultUser = + if config.athenix.forUser != null then config.athenix.forUser else config.athenix.host.wsl.user; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "headless"; + + # ========== Remote Development ========== + services.vscode-server.enable = true; + + # ========== Disable Irrelevant Systems ========== + # WSL doesn't use traditional boot or disk management + disko.enableConfig = lib.mkForce false; + boot.loader.systemd-boot.enable = lib.mkForce false; + boot.loader.grub.enable = lib.mkForce false; + + # WSL manages its own networking + systemd.network.enable = lib.mkForce false; + + # Provide dummy values for required options from boot.nix + athenix.host.filesystem.device = "/dev/null"; + athenix.host.filesystem.swapSize = "0G"; + }; +} diff --git a/variants/nix-zima.nix b/variants/nix-zima.nix new file mode 100644 index 0000000..1174ff0 --- /dev/null +++ b/variants/nix-zima.nix @@ -0,0 +1,50 @@ +# ============================================================================ +# Desktop Configuration +# ============================================================================ +# Hardware and boot configuration for standard desktop workstations. +# Includes Intel CPU support and NVMe storage. + +{ inputs, ... }: +{ + config, + lib, + modulesPath, + ... +}: +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + # ========== Boot Configuration ========== + + boot.initrd.availableKernelModules = [ + "xhci_pci" # USB 3.0 support + "usb_storage" # USB storage devices + "sd_mod" # SD card support + "sdhci_pci" # SD card host controller + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support + boot.extraModulePackages = [ ]; + boot.kernelParams = [ + "quiet" # Minimal boot messages + "splash" # Show Plymouth boot splash + "boot.shell_on_fail" # Emergency shell on boot failure + "udev.log_priority=3" # Reduce udev logging + "rd.systemd.show_status=auto" # Show systemd status during boot + ]; + + # ========== Filesystem Configuration ========== + athenix.host.filesystem.useSwap = lib.mkDefault false; + athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; + athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ]; + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + # ========== Hardware Configuration ========== + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + # ========== Software Profile ========== + athenix.sw.enable = lib.mkDefault true; + athenix.sw.type = lib.mkDefault "desktop"; +}