refactor: Module to ugaif and readme update

This commit is contained in:
UGA Innovation Factory
2025-12-15 15:32:40 -05:00
committed by Hunter Halloran
parent c46b0aa685
commit 205f03337a
31 changed files with 577 additions and 271 deletions

355
README.md
View File

@@ -1,6 +1,19 @@
# UGA Innovation Factory - NixOS Systems # 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 ## 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. - **`hosts/`**: Contains the logic for generating host configurations and hardware-specific types.
- **`sw/`**: Software modules (Desktop, Kiosk, Python, Neovim, etc.). - **`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 ## 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). 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 ## 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. The easiest way to update your system is using the included `update-system` utility:
To apply changes to the current system:
```bash ```bash
update-system 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 ```bash
update-system --impure 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 ```bash
# Local build # Test the configuration builds
sudo nixos-rebuild switch --flake . nix flake check
# Build for a specific host # Push changes to GitHub
sudo nixos-rebuild switch --flake .#nix-laptop1 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 ```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 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) ## Building Artifacts (ISOs, LXC, Proxmox)
@@ -95,103 +159,159 @@ nix build .#proxmox-nix-lxc1
## Configuration Guide ## Configuration Guide
### Adding a New User ### User Management
#### Adding a New User
1. Open `users.nix`. 1. Open `users.nix`.
2. Add a new entry to `modules.users.accounts`. 2. Add a new entry to `ugaif.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.
### 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`. 1. Open `inventory.nix`.
2. Locate the host type (e.g., `nix-laptop`). 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
nix-laptop = { nix-laptop = {
count = 2; count = 2;
devices = { 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
nix-surface = { nix-surface = {
count = 3; count = 3;
devices = { devices = {
"1" = { "1".ugaif.sw.kioskUrl = "https://ha.factory.uga.edu/dashboard-1";
modules = { "2".ugaif.sw.kioskUrl = "https://ha.factory.uga.edu/dashboard-2";
sw = {
kioskUrl = "https://ha.factory.uga.edu/surface-1";
};
};
};
}; };
}; };
``` ```
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. For complex system customizations (Docker, custom services, hardware tweaks), use an external flake:
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`:
```nix ```nix
nix-laptop = { nix-laptop = {
count = 2; count = 2;
devices = { devices = {
"2" = { "2".flakeUrl = "github:myuser/my-system-config";
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 ## 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`) ### User Flake (for `users.nix`)
Use this for user-specific dotfiles, shell configuration, and user packages. It can also override system-level user settings. Use this template for user-specific dotfiles, shell configuration, Home Manager modules, and overriding user account 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 ```nix
{ {
@@ -204,7 +324,7 @@ Note that `inputs` are omitted. This ensures the flake uses the exact same `nixp
nixosModules.default = { pkgs, lib, ... }: { nixosModules.default = { pkgs, lib, ... }: {
# 1. Override System-Level User Settings # 1. Override System-Level User Settings
modules.users.accounts.hdh20267 = { ugaif.users.accounts.hdh20267 = {
shell = pkgs.fish; shell = pkgs.fish;
extraGroups = [ "docker" ]; 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`) ### 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 ```nix
{ {
@@ -255,10 +375,10 @@ Use this for host-specific system services, hardware tweaks, or root-level packa
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
# Example: Override hardware settings defined in the main repo # 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 # Example: Enable specific users
modules.users.enabledUsers = [ "myuser" ]; ugaif.users.enabledUsers = [ "myuser" ];
# Example: Add a custom binary cache # Example: Add a custom binary cache
nix.settings.substituters = [ "https://nix-community.cachix.org" ]; 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 ## Development
### Python Environment ### 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 ```bash
pixi init my_project pixi init my_project
cd 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`

1
assets/plymouth-theme Submodule

Submodule assets/plymouth-theme added at 8658f4fb40

View File

@@ -85,7 +85,7 @@
{ {
# Formatter for 'nix fmt' # Formatter for 'nix fmt'
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style); formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style);
# Generate NixOS configurations from hosts/default.nix # Generate NixOS configurations from hosts/default.nix
nixosConfigurations = hosts.nixosConfigurations; nixosConfigurations = hosts.nixosConfigurations;

View File

@@ -8,11 +8,11 @@
# Boot & Storage Configuration # Boot & Storage Configuration
# ============================================================================ # ============================================================================
# This module defines the Disko partition layout and bootloader settings. # 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. # the target device and swap size.
{ {
options.host = { options.ugaif.host = {
filesystem = { filesystem = {
device = lib.mkOption { device = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@@ -45,7 +45,7 @@
disko.devices = { disko.devices = {
disk.main = { disk.main = {
type = "disk"; type = "disk";
device = config.host.filesystem.device; device = config.ugaif.host.filesystem.device;
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
@@ -71,7 +71,7 @@
swap = { swap = {
name = "swap"; name = "swap";
label = "swap"; label = "swap";
size = config.host.filesystem.swapSize; size = config.ugaif.host.filesystem.swapSize;
content = { content = {
type = "swap"; type = "swap";
}; };

View File

@@ -60,7 +60,7 @@ let
# We use legacyPackages to evaluate the simple data structure of users.nix # We use legacyPackages to evaluate the simple data structure of users.nix
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
usersData = import ../users.nix { inherit pkgs; }; usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.modules.users.accounts or { }; accounts = usersData.ugaif.users.accounts or { };
# Extract flakeUrls and convert to modules # Extract flakeUrls and convert to modules
userFlakeModules = lib.mapAttrsToList ( userFlakeModules = lib.mapAttrsToList (
@@ -70,6 +70,14 @@ let
else else
{ } { }
) accounts; ) accounts;
allModules =
commonModules
++ userFlakeModules
++ extraModules
++ [
{ networking.hostName = hostName; }
];
in in
{ {
system = lib.nixosSystem { system = lib.nixosSystem {
@@ -77,21 +85,9 @@ let
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
modules = modules = allModules;
commonModules
++ userFlakeModules
++ extraModules
++ [
{ networking.hostName = hostName; }
];
}; };
modules = modules = allModules;
commonModules
++ userFlakeModules
++ extraModules
++ [
{ networking.hostName = hostName; }
];
}; };
# Function to generate a set of hosts based on inventory count and overrides # Function to generate a set of hosts based on inventory count and overrides
@@ -127,27 +123,22 @@ let
overrideModule = overrideModule =
{ ... }: { ... }:
let let
# Remove special keys for filesystem overrides, keep other config attrs # Extract device-specific config, removing special keys that need custom handling
fsConf = builtins.removeAttrs devConf [ baseConfig = lib.removeAttrs devConf [
"extraUsers" "extraUsers"
"flakeUrl" "flakeUrl"
"hostname" "hostname"
"modules"
"buildMethods" "buildMethods"
"wslUser" "wslUser"
]; ];
extraConfig = lib.removeAttrs devConf [ # Handle special keys that map to specific ugaif options
"extraUsers" specialConfig = lib.mkMerge [
"flakeUrl" (lib.optionalAttrs (devConf ? extraUsers) { ugaif.users.enabledUsers = devConf.extraUsers; })
"hostname" (lib.optionalAttrs (devConf ? buildMethods) { ugaif.host.buildMethods = devConf.buildMethods; })
"buildMethods" (lib.optionalAttrs (devConf ? wslUser) { ugaif.host.wsl.user = devConf.wslUser; })
"wslUser"
]; ];
in in
lib.mkIf hasOverride (lib.recursiveUpdate (lib.recursiveUpdate { lib.mkIf hasOverride (lib.recursiveUpdate baseConfig specialConfig);
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);
config = mkHost { config = mkHost {
hostName = hostName; hostName = hostName;

View File

@@ -28,16 +28,16 @@
"rd.systemd.show_status=auto" "rd.systemd.show_status=auto"
]; ];
host.filesystem.swapSize = lib.mkDefault "16G"; ugaif.host.filesystem.swapSize = lib.mkDefault "16G";
host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; ugaif.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
host.buildMethods = lib.mkDefault [ "installer-iso" ]; ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
} }
) )
{ {
modules.sw.enable = true; ugaif.sw.enable = true;
modules.sw.type = "desktop"; ugaif.sw.type = "desktop";
} }
] ]

View File

@@ -31,10 +31,13 @@
]; ];
# Ephemeral setup: No swap, no disk # Ephemeral setup: No swap, no disk
host.filesystem.swapSize = lib.mkForce "0G"; ugaif.host.filesystem.swapSize = lib.mkForce "0G";
host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device ugaif.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device
host.buildMethods = lib.mkDefault [ "iso" "ipxe" ]; ugaif.host.buildMethods = lib.mkDefault [
"iso"
"ipxe"
];
# Disable Disko config since we are running from RAM/ISO # Disable Disko config since we are running from RAM/ISO
disko.enableConfig = lib.mkForce false; disko.enableConfig = lib.mkForce false;
@@ -42,7 +45,11 @@
fileSystems."/" = { fileSystems."/" = {
device = "none"; device = "none";
fsType = "tmpfs"; fsType = "tmpfs";
options = [ "defaults" "size=50%" "mode=755" ]; options = [
"defaults"
"size=50%"
"mode=755"
];
}; };
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
@@ -50,7 +57,7 @@
} }
) )
{ {
modules.sw.enable = true; ugaif.sw.enable = true;
modules.sw.type = "stateless-kiosk"; ugaif.sw.type = "stateless-kiosk";
} }
] ]

View File

@@ -35,9 +35,9 @@
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
host.filesystem.device = lib.mkDefault "/dev/nvme0n1"; ugaif.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
host.filesystem.swapSize = lib.mkDefault "34G"; ugaif.host.filesystem.swapSize = lib.mkDefault "34G";
host.buildMethods = lib.mkDefault [ "installer-iso" ]; ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ];
# Suspend / logind behavior # Suspend / logind behavior
services.upower.enable = lib.mkDefault true; services.upower.enable = lib.mkDefault true;
@@ -51,7 +51,7 @@
} }
) )
{ {
modules.sw.enable = true; ugaif.sw.enable = true;
modules.sw.type = "desktop"; ugaif.sw.type = "desktop";
} }
] ]

View File

@@ -35,11 +35,14 @@
]; ];
services.vscode-server.enable = true; services.vscode-server.enable = true;
system.stateVersion = "25.11"; system.stateVersion = "25.11";
host.buildMethods = lib.mkDefault [ "lxc" "proxmox" ]; ugaif.host.buildMethods = lib.mkDefault [
"lxc"
"proxmox"
];
} }
) )
{ {
modules.sw.enable = true; ugaif.sw.enable = true;
modules.sw.type = "headless"; ugaif.sw.type = "headless";
} }
] ]

View File

@@ -43,9 +43,9 @@
boot.kernelPackages = lib.mkForce refKernelPackages; boot.kernelPackages = lib.mkForce refKernelPackages;
host.filesystem.swapSize = lib.mkDefault "8G"; ugaif.host.filesystem.swapSize = lib.mkDefault "8G";
host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; ugaif.host.filesystem.device = lib.mkDefault "/dev/mmcblk0";
host.buildMethods = lib.mkDefault [ "installer-iso" ]; ugaif.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
@@ -53,7 +53,7 @@
) )
inputs.nixos-hardware.nixosModules.microsoft-surface-go inputs.nixos-hardware.nixosModules.microsoft-surface-go
{ {
modules.sw.enable = true; ugaif.sw.enable = true;
modules.sw.type = "tablet-kiosk"; ugaif.sw.type = "tablet-kiosk";
} }
] ]

View File

@@ -2,35 +2,38 @@
[ [
inputs.nixos-wsl.nixosModules.default inputs.nixos-wsl.nixosModules.default
inputs.vscode-server.nixosModules.default inputs.vscode-server.nixosModules.default
({ lib, config, ... }: { (
options.host.wsl.user = lib.mkOption { { lib, config, ... }:
type = lib.types.str; {
default = "engr-ugaif"; options.ugaif.host.wsl.user = lib.mkOption {
description = "The default user to log in as in WSL."; type = lib.types.str;
}; default = "engr-ugaif";
description = "The default user to log in as in WSL.";
};
config = { config = {
wsl.enable = true; wsl.enable = true;
wsl.defaultUser = config.host.wsl.user; wsl.defaultUser = config.ugaif.host.wsl.user;
# Enable the headless software profile
modules.sw.enable = true;
modules.sw.type = "headless";
# Fix for VS Code Server in WSL if needed, though vscode-server input exists # Enable the headless software profile
services.vscode-server.enable = true; ugaif.sw.enable = true;
ugaif.sw.type = "headless";
# Disable Disko and Bootloader for WSL # Fix for VS Code Server in WSL if needed, though vscode-server input exists
disko.enableConfig = lib.mkForce false; services.vscode-server.enable = true;
boot.loader.systemd-boot.enable = lib.mkForce false;
boot.loader.grub.enable = lib.mkForce false;
# Disable networking for wsl (it manages its own networking) # Disable Disko and Bootloader for WSL
systemd.network.enable = lib.mkForce false; disko.enableConfig = lib.mkForce false;
boot.loader.systemd-boot.enable = lib.mkForce false;
# Provide dummy values for required options from boot.nix boot.loader.grub.enable = lib.mkForce false;
host.filesystem.device = "/dev/null";
host.filesystem.swapSize = "0G"; # 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";
};
}
)
] ]

View File

@@ -77,7 +77,7 @@ let
}; };
in in
{ {
options.modules.users = { options.ugaif.users = {
shell = lib.mkOption { shell = lib.mkOption {
type = lib.types.package; type = lib.types.package;
default = pkgs.bash; default = pkgs.bash;
@@ -97,7 +97,7 @@ in
config = { config = {
# Default enabled users (always present) # Default enabled users (always present)
modules.users.enabledUsers = [ ugaif.users.enabledUsers = [
"root" "root"
"engr-ugaif" "engr-ugaif"
]; ];
@@ -106,8 +106,8 @@ in
users.users = users.users =
let let
enabledAccounts = lib.filterAttrs ( enabledAccounts = lib.filterAttrs (
name: _: lib.elem name config.modules.users.enabledUsers name: _: lib.elem name config.ugaif.users.enabledUsers
) config.modules.users.accounts; ) config.ugaif.users.accounts;
in in
lib.mapAttrs ( lib.mapAttrs (
name: user: name: user:
@@ -121,7 +121,7 @@ in
description = if user.description != null then user.description else lib.mkDefault ""; description = if user.description != null then user.description else lib.mkDefault "";
openssh.authorizedKeys.keys = user.opensshKeys; openssh.authorizedKeys.keys = user.opensshKeys;
packages = finalPackages; 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; ) enabledAccounts;
@@ -136,8 +136,8 @@ in
users = users =
let let
enabledAccounts = lib.filterAttrs ( enabledAccounts = lib.filterAttrs (
name: _: lib.elem name config.modules.users.enabledUsers name: _: lib.elem name config.ugaif.users.enabledUsers
) config.modules.users.accounts; ) config.ugaif.users.accounts;
in in
lib.mapAttrs ( lib.mapAttrs (
name: user: name: user:

View File

@@ -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.) # 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 .#<artifact-name>` # It exports a set of packages that can be built using `nix build .#<artifact-name>`
let let
@@ -9,7 +14,8 @@ let
# Creates a self-installing ISO for a specific host configuration # Creates a self-installing ISO for a specific host configuration
# This ISO will automatically partition the disk (using disko) and install the system # This ISO will automatically partition the disk (using disko) and install the system
mkInstaller = hostName: mkInstaller =
hostName:
let let
targetConfig = self.nixosConfigurations.${hostName}.config; targetConfig = self.nixosConfigurations.${hostName}.config;
targetSystem = targetConfig.system.build.toplevel; targetSystem = targetConfig.system.build.toplevel;
@@ -18,7 +24,12 @@ let
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
inherit system; inherit system;
specialArgs = { specialArgs = {
inherit inputs hostName targetSystem diskoScript; inherit
inputs
hostName
targetSystem
diskoScript
;
hostPlatform = system; hostPlatform = system;
}; };
modules = [ modules = [
@@ -29,7 +40,8 @@ let
}; };
# Uses nixos-generators to create artifacts like LXC containers, Proxmox VMA, or Live ISOs # Uses nixos-generators to create artifacts like LXC containers, Proxmox VMA, or Live ISOs
mkGenerator = hostName: format: mkGenerator =
hostName: format:
nixos-generators.nixosGenerate { nixos-generators.nixosGenerate {
inherit system; inherit system;
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
@@ -44,7 +56,8 @@ let
# Creates Netboot (iPXE) artifacts using the native NixOS netboot module # Creates Netboot (iPXE) artifacts using the native NixOS netboot module
# Returns a system configuration that includes the netboot module # Returns a system configuration that includes the netboot module
mkNetboot = hostName: mkNetboot =
hostName:
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
inherit system; inherit system;
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
@@ -60,66 +73,110 @@ let
hostNames = builtins.attrNames hosts.nixosConfigurations; hostNames = builtins.attrNames hosts.nixosConfigurations;
# Generate installer ISOs for hosts that have "installer-iso" in their buildMethods # Generate installer ISOs for hosts that have "installer-iso" in their buildMethods
installerPackages = lib.listToAttrs (lib.concatMap (name: installerPackages = lib.listToAttrs (
let cfg = hosts.nixosConfigurations.${name}; in lib.concatMap (
if lib.elem "installer-iso" cfg.config.host.buildMethods then [{ name:
name = "installer-iso-${name}"; let
value = (mkInstaller name).config.system.build.isoImage; cfg = hosts.nixosConfigurations.${name};
}] else [] in
) hostNames); 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 # Generate Live ISOs for hosts that have "iso" in their buildMethods
isoPackages = lib.listToAttrs (lib.concatMap (name: isoPackages = lib.listToAttrs (
let cfg = hosts.nixosConfigurations.${name}; in lib.concatMap (
if lib.elem "iso" cfg.config.host.buildMethods then [{ name:
name = "iso-${name}"; let
value = mkGenerator name "iso"; cfg = hosts.nixosConfigurations.${name};
}] else [] in
) hostNames); 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 # Generate iPXE artifacts (kernel, initrd, script) for hosts that have "ipxe" in their buildMethods
ipxePackages = lib.listToAttrs (lib.concatMap (name: ipxePackages = lib.listToAttrs (
let cfg = hosts.nixosConfigurations.${name}; in lib.concatMap (
if lib.elem "ipxe" cfg.config.host.buildMethods then [{ name:
name = "ipxe-${name}"; let
value = cfg = hosts.nixosConfigurations.${name};
let in
build = (mkNetboot name).config.system.build; if lib.elem "ipxe" cfg.config.ugaif.host.buildMethods then
in [
pkgs.symlinkJoin { {
name = "netboot-artifacts-${name}"; name = "ipxe-${name}";
paths = [ value =
build.netbootRamdisk let
build.kernel build = (mkNetboot name).config.system.build;
build.netbootIpxeScript in
]; pkgs.symlinkJoin {
}; name = "netboot-artifacts-${name}";
}] else [] paths = [
) hostNames); build.netbootRamdisk
build.kernel
build.netbootIpxeScript
];
};
}
]
else
[ ]
) hostNames
);
# Generate LXC tarballs for hosts that have "lxc" in their buildMethods # Generate LXC tarballs for hosts that have "lxc" in their buildMethods
lxcPackages = lib.listToAttrs (lib.concatMap (name: lxcPackages = lib.listToAttrs (
let cfg = hosts.nixosConfigurations.${name}; in lib.concatMap (
if lib.elem "lxc" cfg.config.host.buildMethods then [{ name:
name = "lxc-${name}"; let
value = cfg = hosts.nixosConfigurations.${name};
if cfg.config.boot.isContainer then in
cfg.config.system.build.tarball if lib.elem "lxc" cfg.config.ugaif.host.buildMethods then
else [
mkGenerator name "lxc"; {
}] else [] name = "lxc-${name}";
) hostNames); value =
if cfg.config.boot.isContainer then cfg.config.system.build.tarball else mkGenerator name "lxc";
}
]
else
[ ]
) hostNames
);
proxmoxPackages = lib.listToAttrs (lib.concatMap (name: proxmoxPackages = lib.listToAttrs (
let cfg = hosts.nixosConfigurations.${name}; in lib.concatMap (
if lib.elem "proxmox" cfg.config.host.buildMethods then [{ name:
name = "proxmox-${name}"; let
value = cfg = hosts.nixosConfigurations.${name};
if cfg.config.boot.isContainer then in
cfg.config.system.build.tarball if lib.elem "proxmox" cfg.config.ugaif.host.buildMethods then
else [
mkGenerator name "proxmox"; {
}] else [] name = "proxmox-${name}";
) hostNames); value =
if cfg.config.boot.isContainer then cfg.config.system.build.tarball else mkGenerator name "proxmox";
}
]
else
[ ]
) hostNames
);
in in
installerPackages // isoPackages // ipxePackages // lxcPackages // proxmoxPackages installerPackages // isoPackages // ipxePackages // lxcPackages // proxmoxPackages

View File

@@ -1,7 +1,17 @@
# This module defines a systemd service that automatically installs NixOS to the disk. # This module defines a systemd service that automatically installs NixOS to the disk.
# It is intended to be used in an installation ISO. # 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. # 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 = [ environment.systemPackages = [
pkgs.git pkgs.git
@@ -14,7 +24,10 @@
systemd.services.auto-install = { systemd.services.auto-install = {
description = "Automatic NixOS install for ${hostName}"; 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" ]; wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];

View File

@@ -47,7 +47,7 @@
nix-surface = { nix-surface = {
count = 3; count = 3;
devices = { devices = {
"1".modules.sw.kioskUrl = "https://google.com"; "1".ugaif.sw.kioskUrl = "https://google.com";
}; };
}; };

View File

@@ -16,7 +16,7 @@
with lib; with lib;
let let
cfg = config.modules.sw; cfg = config.ugaif.sw;
in in
{ {
imports = [ imports = [
@@ -25,7 +25,7 @@ in
./updater.nix ./updater.nix
]; ];
options.modules.sw = { options.ugaif.sw = {
enable = mkEnableOption "Standard Workstation Configuration"; enable = mkEnableOption "Standard Workstation Configuration";
type = mkOption { type = mkOption {

View File

@@ -9,7 +9,7 @@
with lib; with lib;
let let
cfg = config.modules.sw; cfg = config.ugaif.sw;
basePackages = with pkgs; [ basePackages = with pkgs; [
tmux tmux
man man

View File

@@ -6,7 +6,7 @@
}: }:
{ {
modules.sw.python.enable = lib.mkDefault true; ugaif.sw.python.enable = lib.mkDefault true;
services.displayManager.sddm.enable = true; services.displayManager.sddm.enable = true;
services.desktopManager.plasma6.enable = true; services.desktopManager.plasma6.enable = true;

View File

@@ -9,7 +9,7 @@
with lib; with lib;
let let
cfg = config.modules.sw; cfg = config.ugaif.sw;
basePackages = with pkgs; [ basePackages = with pkgs; [
tmux tmux
man man

View File

@@ -15,10 +15,10 @@
with lib; with lib;
let let
cfg = config.modules.sw.python; cfg = config.ugaif.sw.python;
in in
{ {
options.modules.sw.python = { options.ugaif.sw.python = {
enable = mkEnableOption "Python development tools (pixi, uv)" // { enable = mkEnableOption "Python development tools (pixi, uv)" // {
default = true; default = true;
}; };

View File

@@ -9,15 +9,35 @@
}: }:
lib.mkMerge [ lib.mkMerge [
(import ./kiosk-browser.nix { (import ./kiosk-browser.nix {
inherit config lib pkgs inputs; inherit
config
lib
pkgs
inputs
;
}) })
(import ./services.nix { (import ./services.nix {
inherit config lib pkgs inputs; inherit
config
lib
pkgs
inputs
;
}) })
(import ./net.nix { (import ./net.nix {
inherit config lib pkgs inputs; inherit
config
lib
pkgs
inputs
;
}) })
(import ./programs.nix { (import ./programs.nix {
inherit config lib pkgs inputs; inherit
config
lib
pkgs
inputs
;
}) })
] ]

View File

@@ -1,7 +1,11 @@
# This module configures Chromium for kiosk mode under Sway. # 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. # It includes a startup script that determines the kiosk URL based on the machine's MAC address.
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder;

View File

@@ -13,15 +13,16 @@ let
# varName: the shell variable to assign # varName: the shell variable to assign
# prefix: optional string to prepend to the value (default: "") # prefix: optional string to prepend to the value (default: "")
# attrset: attribute set to use (default: hostmap) # attrset: attribute set to use (default: hostmap)
macCaseBuilder = { macCaseBuilder =
varName, {
prefix ? "", varName,
attrset ? hostmap prefix ? "",
}: attrset ? hostmap,
}:
lib.concatStringsSep "\n" ( lib.concatStringsSep "\n" (
lib.mapAttrsToList (mac: val: " ${mac}) ${varName}=${prefix}${val} ;;") attrset lib.mapAttrsToList (mac: val: " ${mac}) ${varName}=${prefix}${val} ;;") attrset
); );
in in
{ {
inherit hostmap macCaseBuilder; inherit hostmap macCaseBuilder;
} }

View File

@@ -1,5 +1,11 @@
# This module configures the network for the stateless kiosk using base networking (no systemd-networkd). # This module configures the network for the stateless kiosk using base networking (no systemd-networkd).
{ config, lib, pkgs, inputs, ... }: {
config,
lib,
pkgs,
inputs,
...
}:
{ {
networking = { networking = {
useNetworkd = false; useNetworkd = false;

View File

@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
{ {
programs.sway = { programs.sway = {
enable = true; enable = true;

View File

@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let let
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder;
shellCases = macCaseBuilder { shellCases = macCaseBuilder {

View File

@@ -6,7 +6,7 @@
}: }:
let let
cfg = config.modules.sw; cfg = config.ugaif.sw;
in in
{ {
programs.dconf = { programs.dconf = {

View File

@@ -9,7 +9,7 @@
with lib; with lib;
let let
cfg = config.modules.sw; cfg = config.ugaif.sw;
basePackages = with pkgs; [ basePackages = with pkgs; [
libcamera libcamera
chromium chromium

View File

@@ -155,7 +155,7 @@
--noerrdialogs \ --noerrdialogs \
--disable-session-crashed-bubble \ --disable-session-crashed-bubble \
--disable-infobars \ --disable-infobars \
${config.modules.sw.kioskUrl} ${config.ugaif.sw.kioskUrl}
''; '';
}; };
}; };

View File

@@ -8,7 +8,7 @@
with lib; with lib;
{ {
options.modules.sw.remoteBuild = lib.mkOption { options.ugaif.sw.remoteBuild = lib.mkOption {
type = types.submodule { type = types.submodule {
options = { options = {
hosts = mkOption { hosts = mkOption {
@@ -29,7 +29,7 @@ with lib;
}; };
config = { 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 = [ environment.systemPackages = [
(pkgs.writeShellScriptBin "update-system" '' (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"; description = "System daemon to one-shot run the Nix updater from fleet flake as root";
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = ExecStart =
let let
hosts = config.modules.sw.remoteBuild.hosts; hosts = config.ugaif.sw.remoteBuild.hosts;
builders = lib.strings.concatMapStringsSep ";" (x: x) hosts; builders = lib.strings.concatMapStringsSep ";" (x: x) hosts;
rebuildCmd = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --refresh"; rebuildCmd = "${pkgs.nixos-rebuild}/bin/nixos-rebuild switch --refresh";
source = "--flake github:UGA-Innovation-Factory/nixos-systems"; source = "--flake github:UGA-Innovation-Factory/nixos-systems";
remoteBuildFlags = if config.modules.sw.remoteBuild.enable remoteBuildFlags = if config.ugaif.sw.remoteBuild.enable then ''--builders "${builders}"'' else "";
then
''--builders "${builders}"''
else "";
in in
"${rebuildCmd} ${remoteBuildFlags} --print-build-logs ${source}#${config.networking.hostName}"; "${rebuildCmd} ${remoteBuildFlags} --print-build-logs ${source}#${config.networking.hostName}";
User = "root"; User = "root";
Group = "root"; Group = "root";
TimeoutStartSec = "0"; TimeoutStartSec = "0";

View File

@@ -5,11 +5,11 @@
# ============================================================================ # ============================================================================
# This file defines the available user accounts. These accounts are NOT # This file defines the available user accounts. These accounts are NOT
# enabled by default on all systems. They must be enabled via the # 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 # Define the users here using the new option
# To generate a password hash, run: mkpasswd -m sha-512 # To generate a password hash, run: mkpasswd -m sha-512
modules.users.accounts = { ugaif.users.accounts = {
root = { root = {
isNormalUser = false; isNormalUser = false;
hashedPassword = "!"; hashedPassword = "!";