diff --git a/README.md b/README.md index 9cac9e3..e39934c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ nix-laptop = { ### Using External Flakes for User Configuration -Users can manage their own Home Manager configuration in a separate flake repository. To use this: +Users can manage their own configuration (both Home Manager and System-level settings) in a separate flake repository. To use this: 1. Open `users.nix`. 2. In the user's configuration block, set the `flakeUrl` option: @@ -90,11 +90,11 @@ hdh20267 = { }; ``` -The external flake must provide a `homeManagerModules.default` output. Note that using this feature may require running `update-system --impure` if the flake is not locked in the system's `flake.lock`. +The external flake must provide a `nixosModules.default` output. This module is imported into the system configuration, allowing the user to override their own system settings (like `shell`, `extraGroups`) and define their Home Manager configuration. ### Using External Flakes for System Configuration -You can also override the system-level configuration for a specific host using an external flake. This is useful for adding system services (like Docker), changing boot parameters, or installing system-wide packages that are not in the standard image. +You can also override the system-level configuration for a specific host using an external flake. This is useful for adding system services (like Docker), changing boot parameters, installing system-wide packages, or even overriding hardware settings (like swap size) without modifying `inventory.nix`. 1. Open `inventory.nix`. 2. In the `devices` override for the host, set the `flakeUrl`: @@ -105,48 +105,57 @@ nix-laptop = { devices = { "2" = { flakeUrl = "github:myuser/my-system-config"; - # You can still combine this with other overrides - swapSize = "64G"; }; }; }; ``` -The external flake must provide a `nixosModules.default` output. +The external flake must provide a `nixosModules.default` output. Any configuration defined in that module will be merged with the host's configuration. ## External Flake Templates If you are creating a new flake to use with `flakeUrl`, use these templates as a starting point. -### Home Manager Flake (for `users.nix`) +### User Flake (for `users.nix`) -Use this for user-specific dotfiles, shell configuration, and user packages. +Use this for user-specific dotfiles, shell configuration, and user packages. It can also override system-level user settings. + +Note that `inputs` are omitted. This ensures the flake uses the exact same `nixpkgs` version as the main system, preventing version drift and saving disk space. ```nix { - description = "My Home Manager Configuration"; + description = "My User Configuration"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - # home-manager is not strictly required as an input if you only export a module, - # but it's good practice for standalone testing. - }; + # No inputs needed! We use the system's packages. - outputs = { self, nixpkgs, ... }: { + outputs = { self }: { # This output is what nixos-systems looks for - homeManagerModules.default = { pkgs, ... }: { - home.stateVersion = "25.11"; + nixosModules.default = { pkgs, lib, ... }: { - home.packages = with pkgs; [ - ripgrep - bat - fzf - ]; + # 1. Override System-Level User Settings + modules.users.accounts.hdh20267 = { + shell = pkgs.fish; + extraGroups = [ "docker" ]; + }; + + # Enable programs needed for the shell + programs.fish.enable = true; - programs.git = { - enable = true; - userName = "My Name"; - userEmail = "me@example.com"; + # 2. Define Home Manager Configuration + home-manager.users.hdh20267 = { pkgs, ... }: { + home.stateVersion = "25.11"; + + home.packages = with pkgs; [ + ripgrep + bat + fzf + ]; + + programs.git = { + enable = true; + userName = "My Name"; + userEmail = "me@example.com"; + }; }; }; }; @@ -161,17 +170,21 @@ Use this for host-specific system services, hardware tweaks, or root-level packa { description = "My System Configuration Override"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; - }; + # No inputs needed! We use the system's packages. - outputs = { self, nixpkgs, ... }: { + outputs = { self }: { # This output is what nixos-systems looks for - nixosModules.default = { pkgs, ... }: { + nixosModules.default = { pkgs, lib, ... }: { environment.systemPackages = [ pkgs.docker ]; virtualisation.docker.enable = true; + # Example: Override hardware settings defined in the main repo + host.filesystem.swapSize = lib.mkForce "64G"; + + # Example: Enable specific users + modules.users.enabledUsers = [ "myuser" ]; + # Example: Add a custom binary cache nix.settings.substituters = [ "https://nix-community.cachix.org" ]; nix.settings.trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; diff --git a/flake.nix b/flake.nix index a671080..8244ab8 100644 --- a/flake.nix +++ b/flake.nix @@ -1,21 +1,41 @@ { + # ============================================================================ + # Flake Entry Point + # ============================================================================ + # This file defines the inputs (dependencies) and outputs (configurations) + # for the NixOS systems. It ties together the hardware, software, and user + # configurations into deployable systems. + inputs = { + # Core NixOS package repository (Release 25.11) nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + + # Older kernel packages for Surface compatibility if needed nixpkgs-old-kernel.url = "github:NixOS/nixpkgs/nixos-25.05"; + + # Home Manager for user environment management home-manager = { url = "github:nix-community/home-manager/release-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; + + # Disko for declarative disk partitioning disko = { url = "github:nix-community/disko"; inputs.nixpkgs.follows = "nixpkgs"; }; + + # Hardware quirks and configurations nixos-hardware.url = "github:NixOS/nixos-hardware/master"; + + # Neovim configuration lazyvim-nixvim.url = "github:azuwis/lazyvim-nixvim"; }; outputs = inputs@{ self, nixpkgs, nixpkgs-old-kernel, home-manager, disko, lazyvim-nixvim, nixos-hardware,... }: { + # Formatter for 'nix fmt' formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt-rfc-style; + # Generate NixOS configurations from hosts/default.nix nixosConfigurations = import ./hosts { inherit inputs; }; }; } diff --git a/hosts/boot.nix b/hosts/boot.nix index 767e150..0a1483d 100644 --- a/hosts/boot.nix +++ b/hosts/boot.nix @@ -4,6 +4,13 @@ { config, lib, ... }: +# ============================================================================ +# Boot & Storage Configuration +# ============================================================================ +# This module defines the Disko partition layout and bootloader settings. +# It exposes 'host.filesystem' options to allow per-host overrides of +# the target device and swap size. + { options.host = { filesystem = { @@ -19,6 +26,7 @@ }; config = { + # Enable Disko for declarative partitioning disko.enableConfig = true; disko.devices = { @@ -28,6 +36,7 @@ content = { type = "gpt"; partitions = { + # EFI System Partition ESP = { name = "ESP"; label = "BOOT"; @@ -42,6 +51,7 @@ }; }; + # Swap Partition (size configurable per host) swap = { name = "swap"; label = "swap"; @@ -49,6 +59,7 @@ content = { type = "swap"; }; }; + # Root Partition (takes remaining space) root = { name = "root"; label = "root"; @@ -65,7 +76,7 @@ }; }; - # Bootloader. + # Bootloader Configuration boot = { loader.systemd-boot.enable = true; loader.efi.canTouchEfiVariables = true; diff --git a/hosts/default.nix b/hosts/default.nix index 08445de..c264086 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -1,11 +1,21 @@ { inputs, hosts ? import ../inventory.nix, ... }: +# ============================================================================ +# Host Generator +# ============================================================================ +# This file contains the logic to generate NixOS configurations for all hosts +# defined in inventory.nix. It handles: +# 1. Common module imports (boot, users, software). +# 2. Host-specific overrides (filesystem, enabled users). +# 3. External flake integration for system overrides. + let nixpkgs = inputs.nixpkgs; lib = nixpkgs.lib; home-manager = inputs.home-manager; disko = inputs.disko; + # Modules shared by all hosts commonModules = [ ./boot.nix ./user-config.nix @@ -29,7 +39,23 @@ let } ]; + # Helper to create a single NixOS system configuration mkHost = { hostName, system ? "x86_64-linux", extraModules ? [ ] }: + let + # Load users.nix to find external user flakes + # We use legacyPackages to evaluate the simple data structure of users.nix + pkgs = nixpkgs.legacyPackages.${system}; + usersData = import ../users.nix { inherit pkgs; }; + accounts = usersData.modules.users.accounts or {}; + + # Extract flakeUrls and convert to modules + userFlakeModules = lib.mapAttrsToList (name: user: + if (user ? flakeUrl && user.flakeUrl != "") then + (builtins.getFlake user.flakeUrl).nixosModules.default + else + {} + ) accounts; + in lib.nixosSystem { inherit system; @@ -37,13 +63,14 @@ let modules = commonModules + ++ userFlakeModules ++ extraModules ++ [ { networking.hostName = hostName; } ]; }; - # Function to generate a set of hosts + # Function to generate a set of hosts based on inventory count and overrides mkHostGroup = { prefix, count, system ? "x86_64-linux", extraModules ? [], deviceOverrides ? {} }: lib.listToAttrs (map (i: { name = "${prefix}${toString i}"; @@ -62,7 +89,7 @@ let then externalFlake.nixosModules.default else {}; - # Config override module + # Config override module (filesystem, users) overrideModule = { ... }: let # Remove special keys that are not filesystem options diff --git a/hosts/user-config.nix b/hosts/user-config.nix index 265412a..f1c3fec 100644 --- a/hosts/user-config.nix +++ b/hosts/user-config.nix @@ -1,5 +1,14 @@ { pkgs, config, lib, ... }: + +# ============================================================================ +# 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 + # Submodule defining the structure of a user account userSubmodule = lib.types.submodule { options = { isNormalUser = lib.mkOption { type = lib.types.bool; default = true; }; @@ -12,6 +21,7 @@ let extraImports = lib.mkOption { type = lib.types.listOf lib.types.path; default = []; }; flakeUrl = lib.mkOption { type = lib.types.str; default = ""; description = "URL of a flake to import Home Manager configuration from (e.g. github:user/dotfiles)."; }; 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."; }; }; }; in @@ -19,7 +29,7 @@ in options.modules.users = { shell = lib.mkOption { type = lib.types.package; - default = pkgs.zsh; + default = pkgs.bash; description = "The default shell for users."; }; accounts = lib.mkOption { @@ -35,6 +45,7 @@ in }; config = { + # Default enabled users (always present) modules.users.enabledUsers = [ "root" "engr-ugaif" ]; # Generate NixOS users @@ -53,7 +64,7 @@ in description = if user.description != null then user.description else lib.mkDefault ""; openssh.authorizedKeys.keys = user.opensshKeys; packages = finalPackages; - shell = config.modules.users.shell; + shell = if user.shell != null then user.shell else config.modules.users.shell; } ) enabledAccounts; @@ -68,8 +79,7 @@ in enabledAccounts = lib.filterAttrs (name: _: lib.elem name config.modules.users.enabledUsers) config.modules.users.accounts; in lib.mapAttrs (name: user: { ... }: { - imports = user.extraImports ++ [ ../sw/theme.nix ../sw/nvim.nix ] ++ - (lib.optional (user.flakeUrl != "") (builtins.getFlake user.flakeUrl).homeManagerModules.default); + imports = user.extraImports ++ [ ../sw/theme.nix ../sw/nvim.nix ]; home.username = name; home.homeDirectory = if name == "root" then "/root" else "/home/${name}"; home.stateVersion = "25.11"; diff --git a/inventory.nix b/inventory.nix index 65394a1..122a4fb 100644 --- a/inventory.nix +++ b/inventory.nix @@ -1,4 +1,22 @@ { + # ============================================================================ + # Fleet Inventory + # ============================================================================ + # This file defines the types of hosts and their counts. It is used by + # hosts/default.nix to generate the full set of NixOS configurations. + # + # Structure: + # = { + # count = ; # Number of hosts to generate (e.g., nix-laptop1, nix-laptop2) + # devices = { # Per-device overrides + # "" = { + # extraUsers = [ ... ]; # Users enabled on this specific device + # flakeUrl = "..."; # Optional external system flake for full override + # ... # Other hardware/filesystem overrides + # }; + # }; + # }; + # Laptop Configuration # Base specs: NVMe drive, 34G Swap nix-laptop = { diff --git a/sw/default.nix b/sw/default.nix index b90dabd..f53a7fd 100644 --- a/sw/default.nix +++ b/sw/default.nix @@ -1,5 +1,12 @@ { config, lib, pkgs, inputs, ... }: +# ============================================================================ +# Software Module Entry Point +# ============================================================================ +# This module manages the software configuration for the system. It provides +# options to select the system type ('desktop' or 'kiosk') and handles +# the conditional importation of the appropriate sub-modules. + with lib; let @@ -46,6 +53,7 @@ in git oh-my-posh inputs.lazyvim-nixvim.packages.${stdenv.hostPlatform.system}.nvim + # Custom update script (writeShellScriptBin "update-system" '' HOSTNAME=$(hostname) FLAKE_URI="github:UGA-Innovation-Factory/nixos-systems" @@ -63,6 +71,7 @@ in '') ]; } + # Import Desktop or Kiosk modules based on type (mkIf (cfg.type == "desktop") (import ./desktop { inherit config lib pkgs inputs; })) (mkIf (cfg.type == "kiosk") (import ./kiosk { inherit config lib pkgs inputs; })) ]); diff --git a/sw/nvim.nix b/sw/nvim.nix index d4e7c99..47feade 100644 --- a/sw/nvim.nix +++ b/sw/nvim.nix @@ -1,5 +1,11 @@ { pkgs, ... }: { + # ============================================================================ + # Neovim Configuration + # ============================================================================ + # This module configures Neovim, specifically setting up TreeSitter parsers + # to ensure syntax highlighting works correctly. + # https://github.com/nvim-treesitter/nvim-treesitter#i-get-query-error-invalid-node-type-at-position xdg.configFile."nvim/parser".source = let diff --git a/sw/python.nix b/sw/python.nix index 2fcdae9..3f9d2f9 100644 --- a/sw/python.nix +++ b/sw/python.nix @@ -1,5 +1,12 @@ { config, lib, pkgs, ... }: +# ============================================================================ +# Python Environment +# ============================================================================ +# This module provides Python development tools. It installs 'pixi' and 'uv' +# for project-based dependency management, rather than installing global +# Python packages which can lead to conflicts. + with lib; let diff --git a/sw/theme.nix b/sw/theme.nix index 5cfbb4a..24e40d5 100644 --- a/sw/theme.nix +++ b/sw/theme.nix @@ -1,5 +1,11 @@ { pkgs, config, osConfig, lib, ... }: +# ============================================================================ +# Shell Theme Configuration +# ============================================================================ +# This module configures the shell environment (Zsh, Oh My Posh) for users. +# It is imported by default for all users to ensure a consistent experience. + let # Fetch upstream OMP theme once jyumppTheme = pkgs.fetchurl { @@ -15,11 +21,9 @@ let isRoot = config.home.username == "root"; themeFile = if isRoot then jyumppRootTheme else jyumppTheme; - - isZsh = osConfig.modules.users.shell == pkgs.zsh; in { - config = lib.mkIf isZsh { + config = { programs.zsh = { enable = true; diff --git a/users.nix b/users.nix index 649129a..097250d 100644 --- a/users.nix +++ b/users.nix @@ -1,5 +1,12 @@ { pkgs, ... }: { + # ============================================================================ + # User Definitions + # ============================================================================ + # This file defines the available user accounts. These accounts are NOT + # enabled by default on all systems. They must be enabled via the + # 'modules.users.enabledUsers' option in inventory.nix or system flakes. + # Define the users here using the new option # To generate a password hash, run: mkpasswd -m sha-512 modules.users.accounts = { @@ -17,6 +24,7 @@ description = "Hunter Halloran"; extraGroups = [ "networkmanager" "wheel" ]; homePackages = [ pkgs.ghostty ]; + shell = pkgs.zsh; # Example of using an external flake for configuration: # flakeUrl = "github:hdh20267/dotfiles"; };