diff --git a/README.md b/README.md index 9316dc8..e68cf4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # UGA Innovation Factory - NixOS Systems -This repository contains the NixOS configuration for the Innovation Factory's fleet of laptops, desktops, and surface tablets. +This repository contains the NixOS configuration for the Innovation Factory's fleet of laptops, desktops, and surface tablets. It provides a declarative, reproducible system configuration using Nix flakes. + +## Table of Contents + +- [Repository Structure](#repository-structure) +- [Configuration Namespace](#configuration-namespace) +- [Prerequisites](#prerequisites) +- [Quick Start](#quick-start) +- [Building Artifacts](#building-artifacts-isos-lxc-proxmox) +- [Configuration Guide](#configuration-guide) + - [User Management](#user-management) + - [Host Configuration](#host-configuration) +- [External Flake Templates](#external-flake-templates) +- [Development](#development) ## Repository Structure @@ -10,6 +23,27 @@ This repository contains the NixOS configuration for the Innovation Factory's fl - **`hosts/`**: Contains the logic for generating host configurations and hardware-specific types. - **`sw/`**: Software modules (Desktop, Kiosk, Python, Neovim, etc.). +## Configuration Namespace + +All UGA Innovation Factory-specific NixOS options are organized under the `ugaif` namespace to clearly distinguish them from standard NixOS options. The main option groups are: + +- **`ugaif.host`**: Hardware and host-level configuration + - `ugaif.host.filesystem`: Disk device and swap size settings + - `ugaif.host.buildMethods`: Supported artifact build methods (ISO, LXC, etc.) + - `ugaif.host.wsl`: WSL-specific configuration + +- **`ugaif.sw`**: Software and system type configuration + - `ugaif.sw.enable`: Enable the software configuration module + - `ugaif.sw.type`: System type (`desktop`, `tablet-kiosk`, `headless`, `stateless-kiosk`) + - `ugaif.sw.kioskUrl`: URL for kiosk mode browsers + - `ugaif.sw.python`: Python development tools settings + - `ugaif.sw.remoteBuild`: Remote build configuration + +- **`ugaif.users`**: User account management + - `ugaif.users.accounts`: User account definitions + - `ugaif.users.enabledUsers`: List of users enabled on the system + - `ugaif.users.shell`: Default shell for users + ## Prerequisites To work with this repository on a non-NixOS system (like macOS or another Linux distro), you need to install Nix. We recommend the Determinate Systems installer for a reliable and feature-complete setup (including Flakes support out of the box). @@ -20,42 +54,72 @@ curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix ## Quick Start -### Updating the System +### For End Users: Updating Your System -The system includes a utility script `update-system` that handles rebuilding and switching configurations. It automatically detects if it is running on a Surface tablet and offloads the build to a more powerful host if necessary. - -To apply changes to the current system: +The easiest way to update your system is using the included `update-system` utility: ```bash update-system ``` -This command pulls the latest configuration from GitHub and rebuilds the system. +This command: +- Pulls the latest configuration from GitHub +- Rebuilds the system with any changes +- Automatically uses remote builders if you're on a Surface tablet -If your configuration uses external flakes (e.g., via `flakeUrl`), you may need to allow impure evaluation: +If you use external flakes (personal dotfiles), add the `--impure` flag: ```bash update-system --impure ``` -### Manual Rebuilds +### For Administrators: Managing the Fleet -If you need to rebuild manually or target a specific host: +#### Applying Configuration Changes + +After modifying files in this repository: ```bash -# Local build -sudo nixos-rebuild switch --flake . +# Test the configuration builds +nix flake check -# Build for a specific host -sudo nixos-rebuild switch --flake .#nix-laptop1 +# Push changes to GitHub +git add . +git commit -m "Description of changes" +git push + +# Users can then run: update-system ``` -### Updating Flake Inputs +#### Manual Rebuilds -To update the lockfile (nixpkgs, home-manager versions, etc.): +For testing or emergency fixes: ```bash +# Rebuild current host +sudo nixos-rebuild switch --flake . + +# Rebuild specific host +sudo nixos-rebuild switch --flake .#nix-laptop1 + +# Test without switching +sudo nixos-rebuild test --flake .#nix-laptop1 +``` + +#### Updating Dependencies + +Update nixpkgs, home-manager, and other flake inputs: + +```bash +# Update all inputs nix flake update + +# Update specific input +nix flake lock --update-input nixpkgs + +# After updating, test and commit the new flake.lock +git add flake.lock +git commit -m "Update flake inputs" ``` ## Building Artifacts (ISOs, LXC, Proxmox) @@ -95,103 +159,159 @@ nix build .#proxmox-nix-lxc1 ## Configuration Guide -### Adding a New User +### User Management + +#### Adding a New User 1. Open `users.nix`. -2. Add a new entry to `modules.users.accounts`. -3. Generate a hashed password using `mkpasswd -m sha-512` (requires `whois` package or similar). - - Hashed passwords are intended for shared credentials as a minimal layer of safety; do not treat them as secure storage for personal secrets (use per-user secrets managed outside the flake instead). -4. Commit and push. +2. Add a new entry to `ugaif.users.accounts`: -### Assigning Users to Hosts +```nix +ugaif.users.accounts = { + # ... existing users ... + + newuser = { + description = "New User Name"; + extraGroups = [ "networkmanager" "wheel" ]; + hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512 + opensshKeys = [ "ssh-ed25519 AAAA..." ]; + }; +}; +``` -By default, only `root` and `engr-ugaif` are enabled. To enable a specific student user on a specific device: +3. Generate a hashed password using `mkpasswd -m sha-512` (requires `whois` package). +4. Commit and push the changes. + +**Note:** Hashed passwords are for shared credentials only. For personal secrets, use external secret management. + +#### Assigning Users to Hosts + +By default, only `root` and `engr-ugaif` are enabled on all systems. To enable additional users on specific devices: 1. Open `inventory.nix`. 2. Locate the host type (e.g., `nix-laptop`). -3. Add or update the `devices` section for the specific index: +3. Add the user to the device's `extraUsers` list: ```nix nix-laptop = { count = 2; devices = { - "1" = { extraUsers = [ "student_username" ]; }; + "1".extraUsers = [ "newuser" "hdh20267" ]; }; }; ``` -### Using External Flakes for User Configuration +#### Using External Flakes for User Configuration -### Customizing the Kiosk URL for Surfaces +Users can manage their own dotfiles and configuration in a separate flake repository. This allows full control over shell configuration, editor settings, and Home Manager modules. -Surface tablets run the kiosk configuration and delegate the Chromium launch URL via `sw.kioskUrl`. You can set a per-device URL directly from `inventory.nix` by providing a `modules` override for the device entry: +**Security Note:** External flakes run with the same privileges as the system configuration. Only use trusted flakes and pin to specific commits. + +To enable this: + +1. Open `users.nix`. +2. Set the `flakeUrl` for the user: + +```nix +hdh20267 = { + description = "User Name"; + extraGroups = [ "networkmanager" "wheel" ]; + hashedPassword = "$6$..."; + flakeUrl = "github:hdh20267/dotfiles"; + + # Optional: Disable system defaults + useZshTheme = false; # Manage your own Zsh config + useNvimPlugins = false; # Manage your own Neovim config +}; +``` + +The external flake must provide a `nixosModules.default` output. See [User Flake Template](#user-flake-for-usersnix) below for an example. + +### Host Configuration + +#### Adding a New Host + +To add more devices of an existing type: + +1. Open `inventory.nix`. +2. Increment the `count` for the host type: + +```nix +nix-laptop.count = 3; # Creates nix-laptop1, nix-laptop2, nix-laptop3 +``` + +#### Device-Specific Configuration Overrides + +You can customize individual devices in `inventory.nix` using either shortcut keys or direct configuration. + +**Shortcut Keys** (for common settings): +- `extraUsers` → Sets `ugaif.users.enabledUsers` +- `hostname` → Sets a custom hostname (default: `{type}{number}`) +- `buildMethods` → Sets `ugaif.host.buildMethods` +- `wslUser` → Sets `ugaif.host.wsl.user` +- `flakeUrl` → Imports an external system flake + +**Direct Configuration** (using the `ugaif` namespace or standard NixOS options): + +```nix +nix-laptop = { + count = 2; + devices = { + "1" = { + # Shortcut keys + extraUsers = [ "hdh20267" ]; + hostname = "laptop-special"; + + # UGAIF options + ugaif.host.filesystem.swapSize = "64G"; + ugaif.sw.extraPackages = with pkgs; [ docker vim ]; + + # Standard NixOS options + networking.firewall.enable = false; + services.openssh.enable = true; + }; + }; +}; +``` + +#### Customizing Kiosk URLs for Surface Tablets + +Surface tablets run in kiosk mode with a full-screen Chromium browser. Set the URL per-device: ```nix nix-surface = { count = 3; devices = { - "1" = { - modules = { - sw = { - kioskUrl = "https://ha.factory.uga.edu/surface-1"; - }; - }; - }; + "1".ugaif.sw.kioskUrl = "https://ha.factory.uga.edu/dashboard-1"; + "2".ugaif.sw.kioskUrl = "https://ha.factory.uga.edu/dashboard-2"; }; }; ``` -Any other device attributes (filesystem overrides, extra users) can still sit beside `modules`; they are merged into the generated host configuration so you just need to set the `kioskUrl` value you want to use for that Surface. +#### Using External Flakes for System Configuration -Users can manage their own configuration (both Home Manager and System-level settings) in a separate flake repository. External flakes run with the same privileges as the primary configuration, so audit any flake before pointing to it and pin to a known-good commit when possible. -To use this: - -1. Open `users.nix`. -2. In the user's configuration block, set the `flakeUrl` option: - -```nix -hdh20267 = { - # ... other settings ... - flakeUrl = "github:hdh20267/dotfiles"; -}; -``` - -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. - -You can also opt-out of the default system configurations for Zsh and Neovim if you prefer to manage them entirely yourself: - -* `useZshTheme` (default: `true`): Set to `false` to disable the system-wide Zsh theme and configuration. -* `useNvimPlugins` (default: `true`): Set to `false` to disable the system-wide Neovim plugins and 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, 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`: +For complex system customizations (Docker, custom services, hardware tweaks), use an external flake: ```nix nix-laptop = { count = 2; devices = { - "2" = { - flakeUrl = "github:myuser/my-system-config"; - }; + "2".flakeUrl = "github:myuser/my-system-config"; }; }; ``` -The external flake must provide a `nixosModules.default` output. Any configuration defined in that module will be merged with the host's configuration, so treat these flakes as privileged code and audit them before importing. +The external flake must provide a `nixosModules.default` output. See [System Flake Template](#system-flake-for-inventorynix) below for an example. ## External Flake Templates -If you are creating a new flake to use with `flakeUrl`, use these templates as a starting point. +If you're creating a flake to use with `flakeUrl`, use these templates as starting points. + +**Important:** Do not specify `inputs` in your flake. This ensures your flake uses the exact same `nixpkgs` version as the main system, preventing version drift and saving disk space. ### User Flake (for `users.nix`) -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. +Use this template for user-specific dotfiles, shell configuration, Home Manager modules, and overriding user account settings. ```nix { @@ -204,7 +324,7 @@ Note that `inputs` are omitted. This ensures the flake uses the exact same `nixp nixosModules.default = { pkgs, lib, ... }: { # 1. Override System-Level User Settings - modules.users.accounts.hdh20267 = { + ugaif.users.accounts.hdh20267 = { shell = pkgs.fish; extraGroups = [ "docker" ]; @@ -239,7 +359,7 @@ Note that `inputs` are omitted. This ensures the flake uses the exact same `nixp ### System Flake (for `inventory.nix`) -Use this for host-specific system services, hardware tweaks, or root-level packages. +Use this template for host-specific system services (Docker, databases, web servers), hardware configuration tweaks, or system-wide packages. ```nix { @@ -255,10 +375,10 @@ Use this for host-specific system services, hardware tweaks, or root-level packa virtualisation.docker.enable = true; # Example: Override hardware settings defined in the main repo - host.filesystem.swapSize = lib.mkForce "64G"; + ugaif.host.filesystem.swapSize = lib.mkForce "64G"; # Example: Enable specific users - modules.users.enabledUsers = [ "myuser" ]; + ugaif.users.enabledUsers = [ "myuser" ]; # Example: Add a custom binary cache nix.settings.substituters = [ "https://nix-community.cachix.org" ]; @@ -268,20 +388,93 @@ Use this for host-specific system services, hardware tweaks, or root-level packa } ``` -### Adding a New Host - -1. Open `inventory.nix`. -2. Increment the `count` for the relevant host type. -3. The new host will be named sequentially (e.g., `nix-laptop3`). - ## Development ### Python Environment -The system comes with `pixi` and `uv` for Python project management. It is recommended to use these tools for project-specific environments rather than installing global Python packages. +All systems include `pixi` and `uv` for Python project management. Use these tools for project-specific environments instead of installing global Python packages (which can cause conflicts). + +**Creating a new project with Pixi:** ```bash pixi init my_project cd my_project -pixi add pandas numpy +pixi add pandas numpy matplotlib +pixi run python script.py ``` + +**Using uv for quick tasks:** + +```bash +uv venv +source .venv/bin/activate +uv pip install requests +``` + +### Testing Changes Locally + +Before pushing changes, verify your configuration builds correctly: + +```bash +# Check all configurations +nix flake check + +# Build a specific configuration +nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel + +# Test an ISO build +nix build .#installer-iso-nix-laptop1 +``` + +### Common Configuration Patterns + +#### Adding System Packages to All Hosts + +Edit `sw/desktop/programs.nix` (or the appropriate type directory) to add packages to the base system: + +```nix +basePackages = with pkgs; [ + htop + vim + git + # Add your packages here +]; +``` + +#### Changing Default User Shell + +Edit `users.nix` to set a different shell for a specific user: + +```nix +myuser = { + description = "My User"; + shell = pkgs.fish; # or pkgs.zsh, pkgs.bash + # ... other settings +}; +``` + +Then ensure the program is enabled in the system configuration (usually already done for common shells). + +## Troubleshooting + +### Flake Check Errors + +If `nix flake check` fails, use `--show-trace` for detailed error information: + +```bash +nix flake check --show-trace +``` + +### External Flakes Not Loading + +If using `flakeUrl`, ensure: +1. The flake repository is public or you have SSH access configured +2. The flake has a `nixosModules.default` output +3. You're using `--impure` flag if the flake has impure operations + +### Build Failures on Surface Tablets + +Surface tablets automatically offload builds to remote builders. If builds fail: +1. Verify the remote builder is accessible: `ssh engr-ugaif@nix-builder` +2. Check remote builder has sufficient disk space: `df -h` +3. Try building locally with remote builds disabled by commenting out `ugaif.sw.remoteBuild.enable` diff --git a/assets/plymouth-theme b/assets/plymouth-theme new file mode 160000 index 0000000..8658f4f --- /dev/null +++ b/assets/plymouth-theme @@ -0,0 +1 @@ +Subproject commit 8658f4fb40039ab859f3ec166e0452bb7389e8ee diff --git a/flake.nix b/flake.nix index 15717e8..b5a2325 100644 --- a/flake.nix +++ b/flake.nix @@ -85,7 +85,7 @@ { # Formatter for 'nix fmt' formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style); - + # Generate NixOS configurations from hosts/default.nix nixosConfigurations = hosts.nixosConfigurations; diff --git a/hosts/boot.nix b/hosts/boot.nix index 33006ea..2e0dcae 100644 --- a/hosts/boot.nix +++ b/hosts/boot.nix @@ -8,11 +8,11 @@ # Boot & Storage Configuration # ============================================================================ # This module defines the Disko partition layout and bootloader settings. -# It exposes 'host.filesystem' options to allow per-host overrides of +# It exposes 'ugaif.host.filesystem' options to allow per-host overrides of # the target device and swap size. { - options.host = { + options.ugaif.host = { filesystem = { device = lib.mkOption { type = lib.types.str; @@ -45,7 +45,7 @@ disko.devices = { disk.main = { type = "disk"; - device = config.host.filesystem.device; + device = config.ugaif.host.filesystem.device; content = { type = "gpt"; partitions = { @@ -71,7 +71,7 @@ swap = { name = "swap"; label = "swap"; - size = config.host.filesystem.swapSize; + size = config.ugaif.host.filesystem.swapSize; content = { type = "swap"; }; diff --git a/hosts/default.nix b/hosts/default.nix index 3bfd1dd..c89a09e 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -60,7 +60,7 @@ let # 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 { }; + accounts = usersData.ugaif.users.accounts or { }; # Extract flakeUrls and convert to modules userFlakeModules = lib.mapAttrsToList ( @@ -70,6 +70,14 @@ let else { } ) accounts; + + allModules = + commonModules + ++ userFlakeModules + ++ extraModules + ++ [ + { networking.hostName = hostName; } + ]; in { system = lib.nixosSystem { @@ -77,21 +85,9 @@ let specialArgs = { inherit inputs; }; - modules = - commonModules - ++ userFlakeModules - ++ extraModules - ++ [ - { networking.hostName = hostName; } - ]; + modules = allModules; }; - modules = - commonModules - ++ userFlakeModules - ++ extraModules - ++ [ - { networking.hostName = hostName; } - ]; + modules = allModules; }; # Function to generate a set of hosts based on inventory count and overrides @@ -127,27 +123,22 @@ let overrideModule = { ... }: let - # Remove special keys for filesystem overrides, keep other config attrs - fsConf = builtins.removeAttrs devConf [ + # Extract device-specific config, removing special keys that need custom handling + baseConfig = lib.removeAttrs devConf [ "extraUsers" "flakeUrl" "hostname" - "modules" "buildMethods" "wslUser" ]; - extraConfig = lib.removeAttrs devConf [ - "extraUsers" - "flakeUrl" - "hostname" - "buildMethods" - "wslUser" + # Handle special keys that map to specific ugaif options + specialConfig = lib.mkMerge [ + (lib.optionalAttrs (devConf ? extraUsers) { ugaif.users.enabledUsers = devConf.extraUsers; }) + (lib.optionalAttrs (devConf ? buildMethods) { ugaif.host.buildMethods = devConf.buildMethods; }) + (lib.optionalAttrs (devConf ? wslUser) { ugaif.host.wsl.user = devConf.wslUser; }) ]; in - lib.mkIf hasOverride (lib.recursiveUpdate (lib.recursiveUpdate { - host.filesystem = fsConf; - modules.users.enabledUsers = devConf.extraUsers or [ ]; - } (lib.optionalAttrs (devConf ? buildMethods) { host.buildMethods = devConf.buildMethods; } // lib.optionalAttrs (devConf ? wslUser) { host.wsl.user = devConf.wslUser; })) extraConfig); + lib.mkIf hasOverride (lib.recursiveUpdate baseConfig specialConfig); config = mkHost { hostName = hostName; diff --git a/hosts/types/nix-desktop.nix b/hosts/types/nix-desktop.nix index e930c9d..1ae67d7 100644 --- a/hosts/types/nix-desktop.nix +++ b/hosts/types/nix-desktop.nix @@ -28,16 +28,16 @@ "rd.systemd.show_status=auto" ]; - host.filesystem.swapSize = lib.mkDefault "16G"; - host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; - host.buildMethods = lib.mkDefault [ "installer-iso" ]; + ugaif.host.filesystem.swapSize = lib.mkDefault "16G"; + ugaif.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ]; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; } ) { - modules.sw.enable = true; - modules.sw.type = "desktop"; + ugaif.sw.enable = true; + ugaif.sw.type = "desktop"; } ] diff --git a/hosts/types/nix-ephemeral.nix b/hosts/types/nix-ephemeral.nix index 5deba70..208dec7 100644 --- a/hosts/types/nix-ephemeral.nix +++ b/hosts/types/nix-ephemeral.nix @@ -31,10 +31,13 @@ ]; # Ephemeral setup: No swap, no disk - host.filesystem.swapSize = lib.mkForce "0G"; - host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device - host.buildMethods = lib.mkDefault [ "iso" "ipxe" ]; - + ugaif.host.filesystem.swapSize = lib.mkForce "0G"; + ugaif.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device + ugaif.host.buildMethods = lib.mkDefault [ + "iso" + "ipxe" + ]; + # Disable Disko config since we are running from RAM/ISO disko.enableConfig = lib.mkForce false; @@ -42,7 +45,11 @@ fileSystems."/" = { device = "none"; fsType = "tmpfs"; - options = [ "defaults" "size=50%" "mode=755" ]; + options = [ + "defaults" + "size=50%" + "mode=755" + ]; }; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; @@ -50,7 +57,7 @@ } ) { - modules.sw.enable = true; - modules.sw.type = "stateless-kiosk"; + ugaif.sw.enable = true; + ugaif.sw.type = "stateless-kiosk"; } ] diff --git a/hosts/types/nix-laptop.nix b/hosts/types/nix-laptop.nix index f4621e0..186e3d6 100644 --- a/hosts/types/nix-laptop.nix +++ b/hosts/types/nix-laptop.nix @@ -35,9 +35,9 @@ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; - host.filesystem.swapSize = lib.mkDefault "34G"; - host.buildMethods = lib.mkDefault [ "installer-iso" ]; + ugaif.host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; + ugaif.host.filesystem.swapSize = lib.mkDefault "34G"; + ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ]; # Suspend / logind behavior services.upower.enable = lib.mkDefault true; @@ -51,7 +51,7 @@ } ) { - modules.sw.enable = true; - modules.sw.type = "desktop"; + ugaif.sw.enable = true; + ugaif.sw.type = "desktop"; } ] diff --git a/hosts/types/nix-lxc.nix b/hosts/types/nix-lxc.nix index f651060..c9ddb28 100644 --- a/hosts/types/nix-lxc.nix +++ b/hosts/types/nix-lxc.nix @@ -35,11 +35,14 @@ ]; services.vscode-server.enable = true; system.stateVersion = "25.11"; - host.buildMethods = lib.mkDefault [ "lxc" "proxmox" ]; + ugaif.host.buildMethods = lib.mkDefault [ + "lxc" + "proxmox" + ]; } ) { - modules.sw.enable = true; - modules.sw.type = "headless"; + ugaif.sw.enable = true; + ugaif.sw.type = "headless"; } ] diff --git a/hosts/types/nix-surface.nix b/hosts/types/nix-surface.nix index 8f40d97..4e9daf6 100644 --- a/hosts/types/nix-surface.nix +++ b/hosts/types/nix-surface.nix @@ -43,9 +43,9 @@ boot.kernelPackages = lib.mkForce refKernelPackages; - host.filesystem.swapSize = lib.mkDefault "8G"; - host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; - host.buildMethods = lib.mkDefault [ "installer-iso" ]; + ugaif.host.filesystem.swapSize = lib.mkDefault "8G"; + ugaif.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; + ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ]; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; @@ -53,7 +53,7 @@ ) inputs.nixos-hardware.nixosModules.microsoft-surface-go { - modules.sw.enable = true; - modules.sw.type = "tablet-kiosk"; + ugaif.sw.enable = true; + ugaif.sw.type = "tablet-kiosk"; } ] diff --git a/hosts/types/nix-wsl.nix b/hosts/types/nix-wsl.nix index 7d3dd6e..3db7380 100644 --- a/hosts/types/nix-wsl.nix +++ b/hosts/types/nix-wsl.nix @@ -2,35 +2,38 @@ [ inputs.nixos-wsl.nixosModules.default inputs.vscode-server.nixosModules.default - ({ lib, config, ... }: { - options.host.wsl.user = lib.mkOption { - type = lib.types.str; - default = "engr-ugaif"; - description = "The default user to log in as in WSL."; - }; + ( + { lib, config, ... }: + { + options.ugaif.host.wsl.user = lib.mkOption { + type = lib.types.str; + default = "engr-ugaif"; + description = "The default user to log in as in WSL."; + }; - config = { - wsl.enable = true; - wsl.defaultUser = config.host.wsl.user; - - # Enable the headless software profile - modules.sw.enable = true; - modules.sw.type = "headless"; + config = { + wsl.enable = true; + wsl.defaultUser = config.ugaif.host.wsl.user; - # Fix for VS Code Server in WSL if needed, though vscode-server input exists - services.vscode-server.enable = true; + # Enable the headless software profile + ugaif.sw.enable = true; + ugaif.sw.type = "headless"; - # Disable Disko and Bootloader for WSL - disko.enableConfig = lib.mkForce false; - boot.loader.systemd-boot.enable = lib.mkForce false; - boot.loader.grub.enable = lib.mkForce false; + # Fix for VS Code Server in WSL if needed, though vscode-server input exists + services.vscode-server.enable = true; - # Disable networking for wsl (it manages its own networking) - systemd.network.enable = lib.mkForce false; - - # Provide dummy values for required options from boot.nix - host.filesystem.device = "/dev/null"; - host.filesystem.swapSize = "0G"; - }; - }) + # Disable Disko and Bootloader for WSL + disko.enableConfig = lib.mkForce false; + boot.loader.systemd-boot.enable = lib.mkForce false; + boot.loader.grub.enable = lib.mkForce false; + + # Disable networking for wsl (it manages its own networking) + systemd.network.enable = lib.mkForce false; + + # Provide dummy values for required options from boot.nix + ugaif.host.filesystem.device = "/dev/null"; + ugaif.host.filesystem.swapSize = "0G"; + }; + } + ) ] diff --git a/hosts/user-config.nix b/hosts/user-config.nix index 4eafdf5..dcdb043 100644 --- a/hosts/user-config.nix +++ b/hosts/user-config.nix @@ -77,7 +77,7 @@ let }; in { - options.modules.users = { + options.ugaif.users = { shell = lib.mkOption { type = lib.types.package; default = pkgs.bash; @@ -97,7 +97,7 @@ in config = { # Default enabled users (always present) - modules.users.enabledUsers = [ + ugaif.users.enabledUsers = [ "root" "engr-ugaif" ]; @@ -106,8 +106,8 @@ in users.users = let enabledAccounts = lib.filterAttrs ( - name: _: lib.elem name config.modules.users.enabledUsers - ) config.modules.users.accounts; + name: _: lib.elem name config.ugaif.users.enabledUsers + ) config.ugaif.users.accounts; in lib.mapAttrs ( name: user: @@ -121,7 +121,7 @@ in 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 config.modules.users.shell; + shell = if user.shell != null then user.shell else config.ugaif.users.shell; } ) enabledAccounts; @@ -136,8 +136,8 @@ in users = let enabledAccounts = lib.filterAttrs ( - name: _: lib.elem name config.modules.users.enabledUsers - ) config.modules.users.accounts; + name: _: lib.elem name config.ugaif.users.enabledUsers + ) config.ugaif.users.accounts; in lib.mapAttrs ( name: user: diff --git a/installer/artifacts.nix b/installer/artifacts.nix index c934e09..beea1b4 100644 --- a/installer/artifacts.nix +++ b/installer/artifacts.nix @@ -1,4 +1,9 @@ -{ inputs, hosts, self, system }: +{ + inputs, + hosts, + self, + system, +}: # This file defines the logic for generating various build artifacts (ISOs, Netboot, LXC, etc.) # It exports a set of packages that can be built using `nix build .#` let @@ -9,7 +14,8 @@ let # Creates a self-installing ISO for a specific host configuration # This ISO will automatically partition the disk (using disko) and install the system - mkInstaller = hostName: + mkInstaller = + hostName: let targetConfig = self.nixosConfigurations.${hostName}.config; targetSystem = targetConfig.system.build.toplevel; @@ -18,7 +24,12 @@ let nixpkgs.lib.nixosSystem { inherit system; specialArgs = { - inherit inputs hostName targetSystem diskoScript; + inherit + inputs + hostName + targetSystem + diskoScript + ; hostPlatform = system; }; modules = [ @@ -29,7 +40,8 @@ let }; # Uses nixos-generators to create artifacts like LXC containers, Proxmox VMA, or Live ISOs - mkGenerator = hostName: format: + mkGenerator = + hostName: format: nixos-generators.nixosGenerate { inherit system; specialArgs = { inherit inputs; }; @@ -44,7 +56,8 @@ let # Creates Netboot (iPXE) artifacts using the native NixOS netboot module # Returns a system configuration that includes the netboot module - mkNetboot = hostName: + mkNetboot = + hostName: nixpkgs.lib.nixosSystem { inherit system; specialArgs = { inherit inputs; }; @@ -60,66 +73,110 @@ let hostNames = builtins.attrNames hosts.nixosConfigurations; # Generate installer ISOs for hosts that have "installer-iso" in their buildMethods - installerPackages = lib.listToAttrs (lib.concatMap (name: - let cfg = hosts.nixosConfigurations.${name}; in - if lib.elem "installer-iso" cfg.config.host.buildMethods then [{ - name = "installer-iso-${name}"; - value = (mkInstaller name).config.system.build.isoImage; - }] else [] - ) hostNames); + installerPackages = lib.listToAttrs ( + lib.concatMap ( + name: + let + cfg = hosts.nixosConfigurations.${name}; + in + if lib.elem "installer-iso" cfg.config.ugaif.host.buildMethods then + [ + { + name = "installer-iso-${name}"; + value = (mkInstaller name).config.system.build.isoImage; + } + ] + else + [ ] + ) hostNames + ); # Generate Live ISOs for hosts that have "iso" in their buildMethods - isoPackages = lib.listToAttrs (lib.concatMap (name: - let cfg = hosts.nixosConfigurations.${name}; in - if lib.elem "iso" cfg.config.host.buildMethods then [{ - name = "iso-${name}"; - value = mkGenerator name "iso"; - }] else [] - ) hostNames); + isoPackages = lib.listToAttrs ( + lib.concatMap ( + name: + let + cfg = hosts.nixosConfigurations.${name}; + in + if lib.elem "iso" cfg.config.ugaif.host.buildMethods then + [ + { + name = "iso-${name}"; + value = mkGenerator name "iso"; + } + ] + else + [ ] + ) hostNames + ); # Generate iPXE artifacts (kernel, initrd, script) for hosts that have "ipxe" in their buildMethods - ipxePackages = lib.listToAttrs (lib.concatMap (name: - let cfg = hosts.nixosConfigurations.${name}; in - if lib.elem "ipxe" cfg.config.host.buildMethods then [{ - name = "ipxe-${name}"; - value = - let - build = (mkNetboot name).config.system.build; - in - pkgs.symlinkJoin { - name = "netboot-artifacts-${name}"; - paths = [ - build.netbootRamdisk - build.kernel - build.netbootIpxeScript - ]; - }; - }] else [] - ) hostNames); + ipxePackages = lib.listToAttrs ( + lib.concatMap ( + name: + let + cfg = hosts.nixosConfigurations.${name}; + in + if lib.elem "ipxe" cfg.config.ugaif.host.buildMethods then + [ + { + name = "ipxe-${name}"; + value = + let + build = (mkNetboot name).config.system.build; + in + pkgs.symlinkJoin { + name = "netboot-artifacts-${name}"; + paths = [ + build.netbootRamdisk + build.kernel + build.netbootIpxeScript + ]; + }; + } + ] + else + [ ] + ) hostNames + ); # Generate LXC tarballs for hosts that have "lxc" in their buildMethods - lxcPackages = lib.listToAttrs (lib.concatMap (name: - let cfg = hosts.nixosConfigurations.${name}; in - if lib.elem "lxc" cfg.config.host.buildMethods then [{ - name = "lxc-${name}"; - value = - if cfg.config.boot.isContainer then - cfg.config.system.build.tarball - else - mkGenerator name "lxc"; - }] else [] - ) hostNames); + lxcPackages = lib.listToAttrs ( + lib.concatMap ( + name: + let + cfg = hosts.nixosConfigurations.${name}; + in + if lib.elem "lxc" cfg.config.ugaif.host.buildMethods then + [ + { + name = "lxc-${name}"; + value = + if cfg.config.boot.isContainer then cfg.config.system.build.tarball else mkGenerator name "lxc"; + } + ] + else + [ ] + ) hostNames + ); - proxmoxPackages = lib.listToAttrs (lib.concatMap (name: - let cfg = hosts.nixosConfigurations.${name}; in - if lib.elem "proxmox" cfg.config.host.buildMethods then [{ - name = "proxmox-${name}"; - value = - if cfg.config.boot.isContainer then - cfg.config.system.build.tarball - else - mkGenerator name "proxmox"; - }] else [] - ) hostNames); + proxmoxPackages = lib.listToAttrs ( + lib.concatMap ( + name: + let + cfg = hosts.nixosConfigurations.${name}; + in + if lib.elem "proxmox" cfg.config.ugaif.host.buildMethods then + [ + { + name = "proxmox-${name}"; + value = + if cfg.config.boot.isContainer then cfg.config.system.build.tarball else mkGenerator name "proxmox"; + } + ] + else + [ ] + ) hostNames + ); in installerPackages // isoPackages // ipxePackages // lxcPackages // proxmoxPackages diff --git a/installer/auto-install.nix b/installer/auto-install.nix index dbcf678..0bfc6e2 100644 --- a/installer/auto-install.nix +++ b/installer/auto-install.nix @@ -1,7 +1,17 @@ # This module defines a systemd service that automatically installs NixOS to the disk. # It is intended to be used in an installation ISO. # It expects `targetSystem` (the closure to install) and `diskoScript` (the partitioning script) to be passed as arguments. -{ config, lib, pkgs, inputs, hostName, hostPlatform, targetSystem, diskoScript, ... }: +{ + config, + lib, + pkgs, + inputs, + hostName, + hostPlatform, + targetSystem, + diskoScript, + ... +}: { environment.systemPackages = [ pkgs.git @@ -14,7 +24,10 @@ systemd.services.auto-install = { description = "Automatic NixOS install for ${hostName}"; - after = [ "network-online.target" "systemd-udev-settle.service" ]; + after = [ + "network-online.target" + "systemd-udev-settle.service" + ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; diff --git a/inventory.nix b/inventory.nix index 18ffa5f..370f028 100644 --- a/inventory.nix +++ b/inventory.nix @@ -47,7 +47,7 @@ nix-surface = { count = 3; devices = { - "1".modules.sw.kioskUrl = "https://google.com"; + "1".ugaif.sw.kioskUrl = "https://google.com"; }; }; diff --git a/sw/default.nix b/sw/default.nix index a05df37..179ceec 100644 --- a/sw/default.nix +++ b/sw/default.nix @@ -16,7 +16,7 @@ with lib; let - cfg = config.modules.sw; + cfg = config.ugaif.sw; in { imports = [ @@ -25,7 +25,7 @@ in ./updater.nix ]; - options.modules.sw = { + options.ugaif.sw = { enable = mkEnableOption "Standard Workstation Configuration"; type = mkOption { diff --git a/sw/desktop/programs.nix b/sw/desktop/programs.nix index 7f8b075..f1211c3 100644 --- a/sw/desktop/programs.nix +++ b/sw/desktop/programs.nix @@ -9,7 +9,7 @@ with lib; let - cfg = config.modules.sw; + cfg = config.ugaif.sw; basePackages = with pkgs; [ tmux man diff --git a/sw/desktop/services.nix b/sw/desktop/services.nix index 7e78c4d..83f6716 100644 --- a/sw/desktop/services.nix +++ b/sw/desktop/services.nix @@ -6,7 +6,7 @@ }: { - modules.sw.python.enable = lib.mkDefault true; + ugaif.sw.python.enable = lib.mkDefault true; services.displayManager.sddm.enable = true; services.desktopManager.plasma6.enable = true; diff --git a/sw/headless/programs.nix b/sw/headless/programs.nix index de289ad..722cc55 100644 --- a/sw/headless/programs.nix +++ b/sw/headless/programs.nix @@ -9,7 +9,7 @@ with lib; let - cfg = config.modules.sw; + cfg = config.ugaif.sw; basePackages = with pkgs; [ tmux man diff --git a/sw/python.nix b/sw/python.nix index 0dc680f..7142e16 100644 --- a/sw/python.nix +++ b/sw/python.nix @@ -15,10 +15,10 @@ with lib; let - cfg = config.modules.sw.python; + cfg = config.ugaif.sw.python; in { - options.modules.sw.python = { + options.ugaif.sw.python = { enable = mkEnableOption "Python development tools (pixi, uv)" // { default = true; }; diff --git a/sw/stateless-kiosk/default.nix b/sw/stateless-kiosk/default.nix index 4b051c2..dedfad4 100644 --- a/sw/stateless-kiosk/default.nix +++ b/sw/stateless-kiosk/default.nix @@ -9,15 +9,35 @@ }: lib.mkMerge [ (import ./kiosk-browser.nix { - inherit config lib pkgs inputs; + inherit + config + lib + pkgs + inputs + ; }) (import ./services.nix { - inherit config lib pkgs inputs; + inherit + config + lib + pkgs + inputs + ; }) (import ./net.nix { - inherit config lib pkgs inputs; + inherit + config + lib + pkgs + inputs + ; }) (import ./programs.nix { - inherit config lib pkgs inputs; + inherit + config + lib + pkgs + inputs + ; }) ] diff --git a/sw/stateless-kiosk/kiosk-browser.nix b/sw/stateless-kiosk/kiosk-browser.nix index 192c095..b1ff685 100644 --- a/sw/stateless-kiosk/kiosk-browser.nix +++ b/sw/stateless-kiosk/kiosk-browser.nix @@ -1,7 +1,11 @@ - # This module configures Chromium for kiosk mode under Sway. # It includes a startup script that determines the kiosk URL based on the machine's MAC address. -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; diff --git a/sw/stateless-kiosk/mac-hostmap.nix b/sw/stateless-kiosk/mac-hostmap.nix index 099b37d..c0d243e 100644 --- a/sw/stateless-kiosk/mac-hostmap.nix +++ b/sw/stateless-kiosk/mac-hostmap.nix @@ -13,15 +13,16 @@ let # varName: the shell variable to assign # prefix: optional string to prepend to the value (default: "") # attrset: attribute set to use (default: hostmap) - macCaseBuilder = { - varName, - prefix ? "", - attrset ? hostmap - }: + macCaseBuilder = + { + varName, + prefix ? "", + attrset ? hostmap, + }: lib.concatStringsSep "\n" ( lib.mapAttrsToList (mac: val: " ${mac}) ${varName}=${prefix}${val} ;;") attrset ); -in +in { inherit hostmap macCaseBuilder; } diff --git a/sw/stateless-kiosk/net.nix b/sw/stateless-kiosk/net.nix index e0e9d27..f8aad34 100644 --- a/sw/stateless-kiosk/net.nix +++ b/sw/stateless-kiosk/net.nix @@ -1,5 +1,11 @@ # This module configures the network for the stateless kiosk using base networking (no systemd-networkd). -{ config, lib, pkgs, inputs, ... }: +{ + config, + lib, + pkgs, + inputs, + ... +}: { networking = { useNetworkd = false; diff --git a/sw/stateless-kiosk/programs.nix b/sw/stateless-kiosk/programs.nix index 55ffbd9..2e35552 100644 --- a/sw/stateless-kiosk/programs.nix +++ b/sw/stateless-kiosk/programs.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: { programs.sway = { enable = true; diff --git a/sw/stateless-kiosk/services.nix b/sw/stateless-kiosk/services.nix index 2416549..4bd2641 100644 --- a/sw/stateless-kiosk/services.nix +++ b/sw/stateless-kiosk/services.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; shellCases = macCaseBuilder { diff --git a/sw/tablet-kiosk/gsettings.nix b/sw/tablet-kiosk/gsettings.nix index 990d7e2..977196b 100644 --- a/sw/tablet-kiosk/gsettings.nix +++ b/sw/tablet-kiosk/gsettings.nix @@ -6,7 +6,7 @@ }: let - cfg = config.modules.sw; + cfg = config.ugaif.sw; in { programs.dconf = { diff --git a/sw/tablet-kiosk/programs.nix b/sw/tablet-kiosk/programs.nix index a883c1f..ef6e541 100644 --- a/sw/tablet-kiosk/programs.nix +++ b/sw/tablet-kiosk/programs.nix @@ -9,7 +9,7 @@ with lib; let - cfg = config.modules.sw; + cfg = config.ugaif.sw; basePackages = with pkgs; [ libcamera chromium diff --git a/sw/tablet-kiosk/services.nix b/sw/tablet-kiosk/services.nix index 5bcfcb6..5911fff 100644 --- a/sw/tablet-kiosk/services.nix +++ b/sw/tablet-kiosk/services.nix @@ -155,7 +155,7 @@ --noerrdialogs \ --disable-session-crashed-bubble \ --disable-infobars \ - ${config.modules.sw.kioskUrl} + ${config.ugaif.sw.kioskUrl} ''; }; }; diff --git a/sw/updater.nix b/sw/updater.nix index 46616c2..ba3942b 100644 --- a/sw/updater.nix +++ b/sw/updater.nix @@ -8,7 +8,7 @@ with lib; { - options.modules.sw.remoteBuild = lib.mkOption { + options.ugaif.sw.remoteBuild = lib.mkOption { type = types.submodule { options = { hosts = mkOption { @@ -29,7 +29,7 @@ with lib; }; config = { - modules.sw.remoteBuild.enable = lib.mkDefault (config.modules.sw.type == "tablet-kiosk"); + ugaif.sw.remoteBuild.enable = lib.mkDefault (config.ugaif.sw.type == "tablet-kiosk"); environment.systemPackages = [ (pkgs.writeShellScriptBin "update-system" '' @@ -62,18 +62,15 @@ with lib; description = "System daemon to one-shot run the Nix updater from fleet flake as root"; serviceConfig = { Type = "oneshot"; - ExecStart = + ExecStart = let - hosts = config.modules.sw.remoteBuild.hosts; + hosts = config.ugaif.sw.remoteBuild.hosts; builders = lib.strings.concatMapStringsSep ";" (x: x) hosts; rebuildCmd = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --refresh"; source = "--flake github:UGA-Innovation-Factory/nixos-systems"; - remoteBuildFlags = if config.modules.sw.remoteBuild.enable - then - ''--builders "${builders}"'' - else ""; + remoteBuildFlags = if config.ugaif.sw.remoteBuild.enable then ''--builders "${builders}"'' else ""; in - "${rebuildCmd} ${remoteBuildFlags} --print-build-logs ${source}#${config.networking.hostName}"; + "${rebuildCmd} ${remoteBuildFlags} --print-build-logs ${source}#${config.networking.hostName}"; User = "root"; Group = "root"; TimeoutStartSec = "0"; diff --git a/users.nix b/users.nix index 653a547..1c60429 100644 --- a/users.nix +++ b/users.nix @@ -5,11 +5,11 @@ # ============================================================================ # 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. + # 'ugaif.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 = { + ugaif.users.accounts = { root = { isNormalUser = false; hashedPassword = "!";