84 Commits

Author SHA1 Message Date
UGA Innovation Factory
6f7e95b9f9 fix: Fail the CI if formatting fails
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m40s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 15s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 20s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 11s
2026-01-30 18:26:26 -05:00
UGA Innovation Factory
7c07727150 feat: USDA-dash now uses encrypted .env files
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m42s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 20s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 10s
2026-01-30 23:19:38 +00:00
UGA Innovation Factory
7e6e8d5e0f chore: Update flake lock
Some checks failed
CI / Format Check (push) Waiting to run
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
2026-01-30 23:07:40 +00:00
UGA Innovation Factory
c6e0a0aedf chore: Update flake lock
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:55:30 +00:00
UGA Innovation Factory
4b4e6a2873 chore: Update flake lock
Some checks failed
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:53:59 +00:00
UGA Innovation Factory
40a9f9f5a6 chore: Update flake lock
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:52:27 +00:00
UGA Innovation Factory
14a61da9ed chore: Update flake lock
Some checks failed
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Format Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
2026-01-30 22:38:14 +00:00
UGA Innovation Factory
a3c8e0640a chore: Update flake lock
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:26:29 +00:00
UGA Innovation Factory
01fc5518c1 chore: Update flake lock
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:24:31 +00:00
UGA Innovation Factory
a2d4f71a77 chore: Update flake lock
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:21:13 +00:00
UGA Innovation Factory
e0cafb7f66 chore: Update usda-docker hash
Some checks failed
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:18:59 +00:00
UGA Innovation Factory
ffbd7a221d Set default timezone for LXC containers to fix Docker /etc/localtime mounts
Some checks failed
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 22:00:15 +00:00
UGA Innovation Factory
d7922247d2 Fix activation script to always regenerate age keys
Some checks failed
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:51:19 +00:00
UGA Innovation Factory
31c829f502 Add SSH-to-age conversion activation script for reliable secret decryption
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:48:57 +00:00
UGA Innovation Factory
e3bae02f58 Re-encrypt usda-vision-env with correct host key
Some checks failed
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:46:02 +00:00
UGA Innovation Factory
aa6d9d5691 Revert experimental changes, use ragenix defaults
Some checks failed
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:45:55 +00:00
UGA Innovation Factory
87045a518f Use rage instead of age for SSH key decryption support
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:40:04 +00:00
UGA Innovation Factory
dffe817e47 Update usda-dash host key and re-encrypt secret
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 21:29:39 +00:00
UGA Innovation Factory
23da829033 feat: Use age for env secret managment
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 20:54:31 +00:00
UGA Innovation Factory
dd19d1488a fix: Convert ssh keys to age keys
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m42s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 20s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 11s
2026-01-30 19:41:34 +00:00
UGA Innovation Factory
862ae2c864 chore: Run nix fmt
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m42s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 13s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 22s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 14s
CI / Build and Publish Documentation (push) Successful in 10s
2026-01-30 19:19:38 +00:00
UGA Innovation Factory
3efba93424 feat: Ragenix secret management per host
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-30 19:19:20 +00:00
UGA Innovation Factory
2e4602cbf3 refactor: Move macCaseBuilder into athenix.lib
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m44s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 19s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 10s
2026-01-27 22:13:32 +00:00
UGA Innovation Factory
ab3710b5f6 chore: Run nix fmt
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m43s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 20s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 11s
2026-01-27 21:44:23 +00:00
UGA Innovation Factory
863cd1ea95 fix: Remove unused or broken config outputs for nix eval of flake components
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-27 21:43:58 +00:00
UGA Innovation Factory
d8cee7e79b refactor: Make hw definitions modules with mkIf guards
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Build and Publish Documentation (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-27 16:30:54 -05:00
UGA Innovation Factory
063336f736 refactor: Fleet and sw behind mkIf guards 2026-01-27 16:11:36 -05:00
UGA Innovation Factory
85653e632f fix: Enable sw by default when imported
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m47s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 18s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
CI / Build and Publish Documentation (push) Successful in 8s
2026-01-27 15:36:31 -05:00
Hunter David Halloran
1533382ff2 Merge pull request 'fix: Lazily fetch external modules only if needed' (#32) from external-refactor into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m45s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 18s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 12s
CI / Build and Publish Documentation (push) Successful in 8s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/32
2026-01-27 20:06:09 +00:00
UGA Innovation Factory
540f5feb78 fix: Lazily fetch external modules only if needed 2026-01-27 15:05:52 -05:00
UGA Innovation Factory
1a7bf29448 docs: Update inline code docs for LSP help
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m39s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 14s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 8s
CI / Build and Publish Documentation (push) Successful in 5s
2026-01-27 14:48:07 -05:00
UGA Innovation Factory
13fdc3a7a1 feat: Update auto-docs
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m39s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 9s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 14s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 9s
CI / Build and Publish Documentation (push) Successful in 6s
2026-01-27 14:25:37 -05:00
Hunter David Halloran
01fdfbf913 Merge pull request 'fix: Change CI to ssh git' (#31) from sw-refactor into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m40s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 9s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 15s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 8s
CI / Build and Publish Documentation (push) Successful in 6s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/31
2026-01-27 19:07:29 +00:00
UGA Innovation Factory
9d0683165f fix: Change CI to ssh git 2026-01-27 14:07:03 -05:00
Hunter David Halloran
b1bc354160 Merge pull request 'refactor: Move sw into properly nested modules with unconditional import' (#30) from sw-refactor into main
Some checks failed
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m39s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 9s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 14s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 8s
CI / Build and Publish Documentation (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/30
2026-01-27 19:00:23 +00:00
UGA Innovation Factory
f669845bf7 refactor: Move sw into properly nested modules with unconditional import 2026-01-27 13:59:57 -05:00
UGA Innovation Factory
bd50f894ae chore: Remove unused variables and imports
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m34s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 6s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 6s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 13s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 7s
2026-01-13 21:07:39 -05:00
UGA Innovation Factory
92e3940644 chore: Run nix fmt
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m35s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 8s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 6s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 14s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 8s
2026-01-13 20:56:55 -05:00
UGA Innovation Factory
1c767ed4c8 fix: Ensure all users are read from and that the config is shared between module levels
Some checks failed
CI / Flake Check (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been cancelled
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been cancelled
CI / Format Check (push) Has been cancelled
2026-01-13 20:56:30 -05:00
UGA Innovation Factory
ffa434e720 chore: Run nix fmt
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m26s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 6s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 5s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 13s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 6s
2026-01-13 18:33:43 -05:00
UGA Innovation Factory
5f5698f608 chore: Update hdh20267 user to use new shell selection 2026-01-13 18:33:43 -05:00
UGA Innovation Factory
f606ea731c feat: Refactor to use flake-parts and import inventory and users thru the flake parts 2026-01-13 18:33:43 -05:00
UGA Innovation Factory
b1d4fe8d68 fix: Refactor flake structure to properly use flake-parts
- Remove incorrect parts/fleet-data.nix import from flake.nix
- Create flake-parts wrappers for fleet-option.nix and users.nix
- Import inventory.nix through fleet-option wrapper
- Fix module argument passing (remove pkgs from mkFleet call)
- Move NixOS-specific modules out of flake-parts imports

This addresses the 'path does not exist' error but introduces infinite recursion that needs to be resolved.
2026-01-13 18:33:43 -05:00
UGA Innovation Factory
cbddecfeb4 fix: Remove incorrect parts/fleet-data.nix import from flake.nix
fleet-data.nix is a NixOS module imported by fleet/common.nix, not a flake-parts module. It should not be imported at the flake level.
2026-01-13 18:33:37 -05:00
UGA Innovation Factory
005207d3e4 flake.lock: Update
Flake lock file updates:

• Updated input 'home-manager':
    'github:nix-community/home-manager/6bd04da47cfb48dfd15eabf08364b78ad894f5b2?narHash=sha256-KpoCBPvwHz3gAQtIUkohE2InRBFK3r0/FM6z5SPWfvM%3D' (2026-01-05)
  → 'github:nix-community/home-manager/82fb7dedaad83e5e279127a38ef410bcfac6d77c?narHash=sha256-MOU5YdVu4DVwuT5ztXgQpPuRRBjSjUGIdUzOQr9iQOY%3D' (2026-01-08)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/30a3c519afcf3f99e2c6df3b359aec5692054d92?narHash=sha256-8IQQUorUGiSmFaPnLSo2%2BT%2BrjHtiNWc%2BOAzeHck7N48%3D' (2026-01-03)
  → 'github:NixOS/nixpkgs/1327e798cb055f96f92685df444e9a2c326ab5ed?narHash=sha256-F4IIxa5xDHjtrmMcayM8lHctUq1oGltfBQu2%2BoqDWP4%3D' (2026-01-12)
• Updated input 'nixpkgs-old-kernel':
    'github:NixOS/nixpkgs/40ee5e1944bebdd128f9fbada44faefddfde29bd?narHash=sha256-0MnuWoN%2Bn1UYaGBIpqpPs9I9ZHW4kynits4mrnh1Pk4%3D' (2025-12-29)
  → 'github:NixOS/nixpkgs/ac62194c3917d5f474c1a844b6fd6da2db95077d?narHash=sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w%3D' (2026-01-02)
2026-01-13 16:21:42 -05:00
UGA Innovation Factory
d34325de53 fix: Remove incorrect ./parts/fleet-data.nix import from flake.nix and use correct flake-parts structure 2026-01-13 16:21:20 -05:00
UGA Innovation Factory
67e7a57402 fix: Set proper keybindings for zsh
All checks were successful
CI / Format Check (push) Successful in 3s
CI / Flake Check (push) Successful in 1m42s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 10s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 16s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
2026-01-13 12:16:46 -05:00
UGA Innovation Factory
dcc3dde702 fix: Repair Surface tablet kernel specifics that got lost in the refactor
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m35s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 7s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 16s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
2026-01-07 21:20:10 -05:00
UGA Innovation Factory
14fb79231f docs: Fix the file reference in comments in fleet-option.nix
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m34s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
2026-01-07 18:37:35 -05:00
Hunter David Halloran
ea4c2df776 Merge pull request 'Refactored the repository structure by renaming variants/ to hw and glue/ to fleet, fixing an infinite recursion bug by separating fleet options evaluation, and adding a lib.mkFleet function to enable external flakes to reuse Athenix's fleet generation' (#29) from inventory-as-module into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m34s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 13s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 10s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/29
2026-01-07 23:20:24 +00:00
UGA Innovation Factory
d89caa8a6b chore: run nix fmt 2026-01-07 18:15:37 -05:00
UGA Innovation Factory
4cb8d8ef13 chore: remove refactored-out files 2026-01-07 18:15:19 -05:00
UGA Innovation Factory
97646f3229 docs: update documentation for refactored structure
- Update NAMESPACE.md: filesystem.device and swapSize defaults now null
- Update README.md: add 'Using Athenix as a Library' section with lib.mkFleet
- Update copilot-instructions.md: reflect new directory structure and defaults
- Update EXTERNAL_MODULES.md: correct paths from variants/ and glue/ to hw/ and fleet/
- Update PROXMOX_LXC.md: update hardware type path reference
2026-01-07 18:12:39 -05:00
UGA Innovation Factory
d3788be951 chore: update inventory.nix formatting
- Remove trailing whitespace and ensure consistent formatting
2026-01-07 18:12:28 -05:00
UGA Innovation Factory
cda1975631 fix: use lib.mkForce for systemd.network.enable in stateless-kiosk
- Prevent conflicts when disabling systemd-networkd in stateless kiosk config
- Ensures the disable takes precedence over other module settings
2026-01-07 18:12:19 -05:00
UGA Innovation Factory
7145f5b3d8 fix: update artifacts.nix to use fleet.modules
- Use fleet.modules instead of trying to access mkFleet output directly
- Fix netboot artifact generation to correctly access module list
- Ensure artifacts can access both nixosConfigurations and modules from fleet
2026-01-07 18:12:07 -05:00
UGA Innovation Factory
1ce7334a73 feat: add lib.mkFleet for external flake consumption
- Create lib/mkFleet.nix to expose fleet generator as library function
- Allow external flakes to use Athenix's fleet logic with custom inventory
- Export lib output in flake.nix with proper input passing
- Enable usage: nixosConfigurations = athenix.lib.mkFleet { fleet = ...; hwTypes = ...; }
2026-01-07 18:11:59 -05:00
UGA Innovation Factory
775080949d refactor: move GC and buildMethods options to sw module
- Move athenix.system.gc options from fleet/common.nix to sw/gc.nix
- Move athenix.host.buildMethods option to sw/gc.nix
- Move home-manager, agenix, disko imports to sw/default.nix
- Better separation: fleet/ handles generation, sw/ handles software config
2026-01-07 18:11:46 -05:00
UGA Innovation Factory
d15b4d8067 feat: add fleet configuration options module
- Create fleet/fleet-option.nix to define athenix.fleet and athenix.hwTypes options
- Use lib.evalModules to evaluate inventory separately from host configs
- Enable external overrides of fleet and hardware types via lib.mkForce
- Fix infinite recursion issue by avoiding config references in imports
2026-01-07 18:11:29 -05:00
UGA Innovation Factory
5875725ca2 refactor: reorganize directory structure (variants -> hw, glue -> fleet)
- Rename variants/ to hw/ for clearer hardware module naming
- Rename glue/ to fleet/ for more intuitive fleet management
- Move boot/fs configuration from glue/boot.nix to separate fleet/boot.nix and fleet/fs.nix
- Improve separation of concerns between boot, filesystem, and common config
2026-01-07 18:11:19 -05:00
Hunter David Halloran
9e066d395b Merge pull request 'updater-ssh' (#28) from updater-ssh into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m38s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 13s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 18s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 12s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/28
2026-01-07 00:16:38 +00:00
UGA Innovation Factory
825e90c581 chore: run nix fmt 2026-01-06 19:15:38 -05:00
UGA Innovation Factory
6a9807a688 fix: system-update should respect ssh requirements 2026-01-06 19:14:58 -05:00
Hunter David Halloran
c4ff0d7fd3 Merge pull request 'feat: add '--ssh' flag to update-ref tool to choose ssh url or default to https url' (#27) from update-ref-https into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m39s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 18s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 12s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/27
2026-01-07 00:03:46 +00:00
Hunter Halloran
cca3e39af0 feat: add '--ssh' flag to update-ref tool to choose ssh url or default to https url 2026-01-06 19:02:27 -05:00
Hunter David Halloran
917275409f Merge pull request 'Merge branch 'options-refactor' to ensure options are defined where they are used, standardize the module input of 'variants' (formerly 'hosts'), and add a 'glue' directory for piecing together the final flake outputs' (#26) from options-refactor into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m38s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 13s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 18s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 12s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/26
2026-01-06 23:39:55 +00:00
UGA Innovation Factory
b3e274484f chore: remove unused assets directory 2026-01-06 18:36:01 -05:00
UGA Innovation Factory
55c49d84b5 chore: run nix fmt 2026-01-06 18:34:21 -05:00
UGA Innovation Factory
6972a999ca docs: update all references from hosts/ to glue/ and variants/
- Update README.md structure section
- Update DEVELOPMENT.md, EXTERNAL_MODULES.md, INVENTORY.md
- Update GitHub Copilot instructions
- Update PROXMOX_LXC.md references
- Clarify new directory organization and purpose
2026-01-06 18:32:18 -05:00
UGA Innovation Factory
faf7bb635e feat: add lazy evaluation for external modules in inventory
- External modules now use 'external' field for lazy evaluation
- Only fetched when building specific host (not during flake check)
- Improves rebuild performance for unrelated hosts
- Update examples and documentation in inventory.nix header
2026-01-06 18:32:06 -05:00
UGA Innovation Factory
c3bbf6f8be refactor: update imports to use glue/ and variants/
- flake.nix: import glue/fleet.nix instead of hosts/
- installer/artifacts.nix: use 'fleet' parameter instead of 'hosts'
- installer/modules.nix: auto-import from variants/ directory
2026-01-06 18:31:58 -05:00
UGA Innovation Factory
77cea838a1 chore: remove old hosts/ directory
- Replaced by glue/ and variants/ structure
- Fleet generation moved to glue/fleet.nix
- Hardware types moved to variants/
2026-01-06 18:31:50 -05:00
UGA Innovation Factory
cb37fad70e refactor: create glue/ and variants/ directories
- Add glue/ for fleet generation logic and common configuration
- Add variants/ for hardware type modules
- Improves separation of concerns and module organization
2026-01-06 18:31:40 -05:00
UGA Innovation Factory
03f532e867 refactor: define options where they are used 2026-01-06 14:43:45 -05:00
Hunter David Halloran
9a2f167efe Merge pull request 'feat: add age packages for fido2 and other secret management' (#25) from age-packages into main
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m40s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 19s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/25
2026-01-06 18:41:34 +00:00
UGA Innovation Factory
6edf858a4e feat: add age packages for fido2 and other secret management 2026-01-06 12:57:34 -05:00
UGA Innovation Factory
3f1801fd84 fix: auto-installer works fully offline
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m35s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 10s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
chore: run nix fmt

refactor: change usage of targetSystem to targetSystemBuild.toplevel

chore: run nix fmt

refactor: change usage of targetSystem to targetSystemBuild.toplevel
2026-01-05 16:19:03 -05:00
UGA Innovation Factory
f68c63590b fix: work on making the installer work offline
fix: ensure system closure is installed for derivations needed by the installer

fix: build closure in build-step instead of on iso
2026-01-05 16:17:33 -05:00
UGA Innovation Factory
c6f4a39eee fix: work on making the installer work offline 2026-01-05 11:58:38 -05:00
UGA Innovation Factory
c2b5e4eafe feat: add zima1 thru zima3 to hosts 2026-01-05 11:58:08 -05:00
UGA Innovation Factory
f07ccc071e docs: Copilot update all docs files
All checks were successful
CI / Format Check (push) Successful in 6s
CI / Flake Check (push) Successful in 1m25s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 10s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 16s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 10s
2026-01-05 10:05:41 -05:00
Hunter Halloran
0378268dcc fix: Disable firewall for camera discovery via external module for usda-dash
All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m16s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 10s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 12s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 10s
2025-12-29 20:31:14 -05:00
Hunter Halloran
cad9cb35ef chore: Update firewall settings via external module for usda-dash
All checks were successful
CI / Format Check (push) Successful in 1s
CI / Flake Check (push) Successful in 1m25s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 11s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 13s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 11s
2025-12-29 20:23:19 -05:00
Hunter Halloran
c8c3894e65 chore: Update firewall settings via external module for usda-dash
Some checks failed
CI / Format Check (push) Successful in 9s
CI / Flake Check (push) Failing after 14s
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been skipped
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Has been skipped
CI / Evaluate Key Configurations (nix-builder) (push) Has been skipped
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been skipped
CI / Evaluate Artifacts (lxc-nix-builder) (push) Has been skipped
2025-12-29 20:19:44 -05:00
92 changed files with 6556 additions and 3088 deletions

View File

@@ -26,18 +26,23 @@ jobs:
format-check: format-check:
name: Format Check name: Format Check
runs-on: [self-hosted, nix-builder] runs-on: [self-hosted, nix-builder]
timeout-minutes: 5
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Check formatting - name: Check formatting
timeout-minutes: 3
run: | run: |
nix fmt **/*.nix set -euo pipefail
if ! git diff --quiet; then echo "Checking code formatting..."
output=$(nix fmt **/*.nix 2>&1)
if [ -n "$output" ]; then
echo "::error::Code is not formatted. Please run 'nix fmt **/*.nix' locally." echo "::error::Code is not formatted. Please run 'nix fmt **/*.nix' locally."
git diff echo "$output"
exit 1 exit 1
fi fi
echo "All files are properly formatted"
eval-configs: eval-configs:
name: Evaluate Key Configurations name: Evaluate Key Configurations
@@ -79,3 +84,39 @@ jobs:
echo "Evaluating artifact ${{ matrix.artifact }}" echo "Evaluating artifact ${{ matrix.artifact }}"
nix eval .#${{ matrix.artifact }}.drvPath \ nix eval .#${{ matrix.artifact }}.drvPath \
--show-trace --show-trace
build-docs:
name: Build and Publish Documentation
runs-on: [self-hosted, nix-builder]
needs: [flake-check]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build documentation
run: |
echo "Building Athenix documentation"
nix build .#docs --print-build-logs
- name: Clone wiki repository
run: |
git clone git@factory.uga.edu:UGA-Innovation-Factory/athenix.wiki.git wiki
cd wiki
git config user.name "Athenix CI"
git config user.email "ci@athenix.factory.uga.edu"
- name: Update wiki with documentation
run: |
# Copy documentation to wiki
cp -r result/* wiki/
# Commit and push changes
cd wiki
git add .
if git diff --staged --quiet; then
echo "No documentation changes to commit"
else
git commit -m "Update documentation from commit ${{ github.sha }}"
git push
fi

View File

@@ -26,8 +26,9 @@ This is a **NixOS system configuration repository** that uses:
- **`flake.nix`**: Entry point - inputs and outputs only - **`flake.nix`**: Entry point - inputs and outputs only
- **`inventory.nix`**: Fleet definitions - host configurations - **`inventory.nix`**: Fleet definitions - host configurations
- **`users.nix`**: User account definitions - **`users.nix`**: User account definitions
- **`hosts/`**: Host generation logic and hardware types - **`hw/`**: Hardware type modules (desktop, laptop, surface, lxc, wsl, etc.)
- **`sw/`**: Software configurations organized by system type - **`fleet/`**: Fleet generation logic and common system configuration
- **`sw/`**: Software configurations by system type
- **`installer/`**: Build artifact generation (ISO, LXC, etc.) - **`installer/`**: Build artifact generation (ISO, LXC, etc.)
- **`templates/`**: Templates for external configurations - **`templates/`**: Templates for external configurations
@@ -44,9 +45,12 @@ All Innovation Factory-specific options MUST use the `athenix` namespace:
### Host Options (`athenix.host.*`) ### Host Options (`athenix.host.*`)
```nix ```nix
athenix.host = { athenix.host = {
filesystem.device = "/dev/sda"; # Boot disk filesystem.device = "/dev/nvme0n1"; # Boot disk (default: null)
filesystem.swapSize = "32G"; # Swap size filesystem.swapSize = "32G"; # Swap size (default: null)
buildMethods = [ "iso" ]; # Artifact types buildMethods = [ "installer-iso" ]; # Artifact types (defined in sw/gc.nix)
useHostPrefix = true; # Hostname prefix behavior
wsl.user = "username"; # WSL default user
};
useHostPrefix = true; # Hostname prefix behavior useHostPrefix = true; # Hostname prefix behavior
wsl.user = "username"; # WSL default user wsl.user = "username"; # WSL default user
}; };
@@ -113,6 +117,17 @@ athenix.forUser = "username"; # Convenience: enable user + set WSL us
3. System modules: Provide `default.nix` that accepts `{ inputs, ... }` 3. System modules: Provide `default.nix` that accepts `{ inputs, ... }`
4. Reference in `inventory.nix` or `users.nix` using `builtins.fetchGit` 4. Reference in `inventory.nix` or `users.nix` using `builtins.fetchGit`
#### Using Athenix as a Library
External flakes can use Athenix's fleet generator:
```nix
outputs = { athenix, ... }: {
nixosConfigurations = athenix.lib.mkFleet {
fleet = import ./custom-inventory.nix;
hwTypes = import ./custom-hardware.nix;
};
};
```
## Important Constraints ## Important Constraints
### What NOT to Do ### What NOT to Do

34
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Build Documentation
on:
push:
branches: [main]
pull_request:
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Build documentation
run: |
nix build .#docs
nix build .#athenix-options
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: athenix-docs
path: result/
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./result

18
.nixd.json Normal file
View File

@@ -0,0 +1,18 @@
{
"eval": {
"target": {
"installable": ".#nixosConfigurations.nix-desktop1.options"
}
},
"formatting": {
"command": ["nixfmt"]
},
"options": {
"nixos": {
"expr": "(builtins.getFlake \"/home/engr-ugaif/athenix\").nixosConfigurations.nix-desktop1.options"
},
"home-manager": {
"expr": "(builtins.getFlake \"/home/engr-ugaif/athenix\").nixosConfigurations.nix-desktop1.config.home-manager.users.engr-ugaif.options"
}
}
}

357
README.md
View File

@@ -1,156 +1,159 @@
# UGA Innovation Factory - Athenix # Athenix - UGA Innovation Factory NixOS Configuration
[![CI](https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions/workflows/ci.yml/badge.svg)](https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions) [![CI](https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions/workflows/ci.yml/badge.svg)](https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions)
This repository contains the NixOS configuration for the Innovation Factory's fleet of laptops, desktops, Surface tablets, and containers. It provides a declarative, reproducible system configuration using Nix flakes. Declarative NixOS configuration management for the Innovation Factory's fleet of workstations, laptops, tablets, and containers using Nix flakes.
## Documentation ## Quick Navigation
- **[Quick Start](#quick-start)** - Get started in 5 minutes - **[docs/INVENTORY.md](docs/INVENTORY.md)** - Define and configure hosts
- **[docs/INVENTORY.md](docs/INVENTORY.md)** - Configure hosts and fleet inventory - **[docs/NAMESPACE.md](docs/NAMESPACE.md)** - All `athenix.*` options reference
- **[docs/NAMESPACE.md](docs/NAMESPACE.md)** - Configuration options reference (`athenix.*`) - **[docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md)** - User accounts and dotfiles
- **[docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md)** - User account management - **[docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md)** - External system and user configurations
- **[docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md)** - External configuration modules - **[docs/BUILDING.md](docs/BUILDING.md)** - Build ISOs, containers, and artifacts
- **[docs/BUILDING.md](docs/BUILDING.md)** - Build ISOs and container images - **[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)** - Development workflow and testing
- **[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)** - Development and testing workflow
## Quick Start ## Getting Started
### For End Users ### For End Users
Update your system to the latest configuration: Update your system:
```bash ```bash
update-system update-system
``` ```
This command automatically fetches the latest configuration, rebuilds your system, and uses remote builders on Surface tablets to speed up builds. This automatically rebuilds your system with the latest configuration from the repository.
**Note:** If you use external user configurations (personal dotfiles), run:
```bash
sudo nixos-rebuild switch --flake git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git --impure
```
### For Administrators ### For Administrators
Make configuration changes:
```bash ```bash
# 1. Make changes to configuration files # Edit inventory
vim inventory.nix vim inventory.nix
# 2. Test configuration # Validate changes
nix flake check nix flake check
# 3. Format code # Format code
nix fmt nix fmt
# 4. Commit and push # Commit and push
git add . git add . && git commit -m "Your message" && git push
git commit -m "Description of changes"
git push
``` ```
Users can now run `update-system` to get the changes. Users automatically get changes when they run `update-system`.
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for detailed development workflow.**
## Repository Structure ## Repository Structure
``` ```
nixos-systems/ flake.nix # Flake entry point (inputs + outputs)
├── flake.nix # Flake entry point inventory.nix # Fleet inventory and host definitions
├── inventory.nix # Fleet inventory - Define hosts here users.nix # User account definitions
├── users.nix # User accounts - Define users here
├── hosts/ # Host generation logic flake.lock # Locked dependency versions
│ ├── types/ # Hardware types (desktop, laptop, surface, lxc, wsl, ephemeral)
│ └── ... hw/ # Hardware type modules (exportable as nixosModules)
├── sw/ # Software configurations by system type ├── default.nix # Auto-exports all variant types
├── desktop/ # Full desktop environment ├── nix-desktop.nix # Desktop workstations
│ ├── tablet-kiosk/ # Surface kiosk mode ├── nix-laptop.nix # Laptop systems
│ ├── stateless-kiosk/# Diskless PXE kiosks ├── nix-surface.nix # Surface Pro tablets
│ ├── headless/ # Servers and containers ├── nix-lxc.nix # LXC containers
│ └── ... ├── nix-wsl.nix # WSL instances
├── installer/ # ISO and container builds ├── nix-zima.nix # ZimaBoard systems
── templates/ # Templates for external configs ── nix-ephemeral.nix # Diskless/netboot systems
│ ├── system/ # System configuration template
│ └── user/ # User configuration template fleet/ # Fleet generation and common configuration
├── docs/ # Documentation ├── default.nix # Processes inventory.nix to generate all hosts
│ ├── INVENTORY.md # Host configuration guide ├── common.nix # Common NixOS configuration (all hosts)
│ ├── NAMESPACE.md # Option reference ├── boot.nix # Boot and filesystem configuration
│ ├── BUILDING.md # Building artifacts └── user-config.nix # User account and home-manager integration
│ └── DEVELOPMENT.md # Development guide
└── assets/ # Assets (Plymouth theme, etc.) sw/ # Software configurations by system type
├── default.nix # Software module entry point
├── python.nix # Python tools (pixi, uv)
├── nvim.nix # Neovim configuration
├── ghostty.nix # Ghostty terminal
├── theme.nix # System theme configuration
├── updater.nix # System update scripts
├── update-ref.nix # Update reference tracking
├── builders/ # Build server configuration
├── desktop/ # Desktop environment
├── headless/ # Server/container without GUI
├── tablet-kiosk/ # Surface tablet kiosk mode
└── stateless-kiosk/ # Diskless PXE netboot systems
installer/ # Build artifacts
├── default.nix # Build configuration
├── artifacts.nix # ISO/LXC/Proxmox definitions
├── auto-install.nix # Installer scripts
├── modules.nix # Installer-specific modules
├── deploy-proxmox-lxc.sh # Proxmox deployment script
└── PROXMOX_LXC.md # Proxmox guide
templates/ # Templates for external modules
├── user/ # User configuration template
│ ├── user.nix # User options + home-manager config
│ └── README.md
└── system/ # System configuration template
├── default.nix # NixOS module
└── README.md
docs/ # Documentation
├── README.md # This file
├── INVENTORY.md # Host configuration guide
├── NAMESPACE.md # Option reference
├── USER_CONFIGURATION.md # User management
├── EXTERNAL_MODULES.md # External module integration
├── BUILDING.md # Build and deployment
└── DEVELOPMENT.md # Development workflow
assets/ # Assets
└── plymouth-theme/ # Boot splash theme
``` ```
## Configuration Overview ## Configuration Overview
All Innovation Factory options use the `athenix.*` namespace. See **[docs/NAMESPACE.md](docs/NAMESPACE.md)** for complete reference. All Innovation Factory-specific options use the `athenix` namespace to avoid conflicts with NixOS options.
**Quick examples:** ### Common Options
```nix ```nix
# Host configuration # Host filesystem and hardware
athenix.host.filesystem.device = "/dev/nvme0n1"; athenix.host = {
athenix.host.filesystem.swapSize = "64G"; filesystem.device = "/dev/sda";
filesystem.swapSize = "32G";
buildMethods = [ "installer-iso" ];
useHostPrefix = true;
};
# Software configuration # System type and packages
athenix.sw.type = "desktop"; # or "headless", "tablet-kiosk" athenix.sw = {
athenix.sw.extraPackages = with pkgs; [ vim docker ]; type = "desktop"; # desktop, tablet-kiosk, stateless-kiosk, headless, builders
extraPackages = with pkgs; [ vim docker ];
};
# User management # User management
athenix.users.myuser.enable = true; athenix.users.myuser.enable = true;
athenix.forUser = "myuser"; # Convenience shortcut athenix.forUser = "myuser"; # Convenience shortcut
``` ```
## Prerequisites See [docs/NAMESPACE.md](docs/NAMESPACE.md) for complete option reference.
To work with this repository, install Nix with flakes support:
```bash
# Recommended: Determinate Systems installer (includes flakes)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Alternative: Official installer (requires enabling flakes manually)
sh <(curl -L https://nixos.org/nix/install) --daemon
```
## Common Tasks ## Common Tasks
### Adding a New User
1. Edit `users.nix`:
```nix
myuser = {
description = "My Full Name";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512
opensshKeys = [ "ssh-ed25519 AAAA... user@host" ];
};
```
2. Enable on hosts in `inventory.nix`:
```nix
nix-laptop = {
devices = 2;
overrides.athenix.users.myuser.enable = true;
};
```
**See [docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md) for complete user management guide.**
### Adding Hosts ### Adding Hosts
Edit `inventory.nix`: Edit `inventory.nix`:
```nix ```nix
# Simple: Create 5 laptops # Simple: Create 5 identical laptops
nix-laptop = { nix-laptop = {
devices = 5; # Creates nix-laptop1 through nix-laptop5 devices = 5;
}; };
# With configuration # With custom configuration per device
nix-surface = { nix-surface = {
devices = { devices = {
"1".athenix.sw.kioskUrl = "https://dashboard1.example.com"; "1".athenix.sw.kioskUrl = "https://dashboard1.example.com";
@@ -158,107 +161,163 @@ nix-surface = {
}; };
}; };
# With overrides for all devices # With common overrides
nix-desktop = { nix-desktop = {
devices = 3; devices = 3;
overrides = { overrides = {
athenix.users.student.enable = true; athenix.users.student.enable = true;
athenix.sw.extraPackages = with pkgs; [ vim ];
}; };
}; };
``` ```
**See [docs/INVENTORY.md](docs/INVENTORY.md) for complete host configuration guide.** **See [docs/INVENTORY.md](docs/INVENTORY.md) for complete guide.**
### Managing Users
Edit `users.nix`:
```nix
athenix.users.myuser = {
description = "My Name";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$..."; # mkpasswd -m sha-512
opensshKeys = [ "ssh-ed25519 AAAA..." ];
};
```
Enable in `inventory.nix`:
```nix
nix-laptop = {
overrides.athenix.users.myuser.enable = true;
};
```
**See [docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md) for complete guide.**
### Using External Configurations ### Using External Configurations
Users and systems can reference external Git repositories for configuration: Reference external repositories for user dotfiles or system configurations:
```nix ```nix
# In users.nix - External dotfiles with user configuration # User dotfiles (in users.nix)
myuser.external = builtins.fetchGit { hdh20267.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles"; url = "https://git.factory.uga.edu/hdh20267/dotfiles";
rev = "abc123..."; rev = "abc123...";
}; };
# The external user.nix file contains both athenix.users.myuser options
# AND home-manager configuration
# In inventory.nix - External system config # System configuration (in inventory.nix)
nix-lxc = { nix-lxc = {
devices."server" = builtins.fetchGit { devices."special" = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config"; url = "https://git.factory.uga.edu/org/server-config";
rev = "abc123..."; rev = "abc123...";
}; };
}; };
``` ```
**Create templates:**
```bash
# User configuration (dotfiles)
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user
# System configuration
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system
```
**See [docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md) for complete guide.** **See [docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md) for complete guide.**
### Building Installation Media ### Building Installation Media
```bash ```bash
# Build installer ISO # Build installer ISO for a specific host
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1 nix build .#installer-iso-nix-laptop1
# Build LXC container # Build LXC container
nix build .#lxc-nix-builder nix build .#lxc-nix-builder
# List all available artifacts # List all available artifacts
nix flake show git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git nix flake show
``` ```
**See [docs/BUILDING.md](docs/BUILDING.md) for complete guide on building ISOs, containers, and using remote builders.** **See [docs/BUILDING.md](docs/BUILDING.md) for complete guide.**
### Using Athenix as a Library
Import Athenix in your own flake to use its fleet generation logic with custom inventory:
```nix
{
inputs.athenix.url = "git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git";
outputs = { self, athenix, ... }: {
# Generate configurations with custom fleet and hardware types
nixosConfigurations = athenix.lib.mkFleet {
fleet = import ./my-inventory.nix;
hwTypes = import ./my-hardware-types.nix;
};
# Or use individual modules
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
athenix.nixosModules.hw.nix-desktop # Use Athenix hardware configs
athenix.nixosModules.sw # Use Athenix software configs
./configuration.nix
];
};
};
}
```
**Exported modules:** `nix-desktop`, `nix-laptop`, `nix-surface`, `nix-lxc`, `nix-wsl`, `nix-ephemeral`, `nix-zima`, `sw`, `common`
## System Types ## System Types
Set via `athenix.sw.type`:
- **`desktop`** - Full GNOME desktop environment - **`desktop`** - Full GNOME desktop environment
- **`tablet-kiosk`** - Surface tablets in kiosk mode - **`tablet-kiosk`** - Surface tablets with Firefox kiosk browser
- **`stateless-kiosk`** - Diskless PXE boot kiosks - **`stateless-kiosk`** - Diskless PXE-booted systems
- **`headless`** - Servers and containers (no GUI) - **`headless`** - Servers and containers without GUI
- **`builders`** - Build servers
Set via `athenix.sw.type` option. See [docs/NAMESPACE.md](docs/NAMESPACE.md) for all options. ## Development Workflow
## Development
**Quick commands:**
```bash ```bash
nix flake check # Validate all configurations # Check all configurations
nix fmt # Format code nix flake check
nix flake update # Update dependencies
nix build .#installer-iso-nix-laptop1 # Build specific artifact # Format code
nix fmt **/*.nix
# Build specific artifact
nix build .#installer-iso-nix-laptop1
# Update flake inputs
nix flake update
``` ```
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for complete development guide.** **See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for detailed workflow.**
## Troubleshooting ## Troubleshooting
**Common issues:** | Issue | Solution |
|-------|----------|
| Build errors | Run `nix flake check --show-trace` for details |
| Configuration validation | `nix flake check` checks all 50+ hosts |
| External modules fail | Verify Git URL accessibility and module structure |
| Remote build issues | Test SSH: `ssh engr-ugaif@nix-builder` |
| List all hosts | `nix eval .#nixosConfigurations --apply builtins.attrNames` |
| Disk space | `nix-collect-garbage -d && nix store optimise` |
- **Build errors:** Run `nix flake check --show-trace` for details ## Prerequisites
- **External modules not loading:** Check repository access and module structure (see templates)
- **Remote build failures:** Test SSH access: `ssh engr-ugaif@nix-builder` Nix with flakes support:
- **Out of disk space:** Run `nix-collect-garbage -d && nix store optimise`
**Useful commands:**
```bash ```bash
nix flake show # List all available outputs # Recommended: Determinate Systems installer
nix flake metadata # Show flake info curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
nix eval .#nixosConfigurations --apply builtins.attrNames # List all hosts
# Or enable flakes in existing Nix installation
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
``` ```
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) and [docs/BUILDING.md](docs/BUILDING.md) for detailed troubleshooting.** ## More Information
## Getting Help - [docs/INVENTORY.md](docs/INVENTORY.md) - Host configuration
- [docs/NAMESPACE.md](docs/NAMESPACE.md) - All option references
- Review documentation in `docs/` directory - [docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md) - User management
- Check templates: `templates/user/` and `templates/system/` - [docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md) - External modules
- Contact Innovation Factory IT team - [docs/BUILDING.md](docs/BUILDING.md) - Building and deployment
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) - Development guide

Submodule assets/plymouth-theme deleted from 8658f4fb40

View File

@@ -1,11 +1,13 @@
# Building Installation Media # Building Installation Media and Artifacts
This guide covers building installer ISOs, live images, and container artifacts from the nixos-systems flake. Guide to building installer ISOs, live images, and container artifacts.
## Table of Contents ## Table of Contents
- [Quick Start](#quick-start) - [Quick Start](#quick-start)
- [Available Artifacts](#available-artifacts) - [Available Artifacts](#available-artifacts)
- [Building Locally](#building-locally)
- [Building from Remote](#building-from-remote)
- [Installer ISOs](#installer-isos) - [Installer ISOs](#installer-isos)
- [Live ISOs](#live-isos) - [Live ISOs](#live-isos)
- [Container Images](#container-images) - [Container Images](#container-images)
@@ -15,116 +17,194 @@ This guide covers building installer ISOs, live images, and container artifacts
## Quick Start ## Quick Start
```bash ```bash
# Build an installer ISO for a specific host # List all available artifacts
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1 nix flake show
# Result will be in result/iso/ # Build installer ISO for a specific host
nix build .#installer-iso-nix-laptop1
# Result is in result/iso/
ls -lh result/iso/ ls -lh result/iso/
``` ```
## Available Artifacts ## Available Artifacts
List all available build outputs: Athenix can build multiple artifact types for deployment:
```bash | Type | Description | Location | Use Case |
nix flake show git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git |------|-------------|----------|----------|
| `installer-iso-*` | Auto-install ISO | `result/iso/` | Install NixOS to disk |
| `iso-*` | Live ISO | `result/iso/` | Boot without installing |
| `ipxe-*` | PXE netboot | `result/` | Diskless netboot systems |
| `lxc-*` | LXC container | `result/tarball/` | LXC/Proxmox containers |
| `proxmox-*` | Proxmox VMA | `result/` | Proxmox VM templates |
Set artifact types per-host via `athenix.host.buildMethods` in `inventory.nix`:
```nix
nix-laptop = {
devices = 5;
overrides.athenix.host.buildMethods = [ "installer-iso" ];
};
nix-lxc = {
devices.builder = {
athenix.host.buildMethods = [ "lxc" "proxmox" ];
};
};
``` ```
Common artifact types: ## Building Locally
| Artifact Type | Description | Example | Build artifacts on your local machine:
|--------------|-------------|---------|
| `installer-iso-*` | Auto-install ISO that installs configuration to disk | `installer-iso-nix-laptop1` |
| `iso-*` | Live ISO (bootable without installation) | `iso-nix-ephemeral1` |
| `ipxe-*` | iPXE netboot artifacts (kernel, initrd, script) | `ipxe-nix-ephemeral1` |
| `lxc-*` | LXC container tarball | `lxc-nix-builder` |
| `proxmox-*` | Proxmox VMA archive | `proxmox-nix-builder` |
## Installer ISOs
Installer ISOs automatically install the NixOS configuration to disk on first boot.
### Building Locally
```bash ```bash
# Build installer for a specific host # Build installer ISO
nix build .#installer-iso-nix-laptop1 nix build .#installer-iso-nix-laptop1
# Result location
ls -lh result/iso/nixos-*.iso
# Copy to USB drive (replace /dev/sdX with your USB device)
sudo dd if=result/iso/nixos-*.iso of=/dev/sdX bs=4M status=progress
```
### Building from Gitea
```bash
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1
```
### Using the Installer
1. Boot from the ISO
2. The system will automatically partition the disk and install NixOS
3. After installation completes, remove the USB drive and reboot
4. Log in with the configured user credentials
**Note:** The installer will **erase all data** on the target disk specified in `athenix.host.filesystem.device`.
## Live ISOs
Live ISOs boot into a temporary system without installing to disk. Useful for:
- Testing configurations
- Recovery operations
- Ephemeral/stateless systems
### Building Live ISOs
```bash
# Build live ISO # Build live ISO
nix build .#iso-nix-ephemeral1 nix build .#iso-nix-ephemeral1
# Result location # Build LXC container
ls -lh result/iso/nixos-*.iso nix build .#lxc-nix-builder
# Build all available outputs
nix build .#
``` ```
### Stateless Kiosk Systems **Result locations:**
- ISOs: `result/iso/nixos-*.iso`
- LXC: `result/tarball/nixos-*.tar.xz`
- Proxmox: `result/`
- iPXE: `result/` (kernel, initrd, script)
For PXE netboot kiosks, use the `ipxe-*` artifacts: ### Build Specific Host
```bash ```bash
# Build iPXE artifacts # Get list of all hosts
nix build .#ipxe-nix-ephemeral1 nix eval .#nixosConfigurations --apply builtins.attrNames
# Result contains: # Build specific host
# - bzImage (kernel) nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
# - initrd (initial ramdisk) ```
# - netboot.ipxe (iPXE script)
ls -lh result/ ## Building from Remote
Build from the Gitea repository without cloning:
```bash
# Build installer ISO
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1
# Build LXC container
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#lxc-nix-builder
# Use specific branch or revision
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git?ref=develop#installer-iso-nix-laptop1
```
## Installer ISOs
Installer ISOs automatically partition and install NixOS on first boot.
### Building
```bash
nix build .#installer-iso-nix-laptop1
ls -lh result/iso/
```
### Burning to USB
```bash
# Find USB device (be careful!)
lsblk
# Burn ISO to USB (replace sdX with your device)
sudo dd if=result/iso/nixos-*.iso of=/dev/sdX bs=4M status=progress
# Sync and eject
sudo sync && sudo eject /dev/sdX
```
### Installation Process
1. Boot from the USB drive
2. System automatically boots into installer
3. Installer partitions disk according to `athenix.host.filesystem`
4. NixOS is installed and configured
5. System reboots automatically
6. Log in with configured user
**Note:** Installer will erase all data on the target disk specified in `athenix.host.filesystem.device`.
### Installer Configuration
Customize installer via host configuration:
```nix
nix-laptop = {
devices = 5;
overrides = {
athenix.host.filesystem.device = "/dev/nvme0n1";
athenix.host.filesystem.swapSize = "32G";
athenix.host.buildMethods = [ "installer-iso" ];
};
};
```
## Live ISOs
Live ISOs boot into a temporary system without installing to disk.
### Building
```bash
nix build .#iso-nix-ephemeral1
```
### Usage
Live ISOs are useful for:
- Testing configurations before installation
- Recovery operations
- Ephemeral/stateless systems
- Booting in kiosk mode
### Customizing Live ISO
```nix
nix-ephemeral = {
devices.live = {
athenix.sw.type = "stateless-kiosk";
athenix.sw.kioskUrl = "https://dashboard.example.com";
athenix.host.buildMethods = [ "iso" ];
};
};
``` ```
## Container Images ## Container Images
### LXC Containers ### LXC Containers
Build LXC container tarballs for Proxmox or other LXC hosts: Build LXC container tarballs for Proxmox or standalone LXC:
```bash ```bash
# Build LXC tarball
nix build .#lxc-nix-builder nix build .#lxc-nix-builder
ls -lh result/tarball/
# Result location
ls -lh result/tarball/nixos-*.tar.xz
``` ```
**Importing to Proxmox:** #### Importing to Proxmox
1. Copy tarball to Proxmox host:
```bash ```bash
# Copy tarball to Proxmox host
scp result/tarball/nixos-*.tar.xz root@proxmox:/var/lib/vz/template/cache/ scp result/tarball/nixos-*.tar.xz root@proxmox:/var/lib/vz/template/cache/
```
# Create container from Proxmox CLI 2. Create container:
```bash
pct create 100 local:vztmpl/nixos-*.tar.xz \ pct create 100 local:vztmpl/nixos-*.tar.xz \
--hostname nix-builder \ --hostname nix-builder \
--memory 4096 \ --memory 4096 \
@@ -132,25 +212,59 @@ pct create 100 local:vztmpl/nixos-*.tar.xz \
--net0 name=eth0,bridge=vmbr0,ip=dhcp --net0 name=eth0,bridge=vmbr0,ip=dhcp
``` ```
See [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) for detailed Proxmox deployment instructions. 3. Start and log in:
```bash
pct start 100
pct shell 100
```
#### Proxmox Integration
For detailed Proxmox deployment instructions, see [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md).
### Proxmox VMA ### Proxmox VMA
Build Proxmox-specific VMA archives: Build Proxmox-specific VMA archives:
```bash ```bash
# Build Proxmox VMA
nix build .#proxmox-nix-builder nix build .#proxmox-nix-builder
# Result location
ls -lh result/ ls -lh result/
``` ```
VMA files can be imported directly into Proxmox for rapid VM creation.
## iPXE / Network Boot
Build iPXE artifacts for diskless PXE boot systems:
```bash
nix build .#ipxe-nix-ephemeral1
ls -lh result/
```
Artifacts include:
- `bzImage` - Linux kernel
- `initrd` - Initial ramdisk
- `netboot.ipxe` - iPXE boot script
### iPXE Setup
Configure your PXE server to boot from these artifacts:
```ipxe
kernel tftp://server/bzImage
initrd tftp://server/initrd
boot
```
See [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) for detailed network boot setup.
## Remote Builders ## Remote Builders
Speed up builds by offloading to build servers. Speed up builds by offloading to build servers.
### One-Time Remote Build ### One-Time Build
```bash ```bash
nix build .#installer-iso-nix-laptop1 \ nix build .#installer-iso-nix-laptop1 \
@@ -159,7 +273,7 @@ nix build .#installer-iso-nix-laptop1 \
### Persistent Configuration ### Persistent Configuration
Add to `~/.config/nix/nix.conf` or `/etc/nix/nix.conf`: Add to `~/.config/nix/nix.conf`:
```conf ```conf
builders = ssh://engr-ugaif@nix-builder x86_64-linux builders = ssh://engr-ugaif@nix-builder x86_64-linux
@@ -171,12 +285,12 @@ Then build normally:
nix build .#installer-iso-nix-laptop1 nix build .#installer-iso-nix-laptop1
``` ```
### SSH Key Setup ### SSH Setup
For remote builders, ensure SSH keys are configured: Ensure SSH is configured for the builder:
```bash ```bash
# Generate SSH key if needed # Generate key if needed
ssh-keygen -t ed25519 ssh-keygen -t ed25519
# Copy to builder # Copy to builder
@@ -188,77 +302,86 @@ ssh engr-ugaif@nix-builder
### Multiple Builders ### Multiple Builders
Configure multiple build servers:
```conf ```conf
builders = ssh://engr-ugaif@nix-builder x86_64-linux ; ssh://engr-ugaif@nix-builder2 x86_64-linux builders = ssh://engr-ugaif@nix-builder1 x86_64-linux ; ssh://engr-ugaif@nix-builder2 x86_64-linux
``` ```
### Automatic Remote Build (Tablets)
Surface tablets are configured to automatically use remote builders:
```nix
athenix.sw.remoteBuild = {
enable = true;
hosts = [ "nix-builder" ];
};
```
This speeds up builds on resource-constrained devices.
## Troubleshooting ## Troubleshooting
### Build Errors ### Build Errors
**Check configuration validity:** Get detailed error information:
```bash ```bash
# Verbose error traces
nix build .#installer-iso-nix-laptop1 --show-trace
# Check all configurations first
nix flake check --show-trace nix flake check --show-trace
``` ```
**Test specific host build:**
```bash
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
```
### Remote Builder Issues
**Test SSH access:**
```bash
ssh engr-ugaif@nix-builder
```
**Check builder disk space:**
```bash
ssh engr-ugaif@nix-builder df -h
```
**Temporarily disable remote builds:**
In `inventory.nix`:
```nix
athenix.sw.remoteBuild.enable = false;
```
### Out of Disk Space ### Out of Disk Space
**Clean up Nix store:**
```bash ```bash
# Clean up Nix store
nix-collect-garbage -d nix-collect-garbage -d
# Optimize store
nix store optimise nix store optimise
``` ```
**Check space:** ### Build Hangs
```bash ```bash
df -h /nix # List processes
ps aux | grep nix
# Cancel build
Ctrl+C
``` ```
### ISO Won't Boot ### Finding Artifact Outputs
**Verify ISO integrity:**
```bash ```bash
sha256sum result/iso/nixos-*.iso # List all buildable outputs
nix flake show
# Check specific output exists
nix flake show | grep installer-iso-nix-laptop1
# Get path to output
nix build .#installer-iso-nix-laptop1 --no-link
``` ```
**Check USB write:** ### Build Not Creating Expected File
```bash
# Use correct block size and sync
sudo dd if=result/iso/nixos-*.iso of=/dev/sdX bs=4M status=progress && sync
```
**Try alternative boot mode:** ```bash
- UEFI systems: Try legacy BIOS mode # Check build log
- Legacy BIOS: Try UEFI mode nix build .#installer-iso-nix-laptop1 -L
# Check what's in result
ls -la result/
# Inspect NixOS build structure
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel -L
```
## See Also ## See Also
- [DEVELOPMENT.md](DEVELOPMENT.md) - Development workflow
- [INVENTORY.md](INVENTORY.md) - Host configuration
- [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) - Proxmox deployment
- [README.md](../README.md) - Main documentation - [README.md](../README.md) - Main documentation
- [INVENTORY.md](INVENTORY.md) - Host configuration guide
- [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) - Proxmox deployment guide

View File

@@ -1,464 +1,456 @@
# Development Guide # Development Guide
This guide covers development workflows for maintaining and extending the nixos-systems repository. Comprehensive guide for maintaining and extending Athenix.
## Table of Contents ## Table of Contents
- [Prerequisites](#prerequisites) - [Prerequisites](#prerequisites)
- [Development Workflow](#development-workflow)
- [Testing Changes](#testing-changes) - [Testing Changes](#testing-changes)
- [Continuous Integration](#continuous-integration) - [Continuous Integration](#continuous-integration)
- [System Rebuilds](#system-rebuilds) - [Common Tasks](#common-tasks)
- [Updating Dependencies](#updating-dependencies) - [Debugging](#debugging)
- [Adding Packages](#adding-packages) - [Troubleshooting](#troubleshooting)
- [Python Development](#python-development)
- [Contributing](#contributing)
## Prerequisites ## Prerequisites
Install Nix with flakes support: ### Install Nix with Flakes
```bash ```bash
# Recommended: Determinate Systems installer (includes flakes) # Recommended: Determinate Systems installer
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Alternative: Official installer (requires enabling flakes manually) # Or official installer
sh <(curl -L https://nixos.org/nix/install) --daemon sh <(curl -L https://nixos.org/nix/install) --daemon
# Enable flakes in existing installation
mkdir -p ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
```
### Clone Repository
```bash
git clone https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git
cd athenix
# Optional: enable direnv for automatic Nix environment
direnv allow
```
## Development Workflow
### Making Changes
1. **Edit configuration files** - Modify `inventory.nix`, `users.nix`, or host/software config
2. **Validate** - Check syntax and configuration
```bash
nix flake check
```
3. **Format code** - Apply consistent formatting
```bash
nix fmt
```
4. **Test** - Build specific artifacts or configurations
```bash
# Test specific host
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
# Or build an artifact
nix build .#installer-iso-nix-laptop1
```
5. **Commit and push**
```bash
git add .
git commit -m "Brief description of changes"
git push
```
### Example: Adding a New User
1. Define user in `users.nix`:
```nix
athenix.users.newuser = {
description = "New User";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "..."; # mkpasswd -m sha-512
};
```
2. Enable on fleet in `inventory.nix`:
```nix
nix-laptop = {
devices = 5;
overrides.athenix.users.newuser.enable = true;
};
```
3. Validate and commit:
```bash
nix flake check
nix fmt
git add . && git commit -m "Add newuser account"
git push
``` ```
## Testing Changes ## Testing Changes
Always test configuration changes before committing. ### Validate Configuration Syntax
### Validate All Configurations Always run before committing:
```bash ```bash
# Check all configurations build correctly
nix flake check nix flake check
```
# Check with verbose error traces Shows any configuration errors across all ~50+ hosts. Output:
nix flake check --show-trace
```
checking 50 configurations...
✓ All checks passed
``` ```
### Test Specific Host Build ### Test Specific Host Build
```bash ```bash
# Build a specific host's configuration # Build specific host (shows if config actually compiles)
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
# Build installer for specific host # Shorter form
nix build .#installer-iso-nix-laptop1 nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel -L
``` ```
### Test Local Changes ### Test Installer Build
If you're on a NixOS system managed by this flake:
```bash ```bash
# Test changes without committing (temporary, doesn't survive reboot) # Test that installer ISO builds
nix build .#installer-iso-nix-laptop1 -L
```
### Test on Running NixOS System
If you're on a NixOS system managed by Athenix:
```bash
# Test changes temporarily (won't survive reboot)
sudo nixos-rebuild test --flake . sudo nixos-rebuild test --flake .
# Apply and switch to new configuration # Apply and switch (persistent)
sudo nixos-rebuild switch --flake . sudo nixos-rebuild switch --flake .
# Build without switching # Build without switching
sudo nixos-rebuild build --flake . sudo nixos-rebuild build --flake .
# Show what will change
sudo nixos-rebuild dry-activate --flake .
```
### Rollback
If a build breaks your system:
```bash
# List recent generations
nix-env --list-generations
# Rollback to previous generation
nix-env --rollback
# Or switch to specific generation
nix-env --switch-generation 42
``` ```
## Continuous Integration ## Continuous Integration
The repository uses Gitea Actions for automated testing and validation. CI jobs run on the self-hosted `nix-builder` machine. ### CI Pipeline
### CI Workflow All pushes and pull requests trigger automated tests on the self-hosted `nix-builder`:
All pull requests and pushes to main trigger the CI pipeline, which includes: 1. **Flake Check** - `nix flake check` validates all 50+ configurations
2. **Format Check** - Verifies code formatted with `nix fmt`
1. **Flake Check** - Validates all NixOS configurations 3. **Build Key Hosts** - Builds `nix-builder`, `nix-laptop1`, `nix-desktop1`
- Runs `nix flake check` to ensure all systems build correctly 4. **Build Artifacts** - Tests `lxc-nix-builder` and `installer-iso-nix-laptop1`
- Catches configuration errors early
2. **Format Check** - Ensures code formatting consistency
- Verifies code is formatted with `nix fmt`
- Automatically fails if formatting is incorrect
3. **Build Key Configurations** - Tests critical system builds
- Builds: `nix-builder`, `nix-laptop1`, `nix-desktop1`
- Ensures core configurations compile successfully
4. **Build Artifacts** - Validates installer and container builds
- Builds: `lxc-nix-builder`, `installer-iso-nix-laptop1`
- Verifies deployment artifacts are buildable
### Viewing CI Status ### Viewing CI Status
Check the CI status badge at the top of the README or view detailed logs:
```bash ```bash
# View workflow status # Web interface
https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions
# Or check locally
git log --oneline -n 5
# Look for ✓ or ✗ next to commits
``` ```
### Running CI Checks Locally ### Running CI Checks Locally
Before pushing changes, run the same checks that CI performs: Test before pushing:
```bash ```bash
# Run all checks # Flake check
nix flake check --show-trace nix flake check --show-trace
# Check formatting # Format check
nix fmt nix fmt --check
git diff --exit-code # Should return no changes
# Build specific configuration # Format code
nix build .#nixosConfigurations.nix-builder.config.system.build.toplevel nix fmt **/*.nix
# Build artifacts # Build key configurations
nix build .#lxc-nix-builder nix build .#nixosConfigurations.nix-builder.config.system.build.toplevel -L
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel -L
``` ```
### Self-Hosted Runner ## Common Tasks
CI jobs run on the `nix-builder` host as a self-hosted Gitea Actions runner. This provides: ### Adding a New Host
- Native Nix environment without installation overhead Edit `inventory.nix`:
- Access to local Nix store for faster builds
- Consistent build environment matching deployment targets
- Direct access to build caching infrastructure
#### Setting Up the Gitea Actions Runner ```nix
nix-surface = {
The nix-builder host is configured with a Gitea Actions self-hosted runner in `inventory.nix`. To complete the setup: devices = 3; # Creates nix-surface1, nix-surface2, nix-surface3
overrides = {
1. **Generate a Gitea Runner Token**: athenix.sw.type = "tablet-kiosk";
- Go to https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/settings/actions/runners athenix.sw.kioskUrl = "https://dashboard.example.com";
- Click "Create new Runner" };
- Copy the registration token };
2. **Create the token file on nix-builder**:
```bash
ssh engr-ugaif@nix-builder
echo "YOUR_TOKEN_HERE" | sudo tee /var/lib/gitea-runner-token > /dev/null
sudo chmod 600 /var/lib/gitea-runner-token
``` ```
3. **Rebuild the system** to start the runner: Test:
```bash
sudo nixos-rebuild switch --flake git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#nix-builder
```
4. **Verify the runner is registered**:
- Check https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/settings/actions/runners
- The runner should appear with the `nix-builder` label
The runner service is configured in the nix-builder device configuration and will automatically:
- Register with the repository on first start
- Use the `nix-builder` label for workflow targeting
- Run as the `engr-ugaif` user
- Store work in `/var/lib/gitea-runner`
### Troubleshooting CI Failures
If CI fails:
1. **Check the error logs** in the Gitea Actions tab
2. **Run the same command locally** to reproduce the issue
3. **Use `--show-trace`** for detailed error information
4. **Verify formatting** with `nix fmt` if format check fails
5. **Check for external dependencies** that might be unavailable
Common CI issues:
- **Flake check fails**: Configuration error in a host definition
- **Format check fails**: Run `nix fmt` locally and commit changes
- **Build fails**: Missing dependency or syntax error in Nix expressions
- **Cache issues**: Usually self-resolving; can retry the workflow
## System Rebuilds
### From Local Directory
```bash ```bash
# Rebuild current host from local directory
sudo nixos-rebuild switch --flake .
# Rebuild specific host
sudo nixos-rebuild switch --flake .#nix-laptop1
# Test without switching (temporary, doesn't persist reboot)
sudo nixos-rebuild test --flake .#nix-laptop1
# Build a new generation without activating it
sudo nixos-rebuild build --flake .
```
### From GitHub
```bash
# Rebuild from GitHub main branch
sudo nixos-rebuild switch --flake git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git
# Use --impure for external user configurations with fetchGit
sudo nixos-rebuild switch --flake git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git --impure
# Rebuild specific host from GitHub
sudo nixos-rebuild switch --flake git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#nix-laptop1
```
### Boot into Previous Generation
If something breaks:
```bash
# List generations
sudo nixos-rebuild list-generations
# Rollback to previous generation
sudo nixos-rebuild switch --rollback
# Or select specific generation at boot (GRUB menu)
# Reboot and select "NixOS - Configuration X" from boot menu
```
## Updating Dependencies
### Update All Inputs
```bash
# Update all flake inputs (nixpkgs, home-manager, etc.)
nix flake update
# Review changes
git diff flake.lock
# Test the updates
nix flake check nix flake check
nix build .#installer-iso-nix-surface1 -L
# Commit if successful
git add flake.lock
git commit -m "Update flake inputs"
git push
``` ```
### Update Specific Input ### Modifying Software Configuration
Edit appropriate file in `sw/`:
```bash ```bash
# Update only nixpkgs # Desktop software
nix flake lock --update-input nixpkgs
# Update home-manager
nix flake lock --update-input home-manager
# Update multiple specific inputs
nix flake lock --update-input nixpkgs --update-input home-manager
```
### Check for Security Updates
```bash
# After updating, check for known vulnerabilities
nix flake check
# Review nixpkgs changelog
git log HEAD..nixpkgs/nixos-25.11 --oneline | head -20
```
## Adding Packages
### System-Wide Packages by Type
Add packages based on system type:
**Desktop systems:**
```bash
# Edit sw/desktop/programs.nix
vim sw/desktop/programs.nix vim sw/desktop/programs.nix
# Or for all systems
vim sw/default.nix
``` ```
**Tablet kiosks:** Use `athenix.sw.extraPackages` for host-specific additions:
```bash
# Edit sw/tablet-kiosk/programs.nix
vim sw/tablet-kiosk/programs.nix
```
**Headless systems:**
```bash
# Edit sw/headless/programs.nix
vim sw/headless/programs.nix
```
### Packages for Specific Hosts
Add to `athenix.sw.extraPackages` in `inventory.nix`:
```nix ```nix
nix-laptop = { nix-laptop = {
devices = 2; devices = 5;
overrides = { overrides.athenix.sw.extraPackages = with pkgs; [ special-tool ];
athenix.sw.extraPackages = with pkgs; [ };
vim ```
docker
kubernetes-helm ### Adding a System Type
Create new type in `sw/`:
```bash
mkdir -p sw/my-type
touch sw/my-type/{default.nix,programs.nix,services.nix}
```
Then reference in `sw/default.nix`:
```nix
{
imports = [
./my-type/default.nix
# ... other types
]; ];
}
```
### Using External Configurations
For user dotfiles:
```nix
# users.nix
athenix.users.myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123..."; # Pin to commit
};
```
For system config:
```nix
# inventory.nix
nix-lxc = {
devices."server".external = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config";
rev = "abc123...";
}; };
}; };
``` ```
### User-Specific Packages ### Updating Dependencies
Add to user's home-manager configuration in their external `user.nix`:
```nix
# In external user.nix
home.packages = with pkgs; [
ripgrep
fd
bat
];
```
### Search for Packages
```bash ```bash
# Search nixpkgs # Update all flake inputs
nix search nixpkgs firefox nix flake update
nix search nixpkgs python3
# Show package details # Update specific input
nix eval nixpkgs#firefox.meta.description nix flake update nixpkgs
# Show what changed
git diff flake.lock
# Test after update
nix flake check --show-trace
# If tests pass, commit
git add flake.lock && git commit -m "Update dependencies"
``` ```
## Python Development ## Debugging
All systems include modern Python tools: `pixi` and `uv`. ### Verbose Output
### Pixi (Recommended for Projects) Get detailed error messages:
```bash ```bash
# Initialize new project # Show full error traces
pixi init my-project nix flake check --show-trace
cd my-project
# Add dependencies # With maximum verbosity
pixi add pandas numpy matplotlib jupyter nix build .#installer-iso-nix-laptop1 -vvv
# Run Python # Show build log
pixi run python nix build .#installer-iso-nix-laptop1 -L
# Run Jupyter
pixi run jupyter notebook
# Run scripts
pixi run python script.py
# Shell with dependencies
pixi shell
``` ```
### uv (Quick Virtual Environments) ### Inspect Configuration
```bash ```bash
# Create virtual environment # Evaluate configuration for specific host
uv venv nix eval .#nixosConfigurations.nix-laptop1.config.athenix.sw --json
# Activate # Get all host names
source .venv/bin/activate nix eval .#nixosConfigurations --apply builtins.attrNames
# Install packages # Check specific option
uv pip install requests pandas nix eval .#nixosConfigurations.nix-laptop1.config.users.users
# Freeze requirements
uv pip freeze > requirements.txt
# Install from requirements
uv pip install -r requirements.txt
``` ```
### System Python ### Test Module Loading
Python development tools are configured in `sw/python.nix` and can be controlled via:
```nix
athenix.sw.python.enable = true; # Default: enabled
```
## Contributing
### Code Style
- Run formatter before committing: `nix fmt`
- Follow existing code structure and conventions
- Add comments for complex logic
- Use the `athenix.*` namespace for all custom options
### Testing Workflow
1. Make changes
2. Run formatter: `nix fmt`
3. Test locally: `nix flake check`
4. Test specific builds if needed
5. Commit changes
6. Push to GitHub
```bash ```bash
# Full workflow # Evaluate specific module
nix fmt nix-build -A nixosConfigurations.nix-laptop1.config.system.build.toplevel
nix flake check
git add . # Or with flakes
git commit -m "Description of changes" nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel --verbose
git push
``` ```
### Documentation ### Check Derivation Dependencies
Update relevant documentation when making changes:
- `README.md` - Overview and quick start
- `docs/INVENTORY.md` - Inventory configuration
- `docs/NAMESPACE.md` - Configuration options
- `USER_CONFIGURATION.md` - User management
- `EXTERNAL_MODULES.md` - External modules
### Creating Issues
When reporting bugs or requesting features:
1. Check existing issues first
2. Provide clear description
3. Include error messages and traces
4. Specify which hosts are affected
5. Include `flake.lock` info if relevant
## Useful Commands
```bash ```bash
# Show all available outputs # Show what dependencies a build needs
nix flake show nix show-derivation .#installer-iso-nix-laptop1
# Evaluate specific option # Or human-readable
nix eval .#nixosConfigurations.nix-laptop1.config.networking.hostName nix build .#installer-iso-nix-laptop1 --dry-run
```
## Troubleshooting
### Common Errors
#### "Evaluation error"
```
error: evaluation aborted with the following error message: '...'
```
**Solution:** Check syntax in modified files. Use `nix fmt` and `nix flake check --show-trace`.
#### "Unknown variable" or "Option does not exist"
```
error: The option `athenix.xyz' does not exist.
```
**Solution:** Check NAMESPACE.md for available options. Options must be in `athenix.*` namespace.
#### "Hash mismatch" (for external modules)
```
error: Hash mismatch in fetched input
```
**Solution:** Update the pin. For `builtins.fetchGit`, use actual commit hash. Or:
```bash
nix flake update
```
#### Build runs out of memory
```bash
# Reduce parallel jobs
nix build . --max-jobs 1
```
#### "No such file or directory" in build
```bash
# Check path exists
ls -la /path/to/file
# Or check relative to repo
ls -la sw/my-file.nix
```
### Helpful Diagnostics
```bash
# List all hosts # List all hosts
nix eval .#nixosConfigurations --apply builtins.attrNames nix eval .#nixosConfigurations --apply builtins.attrNames
# Check flake metadata # Show flake structure
nix flake metadata nix flake show | head -50
# Show evaluation trace # Check Nix store size
nix eval --show-trace .#nixosConfigurations.nix-laptop1 du -sh /nix/store
# Build and enter debug shell # List top space users in store
nix develop nix store du --human-readable | head -20
# Clean up old generations # Find store paths for a package
nix-collect-garbage -d nix store path-info -rS $(which some-package)
# Optimize Nix store
nix store optimise
``` ```
### Getting Help
1. **Check documentation** - Review relevant doc file
2. **Look at existing examples** - Check `inventory.nix` or `users.nix`
3. **Search for similar patterns** - `grep -r "athenix.option" .`
4. **Run tests locally** - `nix flake check --show-trace` with full output
5. **Review git history** - `git log --patch -- filename.nix`
## See Also ## See Also
- [README.md](../README.md) - Main documentation - [BUILDING.md](BUILDING.md) - Building artifacts
- [INVENTORY.md](INVENTORY.md) - Host inventory configuration - [INVENTORY.md](INVENTORY.md) - Host configuration
- [BUILDING.md](BUILDING.md) - Building installation media - [NAMESPACE.md](NAMESPACE.md) - Configuration options
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management - [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External modules
- [README.md](../README.md) - Main documentation

View File

@@ -1,6 +1,6 @@
# External Configuration Modules # External Configuration Modules
This guide explains how to use external modules for system and user configurations in nixos-systems. Guide to using external modules for system and user configurations.
## Table of Contents ## Table of Contents
@@ -8,116 +8,141 @@ This guide explains how to use external modules for system and user configuratio
- [System Modules](#system-modules) - [System Modules](#system-modules)
- [User Modules](#user-modules) - [User Modules](#user-modules)
- [Fetch Methods](#fetch-methods) - [Fetch Methods](#fetch-methods)
- [Templates](#templates) - [Creating External Modules](#creating-external-modules)
- [Integration Details](#integration-details) - [Best Practices](#best-practices)
## Overview ## Overview
External modules allow you to maintain configurations in separate Git repositories and reference them from `inventory.nix` (for systems) or `users.nix` (for users). External modules allow you to maintain configurations in separate Git repositories and reference them from Athenix.
**Benefits:** **Benefits:**
- **Separation:** Keep configs in separate repositories - **Separation** - Keep complex configs in separate repositories
- **Versioning:** Pin to specific commits for reproducibility - **Reproducibility** - Pin specific commits for deterministic builds
- **Reusability:** Share configurations across deployments - **Reusability** - Share configurations across multiple deployments
- **Flexibility:** Mix external modules with local overrides - **Flexibility** - Mix external modules with local configuration
- **Ownership** - Users maintain their own dotfiles
## System Modules ## System Modules
External system modules provide complete NixOS configurations for hosts. External system modules provide host-specific NixOS configurations.
### Usage in inventory.nix ### Usage
In `inventory.nix`, reference an external module using the `external` field:
```nix ```nix
nix-lxc = { nix-lxc = {
devices = { devices = {
# Traditional inline configuration # Inline configuration (traditional method)
"local-server" = { "local-server" = {
athenix.users.admin.enable = true; athenix.sw.type = "headless";
services.nginx.enable = true; services.nginx.enable = true;
}; };
# External module from Git # External module (lazy evaluation - fetched only when building this host)
"remote-server" = builtins.fetchGit { "remote-server".external = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config"; url = "https://git.factory.uga.edu/org/server-config";
rev = "abc123..."; # Pin to specific commit rev = "abc123def456..."; # Must pin to specific commit
};
# External module with additional local config
"mixed-server" = {
external = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config";
rev = "abc123def456...";
};
# Additional local overrides
athenix.users.admin.enable = true;
services.openssh.permitRootLogin = "no";
}; };
}; };
}; };
``` ```
### External Repository Structure **Key Features:**
- **Lazy Evaluation**: External modules are only fetched when building the specific host
- **Efficient Rebuilds**: Other hosts can be rebuilt without fetching unrelated external modules
- **Submodule Support**: Works with Git submodules without affecting other hosts
### Repository Structure
``` ```
server-config/ server-config/
├── default.nix # Required: NixOS module ├── default.nix # Required: NixOS module
── README.md # Optional: Documentation ── README.md # Recommended: Documentation
└── optional/
├── config/ # Optional: Configuration files
└── scripts/ # Optional: Helper scripts
``` ```
**default.nix:** ### Module Content (default.nix)
```nix ```nix
# The module receives inputs and standard NixOS module parameters
{ inputs, ... }: { inputs, ... }:
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ {
# Your NixOS configuration # Your NixOS configuration
# Use any standard NixOS option or athenix.* options
services.nginx = { services.nginx = {
enable = true; enable = true;
virtualHosts."example.com" = { virtualHosts."example.com" = {
root = "/var/www"; root = "/var/www";
forceSSL = true;
enableACME = true;
}; };
}; };
# Use athenix namespace options # Use athenix options
athenix.users.admin.enable = true;
athenix.sw.type = "headless"; athenix.sw.type = "headless";
athenix.sw.extraPackages = with pkgs; [ git htop ];
# Standard NixOS configuration
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.openssh.enable = true;
} }
``` ```
### What External Modules Receive ### What System Modules Receive
- **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.) - **`inputs`** - All flake inputs (nixpkgs, home-manager, disko, etc.)
- **`config`** - Full NixOS configuration - **`config`** - Current NixOS configuration (read/write)
- **`lib`** - Nixpkgs library functions - **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set - **`pkgs`** - Package set
### Module Integration Order ### Configuration Order
When a host is built, modules are loaded in this order: When a host is built, modules load in this order:
1. User NixOS modules (from `users.nix` - `nixos.nix` files) 1. Hardware type module (from `hw/nix-*.nix`)
2. Host type module (from `hosts/types/`) 2. Common system configuration (from `fleet/common.nix`)
3. Configuration overrides (from `inventory.nix`) 3. Software type module (from `sw/{type}/`)
4. Hostname assignment 4. User NixOS modules (from `users.nix` - `nixos.nix` files)
5. External system module (if using `builtins.fetchGit`) 5. Device-specific overrides (from `inventory.nix`)
6. External system module (if present)
Later modules can override earlier ones using standard NixOS module precedence. Each later module can override earlier ones using standard NixOS precedence rules.
### Template
Create a new system module:
```bash
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system
```
See [templates/system/](../templates/system/) for the complete template.
## User Modules ## User Modules
External user modules provide home-manager configurations (dotfiles, packages, programs). External user modules provide home-manager configurations (dotfiles, environment setup).
### Usage in users.nix ### Usage
In `users.nix`, reference an external user module:
```nix ```nix
athenix.users = { athenix.users = {
# External user module (dotfiles, home-manager, and user options) # External user module
myuser = builtins.fetchGit { myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles"; url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123..."; rev = "abc123def456..."; # Pin to specific commit
}; };
# Inline user definition # Inline user definition
inlineuser = { otheruser = {
description = "Inline User"; description = "Other User";
extraGroups = [ "wheel" ]; extraGroups = [ "wheel" ];
shell = pkgs.zsh; shell = pkgs.zsh;
hashedPassword = "$6$..."; hashedPassword = "$6$...";
@@ -125,148 +150,179 @@ athenix.users = {
}; };
``` ```
### External Repository Structure Then enable on hosts in `inventory.nix`:
```
dotfiles/
├── user.nix # Required: User options AND home-manager config
├── nixos.nix # Optional: System-level config
└── config/ # Optional: Actual dotfiles
├── bashrc
└── vimrc
```
**user.nix (required):**
```nix ```nix
nix-laptop = {
devices = 5;
overrides.athenix.users.myuser.enable = true;
};
```
### Repository Structure
```
my-dotfiles/
├── user.nix # Required: User options + home-manager config
├── nixos.nix # Optional: System-level configuration
├── README.md # Recommended: Documentation
└── config/ # Optional: Your actual dotfiles
├── zshrc
├── vimrc
├── nvim/
└── ...
```
### user.nix (Required)
Provides both user account settings AND home-manager configuration:
```nix
# Receives { inputs } and standard home-manager module parameters
{ inputs, ... }: { inputs, ... }:
{ config, lib, pkgs, osConfig ? null, ... }: { config, lib, pkgs, osConfig ? null, ... }:
{ {
# ========== User Account Configuration ========== # ========== User Account Configuration ==========
# These options define the user account itself
athenix.users.myusername = { athenix.users.myusername = {
description = "Your Full Name"; description = "My Full Name";
extraGroups = [ "wheel" "docker" ];
shell = pkgs.zsh; shell = pkgs.zsh;
hashedPassword = "!"; hashedPassword = "!"; # SSH keys only
opensshKeys = [ "ssh-ed25519 AAAA..." ]; opensshKeys = [
"ssh-ed25519 AAAA... user@laptop"
];
useZshTheme = true; useZshTheme = true;
useNvimPlugins = true; useNvimPlugins = true;
}; };
# ========== Home Manager Configuration ========== # ========== Home Manager Configuration ==========
# User environment, packages, and dotfiles
# Packages # Packages
home.packages = with pkgs; [ home.packages = with pkgs; [
vim vim
git git
htop ripgrep
fzf
] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox; ] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
# Programs
programs.git = { programs.git = {
enable = true; enable = true;
userName = "My Name"; userName = "My Name";
userEmail = "me@example.com"; userEmail = "me@example.com";
extraConfig = {
init.defaultBranch = "main";
core.editor = "vim";
};
};
programs.zsh = {
enable = true;
initExtra = ''
# Your Zsh configuration
export EDITOR=vim
'';
}; };
# Manage dotfiles # Manage dotfiles
home.file.".bashrc".source = ./dotfiles/bashrc; home.file.".zshrc".source = ./config/zshrc;
home.file.".vimrc".source = ./config/vimrc;
home.file.".config/nvim".source = ./config/nvim;
# Services
services.gpg-agent.enable = true;
} }
``` ```
**nixos.nix (optional):** ### nixos.nix (Optional)
System-level configuration for this user (rarely needed):
```nix ```nix
{ inputs, ... }: { inputs, ... }:
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ {
# System-level configuration for this user # System-level configuration
users.users.myuser.extraGroups = [ "docker" ]; # Only needed if the user requires specific system-wide settings
users.users.myusername.extraGroups = [ "docker" ];
environment.systemPackages = [ pkgs.docker ]; environment.systemPackages = [ pkgs.docker ];
# Security settings
security.sudo.extraRules = [{
users = [ "myusername" ];
commands = [{
command = "/usr/bin/something";
options = [ "NOPASSWD" ];
}];
}];
} }
``` ```
### What User Modules Receive ### What User Modules Receive
**In user.nix:** **In user.nix:**
- **`inputs`** - Flake inputs (nixpkgs, home-manager, etc.) - **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.)
- **`config`** - Home-manager configuration - **`config`** - Home-manager configuration (read/write)
- **`lib`** - Nixpkgs library functions - **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set - **`pkgs`** - Package set
- **`osConfig`** - OS-level configuration (read-only) - **`osConfig`** - OS configuration (read-only) - useful for conditional setup
**In nixos.nix:** **In nixos.nix:**
- **`inputs`** - Flake inputs - **`inputs`** - Flake inputs
- **`config`** - NixOS configuration - **`config`** - NixOS configuration (read/write)
- **`lib`** - Nixpkgs library functions - **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set - **`pkgs`** - Package set
### User Options in users.nix ### Conditional Setup Example
Use `osConfig` to conditionally set up dotfiles based on the system type:
```nix ```nix
username = { # In user.nix
# Identity { inputs, ... }:
description = "Full Name"; { config, lib, pkgs, osConfig ? null, ... }:
{
athenix.users.myuser = { /* ... */ };
# External configuration # Install Firefox only on desktop systems
external = builtins.fetchGit { ... }; home.packages = with pkgs; [
# System settings ripgrep
extraGroups = [ "wheel" "networkmanager" ]; ] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
hashedPassword = "$6$...";
opensshKeys = [ "ssh-ed25519 ..." ];
shell = pkgs.zsh;
# Theme integration # Different shell config per system
useZshTheme = true; # Apply system zsh theme (default: true) programs.zsh.initExtra = ''
useNvimPlugins = true; # Apply system nvim config (default: true) ${lib.optionalString (osConfig.athenix.sw.type or null == "headless") "
# Headless-only settings
# Enable on specific systems (see docs/INVENTORY.md) "}
enable = false; # Set in inventory.nix via athenix.users.username.enable '';
}; }
``` ```
### Template
Create a new user module:
```bash
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user
```
See [templates/user/](../templates/user/) for the complete template.
## Fetch Methods ## Fetch Methods
### Recommended: fetchGit with Revision ### builtins.fetchGit (Recommended)
Pin to a specific commit for reproducibility: Pin to a specific Git revision:
```nix ```nix
builtins.fetchGit { builtins.fetchGit {
url = "https://github.com/user/repo"; url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123def456..."; # Full commit hash (40 characters) rev = "abc123def456..."; # Required: specific commit hash
ref = "main"; # Optional: branch name
} }
``` ```
**Finding the commit hash:** **Advantages:**
```bash - Reproducible (pinned to exact commit)
# Latest commit on main branch - Works with any Git repository
git ls-remote https://github.com/user/repo main - Supports SSH or HTTPS URLs
# Or from a local clone **Important:** Always specify `rev` (commit hash) for reproducibility. Don't use branches which can change.
git rev-parse HEAD
```
### fetchGit with Branch (Less Reproducible) ### builtins.fetchTarball
Always fetches latest from branch:
```nix
builtins.fetchGit {
url = "https://github.com/user/repo";
ref = "develop";
}
```
⚠️ **Warning:** Builds may not be reproducible as the branch HEAD can change.
### fetchTarball (For Releases)
Download specific release archives: Download specific release archives:
@@ -287,174 +343,141 @@ nix-prefetch-url --unpack https://github.com/user/repo/archive/v1.0.0.tar.gz
Use local directories during development: Use local directories during development:
```nix ```nix
/home/username/dev/my-config # users.nix
athenix.users.myuser.external = /home/user/my-dotfiles;
# Or relative to repository # inventory.nix
./my-local-config nix-laptop = {
devices = {
"dev".athenix.users.myuser.enable = true;
};
};
``` ```
⚠️ **Warning:** Only for testing. Use Git-based methods for production. **Note:** Only works if the path exists on the machine running `nix flake check` or `nix build`.
## Templates ## Creating External Modules
### System Module Template ### System Module Template
Create a new system module repository from the template:
```bash ```bash
# Initialize in new directory
mkdir my-server-config
cd my-server-config
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system
``` ```
See [templates/system/README.md](../templates/system/README.md) for detailed usage. This creates:
```
my-system-config/
├── flake.nix # Optional: for testing standalone
├── default.nix # Your NixOS module
└── README.md # Documentation
```
### User Module Template ### User Module Template
Create a new user module repository:
```bash ```bash
# Initialize in new directory
mkdir my-dotfiles
cd my-dotfiles
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user
``` ```
See [templates/user/README.md](../templates/user/README.md) for detailed usage. This creates:
## Integration Details ```
my-dotfiles/
### Detection Logic ├── flake.nix # Optional: for testing standalone
├── user.nix # User options + home-manager config
The system automatically detects external modules when a device or user value is: ├── nixos.nix # Optional: system-level config
- A path (`builtins.isPath`) └── README.md # Documentation
- A string starting with `/` (absolute path)
- A derivation (`lib.isDerivation`)
- An attrset with `outPath` attribute (result of `fetchGit`/`fetchTarball`)
### System Module Integration
External system modules are imported and merged into the NixOS configuration:
```nix
import externalModulePath { inherit inputs; }
``` ```
They can use all standard NixOS options plus `athenix.*` namespace options. ### Testing External Modules
### User Module Integration Test your external module locally before pushing:
External user modules are loaded in two contexts: ```bash
# In your module repository
cd /path/to/my-module
**User options (NixOS module context):** # Test the Nix syntax
```nix nix flake check
import (externalPath + "/user.nix") { inherit inputs; }
# Evaluated as NixOS module to extract athenix.users.<username> options
``` ```
**Home-manager configuration:** ## Best Practices
### 1. Always Pin to Specific Commits
❌ Wrong - using branch names:
```nix ```nix
import (externalPath + "/user.nix") { inherit inputs; } builtins.fetchGit {
# Imported into home-manager for home.*, programs.*, services.* options url = "https://git.factory.uga.edu/username/dotfiles";
``` # No rev specified or using "main"
**System-level config (optional):**
```nix
import (externalPath + "/nixos.nix") { inherit inputs; }
# If present, imported as NixOS module for system-level configuration
```
### Combining External and Local Config
You can mix external modules with local overrides:
```nix
nix-lxc = {
devices = {
"server" = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/base-config";
rev = "abc123...";
};
};
overrides = {
# Apply to all devices, including external ones
athenix.users.admin.enable = true;
networking.firewall.allowedTCPPorts = [ 80 443 ];
};
};
```
### Minimal User Module
**user.nix:**
```nix
{ inputs, ... }:
{ config, lib, pkgs, osConfig ? null, ... }:
{
# User account options
athenix.users.myusername = {
description = "My Name";
shell = pkgs.zsh;
hashedPassword = "!";
};
# Home-manager config
home.packages = with pkgs; [ vim git ];
} }
``` ```
### Full User Module with Dotfiles ✅ Correct - using commit hash:
```
dotfiles/
├── user.nix
├── nixos.nix
└── config/
├── bashrc
├── vimrc
└── gitconfig
```
**user.nix:**
```nix ```nix
{ inputs, ... }: builtins.fetchGit {
{ config, lib, pkgs, osConfig ? null, ... }: url = "https://git.factory.uga.edu/username/dotfiles";
{ rev = "abc123def456789...";
# User account configuration
athenix.users.myusername = {
description = "My Full Name";
shell = pkgs.zsh;
extraGroups = [ "wheel" "networkmanager" ];
hashedPassword = "!";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
useZshTheme = true;
useNvimPlugins = true;
};
# Home-manager configuration
home.packages = with pkgs; [
ripgrep
fd
bat
] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
programs.git = {
enable = true;
userName = "My Full Name";
userEmail = "me@example.com";
extraConfig.init.defaultBranch = "main";
};
home.file = {
".bashrc".source = ./config/bashrc;
".vimrc".source = ./config/vimrc;
".gitconfig".source = ./config/gitconfig;
};
} }
``` ```
### 2. Keep External Modules Focused
Each external module should have a clear purpose:
- User dotfiles (one repo per user)
- System service configuration (one repo per service/cluster)
- Hardware-specific config (one repo per hardware setup)
### 3. Document Your Modules
Include a README with:
- What the module configures
- Required dependencies
- Usage examples
- Configuration options
### 4. Use Semantic Versioning
Tag releases in Git:
```bash
git tag v1.0.0
git push origin v1.0.0
```
Reference specific versions:
```nix
builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config";
rev = "v1.0.0"; # Can use tags too
}
```
### 5. Test Before Updating Pins
When updating commit hashes:
```bash
# Test new revision locally
nix flake update
# Validate all configurations
nix flake check --show-trace
# Only commit after validation
git add . && git commit -m "Update module versions"
```
## See Also ## See Also
- [INVENTORY.md](INVENTORY.md) - Host configuration guide - [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management guide - [INVENTORY.md](INVENTORY.md) - Host configuration
- [NAMESPACE.md](NAMESPACE.md) - Configuration options reference - [NAMESPACE.md](NAMESPACE.md) - Configuration options
- [templates/system/](../templates/system/) - System module template
- [templates/user/](../templates/user/) - User module template
- [README.md](../README.md) - Main documentation - [README.md](../README.md) - Main documentation
- [templates/user/](../templates/user/) - User module template
- [templates/system/](../templates/system/) - System module template

View File

@@ -1,101 +1,272 @@
# Host Inventory Configuration # Host Inventory Configuration
This guide explains how to configure hosts in `inventory.nix` to define your fleet of devices. This document explains the `inventory.nix` file, which defines all hosts in your fleet.
## Table of Contents ## Table of Contents
- [Understanding Inventory Structure](#understanding-inventory-structure) - [Overview](#overview)
- [Hostname Generation Rules](#hostname-generation-rules) - [Structure](#structure)
- [Adding Hosts](#adding-hosts) - [Hostname Generation](#hostname-generation)
- [Device Configuration Options](#device-configuration-options) - [Configuration Methods](#configuration-methods)
- [Options](#options)
- [Examples](#examples) - [Examples](#examples)
## Understanding Inventory Structure ## Overview
The `inventory.nix` file defines all hosts in the fleet using a flexible system. Top-level keys are always hostname **prefixes**, and actual hostnames are generated from device configurations. `inventory.nix` defines your fleet of hosts. Top-level keys are hostname **prefixes**, and actual hostnames are generated from device specifications. This allows you to manage large fleets with minimal repetition.
## Hostname Generation Rules **Key concepts:**
- Each top-level key generates one or more NixOS configurations
- Host type defaults to the prefix name (can be overridden)
- System architecture defaults to `x86_64-linux`
- Common configuration can be applied to all devices in a group via `overrides`
- **Numeric suffixes**: no dash (e.g., `nix-laptop1`, `nix-laptop2`) ## Structure
- **Non-numeric suffixes**: with dash (e.g., `nix-laptop-alpha`, `nix-laptop-beta`)
- **Custom hostnames**: Set `athenix.host.useHostPrefix = false` to use suffix as full hostname
## Adding Hosts ```nix
{
"prefix-name" = {
# Optional: Device count or explicit device map
devices = 5; # or { "1" = { ... }; "alpha" = { ... }; }
### Method 1: Quick Count (Simplest) # Optional: Hardware type (defaults to prefix name)
type = "nix-desktop";
# Optional: System architecture
system = "x86_64-linux";
# Optional: Configuration applied to ALL devices in this group
overrides = {
athenix.users.student.enable = true;
};
# Optional: Per-device configuration
"device-suffix" = { ... };
};
}
```
## Hostname Generation
Hostnames are generated automatically based on the device key:
- **Numeric keys** (`"1"`, `"2"`, `"42"`) → no dash: `prefix1`, `prefix2`, `prefix42`
- **Non-numeric keys** (`"alpha"`, `"special"`) → with dash: `prefix-alpha`, `prefix-special`
- **Custom hostnames** → Set `athenix.host.useHostPrefix = false` to use the suffix as the full hostname (no prefix)
**Examples:**
```nix ```nix
nix-laptop = { nix-laptop = {
devices = 5; # Creates: nix-laptop1, nix-laptop2, ..., nix-laptop5 devices = 3; # Generates: nix-laptop1, nix-laptop2, nix-laptop3
};
nix-surface = {
devices = {
"1" = { }; # → nix-surface1
"special" = { }; # → nix-surface-special
};
};
custom-machine = {
devices."lab-machine" = {
athenix.host.useHostPrefix = false; # → lab-machine (not custom-machine-lab-machine)
};
}; };
``` ```
### Method 2: Explicit Count with Overrides ## Configuration Methods
### Method 1: Simple Count
Create N identical hosts:
```nix ```nix
nix-laptop = { nix-laptop = {
devices = 5; devices = 5;
overrides = {
# Applied to ALL nix-laptop hosts
athenix.users.student.enable = true;
athenix.sw.extraPackages = with pkgs; [ vim git ];
};
}; };
# Generates: nix-laptop1, nix-laptop2, nix-laptop3, nix-laptop4, nix-laptop5
``` ```
### Method 3: Individual Device Configuration ### Method 2: Simple Count with Overrides
Create N hosts with common configuration:
```nix
nix-desktop = {
devices = 3;
overrides = {
athenix.users.student.enable = true;
athenix.sw.extraPackages = with pkgs; [ vim git ];
services.openssh.enable = true;
};
};
# All three hosts get the overrides configuration
```
### Method 3: Explicit Device Map
Configure each device individually:
```nix ```nix
nix-surface = { nix-surface = {
devices = { devices = {
"1".athenix.sw.kioskUrl = "https://dashboard1.example.com"; "1".athenix.sw.kioskUrl = "https://dashboard1.example.com";
"2".athenix.sw.kioskUrl = "https://dashboard2.example.com"; "2".athenix.sw.kioskUrl = "https://dashboard2.example.com";
"3".athenix.sw.kioskUrl = "https://dashboard3.example.com"; "3" = {
athenix.sw.kioskUrl = "https://dashboard3.example.com";
services.openssh.enable = false;
};
}; };
}; };
``` ```
### Method 4: Mixed (Default Count + Custom Devices) ### Method 4: External Module
Reference a Git repository using the `external` field (lazy evaluation):
```nix ```nix
nix-surface = { nix-lxc = {
defaultCount = 2; # Creates nix-surface1, nix-surface2 devices."builder".external = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/builder-config";
rev = "abc123...";
};
};
```
### Method 5: Mixed Approach
Combine default count, custom devices, and overrides:
```nix
nix-lab = {
defaultCount = 5; # Creates nix-lab1 through nix-lab5
devices = { devices = {
"special" = { # Creates nix-surface-special "special" = {
athenix.sw.kioskUrl = "https://special-dashboard.example.com"; athenix.sw.extraPackages = with pkgs; [ special-software ];
}; };
}; };
overrides = { overrides = {
# Applied to all devices (including "special") # Applied to all devices (default count + custom)
athenix.sw.kioskUrl = "https://default-dashboard.example.com"; athenix.users.lab-admin.enable = true;
}; };
}; };
``` ```
## Device Configuration Options ## Options
### Direct Configuration (Recommended) ### Top-Level Device Options
Use any NixOS or `athenix.*` option: #### `devices`
Specify hosts to create. Can be:
- **Number**: Create N hosts with keys `"1"`, `"2"`, ..., `"N"`
- **Attribute set**: Map of device names to configurations
**Type**: `int | attrs`
**Examples:**
```nix
devices = 5; # Creates 5 hosts
devices = {
"1" = { };
"alpha" = { };
};
```
#### `defaultCount`
When using a device map, also create N numbered hosts.
**Type**: `int` (optional)
**Example:**
```nix
defaultCount = 3; # Creates "1", "2", "3" in addition to devices map
devices = {
"special" = { };
};
# Result: hosts "1", "2", "3", and "special"
```
#### `type`
Hardware type module to use. Defaults to the prefix name (inferred from top-level key).
**Type**: `string` (optional)
**Options**: `nix-desktop`, `nix-laptop`, `nix-surface`, `nix-lxc`, `nix-wsl`, `nix-ephemeral`
**Example:**
```nix
lab-machines = {
type = "nix-desktop"; # Use desktop hardware configuration
devices = 5;
};
```
#### `system`
System architecture. Defaults to `x86_64-linux`.
**Type**: `string` (optional)
**Example:**
```nix
arm-devices = {
system = "aarch64-linux";
devices = 2;
};
```
#### `overrides`
Configuration applied to all devices in this group. Useful for fleet-wide settings.
**Type**: `attrs` (optional)
**Example:**
```nix
nix-laptop = {
devices = 10;
overrides = {
# Applied to all 10 laptops
athenix.users.staff.enable = true;
services.openssh.enable = true;
boot.loader.timeout = 10;
};
};
```
### Per-Device Options
Any NixOS or `athenix.*` option can be set per-device:
```nix ```nix
nix-surface = {
devices = {
"1" = { "1" = {
# Athenix options # athenix.* namespace options
athenix.users.myuser.enable = true; athenix.users.student.enable = true;
athenix.host.filesystem.swapSize = "64G"; athenix.host.filesystem.device = "/dev/sda";
athenix.sw.extraPackages = with pkgs; [ docker ]; athenix.host.filesystem.swapSize = "16G";
athenix.sw.kioskUrl = "https://example.com"; athenix.sw.kioskUrl = "https://dashboard1.example.com";
athenix.sw.extraPackages = with pkgs; [ firefox ];
# Standard NixOS options # Standard NixOS options
networking.firewall.enable = false; networking.firewall.enable = false;
services.openssh.enable = true; services.openssh.enable = true;
time.timeZone = "America/New_York"; time.timeZone = "America/New_York";
boot.kernelPackages = pkgs.linuxPackages_latest;
};
};
}; };
``` ```
### Convenience: `athenix.forUser` ### Convenience: `athenix.forUser`
Quick setup for single-user systems (especially WSL): Quick setup for single-user systems (especially WSL). This automatically enables a user and sets the WSL default user:
```nix ```nix
nix-wsl = { nix-wsl = {

View File

@@ -1,38 +1,47 @@
# Configuration Namespace Reference # Configuration Namespace Reference
All UGA Innovation Factory-specific options are under the `athenix` namespace to avoid conflicts with standard NixOS options. All UGA Innovation Factory-specific options are in the `athenix` namespace to avoid conflicts with standard NixOS options.
## Table of Contents ## Table of Contents
- [Host Configuration (`athenix.host`)](#host-configuration-athenixhost) - [Host Configuration (`athenix.host`)](#host-configuration-athenixhost)
- [Software Configuration (`athenix.sw`)](#software-configuration-athenixsw) - [Software Configuration (`athenix.sw`)](#software-configuration-athenixsw)
- [User Management (`athenix.users`)](#user-management-athenixusers) - [User Management (`athenix.users`)](#user-management-athenixusers)
- [System Configuration (`athenix.system`)](#system-configuration-athenixsystem)
- [Convenience Options](#convenience-options) - [Convenience Options](#convenience-options)
## Host Configuration (`athenix.host`) ## Host Configuration (`athenix.host`)
Hardware and host-specific settings. Hardware and boot-related settings.
### `athenix.host.filesystem` ### `athenix.host.filesystem.device`
Disk and storage configuration. Boot disk device path.
**Options:** **Type:** String or null
- `athenix.host.filesystem.device` - Boot disk device (default: `/dev/sda`)
- `athenix.host.filesystem.swapSize` - Swap file size (default: `"32G"`) **Default:** `null` (must be set by hardware type or per-host)
**Example:** **Example:**
```nix ```nix
athenix.host.filesystem = { athenix.host.filesystem.device = "/dev/nvme0n1";
device = "/dev/nvme0n1"; ```
swapSize = "64G";
}; ### `athenix.host.filesystem.swapSize`
Swap partition size.
**Type:** String (size with unit, e.g., `"32G"`, `"2G"`) or null
**Default:** `null` (must be set by hardware type or per-host)
**Example:**
```nix
athenix.host.filesystem.swapSize = "64G";
``` ```
### `athenix.host.buildMethods` ### `athenix.host.buildMethods`
List of supported build artifact types for this host. Artifact types to build for this host.
**Type:** List of strings **Type:** List of strings
@@ -40,14 +49,21 @@ List of supported build artifact types for this host.
**Default:** `[ "installer-iso" ]` **Default:** `[ "installer-iso" ]`
**Description:**
- `"installer-iso"` - Installer ISO with auto-install
- `"iso"` - Live ISO (boot without installation)
- `"ipxe"` - iPXE netboot artifacts
- `"lxc"` - LXC container tarball
- `"proxmox"` - Proxmox VMA template
**Example:** **Example:**
```nix ```nix
athenix.host.buildMethods = [ "lxc" "proxmox" ]; athenix.host.buildMethods = [ "installer-iso" "lxc" ];
``` ```
### `athenix.host.useHostPrefix` ### `athenix.host.useHostPrefix`
Whether to prepend the host type prefix to the hostname (used in inventory generation). Whether to prepend the host type prefix to the generated hostname.
**Type:** Boolean **Type:** Boolean
@@ -55,15 +71,19 @@ Whether to prepend the host type prefix to the hostname (used in inventory gener
**Example:** **Example:**
```nix ```nix
athenix.host.useHostPrefix = false; # "builder" instead of "nix-lxc-builder" # With useHostPrefix = true (default)
# Device "1" under "nix-laptop" → "nix-laptop1"
# With useHostPrefix = false
# Device "builder" under "nix-lxc" → "builder" (not "nix-lxc-builder")
athenix.host.useHostPrefix = false;
``` ```
### `athenix.host.wsl` ### `athenix.host.wsl.user`
WSL-specific configuration options. Default WSL user account (only for `nix-wsl` type).
**Options:** **Type:** String (username)
- `athenix.host.wsl.user` - Default WSL user for this instance
**Example:** **Example:**
```nix ```nix
@@ -72,11 +92,11 @@ athenix.host.wsl.user = "myusername";
## Software Configuration (`athenix.sw`) ## Software Configuration (`athenix.sw`)
System software and application configuration. System type, packages, and application configuration.
### `athenix.sw.enable` ### `athenix.sw.enable`
Enable the software configuration module. Enable software configuration.
**Type:** Boolean **Type:** Boolean
@@ -84,28 +104,32 @@ Enable the software configuration module.
### `athenix.sw.type` ### `athenix.sw.type`
System type that determines the software profile. System profile/type. Determines which software packages and services are installed.
**Type:** Enum **Type:** String or list of strings
**Options:** **Options:**
- `"desktop"` - Full desktop environment (GNOME) - `"desktop"` - Full GNOME desktop environment with development tools
- `"tablet-kiosk"` - Surface tablets with kiosk mode browser - `"tablet-kiosk"` - Surface tablets with Firefox kiosk browser
- `"stateless-kiosk"` - Diskless PXE boot kiosks - `"stateless-kiosk"` - Diskless PXE-booted ephemeral systems
- `"headless"` - Servers and containers without GUI - `"headless"` - Servers and containers without GUI
- `"builders"` - Build servers with build dependencies
**Default:** `"desktop"` **Default:** `"desktop"`
**Example:** **Example:**
```nix ```nix
athenix.sw.type = "headless"; athenix.sw.type = "desktop";
# Multiple types supported
athenix.sw.type = [ "desktop" "headless" ];
``` ```
### `athenix.sw.kioskUrl` ### `athenix.sw.kioskUrl`
URL to display in kiosk mode browsers (for `tablet-kiosk` and `stateless-kiosk` types). URL to display in kiosk browser (for `tablet-kiosk` and `stateless-kiosk` types).
**Type:** String **Type:** String (URL)
**Default:** `"https://ha.factory.uga.edu"` **Default:** `"https://ha.factory.uga.edu"`
@@ -114,12 +138,13 @@ URL to display in kiosk mode browsers (for `tablet-kiosk` and `stateless-kiosk`
athenix.sw.kioskUrl = "https://dashboard.example.com"; athenix.sw.kioskUrl = "https://dashboard.example.com";
``` ```
### `athenix.sw.python` ### `athenix.sw.python.enable`
Python development tools configuration. Enable Python development tools (pixi, uv, etc.).
**Options:** **Type:** Boolean
- `athenix.sw.python.enable` - Enable Python tools (pixi, uv) (default: `true`)
**Default:** `true`
**Example:** **Example:**
```nix ```nix
@@ -128,11 +153,13 @@ athenix.sw.python.enable = true;
### `athenix.sw.remoteBuild` ### `athenix.sw.remoteBuild`
Remote build server configuration for offloading builds. Configure remote build servers for offloading builds.
**Type:** Attribute set
**Options:** **Options:**
- `athenix.sw.remoteBuild.enable` - Use remote builders (default: enabled on tablets) - `enable` - Enable remote builders (Boolean, default: `true` for tablets)
- `athenix.sw.remoteBuild.hosts` - List of build server hostnames - `hosts` - List of remote builder hostnames (List of strings)
**Example:** **Example:**
```nix ```nix
@@ -144,7 +171,7 @@ athenix.sw.remoteBuild = {
### `athenix.sw.extraPackages` ### `athenix.sw.extraPackages`
Additional system packages to install beyond the type defaults. Additional system packages beyond the type defaults.
**Type:** List of packages **Type:** List of packages
@@ -154,14 +181,15 @@ Additional system packages to install beyond the type defaults.
```nix ```nix
athenix.sw.extraPackages = with pkgs; [ athenix.sw.extraPackages = with pkgs; [
vim vim
htop
docker docker
htop
ripgrep
]; ];
``` ```
### `athenix.sw.excludePackages` ### `athenix.sw.excludePackages`
Packages to exclude from the default list for this system type. Packages to remove from the default list for this system type.
**Type:** List of packages **Type:** List of packages
@@ -170,78 +198,160 @@ Packages to exclude from the default list for this system type.
**Example:** **Example:**
```nix ```nix
athenix.sw.excludePackages = with pkgs; [ athenix.sw.excludePackages = with pkgs; [
firefox # Remove Firefox from default desktop packages firefox # Don't install Firefox on this system
]; ];
``` ```
## User Management (`athenix.users`) ## User Management (`athenix.users`)
User account configuration and management. User account configuration and access control.
### `athenix.users.<username>.enable` ### `athenix.users.<username>.enable`
Enable a specific user account on this system. Enable a user account on this system.
**Type:** Boolean **Type:** Boolean
**Default:** `false` (except `root` and `engr-ugaif` which default to `true`) **Default:** `false` (except `root` and `engr-ugaif` which are `true`)
**Example:** **Example:**
```nix ```nix
athenix.users = { # In inventory.nix
myuser.enable = true; nix-laptop = {
student.enable = true; devices = 5;
overrides.athenix.users.myuser.enable = true;
}; };
``` ```
### User Account Options ### User Account Options (in `users.nix`)
Each user in `users.nix` can be configured with: Define user accounts in `users.nix` with these options:
#### `description`
Full name or description of the user.
**Type:** String
```nix ```nix
# Option 1: Define inline in users.nix athenix.users.myuser.description = "John Doe";
athenix.users.myuser = { ```
description = "Full Name";
isNormalUser = true; # Default: true
extraGroups = [ "wheel" "docker" ]; # Additional groups
shell = pkgs.zsh; # Login shell
hashedPassword = "$6$..."; # Hashed password
opensshKeys = [ "ssh-ed25519 ..." ]; # SSH public keys
useZshTheme = true; # Use system Zsh theme
useNvimPlugins = true; # Use system Neovim config
enable = false; # Enable per-system in inventory.nix #### `extraGroups`
};
# Option 2: Use external configuration (recommended) Additional Unix groups for the user.
# The external user.nix can set athenix.users.myuser options directly
athenix.users.anotheruser.external = builtins.fetchGit { **Type:** List of strings
**Common groups:**
- `"wheel"` - Sudo access
- `"networkmanager"` - Network configuration
- `"docker"` - Docker access
- `"video"` - Video device access
- `"audio"` - Audio device access
- `"input"` - Input device access (keyboards, mice)
```nix
athenix.users.myuser.extraGroups = [ "wheel" "docker" "networkmanager" ];
```
#### `shell`
Login shell for the user.
**Type:** Package
**Default:** `pkgs.bash`
```nix
athenix.users.myuser.shell = pkgs.zsh;
```
#### `hashedPassword`
Password hash for the user.
**Type:** String (SHA-512 hash)
**Generation:**
```bash
mkpasswd -m sha-512
```
```nix
athenix.users.myuser.hashedPassword = "$6$...";
```
#### `opensshKeys`
SSH public keys for this user.
**Type:** List of strings
```nix
athenix.users.myuser.opensshKeys = [
"ssh-ed25519 AAAA... user@host"
"ssh-rsa AAAA... user@other"
];
```
#### `useZshTheme`
Apply system Zsh theme configuration to this user.
**Type:** Boolean
**Default:** `true`
```nix
athenix.users.myuser.useZshTheme = true;
```
#### `useNvimPlugins`
Apply system Neovim configuration to this user.
**Type:** Boolean
**Default:** `true`
```nix
athenix.users.myuser.useNvimPlugins = true;
```
#### `external`
Reference external user configuration (dotfiles, home-manager).
**Type:** Path or Git reference
**Example:**
```nix
athenix.users.myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles"; url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123..."; rev = "abc123...";
}; };
``` ```
## System Configuration (`athenix.system`) See [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) for detailed external module usage.
System-wide settings and services. ### Enabling Users on Systems
### `athenix.system.gc` Users defined in `users.nix` are **not enabled by default**. Enable them in `inventory.nix`:
Automatic garbage collection configuration.
**Options:**
- `athenix.system.gc.enable` - Enable automatic garbage collection (default: `true`)
- `athenix.system.gc.frequency` - How often to run (default: `"weekly"`)
- `athenix.system.gc.retentionDays` - Days to keep old generations (default: `30`)
- `athenix.system.gc.optimise` - Optimize Nix store automatically (default: `true`)
**Example:**
```nix ```nix
athenix.system.gc = { # Option 1: Enable on all devices in a group
enable = true; nix-laptop = {
frequency = "daily"; devices = 5;
retentionDays = 14; overrides.athenix.users.student.enable = true;
optimise = true; };
# Option 2: Enable on specific devices
nix-surface = {
devices = {
"1".athenix.users.admin.enable = true;
"2".athenix.users.admin.enable = true;
};
}; };
``` ```
@@ -249,7 +359,7 @@ athenix.system.gc = {
### `athenix.forUser` ### `athenix.forUser`
Quick setup option that enables a user account in one line. Quick setup for single-user systems. Automatically enables a user and sets it as the default.
**Type:** String (username) or null **Type:** String (username) or null
@@ -257,11 +367,7 @@ Quick setup option that enables a user account in one line.
**Example:** **Example:**
```nix ```nix
athenix.forUser = "myusername"; # Equivalent to athenix.users.myusername.enable = true # In inventory.nix - enables the user automatically
```
**Usage in inventory.nix:**
```nix
nix-wsl = { nix-wsl = {
devices = { devices = {
"alice".athenix.forUser = "alice-uga"; "alice".athenix.forUser = "alice-uga";
@@ -269,9 +375,17 @@ nix-wsl = {
}; };
``` ```
Equivalent to:
```nix
"alice" = {
athenix.users.alice-uga.enable = true;
athenix.host.wsl.user = "alice-uga";
};
```
## See Also ## See Also
- [INVENTORY.md](INVENTORY.md) - Host inventory configuration guide - [INVENTORY.md](INVENTORY.md) - Host configuration examples
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management guide - [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User account management guide
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External configuration modules - [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External module integration
- [README.md](../README.md) - Main documentation - [README.md](../README.md) - Main documentation

View File

@@ -1,103 +1,585 @@
# User Configuration Guide # User Configuration Guide
Complete guide to managing user accounts in nixos-systems. Comprehensive guide to managing user accounts in Athenix.
## Table of Contents ## Table of Contents
- [Overview](#overview) - [Overview](#overview)
- [Quick Start](#quick-start) - [Quick Start](#quick-start)
- [User Account Options](#user-account-options) - [Defining Users](#defining-users)
- [External User Configurations](#external-user-configurations)
- [Enabling Users on Hosts](#enabling-users-on-hosts) - [Enabling Users on Hosts](#enabling-users-on-hosts)
- [External User Configurations](#external-user-configurations)
- [Password Management](#password-management) - [Password Management](#password-management)
- [SSH Keys](#ssh-keys) - [SSH Keys](#ssh-keys)
- [User Groups](#user-groups)
- [Examples](#examples) - [Examples](#examples)
## Overview ## Overview
Users are defined in `users.nix` but are **not enabled by default** on all systems. Each system must explicitly enable users in `inventory.nix`. User accounts are defined in `users.nix` but are **not enabled by default**. Each host must explicitly enable users in `inventory.nix`.
**Default enabled users:** **Always-enabled users:**
- `root` - System administrator - `root` - System administrator (enable: true)
- `engr-ugaif` - Innovation Factory default account - `engr-ugaif` - Innovation Factory default account (enable: true)
All other users are disabled by default and must be explicitly enabled per-host.
## Quick Start ## Quick Start
### 1. Define User in users.nix ### 1. Define User in users.nix
```nix ```nix
athenix.users = { athenix.users.myuser = {
# Option 1: Inline definition description = "John Doe";
myuser = {
description = "My Full Name";
extraGroups = [ "wheel" "networkmanager" ]; extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh; shell = pkgs.zsh;
hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512 hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512
opensshKeys = [ opensshKeys = [ "ssh-ed25519 AAAA..." ];
"ssh-ed25519 AAAA... user@machine"
];
};
# Option 2: External configuration (recommended for personalization)
myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123..."; # Pin to specific commit
};
}; };
``` ```
### 2. Enable User on Hosts ### 2. Enable on Hosts in inventory.nix
In `inventory.nix`:
```nix ```nix
nix-laptop = { nix-laptop = {
devices = 2; devices = 5;
overrides.athenix.users.myuser.enable = true; # Enables on all nix-laptop hosts overrides.athenix.users.myuser.enable = true;
};
# Or for specific devices
nix-desktop = {
devices = {
"1".athenix.users.myuser.enable = true;
"2".athenix.users.otheruser.enable = true;
};
};
# Or use convenience option
nix-wsl = {
devices."alice".athenix.forUser = "alice-user"; # Automatically enables user
}; };
``` ```
## User Account Options ### 3. Users can now log in
Each user in `users.nix` can have the following options: Users defined and enabled this way are automatically created on the system.
## Defining Users
Define users in `users.nix` under `athenix.users`:
### Inline User Definition
```nix ```nix
username = { athenix.users.myuser = {
# === Identity === description = "My Full Name";
description = "Full Name"; # User's full name extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$...";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
useZshTheme = true;
useNvimPlugins = true;
};
```
# === System Access === ### External User Configuration
isNormalUser = true; # Default: true (false for root)
extraGroups = [ # Additional Unix groups Reference an external Git repository (recommended for personal dotfiles):
"wheel" # Sudo access
"networkmanager" # Network configuration ```nix
"docker" # Docker access athenix.users.myuser.external = builtins.fetchGit {
"video" # Video device access url = "https://git.factory.uga.edu/username/dotfiles";
"audio" # Audio device access rev = "abc123..."; # Pin to specific commit
};
```
The external repository should contain:
- `user.nix` (required) - User account options AND home-manager configuration
- `nixos.nix` (optional) - System-level configuration
See [External User Configurations](#external-user-configurations) section below.
## User Account Options
### `description`
Full name or description of the user.
**Type:** String
```nix
athenix.users.myuser.description = "John Doe";
```
### `extraGroups`
Additional Unix groups for the user. Default is empty.
**Type:** List of strings
**Common groups:**
- `"wheel"` - Sudo access
- `"networkmanager"` - Network configuration
- `"docker"` - Docker and Podman access
- `"video"` - Video device access (GPU, displays)
- `"audio"` - Audio device access
- `"input"` - Input devices (keyboards, mice)
- `"kvm"` - KVM virtual machine access
- `"libvirtd"` - Libvirt daemon access
```nix
athenix.users.myuser.extraGroups = [
"wheel"
"networkmanager"
"docker"
"video"
]; ];
shell = pkgs.zsh; # Login shell (default: pkgs.bash) ```
hashedPassword = "$6$..."; # Hashed password (see below)
# === SSH Access === ### `shell`
opensshKeys = [ # SSH public keys
Login shell for the user.
**Type:** Package
**Default:** `pkgs.bash`
```nix
athenix.users.myuser.shell = pkgs.zsh;
# or
athenix.users.myuser.shell = pkgs.fish;
```
### `hashedPassword`
Password hash for the user. Use `!` to disable password login (SSH keys only).
**Type:** String (SHA-512 hash)
**Generation:**
```bash
# Generate a hashed password
mkpasswd -m sha-512
# Or interactively
mkpasswd -m sha-512 -c
```
```nix
athenix.users.myuser.hashedPassword = "$6$...";
# Disable password login (require SSH keys)
athenix.users.myuser.hashedPassword = "!";
```
### `opensshKeys`
SSH public keys for remote access. Users without SSH keys require password login.
**Type:** List of strings
```nix
athenix.users.myuser.opensshKeys = [
"ssh-ed25519 AAAA... user@laptop"
"ssh-rsa AAAA... user@desktop"
];
```
**Getting your SSH public key:**
```bash
# Print your public key
cat ~/.ssh/id_ed25519.pub
# Generate a new key if needed
ssh-keygen -t ed25519 -C "user@host"
```
### `useZshTheme`
Apply system Zsh theme configuration to this user (if using Zsh as shell).
**Type:** Boolean
**Default:** `true`
```nix
athenix.users.myuser.useZshTheme = true;
```
### `useNvimPlugins`
Apply system Neovim configuration and plugins to this user.
**Type:** Boolean
**Default:** `true`
```nix
athenix.users.myuser.useNvimPlugins = true;
```
## Enabling Users on Hosts
Users are **not enabled by default**. Enable them in `inventory.nix`:
### Enable on All Devices in a Group
```nix
nix-laptop = {
devices = 5;
overrides.athenix.users.myuser.enable = true;
};
```
### Enable on Specific Devices
```nix
nix-desktop = {
devices = {
"1".athenix.users.admin.enable = true;
"2".athenix.users.staff.enable = true;
"3".athenix.users.staff.enable = true;
};
};
```
### Enable Multiple Users
```nix
nix-laptop = {
devices = 5;
overrides = {
athenix.users.student.enable = true;
athenix.users.teacher.enable = true;
};
};
```
### Using `athenix.forUser` Convenience
Quick setup for single-user systems (especially WSL):
```nix
nix-wsl = {
devices = {
"alice".athenix.forUser = "alice-uga";
"bob".athenix.forUser = "bob-uga";
};
};
```
This automatically enables the user and sets it as the default WSL user.
## External User Configurations
External user configurations (dotfiles) allow users to maintain their own home-manager setup in separate repositories.
### Repository Structure
```
my-dotfiles/
├── user.nix # Required: User options + home-manager config
├── nixos.nix # Optional: System-level configuration
└── config/ # Optional: Your actual dotfiles
├── bashrc
├── zshrc
├── vimrc
└── ...
```
### user.nix (Required)
This file must provide BOTH user account options AND home-manager configuration:
```nix
{ inputs, ... }:
{ config, lib, pkgs, osConfig ? null, ... }:
{
# ========== User Account Configuration ==========
# These options define the user account itself
athenix.users.myusername = {
description = "My Full Name";
extraGroups = [ "wheel" "docker" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "!"; # SSH keys only
opensshKeys = [
"ssh-ed25519 AAAA... user@host" "ssh-ed25519 AAAA... user@host"
"ssh-rsa AAAA... user@otherhost"
]; ];
useZshTheme = true;
useNvimPlugins = true;
};
# === External Configuration === # ========== Home Manager Configuration ==========
# User environment, packages, and dotfiles
home.packages = with pkgs; [
vim
ripgrep
fzf
] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
programs.git = {
enable = true;
userName = "My Name";
userEmail = "me@example.com";
extraConfig = {
init.defaultBranch = "main";
core.editor = "vim";
};
};
programs.zsh = {
enable = true;
initExtra = ''
# Your Zsh configuration
'';
};
# Manage dotfiles
home.file.".config/zshrc".source = ./config/zshrc;
home.file.".config/bashrc".source = ./config/bashrc;
home.file.".vimrc".source = ./config/vimrc;
}
```
### nixos.nix (Optional)
System-level configuration for this user (rarely needed):
```nix
{ inputs, ... }:
{ config, lib, pkgs, ... }:
{
# System-level configuration for this user
users.users.myusername.extraGroups = [ "docker" ];
environment.systemPackages = [ pkgs.docker ];
}
```
### Using External User Configuration
In `users.nix`:
```nix
athenix.users.myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123..."; # Pin to specific commit
};
```
Then enable on hosts in `inventory.nix`:
```nix
nix-laptop = {
devices = 5;
overrides.athenix.users.myuser.enable = true;
};
```
### External Module Parameters
The `user.nix` module receives:
- **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.)
- **`config`** - Home-manager configuration
- **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set
- **`osConfig`** - OS-level configuration (read-only, can be used for conditional setup)
### Creating External User Configuration
Use the template:
```bash
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user
```
## Password Management
### Generate Password Hash
```bash
# Interactive (won't echo)
mkpasswd -m sha-512 -c
# From string
echo "mypassword" | mkpasswd -m sha-512 -s
```
### Disable Password Login
Set `hashedPassword = "!"` and provide SSH keys:
```nix
athenix.users.myuser = {
description = "SSH-only user";
hashedPassword = "!";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
};
```
### Update User Password on Running System
```bash
# As the user
passwd
# As root (to change another user's password)
sudo passwd username
```
## SSH Keys
### Add SSH Keys to a User
```nix
athenix.users.myuser.opensshKeys = [
"ssh-ed25519 AAAA... user@laptop"
"ssh-ed25519 BBBB... user@desktop"
];
```
### Get Your SSH Public Key
```bash
# Display your public key
cat ~/.ssh/id_ed25519.pub
# Or for RSA
cat ~/.ssh/id_rsa.pub
```
### Generate New SSH Key
```bash
# Ed25519 (recommended)
ssh-keygen -t ed25519 -C "user@host"
# RSA (older systems)
ssh-keygen -t rsa -b 4096 -C "user@host"
```
## User Groups
### wheel
Allows passwordless sudo access.
```nix
athenix.users.myuser.extraGroups = [ "wheel" ];
```
### networkmanager
Configure network connections (requires `networkmanager` to be enabled):
```nix
athenix.users.myuser.extraGroups = [ "networkmanager" ];
```
### docker
Access Docker daemon (must have Docker enabled on system):
```nix
athenix.users.myuser.extraGroups = [ "docker" ];
```
### video and audio
Access GPU and audio devices:
```nix
athenix.users.myuser.extraGroups = [ "video" "audio" ];
```
## Examples
### Example 1: Basic Lab User
```nix
# users.nix
athenix.users.student = {
description = "Student Account";
extraGroups = [ "networkmanager" ];
shell = pkgs.bash;
hashedPassword = "$6$...";
opensshKeys = []; # Password login only
};
# inventory.nix
nix-laptop = {
devices = 20;
overrides.athenix.users.student.enable = true;
};
```
### Example 2: Developer with SSH Keys
```nix
# users.nix
athenix.users.developer = {
description = "Developer";
extraGroups = [ "wheel" "docker" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "!";
opensshKeys = [
"ssh-ed25519 AAAA... dev@laptop"
];
useZshTheme = true;
useNvimPlugins = true;
};
# inventory.nix
nix-desktop = {
devices = 3;
overrides.athenix.users.developer.enable = true;
};
```
### Example 3: WSL User with Dotfiles
```nix
# users.nix
athenix.users.alice.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/alice/dotfiles";
rev = "abc123...";
};
# inventory.nix
nix-wsl = {
devices = {
"alice".athenix.forUser = "alice-uga";
};
};
```
### Example 4: Multiple Users on Single System
```nix
# users.nix
athenix.users = {
admin = {
description = "System Administrator";
extraGroups = [ "wheel" ];
shell = pkgs.bash;
hashedPassword = "!";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
};
guest = {
description = "Guest User";
extraGroups = [];
shell = pkgs.bash;
hashedPassword = "$6$...";
};
};
# inventory.nix
nix-desktop = {
devices = {
"admin-station" = {
athenix.users.admin.enable = true;
};
"guest-station" = {
athenix.users.guest.enable = true;
};
};
};
```
## See Also
- [INVENTORY.md](INVENTORY.md) - Host configuration
- [NAMESPACE.md](NAMESPACE.md) - All configuration options
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External modules in detail
- [README.md](../README.md) - Main documentation
external = builtins.fetchGit { ... }; # External user module (see below) external = builtins.fetchGit { ... }; # External user module (see below)
# === Theme Integration === # === Theme Integration ===

110
flake.lock generated
View File

@@ -115,11 +115,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1765794845, "lastModified": 1766150702,
"narHash": "sha256-YD5QWlGnusNbZCqR3pxG8tRxx9yUXayLZfAJRWspq2s=", "narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "7194cfe5b7a3660726b0fe7296070eaef601cae9", "rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -159,6 +159,26 @@
} }
}, },
"flake-parts": { "flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1768135262,
"narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"lazyvim-nixvim", "lazyvim-nixvim",
@@ -219,6 +239,24 @@
"inputs": { "inputs": {
"systems": "systems_4" "systems": "systems_4"
}, },
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_4": {
"inputs": {
"systems": "systems_5"
},
"locked": { "locked": {
"lastModified": 1681202837, "lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
@@ -318,11 +356,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1765979862, "lastModified": 1767910483,
"narHash": "sha256-/r9/1KamvbHJx6I40H4HsSXnEcBAkj46ZwibhBx9kg0=", "narHash": "sha256-MOU5YdVu4DVwuT5ztXgQpPuRRBjSjUGIdUzOQr9iQOY=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "d3135ab747fd9dac250ffb90b4a7e80634eacbe9", "rev": "82fb7dedaad83e5e279127a38ef410bcfac6d77c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -386,7 +424,7 @@
}, },
"lazyvim-nixvim": { "lazyvim-nixvim": {
"inputs": { "inputs": {
"flake-parts": "flake-parts", "flake-parts": "flake-parts_2",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixvim": "nixvim" "nixvim": "nixvim"
}, },
@@ -464,11 +502,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1764440730, "lastModified": 1767185284,
"narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=", "narHash": "sha256-ljDBUDpD1Cg5n3mJI81Hz5qeZAwCGxon4kQW3Ho3+6Q=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3", "rev": "40b1a28dce561bea34858287fbb23052c3ee63fe",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -518,11 +556,11 @@
}, },
"nixpkgs-old-kernel": { "nixpkgs-old-kernel": {
"locked": { "locked": {
"lastModified": 1765687488, "lastModified": 1767313136,
"narHash": "sha256-7YAJ6xgBAQ/Nr+7MI13Tui1ULflgAdKh63m1tfYV7+M=", "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d02bcc33948ca19b0aaa0213fe987ceec1f4ebe1", "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -534,11 +572,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1765838191, "lastModified": 1768242861,
"narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", "narHash": "sha256-F4IIxa5xDHjtrmMcayM8lHctUq1oGltfBQu2+oqDWP4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", "rev": "1327e798cb055f96f92685df444e9a2c326ab5ed",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -608,6 +646,7 @@
"inputs": { "inputs": {
"agenix": "agenix", "agenix": "agenix",
"disko": "disko", "disko": "disko",
"flake-parts": "flake-parts",
"home-manager": "home-manager_2", "home-manager": "home-manager_2",
"lazyvim-nixvim": "lazyvim-nixvim", "lazyvim-nixvim": "lazyvim-nixvim",
"nixos-generators": "nixos-generators", "nixos-generators": "nixos-generators",
@@ -615,6 +654,7 @@
"nixos-wsl": "nixos-wsl", "nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"nixpkgs-old-kernel": "nixpkgs-old-kernel", "nixpkgs-old-kernel": "nixpkgs-old-kernel",
"usda-vision": "usda-vision",
"vscode-server": "vscode-server" "vscode-server": "vscode-server"
} }
}, },
@@ -699,6 +739,21 @@
"type": "github" "type": "github"
} }
}, },
"systems_5": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": { "treefmt-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -721,13 +776,34 @@
"type": "github" "type": "github"
} }
}, },
"vscode-server": { "usda-vision": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_3", "flake-utils": "flake-utils_3",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": {
"lastModified": 1769814438,
"narHash": "sha256-DEZrmqpqbrd996W5p1r4GA1C8Jmo31n3N642ccS0deY=",
"ref": "refs/heads/main",
"rev": "78bfcf02612817a2cee1edbf92deeac9bf657613",
"revCount": 126,
"type": "git",
"url": "https://git.factory.uga.edu/MODEL/usda-vision.git"
},
"original": {
"type": "git",
"url": "https://git.factory.uga.edu/MODEL/usda-vision.git"
}
},
"vscode-server": {
"inputs": {
"flake-utils": "flake-utils_4",
"nixpkgs": [
"nixpkgs"
]
},
"locked": { "locked": {
"lastModified": 1753541826, "lastModified": 1753541826,
"narHash": "sha256-foGgZu8+bCNIGeuDqQ84jNbmKZpd+JvnrL2WlyU4tuU=", "narHash": "sha256-foGgZu8+bCNIGeuDqQ84jNbmKZpd+JvnrL2WlyU4tuU=",

View File

@@ -4,7 +4,7 @@
# ============================================================================ # ============================================================================
# This file defines the inputs (dependencies) and outputs (configurations) # This file defines the inputs (dependencies) and outputs (configurations)
# for Athenix. It ties together the hardware, software, and user # for Athenix. It ties together the hardware, software, and user
# configurations into deployable systems. # configurations into deployable systems using flake-parts.
inputs = { inputs = {
# Core NixOS package repository (Release 25.11) # Core NixOS package repository (Release 25.11)
@@ -13,6 +13,12 @@
# Older kernel packages for Surface compatibility if needed # Older kernel packages for Surface compatibility if needed
nixpkgs-old-kernel.url = "github:NixOS/nixpkgs/nixos-25.05"; nixpkgs-old-kernel.url = "github:NixOS/nixpkgs/nixos-25.05";
# Flake-parts for modular flake organization
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
};
# Home Manager for user environment management # Home Manager for user environment management
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-25.11"; url = "github:nix-community/home-manager/release-25.11";
@@ -53,49 +59,37 @@
url = "github:nix-community/NixOS-WSL/main"; url = "github:nix-community/NixOS-WSL/main";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# USDA Vision Dashboard application
usda-vision = {
url = "git+https://git.factory.uga.edu/MODEL/usda-vision.git";
inputs.nixpkgs.follows = "nixpkgs";
}; };
};
outputs = outputs =
inputs@{ inputs@{ self, flake-parts, ... }:
self, flake-parts.lib.mkFlake { inherit inputs; } {
nixpkgs, # Support all common systems
nixpkgs-old-kernel, systems = [
home-manager,
disko,
agenix,
lazyvim-nixvim,
nixos-hardware,
vscode-server,
nixos-generators,
...
}:
let
hosts = import ./hosts { inherit inputs; };
linuxSystem = "x86_64-linux";
artifacts = import ./installer/artifacts.nix {
inherit inputs hosts self;
system = linuxSystem;
};
forAllSystems = nixpkgs.lib.genAttrs [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"
"x86_64-darwin" "x86_64-darwin"
"aarch64-darwin" "aarch64-darwin"
]; ];
in
{
# Formatter for 'nix fmt'
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style);
# Generate NixOS configurations from hosts/default.nix # Import flake-parts modules
nixosConfigurations = hosts.nixosConfigurations; imports = [
./parts/formatter.nix
# Expose artifacts to all systems, but they are always built for x86_64-linux ./parts/lib.nix
packages = forAllSystems (_: artifacts); ./parts/nixos-configurations.nix
./parts/nixos-modules.nix
# Expose modules for external use ./parts/packages.nix
nixosModules = import ./installer/modules.nix { inherit inputs; }; ./parts/templates.nix
./parts/docs.nix
# Templates for external configurations ./inventory.nix
templates = import ./templates; ./users.nix
];
}; };
} }

64
fleet/boot.nix Normal file
View File

@@ -0,0 +1,64 @@
# ============================================================================
# Boot configuration module
# ============================================================================
# This module defines:
# - Bootloader configuration (systemd-boot with Plymouth)
# - Timezone and locale settings
# - Systemd sleep configuration
#
# Only applies to:
# - Linux systems (not Darwin/macOS)
# - Systems with actual boot hardware (not containers/WSL)
{
config,
lib,
pkgs,
...
}:
let
# Check if this is a bootable system (not container, not WSL)
isBootable = !(config.boot.isContainer or false) && (pkgs.stdenv.isLinux);
in
{
config = lib.mkIf isBootable {
boot = {
loader.systemd-boot.enable = lib.mkDefault true;
loader.efi.canTouchEfiVariables = lib.mkDefault true;
plymouth.enable = lib.mkDefault true;
# Enable "Silent boot"
consoleLogLevel = 3;
initrd.verbose = false;
# Hide the OS choice for bootloaders.
# It's still possible to open the bootloader list by pressing any key
# It will just not appear on screen unless a key is pressed
loader.timeout = lib.mkDefault 0;
};
# Set your time zone.
time.timeZone = "America/New_York";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
systemd.sleep.extraConfig = ''
SuspendState=freeze
HibernateDelaySec=2h
'';
};
}

196
fleet/common.nix Normal file
View File

@@ -0,0 +1,196 @@
# ============================================================================
# Common Host Module
# ============================================================================
# This module contains all the common configuration shared by all host types.
# It is automatically imported by the fleet generator for every host.
{
config,
lib,
inputs,
...
}:
let
# Import all hardware modules so they're available for enabling
hwTypes = import ../hw { inherit inputs; };
hwModules = lib.attrValues hwTypes;
# User account submodule definition
userSubmodule = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether this user account is enabled on this system.";
};
isNormalUser = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether this is a normal user account (vs system user).";
};
description = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Full name or description of the user (GECOS field).";
example = "John Doe";
};
extraGroups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Additional groups for the user (wheel, docker, etc.).";
};
hashedPassword = lib.mkOption {
type = lib.types.str;
default = "!";
description = "Hashed password for the user account. Default '!' means locked.";
};
extraPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "Additional system packages available to this user.";
};
excludePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "System packages to exclude for this user.";
};
homePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "Packages to install in the user's home-manager profile.";
};
extraImports = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
description = "Additional home-manager modules to import for this user.";
};
external = lib.mkOption {
type = lib.types.nullOr (
lib.types.oneOf [
lib.types.path
(lib.types.submodule {
options = {
url = lib.mkOption {
type = lib.types.str;
description = "Git repository URL to fetch user configuration from.";
};
rev = lib.mkOption {
type = lib.types.str;
description = "Git commit hash, tag, or branch to fetch.";
};
submodules = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to fetch Git submodules.";
};
};
})
]
);
default = null;
description = "External dotfiles repository (user.nix + optional nixos.nix).";
};
opensshKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "SSH public keys for the user (authorized_keys).";
};
shell = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"bash"
"zsh"
"fish"
"tcsh"
]
);
default = "bash";
description = "Default shell for the user.";
};
editor = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"vim"
"neovim"
"emacs"
"nano"
"code"
]
);
default = "neovim";
description = "Default text editor for the user (sets EDITOR).";
};
useZshTheme = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Zsh theme (Oh My Posh).";
};
useNvimPlugins = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Neovim configuration.";
};
};
};
in
{
imports = [
./fs.nix
./boot.nix
./user-config.nix
../sw
inputs.vscode-server.nixosModules.default
inputs.nixos-wsl.nixosModules.default
]
++ hwModules;
options.athenix.users = lib.mkOption {
type = lib.types.attrsOf userSubmodule;
default = { };
description = "User accounts configuration. Set enable=true for users that should exist on this system.";
};
options.athenix = {
forUser = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Convenience option to configure a host for a specific user.
When set, automatically:
- Enables the user account (athenix.users.<username>.enable = true)
- Sets as default WSL user (on WSL systems)
The username must exist in athenix.users (defined in users.nix).
'';
example = "engr-ugaif";
};
host.useHostPrefix = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to prepend the hardware type prefix to the hostname.
When true:
- "nix-laptop" with device "1" hostname "nix-laptop1"
- "nix-wsl" with device "alice" hostname "nix-wsl-alice"
When false:
- Device name becomes the full hostname (useful for custom names)
'';
};
};
config = lib.mkMerge [
(lib.mkIf (config.athenix.forUser != null) {
athenix.users.${config.athenix.forUser}.enable = true;
})
{
system.stateVersion = "25.11";
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
}
];
}

View File

@@ -1,36 +1,25 @@
{ {
inputs, inputs,
hosts ? import ../inventory.nix, lib,
config,
self ? null,
users ? { },
... ...
}: }:
# ============================================================================ # ============================================================================
# Host Generator # Fleet Generator
# ============================================================================ # ============================================================================
# This file contains the logic to generate NixOS configurations for all hosts # This file contains the logic to generate NixOS configurations for all hosts
# defined in inventory.nix. It supports both hostname-based and count-based # defined in inventory.nix. It supports both hostname-based and count-based
# configurations with flexible type associations. # configurations with flexible type associations.
#
# Inventory format:
# {
# "my-hostname" = {
# type = "nix-desktop"; # Host type module to use
# system = "x86_64-linux"; # Optional
# # ... any athenix.* options or device-specific config
# };
#
# "lab-prefix" = {
# type = "nix-laptop";
# count = 5; # Generates lab-prefix1, lab-prefix2, ... lab-prefix5
# devices = {
# "machine-1" = { ... }; # Override for lab-prefix1
# };
# };
# }
let let
nixpkgs = inputs.nixpkgs;
lib = nixpkgs.lib; # Evaluate inventory to get fleet data
# Import fleet-option.nix (defines athenix.fleet) and inventory.nix (sets values)
# We use a minimal module here to avoid circular dependencies from common.nix's imports
# Helper to create a single NixOS system configuration # Helper to create a single NixOS system configuration
mkHost = mkHost =
{ {
@@ -38,13 +27,42 @@ let
system ? "x86_64-linux", system ? "x86_64-linux",
hostType, hostType,
configOverrides ? { }, configOverrides ? { },
externalModulePath ? null, externalModuleThunk ? null,
}: }:
let let
# Lazy evaluation: only fetch external module when building this host
externalModulePath =
if externalModuleThunk != null then
let
# Force evaluation of the thunk
fetchedPath =
if
builtins.isAttrs externalModuleThunk
&& externalModuleThunk ? _type
&& externalModuleThunk._type == "lazy-fetchGit"
then
# New format: lazy fetchGit - only execute when needed
(builtins.fetchGit {
inherit (externalModuleThunk) url rev submodules;
}).outPath
else
# Legacy: pre-fetched derivation or path
externalModuleThunk;
# Extract outPath from fetchGit/fetchTarball results
extractedPath =
if builtins.isAttrs fetchedPath && fetchedPath ? outPath then fetchedPath.outPath else fetchedPath;
in
if builtins.isPath extractedPath then
extractedPath + "/default.nix"
else if lib.isDerivation extractedPath then
extractedPath + "/default.nix"
else
extractedPath + "/default.nix"
else
null;
# Load users.nix to find external user modules # Load users.nix to find external user modules
pkgs = nixpkgs.legacyPackages.${system}; accounts = config.athenix.users or { };
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.athenix.users or { };
# Build a map of user names to their nixos module paths (if they exist) # Build a map of user names to their nixos module paths (if they exist)
# We'll use this to conditionally import modules based on user.enable # We'll use this to conditionally import modules based on user.enable
@@ -53,10 +71,19 @@ let
name: user: name: user:
if (user ? external && user.external != null) then if (user ? external && user.external != null) then
let let
# Resolve external path (lazy fetchGit if needed)
externalPath = externalPath =
if builtins.isAttrs user.external && user.external ? outPath then if builtins.isAttrs user.external && user.external ? url && user.external ? rev then
# New format: lazy fetchGit
(builtins.fetchGit {
inherit (user.external) url rev;
submodules = user.external.submodules or false;
}).outPath
else if builtins.isAttrs user.external && user.external ? outPath then
# Legacy: pre-fetched
user.external.outPath user.external.outPath
else else
# Direct path
user.external; user.external;
nixosModulePath = externalPath + "/nixos.nix"; nixosModulePath = externalPath + "/nixos.nix";
in in
@@ -94,14 +121,6 @@ let
} }
) userNixosModulePaths; ) userNixosModulePaths;
# Load the host type module
typeFile = ./types + "/${hostType}.nix";
typeModule =
if builtins.pathExists typeFile then
import typeFile { inherit inputs; }
else
throw "Host type '${hostType}' not found in hosts/types/";
# External module from fetchGit/fetchurl # External module from fetchGit/fetchurl
externalPathModule = externalPathModule =
if externalModulePath != null then import externalModulePath { inherit inputs; } else { }; if externalModulePath != null then import externalModulePath { inherit inputs; } else { };
@@ -129,19 +148,35 @@ let
]; ];
}; };
# Hardware-specific external modules
hwSpecificModules =
lib.optional (hostType == "nix-lxc")
"${inputs.nixpkgs.legacyPackages.${system}.path}/nixos/modules/virtualisation/proxmox-lxc.nix";
allModules = allModules =
userNixosModules userNixosModules
++ [ ++ [
typeModule ./common.nix
overrideModule overrideModule
{ networking.hostName = hostName; } { networking.hostName = hostName; }
# Set athenix.host.name for secrets and other modules to use
{ athenix.host.name = hostName; }
{
# Inject user definitions from flake-parts level
config.athenix.users = lib.mapAttrs (_: user: lib.mapAttrs (_: lib.mkDefault) user) users;
}
# Enable the appropriate hardware module based on hostType
{ config.athenix.hw.${hostType}.enable = lib.mkDefault true; }
] ]
++ hwSpecificModules
++ lib.optional (externalModulePath != null) externalPathModule; ++ lib.optional (externalModulePath != null) externalPathModule;
in in
{ {
system = lib.nixosSystem { system = lib.nixosSystem {
inherit system; inherit system;
specialArgs = { inherit inputs; }; specialArgs = {
inputs = if self != null then inputs // { inherit self; } else inputs;
};
modules = allModules; modules = allModules;
}; };
modules = allModules; modules = allModules;
@@ -153,8 +188,6 @@ let
let let
hostType = config.type or prefix; hostType = config.type or prefix;
system = config.system or "x86_64-linux"; system = config.system or "x86_64-linux";
devices = config.devices or { };
hasCount = config ? count;
# Helper to generate hostname from prefix and suffix # Helper to generate hostname from prefix and suffix
# Numbers get no dash: "nix-surface1", "nix-surface2" # Numbers get no dash: "nix-surface1", "nix-surface2"
@@ -192,48 +225,38 @@ let
lib.mapAttrsToList ( lib.mapAttrsToList (
deviceKey: deviceConfig: deviceKey: deviceConfig:
let let
# Check if deviceConfig is a path/derivation (from fetchGit, fetchurl, etc.) # Check if deviceConfig has an 'external' field for lazy evaluation
# fetchGit/fetchTarball return an attrset with outPath attribute hasExternalField = builtins.isAttrs deviceConfig && deviceConfig ? external;
isExternalModule =
(builtins.isPath deviceConfig)
|| (builtins.isString deviceConfig && lib.hasPrefix "/" deviceConfig)
|| (lib.isDerivation deviceConfig)
|| (builtins.isAttrs deviceConfig && deviceConfig ? outPath);
# Extract the actual path from fetchGit/fetchTarball results # Extract external module spec (don't evaluate fetchGit yet!)
extractedPath = externalModuleThunk =
if builtins.isAttrs deviceConfig && deviceConfig ? outPath then if hasExternalField then
deviceConfig.outPath let
ext = deviceConfig.external;
in
# New format: { url, rev, submodules? } - create lazy fetchGit thunk
if builtins.isAttrs ext && ext ? url && ext ? rev then
{
_type = "lazy-fetchGit";
inherit (ext) url rev;
submodules = ext.submodules or false;
}
# Legacy: pre-fetched or path
else else
deviceConfig; ext
# If external module, we use base config + overrides as the config
# and pass the module path separately
actualConfig =
if isExternalModule then (lib.recursiveUpdate baseConfig overrides) else deviceConfig;
# Merge: base config -> overrides -> device-specific config (only if not external module)
mergedConfig =
if isExternalModule then
actualConfig
else else
lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) deviceConfig; null;
# Remove 'external' from config to avoid conflicts
cleanDeviceConfig =
if hasExternalField then lib.removeAttrs deviceConfig [ "external" ] else deviceConfig;
# Merge: base config -> overrides -> device-specific config
mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) cleanDeviceConfig;
# Check useHostPrefix from the merged config # Check useHostPrefix from the merged config
usePrefix = mergedConfig.athenix.host.useHostPrefix or true; usePrefix = mergedConfig.athenix.host.useHostPrefix or true;
hostName = mkHostName prefix deviceKey usePrefix; hostName = mkHostName prefix deviceKey usePrefix;
# If external module, also add a default.nix path for import
externalModulePath =
if isExternalModule then
if builtins.isPath extractedPath then
extractedPath + "/default.nix"
else if lib.isDerivation extractedPath then
extractedPath + "/default.nix"
else
extractedPath + "/default.nix"
else
null;
in in
{ {
name = hostName; name = hostName;
@@ -242,7 +265,7 @@ let
hostName hostName
system system
hostType hostType
externalModulePath externalModuleThunk
; ;
configOverrides = mergedConfig; configOverrides = mergedConfig;
}; };
@@ -289,10 +312,12 @@ let
{ }; { };
in in
lib.recursiveUpdate deviceHosts countHosts lib.recursiveUpdate deviceHosts countHosts
) hosts; );
fleetData = config.athenix.fleet;
# Flatten the nested structure # Flatten the nested structure
allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues processInventory); allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues (processInventory fleetData));
in in
{ {
nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts; nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts;

227
fleet/fleet-option.nix Normal file
View File

@@ -0,0 +1,227 @@
# ============================================================================
# Fleet Option Definition
# ============================================================================
# This module defines the athenix.fleet and athenix.hwTypes options.
# Self-contained fleet management without dependencies on user configuration.
{ inputs, lib, ... }:
let
fleetDefinition = lib.mkOption {
description = "Hardware types definitions for the fleet.";
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.oneOf [
lib.types.str
lib.types.listOf
lib.types.str
];
default = name;
description = "Type(s) of system configuration for this device.";
};
system = lib.mkOption {
type = lib.types.str;
default = "x86_64-linux";
description = "NixOS system architecture for this hardware type.";
};
devices = lib.mkOption {
type = lib.types.oneOf [
lib.types.int
(lib.types.attrsOf (
lib.types.submodule (
{ ... }:
{
freeformType = lib.types.attrs;
}
)
))
];
};
count = lib.mkOption {
type = lib.types.int;
default = 0;
description = "Number of devices of this type to create.";
};
defaultCount = lib.mkOption {
type = lib.types.int;
default = 0;
description = "Default number of devices to create with default configurations and numbered hostnames.";
};
overrides = lib.mkOption {
type = lib.types.attrs;
default = { };
description = "Overrides to apply to all devices of this type.";
};
};
}
)
);
};
# Forward declaration for user options (full definition in user-config.nix)
# This allows users.nix to be evaluated at flake level
userSubmodule = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether this user account is enabled on this system.";
};
isNormalUser = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether this is a normal user account (vs system user).";
};
description = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Full name or description of the user (GECOS field).";
example = "John Doe";
};
extraGroups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Additional groups for the user (wheel, docker, etc.).";
example = [
"wheel"
"networkmanager"
"docker"
];
};
hashedPassword = lib.mkOption {
type = lib.types.str;
default = "!";
description = ''
Hashed password for the user account.
Generate with: mkpasswd -m sha-512
Default "!" means account is locked (SSH key only).
'';
};
extraPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "Additional system packages available to this user.";
example = lib.literalExpression "[ pkgs.vim pkgs.git ]";
};
excludePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "System packages to exclude for this user.";
};
homePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
description = "Packages to install in the user's home-manager profile.";
example = lib.literalExpression "[ pkgs.firefox pkgs.vscode ]";
};
extraImports = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
description = "Additional home-manager modules to import for this user.";
};
external = lib.mkOption {
type = lib.types.nullOr (
lib.types.oneOf [
lib.types.path
(lib.types.submodule {
options = {
url = lib.mkOption {
type = lib.types.str;
description = "Git repository URL to fetch user configuration from.";
example = "https://github.com/username/dotfiles";
};
rev = lib.mkOption {
type = lib.types.str;
description = "Git commit hash, tag, or branch to fetch.";
example = "abc123def456...";
};
submodules = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to fetch Git submodules.";
};
};
})
]
);
default = null;
description = ''
External user configuration module from Git or local path.
Can be either:
- A local path: /path/to/config
- A Git repository: { url = "..."; rev = "..."; submodules? = false; }
The Git repository is only fetched when the user is actually enabled.
Should contain user.nix (user options + home-manager config)
and optionally nixos.nix (system-level config).
'';
example = lib.literalExpression ''
{
url = "https://github.com/username/dotfiles";
rev = "abc123def456789abcdef0123456789abcdef012";
submodules = false;
}'';
};
opensshKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "SSH public keys for the user (authorized_keys).";
example = [ "ssh-ed25519 AAAAC3Nza... user@host" ];
};
shell = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"bash"
"zsh"
"fish"
"tcsh"
]
);
default = "bash";
description = "Default shell for the user.";
};
editor = lib.mkOption {
type = lib.types.nullOr (
lib.types.enum [
"vim"
"neovim"
"emacs"
"nano"
"code"
]
);
default = "neovim";
description = "Default text editor for the user (sets EDITOR).";
};
useZshTheme = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Zsh theme (Oh My Posh).";
};
useNvimPlugins = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Neovim configuration.";
};
};
};
in
{
options.athenix = {
fleet = fleetDefinition;
hwTypes = lib.mkOption {
description = "Hardware types definitions for the fleet.";
type = lib.types.attrs;
};
users = lib.mkOption {
type = lib.types.attrsOf userSubmodule;
description = "User accounts configuration. Set enable=true for users that should exist on this system.";
};
};
config.athenix.hwTypes = lib.mkDefault (import ../hw { inherit inputs; });
}

132
fleet/fs.nix Normal file
View File

@@ -0,0 +1,132 @@
# ============================================================================
# FS & Storage Configuration
# ============================================================================
# This module defines:
# - Disko partition layout (EFI, swap, root)
# - Filesystem options (device, swap size)
#
# Only applies to systems with physical disk management needs
# (not containers, not WSL, not systems without a configured device)
{ config, lib, ... }:
let
cfg = config.athenix.host.filesystem;
# Only enable disk config if device is set and disko is enabled
hasDiskConfig = cfg.device != null && config.disko.enableConfig;
in
{
options.athenix = {
host = {
name = lib.mkOption {
type = lib.types.str;
description = ''
Fleet-assigned hostname for this system.
Used for secrets discovery and other host-specific configurations.
'';
};
filesystem = {
device = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
The main disk device to use for automated partitioning and installation.
When set, enables disko for declarative disk management with:
- 1GB EFI boot partition
- Optional swap partition (see swapSize)
- Root partition using remaining space
Leave null for systems that don't need disk partitioning (containers, WSL).
'';
example = "/dev/nvme0n1";
};
useSwap = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to create and use a swap partition.
Disable for systems with ample RAM or SSDs where swap is undesirable.
'';
};
swapSize = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Size of the swap partition (e.g., "16G", "32G").
Recommended sizes:
- 8-16GB for desktops with 16GB+ RAM
- 32GB for laptops (enables hibernation)
- Match RAM size for systems <8GB RAM
'';
example = "32G";
};
};
};
};
config = lib.mkMerge [
{
# ========== Disk Partitioning (Disko) ==========
disko.enableConfig = lib.mkDefault (cfg.device != null);
}
(lib.mkIf hasDiskConfig {
disko.devices = {
disk.main = {
type = "disk";
device = cfg.device;
content = {
type = "gpt";
partitions = {
# EFI System Partition
ESP = {
name = "ESP";
label = "BOOT";
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
extraArgs = [
"-n"
"BOOT"
];
};
};
# Swap Partition (size configurable per host)
swap = lib.mkIf cfg.useSwap {
name = "swap";
label = "swap";
size = cfg.swapSize;
content = {
type = "swap";
};
};
# Root Partition (takes remaining space)
root = {
name = "root";
label = "root";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
extraArgs = [
"-L"
"ROOT"
];
};
};
};
};
};
};
})
];
}

151
fleet/user-config.nix Normal file
View File

@@ -0,0 +1,151 @@
{
pkgs,
config,
lib,
inputs,
...
}:
# ============================================================================
# User Configuration Module
# ============================================================================
# This module implements user account creation and home-manager setup.
# Options are defined in fleet-option.nix for early availability.
let
# Helper: Resolve external module path (with lazy Git fetching)
resolveExternalPath =
external:
if external == null then
null
# New format: { url, rev, submodules? } - only fetch when needed
else if builtins.isAttrs external && external ? url && external ? rev then
(builtins.fetchGit {
inherit (external) url rev;
submodules = external.submodules or false;
}).outPath
# Legacy: pre-fetched derivation/package
else if builtins.isAttrs external && external ? outPath then
external.outPath
# Direct path
else
external;
# Helper: Check if path exists and is valid
isValidPath =
path:
path != null
&& (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path))
&& builtins.pathExists path;
in
{
config = {
# Generate NixOS users
users.users =
let
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
in
lib.mapAttrs (
name: user:
let
isPlasma6 = config.services.desktopManager.plasma6.enable;
defaultPackages = lib.optionals (isPlasma6 && name != "root") [ pkgs.kdePackages.kate ];
finalPackages = lib.subtractLists user.excludePackages (defaultPackages ++ user.extraPackages);
shells = {
bash = pkgs.bash;
zsh = pkgs.zsh;
fish = pkgs.fish;
tcsh = pkgs.tcsh;
};
in
rec {
isNormalUser = user.isNormalUser;
inherit (user) extraGroups hashedPassword;
description = if user.description != null then user.description else lib.mkDefault "";
openssh.authorizedKeys.keys = user.opensshKeys;
shell = if user.shell != null then shells.${user.shell} else pkgs.bash;
packages = finalPackages ++ [ shell ];
}
) enabledAccounts;
# Home Manager configs per user
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
osConfig = config;
inherit inputs;
};
users =
let
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
in
lib.mapAttrs (
name: user:
let
# Resolve external module paths
hasExternal = user.external != null;
externalPath = resolveExternalPath user.external;
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
hasExternalUser = isValidPath userNixPath;
# Import external user.nix for home-manager (filter out athenix.* options)
externalUserModule =
if hasExternalUser then
let
fullModule = import userNixPath { inherit inputs; };
in
# Only pass through non-athenix options to home-manager
{
config,
lib,
pkgs,
osConfig,
...
}:
let
evaluated = fullModule {
inherit
config
lib
pkgs
osConfig
;
};
in
lib.filterAttrs (attrName: _: attrName != "athenix") evaluated
else
{ };
# Common imports based on user flags
commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [
(import ../sw/nvim.nix { inherit user; })
];
# Build imports list
allImports = user.extraImports ++ commonImports ++ lib.optional hasExternalUser externalUserModule;
in
lib.mkMerge [
{
imports = allImports;
# Always set these required options
home.username = name;
home.homeDirectory = lib.mkOverride 999 (if name == "root" then "/root" else "/home/${name}");
home.stateVersion = "25.11";
programs.${user.editor} = {
enable = true;
defaultEditor = true;
};
}
(lib.mkIf (!hasExternal) {
# For local users only, add their packages
home.packages = user.homePackages;
})
]
) enabledAccounts;
};
};
}

View File

@@ -1,189 +0,0 @@
# ============================================================================
# Boot & Storage Configuration
# ============================================================================
# This module defines:
# - Disko partition layout (EFI, swap, root)
# - Bootloader configuration (systemd-boot with Plymouth)
# - Filesystem options (device, swap size)
# - Build method options (ISO, iPXE, LXC, Proxmox)
# - Garbage collection settings
# - Convenience options (forUser, useHostPrefix)
{ config, lib, ... }:
{
options.athenix = {
forUser = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Convenience option to configure a host for a specific user.
Automatically enables the user (sets athenix.users.username.enable = true).
Value should be a username from athenix.users.accounts.
'';
};
host = {
useHostPrefix = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to prepend the host prefix to the hostname (used in inventory).";
};
filesystem = {
device = lib.mkOption {
type = lib.types.str;
description = "The main disk device to use for installation.";
};
swapSize = lib.mkOption {
type = lib.types.str;
description = "The size of the swap partition.";
};
};
buildMethods = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "installer-iso" ];
description = ''
List of allowed build methods for this host.
Supported methods:
- "installer-iso": Generates an auto-install ISO that installs this configuration to disk.
- "iso": Generates a live ISO (using nixos-generators).
- "ipxe": Generates iPXE netboot artifacts (kernel, initrd, script).
- "lxc": Generates an LXC container tarball.
- "proxmox": Generates a Proxmox VMA archive.
'';
};
};
system.gc = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable automatic garbage collection.";
};
frequency = lib.mkOption {
type = lib.types.str;
default = "weekly";
description = "How often to run garbage collection (systemd timer format).";
};
retentionDays = lib.mkOption {
type = lib.types.int;
default = 30;
description = "Number of days to keep old generations before deletion.";
};
optimise = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to automatically optimize the Nix store.";
};
};
};
config = lib.mkMerge [
# Enable forUser if specified
(lib.mkIf (config.athenix.forUser != null) {
athenix.users.${config.athenix.forUser}.enable = true;
})
# Main configuration
{
# ========== Disk Partitioning (Disko) ==========
disko.enableConfig = lib.mkDefault true;
disko.devices = {
disk.main = {
type = "disk";
device = config.athenix.host.filesystem.device;
content = {
type = "gpt";
partitions = {
# EFI System Partition
ESP = {
name = "ESP";
label = "BOOT";
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
extraArgs = [
"-n"
"BOOT"
];
};
};
# Swap Partition (size configurable per host)
swap = {
name = "swap";
label = "swap";
size = config.athenix.host.filesystem.swapSize;
content = {
type = "swap";
};
};
# Root Partition (takes remaining space)
root = {
name = "root";
label = "root";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
extraArgs = [
"-L"
"ROOT"
];
};
};
};
};
};
};
# Bootloader Configuration
boot = {
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
plymouth.enable = true;
# Enable "Silent boot"
consoleLogLevel = 3;
initrd.verbose = false;
# Hide the OS choice for bootloaders.
# It's still possible to open the bootloader list by pressing any key
# It will just not appear on screen unless a key is pressed
loader.timeout = lib.mkDefault 0;
};
# Set your time zone.
time.timeZone = "America/New_York";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
systemd.sleep.extraConfig = ''
SuspendState=freeze
HibernateDelaySec=2h
'';
system.stateVersion = "25.11"; # Did you read the comment?
}
];
}

View File

@@ -1,47 +0,0 @@
# ============================================================================
# Common Modules
# ============================================================================
# This module contains all the common configuration shared by all host types.
# It includes:
# - Boot and user configuration
# - Software configurations
# - User management (users.nix)
# - Home Manager integration
# - Secret management (agenix)
# - Disk partitioning (disko)
# - System-wide Nix settings (experimental features, garbage collection)
{ inputs }:
{
config,
lib,
...
}:
{
imports = [
./boot.nix
./user-config.nix
../sw
../users.nix
inputs.home-manager.nixosModules.home-manager
inputs.agenix.nixosModules.default
inputs.disko.nixosModules.disko
];
system.stateVersion = "25.11";
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# Automatic Garbage Collection
nix.gc = lib.mkIf config.athenix.system.gc.enable {
automatic = true;
dates = config.athenix.system.gc.frequency;
options = "--delete-older-than ${toString config.athenix.system.gc.retentionDays}d";
};
# Optimize storage
nix.optimise.automatic = config.athenix.system.gc.optimise;
}

View File

@@ -1,52 +0,0 @@
# ============================================================================
# Desktop Configuration
# ============================================================================
# Hardware and boot configuration for standard desktop workstations.
# Includes Intel CPU support and NVMe storage.
{ inputs, ... }:
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(import ../common.nix { inherit inputs; })
(modulesPath + "/installer/scan/not-detected.nix")
];
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe SSD support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
];
# ========== Filesystem Configuration ==========
athenix.host.filesystem.swapSize = lib.mkDefault "16G";
athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# ========== Hardware Configuration ==========
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "desktop";
}

View File

@@ -1,68 +0,0 @@
# ============================================================================
# Ephemeral/Diskless System Configuration
# ============================================================================
# Configuration for systems that run entirely from RAM without persistent storage.
# Suitable for kiosks, netboot clients, and stateless workstations.
# All data is lost on reboot.
{ inputs, ... }:
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(import ../common.nix { inherit inputs; })
(modulesPath + "/installer/scan/not-detected.nix")
];
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
];
# ========== Ephemeral Configuration ==========
# No persistent storage - everything runs from RAM
athenix.host.filesystem.swapSize = lib.mkForce "0G";
athenix.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device
athenix.host.buildMethods = lib.mkDefault [
"iso" # Live ISO image
"ipxe" # Network boot
];
# Disable disk management for RAM-only systems
disko.enableConfig = lib.mkForce false;
# Define tmpfs root filesystem
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"defaults"
"size=50%"
"mode=755"
];
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "stateless-kiosk";
}

View File

@@ -1,65 +0,0 @@
# ============================================================================
# Laptop Configuration
# ============================================================================
# Hardware and boot configuration for laptop systems with mobile features.
# Includes power management, lid switch handling, and Intel graphics fixes.
{ inputs, ... }:
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(import ../common.nix { inherit inputs; })
(modulesPath + "/installer/scan/not-detected.nix")
];
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"thunderbolt" # Thunderbolt support
"nvme" # NVMe SSD support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
"i915.enable_psr=0" # Disable Panel Self Refresh (stability)
"i915.enable_dc=0" # Disable display power saving
"i915.enable_fbc=0" # Disable framebuffer compression
];
# ========== Hardware Configuration ==========
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Filesystem Configuration ==========
athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
athenix.host.filesystem.swapSize = lib.mkDefault "34G"; # Larger swap for hibernation
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
# ========== Power Management ==========
services.upower.enable = lib.mkDefault true;
services.logind.settings = {
Login = {
HandleLidSwitch = "suspend";
HandleLidSwitchExternalPower = "suspend";
HandleLidSwitchDocked = "ignore";
};
};
athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "desktop";
}

View File

@@ -1,62 +0,0 @@
# ============================================================================
# Proxmox LXC Container Configuration
# ============================================================================
# Configuration for lightweight Linux containers running in Proxmox.
# Disables boot/disk management and enables remote development support.
{ inputs, ... }:
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(import ../common.nix { inherit inputs; })
inputs.vscode-server.nixosModules.default
"${modulesPath}/virtualisation/proxmox-lxc.nix"
];
# ========== Nix Configuration ==========
nix.settings.trusted-users = [
"root"
"engr-ugaif"
];
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# ========== Container-Specific Configuration ==========
boot.isContainer = true;
boot.loader.systemd-boot.enable = lib.mkForce false; # No bootloader in container
disko.enableConfig = lib.mkForce false; # No disk management in container
console.enable = true;
# Allow getty to work in containers
systemd.services."getty@".unitConfig.ConditionPathExists = [
""
"/dev/%I"
];
# Suppress unnecessary systemd units for containers
systemd.suppressedSystemUnits = [
"dev-mqueue.mount"
"sys-kernel-debug.mount"
"sys-fs-fuse-connections.mount"
];
# ========== Remote Development ==========
services.vscode-server.enable = true;
# ========== System Configuration ==========
system.stateVersion = "25.11";
athenix.host.buildMethods = lib.mkDefault [
"lxc" # LXC container tarball
"proxmox" # Proxmox VMA archive
];
athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "headless";
}

View File

@@ -1,70 +0,0 @@
# ============================================================================
# Microsoft Surface Tablet Configuration
# ============================================================================
# Hardware configuration for Surface Go tablets in kiosk mode.
# Uses nixos-hardware module and older kernel for Surface-specific drivers.
{ inputs, ... }:
{
config,
lib,
pkgs,
modulesPath,
...
}:
let
# Use older kernel version for better Surface Go compatibility
refSystem = inputs.nixpkgs-old-kernel.lib.nixosSystem {
system = pkgs.stdenv.hostPlatform.system;
modules = [ inputs.nixos-hardware.nixosModules.microsoft-surface-go ];
};
refKernelPackages = refSystem.config.boot.kernelPackages;
in
{
imports = [
(import ../common.nix { inherit inputs; })
(modulesPath + "/installer/scan/not-detected.nix")
inputs.nixos-hardware.nixosModules.microsoft-surface-go
];
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe support (though Surface uses eMMC)
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
"intel_ipu3_imgu" # Intel camera image processing
"intel_ipu3_isys" # Intel camera sensor interface
"fbcon=map:1" # Framebuffer console mapping
"i915.enable_psr=0" # Disable Panel Self Refresh (breaks resume)
"i915.enable_dc=0" # Disable display power saving
];
# Use older kernel for better Surface hardware support
boot.kernelPackages = lib.mkForce refKernelPackages;
# ========== Filesystem Configuration ==========
athenix.host.filesystem.swapSize = lib.mkDefault "8G";
athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; # eMMC storage # eMMC storage
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# ========== Hardware Configuration ==========
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "tablet-kiosk"; # Touch-optimized kiosk mode
}

View File

@@ -1,276 +0,0 @@
{
pkgs,
config,
lib,
inputs,
...
}:
# ============================================================================
# User Configuration Module
# ============================================================================
# This module defines the schema for user accounts and handles their creation.
# It bridges the gap between the data in 'users.nix' and the actual NixOS
# and Home Manager configuration.
let
# Load users.nix to get account definitions
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.athenix.users or { };
# Helper: Resolve external module path from fetchGit/fetchTarball/path
resolveExternalPath =
external:
if external == null then
null
else if builtins.isAttrs external && external ? outPath then
external.outPath
else
external;
# Helper: Check if path exists and is valid
isValidPath =
path:
path != null
&& (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path))
&& builtins.pathExists path;
# Extract athenix.users options from external user.nix modules
# First, build a cache of options per user from their external user.nix (if any).
externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) (
name:
let
user = accounts.${name};
externalPath = resolveExternalPath (user.external or null);
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
in
if isValidPath userNixPath then
let
# Import and evaluate the module with minimal args
outerModule = import userNixPath { inherit inputs; };
evaluatedModule = outerModule {
config = { };
inherit lib pkgs;
osConfig = null;
};
# Extract just the athenix.users.<name> options
athenixUsers = evaluatedModule.athenix.users or { };
in
athenixUsers.${name} or { }
else
{ }
);
# externalUserOptions only contains users that actually have options defined
externalUserOptions = lib.filterAttrs (
_: moduleOptions: moduleOptions != { }
) externalUserModuleOptions;
# Submodule defining the structure of a user account
userSubmodule = lib.types.submodule {
options = {
isNormalUser = lib.mkOption {
type = lib.types.bool;
default = true;
};
description = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
extraGroups = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
hashedPassword = lib.mkOption {
type = lib.types.str;
default = "!";
};
extraPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
};
excludePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
};
homePackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [ ];
};
extraImports = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
};
external = lib.mkOption {
type = lib.types.nullOr (
lib.types.oneOf [
lib.types.path
lib.types.package
lib.types.attrs
]
);
default = null;
description = ''
External user configuration module. Can be:
- A path to a local module directory
- A fetchGit/fetchTarball result pointing to a repository
The external module can contain:
- user.nix (optional): Sets athenix.users.<name> options AND home-manager config
- nixos.nix (optional): System-level NixOS configuration
Example: builtins.fetchGit { url = "https://github.com/user/dotfiles"; rev = "..."; }
'';
};
opensshKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of SSH public keys for the user.";
};
shell = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = null;
description = "The shell for this user.";
};
editor = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = null;
description = "The default editor for this user.";
};
useZshTheme = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Zsh theme.";
};
useNvimPlugins = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to apply the system Neovim configuration.";
};
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether this user account is enabled on this system.";
};
};
};
in
{
options.athenix.users = lib.mkOption {
type = lib.types.attrsOf userSubmodule;
default = { };
description = "User accounts configuration. Set enable=true for users that should exist on this system.";
};
config = {
# Merge user definitions from users.nix with options from external user.nix modules
# External options take precedence over users.nix (which uses lib.mkDefault)
athenix.users = lib.mapAttrs (
name: user:
user
// {
description = lib.mkDefault (user.description or null);
shell = lib.mkDefault (user.shell or null);
extraGroups = lib.mkDefault (user.extraGroups or [ ]);
}
// (externalUserOptions.${name} or { })
) accounts;
# Generate NixOS users
users.users =
let
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
in
lib.mapAttrs (
name: user:
let
isPlasma6 = config.services.desktopManager.plasma6.enable;
defaultPackages = lib.optionals (isPlasma6 && name != "root") [ pkgs.kdePackages.kate ];
finalPackages = lib.subtractLists user.excludePackages (defaultPackages ++ user.extraPackages);
in
{
inherit (user) isNormalUser extraGroups hashedPassword;
description = if user.description != null then user.description else lib.mkDefault "";
openssh.authorizedKeys.keys = user.opensshKeys;
packages = finalPackages;
shell = if user.shell != null then user.shell else pkgs.bash;
}
) enabledAccounts;
# Home Manager configs per user
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
osConfig = config;
inherit inputs;
};
users =
let
enabledAccounts = lib.filterAttrs (_: user: user.enable) config.athenix.users;
in
lib.mapAttrs (
name: user:
let
# Resolve external module paths
hasExternal = user.external != null;
externalPath = resolveExternalPath user.external;
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
hasExternalUser = isValidPath userNixPath;
# Import external user.nix for home-manager (filter out athenix.* options)
externalUserModule =
if hasExternalUser then
let
fullModule = import userNixPath { inherit inputs; };
in
# Only pass through non-athenix options to home-manager
{
config,
lib,
pkgs,
osConfig,
...
}:
let
evaluated = fullModule {
inherit
config
lib
pkgs
osConfig
;
};
in
lib.filterAttrs (attrName: _: attrName != "athenix") evaluated
else
{ };
# Common imports based on user flags
commonImports = lib.optional user.useZshTheme ../sw/theme.nix ++ [
(import ../sw/nvim.nix { inherit user; })
];
# Build imports list
allImports = user.extraImports ++ commonImports ++ lib.optional hasExternalUser externalUserModule;
in
lib.mkMerge [
{
imports = allImports;
# Always set these required options
home.username = name;
home.homeDirectory = if name == "root" then "/root" else "/home/${name}";
home.stateVersion = "25.11";
}
(lib.mkIf (!hasExternal) {
# For local users only, add their packages
home.packages = user.homePackages;
})
]
) enabledAccounts;
};
};
}

23
hw/default.nix Normal file
View File

@@ -0,0 +1,23 @@
# ============================================================================
# Host Types Module
# ============================================================================
# This module exports all available host types as an attribute set.
# Each type is a NixOS module (a function suitable for lib.types.submodule).
{ inputs }:
let
lib = inputs.nixpkgs.lib;
inherit (builtins) readDir attrNames;
inherit (lib) filterAttrs removeSuffix genAttrs;
files = readDir ./.;
# Keep only regular *.nix files except default.nix
nixFiles = filterAttrs (
name: type: type == "regular" && lib.hasSuffix ".nix" name && name != "default.nix"
) files;
moduleNames = map (name: removeSuffix ".nix" name) (attrNames nixFiles);
in
# Export: { name = <module function from ./name.nix>; }
genAttrs moduleNames (name: import (./. + ("/" + name + ".nix")))

73
hw/nix-desktop.nix Normal file
View File

@@ -0,0 +1,73 @@
# ============================================================================
# Desktop Configuration
# ============================================================================
# Hardware and boot configuration for standard desktop workstations.
# Includes Intel CPU support and NVMe storage.
{
config,
lib,
modulesPath,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-desktop;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
options.athenix.hw.nix-desktop = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable desktop workstation hardware configuration.";
};
};
};
default = { };
description = "Desktop workstation hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe SSD support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
];
# ========== Filesystem Configuration ==========
athenix.host.filesystem.swapSize = lib.mkDefault "16G";
athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# ========== Hardware Configuration ==========
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.desktop.enable = lib.mkDefault true;
};
}

88
hw/nix-ephemeral.nix Normal file
View File

@@ -0,0 +1,88 @@
# ============================================================================
# Ephemeral/Diskless System Configuration
# ============================================================================
# Configuration for systems that run entirely from RAM without persistent storage.
# Suitable for kiosks, netboot clients, and stateless workstations.
# All data is lost on reboot.
{
config,
lib,
modulesPath,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-ephemeral;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
options.athenix.hw.nix-ephemeral = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable ephemeral/diskless system hardware configuration.";
};
};
};
default = { };
description = "Ephemeral hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
];
# ========== Ephemeral Configuration ==========
# No persistent storage - everything runs from RAM
athenix.host.filesystem.swapSize = lib.mkForce "0G";
athenix.host.filesystem.device = lib.mkForce "/dev/null"; # Dummy device
athenix.host.buildMethods = lib.mkDefault [
"iso" # Live ISO image
"ipxe" # Network boot
];
# Disable disk management for RAM-only systems
disko.enableConfig = lib.mkForce false;
# Define tmpfs root filesystem
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"defaults"
"size=50%"
"mode=755"
];
};
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
athenix.sw.enable = lib.mkDefault true;
athenix.sw.stateless-kiosk.enable = lib.mkDefault true;
};
}

85
hw/nix-laptop.nix Normal file
View File

@@ -0,0 +1,85 @@
# ============================================================================
# Laptop Configuration
# ============================================================================
# Hardware and boot configuration for laptop systems with mobile features.
# Includes power management, lid switch handling, and Intel graphics fixes.
{
config,
lib,
modulesPath,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-laptop;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
options.athenix.hw.nix-laptop = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable laptop hardware configuration with power management.";
};
};
};
default = { };
description = "Laptop hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"thunderbolt" # Thunderbolt support
"nvme" # NVMe SSD support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
"i915.enable_psr=0" # Disable Panel Self Refresh (stability)
"i915.enable_dc=0" # Disable display power saving
"i915.enable_fbc=0" # Disable framebuffer compression
];
# ========== Hardware Configuration ==========
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Filesystem Configuration ==========
athenix.host.filesystem.device = lib.mkDefault "/dev/nvme0n1";
athenix.host.filesystem.swapSize = lib.mkDefault "34G"; # Larger swap for hibernation
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
# ========== Power Management ==========
services.upower.enable = lib.mkDefault true;
services.logind.settings = {
Login = {
HandleLidSwitch = "suspend";
HandleLidSwitchExternalPower = "suspend";
HandleLidSwitchDocked = "ignore";
};
};
athenix.sw.enable = lib.mkDefault true;
athenix.sw.desktop.enable = lib.mkDefault true;
};
}

79
hw/nix-lxc.nix Normal file
View File

@@ -0,0 +1,79 @@
# ============================================================================
# Proxmox LXC Container Configuration
# ============================================================================
# Configuration for lightweight Linux containers running in Proxmox.
# Disables boot/disk management and enables remote development support.
{
config,
lib,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-lxc;
in
{
options.athenix.hw.nix-lxc = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Proxmox LXC container hardware configuration.";
};
};
};
default = { };
description = "Proxmox LXC hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Nix Configuration ==========
nix.settings.trusted-users = [
"root"
"engr-ugaif"
];
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# ========== Container-Specific Configuration ==========
boot.isContainer = true;
boot.loader.systemd-boot.enable = lib.mkForce false; # No bootloader in container
disko.enableConfig = lib.mkForce false; # No disk management in container
console.enable = true;
# Set timezone to fix /etc/localtime for Docker containers
time.timeZone = lib.mkDefault "America/New_York";
# Allow getty to work in containers
systemd.services."getty@".unitConfig.ConditionPathExists = [
""
"/dev/%I"
];
# Suppress unnecessary systemd units for containers
systemd.suppressedSystemUnits = [
"dev-mqueue.mount"
"sys-kernel-debug.mount"
"sys-fs-fuse-connections.mount"
];
# ========== Remote Development ==========
services.vscode-server.enable = true;
# ========== System Configuration ==========
system.stateVersion = "25.11";
athenix.host.buildMethods = lib.mkDefault [
"lxc" # LXC container tarball
"proxmox" # Proxmox VMA archive
];
athenix.sw.enable = lib.mkDefault true;
athenix.sw.headless.enable = lib.mkDefault true;
};
}

89
hw/nix-surface.nix Normal file
View File

@@ -0,0 +1,89 @@
# ============================================================================
# Microsoft Surface Tablet Configuration
# ============================================================================
# Hardware configuration for Surface Go tablets in kiosk mode.
# Uses nixos-hardware module and older kernel for Surface-specific drivers.
{
config,
lib,
pkgs,
modulesPath,
inputs,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-surface;
# Use older kernel version for better Surface Go compatibility
refSystem = inputs.nixpkgs-old-kernel.lib.nixosSystem {
system = pkgs.stdenv.hostPlatform.system;
modules = [ inputs.nixos-hardware.nixosModules.microsoft-surface-go ];
};
refKernelPackages = refSystem.config.boot.kernelPackages;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
inputs.nixos-hardware.nixosModules.microsoft-surface-go
];
options.athenix.hw.nix-surface = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Microsoft Surface tablet hardware configuration.";
};
};
};
default = { };
description = "Microsoft Surface hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"nvme" # NVMe support (though Surface uses eMMC)
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
"intel_ipu3_imgu" # Intel camera image processing
"intel_ipu3_isys" # Intel camera sensor interface
"fbcon=map:1" # Framebuffer console mapping
"i915.enable_psr=0" # Disable Panel Self Refresh (breaks resume)
"i915.enable_dc=0" # Disable display power saving
];
# Use older kernel for better Surface hardware support
boot.kernelPackages = lib.mkForce refKernelPackages;
# ========== Filesystem Configuration ==========
athenix.host.filesystem.swapSize = lib.mkDefault "8G";
athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0"; # eMMC storage # eMMC storage
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# ========== Hardware Configuration ==========
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.tablet-kiosk.enable = lib.mkDefault true; # Touch-optimized kiosk mode
};
}

View File

@@ -4,27 +4,46 @@
# Configuration for NixOS running in WSL2 on Windows. # Configuration for NixOS running in WSL2 on Windows.
# Integrates with nixos-wsl for WSL-specific functionality. # Integrates with nixos-wsl for WSL-specific functionality.
{ inputs, ... }:
{ {
lib, lib,
config, config,
... ...
}: }:
{
imports = [
(import ../common.nix { inherit inputs; })
inputs.nixos-wsl.nixosModules.default
inputs.vscode-server.nixosModules.default
];
# ========== Options ========== with lib;
let
cfg = config.athenix.hw.nix-wsl;
in
{
options.athenix.hw.nix-wsl = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Windows Subsystem for Linux hardware configuration.";
};
};
};
default = { };
description = "WSL hardware type configuration.";
};
# WSL user option (at module level, not inside config)
options.athenix.host.wsl.user = lib.mkOption { options.athenix.host.wsl.user = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "engr-ugaif"; default = "engr-ugaif";
description = "The default user to log in as in WSL."; description = ''
The default user to automatically log in as when starting WSL.
This user must be enabled via athenix.users.<username>.enable = true.
Tip: Use athenix.forUser = "username" as a shortcut to set both.
'';
example = "alice";
}; };
config = { config = mkIf cfg.enable {
# ========== WSL Configuration ========== # ========== WSL Configuration ==========
wsl.enable = true; wsl.enable = true;
# Use forUser if set, otherwise fall back to wsl.user option # Use forUser if set, otherwise fall back to wsl.user option
@@ -33,7 +52,7 @@
# ========== Software Profile ========== # ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true; athenix.sw.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "headless"; athenix.sw.headless.enable = lib.mkDefault true;
# ========== Remote Development ========== # ========== Remote Development ==========
services.vscode-server.enable = true; services.vscode-server.enable = true;
@@ -50,5 +69,8 @@
# Provide dummy values for required options from boot.nix # Provide dummy values for required options from boot.nix
athenix.host.filesystem.device = "/dev/null"; athenix.host.filesystem.device = "/dev/null";
athenix.host.filesystem.swapSize = "0G"; athenix.host.filesystem.swapSize = "0G";
# WSL doesn't use installer ISOs
athenix.host.buildMethods = lib.mkDefault [ ];
}; };
} }

71
hw/nix-zima.nix Normal file
View File

@@ -0,0 +1,71 @@
# ============================================================================
# Desktop Configuration
# ============================================================================
# Hardware and boot configuration for standard desktop workstations.
# Includes Intel CPU support and NVMe storage.
{
config,
lib,
modulesPath,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-zima;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
options.athenix.hw.nix-zima = mkOption {
type = types.submodule {
options = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Zima-specific hardware configuration.";
};
};
};
default = { };
description = "Zima hardware type configuration.";
};
config = mkIf cfg.enable {
# ========== Boot Configuration ==========
boot.initrd.availableKernelModules = [
"xhci_pci" # USB 3.0 support
"usb_storage" # USB storage devices
"sd_mod" # SD card support
"sdhci_pci" # SD card host controller
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; # Intel virtualization support
boot.extraModulePackages = [ ];
boot.kernelParams = [
"quiet" # Minimal boot messages
"splash" # Show Plymouth boot splash
"boot.shell_on_fail" # Emergency shell on boot failure
"udev.log_priority=3" # Reduce udev logging
"rd.systemd.show_status=auto" # Show systemd status during boot
];
# ========== Filesystem Configuration ==========
athenix.host.filesystem.useSwap = lib.mkDefault false;
athenix.host.filesystem.device = lib.mkDefault "/dev/mmcblk0";
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# ========== Hardware Configuration ==========
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.desktop.enable = lib.mkDefault true;
};
}

View File

@@ -46,7 +46,7 @@ Add the host to `inventory.nix` with the `nix-lxc` type or ensure it has the app
} }
``` ```
Your host type configuration (`hosts/types/nix-lxc.nix`) should include: Your host type configuration (`hw/nix-lxc.nix`) should include:
```nix ```nix
{ {

View File

@@ -1,8 +1,9 @@
{ {
inputs, inputs,
hosts, fleet,
self, self,
system, system,
users ? { },
}: }:
# 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>`
@@ -18,7 +19,7 @@ let
hostName: hostName:
let let
targetConfig = self.nixosConfigurations.${hostName}.config; targetConfig = self.nixosConfigurations.${hostName}.config;
targetSystem = targetConfig.system.build.toplevel; targetSystemBuild = targetConfig.system.build;
diskoScript = targetConfig.system.build.diskoScript; diskoScript = targetConfig.system.build.diskoScript;
in in
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
@@ -27,8 +28,9 @@ let
inherit inherit
inputs inputs
hostName hostName
targetSystem targetSystemBuild
diskoScript diskoScript
users
; ;
hostPlatform = system; hostPlatform = system;
}; };
@@ -45,7 +47,10 @@ let
nixos-generators.nixosGenerate { nixos-generators.nixosGenerate {
inherit system; inherit system;
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
modules = hosts.modules.${hostName} ++ [ modules = fleet.modules.${hostName} ++ [
{
config.athenix.users = lib.mapAttrs (_: user: lib.mapAttrs (_: lib.mkDefault) user) users;
}
{ {
disko.enableConfig = lib.mkForce false; disko.enableConfig = lib.mkForce false;
services.upower.enable = lib.mkForce false; services.upower.enable = lib.mkForce false;
@@ -61,8 +66,11 @@ let
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
inherit system; inherit system;
specialArgs = { inherit inputs; }; specialArgs = { inherit inputs; };
modules = hosts.modules.${hostName} ++ [ modules = fleet.modules.${hostName} ++ [
"${nixpkgs}/nixos/modules/installer/netboot/netboot.nix" "${nixpkgs}/nixos/modules/installer/netboot/netboot.nix"
{
config.athenix.users = lib.mapAttrs (_: user: lib.mapAttrs (_: lib.mkDefault) user) users;
}
{ {
disko.enableConfig = lib.mkForce false; disko.enableConfig = lib.mkForce false;
services.upower.enable = lib.mkForce false; services.upower.enable = lib.mkForce false;
@@ -70,14 +78,14 @@ let
]; ];
}; };
hostNames = builtins.attrNames hosts.nixosConfigurations; hostNames = builtins.attrNames fleet.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 ( installerPackages = lib.listToAttrs (
lib.concatMap ( lib.concatMap (
name: name:
let let
cfg = hosts.nixosConfigurations.${name}; cfg = fleet.nixosConfigurations.${name};
in in
if lib.elem "installer-iso" cfg.config.athenix.host.buildMethods then if lib.elem "installer-iso" cfg.config.athenix.host.buildMethods then
[ [
@@ -96,7 +104,7 @@ let
lib.concatMap ( lib.concatMap (
name: name:
let let
cfg = hosts.nixosConfigurations.${name}; cfg = fleet.nixosConfigurations.${name};
in in
if lib.elem "iso" cfg.config.athenix.host.buildMethods then if lib.elem "iso" cfg.config.athenix.host.buildMethods then
[ [
@@ -115,7 +123,7 @@ let
lib.concatMap ( lib.concatMap (
name: name:
let let
cfg = hosts.nixosConfigurations.${name}; cfg = fleet.nixosConfigurations.${name};
in in
if lib.elem "ipxe" cfg.config.athenix.host.buildMethods then if lib.elem "ipxe" cfg.config.athenix.host.buildMethods then
[ [
@@ -145,7 +153,7 @@ let
lib.concatMap ( lib.concatMap (
name: name:
let let
cfg = hosts.nixosConfigurations.${name}; cfg = fleet.nixosConfigurations.${name};
in in
if lib.elem "lxc" cfg.config.athenix.host.buildMethods then if lib.elem "lxc" cfg.config.athenix.host.buildMethods then
[ [
@@ -164,7 +172,7 @@ let
lib.concatMap ( lib.concatMap (
name: name:
let let
cfg = hosts.nixosConfigurations.${name}; cfg = fleet.nixosConfigurations.${name};
in in
if lib.elem "proxmox" cfg.config.athenix.host.buildMethods then if lib.elem "proxmox" cfg.config.athenix.host.buildMethods then
[ [

View File

@@ -2,13 +2,10 @@
# 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, pkgs,
inputs,
hostName, hostName,
hostPlatform, hostPlatform,
targetSystem, targetSystemBuild,
diskoScript, diskoScript,
... ...
}: }:
@@ -17,11 +14,21 @@
pkgs.git pkgs.git
pkgs.bashInteractive pkgs.bashInteractive
pkgs.curl pkgs.curl
targetSystem targetSystemBuild.toplevel
]; ];
nixpkgs.hostPlatform = hostPlatform; nixpkgs.hostPlatform = hostPlatform;
nix.settings.experimental-features = "nix-command flakes";
system.extraDependencies = with targetSystemBuild; [
toplevel
etc
bootStage2
];
isoImage.storeContents = [ targetSystemBuild.toplevel ];
systemd.services.auto-install = { systemd.services.auto-install = {
description = "Automatic NixOS install for ${hostName}"; description = "Automatic NixOS install for ${hostName}";
after = [ after = [
@@ -44,8 +51,12 @@
echo ">>> Running disko script..." echo ">>> Running disko script..."
${diskoScript} ${diskoScript}
echo ">>> Running nixos-install..." echo ">>> Setting up NixOS..."
nixos-install --no-root-passwd --system ${targetSystem} nixos-install \
--system ${targetSystemBuild.toplevel} \
--no-root-passwd \
--no-channel-copy \
--substituters ""
echo ">>> Done. Rebooting." echo ">>> Done. Rebooting."
systemctl reboot systemctl reboot

View File

@@ -6,40 +6,26 @@
# #
# Usage in another flake: # Usage in another flake:
# # Full host type configurations (includes hardware + software + system config) # # Full host type configurations (includes hardware + software + system config)
# inputs.nixos-systems.nixosModules.nix-desktop # inputs.athenix.nixosModules.nix-desktop
# inputs.nixos-systems.nixosModules.nix-laptop # inputs.athenix.nixosModules.nix-laptop
# #
# # Software-only configurations (for custom hardware setups) # # Software-only configuration (for custom hardware setups)
# # Note: These include theme.nix in home-manager.sharedModules automatically # inputs.athenix.nixosModules.sw
# inputs.nixos-systems.nixosModules.sw-desktop
# inputs.nixos-systems.nixosModules.sw-headless
#
# # Home Manager modules (user-level configuration)
# # Theme module (no parameters):
# home-manager.users.myuser.imports = [ inputs.nixos-systems.homeManagerModules.theme ];
#
# # Neovim module (requires user parameter):
# home-manager.users.myuser.imports = [
# (inputs.nixos-systems.homeManagerModules.nvim {
# user = config.athenix.users.accounts.myuser;
# })
# ];
{ inputs }: { inputs }:
# Expose hardware type modules from hw/ directory
# This returns an attribute set like: { nix-desktop = ...; nix-laptop = ...; nix-lxc = ...; }
let
hostTypes = import ../hw { inherit inputs; };
in
{ {
# ========== Full Host Type Modules ========== # Software configuration module - main module with all athenix.sw options
# Complete system configurations including hardware, boot, and software # Use athenix.sw.<type>.enable to enable software profiles: desktop, tablet-kiosk, headless, stateless-kiosk, builders
nix-desktop = import ../hosts/types/nix-desktop.nix { inherit inputs; }; # Desktop workstations hw = hostTypes;
nix-laptop = import ../hosts/types/nix-laptop.nix { inherit inputs; }; # Laptop systems sw =
nix-surface = import ../hosts/types/nix-surface.nix { inherit inputs; }; # Surface tablets {
nix-lxc = import ../hosts/types/nix-lxc.nix { inherit inputs; }; # Proxmox containers inputs,
nix-wsl = import ../hosts/types/nix-wsl.nix { inherit inputs; }; # WSL2 systems ...
nix-ephemeral = import ../hosts/types/nix-ephemeral.nix { inherit inputs; }; # Diskless/RAM-only }@args:
(import ../sw/default.nix (args // { inherit inputs; }));
# ========== Software Configuration Module ==========
# Main software module with all athenix.sw options
# Use athenix.sw.type to select profile: "desktop", "tablet-kiosk", "headless", "stateless-kiosk"
# Use athenix.sw.extraPackages to add additional packages
# Use athenix.sw.kioskUrl to set kiosk mode URL
sw = { inputs, ... }@args: (import ../sw/default.nix (args // { inherit inputs; }));
} }

View File

@@ -1,4 +1,3 @@
{
# ============================================================================ # ============================================================================
# Fleet Inventory # Fleet Inventory
# ============================================================================ # ============================================================================
@@ -44,8 +43,12 @@
# athenix.forUser = "username"; # Automatically enables user (sets athenix.users.username.enable = true) # athenix.forUser = "username"; # Automatically enables user (sets athenix.users.username.enable = true)
# #
# External modules (instead of config): # External modules (instead of config):
# Device values can be either a config attrset OR a fetchGit/fetchurl call # Device values can be a config attrset with an optional 'external' field:
# that points to an external Nix module. The module will be imported and evaluated. # devices."hostname" = {
# external = { url = "..."; rev = "..."; submodules? = false; }; # Lazy: only fetched when building this host
# # ... additional config options
# };
# The external module will be imported and evaluated only when this specific host is built.
# #
# Examples: # Examples:
# "lab" = { devices = 3; }; # Quick: lab1, lab2, lab3 # "lab" = { devices = 3; }; # Quick: lab1, lab2, lab3
@@ -62,11 +65,15 @@
# devices."alice".athenix.forUser = "alice123"; # Sets up for user alice123 # devices."alice".athenix.forUser = "alice123"; # Sets up for user alice123
# }; # };
# "external" = { # "external" = {
# devices."remote" = builtins.fetchGit { # External module via Git # devices."remote".external = { url = "..."; rev = "..."; }; # External module via Git (lazy)
# url = "https://github.com/example/config"; # url = "https://github.com/example/config";
# rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; # rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014";
# }; # };
# }; # ========== Lab Laptops ========== # };
{ ... }:
{
athenix.fleet = {
# ========== Lab Laptops ==========
# Creates: nix-laptop1, nix-laptop2 # Creates: nix-laptop1, nix-laptop2
# Both get hdh20267 user via overrides # Both get hdh20267 user via overrides
nix-laptop = { nix-laptop = {
@@ -85,10 +92,10 @@
nix-surface = { nix-surface = {
defaultCount = 3; defaultCount = 3;
devices = { devices = {
"1".athenix.sw.kioskUrl = "https://google.com"; "1".athenix.sw.tablet-kiosk.kioskUrl = "https://google.com";
}; };
overrides = { overrides = {
athenix.sw.kioskUrl = "https://yahoo.com"; athenix.sw.tablet-kiosk.kioskUrl = "https://yahoo.com";
}; };
}; };
@@ -99,11 +106,10 @@
"nix-builder" = { "nix-builder" = {
# Gitea Actions self-hosted runner configuration # Gitea Actions self-hosted runner configuration
athenix.sw = { athenix.sw = {
type = [ headless.enable = true;
"headless" builders = {
"builders" enable = true;
]; giteaRunner = {
builders.giteaRunner = {
enable = true; enable = true;
url = "https://git.factory.uga.edu"; url = "https://git.factory.uga.edu";
# Token file must be created manually at this path with a Gitea runner token # Token file must be created manually at this path with a Gitea runner token
@@ -120,10 +126,11 @@
}; };
}; };
}; };
"usda-dash" = builtins.fetchGit { };
url = "git@factory.uga.edu:MODEL/usda-dash-config.git"; "usda-dash".external = {
rev = "49cded91cff4a956d4e01ac6b8fe4efa86f82182"; url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git";
submodules = true; rev = "ce2700b0196e106f7c013bbcee851a5f96b146a3";
submodules = false;
}; };
}; };
overrides = { overrides = {
@@ -139,37 +146,12 @@
}; };
}; };
# ========== ZimaBoard Desktops ==========
# Creates: nix-zima1, nix-zima2, nix-zima3
nix-zima.devices = 3;
# ========== Ephemeral/Netboot System ========== # ========== Ephemeral/Netboot System ==========
# Creates: nix-ephemeral1 # Creates: nix-ephemeral1
nix-ephemeral.devices = 1; nix-ephemeral.devices = 1;
};
# ========== Example: External Module Configurations ==========
# Uncomment to use external modules from Git repositories:
#
# external-systems = {
# devices = {
# # Option 1: fetchGit with specific revision (recommended for reproducibility)
# "prod-server" = builtins.fetchGit {
# url = "https://github.com/example/server-config";
# rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014"; # Full commit hash
# ref = "main"; # Optional: branch/tag name
# };
#
# # Option 2: fetchGit with latest from branch (less reproducible)
# "dev-server" = builtins.fetchGit {
# url = "https://github.com/example/server-config";
# ref = "develop";
# };
#
# # Option 3: fetchTarball for specific release
# "test-server" = builtins.fetchTarball {
# url = "https://github.com/example/server-config/archive/v1.0.0.tar.gz";
# sha256 = "sha256:0000000000000000000000000000000000000000000000000000";
# };
#
# # Option 4: Mix external module with local overrides
# # Note: The external module's default.nix should export a NixOS module
# # that accepts { inputs, ... } as parameters
# };
# };
} }

8
lib/default.nix Normal file
View File

@@ -0,0 +1,8 @@
{
lib,
...
}:
{
mkFleet = import ./mkFleet.nix;
macCaseBuilder = import ./macCaseBuilder.nix { inherit lib; };
}

33
lib/macCaseBuilder.nix Normal file
View File

@@ -0,0 +1,33 @@
{ lib }:
let
# Default MAC address to station number mapping
defaultHostmap = {
"00:e0:4c:46:0b:32" = "1";
"00:e0:4c:46:07:26" = "2";
"00:e0:4c:46:05:94" = "3";
"00:e0:4c:46:07:11" = "4";
"00:e0:4c:46:08:02" = "5";
"00:e0:4c:46:08:5c" = "6";
};
# macCaseBuilder: builds a shell case statement from a hostmap
# Parameters:
# varName: the shell variable to assign
# prefix: optional string to prepend to the value (default: "")
# hostmap: optional attribute set to use (default: built-in hostmap)
#
# Example:
# macCaseBuilder { varName = "STATION"; prefix = "nix-"; }
# # Generates case statements like: 00:e0:4c:46:0b:32) STATION=nix-1 ;;
builder =
{
varName,
prefix ? "",
hostmap ? defaultHostmap,
}:
lib.concatStringsSep "\n" (
lib.mapAttrsToList (mac: val: " ${mac}) ${varName}=${prefix}${val} ;;") hostmap
);
in
# Export the builder function with hostmap as an accessible attribute
lib.setFunctionArgs builder { } // { hostmap = defaultHostmap; }

18
lib/mkFleet.nix Normal file
View File

@@ -0,0 +1,18 @@
# Generate fleet configurations with custom fleet and hardware types
# Usage: nixosConfigurations = athenix.lib.mkFleet { fleet = { ... }; hwTypes = { ... }; }
{
inputs,
lib,
config,
self ? null,
users ? { },
}:
import ../fleet/default.nix {
inherit
inputs
lib
config
self
users
;
}

194
parts/docs.nix Normal file
View File

@@ -0,0 +1,194 @@
# Documentation generation
{
inputs,
self,
lib,
...
}:
let
pkgs = inputs.nixpkgs.legacyPackages.x86_64-linux;
# Extract options from a sample configuration
getAthenixOptions =
configName:
let
nixosConfig = self.nixosConfigurations.${configName};
evaledOptions = nixosConfig.options;
# Filter to just athenix namespace
athenixOptions = evaledOptions.athenix or { };
in
athenixOptions;
# Generate wiki home page
wikiHome = pkgs.writeText "Home.md" ''
# Athenix - NixOS Fleet Management
Athenix is a NixOS configuration system for managing the UGA Innovation Factory's fleet of devices using Nix flakes and a custom configuration framework.
## Quick Start
- [Configuration Options](Configuration-Options) - All available `athenix.*` options
- [User Guide](User-Configuration) - Setting up user accounts and dotfiles
- [Building](Building) - Creating installers and system images
- [Development](Development) - Contributing to Athenix
## Features
- **Inventory-based fleet management** - Define entire device fleets in a single file
- **Multiple hardware types** - Desktops, laptops, Surface tablets, LXC containers, WSL
- **Flexible software configurations** - Desktop, headless, kiosk, and builder modes
- **External module support** - Load user dotfiles and system configs from Git repos
- **Declarative everything** - Reproducible builds with pinned dependencies
## Software Types
Enable different system configurations:
- **desktop** - Full KDE Plasma 6 desktop environment
- **headless** - Minimal server/container configuration
- **tablet-kiosk** - Touch-optimized kiosk for Surface tablets
- **stateless-kiosk** - Diskless PXE boot kiosk
- **builders** - CI/CD build server with Gitea Actions runner
## Hardware Types
- **nix-desktop** - Desktop workstations
- **nix-laptop** - Laptop computers
- **nix-surface** - Microsoft Surface Pro tablets
- **nix-lxc** - LXC containers (Proxmox)
- **nix-wsl** - Windows Subsystem for Linux
- **nix-ephemeral** - Stateless systems (PXE boot)
## Documentation
Browse the documentation using the sidebar or start with:
- [README](README) - Repository overview and getting started
- [Configuration Options](Configuration-Options) - Complete option reference
- [Inventory Guide](Inventory) - Managing the device fleet
- [External Modules](External-Modules) - Using external configurations
'';
# Generate markdown documentation from options
optionsToMarkdown =
options:
pkgs.writeText "options.md" ''
# Configuration Options
This document describes all available configuration options in the Athenix namespace.
## Quick Reference
- **athenix.sw** - Software configuration (desktop, headless, kiosk modes)
- **athenix.users** - User account management
- **athenix.host** - Host-specific settings (filesystem, build methods)
- **athenix.fleet** - Fleet inventory definitions
- **athenix.forUser** - Convenience option to enable a user
- **athenix.system.gc** - Garbage collection settings
## Detailed Options
For detailed option information, use:
```bash
# View all athenix options
nix eval .#nixosConfigurations.nix-desktop1.options.athenix --apply builtins.attrNames
# View specific option description
nix eval .#nixosConfigurations.nix-desktop1.options.athenix.sw.desktop.enable.description
# Export all options to JSON
nix build .#athenix-options
cat result | jq
```
## Software Types
Enable different system configurations:
- **desktop** - Full KDE Plasma desktop environment
- **headless** - Server/container configuration
- **tablet-kiosk** - Touch-optimized kiosk for tablets
- **stateless-kiosk** - Diskless PXE boot kiosk
- **builders** - Build server with optional Gitea Actions runner
See the individual option descriptions for detailed information.
'';
in
{
perSystem =
{ system, ... }:
lib.mkIf (system == "x86_64-linux") {
packages = {
# Generate option documentation in markdown
docs =
pkgs.runCommand "athenix-docs"
{
nativeBuildInputs = [ pkgs.jq ];
}
''
mkdir -p $out
# Generate wiki home page
cat > $out/Home.md << 'EOF'
${builtins.readFile wikiHome}
EOF
# Copy main README
cp ${../README.md} $out/README.md
# Copy documentation with wiki-friendly names
cp ${../docs/BUILDING.md} $out/Building.md
cp ${../docs/DEVELOPMENT.md} $out/Development.md
cp ${../docs/EXTERNAL_MODULES.md} $out/External-Modules.md
cp ${../docs/INVENTORY.md} $out/Inventory.md
cp ${../docs/NAMESPACE.md} $out/Namespace.md
cp ${../docs/USER_CONFIGURATION.md} $out/User-Configuration.md
# Generate options reference
cat > $out/Configuration-Options.md << 'EOF'
${builtins.readFile (optionsToMarkdown (getAthenixOptions "nix-desktop1"))}
EOF
echo "Documentation generated in $out"
'';
# Extract just the athenix namespace options as JSON
athenix-options =
let
nixosConfig =
self.nixosConfigurations.nix-desktop1
or (builtins.head (builtins.attrValues self.nixosConfigurations));
# Recursively extract option information
extractOption =
opt:
if opt ? _type && opt._type == "option" then
{
inherit (opt) description;
type = opt.type.description or (opt.type.name or "unknown");
default =
if opt ? default then
if builtins.isAttrs opt.default && opt.default ? _type then "<special>" else opt.default
else
null;
example =
if opt ? example then
if builtins.isAttrs opt.example && opt.example ? _type then "<special>" else opt.example
else
null;
}
else if builtins.isAttrs opt then
lib.mapAttrs (name: value: extractOption value) (
# Filter out internal attributes
lib.filterAttrs (n: _: !lib.hasPrefix "_" n) opt
)
else
null;
athenixOpts = nixosConfig.options.athenix or { };
in
pkgs.writeText "athenix-options.json" (builtins.toJSON (extractOption athenixOpts));
};
};
}

9
parts/formatter.nix Normal file
View File

@@ -0,0 +1,9 @@
# Formatter configuration for flake-parts
{ ... }:
{
perSystem =
{ pkgs, ... }:
{
formatter = pkgs.nixfmt-rfc-style;
};
}

8
parts/lib.nix Normal file
View File

@@ -0,0 +1,8 @@
# Library functions for flake-parts
{ inputs, ... }:
{
flake.lib = import ../lib {
inherit inputs;
lib = inputs.nixpkgs.lib;
};
}

View File

@@ -0,0 +1,28 @@
# NixOS configurations generated from fleet
{
inputs,
self,
lib,
config,
...
}:
{
imports = [
../fleet/fleet-option.nix
];
flake.nixosConfigurations =
let
users = config.athenix.users;
fleet = self.lib.mkFleet {
inherit
inputs
lib
config
self
users
;
};
in
fleet.nixosConfigurations;
}

5
parts/nixos-modules.nix Normal file
View File

@@ -0,0 +1,5 @@
# Expose host type modules and installer modules for external use
{ inputs, ... }:
{
flake.nixosModules = import ../installer/modules.nix { inherit inputs; };
}

37
parts/packages.nix Normal file
View File

@@ -0,0 +1,37 @@
# Build artifacts (ISOs, LXC containers, etc.)
{
inputs,
self,
lib,
config,
...
}:
{
perSystem =
{ system, ... }:
lib.mkIf (system == "x86_64-linux") {
packages =
let
users = config.athenix.users;
fleet = self.lib.mkFleet {
inherit
inputs
lib
config
self
users
;
};
artifacts = import ../installer/artifacts.nix {
inherit
inputs
fleet
self
system
users
;
};
in
artifacts;
};
}

5
parts/templates.nix Normal file
View File

@@ -0,0 +1,5 @@
# Templates for external configurations
{ ... }:
{
flake.templates = import ../templates;
}

187
secrets.nix Normal file
View File

@@ -0,0 +1,187 @@
# ============================================================================
# Agenix Secret Recipients Configuration (Auto-Generated)
# ============================================================================
# This file automatically discovers hosts and their public keys from the
# secrets/ directory structure and generates recipient configurations.
#
# Directory structure:
# secrets/{hostname}/*.pub -> SSH/age public keys for that host
# secrets/global/*.pub -> Keys accessible to all hosts
#
# Usage:
# ragenix -e secrets/global/example.age # Edit/create secret
# ragenix -r # Re-key all secrets
#
# To add admin keys for editing secrets, create secrets/admins/*.pub files
# with your personal age public keys (generated with: age-keygen)
let
lib = builtins;
# Helper functions not in builtins
filterAttrs =
pred: set:
lib.listToAttrs (
lib.filter (item: pred item.name item.value) (
lib.map (name: {
inherit name;
value = set.${name};
}) (lib.attrNames set)
)
);
concatLists = lists: lib.foldl' (acc: list: acc ++ list) [ ] lists;
unique =
list:
let
go =
acc: remaining:
if remaining == [ ] then
acc
else if lib.elem (lib.head remaining) acc then
go acc (lib.tail remaining)
else
go (acc ++ [ (lib.head remaining) ]) (lib.tail remaining);
in
go [ ] list;
hasSuffix =
suffix: str:
let
lenStr = lib.stringLength str;
lenSuffix = lib.stringLength suffix;
in
lenStr >= lenSuffix && lib.substring (lenStr - lenSuffix) lenSuffix str == suffix;
nameValuePair = name: value: { inherit name value; };
secretsPath = ./secrets;
# Read all directories in secrets/
secretDirs = if lib.pathExists secretsPath then lib.readDir secretsPath else { };
# Filter to only directories (excludes files)
isDirectory = name: type: type == "directory";
directories = lib.filter (name: isDirectory name secretDirs.${name}) (lib.attrNames secretDirs);
# Read public keys from a directory and convert to age format
readHostKeys =
dirName:
let
dirPath = secretsPath + "/${dirName}";
files = if lib.pathExists dirPath then lib.readDir dirPath else { };
# Prefer .age.pub files (pre-converted), fall back to .pub files
agePubFiles = filterAttrs (name: type: type == "regular" && hasSuffix ".age.pub" name) files;
sshPubFiles = filterAttrs (
name: type: type == "regular" && hasSuffix ".pub" name && !(hasSuffix ".age.pub" name)
) files;
# Read age public keys (already in correct format)
ageKeys = lib.map (
name:
let
content = lib.readFile (dirPath + "/${name}");
# Trim whitespace/newlines
trimmed = lib.replaceStrings [ "\n" " " "\r" "\t" ] [ "" "" "" "" ] content;
in
trimmed
) (lib.attrNames agePubFiles);
# For SSH keys, just include them as-is (user needs to convert with ssh-to-age)
# Or they can run the update-age-keys.sh script
sshKeys =
if (lib.length (lib.attrNames sshPubFiles)) > 0 then
lib.trace "Warning: ${dirName} has unconverted SSH keys. Run secrets/update-age-keys.sh" [ ]
else
[ ];
in
lib.filter (k: k != null && k != "") (ageKeys ++ sshKeys);
# Build host key mappings: { hostname = [ "age1..." "age2..." ]; }
hostKeys = lib.listToAttrs (
lib.map (dir: nameValuePair dir (readHostKeys dir)) (
lib.filter (d: d != "global" && d != "admins") directories
)
);
# Global keys that all hosts can use
globalKeys = if lib.elem "global" directories then readHostKeys "global" else [ ];
# Admin keys for editing secrets
adminKeys = if lib.elem "admins" directories then readHostKeys "admins" else [ ];
# All host keys combined
allHostKeys = concatLists (lib.attrValues hostKeys);
# Find all .age files in the secrets directory
findSecrets =
dir:
let
dirPath = secretsPath + "/${dir}";
files = if lib.pathExists dirPath then lib.readDir dirPath else { };
ageFiles = filterAttrs (name: type: type == "regular" && hasSuffix ".age" name) files;
in
lib.map (name: "secrets/${dir}/${name}") (lib.attrNames ageFiles);
# Generate recipient list for a secret based on its location
getRecipients =
secretPath:
let
# Extract directory name from path: "secrets/nix-builder/foo.age" -> "nix-builder"
pathParts = lib.split "/" secretPath;
dirName = lib.elemAt pathParts 2;
in
if dirName == "global" then
# Global secrets: all hosts + admins
allHostKeys ++ globalKeys ++ adminKeys
else if hostKeys ? ${dirName} then
# Host-specific secrets: that host + global keys + admins
hostKeys.${dirName} ++ globalKeys ++ adminKeys
else
# Fallback: just admins
adminKeys;
# Find all secrets across all directories
allSecrets = concatLists (lib.map findSecrets directories);
# Generate the configuration
secretsConfig = lib.listToAttrs (
lib.map (
secretPath:
let
recipients = getRecipients secretPath;
# Remove duplicates and empty keys
uniqueRecipients = unique (lib.filter (k: k != null && k != "") recipients);
in
nameValuePair secretPath {
publicKeys = uniqueRecipients;
}
) allSecrets
);
# Generate wildcard rules for each directory to allow creating new secrets
wildcardRules = lib.listToAttrs (
lib.concatMap (dir: [
# Match with and without .age extension for ragenix compatibility
(nameValuePair "secrets/${dir}/*" {
publicKeys =
let
recipients = getRecipients "secrets/${dir}/dummy.age";
in
unique (lib.filter (k: k != null && k != "") recipients);
})
(nameValuePair "secrets/${dir}/*.age" {
publicKeys =
let
recipients = getRecipients "secrets/${dir}/dummy.age";
in
unique (lib.filter (k: k != null && k != "") recipients);
})
]) (lib.filter (d: d != "admins") directories)
);
in
secretsConfig // wildcardRules

174
secrets/DESIGN.md Normal file
View File

@@ -0,0 +1,174 @@
# Athenix Secrets System Design
## Overview
The Athenix secrets management system integrates ragenix (agenix) with automatic host discovery based on the repository's fleet inventory structure. It provides a seamless workflow for managing encrypted secrets across all systems.
## Architecture
### Auto-Discovery Module (`sw/secrets.nix`)
**Purpose**: Automatically load and configure secrets at system deployment time.
**Features**:
- Discovers `.age` encrypted files from `secrets/` directories
- Loads global secrets from `secrets/global/` on ALL systems
- Loads host-specific secrets from `secrets/{hostname}/` on matching hosts
- Auto-configures decryption keys based on `.pub` files in directories
- Supports custom secret configuration via `default.nix` in each directory
**Key Behaviors**:
- Secrets are decrypted to `/run/agenix/{name}` at boot
- Identity paths include: system SSH keys + global keys + host-specific keys
- Host-specific secrets override global secrets with the same name
### Dynamic Recipients Configuration (`secrets/secrets.nix`)
**Purpose**: Generate ragenix recipient configuration from directory structure.
**Features**:
- Automatically discovers hosts from `secrets/` subdirectories
- Reads age public keys from `.age.pub` files (converted from SSH keys)
- Generates recipient lists based on secret location:
- `secrets/global/*.age` → ALL hosts + admins
- `secrets/{hostname}/*.age` → that host + global keys + admins
- Supports admin keys in `secrets/admins/` for secret editing
**Key Behaviors**:
- No manual recipient list maintenance required
- Adding a new host = create directory + add .pub key + run `update-age-keys.sh`
- Works with ragenix CLI: `ragenix -e`, `ragenix -r`
## Workflow
### Adding a New Host
1. **Capture SSH host key**:
```bash
# From the running system
cat /etc/ssh/ssh_host_ed25519_key.pub > secrets/new-host/ssh_host_ed25519_key.pub
```
2. **Convert to age format**:
```bash
cd secrets/
./update-age-keys.sh
```
3. **Re-key existing secrets** (if needed):
```bash
ragenix -r
```
### Creating a New Secret
1. **Choose location**:
- `secrets/global/` → all systems can decrypt
- `secrets/{hostname}/` → only that host can decrypt
2. **Create/edit secret**:
```bash
ragenix -e secrets/global/my-secret.age
```
3. **Recipients are auto-determined** from `secrets.nix`:
- Global secrets: all host keys + admin keys
- Host-specific: that host + global keys + admin keys
### Cross-Host Secret Management
Any Athenix host can manage secrets for other hosts because:
- All public keys are in the repository (`*.age.pub` files)
- `secrets/secrets.nix` auto-generates recipient lists
- Hosts decrypt using their own private keys (not shared)
Example: From `nix-builder`, create a secret for `usda-dash`:
```bash
ragenix -e secrets/usda-dash/database-password.age
# Encrypted for usda-dash's public key + admins
# usda-dash will decrypt using its private key at /etc/ssh/ssh_host_ed25519_key
```
## Directory Structure
```
secrets/
├── secrets.nix # Auto-generated recipient config
├── update-age-keys.sh # Helper to convert SSH → age keys
├── README.md # User documentation
├── DESIGN.md # This file
├── global/ # Secrets for ALL hosts
│ ├── *.pub # SSH public keys
│ ├── *.age.pub # Age public keys (generated)
│ ├── *.age # Encrypted secrets
│ └── default.nix # Optional: custom secret config
├── {hostname}/ # Host-specific secrets
│ ├── *.pub
│ ├── *.age.pub
│ ├── *.age
│ └── default.nix
└── admins/ # Admin keys for editing
└── *.age.pub
```
## Security Model
1. **Public keys in git**: Safe to commit (only public keys, `.age.pub` and `.pub`)
2. **Private keys on hosts**: Never leave the system (`/etc/ssh/ssh_host_*_key`, `/etc/age/identity.key`)
3. **Encrypted secrets in git**: Safe to commit (`.age` files)
4. **Decrypted secrets**: Only in memory/tmpfs (`/run/agenix/*`)
## Integration Points
### With NixOS Configuration
```nix
# Access decrypted secrets in any NixOS module
config.age.secrets.my-secret.path # => /run/agenix/my-secret
# Example usage
services.myapp.passwordFile = config.age.secrets.database-password.path;
```
### With Inventory System
The system automatically matches `secrets/{hostname}/` to hostnames from `inventory.nix`. No manual configuration needed.
### With External Modules
External user/system modules can reference secrets:
```nix
# In external module
{ config, ... }:
{
programs.git.extraConfig.credential.helper =
"store --file ${config.age.secrets.git-credentials.path}";
}
```
## Advantages
1. **Zero manual recipient management**: Just add directories and keys
2. **Cross-host secret creation**: Any host can manage secrets for others
3. **Automatic host discovery**: Syncs with inventory structure
4. **Flexible permission model**: Global vs host-specific + custom configs
5. **Version controlled**: All public data in git, auditable history
6. **Secure by default**: Private keys never shared, secrets encrypted at rest
## Limitations
1. **Requires age key conversion**: SSH keys must be converted to age format (automated by script)
2. **Bootstrap chicken-egg**: Need initial host key before encrypting secrets (capture from first boot or generate locally)
3. **No secret rotation automation**: Must manually re-key with `ragenix -r`
4. **Git history contains old encrypted versions**: Rotating keys doesn't remove old ciphertexts from history
## Future Enhancements
- Auto-run `update-age-keys.sh` in pre-commit hook
- Integrate with inventory.nix to auto-generate host directories
- Support for multiple identity types per host
- Automated secret rotation scheduling
- Integration with hardware security modules (YubiKey, etc.)

250
secrets/README.md Normal file
View File

@@ -0,0 +1,250 @@
# Secrets Management with Agenix
This directory contains age-encrypted secrets for Athenix hosts. Secrets are automatically loaded based on directory structure.
## Directory Structure
```
secrets/
├── global/ # Secrets installed on ALL systems
│ ├── default.nix # Optional: Custom config for global secrets
│ └── example.age # Decrypted to /run/agenix/example on all hosts
├── nix-builder/ # Secrets only for nix-builder host
│ ├── default.nix # Optional: Custom config for nix-builder secrets
│ └── ssh_host_ed25519_key.age
└── usda-dash/ # Secrets only for usda-dash host
└── ssh_host_ed25519_key.age
```
## How It Works
1. **Global secrets** (`./secrets/global/*.age`) are installed on every system
2. **Host-specific secrets** (`./secrets/{hostname}/*.age`) are only installed on matching hosts
3. Only `.age` encrypted files are loaded; `.pub` public keys are ignored
4. Secrets are decrypted at boot to `/run/agenix/{secret-name}` with mode `0400` and owner `root:root`
5. **Custom configurations** can be defined in `default.nix` files within each directory
## Creating Secrets
### 1. Generate Age Keys
For a new host, generate an age identity:
```bash
# On the target system
mkdir -p /etc/age
age-keygen -o /etc/age/identity.key
chmod 600 /etc/age/identity.key
```
Or use SSH host keys (automatically done by Athenix):
```bash
# Get the age public key from SSH host key
nix shell nixpkgs#ssh-to-age -c sh -c 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
```
### 2. Store Public Keys
Save the public key to `secrets/{hostname}/` for reference:
```bash
# Example for nix-builder
echo "age1..." > secrets/nix-builder/identity.pub
```
Or from SSH host key:
```bash
cat /etc/ssh/ssh_host_ed25519_key.pub > secrets/nix-builder/ssh_host_ed25519_key.pub
```
**Then convert SSH keys to age format:**
```bash
cd secrets/
./update-age-keys.sh
```
This creates `.age.pub` files that `secrets.nix` uses for ragenix recipient configuration.
### 3. Encrypt Secrets
Encrypt a secret for specific hosts:
```bash
# For a single host
age -r age1publickey... -o secrets/nix-builder/my-secret.age <<< "secret value"
# For multiple hosts (recipient list)
age -R recipients.txt -o secrets/global/shared-secret.age < plaintext-file
# Using SSH public keys
age -R secrets/nix-builder/ssh_host_ed25519_key.pub \
-o secrets/nix-builder/ssh_host_key.age < /etc/ssh/ssh_host_ed25519_key
```
### 4. Creating and Editing Secrets
**For new secrets**, use the helper script (automatically determines recipients):
```bash
cd secrets/
# Create a host-specific secret
./create-secret.sh usda-dash/database-url.age <<< "postgresql://..."
# Create a global secret
echo "shared-api-key" | ./create-secret.sh global/api-key.age
# From a file
./create-secret.sh nix-builder/ssh-key.age < ~/.ssh/id_ed25519
```
The script automatically includes the correct recipients:
- **Host-specific**: that host's keys + global keys + admin keys
- **Global**: all host keys + admin keys
**To edit existing secrets**, use `ragenix`:
```bash
# Install ragenix
nix shell github:yaxitech/ragenix
# Edit an existing secret (you must have a decryption key)
ragenix -e secrets/global/existing-secret.age
# Re-key all secrets after adding new hosts
ragenix -r
```
**Why create with `age` first?** Ragenix requires the `.age` file to exist before editing. The `secrets/secrets.nix` configuration auto-discovers recipients from the directory structure, but ragenix doesn't support wildcard patterns for creating new files.
**Recipient management** is automatic:
- **Global secrets** (`secrets/global/*.age`): encrypted for ALL hosts + admins
- **Host secrets** (`secrets/{hostname}/*.age`): encrypted for that host + global keys + admins
- **Admin keys** from `secrets/admins/*.age.pub` allow editing from your workstation
After creating new .age files with `age`, use `ragenix -r` to re-key all secrets with the updated recipient configuration.
To add admin keys for editing secrets:
```bash
# Generate personal age key
age-keygen -o ~/.config/age/personal.key
# Extract public key and add to secrets
grep "public key:" ~/.config/age/personal.key | cut -d: -f2 | tr -d ' ' > secrets/admins/your-name.age.pub
```
## Using Secrets in Configuration
Secrets are automatically loaded. Reference them in your NixOS configuration:
```nix
# Example: Using a secret for a service
services.myservice = {
enable = true;
passwordFile = config.age.secrets.my-password.path; # /run/agenix/my-password
};
# Example: Setting up SSH host key from secret
services.openssh = {
hostKeys = [{
path = config.age.secrets.ssh_host_ed25519_key.path;
type = "ed25519";
}];
};
```
## Custom Secret Configuration
For secrets needing custom permissions, use `athenix.sw.secrets.extraSecrets`:
```nix
# In inventory.nix or host config
athenix.sw.secrets.extraSecrets = {
"nginx-cert" = {
file = ./secrets/custom/cert.age;
mode = "0440";
owner = "nginx";
group = "nginx";
};
};
```
### Using default.nix in Secret Directories
Alternatively, create a `default.nix` file in the secret directory to configure all secrets in that directory:
```nix
# secrets/global/default.nix
{
"example" = {
mode = "0440"; # Custom file mode (default: "0400")
owner = "nginx"; # Custom owner (default: "root")
group = "nginx"; # Custom group (default: "root")
path = "/run/secrets/example"; # Custom path (default: /run/agenix/{name})
};
"api-key" = {
mode = "0400";
owner = "myservice";
group = "myservice";
};
}
```
The `default.nix` file should return an attribute set where:
- **Keys** are secret names (without the `.age` extension)
- **Values** are configuration objects with optional fields:
- `mode` - File permissions (string, e.g., `"0440"`)
- `owner` - File owner (string, e.g., `"nginx"`)
- `group` - File group (string, e.g., `"nginx"`)
- `path` - Custom installation path (string, e.g., `"/custom/path"`)
Secrets not listed in `default.nix` will use default settings.
## Security Best Practices
1. **Never commit unencrypted secrets** - Only `.age` and `.pub` files belong in this directory
2. **Use host-specific secrets** when possible - Limit exposure by using hostname directories
3. **Rotate secrets regularly** - Re-encrypt with new keys periodically
4. **Backup age identity keys** - Store `/etc/age/identity.key` securely offline
5. **Use SSH keys** - Leverage existing SSH host keys for age encryption when possible
6. **Pin to commits** - When using external secrets modules, always use `rev = "commit-hash"`
## Converting SSH Keys to Age Format
```bash
# Convert SSH public key to age public key
nix shell nixpkgs#ssh-to-age -c ssh-to-age < secrets/nix-builder/ssh_host_ed25519_key.pub
# Convert SSH private key to age identity (for editing secrets)
nix shell nixpkgs#ssh-to-age -c ssh-to-age -private-key -i ~/.ssh/id_ed25519
```
## Disabling Automatic Secrets
To disable automatic secret loading:
```nix
# In inventory.nix or host config
athenix.sw.secrets.enable = false;
```
## Troubleshooting
### Secret not found
- Ensure the `.age` file exists in `secrets/global/` or `secrets/{hostname}/`
- Check `hostname` matches directory name: `echo $HOSTNAME` on the target system
- Run `nix flake check` to verify secrets are discovered
### Permission denied
- Verify secret permissions in `/run/agenix/`
- Check if custom permissions are needed (use `extraSecrets`)
- Ensure the service user/group has access to the secret file
### Age decrypt failed
- Verify the host's age identity exists: `ls -l /etc/age/identity.key`
- Check that the secret was encrypted with the host's public key
- Confirm SSH host key hasn't changed (would change derived age key)
## References
- [ragenix GitHub](https://github.com/yaxitech/ragenix)
- [agenix upstream](https://github.com/ryantm/agenix)
- [age encryption tool](https://age-encryption.org/)

View File

@@ -0,0 +1 @@
age14emzyraytqzmre58c452t07rtcj87cwqwmd9z3gj7upugtxk8s3sda5tju

BIN
secrets/core Normal file

Binary file not shown.

121
secrets/create-secret.sh Executable file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bash
set -euo pipefail
# Create a new age-encrypted secret with auto-determined recipients
# Usage: ./create-secret.sh <path> [content]
# path: relative to secrets/ (e.g., "usda-dash/my-secret.age" or "global/shared.age")
# content: stdin if not provided
SECRETS_DIR="$(cd "$(dirname "$0")" && pwd)"
if [ $# -lt 1 ]; then
echo "Usage: $0 <path> [content]" >&2
echo "Examples:" >&2
echo " $0 usda-dash/database-url.age <<< 'postgresql://...'" >&2
echo " $0 global/api-key.age < secret-file.txt" >&2
echo " echo 'secret' | $0 nix-builder/token.age" >&2
exit 1
fi
SECRET_PATH="$1"
shift
# Extract directory from path (e.g., "usda-dash/file.age" -> "usda-dash")
SECRET_DIR="$(dirname "$SECRET_PATH")"
SECRET_FILE="$(basename "$SECRET_PATH")"
# Ensure .age extension
if [[ ! "$SECRET_FILE" =~ \.age$ ]]; then
echo "Error: Secret file must have .age extension" >&2
exit 1
fi
TARGET_FILE="$SECRETS_DIR/$SECRET_PATH"
# Ensure target directory exists
mkdir -p "$(dirname "$TARGET_FILE")"
# Collect recipient keys
RECIPIENTS=()
if [ "$SECRET_DIR" = "global" ]; then
echo "Creating global secret (encrypted for all hosts + admins)..." >&2
# Add all host keys
for host_dir in "$SECRETS_DIR"/*/; do
host_name="$(basename "$host_dir")"
# Skip non-host directories
if [ "$host_name" = "admins" ] || [ "$host_name" = "global" ]; then
continue
fi
# Add all .age.pub files from this host
while IFS= read -r -d '' key_file; do
RECIPIENTS+=("$key_file")
done < <(find "$host_dir" -maxdepth 1 -name "*.age.pub" -print0)
done
# Add global keys
while IFS= read -r -d '' key_file; do
RECIPIENTS+=("$key_file")
done < <(find "$SECRETS_DIR/global" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
else
echo "Creating host-specific secret for $SECRET_DIR..." >&2
# Check if host directory exists
if [ ! -d "$SECRETS_DIR/$SECRET_DIR" ]; then
echo "Error: Host directory $SECRET_DIR does not exist" >&2
echo "Create it first: mkdir -p secrets/$SECRET_DIR" >&2
exit 1
fi
# Add this host's keys
while IFS= read -r -d '' key_file; do
RECIPIENTS+=("$key_file")
done < <(find "$SECRETS_DIR/$SECRET_DIR" -maxdepth 1 -name "*.age.pub" -print0)
# Add global keys (so global hosts can also decrypt)
while IFS= read -r -d '' key_file; do
RECIPIENTS+=("$key_file")
done < <(find "$SECRETS_DIR/global" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
fi
# Add admin keys (for editing from workstations)
if [ -d "$SECRETS_DIR/admins" ]; then
while IFS= read -r -d '' key_file; do
RECIPIENTS+=("$key_file")
done < <(find "$SECRETS_DIR/admins" -maxdepth 1 -name "*.age.pub" -print0 2>/dev/null || true)
fi
# Check if we have any recipients
if [ ${#RECIPIENTS[@]} -eq 0 ]; then
echo "Error: No recipient keys found!" >&2
echo "Run ./update-age-keys.sh first to generate .age.pub files" >&2
exit 1
fi
echo "Found ${#RECIPIENTS[@]} recipient key(s):" >&2
for key in "${RECIPIENTS[@]}"; do
echo " - $(basename "$key")" >&2
done
# Create recipient list file (temporary)
RECIPIENT_LIST=$(mktemp)
trap "rm -f $RECIPIENT_LIST" EXIT
for key in "${RECIPIENTS[@]}"; do
cat "$key" >> "$RECIPIENT_LIST"
done
# Encrypt the secret
if [ $# -gt 0 ]; then
# Content provided as argument
echo "$@" | age -R "$RECIPIENT_LIST" -o "$TARGET_FILE"
else
# Content from stdin
age -R "$RECIPIENT_LIST" -o "$TARGET_FILE"
fi
echo "✓ Created $TARGET_FILE" >&2
echo " Edit with: ragenix -e secrets/$SECRET_PATH" >&2

View File

@@ -0,0 +1 @@
age1udmpqkedupd33gyut85ud3nvppydzeg04kkuneymkvxcjjej244s4v8xjc

View File

@@ -0,0 +1,10 @@
# Host-specific secret configuration for nix-builder
{
# SSH host key should be readable by sshd
ssh_host_ed25519_key = {
mode = "0600";
owner = "root";
group = "root";
path = "/etc/ssh/ssh_host_ed25519_key";
};
}

View File

@@ -0,0 +1 @@
age1u5tczg2sx90n03uuz9h549f4h3h7sq5uehhqpampzs7vj8ew7y6s2mjwz0

176
secrets/secrets.nix Normal file
View File

@@ -0,0 +1,176 @@
# ============================================================================
# Agenix Secret Recipients Configuration (Auto-Generated)
# ============================================================================
# This file automatically discovers hosts and their public keys from the
# secrets/ directory structure and generates recipient configurations.
#
# Directory structure:
# secrets/{hostname}/*.pub -> SSH/age public keys for that host
# secrets/global/*.pub -> Keys accessible to all hosts
#
# Usage:
# ragenix -e secrets/global/example.age # Edit/create secret
# ragenix -r # Re-key all secrets
#
# To add admin keys for editing secrets, create secrets/admins/*.pub files
# with your personal age public keys (generated with: age-keygen)
let
lib = builtins;
# Helper functions not in builtins
filterAttrs =
pred: set:
lib.listToAttrs (
lib.filter (item: pred item.name item.value) (
lib.map (name: {
inherit name;
value = set.${name};
}) (lib.attrNames set)
)
);
concatLists = lists: lib.foldl' (acc: list: acc ++ list) [ ] lists;
unique =
list:
let
go =
acc: remaining:
if remaining == [ ] then
acc
else if lib.elem (lib.head remaining) acc then
go acc (lib.tail remaining)
else
go (acc ++ [ (lib.head remaining) ]) (lib.tail remaining);
in
go [ ] list;
hasSuffix =
suffix: str:
let
lenStr = lib.stringLength str;
lenSuffix = lib.stringLength suffix;
in
lenStr >= lenSuffix && lib.substring (lenStr - lenSuffix) lenSuffix str == suffix;
nameValuePair = name: value: { inherit name value; };
secretsPath = ./secrets;
# Read all directories in secrets/
secretDirs = if lib.pathExists secretsPath then lib.readDir secretsPath else { };
# Filter to only directories (excludes files)
isDirectory = name: type: type == "directory";
directories = lib.filter (name: isDirectory name secretDirs.${name}) (lib.attrNames secretDirs);
# Read public keys from a directory and convert to age format
readHostKeys =
dirName:
let
dirPath = secretsPath + "/${dirName}";
files = if lib.pathExists dirPath then lib.readDir dirPath else { };
# Prefer .age.pub files (pre-converted), fall back to .pub files
agePubFiles = filterAttrs (name: type: type == "regular" && hasSuffix ".age.pub" name) files;
sshPubFiles = filterAttrs (
name: type: type == "regular" && hasSuffix ".pub" name && !(hasSuffix ".age.pub" name)
) files;
# Read age public keys (already in correct format)
ageKeys = lib.map (
name:
let
content = lib.readFile (dirPath + "/${name}");
# Trim whitespace/newlines
trimmed = lib.replaceStrings [ "\n" " " "\r" "\t" ] [ "" "" "" "" ] content;
in
trimmed
) (lib.attrNames agePubFiles);
# For SSH keys, just include them as-is (user needs to convert with ssh-to-age)
# Or they can run the update-age-keys.sh script
sshKeys =
if (lib.length (lib.attrNames sshPubFiles)) > 0 then
lib.trace "Warning: ${dirName} has unconverted SSH keys. Run secrets/update-age-keys.sh" [ ]
else
[ ];
in
lib.filter (k: k != null && k != "") (ageKeys ++ sshKeys);
# Build host key mappings: { hostname = [ "age1..." "age2..." ]; }
hostKeys = lib.listToAttrs (
lib.map (dir: nameValuePair dir (readHostKeys dir)) (
lib.filter (d: d != "global" && d != "admins") directories
)
);
# Global keys that all hosts can use
globalKeys = if lib.elem "global" directories then readHostKeys "global" else [ ];
# Admin keys for editing secrets
adminKeys = if lib.elem "admins" directories then readHostKeys "admins" else [ ];
# All host keys combined
allHostKeys = concatLists (lib.attrValues hostKeys);
# Find all .age files in the secrets directory
findSecrets =
dir:
let
dirPath = secretsPath + "/${dir}";
files = if lib.pathExists dirPath then lib.readDir dirPath else { };
ageFiles = filterAttrs (name: type: type == "regular" && hasSuffix ".age" name) files;
in
lib.map (name: "secrets/${dir}/${name}") (lib.attrNames ageFiles);
# Generate recipient list for a secret based on its location
getRecipients =
secretPath:
let
# Extract directory name from path: "secrets/nix-builder/foo.age" -> "nix-builder"
pathParts = lib.split "/" secretPath;
dirName = lib.elemAt pathParts 2;
in
if dirName == "global" then
# Global secrets: all hosts + admins
allHostKeys ++ globalKeys ++ adminKeys
else if hostKeys ? ${dirName} then
# Host-specific secrets: that host + global keys + admins
hostKeys.${dirName} ++ globalKeys ++ adminKeys
else
# Fallback: just admins
adminKeys;
# Find all secrets across all directories
allSecrets = concatLists (lib.map findSecrets directories);
# Generate the configuration
secretsConfig = lib.listToAttrs (
lib.map (
secretPath:
let
recipients = getRecipients secretPath;
# Remove duplicates and empty keys
uniqueRecipients = unique (lib.filter (k: k != null && k != "") recipients);
in
nameValuePair secretPath {
publicKeys = uniqueRecipients;
}
) allSecrets
);
in
secretsConfig
// {
# Export helper information for debugging
_meta = {
hostKeys = hostKeys;
globalKeys = globalKeys;
adminKeys = adminKeys;
allHostKeys = allHostKeys;
discoveredSecrets = allSecrets;
};
}

36
secrets/update-age-keys.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# ============================================================================
# Update Age Keys from SSH Public Keys
# ============================================================================
# This script converts SSH public keys to age format for use with ragenix.
# Run this after adding new SSH .pub files to create corresponding .age.pub files.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "Converting SSH public keys to age format..."
# Find all .pub files that are SSH keys (not already .age.pub)
find . -name "*.pub" -not -name "*.age.pub" -type f | while read -r pubkey; do
# Check if it's an SSH key
if grep -q "^ssh-" "$pubkey" 2>/dev/null || grep -q "^ecdsa-" "$pubkey" 2>/dev/null; then
age_key=$(nix shell nixpkgs#ssh-to-age -c ssh-to-age < "$pubkey" 2>/dev/null || true)
if [ -n "$age_key" ]; then
# Create .age.pub file with the age key
age_file="${pubkey%.pub}.age.pub"
echo "$age_key" > "$age_file"
echo "✓ Converted: $pubkey -> $age_file"
else
echo "⚠ Skipped: $pubkey (conversion failed)"
fi
fi
done
echo ""
echo "Done! Age public keys have been generated."
echo "You can now use ragenix to manage secrets:"
echo " ragenix -e secrets/global/my-secret.age"
echo " ragenix -r # Re-key all secrets with updated keys"

View File

@@ -0,0 +1,8 @@
# Host-specific secret configuration for usda-dash
{
usda-vision-azure-env = {
mode = "0600";
owner = "root";
group = "root";
};
}

View File

@@ -0,0 +1 @@
age1lr24yvk7rdfh5wkle7h32jpxqxm2e8vk85mc4plv370u2sh4yfmszaaejx

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHI73LOEK2RgfjhZWpryntlLbx0LouHrhQ6v0vZu4Etr root@usda-dash

Binary file not shown.

View File

@@ -11,7 +11,108 @@
... ...
}: }:
lib.mkMerge [ with lib;
let
cfg = config.athenix.sw.builders;
in
{
options.athenix.sw.builders = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable build server configuration.
Includes:
- SSH host keys for common Git servers (factory.uga.edu, github.com)
- Gitea Actions runner support (optional)
- Build tools and dependencies
Recommended for: CI/CD servers, build containers, development infrastructure
'';
example = true;
};
giteaRunner = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable Gitea Actions self-hosted runner.
This runner will connect to a Gitea instance and execute CI/CD workflows.
Requires manual setup of the token file before the service will start.
'';
example = true;
};
url = mkOption {
type = lib.types.str;
description = ''
URL of the Gitea instance to connect to.
This should be the base URL without any path components.
'';
example = "https://git.factory.uga.edu";
};
tokenFile = mkOption {
type = lib.types.path;
default = "/var/lib/gitea-runner-token";
description = ''
Path to file containing Gitea runner registration token.
To generate:
1. Go to your Gitea repository settings
2. Navigate to Actions > Runners
3. Click "Create new Runner"
4. Save the token to this file:
echo "TOKEN=your-token-here" | sudo tee /var/lib/gitea-runner-token > /dev/null
The service will not start until this file exists.
'';
example = "/var/secrets/gitea-runner-token";
};
extraLabels = mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
Additional labels to identify this runner in workflow files.
Use labels to target specific runners for different job types.
'';
example = [
"self-hosted"
"nix"
"x86_64-linux"
];
};
name = mkOption {
type = lib.types.str;
default = "athenix";
description = ''
Unique name for this runner instance.
Shown in Gitea's runner list and logs.
'';
example = "nix-builder-1";
};
};
};
default = { };
description = "Gitea Actions runner configuration.";
};
};
};
default = { };
description = "Build server configuration (CI/CD, Gitea Actions).";
};
config = mkIf cfg.enable (mkMerge [
(import ./programs.nix { (import ./programs.nix {
inherit inherit
config config
@@ -28,4 +129,5 @@ lib.mkMerge [
inputs inputs
; ;
}) })
] ]);
}

View File

@@ -1,8 +1,6 @@
{ {
config, config,
lib, lib,
pkgs,
inputs,
... ...
}: }:
@@ -10,7 +8,7 @@ with lib;
let let
cfg = config.athenix.sw; cfg = config.athenix.sw;
basePackages = with pkgs; [ basePackages = [
# Build-related packages can be added here if needed # Build-related packages can be added here if needed
]; ];
in in

View File

@@ -10,115 +10,79 @@
# Software Module Entry Point # Software Module Entry Point
# ============================================================================ # ============================================================================
# This module manages the software configuration for the system. It provides # This module manages the software configuration for the system. It provides
# options to select the system type ('desktop' or 'kiosk') and handles # enable options for each system type (desktop, headless, builders, etc.)
# the conditional importation of the appropriate sub-modules. # that can be enabled independently or in combination. Each type is a proper
# NixOS submodule with its own enable flag and type-specific options.
with lib; with lib;
let let
cfg = config.athenix.sw; cfg = config.athenix.sw;
# Normalize type to always be a list
swTypes = if isList cfg.type then cfg.type else [ cfg.type ];
# Helper to check if a type is enabled
hasType = type: elem type swTypes;
in in
{ {
imports = [ imports = [
./python.nix ./python.nix
./ghostty.nix ./ghostty.nix
./gc.nix
./updater.nix ./updater.nix
./update-ref.nix ./update-ref.nix
./secrets.nix
./desktop
./headless
./builders
./tablet-kiosk
./stateless-kiosk
inputs.home-manager.nixosModules.home-manager
inputs.agenix.nixosModules.default
inputs.disko.nixosModules.disko
]; ];
options.athenix.sw = { options.athenix.sw = {
enable = mkEnableOption "Standard Workstation Configuration"; enable = mkOption {
type = lib.types.bool;
type = mkOption { default = true;
type = types.oneOf [
(types.enum [
"desktop"
"tablet-kiosk"
"headless"
"stateless-kiosk"
"builders"
])
(types.listOf (
types.enum [
"desktop"
"tablet-kiosk"
"headless"
"stateless-kiosk"
"builders"
]
))
];
default = "desktop";
description = "Type(s) of system configuration. Can be a single type or a list of types to combine multiple configurations.";
};
extraPackages = mkOption {
type = types.listOf types.package;
default = [ ];
description = "Extra packages to install.";
};
excludePackages = mkOption {
type = types.listOf types.package;
default = [ ];
description = "Packages to exclude from the default list.";
};
kioskUrl = mkOption {
type = types.str;
default = "https://ha.factory.uga.edu";
description = "URL to open in Chromium kiosk mode.";
};
# Builders-specific options
builders = mkOption {
type = types.submodule {
options = {
giteaRunner = {
enable = mkEnableOption "Gitea Actions self-hosted runner";
url = mkOption {
type = types.str;
description = "Gitea instance URL for the runner";
};
tokenFile = mkOption {
type = types.path;
default = "/var/lib/gitea-runner-token";
description = '' description = ''
Path to file containing Gitea runner token. Enable standard workstation configuration with base packages.
Generate in Gitea repository settings under Actions > Runners.
The token must have runner registration access. Provides:
- Base CLI tools (htop, git, binutils)
- Shell configuration (Zsh)
- Secret management (agenix)
- Oh My Posh shell theme
This is typically enabled automatically when any sw type is enabled.
''; '';
}; };
extraLabels = mkOption { type = mkOption {
type = types.listOf types.str; type = lib.types.nullOr (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
default = null;
description = "DEPRECATED: Use athenix.sw.<type>.enable instead. Legacy type selection.";
visible = false;
};
extraPackages = mkOption {
type = lib.types.listOf lib.types.package;
default = [ ]; default = [ ];
description = "Extra labels to identify this runner in workflows"; description = ''
Additional system packages to install beyond the defaults.
These packages are added to environment.systemPackages.
'';
example = lib.literalExpression "[ pkgs.vim pkgs.wget pkgs.curl ]";
}; };
name = mkOption { excludePackages = mkOption {
type = types.str; type = lib.types.listOf lib.types.package;
default = "athenix"; default = [ ];
description = "Name of the Gitea runner service"; description = ''
}; Packages to exclude from the default package list.
}; Useful for removing unwanted default packages.
}; '';
}; example = lib.literalExpression "[ pkgs.htop ]";
default = { };
description = "Builder-specific configuration options";
}; };
}; };
config = mkIf cfg.enable (mkMerge [ config = mkIf cfg.enable {
{
# ========== System-Wide Configuration ========== # ========== System-Wide Configuration ==========
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
@@ -135,59 +99,9 @@ in
zsh # Z shell zsh # Z shell
git # Version control git # Version control
oh-my-posh # Shell prompt theme oh-my-posh # Shell prompt theme
age # Simple file encryption tool
age-plugin-fido2-hmac # age FIDO2 support
inputs.agenix.packages.${stdenv.hostPlatform.system}.default # Secret management inputs.agenix.packages.${stdenv.hostPlatform.system}.default # Secret management
]; ];
} };
# ========== Software Profile Imports ==========
(mkIf (hasType "desktop") (
import ./desktop {
inherit
config
lib
pkgs
inputs
;
}
))
(mkIf (hasType "tablet-kiosk") (
import ./tablet-kiosk {
inherit
config
lib
pkgs
inputs
;
}
))
(mkIf (hasType "headless") (
import ./headless {
inherit
config
lib
pkgs
inputs
;
}
))
(mkIf (hasType "stateless-kiosk") (
import ./stateless-kiosk {
inherit
config
lib
pkgs
inputs
;
}
))
(mkIf (hasType "builders") (
import ./builders {
inherit
config
lib
pkgs
inputs
;
}
))
]);
} }

View File

@@ -10,7 +10,41 @@
inputs, inputs,
... ...
}: }:
lib.mkMerge [
with lib;
let
cfg = config.athenix.sw.desktop;
in
{
options.athenix.sw.desktop = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable full desktop environment with KDE Plasma 6.
Includes:
- KDE Plasma 6 desktop with SDDM display manager
- Full graphical software suite (Firefox, Chromium, LibreOffice)
- Printing and scanning support (CUPS)
- Virtualization (libvirt, virt-manager)
- Bluetooth and audio (PipeWire)
- Video conferencing (Zoom, Teams)
Recommended for: Workstations, development machines, user desktops
'';
example = true;
};
};
};
default = { };
description = "Desktop environment configuration (KDE Plasma 6).";
};
config = mkIf cfg.enable (mkMerge [
(import ./programs.nix { (import ./programs.nix {
inherit inherit
config config
@@ -27,4 +61,5 @@ lib.mkMerge [
inputs inputs
; ;
}) })
] ]);
}

View File

@@ -2,7 +2,6 @@
config, config,
lib, lib,
pkgs, pkgs,
inputs,
... ...
}: }:

View File

@@ -1,5 +1,4 @@
{ {
config,
lib, lib,
pkgs, pkgs,
... ...

75
sw/gc.nix Normal file
View File

@@ -0,0 +1,75 @@
{
config,
lib,
...
}:
{
options.athenix = {
system.gc = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable automatic garbage collection of old NixOS generations.
Helps keep disk usage under control on long-running systems.
'';
};
frequency = lib.mkOption {
type = lib.types.str;
default = "weekly";
description = ''
How often to run garbage collection (systemd timer format).
Common values: "daily", "weekly", "monthly"
Advanced: "*-*-* 03:00:00" (daily at 3 AM)
'';
example = "daily";
};
retentionDays = lib.mkOption {
type = lib.types.int;
default = 30;
description = ''
Number of days to keep old system generations before deletion.
Older generations allow rolling back system changes.
Recommended: 30-90 days for workstations, 7-14 for servers.
'';
example = 60;
};
optimise = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to automatically hard-link identical files in the Nix store.
Can save significant disk space but uses CPU during optimization.
'';
};
};
host.buildMethods = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "installer-iso" ];
description = ''
List of allowed build methods for this host (used by installer/artifacts.nix).
Supported methods:
- "installer-iso": Generates an auto-install ISO that installs this configuration to disk.
- "iso": Generates a live ISO (using nixos-generators).
- "ipxe": Generates iPXE netboot artifacts (kernel, initrd, script).
- "lxc": Generates an LXC container tarball.
- "proxmox": Generates a Proxmox VMA archive.
'';
};
};
config = {
# Automatic Garbage Collection
nix.gc = lib.mkIf config.athenix.system.gc.enable {
automatic = true;
dates = config.athenix.system.gc.frequency;
options = "--delete-older-than ${toString config.athenix.system.gc.retentionDays}d";
};
# Optimize storage
nix.optimise.automatic = config.athenix.system.gc.optimise;
};
}

View File

@@ -12,7 +12,11 @@
# It reconstructs the terminfo database from the provided definition and # It reconstructs the terminfo database from the provided definition and
# adds it to the system packages. # adds it to the system packages.
with lib;
let let
cfg = config.athenix.sw;
ghostty-terminfo = pkgs.runCommand "ghostty-terminfo" { } '' ghostty-terminfo = pkgs.runCommand "ghostty-terminfo" { } ''
mkdir -p $out/share/terminfo mkdir -p $out/share/terminfo
cat > ghostty.info <<'EOF' cat > ghostty.info <<'EOF'
@@ -101,5 +105,7 @@ let
''; '';
in in
{ {
config = mkIf cfg.enable {
environment.systemPackages = [ ghostty-terminfo ]; environment.systemPackages = [ ghostty-terminfo ];
};
} }

View File

@@ -11,7 +11,38 @@
... ...
}: }:
lib.mkMerge [ with lib;
let
cfg = config.athenix.sw.headless;
in
{
options.athenix.sw.headless = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable minimal headless server configuration.
Includes:
- SSH server with password authentication
- Minimal CLI tools (tmux, man)
- Systemd-networkd for networking
- No graphical environment
Recommended for: Servers, containers (LXC), WSL, remote systems
'';
example = true;
};
};
};
default = { };
description = "Headless server configuration (SSH, minimal CLI tools).";
};
config = mkIf cfg.enable (mkMerge [
(import ./programs.nix { (import ./programs.nix {
inherit inherit
config config
@@ -28,4 +59,5 @@ lib.mkMerge [
inputs inputs
; ;
}) })
] ]);
}

View File

@@ -2,7 +2,6 @@
config, config,
lib, lib,
pkgs, pkgs,
inputs,
... ...
}: }:

View File

@@ -1,7 +1,4 @@
{ {
config,
lib,
pkgs,
... ...
}: }:

View File

@@ -18,11 +18,28 @@ let
cfg = config.athenix.sw.python; cfg = config.athenix.sw.python;
in in
{ {
options.athenix.sw.python = { options.athenix.sw.python = lib.mkOption {
enable = mkEnableOption "Python development tools (pixi, uv)" // { type = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true; default = true;
description = ''
Enable Python development tools (pixi, uv).
Provides:
- pixi: Fast, cross-platform package manager for Python
- uv: Extremely fast Python package installer and resolver
These tools manage project-based dependencies rather than global
Python packages, avoiding conflicts and improving reproducibility.
'';
}; };
}; };
};
default = { };
description = "Python development environment configuration.";
};
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ environment.systemPackages = [

230
sw/secrets.nix Normal file
View File

@@ -0,0 +1,230 @@
# ============================================================================
# Automatic Secret Management with Agenix
# ============================================================================
# This module automatically loads age-encrypted secrets from ./secrets based on
# the hostname. Secrets are organized by directory:
# - ./secrets/global/ -> Installed on ALL systems
# - ./secrets/{hostname}/ -> Installed only on matching host
#
# Secret files should be .age encrypted files. Public keys (.pub) are ignored.
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.athenix.sw;
secretsPath = ../secrets;
# Get the fleet-assigned hostname (avoids issues with LXC empty hostnames)
hostname = config.athenix.host.name;
# Read all directories in ./secrets
secretDirs = if builtins.pathExists secretsPath then builtins.readDir secretsPath else { };
# Filter to only directories (excludes files)
isDirectory = name: type: type == "directory";
directories = lib.filterAttrs isDirectory secretDirs;
# Read secrets from a specific directory
readSecretsFromDir =
dirName:
let
dirPath = secretsPath + "/${dirName}";
files = builtins.readDir dirPath;
# Check if there's a default.nix with custom secret configurations
hasDefaultNix = files ? "default.nix";
customConfigs = if hasDefaultNix then import (dirPath + "/default.nix") else { };
# Only include .age files (exclude .pub public keys and other files)
secretFiles = lib.filterAttrs (name: type: type == "regular" && lib.hasSuffix ".age" name) files;
in
lib.mapAttrs' (
name: _:
let
# Remove .age extension for the secret name
secretName = lib.removeSuffix ".age" name;
# Get custom config for this secret if defined
customConfig = customConfigs.${secretName} or { };
# Base configuration with file path
baseConfig = {
file = dirPath + "/${name}";
};
in
lib.nameValuePair secretName (baseConfig // customConfig)
) secretFiles;
# Read public keys from a specific directory and map to private key paths
readIdentityPathsFromDir =
dirName:
let
dirPath = secretsPath + "/${dirName}";
files = if builtins.pathExists dirPath then builtins.readDir dirPath else { };
# Only include .pub public key files
pubKeyFiles = lib.filterAttrs (name: type: type == "regular" && lib.hasSuffix ".pub" name) files;
in
lib.mapAttrsToList (
name: _:
let
# Map public key filename to expected private key location
baseName = lib.removeSuffix ".pub" name;
filePath = dirPath + "/${name}";
fileContent = builtins.readFile filePath;
# Check if it's an SSH key by looking at the content
isSSHKey = lib.hasPrefix "ssh-" fileContent || lib.hasPrefix "ecdsa-" fileContent;
in
if lib.hasPrefix "ssh_host_" name then
# SSH host keys: ssh_host_ed25519_key.pub -> /etc/ssh/ssh_host_ed25519_key
"/etc/ssh/${baseName}"
else if name == "identity.pub" then
# Standard age identity: identity.pub -> /etc/age/identity.key
"/etc/age/identity.key"
else if isSSHKey then
# Other SSH keys (user keys, etc.): hunter_halloran_key.pub -> /etc/ssh/hunter_halloran_key
"/etc/ssh/${baseName}"
else
# Generic age keys: key.pub -> /etc/age/key
"/etc/age/${baseName}"
) pubKeyFiles;
# Determine which secrets apply to this host
applicableSecrets =
let
# Global secrets apply to all hosts
globalSecrets = if directories ? "global" then readSecretsFromDir "global" else { };
# Host-specific secrets
hostSecrets = if directories ? ${hostname} then readSecretsFromDir hostname else { };
in
globalSecrets // hostSecrets; # Host-specific secrets override global if same name
# Determine which identity paths (private keys) to use for decryption
identityPaths =
let
# Global identity paths (keys in global/ that all hosts can use)
globalPaths = if directories ? "global" then readIdentityPathsFromDir "global" else [ ];
# Host-specific identity paths
hostPaths = if directories ? ${hostname} then readIdentityPathsFromDir hostname else [ ];
# Default paths that NixOS/agenix use
defaultPaths = [
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/age/identity.key"
];
# Combine all paths and remove duplicates
allPaths = lib.unique (defaultPaths ++ globalPaths ++ hostPaths);
in
allPaths;
in
{
options.athenix.sw.secrets = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Enable automatic secret management using agenix.
Secrets are loaded from ./secrets based on directory structure:
- ./secrets/global/ -> All systems
- ./secrets/{hostname}/ -> Specific host only
Only .age encrypted files are loaded; .pub files are ignored.
'';
};
extraSecrets = mkOption {
type = types.attrsOf (
types.submodule {
options = {
file = mkOption {
type = types.path;
description = "Path to the encrypted secret file";
};
mode = mkOption {
type = types.str;
default = "0400";
description = "Permissions mode for the decrypted secret";
};
owner = mkOption {
type = types.str;
default = "root";
description = "Owner of the decrypted secret file";
};
group = mkOption {
type = types.str;
default = "root";
description = "Group of the decrypted secret file";
};
};
}
);
default = { };
description = ''
Additional secrets to define manually, beyond the auto-discovered ones.
Use this for secrets that need custom permissions or are stored elsewhere.
'';
example = lib.literalExpression ''
{
"my-secret" = {
file = ./secrets/custom/secret.age;
mode = "0440";
owner = "nginx";
group = "nginx";
};
}
'';
};
};
config = mkIf (cfg.enable && cfg.secrets.enable) {
# Auto-discovered secrets with default permissions
age.secrets = applicableSecrets // cfg.secrets.extraSecrets;
# Generate age identity files from SSH host keys at boot
# This is needed because age can't reliably use OpenSSH private keys directly
# Must run before agenix tries to decrypt secrets
system.activationScripts.convertSshToAge = {
deps = [
"users"
"groups"
];
text = ''
mkdir -p /etc/age
if [ -f /etc/ssh/ssh_host_ed25519_key ]; then
${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i /etc/ssh/ssh_host_ed25519_key > /etc/age/ssh_host_ed25519.age || true
chmod 600 /etc/age/ssh_host_ed25519.age 2>/dev/null || true
fi
if [ -f /etc/ssh/ssh_host_rsa_key ]; then
${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i /etc/ssh/ssh_host_rsa_key > /etc/age/ssh_host_rsa.age 2>/dev/null || true
chmod 600 /etc/age/ssh_host_rsa.age 2>/dev/null || true
fi
'';
};
# Add the converted age keys to identity paths (in addition to auto-discovered ones)
age.identityPaths = identityPaths ++ [
"/etc/age/ssh_host_ed25519.age"
"/etc/age/ssh_host_rsa.age"
];
# Optional: Add assertion to warn if no secrets found
warnings =
let
hasSecrets = (builtins.length (builtins.attrNames applicableSecrets)) > 0;
in
lib.optional (
!hasSecrets
) "No age-encrypted secrets found in ./secrets/global/ or ./secrets/${hostname}/";
};
}

View File

@@ -7,7 +7,52 @@
inputs, inputs,
... ...
}: }:
lib.mkMerge [
with lib;
let
cfg = config.athenix.sw.stateless-kiosk;
in
{
options.athenix.sw.stateless-kiosk = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable stateless kiosk mode for diskless PXE boot systems.
Includes:
- Sway (Wayland compositor)
- Chromium in fullscreen kiosk mode
- MAC address-based URL routing
- Network-only boot (no local storage)
- Auto-start browser on boot
Recommended for: Assembly line stations, diskless kiosks, PXE boot displays
'';
example = true;
};
kioskUrl = mkOption {
type = lib.types.str;
default = "https://ha.factory.uga.edu";
description = ''
Default URL to display in the kiosk browser.
Note: For stateless-kiosk, MAC address-based routing may override this.
See sw/stateless-kiosk/mac-hostmap.nix for MAC-to-URL mappings.
'';
example = "https://homeassistant.lan:8123/lovelace/dashboard";
};
};
};
default = { };
description = "Stateless kiosk configuration (PXE boot, Sway, MAC-based routing).";
};
config = mkIf cfg.enable (mkMerge [
(import ./kiosk-browser.nix { (import ./kiosk-browser.nix {
inherit inherit
config config
@@ -40,4 +85,5 @@ lib.mkMerge [
inputs inputs
; ;
}) })
] ]);
}

View File

@@ -1,14 +1,13 @@
# 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, pkgs,
inputs,
... ...
}: }:
let let
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; macCaseBuilder = inputs.self.lib.macCaseBuilder;
macCases = macCaseBuilder { macCases = macCaseBuilder {
varName = "STATION"; varName = "STATION";
}; };

View File

@@ -1,28 +0,0 @@
# Shared MAC address to station mapping and case builder for stateless-kiosk modules
{ lib }:
let
hostmap = {
"00:e0:4c:46:0b:32" = "1";
"00:e0:4c:46:07:26" = "2";
"00:e0:4c:46:05:94" = "3";
"00:e0:4c:46:07:11" = "4";
"00:e0:4c:46:08:02" = "5";
"00:e0:4c:46:08:5c" = "6";
};
# macCaseBuilder: builds a shell case statement from a hostmap
# 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,
}:
lib.concatStringsSep "\n" (
lib.mapAttrsToList (mac: val: " ${mac}) ${varName}=${prefix}${val} ;;") attrset
);
in
{
inherit hostmap macCaseBuilder;
}

View File

@@ -26,5 +26,5 @@
}; };
# Disable systemd-networkd and systemd-hostnamed # Disable systemd-networkd and systemd-hostnamed
systemd.network.enable = false; systemd.network.enable = lib.mkForce false;
} }

View File

@@ -1,7 +1,4 @@
{ {
config,
lib,
pkgs,
... ...
}: }:
{ {

View File

@@ -1,11 +1,10 @@
{ {
config,
lib,
pkgs, pkgs,
inputs,
... ...
}: }:
let let
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder; macCaseBuilder = inputs.self.lib.macCaseBuilder;
shellCases = macCaseBuilder { shellCases = macCaseBuilder {
varName = "NEW_HOST"; varName = "NEW_HOST";
prefix = "nix-station"; prefix = "nix-station";

View File

@@ -5,7 +5,51 @@
inputs, inputs,
... ...
}: }:
lib.mkMerge [
with lib;
let
cfg = config.athenix.sw.tablet-kiosk;
in
{
options.athenix.sw.tablet-kiosk = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable tablet kiosk mode with touch-optimized interface.
Includes:
- Phosh mobile desktop environment
- Chromium in fullscreen kiosk mode
- On-screen keyboard (Squeekboard)
- Auto-login and auto-start browser
- Touch gesture support
- Optimized for Surface Pro tablets
Recommended for: Surface tablets, touchscreen kiosks, interactive displays
'';
example = true;
};
kioskUrl = mkOption {
type = lib.types.str;
default = "https://ha.factory.uga.edu";
description = ''
URL to display in the kiosk browser on startup.
The browser will automatically navigate to this URL in fullscreen mode.
'';
example = "https://dashboard.example.com";
};
};
};
default = { };
description = "Tablet kiosk configuration (Phosh, touch interface).";
};
config = mkIf cfg.enable (mkMerge [
(import ./programs.nix { (import ./programs.nix {
inherit inherit
config config
@@ -30,4 +74,5 @@ lib.mkMerge [
inputs inputs
; ;
}) })
] ]);
}

View File

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

View File

@@ -1,7 +1,6 @@
{ {
pkgs, pkgs,
config, config,
osConfig,
lib, lib,
... ...
}: }:
@@ -33,6 +32,18 @@ in
programs.zsh = { programs.zsh = {
enable = true; enable = true;
initContent = ''
bindkey '^[[H' beginning-of-line # Home key
bindkey '^[[F' end-of-line # End key
bindkey '^[[3~' delete-char # Delete key
bindkey '^[[1~' beginning-of-line # Alternative Home key
bindkey '^[[4~' end-of-line # Alternative End key
bindkey '^[[2~' overwrite-mode # Insert key
bindkey '^[[5~' up-line-or-history # Page Up
bindkey '^[[6~' down-line-or-history # Page Down
bindkey -e
'';
# Plugins # Plugins
historySubstringSearch = { historySubstringSearch = {
enable = true; enable = true;

View File

@@ -1,5 +1,17 @@
{ pkgs, ... }:
{ {
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.athenix.sw;
in
{
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
python3 python3
git git
@@ -16,7 +28,7 @@
update-ref [-R PATH|--athenix-repo=PATH] [-b BRANCH|--athenix-branch=BRANCH] update-ref [-R PATH|--athenix-repo=PATH] [-b BRANCH|--athenix-branch=BRANCH]
[-m "msg"|--message "msg"] [-m "msg"|--message "msg"]
[-p[=false] [remote[=URL]]|--push[=false] [remote[=URL]]] [-p[=false] [remote[=URL]]|--push[=false] [remote[=URL]]]
[--make-local|-l] [--make-remote|-r] [--make-local|-l] [--make-remote|-r] [--ssh]
user=<username> | system=<device-type>:<hostkey> user=<username> | system=<device-type>:<hostkey>
EOF EOF
exit 2 exit 2
@@ -60,16 +72,39 @@
extract_existing_fetch_url() { extract_existing_fetch_url() {
# args: mode file username key # args: mode file username key
python3 - "$1" "$2" "$3" "$4" <<'PY' python3 - "$1" "$2" "$3" "$4" "$5"<<'PY'
import sys, re, pathlib import sys, re, pathlib
mode, file, username, key = sys.argv[1:5] mode, file, username, key, use_ssh = sys.argv[1:5]
t = pathlib.Path(file).read_text() t = pathlib.Path(file).read_text()
def url_from_block(block: str) -> str: def url_from_block(block: str) -> str:
if not block: if not block:
return "" return ""
m = re.search(r'url\s*=\s*"([^"]+)"\s*;', block) m = re.search(r'url\s*=\s*"([^"]+)"\s*;', block)
return m.group(1) if m else "" url = m.group(1) if m else ""
if use_ssh = "true":
return url
# Already https
if url.startswith("https://"):
return url
# ssh://git@host/org/repo.git
m = re.match(r"ssh://(?:.+?)@([^/]+)/(.+)", url)
if m:
host, path = m.groups()
return f"https://{host}/{path}"
# git@host:org/repo.git
m = re.match(r"(?:.+?)@([^:]+):(.+)", url)
if m:
host, path = m.groups()
return f"https://{host}/{path}"
# If you gave me something cursed
raise ValueError(f"Unrecognized SSH git URL format: {url}")
if mode == "user": if mode == "user":
m = re.search(r'(?s)\n\s*' + re.escape(username) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t) m = re.search(r'(?s)\n\s*' + re.escape(username) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t)
@@ -154,6 +189,7 @@
--make-local|-l) MODE_FORCE="local"; shift ;; --make-local|-l) MODE_FORCE="local"; shift ;;
--make-remote|-r) MODE_FORCE="remote"; shift ;; --make-remote|-r) MODE_FORCE="remote"; shift ;;
--ssh) USE_SSH="true"; shift ;;
-h|--help) usage ;; -h|--help) usage ;;
*) die "Unknown argument: $1" ;; *) die "Unknown argument: $1" ;;
esac esac
@@ -214,7 +250,7 @@
EXISTING_URL="" EXISTING_URL=""
ENTRY_EXISTS=0 ENTRY_EXISTS=0
if [ "$MODE" = "user" ]; then if [ "$MODE" = "user" ]; then
EXISTING_URL="$(extract_existing_fetch_url user "$FILE" "$USERNAME" "")" EXISTING_URL="$(extract_existing_fetch_url user "$FILE" "$USERNAME" "" "false")"
[ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true [ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true
else else
FULL="$(derive_full_hostname "$DEVTYPE" "$HOSTKEY")" FULL="$(derive_full_hostname "$DEVTYPE" "$HOSTKEY")"
@@ -484,4 +520,5 @@
printf " rev = %s\n" "$CUR_REV" >&2 printf " rev = %s\n" "$CUR_REV" >&2
'') '')
]; ];
};
} }

View File

@@ -9,27 +9,47 @@ with lib;
{ {
options.athenix.sw.remoteBuild = lib.mkOption { options.athenix.sw.remoteBuild = lib.mkOption {
type = types.submodule { type = lib.types.submodule {
options = { options = {
hosts = mkOption { hosts = mkOption {
type = types.listOf types.str; type = lib.types.listOf lib.types.str;
default = [ "engr-ugaif@192.168.11.133 x86_64-linux" ]; default = [ "engr-ugaif@192.168.11.133 x86_64-linux" ];
description = "List of remote build hosts for system rebuilding."; description = ''
List of remote build hosts for system rebuilding.
Format: "user@hostname architecture"
Each host must have SSH access and nix-daemon available.
Useful for offloading builds from low-power devices (tablets, laptops)
to more powerful build servers.
'';
example = lib.literalExpression ''
[
"builder@nix-builder x86_64-linux"
"user@192.168.1.100 aarch64-linux"
]'';
}; };
enable = mkOption { enable = mkOption {
type = types.bool; type = lib.types.bool;
default = false; default = false;
description = "Whether to enable remote build for 'update-system' command."; description = ''
Whether to enable remote builds for the 'update-system' command.
When enabled, 'update-system' will use the configured remote hosts
to build the new system configuration instead of building locally.
Automatically enabled for tablet-kiosk systems.
'';
}; };
}; };
}; };
default = { }; default = { };
description = "Remote build configuration"; description = "Remote build configuration for system updates.";
}; };
config = { config = {
athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.type == "tablet-kiosk"); athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.tablet-kiosk.enable);
environment.systemPackages = [ environment.systemPackages = [
(pkgs.writeShellScriptBin "update-system" '' (pkgs.writeShellScriptBin "update-system" ''
@@ -201,8 +221,10 @@ 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";
path = with pkgs; [ path = with pkgs; [
git git
openssh
nixos-rebuild nixos-rebuild
nix nix
coreutils
]; ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";

View File

@@ -1,4 +1,4 @@
{ inputs, ... }: { ... }:
# ============================================================================ # ============================================================================
# User Configuration # User Configuration
@@ -15,7 +15,6 @@
# nixos-systems configuration (nixpkgs, home-manager, etc.). # nixos-systems configuration (nixpkgs, home-manager, etc.).
{ {
config,
lib, lib,
pkgs, pkgs,
osConfig ? null, # Only available in home-manager context osConfig ? null, # Only available in home-manager context
@@ -60,7 +59,7 @@
fd fd
bat bat
] ]
++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox; ++ lib.optional (osConfig.athenix.sw.desktop.enable or false) firefox;
# Conditionally add packages based on system type # Conditionally add packages based on system type
# ========== Programs ========== # ========== Programs ==========

View File

@@ -1,4 +1,4 @@
{ pkgs, ... }: { ... }:
{ {
# ============================================================================ # ============================================================================
# User Definitions # User Definitions
@@ -13,8 +13,9 @@
# #
# External User Configuration: # External User Configuration:
# Users can specify external configuration modules via the 'external' attribute: # Users can specify external configuration modules via the 'external' attribute:
# external = builtins.fetchGit { url = "..."; rev = "..."; }; # external = { url = "..."; rev = "..."; submodules? = false; };
# external = /path/to/local/config; # external = /path/to/local/config;
# external = builtins.fetchGit { ... }; # legacy, still supported
# #
# External repositories should contain: # External repositories should contain:
# - user.nix (required): Defines athenix.users.<name> options AND home-manager config # - user.nix (required): Defines athenix.users.<name> options AND home-manager config
@@ -26,7 +27,7 @@
# #
# User options can be set in users.nix OR in the external module's user.nix. # User options can be set in users.nix OR in the external module's user.nix.
# External module options take precedence over users.nix defaults. # External module options take precedence over users.nix defaults.
athenix.users = { config.athenix.users = {
root = { root = {
isNormalUser = false; isNormalUser = false;
hashedPassword = "!"; hashedPassword = "!";
@@ -47,9 +48,10 @@
enable = true; # Default user, enabled everywhere enable = true; # Default user, enabled everywhere
}; };
hdh20267 = { hdh20267 = {
external = builtins.fetchGit { external = {
url = "https://git.factory.uga.edu/hdh20267/hdh20267-nix"; url = "https://git.factory.uga.edu/hdh20267/hdh20267-nix";
rev = "c538e0c0510045b58264627bb897fc499dc7c490"; rev = "dbdf65c7bd59e646719f724a3acd2330e0c922ec";
# submodules = false; # optional, defaults to false
}; };
}; };
sv22900 = { sv22900 = {
@@ -58,7 +60,7 @@
"networkmanager" "networkmanager"
"wheel" "wheel"
]; ];
shell = pkgs.zsh; shell = "zsh";
# enable = false by default, set to true per-system # enable = false by default, set to true per-system
}; };
}; };