320 Commits

Author SHA1 Message Date
UGA Innovation Factory 9fa470299a update external config for usda-dash 2025-12-19 09:42:15 -05:00
UGA Innovation Factory 2ad7d3f311 update external config for usda-dash 2025-12-18 21:18:38 -05:00
UGA Innovation Factory d9a8373bad update external config for usda-dash 2025-12-18 21:17:22 -05:00
UGA Innovation Factory 9168430a29 update external config for usda-dash 2025-12-18 21:16:09 -05:00
UGA Innovation Factory ae316529f8 update external config for usda-dash 2025-12-18 21:14:03 -05:00
UGA Innovation Factory 7d1c9ac173 update external config for usda-dash 2025-12-18 21:12:06 -05:00
UGA Innovation Factory 98c9f0d57b update external config for usda-dash 2025-12-18 21:07:34 -05:00
UGA Innovation Factory 7d121bd331 update external config for usda-dash 2025-12-18 21:01:36 -05:00
UGA Innovation Factory 8c32462cc0 update external config for usda-dash 2025-12-18 20:52:54 -05:00
UGA Innovation Factory 986b3ef5b3 update external config for usda-dash 2025-12-18 20:47:33 -05:00
UGA Innovation Factory f66603c3c4 update external config for usda-dash 2025-12-18 20:43:05 -05:00
UGA Innovation Factory f3d204c6ea update external config for usda-dash 2025-12-18 19:56:18 -05:00
UGA Innovation Factory f9e1c32cf7 update external config for usda-dash 2025-12-18 19:51:25 -05:00
UGA Innovation Factory 9343784871 update external config for usda-dash 2025-12-18 19:41:46 -05:00
UGA Innovation Factory a7ff2b3bf9 update external config for usda-dash 2025-12-18 19:32:54 -05:00
UGA Innovation Factory c0e3ca09f9 update external config for usda-dash 2025-12-18 19:25:52 -05:00
UGA Innovation Factory cfc3ebf903 update external config for usda-dash 2025-12-18 19:23:15 -05:00
UGA Innovation Factory cee87825a7 update external config for usda-dash 2025-12-18 19:22:09 -05:00
UGA Innovation Factory e8805d046f update external config for usda-dash 2025-12-18 19:19:25 -05:00
UGA Innovation Factory 9bbcc8479c update external config for usda-dash 2025-12-18 19:08:03 -05:00
UGA Innovation Factory 56ca4bcccf update external config for usda-dash 2025-12-18 18:50:25 -05:00
UGA Innovation Factory a7362a9ce9 update external config for usda-dash 2025-12-18 18:47:19 -05:00
UGA Innovation Factory ca6a7920e5 update external config for usda-dash 2025-12-18 18:41:32 -05:00
UGA Innovation Factory 4af1549b41 update external config for usda-dash 2025-12-18 18:38:14 -05:00
hdh20267 08802d3147 Merge pull request 'add keys to allow builder to work with ssh git refs' (#16) from builder-ci into main
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m27s
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 9s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 12s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/16
2025-12-18 23:16:54 +00:00
UGA Innovation Factory 319111b469 add keys to allow builder to work with ssh git refs 2025-12-18 18:16:35 -05:00
hdh20267 44e8a897ca Merge pull request 'update external config for usda-dash' (#15) from usda-docker into main
CI / Format Check (push) Successful in 7s
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 11s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/15
2025-12-18 21:56:38 +00:00
UGA Innovation Factory 6e755a1db0 update external config for usda-dash 2025-12-18 16:56:06 -05:00
hdh20267 1d70023307 Merge pull request 'update external config for usda-dash' (#14) from usda-docker into main
CI / Format Check (push) Successful in 2s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/14
2025-12-18 21:45:12 +00:00
UGA Innovation Factory 4819082ed3 update external config for usda-dash 2025-12-18 16:44:48 -05:00
hdh20267 22564e96d4 Merge pull request 'update external config for usda-dash' (#13) from usda-docker into main
CI / Format Check (push) Successful in 1s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/13
2025-12-18 21:40:55 +00:00
UGA Innovation Factory ee03958c1e update external config for usda-dash 2025-12-18 16:40:19 -05:00
hdh20267 d4079dae32 Merge pull request 'update external config for usda-dash' (#8) from usda-docker into main
CI / Format Check (push) Successful in 2s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/8
2025-12-18 21:15:22 +00:00
UGA Innovation Factory c3211f7446 update external config for usda-dash 2025-12-18 16:15:07 -05:00
hdh20267 65e0840c31 Merge pull request 'update external docker module' (#7) from usda-docker into main
CI / Format Check (push) Successful in 2s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/7
2025-12-18 21:09:00 +00:00
UGA Innovation Factory 1f55099138 update external docker module 2025-12-18 16:08:45 -05:00
hdh20267 1dc81173e5 Merge pull request 'update external usda-dash-config repo' (#6) from usda-docker into main
CI / Format Check (push) Successful in 2s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/6
2025-12-18 20:59:28 +00:00
UGA Innovation Factory 0e898c316d update external usda-dash-config repo 2025-12-18 15:58:59 -05:00
hdh20267 6b6f449d9f Merge pull request 'use submodules for usda-dash' (#5) from usda-docker into main
CI / Format Check (push) Successful in 2s
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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/5
2025-12-18 20:47:18 +00:00
UGA Innovation Factory 71ea6258e9 use submodules for usda-dash 2025-12-18 15:46:48 -05:00
hdh20267 fd7a4b1156 Merge pull request 'use ssh for usda-dash git fetch' (#4) from usda-docker into main
CI / Format Check (push) Successful in 1s
CI / Evaluate Key Configurations (nix-desktop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-laptop1) (push) Has been cancelled
CI / Evaluate Key Configurations (nix-builder) (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 / Flake Check (push) Has been cancelled
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/4
2025-12-18 20:38:33 +00:00
UGA Innovation Factory ac4192f764 use ssh for usda-dash git fetch 2025-12-18 15:37:45 -05:00
hdh20267 795d179788 Merge pull request 'docker compose runner for usda-dash' (#3) from usda-docker into main
CI / Format Check (push) Successful in 1s
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 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 16s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 9s
Reviewed-on: http://git.factory.uga.edu/UGA-Innovation-Factory/athenix/pulls/3
2025-12-18 20:23:37 +00:00
UGA Innovation Factory 2acb63e220 docker compose runner for usda-dash 2025-12-18 15:22:32 -05:00
hdh20267 2b7fcf5f21 Merge pull request 'run on pr review, not pr' (#2) from ci-update into main
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m17s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 12s
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 17s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 10s
Reviewed-on: http://192.168.10.171/UGA-Innovation-Factory/athenix/pulls/2
2025-12-18 19:19:46 +00:00
UGA Innovation Factory 7b8dcc7621 run on pr review, not pr 2025-12-18 19:17:11 +00:00
hdh20267 7941135cb3 Merge pull request 'ci-update' (#1) from ci-update into main
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m15s
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 16s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 10s
Reviewed-on: http://192.168.10.171/UGA-Innovation-Factory/athenix/pulls/1
2025-12-18 19:10:31 +00:00
UGA Innovation Factory 5eeaa48f09 Revert "change requirements"
CI / Format Check (pull_request) Successful in 2s
CI / Evaluate Key Configurations (nix-desktop1) (pull_request) Has been skipped
CI / Evaluate Key Configurations (nix-laptop1) (pull_request) Has been skipped
CI / Evaluate Artifacts (lxc-nix-builder) (pull_request) Has been skipped
CI / Flake Check (pull_request) Has been skipped
CI / Evaluate Key Configurations (nix-builder) (pull_request) Has been skipped
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (pull_request) Has been skipped
This reverts commit 75359032b1.
2025-12-18 13:57:40 -05:00
UGA Innovation Factory 75359032b1 change requirements
CI / Format Check (pull_request) Has been skipped
CI / Flake Check (pull_request) Has been skipped
CI / Evaluate Key Configurations (nix-builder) (pull_request) Has been skipped
CI / Evaluate Key Configurations (nix-desktop1) (pull_request) Has been skipped
CI / Evaluate Key Configurations (nix-laptop1) (pull_request) Has been skipped
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (pull_request) Has been skipped
CI / Evaluate Artifacts (lxc-nix-builder) (pull_request) Has been skipped
2025-12-18 13:54:23 -05:00
UGA Innovation Factory af81786d52 don't auto-check for all prs 2025-12-18 13:52:43 -05:00
UGA Innovation Factory 90688ec5f1 just evaluate instead of building artificats for CI
CI / Flake Check (push) Successful in 1m15s
CI / Format Check (push) Successful in 1s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 9s
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 11s
2025-12-18 12:55:27 -05:00
UGA Innovation Factory 0ba0e854cf migrate CI to gitea
CI / Flake Check (push) Successful in 1m33s
CI / Format Check (push) Successful in 2s
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 / Build Artifacts (installer-iso-nix-laptop1) (push) Successful in 3m33s
CI / Build Artifacts (lxc-nix-builder) (push) Successful in 57s
2025-12-18 12:35:35 -05:00
UGA Innovation Factory 811eb1bd4b finish rename 2025-12-18 12:24:26 -05:00
UGA Innovation Factory 6ab5f20946 Rename project to 'Athenix' 2025-12-18 12:07:25 -05:00
Hunter Halloran 85ffa56d12 nix flake update 2025-12-17 20:26:03 -05:00
Hunter 6ee51cd747 Merge pull request #10 from UGA-Innovation-Factory/documentation-patch
documentation update
2025-12-17 19:41:44 -05:00
Hunter Halloran ab3b4a017d documentation update 2025-12-17 19:39:02 -05:00
Hunter fb6ac75f3c Merge pull request #7 from UGA-Innovation-Factory/user-module-redesign
overhaul external user modules
2025-12-17 16:42:41 -05:00
Hunter 89865a0a06 Merge pull request #9 from UGA-Innovation-Factory/copilot/sub-pr-7-again
Fix user attribute merging to preserve all fields from users.nix
2025-12-17 16:39:09 -05:00
Hunter Halloran 59937416c1 nix fmt 2025-12-17 16:32:12 -05:00
Hunter Halloran a4600fa66a Merge branch 'copilot/sub-pr-7-again' of github.com:UGA-Innovation-Factory/nixos-systems into copilot/sub-pr-7-again 2025-12-17 16:28:22 -05:00
copilot-swe-agent[bot] f51ef4ce01 Fix user merging logic to preserve all fields from users.nix
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 16:27:18 -05:00
copilot-swe-agent[bot] fdda1795db Initial plan 2025-12-17 16:27:18 -05:00
Hunter Halloran 99ed790ea5 fix merge conflict 2025-12-17 16:24:31 -05:00
copilot-swe-agent[bot] 5084ece692 Fix user merging logic to preserve all fields from users.nix
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 21:23:19 +00:00
copilot-swe-agent[bot] 316c642dc7 Initial plan 2025-12-17 21:20:41 +00:00
Hunter Halloran 94824dd6bf Merge remote-tracking branch 'origin/user-module-redesign' into user-module-redesign 2025-12-17 16:19:14 -05:00
Hunter Halloran b811bc4ea3 resolve more comments 2025-12-17 16:18:38 -05:00
Hunter f417e2106c Update hosts/user-config.nix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 16:16:40 -05:00
Hunter 4533cd2383 Update docs/USER_CONFIGURATION.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 16:16:19 -05:00
Hunter 8ec3bd14ac Update hosts/user-config.nix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 16:14:56 -05:00
Hunter e58e54fbd6 Update templates/user/nixos.nix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 16:14:26 -05:00
Hunter 6691342f52 Update templates/user/user.nix
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 16:11:35 -05:00
Hunter Halloran 7588273af1 resolve comment https://github.com/UGA-Innovation-Factory/nixos-systems/pull/7#discussion_r2628646450 2025-12-17 16:10:17 -05:00
Hunter Halloran 5c574b447c nix fmt 2025-12-17 15:47:55 -05:00
Hunter Halloran 36dcf08e32 fix to use the new format 2025-12-17 15:45:51 -05:00
Hunter Halloran b9d7af509f overhaul external user modules 2025-12-17 15:39:23 -05:00
Hunter Halloran b1ae664ab0 nix fmt 2025-12-17 11:44:10 -05:00
Hunter Halloran 35b4572b42 update ci 2025-12-17 11:42:17 -05:00
Hunter Halloran 36550aafd5 gh runner cleanup 2025-12-17 11:22:01 -05:00
Hunter Halloran 35cbfceb81 gh runner cleanup 2025-12-17 11:20:40 -05:00
Hunter Halloran e7cdc324f8 gh runner cleanup 2025-12-17 11:17:21 -05:00
Hunter Halloran c01328d826 gh runner cleanup 2025-12-17 11:15:06 -05:00
Hunter Halloran 1d2430c2c4 gh runner cleanup 2025-12-17 11:14:07 -05:00
Hunter Halloran c2664a11a5 gh runner cleanup 2025-12-17 11:12:34 -05:00
Hunter Halloran da7f939e1c gh runner cleanup 2025-12-17 11:11:41 -05:00
Hunter Halloran 147ba0e9e1 gh runner cleanup 2025-12-17 11:10:22 -05:00
Hunter Halloran 2b560ab733 gh runner cleanup 2025-12-17 11:07:46 -05:00
Hunter Halloran 5e07500423 gh runner cleanup 2025-12-17 11:06:51 -05:00
Hunter Halloran 36e122ecb6 fix gh runner perms 2025-12-17 11:04:22 -05:00
Hunter Halloran 49e312b67c still work if gh token doesn't exist yet 2025-12-17 10:59:13 -05:00
Hunter Halloran f77bd93b6a fix user nixos.nix import conditions 2025-12-17 10:56:34 -05:00
Hunter Halloran a0e09336ad builder config with options 2025-12-17 10:43:43 -05:00
copilot-swe-agent[bot] 8cdedae4db Add GitHub Actions runner configuration to nix-builder
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 15:19:05 +00:00
copilot-swe-agent[bot] a00b8acad9 Configure CI to run on self-hosted nix-builder runner
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 15:14:34 +00:00
copilot-swe-agent[bot] 9b3e1de6e5 Fix nix fmt command to include **/*.nix argument
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 15:09:22 +00:00
copilot-swe-agent[bot] ddefebf27e Add explicit GITHUB_TOKEN permissions for security
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 14:58:57 +00:00
copilot-swe-agent[bot] 46ff1cdc47 Fix Nix config consistency in format-check job
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 14:57:44 +00:00
copilot-swe-agent[bot] 482f5e945a Document CI workflow in DEVELOPMENT.md
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 14:56:11 +00:00
copilot-swe-agent[bot] 6ee3906cb5 Add GitHub Actions CI workflow for flake checking
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 14:55:14 +00:00
copilot-swe-agent[bot] 26e6b8e9d8 Initial plan 2025-12-17 14:51:58 +00:00
Hunter Halloran aee73dd1c3 update copilot instructions 2025-12-17 09:38:52 -05:00
Hunter Halloran 2c266cb10a fix ugaif.forUser 2025-12-17 09:37:12 -05:00
Hunter Halloran 22b1fc549b documentation overhaul 2025-12-17 09:25:51 -05:00
Hunter Halloran c7c8101b81 remove deprecated uses with external flakes and more consistent ugaif namespace usage 2025-12-17 09:15:11 -05:00
Hunter b43e457edc Merge pull request #5 from UGA-Innovation-Factory/copilot/setup-copilot-instructions 2025-12-17 08:29:09 -05:00
copilot-swe-agent[bot] 2223df69da Add GitHub Copilot instructions
Co-authored-by: Jyumpp <11142390+Jyumpp@users.noreply.github.com>
2025-12-17 13:23:22 +00:00
copilot-swe-agent[bot] c014206e27 Initial plan 2025-12-17 13:19:55 +00:00
Hunter Halloran d1a24e5fb3 hdh20267 user to external module 2025-12-17 00:03:18 -05:00
UGA Innovation Factory e6e96bd315 update usda-dash commit 2025-12-16 17:50:05 -05:00
UGA Innovation Factory 8e50f3eaac a way to deploy proxmox lxcs 2025-12-16 17:30:33 -05:00
UGA Innovation Factory 4c8f0d39de change what sw modules export 2025-12-16 16:48:21 -05:00
UGA Innovation Factory 5f716eae72 readme formatting 2025-12-16 16:20:42 -05:00
UGA Innovation Factory 3f82f00d54 much better readme 2025-12-16 16:18:19 -05:00
UGA Innovation Factory d04fc7c1a4 git on path 2025-12-16 16:10:49 -05:00
UGA Innovation Factory 7e7e0f9676 add templates for external configs 2025-12-16 16:09:08 -05:00
UGA Innovation Factory e03199df28 fix when overrides for device types gets merged 2025-12-16 14:52:00 -05:00
UGA Innovation Factory 769e1b0fed run nix fmt 2025-12-16 14:36:52 -05:00
UGA Innovation Factory adb98c4136 change how enabled users are handled 2025-12-16 14:35:02 -05:00
UGA Innovation Factory a43dfea615 update inline docs and make home-manager module exports 2025-12-16 14:16:53 -05:00
UGA Innovation Factory 870f5601b0 respect nvim user config option 2025-12-16 13:47:50 -05:00
UGA Innovation Factory 69b63479d6 add sw types as modules 2025-12-15 17:21:55 -05:00
UGA Innovation Factory d8ff54ab51 make 'use ugaif sw' default to true, but settable 2025-12-15 17:13:16 -05:00
UGA Innovation Factory 69762129d4 make more modular and do some refactoring 2025-12-15 17:07:31 -05:00
UGA Innovation Factory 140648cd8f run nix fmt 2025-12-15 15:57:56 -05:00
UGA Innovation Factory 19737c2adb refactor module to ugaif and readme update 2025-12-15 15:32:40 -05:00
UGA Innovation Factory 5ba8dce77b working on the stateless kiosk, dynamic hostnames work now! 2025-12-12 16:46:31 -05:00
UGA Innovation Factory d583fac582 working on the stateless kiosk, dynamic hostnames not yet working 2025-12-12 16:12:24 -05:00
UGA Innovation Factory c5b2caf553 alireza added 2025-12-11 18:00:22 -05:00
UGA Innovation Factory be17181af3 alireza added 2025-12-11 17:57:32 -05:00
UGA Innovation Factory ff07f9a99c alireza added 2025-12-11 17:56:45 -05:00
UGA Innovation Factory 378a8aa7b7 allow wsl users to be changed with options 2025-12-11 17:47:37 -05:00
UGA Innovation Factory 11a9fcd038 add wsl profiles 2025-12-11 17:42:17 -05:00
UGA Innovation Factory 9f7198065b How to install nix 2025-12-11 16:45:25 -05:00
UGA Innovation Factory 5c0cb52647 show how to build isos locally and remotely 2025-12-11 16:43:07 -05:00
Hunter Halloran 932462505f updater in own file 2025-12-11 14:05:06 -05:00
Hunter Halloran a4b049a56f no timeout for service 2025-12-11 14:01:37 -05:00
Hunter Halloran 044a990478 Revert "make update logs verbose"
This reverts commit 267289cf61.
2025-12-11 13:58:15 -05:00
Hunter Halloran 267289cf61 make update logs verbose 2025-12-11 13:56:40 -05:00
Hunter Halloran 11861bfd59 show logs in the updater 2025-12-11 13:55:42 -05:00
Hunter Halloran b7080dcb45 polkit service work 2025-12-11 13:49:32 -05:00
Hunter Halloran 1bc4df63c5 don't ask for password for update, it's not needed 2025-12-11 13:44:35 -05:00
Hunter Halloran 112db5e481 polkit typo 2025-12-11 13:18:02 -05:00
Hunter Halloran bb4c52cf64 polkit typo 2025-12-11 13:03:37 -05:00
Hunter Halloran cad6d2196f show logs with journald for update-system, but also close journal after finished 2025-12-11 12:38:14 -05:00
Hunter Halloran 996a78e410 show logs with journald for update-system 2025-12-11 12:16:46 -05:00
Hunter Halloran 372c612f22 switch update-system to systemd service 2025-12-11 12:11:23 -05:00
Hunter Halloran 7e94b029bb and all the right checks and working 2025-12-10 18:12:32 -05:00
Hunter Halloran 3d91ed5bc1 I can spell I promise 2025-12-10 18:02:21 -05:00
Hunter Halloran d9900234ce updated flake lock 2025-12-10 18:00:46 -05:00
Hunter Halloran a350d57acf fixed flake 2025-12-10 17:59:35 -05:00
Hunter Halloran 04159c6b8f enabled package ragenix 2025-12-10 17:58:25 -05:00
UGA Innovation Factory 6e472d3a90 move artifacts into installer dir 2025-12-10 14:55:10 -05:00
UGA Innovation Factory f81de019a7 auto-install should work offline now 2025-12-10 14:47:55 -05:00
UGA Innovation Factory d1d0b44ea2 inline docs 2025-12-10 14:46:58 -05:00
UGA Innovation Factory 6e91b7e6d0 support for ipxe boot ephemeral systems 2025-12-10 14:42:22 -05:00
UGA Innovation Factory a97848f2ee update documentation readme 2025-12-10 14:07:59 -05:00
UGA Innovation Factory 74551af08a update readme for how to build artifacts 2025-12-10 13:38:50 -05:00
UGA Innovation Factory 27dad325fe iso and lxc generation artifacts 2025-12-10 13:29:23 -05:00
UGA Innovation Factory 3f2efc0b61 readme updates 2025-12-10 10:51:39 -05:00
UGA Innovation Factory 25926cd2f8 change kisokUrl from inventory.nix 2025-12-10 10:47:32 -05:00
UGA Innovation Factory d74b1eeb9b enable python by default 2025-12-10 10:37:16 -05:00
UGA Innovation Factory 698bcdf617 enable python by default 2025-12-10 10:34:58 -05:00
UGA Innovation Factory 2d27ec13e7 rename kiosk to tablet-kiosk 2025-12-10 10:31:09 -05:00
UGA Innovation Factory 5055e24c0a unify lxc and systems config 2025-12-10 10:23:28 -05:00
UGA Innovation Factory 14202a8233 formatter and lxc configuration 2025-12-10 14:52:34 +00:00
Hunter d4835360f5 make kioskUrl a settable option 2025-12-10 09:20:34 -05:00
Hunter 183914b194 home manager flake fixes 2025-12-09 20:00:11 -05:00
Hunter 0ddeee3143 update flake lock 2025-12-09 19:38:35 -05:00
Hunter d921c9bbad add ghostty terminfo 2025-12-09 19:35:52 -05:00
Hunter 84781b1a71 all the documentation 2025-12-09 19:24:13 -05:00
Hunter 416a67b4ea root description 2025-12-09 18:49:28 -05:00
Hunter 4b1e1c392c osConfig naming issue 2025-12-09 18:47:41 -05:00
Hunter 87d3ce6d06 Merge pull request #2 from UGA-Innovation-Factory/refactor
enable system flake additions
2025-12-09 18:44:07 -05:00
Hunter 6738e053ba enable system flake additions 2025-12-09 18:43:05 -05:00
Hunter 75ca1b98e2 Merge pull request #1 from UGA-Innovation-Factory/refactor
Refactor Into Sane Files
2025-12-09 18:24:00 -05:00
Hunter 93f55a74b0 add hdh20267 to both laptops 2025-12-09 18:15:40 -05:00
Hunter 132e4f9a9d root user description fix 2025-12-09 18:13:09 -05:00
Hunter 445f5a940c config option used wrong 2025-12-09 17:45:22 -05:00
Hunter d7132e4129 missing definition 2025-12-09 17:42:54 -05:00
Hunter 785564e69e full refactor 2025-12-09 17:38:41 -05:00
UGA Innovation Factory 1f5feb1b42 no more mobile user 2025-12-09 15:40:35 +00:00
UGA Innovation Factory d05ceec560 force gsettings input-sources 2025-12-09 15:17:48 +00:00
UGA Innovation Factory 8d072ca6c4 force gsettings input-sources 2025-12-09 15:10:12 +00:00
UGA Innovation Factory 74722dd678 add mobile user for tablet 2025-12-09 14:56:54 +00:00
UGA Innovation Factory c491647402 add mobile user for tablet 2025-12-09 14:55:49 +00:00
UGA Innovation Factory b13ce16ec2 set chassis as handset 2025-12-09 00:13:02 +00:00
UGA Innovation Factory f4afbd9bf2 set chassis as tablet 2025-12-08 23:59:23 +00:00
UGA Innovation Factory 15d6a39f1b start phosh as docked 2025-12-08 23:55:43 +00:00
UGA Innovation Factory 7fee95a445 start phosh as docked 2025-12-08 23:48:45 +00:00
UGA Innovation Factory 77b456508b system renamed to stdenv.hostPlatform.system 2025-12-08 23:18:22 +00:00
UGA Innovation Factory 27459256a8 show battery percentage on tablets 2025-12-08 23:15:49 +00:00
UGA Innovation Factory 9c015c8d8c show battery percentage on tablets 2025-12-08 23:13:54 +00:00
UGA Innovation Factory 2f558a0c80 gsettings 2025-12-08 23:04:36 +00:00
UGA Innovation Factory 0752ab6878 idle-delay 0 2025-12-08 22:35:22 +00:00
UGA Innovation Factory b161a33cf7 disable lock screen 2025-12-08 22:04:41 +00:00
UGA Innovation Factory be9e8b2a21 disable lock screen 2025-12-08 22:01:52 +00:00
UGA Innovation Factory 3ad9b93100 ignore power button 2025-12-08 21:54:41 +00:00
UGA Innovation Factory 0b3e750b2f ignore power button 2025-12-08 21:51:26 +00:00
UGA Innovation Factory 1a23a9dbd4 work on fixing wake-from-sleep on tablets 2025-12-08 21:34:26 +00:00
UGA Innovation Factory f9568239f3 add 3rd tablet 2025-12-08 20:53:29 +00:00
UGA Innovation Factory 9d41e1c80d add 2nd tablet 2025-12-08 20:42:02 +00:00
UGA Innovation Factory 8c9b7844e3 force osk with user service 2025-12-08 20:30:44 +00:00
UGA Innovation Factory 03dc0f0ffb force osk with user service 2025-12-08 20:28:03 +00:00
UGA Innovation Factory 088b679085 force osk with user service 2025-12-08 20:27:16 +00:00
UGA Innovation Factory 39776b04bf force osk with user service 2025-12-08 20:20:57 +00:00
UGA Innovation Factory 6b9469b990 force osk with user service 2025-12-08 20:12:40 +00:00
UGA Innovation Factory d2f23076ea force osk with user service 2025-12-08 20:00:55 +00:00
UGA Innovation Factory 31f1094a22 dconf env 2025-12-08 19:58:31 +00:00
UGA Innovation Factory 87c68e9c07 dconf env 2025-12-08 19:56:28 +00:00
UGA Innovation Factory b966bfd7f5 dconf env 2025-12-08 19:54:14 +00:00
UGA Innovation Factory d0da7c7832 change chrome defaults 2025-12-08 19:50:18 +00:00
UGA Innovation Factory ada798645b camera and force osk 2025-12-08 19:47:31 +00:00
UGA Innovation Factory b292405ba6 gpt halucinated a package name 2025-12-08 19:38:02 +00:00
UGA Innovation Factory cedce7fb7c try to make camera work 2025-12-08 19:36:58 +00:00
UGA Innovation Factory e6eb4a9653 add ibus 2025-12-08 19:21:22 +00:00
UGA Innovation Factory 0a24d667b1 add ibus 2025-12-08 19:19:59 +00:00
UGA Innovation Factory b86329b020 disable surface keyboard we don't have 2025-12-08 18:24:16 +00:00
UGA Innovation Factory 1b4d59060c disable surface keyboard we don't have 2025-12-08 18:18:07 +00:00
UGA Innovation Factory 95c5364a69 disable surface keyboard we don't have 2025-12-08 18:15:26 +00:00
UGA Innovation Factory c2acf27b2b keep some inputs 2025-12-08 18:06:19 +00:00
UGA Innovation Factory f1b8d64566 try to force non-existent keyboards to not report 2025-12-08 18:02:49 +00:00
UGA Innovation Factory ba2b8dfff9 udev to say keyboards aren't hardware 2025-12-08 17:56:21 +00:00
UGA Innovation Factory 3071aca30d udev to say keyboards aren't hardware 2025-12-08 17:47:59 +00:00
UGA Innovation Factory 7dd5232890 force gsettings on-screen-keyboard true 2025-12-08 17:38:39 +00:00
UGA Innovation Factory ade3e61d1b xdg data dirs mkForce 2025-12-08 17:14:25 +00:00
UGA Innovation Factory 138736be3c xdg data dirs mkForce 2025-12-08 17:08:48 +00:00
UGA Innovation Factory a9994194c2 xdg data dirs mkForce 2025-12-08 17:06:47 +00:00
UGA Innovation Factory 9136259310 xdg data dirs mkForce 2025-12-08 17:06:21 +00:00
UGA Innovation Factory 1ea1e12ac5 xdg data dirs mkForce 2025-12-08 17:03:46 +00:00
UGA Innovation Factory c4dff8ff7a dconf to gsettings XDG dir 2025-12-08 17:01:43 +00:00
UGA Innovation Factory 46bbbe1655 displaymanager typo 2025-12-08 16:52:02 +00:00
UGA Innovation Factory 677d5dec2a osk gsettings trials 2025-12-08 16:51:11 +00:00
UGA Innovation Factory 2551843726 osk gsettings trials 2025-12-08 16:42:29 +00:00
UGA Innovation Factory f0d0d3372f osk gsettings trials 2025-12-08 16:36:07 +00:00
UGA Innovation Factory bf8fdd1c05 osk testing 2025-12-08 16:32:21 +00:00
UGA Innovation Factory 3cf655c54c start chrome and try keyboard 2025-12-05 22:26:20 +00:00
UGA Innovation Factory ff4d443aee squeekboard for phosh 2025-12-05 22:17:52 +00:00
UGA Innovation Factory 661611e655 phosh passcode 2025-12-05 22:12:14 +00:00
UGA Innovation Factory 94a32ef6a8 phosh scale 2025-12-05 22:06:55 +00:00
UGA Innovation Factory f7fc1fb636 phosh scale 2025-12-05 22:04:43 +00:00
UGA Innovation Factory 30126ef044 try phosh desktopmanager 2025-12-05 22:03:27 +00:00
UGA Innovation Factory 13c51ec331 back to cage and chrome extension keyboard 2025-12-05 21:28:29 +00:00
UGA Innovation Factory b395a76964 wayfire keyboard configs 2025-12-05 21:16:26 +00:00
UGA Innovation Factory 881b8f6f54 wayfire config 2025-12-05 21:10:49 +00:00
UGA Innovation Factory 5f3e9b296e wayfire config 2025-12-05 21:10:23 +00:00
UGA Innovation Factory 8d7a07ca27 wayfire compositor 2025-12-05 21:05:44 +00:00
UGA Innovation Factory eb5370d2aa squeekboard 2025-12-05 20:56:20 +00:00
UGA Innovation Factory 7a9e762790 squeekboard 2025-12-05 20:53:49 +00:00
UGA Innovation Factory 5a5e2a486a change virtual keyboard 2025-12-05 20:49:24 +00:00
UGA Innovation Factory 3e114f71a1 use greetd 2025-12-05 20:42:39 +00:00
UGA Innovation Factory acbdb32012 path for dbus 2025-12-05 20:38:43 +00:00
UGA Innovation Factory d13d6b7edc dbus daemon 2025-12-05 20:34:47 +00:00
UGA Innovation Factory 1db81a44c5 dbus daemon 2025-12-05 20:32:09 +00:00
UGA Innovation Factory 45c178cfc8 dbus daemon 2025-12-05 20:31:05 +00:00
UGA Innovation Factory 3aa1b27222 sway compositor instead of cage 2025-12-05 20:28:17 +00:00
UGA Innovation Factory 30453ec771 sway compositor instead of cage 2025-12-05 20:26:03 +00:00
UGA Innovation Factory 2ae4d0ef53 sway compositor instead of cage 2025-12-05 20:21:44 +00:00
UGA Innovation Factory 5fc3d3488b new virtual keyboard plugin 2025-12-05 19:57:03 +00:00
UGA Innovation Factory de73b918b5 new virtual keyboard plugin 2025-12-05 19:50:31 +00:00
UGA Innovation Factory e68bbcc158 remove extra semicolon 2025-12-05 19:30:56 +00:00
UGA Innovation Factory 0e7c382a92 iotvr wpa2 2nd try 2025-12-05 19:09:31 +00:00
UGA Innovation Factory ed8fdaa555 iotvr wpa2 2025-12-05 19:07:51 +00:00
UGA Innovation Factory 770ec87aa3 no iotvr network 2025-12-05 19:04:06 +00:00
UGA Innovation Factory 434d267b78 add quotes to hashed wifi password 2025-12-05 18:38:41 +00:00
Hunter Halloran 7bc4b8b6d1 wifi password attempt 2025-12-05 13:35:24 -05:00
UGA Innovation Factory a3a377b220 mmcblk0 for tablets 2025-12-05 18:27:13 +00:00
UGA Innovation Factory 1273fd6245 no networkmanager for kiosk tablet 2025-12-05 18:16:34 +00:00
UGA Innovation Factory eecdb4d04a no networkmanager for kiosk tablet 2025-12-05 18:12:32 +00:00
UGA Innovation Factory 0f26d6c924 wifi setup 2025-12-05 18:10:26 +00:00
Hunter Halloran 7ea7cb5138 kiosk mode for tablets 2025-12-05 12:59:41 -05:00
Hunter Halloran b73141a5e6 ghostty 2025-12-05 12:32:10 -05:00
Hunter Halloran 0ded8bcf2b ghostty 2025-12-05 12:31:00 -05:00
Hunter Halloran f864752d26 ghostty 2025-12-05 12:30:17 -05:00
Hunter Halloran 8bcae84e6f ghostty 2025-12-05 12:28:16 -05:00
Hunter Halloran a596bbbc0e gesture on chrome 2025-12-05 12:03:11 -05:00
Hunter Halloran 8c0a2f3683 remove net.nix 2025-12-05 11:36:23 -05:00
Hunter 4abc548c83 pin old kernel for tablets 2025-12-04 19:59:50 -05:00
Hunter a2332a5048 rust patch 2025-12-04 17:12:13 -05:00
Hunter 0e7c4817d6 no rust 2025-12-04 17:04:53 -05:00
UGA Innovation Factory 77187480d4 flake fixes 2025-12-04 21:50:33 +00:00
Hunter 3edbad93c2 surface go support 2025-12-04 16:43:34 -05:00
Hunter ac6167adb2 nevermind on qt wayland 2025-12-04 16:28:46 -05:00
Hunter 817f0b83b5 qt6 wayland 2025-12-04 16:26:52 -05:00
Hunter d9347e6a67 qt6 wayland 2025-12-04 16:18:01 -05:00
Hunter 78f0bdd2cf add nix-ld 2025-12-04 16:07:34 -05:00
Hunter 16e300de5a packaging fix 2025-12-04 16:02:44 -05:00
Hunter 01e2093424 packaging fix 2025-12-04 16:00:59 -05:00
Hunter 1c4d8a0314 packaging fix 2025-12-04 15:59:27 -05:00
Hunter a6a04360dd add python services 2025-12-04 15:57:36 -05:00
Hunter c7bcefc03e add python services 2025-12-04 15:56:34 -05:00
Hunter c1bbe28b1f add back packages 2025-12-04 13:07:39 -05:00
Hunter 60d52232f3 less packages 2025-12-04 13:04:47 -05:00
Hunter ccc245f945 ok I removed packages in general temporarily 2025-12-04 12:35:13 -05:00
Hunter 3200458431 oops infinite recursion 2025-12-04 12:23:08 -05:00
Hunter 291c453a5e try a different list subtration 2025-12-04 12:19:37 -05:00
Hunter 109a1c8d85 remove default software for desktop build (temporarily for build) 2025-12-04 12:06:13 -05:00
Hunter 6cf00ccf6f change default swap size settings 2025-12-04 11:41:07 -05:00
Hunter 7bd10019c5 desktop addition 2025-12-04 10:28:05 -05:00
Hunter 9daaecac5f 2nd laptop 2025-12-04 09:52:49 -05:00
Hunter 82319f983e lid switch change 2025-12-03 20:11:13 -05:00
Hunter 0421bb5504 power profiles 2025-12-03 20:09:55 -05:00
Hunter 7be40af2c4 kde renaming issues 2025-12-03 20:07:08 -05:00
Hunter 9c23337039 sensors are stupid 2025-12-03 20:05:40 -05:00
Hunter 4d9a93dc2d ok hwmon is different 2025-12-03 20:04:00 -05:00
Hunter 80a122528f sensors fix maybe\? 2025-12-03 20:03:06 -05:00
Hunter 6552390643 some sane defaults 2025-12-03 20:01:48 -05:00
Hunter 274226dcbb fonts.fonts to fonts.package after name change 2025-12-03 19:56:49 -05:00
Hunter b8f881fdbc add office tools 2025-12-03 19:45:24 -05:00
Hunter 8e29328af8 add nerdfont 2025-12-03 19:33:46 -05:00
Hunter 5b874b959f add nerdfont 2025-12-03 19:32:22 -05:00
Hunter 1e0f20df4e new flake lock 2025-12-03 19:19:55 -05:00
Hunter 39f2b60eea remove lockfile 2025-12-03 19:18:25 -05:00
Hunter 008512e6b0 remove unneeded networking file 2025-12-03 19:16:50 -05:00
Hunter ab470d1695 nix cleanup unused modules 2025-12-03 19:15:10 -05:00
Hunter 6fbfbb8612 try to fix suspend 2025-12-03 19:05:54 -05:00
Hunter 0c913ec3be refactor hosts 2025-12-03 18:57:48 -05:00
Hunter afa4e3b020 make boot non world accessable 2025-12-03 18:27:14 -05:00
Hunter db11b402d1 disko esp size fix 2025-12-03 18:15:36 -05:00
Hunter 1f374d9581 NixOS systems config for laptop 2025-12-03 18:06:10 -05:00
98 changed files with 2633 additions and 7081 deletions
+3 -44
View File
@@ -26,23 +26,18 @@ jobs:
format-check:
name: Format Check
runs-on: [self-hosted, nix-builder]
timeout-minutes: 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check formatting
timeout-minutes: 3
run: |
set -euo pipefail
echo "Checking code formatting..."
output=$(nix fmt **/*.nix 2>&1)
if [ -n "$output" ]; then
nix fmt **/*.nix
if ! git diff --quiet; then
echo "::error::Code is not formatted. Please run 'nix fmt **/*.nix' locally."
echo "$output"
git diff
exit 1
fi
echo "All files are properly formatted"
eval-configs:
name: Evaluate Key Configurations
@@ -84,39 +79,3 @@ jobs:
echo "Evaluating artifact ${{ matrix.artifact }}"
nix eval .#${{ matrix.artifact }}.drvPath \
--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
+5 -20
View File
@@ -26,9 +26,8 @@ This is a **NixOS system configuration repository** that uses:
- **`flake.nix`**: Entry point - inputs and outputs only
- **`inventory.nix`**: Fleet definitions - host configurations
- **`users.nix`**: User account definitions
- **`hw/`**: Hardware type modules (desktop, laptop, surface, lxc, wsl, etc.)
- **`fleet/`**: Fleet generation logic and common system configuration
- **`sw/`**: Software configurations by system type
- **`hosts/`**: Host generation logic and hardware types
- **`sw/`**: Software configurations organized by system type
- **`installer/`**: Build artifact generation (ISO, LXC, etc.)
- **`templates/`**: Templates for external configurations
@@ -45,12 +44,9 @@ All Innovation Factory-specific options MUST use the `athenix` namespace:
### Host Options (`athenix.host.*`)
```nix
athenix.host = {
filesystem.device = "/dev/nvme0n1"; # Boot disk (default: null)
filesystem.swapSize = "32G"; # Swap size (default: null)
buildMethods = [ "installer-iso" ]; # Artifact types (defined in sw/gc.nix)
useHostPrefix = true; # Hostname prefix behavior
wsl.user = "username"; # WSL default user
};
filesystem.device = "/dev/sda"; # Boot disk
filesystem.swapSize = "32G"; # Swap size
buildMethods = [ "iso" ]; # Artifact types
useHostPrefix = true; # Hostname prefix behavior
wsl.user = "username"; # WSL default user
};
@@ -117,17 +113,6 @@ athenix.forUser = "username"; # Convenience: enable user + set WSL us
3. System modules: Provide `default.nix` that accepts `{ inputs, ... }`
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
### What NOT to Do
-34
View File
@@ -1,34 +0,0 @@
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
View File
@@ -1,18 +0,0 @@
{
"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"
}
}
}
+149 -208
View File
@@ -1,159 +1,156 @@
# Athenix - UGA Innovation Factory NixOS Configuration
# UGA Innovation Factory - Athenix
[![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)
Declarative NixOS configuration management for the Innovation Factory's fleet of workstations, laptops, tablets, and containers using Nix flakes.
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.
## Quick Navigation
## Documentation
- **[docs/INVENTORY.md](docs/INVENTORY.md)** - Define and configure hosts
- **[docs/NAMESPACE.md](docs/NAMESPACE.md)** - All `athenix.*` options reference
- **[docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md)** - User accounts and dotfiles
- **[docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md)** - External system and user configurations
- **[docs/BUILDING.md](docs/BUILDING.md)** - Build ISOs, containers, and artifacts
- **[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)** - Development workflow and testing
- **[Quick Start](#quick-start)** - Get started in 5 minutes
- **[docs/INVENTORY.md](docs/INVENTORY.md)** - Configure hosts and fleet inventory
- **[docs/NAMESPACE.md](docs/NAMESPACE.md)** - Configuration options reference (`athenix.*`)
- **[docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md)** - User account management
- **[docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md)** - External configuration modules
- **[docs/BUILDING.md](docs/BUILDING.md)** - Build ISOs and container images
- **[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)** - Development and testing workflow
## Getting Started
## Quick Start
### For End Users
Update your system:
Update your system to the latest configuration:
```bash
update-system
```
This automatically rebuilds your system with the latest configuration from the repository.
This command automatically fetches the latest configuration, rebuilds your system, and uses remote builders on Surface tablets to speed up builds.
**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
Make configuration changes:
```bash
# Edit inventory
# 1. Make changes to configuration files
vim inventory.nix
# Validate changes
# 2. Test configuration
nix flake check
# Format code
# 3. Format code
nix fmt
# Commit and push
git add . && git commit -m "Your message" && git push
# 4. Commit and push
git add .
git commit -m "Description of changes"
git push
```
Users automatically get changes when they run `update-system`.
Users can now run `update-system` to get the changes.
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for detailed development workflow.**
## Repository Structure
```
flake.nix # Flake entry point (inputs + outputs)
inventory.nix # Fleet inventory and host definitions
users.nix # User account definitions
flake.lock # Locked dependency versions
hw/ # Hardware type modules (exportable as nixosModules)
├── default.nix # Auto-exports all variant types
├── nix-desktop.nix # Desktop workstations
├── nix-laptop.nix # Laptop systems
├── nix-surface.nix # Surface Pro tablets
├── nix-lxc.nix # LXC containers
├── nix-wsl.nix # WSL instances
├── nix-zima.nix # ZimaBoard systems
── nix-ephemeral.nix # Diskless/netboot systems
fleet/ # Fleet generation and common configuration
├── default.nix # Processes inventory.nix to generate all hosts
├── common.nix # Common NixOS configuration (all hosts)
├── boot.nix # Boot and filesystem configuration
└── user-config.nix # User account and home-manager integration
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
nixos-systems/
├── flake.nix # Flake entry point
├── inventory.nix # Fleet inventory - Define hosts here
├── users.nix # User accounts - Define users here
├── hosts/ # Host generation logic
│ ├── types/ # Hardware types (desktop, laptop, surface, lxc, wsl, ephemeral)
│ └── ...
├── sw/ # Software configurations by system type
├── desktop/ # Full desktop environment
│ ├── tablet-kiosk/ # Surface kiosk mode
│ ├── stateless-kiosk/# Diskless PXE kiosks
│ ├── headless/ # Servers and containers
│ └── ...
├── installer/ # ISO and container builds
── templates/ # Templates for external configs
│ ├── system/ # System configuration template
│ └── user/ # User configuration template
├── docs/ # Documentation
│ ├── INVENTORY.md # Host configuration guide
│ ├── NAMESPACE.md # Option reference
│ ├── BUILDING.md # Building artifacts
│ └── DEVELOPMENT.md # Development guide
└── assets/ # Assets (Plymouth theme, etc.)
```
## Configuration Overview
All Innovation Factory-specific options use the `athenix` namespace to avoid conflicts with NixOS options.
All Innovation Factory options use the `athenix.*` namespace. See **[docs/NAMESPACE.md](docs/NAMESPACE.md)** for complete reference.
### Common Options
**Quick examples:**
```nix
# Host filesystem and hardware
athenix.host = {
filesystem.device = "/dev/sda";
filesystem.swapSize = "32G";
buildMethods = [ "installer-iso" ];
useHostPrefix = true;
};
# Host configuration
athenix.host.filesystem.device = "/dev/nvme0n1";
athenix.host.filesystem.swapSize = "64G";
# System type and packages
athenix.sw = {
type = "desktop"; # desktop, tablet-kiosk, stateless-kiosk, headless, builders
extraPackages = with pkgs; [ vim docker ];
};
# Software configuration
athenix.sw.type = "desktop"; # or "headless", "tablet-kiosk"
athenix.sw.extraPackages = with pkgs; [ vim docker ];
# User management
athenix.users.myuser.enable = true;
athenix.forUser = "myuser"; # Convenience shortcut
```
See [docs/NAMESPACE.md](docs/NAMESPACE.md) for complete option reference.
## Prerequisites
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
### 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
Edit `inventory.nix`:
```nix
# Simple: Create 5 identical laptops
# Simple: Create 5 laptops
nix-laptop = {
devices = 5;
devices = 5; # Creates nix-laptop1 through nix-laptop5
};
# With custom configuration per device
# With configuration
nix-surface = {
devices = {
"1".athenix.sw.kioskUrl = "https://dashboard1.example.com";
@@ -161,163 +158,107 @@ nix-surface = {
};
};
# With common overrides
# With overrides for all devices
nix-desktop = {
devices = 3;
overrides = {
athenix.users.student.enable = true;
athenix.sw.extraPackages = with pkgs; [ vim ];
};
};
```
**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.**
**See [docs/INVENTORY.md](docs/INVENTORY.md) for complete host configuration guide.**
### Using External Configurations
Reference external repositories for user dotfiles or system configurations:
Users and systems can reference external Git repositories for configuration:
```nix
# User dotfiles (in users.nix)
hdh20267.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/hdh20267/dotfiles";
# In users.nix - External dotfiles with user configuration
myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123...";
};
# The external user.nix file contains both athenix.users.myuser options
# AND home-manager configuration
# System configuration (in inventory.nix)
# In inventory.nix - External system config
nix-lxc = {
devices."special" = builtins.fetchGit {
devices."server" = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config";
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.**
### Building Installation Media
```bash
# Build installer ISO for a specific host
nix build .#installer-iso-nix-laptop1
# 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 .#lxc-nix-builder
# List all available artifacts
nix flake show
nix flake show git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git
```
**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`
**See [docs/BUILDING.md](docs/BUILDING.md) for complete guide on building ISOs, containers, and using remote builders.**
## System Types
Set via `athenix.sw.type`:
- **`desktop`** - Full GNOME desktop environment
- **`tablet-kiosk`** - Surface tablets with Firefox kiosk browser
- **`stateless-kiosk`** - Diskless PXE-booted systems
- **`headless`** - Servers and containers without GUI
- **`builders`** - Build servers
- **`tablet-kiosk`** - Surface tablets in kiosk mode
- **`stateless-kiosk`** - Diskless PXE boot kiosks
- **`headless`** - Servers and containers (no GUI)
## Development Workflow
Set via `athenix.sw.type` option. See [docs/NAMESPACE.md](docs/NAMESPACE.md) for all options.
## Development
**Quick commands:**
```bash
# Check all configurations
nix flake check
# Format code
nix fmt **/*.nix
# Build specific artifact
nix build .#installer-iso-nix-laptop1
# Update flake inputs
nix flake update
nix flake check # Validate all configurations
nix fmt # Format code
nix flake update # Update dependencies
nix build .#installer-iso-nix-laptop1 # Build specific artifact
```
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for detailed workflow.**
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for complete development guide.**
## Troubleshooting
| 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` |
**Common issues:**
## Prerequisites
Nix with flakes support:
- **Build errors:** Run `nix flake check --show-trace` for details
- **External modules not loading:** Check repository access and module structure (see templates)
- **Remote build failures:** Test SSH access: `ssh engr-ugaif@nix-builder`
- **Out of disk space:** Run `nix-collect-garbage -d && nix store optimise`
**Useful commands:**
```bash
# Recommended: Determinate Systems installer
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Or enable flakes in existing Nix installation
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
nix flake show # List all available outputs
nix flake metadata # Show flake info
nix eval .#nixosConfigurations --apply builtins.attrNames # List all hosts
```
## More Information
**See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) and [docs/BUILDING.md](docs/BUILDING.md) for detailed troubleshooting.**
- [docs/INVENTORY.md](docs/INVENTORY.md) - Host configuration
- [docs/NAMESPACE.md](docs/NAMESPACE.md) - All option references
- [docs/USER_CONFIGURATION.md](docs/USER_CONFIGURATION.md) - User management
- [docs/EXTERNAL_MODULES.md](docs/EXTERNAL_MODULES.md) - External modules
- [docs/BUILDING.md](docs/BUILDING.md) - Building and deployment
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) - Development guide
## Getting Help
- Review documentation in `docs/` directory
- Check templates: `templates/user/` and `templates/system/`
- Contact Innovation Factory IT team
Submodule assets/plymouth-theme added at 8658f4fb40
+116 -239
View File
@@ -1,13 +1,11 @@
# Building Installation Media and Artifacts
# Building Installation Media
Guide to building installer ISOs, live images, and container artifacts.
This guide covers building installer ISOs, live images, and container artifacts from the nixos-systems flake.
## Table of Contents
- [Quick Start](#quick-start)
- [Available Artifacts](#available-artifacts)
- [Building Locally](#building-locally)
- [Building from Remote](#building-from-remote)
- [Installer ISOs](#installer-isos)
- [Live ISOs](#live-isos)
- [Container Images](#container-images)
@@ -17,194 +15,116 @@ Guide to building installer ISOs, live images, and container artifacts.
## Quick Start
```bash
# List all available artifacts
nix flake show
# Build an installer ISO for a specific host
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1
# Build installer ISO for a specific host
nix build .#installer-iso-nix-laptop1
# Result is in result/iso/
# Result will be in result/iso/
ls -lh result/iso/
```
## Available Artifacts
Athenix can build multiple artifact types for deployment:
| Type | Description | Location | Use Case |
|------|-------------|----------|----------|
| `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" ];
};
};
```
## Building Locally
Build artifacts on your local machine:
List all available build outputs:
```bash
# Build installer ISO
nix build .#installer-iso-nix-laptop1
# Build live ISO
nix build .#iso-nix-ephemeral1
# Build LXC container
nix build .#lxc-nix-builder
# Build all available outputs
nix build .#
nix flake show git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git
```
**Result locations:**
- ISOs: `result/iso/nixos-*.iso`
- LXC: `result/tarball/nixos-*.tar.xz`
- Proxmox: `result/`
- iPXE: `result/` (kernel, initrd, script)
Common artifact types:
### Build Specific Host
```bash
# Get list of all hosts
nix eval .#nixosConfigurations --apply builtins.attrNames
# Build specific host
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
```
## 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
```
| Artifact Type | Description | Example |
|--------------|-------------|---------|
| `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 partition and install NixOS on first boot.
Installer ISOs automatically install the NixOS configuration to disk on first boot.
### Building
### Building Locally
```bash
# Build installer for a specific host
nix build .#installer-iso-nix-laptop1
ls -lh result/iso/
# 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
```
### Burning to USB
### Building from Gitea
```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
nix build git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#installer-iso-nix-laptop1
```
### Installation Process
### Using the Installer
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
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:** 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" ];
};
};
```
**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.
### Building
```bash
nix build .#iso-nix-ephemeral1
```
### Usage
Live ISOs are useful for:
- Testing configurations before installation
Live ISOs boot into a temporary system without installing to disk. Useful for:
- Testing configurations
- Recovery operations
- Ephemeral/stateless systems
- Booting in kiosk mode
### Customizing Live ISO
### Building Live ISOs
```nix
nix-ephemeral = {
devices.live = {
athenix.sw.type = "stateless-kiosk";
athenix.sw.kioskUrl = "https://dashboard.example.com";
athenix.host.buildMethods = [ "iso" ];
};
};
```bash
# Build live ISO
nix build .#iso-nix-ephemeral1
# Result location
ls -lh result/iso/nixos-*.iso
```
### Stateless Kiosk Systems
For PXE netboot kiosks, use the `ipxe-*` artifacts:
```bash
# Build iPXE artifacts
nix build .#ipxe-nix-ephemeral1
# Result contains:
# - bzImage (kernel)
# - initrd (initial ramdisk)
# - netboot.ipxe (iPXE script)
ls -lh result/
```
## Container Images
### LXC Containers
Build LXC container tarballs for Proxmox or standalone LXC:
Build LXC container tarballs for Proxmox or other LXC hosts:
```bash
# Build LXC tarball
nix build .#lxc-nix-builder
ls -lh result/tarball/
# Result location
ls -lh result/tarball/nixos-*.tar.xz
```
#### Importing to Proxmox
1. Copy tarball to Proxmox host:
**Importing to Proxmox:**
```bash
# Copy tarball to Proxmox host
scp result/tarball/nixos-*.tar.xz root@proxmox:/var/lib/vz/template/cache/
```
2. Create container:
```bash
# Create container from Proxmox CLI
pct create 100 local:vztmpl/nixos-*.tar.xz \
--hostname nix-builder \
--memory 4096 \
@@ -212,59 +132,25 @@ pct create 100 local:vztmpl/nixos-*.tar.xz \
--net0 name=eth0,bridge=vmbr0,ip=dhcp
```
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).
See [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) for detailed Proxmox deployment instructions.
### Proxmox VMA
Build Proxmox-specific VMA archives:
```bash
# Build Proxmox VMA
nix build .#proxmox-nix-builder
# Result location
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
Speed up builds by offloading to build servers.
### One-Time Build
### One-Time Remote Build
```bash
nix build .#installer-iso-nix-laptop1 \
@@ -273,7 +159,7 @@ nix build .#installer-iso-nix-laptop1 \
### Persistent Configuration
Add to `~/.config/nix/nix.conf`:
Add to `~/.config/nix/nix.conf` or `/etc/nix/nix.conf`:
```conf
builders = ssh://engr-ugaif@nix-builder x86_64-linux
@@ -285,12 +171,12 @@ Then build normally:
nix build .#installer-iso-nix-laptop1
```
### SSH Setup
### SSH Key Setup
Ensure SSH is configured for the builder:
For remote builders, ensure SSH keys are configured:
```bash
# Generate key if needed
# Generate SSH key if needed
ssh-keygen -t ed25519
# Copy to builder
@@ -302,86 +188,77 @@ ssh engr-ugaif@nix-builder
### Multiple Builders
Configure multiple build servers:
```conf
builders = ssh://engr-ugaif@nix-builder1 x86_64-linux ; ssh://engr-ugaif@nix-builder2 x86_64-linux
builders = ssh://engr-ugaif@nix-builder 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
### Build Errors
Get detailed error information:
**Check configuration validity:**
```bash
# Verbose error traces
nix build .#installer-iso-nix-laptop1 --show-trace
# Check all configurations first
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
**Clean up Nix store:**
```bash
# Clean up Nix store
nix-collect-garbage -d
# Optimize store
nix store optimise
```
### Build Hangs
**Check space:**
```bash
# List processes
ps aux | grep nix
# Cancel build
Ctrl+C
df -h /nix
```
### Finding Artifact Outputs
### ISO Won't Boot
**Verify ISO integrity:**
```bash
# 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
sha256sum result/iso/nixos-*.iso
```
### Build Not Creating Expected File
**Check USB write:**
```bash
# Check build log
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
# 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:**
- UEFI systems: Try legacy BIOS mode
- Legacy BIOS: Try UEFI mode
## 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
- [INVENTORY.md](INVENTORY.md) - Host configuration guide
- [installer/PROXMOX_LXC.md](../installer/PROXMOX_LXC.md) - Proxmox deployment guide
+341 -333
View File
@@ -1,456 +1,464 @@
# Development Guide
Comprehensive guide for maintaining and extending Athenix.
This guide covers development workflows for maintaining and extending the nixos-systems repository.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Development Workflow](#development-workflow)
- [Testing Changes](#testing-changes)
- [Continuous Integration](#continuous-integration)
- [Common Tasks](#common-tasks)
- [Debugging](#debugging)
- [Troubleshooting](#troubleshooting)
- [System Rebuilds](#system-rebuilds)
- [Updating Dependencies](#updating-dependencies)
- [Adding Packages](#adding-packages)
- [Python Development](#python-development)
- [Contributing](#contributing)
## Prerequisites
### Install Nix with Flakes
Install Nix with flakes support:
```bash
# Recommended: Determinate Systems installer
# Recommended: Determinate Systems installer (includes flakes)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Or official installer
# Alternative: Official installer (requires enabling flakes manually)
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
### Validate Configuration Syntax
Always test configuration changes before committing.
Always run before committing:
### Validate All Configurations
```bash
# Check all configurations build correctly
nix flake check
```
Shows any configuration errors across all ~50+ hosts. Output:
```
checking 50 configurations...
✓ All checks passed
# Check with verbose error traces
nix flake check --show-trace
```
### Test Specific Host Build
```bash
# Build specific host (shows if config actually compiles)
# Build a specific host's configuration
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel
# Shorter form
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel -L
# Build installer for specific host
nix build .#installer-iso-nix-laptop1
```
### Test Installer Build
### Test Local Changes
If you're on a NixOS system managed by this flake:
```bash
# 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)
# Test changes without committing (temporary, doesn't survive reboot)
sudo nixos-rebuild test --flake .
# Apply and switch (persistent)
# Apply and switch to new configuration
sudo nixos-rebuild switch --flake .
# Build without switching
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
### CI Pipeline
The repository uses Gitea Actions for automated testing and validation. CI jobs run on the self-hosted `nix-builder` machine.
All pushes and pull requests trigger automated tests on the self-hosted `nix-builder`:
### CI Workflow
1. **Flake Check** - `nix flake check` validates all 50+ configurations
2. **Format Check** - Verifies code formatted with `nix fmt`
3. **Build Key Hosts** - Builds `nix-builder`, `nix-laptop1`, `nix-desktop1`
4. **Build Artifacts** - Tests `lxc-nix-builder` and `installer-iso-nix-laptop1`
All pull requests and pushes to main trigger the CI pipeline, which includes:
1. **Flake Check** - Validates all NixOS configurations
- Runs `nix flake check` to ensure all systems build correctly
- 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
```bash
# Web interface
https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions
Check the CI status badge at the top of the README or view detailed logs:
# Or check locally
git log --oneline -n 5
# Look for ✓ or ✗ next to commits
```bash
# View workflow status
https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/actions
```
### Running CI Checks Locally
Test before pushing:
Before pushing changes, run the same checks that CI performs:
```bash
# Flake check
# Run all checks
nix flake check --show-trace
# Format check
nix fmt --check
# Check formatting
nix fmt
git diff --exit-code # Should return no changes
# Format code
nix fmt **/*.nix
# Build specific configuration
nix build .#nixosConfigurations.nix-builder.config.system.build.toplevel
# Build key configurations
nix build .#nixosConfigurations.nix-builder.config.system.build.toplevel -L
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel -L
# Build artifacts
nix build .#lxc-nix-builder
```
## Common Tasks
### Self-Hosted Runner
### Adding a New Host
CI jobs run on the `nix-builder` host as a self-hosted Gitea Actions runner. This provides:
Edit `inventory.nix`:
- Native Nix environment without installation overhead
- Access to local Nix store for faster builds
- Consistent build environment matching deployment targets
- Direct access to build caching infrastructure
```nix
nix-surface = {
devices = 3; # Creates nix-surface1, nix-surface2, nix-surface3
overrides = {
athenix.sw.type = "tablet-kiosk";
athenix.sw.kioskUrl = "https://dashboard.example.com";
};
};
```
#### Setting Up the Gitea Actions Runner
Test:
The nix-builder host is configured with a Gitea Actions self-hosted runner in `inventory.nix`. To complete the setup:
1. **Generate a Gitea Runner Token**:
- Go to https://git.factory.uga.edu/UGA-Innovation-Factory/athenix/settings/actions/runners
- 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:
```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
# 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 build .#installer-iso-nix-surface1 -L
# Commit if successful
git add flake.lock
git commit -m "Update flake inputs"
git push
```
### Modifying Software Configuration
Edit appropriate file in `sw/`:
### Update Specific Input
```bash
# Desktop software
vim sw/desktop/programs.nix
# Update only nixpkgs
nix flake lock --update-input nixpkgs
# Or for all systems
vim sw/default.nix
# Update home-manager
nix flake lock --update-input home-manager
# Update multiple specific inputs
nix flake lock --update-input nixpkgs --update-input home-manager
```
Use `athenix.sw.extraPackages` for host-specific additions:
### 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
```
**Tablet kiosks:**
```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-laptop = {
devices = 5;
overrides.athenix.sw.extraPackages = with pkgs; [ special-tool ];
};
```
### 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...";
devices = 2;
overrides = {
athenix.sw.extraPackages = with pkgs; [
vim
docker
kubernetes-helm
];
};
};
```
### Updating Dependencies
### User-Specific Packages
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
# Update all flake inputs
nix flake update
# Search nixpkgs
nix search nixpkgs firefox
nix search nixpkgs python3
# Update specific input
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"
# Show package details
nix eval nixpkgs#firefox.meta.description
```
## Debugging
## Python Development
### Verbose Output
All systems include modern Python tools: `pixi` and `uv`.
Get detailed error messages:
### Pixi (Recommended for Projects)
```bash
# Show full error traces
nix flake check --show-trace
# Initialize new project
pixi init my-project
cd my-project
# With maximum verbosity
nix build .#installer-iso-nix-laptop1 -vvv
# Add dependencies
pixi add pandas numpy matplotlib jupyter
# Show build log
nix build .#installer-iso-nix-laptop1 -L
# Run Python
pixi run python
# Run Jupyter
pixi run jupyter notebook
# Run scripts
pixi run python script.py
# Shell with dependencies
pixi shell
```
### Inspect Configuration
### uv (Quick Virtual Environments)
```bash
# Evaluate configuration for specific host
nix eval .#nixosConfigurations.nix-laptop1.config.athenix.sw --json
# Create virtual environment
uv venv
# Get all host names
nix eval .#nixosConfigurations --apply builtins.attrNames
# Activate
source .venv/bin/activate
# Check specific option
nix eval .#nixosConfigurations.nix-laptop1.config.users.users
# Install packages
uv pip install requests pandas
# Freeze requirements
uv pip freeze > requirements.txt
# Install from requirements
uv pip install -r requirements.txt
```
### Test Module Loading
### System Python
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
# Evaluate specific module
nix-build -A nixosConfigurations.nix-laptop1.config.system.build.toplevel
# Or with flakes
nix build .#nixosConfigurations.nix-laptop1.config.system.build.toplevel --verbose
# Full workflow
nix fmt
nix flake check
git add .
git commit -m "Description of changes"
git push
```
### Check Derivation Dependencies
### Documentation
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
# Show what dependencies a build needs
nix show-derivation .#installer-iso-nix-laptop1
# Show all available outputs
nix flake show
# Or human-readable
nix build .#installer-iso-nix-laptop1 --dry-run
```
# Evaluate specific option
nix eval .#nixosConfigurations.nix-laptop1.config.networking.hostName
## 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
nix eval .#nixosConfigurations --apply builtins.attrNames
# Show flake structure
nix flake show | head -50
# Check flake metadata
nix flake metadata
# Check Nix store size
du -sh /nix/store
# Show evaluation trace
nix eval --show-trace .#nixosConfigurations.nix-laptop1
# List top space users in store
nix store du --human-readable | head -20
# Build and enter debug shell
nix develop
# Find store paths for a package
nix store path-info -rS $(which some-package)
# Clean up old generations
nix-collect-garbage -d
# 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
- [BUILDING.md](BUILDING.md) - Building artifacts
- [INVENTORY.md](INVENTORY.md) - Host configuration
- [NAMESPACE.md](NAMESPACE.md) - Configuration options
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External modules
- [README.md](../README.md) - Main documentation
- [INVENTORY.md](INVENTORY.md) - Host inventory configuration
- [BUILDING.md](BUILDING.md) - Building installation media
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management
+279 -302
View File
@@ -1,6 +1,6 @@
# External Configuration Modules
Guide to using external modules for system and user configurations.
This guide explains how to use external modules for system and user configurations in nixos-systems.
## Table of Contents
@@ -8,141 +8,116 @@ Guide to using external modules for system and user configurations.
- [System Modules](#system-modules)
- [User Modules](#user-modules)
- [Fetch Methods](#fetch-methods)
- [Creating External Modules](#creating-external-modules)
- [Best Practices](#best-practices)
- [Templates](#templates)
- [Integration Details](#integration-details)
## Overview
External modules allow you to maintain configurations in separate Git repositories and reference them from Athenix.
External modules allow you to maintain configurations in separate Git repositories and reference them from `inventory.nix` (for systems) or `users.nix` (for users).
**Benefits:**
- **Separation** - Keep complex configs in separate repositories
- **Reproducibility** - Pin specific commits for deterministic builds
- **Reusability** - Share configurations across multiple deployments
- **Flexibility** - Mix external modules with local configuration
- **Ownership** - Users maintain their own dotfiles
- **Separation:** Keep configs in separate repositories
- **Versioning:** Pin to specific commits for reproducibility
- **Reusability:** Share configurations across deployments
- **Flexibility:** Mix external modules with local overrides
## System Modules
External system modules provide host-specific NixOS configurations.
External system modules provide complete NixOS configurations for hosts.
### Usage
In `inventory.nix`, reference an external module using the `external` field:
### Usage in inventory.nix
```nix
nix-lxc = {
devices = {
# Inline configuration (traditional method)
# Traditional inline configuration
"local-server" = {
athenix.sw.type = "headless";
athenix.users.admin.enable = true;
services.nginx.enable = true;
};
# External module (lazy evaluation - fetched only when building this host)
"remote-server".external = builtins.fetchGit {
# External module from Git
"remote-server" = builtins.fetchGit {
url = "https://git.factory.uga.edu/org/server-config";
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";
rev = "abc123..."; # Pin to specific commit
};
};
};
```
**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
### External Repository Structure
```
server-config/
├── default.nix # Required: NixOS module
── README.md # Recommended: Documentation
└── optional/
├── config/ # Optional: Configuration files
└── scripts/ # Optional: Helper scripts
├── default.nix # Required: NixOS module
── README.md # Optional: Documentation
```
### Module Content (default.nix)
**default.nix:**
```nix
# The module receives inputs and standard NixOS module parameters
{ inputs, ... }:
{ config, lib, pkgs, ... }:
{
# Your NixOS configuration
# Use any standard NixOS option or athenix.* options
services.nginx = {
enable = true;
virtualHosts."example.com" = {
root = "/var/www";
forceSSL = true;
enableACME = true;
};
};
# Use athenix options
# Use athenix namespace options
athenix.users.admin.enable = true;
athenix.sw.type = "headless";
athenix.sw.extraPackages = with pkgs; [ git htop ];
# Standard NixOS configuration
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.openssh.enable = true;
}
```
### What System Modules Receive
### What External Modules Receive
- **`inputs`** - All flake inputs (nixpkgs, home-manager, disko, etc.)
- **`config`** - Current NixOS configuration (read/write)
- **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.)
- **`config`** - Full NixOS configuration
- **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set
### Configuration Order
### Module Integration Order
When a host is built, modules load in this order:
When a host is built, modules are loaded in this order:
1. Hardware type module (from `hw/nix-*.nix`)
2. Common system configuration (from `fleet/common.nix`)
3. Software type module (from `sw/{type}/`)
4. User NixOS modules (from `users.nix` - `nixos.nix` files)
5. Device-specific overrides (from `inventory.nix`)
6. External system module (if present)
1. User NixOS modules (from `users.nix` - `nixos.nix` files)
2. Host type module (from `hosts/types/`)
3. Configuration overrides (from `inventory.nix`)
4. Hostname assignment
5. External system module (if using `builtins.fetchGit`)
Each later module can override earlier ones using standard NixOS precedence rules.
Later modules can override earlier ones using standard NixOS module precedence.
### 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
External user modules provide home-manager configurations (dotfiles, environment setup).
External user modules provide home-manager configurations (dotfiles, packages, programs).
### Usage
In `users.nix`, reference an external user module:
### Usage in users.nix
```nix
athenix.users = {
# External user module
myuser.external = builtins.fetchGit {
# External user module (dotfiles, home-manager, and user options)
myuser = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123def456..."; # Pin to specific commit
rev = "abc123...";
};
# Inline user definition
otheruser = {
description = "Other User";
inlineuser = {
description = "Inline User";
extraGroups = [ "wheel" ];
shell = pkgs.zsh;
hashedPassword = "$6$...";
@@ -150,179 +125,148 @@ athenix.users = {
};
```
Then enable on hosts in `inventory.nix`:
### External Repository Structure
```
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-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, ... }:
{ 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" ];
description = "Your Full Name";
shell = pkgs.zsh;
hashedPassword = "!"; # SSH keys only
opensshKeys = [
"ssh-ed25519 AAAA... user@laptop"
];
hashedPassword = "!";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
useZshTheme = true;
useNvimPlugins = true;
};
# ========== Home Manager Configuration ==========
# User environment, packages, and dotfiles
# Packages
home.packages = with pkgs; [
vim
git
ripgrep
fzf
htop
] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
# Programs
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
export EDITOR=vim
'';
};
# Manage dotfiles
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;
home.file.".bashrc".source = ./dotfiles/bashrc;
}
```
### nixos.nix (Optional)
System-level configuration for this user (rarely needed):
**nixos.nix (optional):**
```nix
{ inputs, ... }:
{ config, lib, pkgs, ... }:
{
# System-level configuration
# Only needed if the user requires specific system-wide settings
users.users.myusername.extraGroups = [ "docker" ];
# System-level configuration for this user
users.users.myuser.extraGroups = [ "docker" ];
environment.systemPackages = [ pkgs.docker ];
# Security settings
security.sudo.extraRules = [{
users = [ "myusername" ];
commands = [{
command = "/usr/bin/something";
options = [ "NOPASSWD" ];
}];
}];
}
```
### What User Modules Receive
**In user.nix:**
- **`inputs`** - All flake inputs (nixpkgs, home-manager, etc.)
- **`config`** - Home-manager configuration (read/write)
- **`inputs`** - Flake inputs (nixpkgs, home-manager, etc.)
- **`config`** - Home-manager configuration
- **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set
- **`osConfig`** - OS configuration (read-only) - useful for conditional setup
- **`osConfig`** - OS-level configuration (read-only)
**In nixos.nix:**
- **`inputs`** - Flake inputs
- **`config`** - NixOS configuration (read/write)
- **`config`** - NixOS configuration
- **`lib`** - Nixpkgs library functions
- **`pkgs`** - Package set
### Conditional Setup Example
Use `osConfig` to conditionally set up dotfiles based on the system type:
### User Options in users.nix
```nix
# In user.nix
{ inputs, ... }:
{ config, lib, pkgs, osConfig ? null, ... }:
{
athenix.users.myuser = { /* ... */ };
username = {
# Identity
description = "Full Name";
# Install Firefox only on desktop systems
home.packages = with pkgs; [
ripgrep
] ++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
# External configuration
external = builtins.fetchGit { ... };
# System settings
extraGroups = [ "wheel" "networkmanager" ];
hashedPassword = "$6$...";
opensshKeys = [ "ssh-ed25519 ..." ];
shell = pkgs.zsh;
# Different shell config per system
programs.zsh.initExtra = ''
${lib.optionalString (osConfig.athenix.sw.type or null == "headless") "
# Headless-only settings
"}
'';
}
# Theme integration
useZshTheme = true; # Apply system zsh theme (default: true)
useNvimPlugins = true; # Apply system nvim config (default: true)
# 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
### builtins.fetchGit (Recommended)
### Recommended: fetchGit with Revision
Pin to a specific Git revision:
Pin to a specific commit for reproducibility:
```nix
builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123def456..."; # Required: specific commit hash
url = "https://github.com/user/repo";
rev = "abc123def456..."; # Full commit hash (40 characters)
ref = "main"; # Optional: branch name
}
```
**Advantages:**
- Reproducible (pinned to exact commit)
- Works with any Git repository
- Supports SSH or HTTPS URLs
**Finding the commit hash:**
```bash
# Latest commit on main branch
git ls-remote https://github.com/user/repo main
**Important:** Always specify `rev` (commit hash) for reproducibility. Don't use branches which can change.
# Or from a local clone
git rev-parse HEAD
```
### builtins.fetchTarball
### fetchGit with Branch (Less Reproducible)
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:
@@ -343,141 +287,174 @@ nix-prefetch-url --unpack https://github.com/user/repo/archive/v1.0.0.tar.gz
Use local directories during development:
```nix
# users.nix
athenix.users.myuser.external = /home/user/my-dotfiles;
/home/username/dev/my-config
# inventory.nix
nix-laptop = {
# Or relative to repository
./my-local-config
```
⚠️ **Warning:** Only for testing. Use Git-based methods for production.
## Templates
### System Module Template
```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
```
See [templates/system/README.md](../templates/system/README.md) for detailed usage.
### User Module Template
```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
```
See [templates/user/README.md](../templates/user/README.md) for detailed usage.
## Integration Details
### Detection Logic
The system automatically detects external modules when a device or user value is:
- A path (`builtins.isPath`)
- 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.
### User Module Integration
External user modules are loaded in two contexts:
**User options (NixOS module context):**
```nix
import (externalPath + "/user.nix") { inherit inputs; }
# Evaluated as NixOS module to extract athenix.users.<username> options
```
**Home-manager configuration:**
```nix
import (externalPath + "/user.nix") { inherit inputs; }
# Imported into home-manager for home.*, programs.*, services.* options
```
**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 = {
"dev".athenix.users.myuser.enable = true;
"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 ];
};
};
```
**Note:** Only works if the path exists on the machine running `nix flake check` or `nix build`.
### Minimal User Module
## Creating External Modules
### System Module Template
Create a new system module repository from the template:
```bash
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#system
```
This creates:
```
my-system-config/
├── flake.nix # Optional: for testing standalone
├── default.nix # Your NixOS module
└── README.md # Documentation
```
### User Module Template
Create a new user module repository:
```bash
nix flake init -t git+https://git.factory.uga.edu/UGA-Innovation-Factory/athenix.git#user
```
This creates:
```
my-dotfiles/
├── flake.nix # Optional: for testing standalone
├── user.nix # User options + home-manager config
├── nixos.nix # Optional: system-level config
└── README.md # Documentation
```
### Testing External Modules
Test your external module locally before pushing:
```bash
# In your module repository
cd /path/to/my-module
# Test the Nix syntax
nix flake check
```
## Best Practices
### 1. Always Pin to Specific Commits
❌ Wrong - using branch names:
**user.nix:**
```nix
builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
# No rev specified or using "main"
{ 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 ];
}
```
✅ Correct - using commit hash:
### Full User Module with Dotfiles
```
dotfiles/
├── user.nix
├── nixos.nix
└── config/
├── bashrc
├── vimrc
└── gitconfig
```
**user.nix:**
```nix
builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123def456789...";
{ inputs, ... }:
{ config, lib, pkgs, osConfig ? null, ... }:
{
# 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
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management
- [INVENTORY.md](INVENTORY.md) - Host configuration
- [NAMESPACE.md](NAMESPACE.md) - Configuration options
- [README.md](../README.md) - Main documentation
- [templates/user/](../templates/user/) - User module template
- [INVENTORY.md](INVENTORY.md) - Host configuration guide
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management guide
- [NAMESPACE.md](NAMESPACE.md) - Configuration options reference
- [templates/system/](../templates/system/) - System module template
- [templates/user/](../templates/user/) - User module template
- [README.md](../README.md) - Main documentation
+48 -219
View File
@@ -1,272 +1,101 @@
# Host Inventory Configuration
This document explains the `inventory.nix` file, which defines all hosts in your fleet.
This guide explains how to configure hosts in `inventory.nix` to define your fleet of devices.
## Table of Contents
- [Overview](#overview)
- [Structure](#structure)
- [Hostname Generation](#hostname-generation)
- [Configuration Methods](#configuration-methods)
- [Options](#options)
- [Understanding Inventory Structure](#understanding-inventory-structure)
- [Hostname Generation Rules](#hostname-generation-rules)
- [Adding Hosts](#adding-hosts)
- [Device Configuration Options](#device-configuration-options)
- [Examples](#examples)
## Overview
## Understanding Inventory Structure
`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.
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.
**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`
## Hostname Generation Rules
## Structure
- **Numeric suffixes**: no dash (e.g., `nix-laptop1`, `nix-laptop2`)
- **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
```nix
{
"prefix-name" = {
# Optional: Device count or explicit device map
devices = 5; # or { "1" = { ... }; "alpha" = { ... }; }
## Adding Hosts
# 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:**
### Method 1: Quick Count (Simplest)
```nix
nix-laptop = {
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)
};
devices = 5; # Creates: nix-laptop1, nix-laptop2, ..., nix-laptop5
};
```
## Configuration Methods
### Method 1: Simple Count
Create N identical hosts:
### Method 2: Explicit Count with Overrides
```nix
nix-laptop = {
devices = 5;
};
# Generates: nix-laptop1, nix-laptop2, nix-laptop3, nix-laptop4, nix-laptop5
```
### Method 2: Simple Count with Overrides
Create N hosts with common configuration:
```nix
nix-desktop = {
devices = 3;
overrides = {
# Applied to ALL nix-laptop hosts
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:
### Method 3: Individual Device Configuration
```nix
nix-surface = {
devices = {
"1".athenix.sw.kioskUrl = "https://dashboard1.example.com";
"2".athenix.sw.kioskUrl = "https://dashboard2.example.com";
"3" = {
athenix.sw.kioskUrl = "https://dashboard3.example.com";
services.openssh.enable = false;
};
"3".athenix.sw.kioskUrl = "https://dashboard3.example.com";
};
};
```
### Method 4: External Module
Reference a Git repository using the `external` field (lazy evaluation):
```nix
nix-lxc = {
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 = {
"special" = {
athenix.sw.extraPackages = with pkgs; [ special-software ];
};
};
overrides = {
# Applied to all devices (default count + custom)
athenix.users.lab-admin.enable = true;
};
};
```
## Options
### Top-Level Device Options
#### `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:
### Method 4: Mixed (Default Count + Custom Devices)
```nix
nix-surface = {
defaultCount = 2; # Creates nix-surface1, nix-surface2
devices = {
"1" = {
# athenix.* namespace options
athenix.users.student.enable = true;
athenix.host.filesystem.device = "/dev/sda";
athenix.host.filesystem.swapSize = "16G";
athenix.sw.kioskUrl = "https://dashboard1.example.com";
athenix.sw.extraPackages = with pkgs; [ firefox ];
# Standard NixOS options
networking.firewall.enable = false;
services.openssh.enable = true;
time.timeZone = "America/New_York";
boot.kernelPackages = pkgs.linuxPackages_latest;
"special" = { # Creates nix-surface-special
athenix.sw.kioskUrl = "https://special-dashboard.example.com";
};
};
overrides = {
# Applied to all devices (including "special")
athenix.sw.kioskUrl = "https://default-dashboard.example.com";
};
};
```
## Device Configuration Options
### Direct Configuration (Recommended)
Use any NixOS or `athenix.*` option:
```nix
"1" = {
# Athenix options
athenix.users.myuser.enable = true;
athenix.host.filesystem.swapSize = "64G";
athenix.sw.extraPackages = with pkgs; [ docker ];
athenix.sw.kioskUrl = "https://example.com";
# Standard NixOS options
networking.firewall.enable = false;
services.openssh.enable = true;
time.timeZone = "America/New_York";
};
```
### Convenience: `athenix.forUser`
Quick setup for single-user systems (especially WSL). This automatically enables a user and sets the WSL default user:
Quick setup for single-user systems (especially WSL):
```nix
nix-wsl = {
+93 -207
View File
@@ -1,69 +1,53 @@
# Configuration Namespace Reference
All UGA Innovation Factory-specific options are in the `athenix` namespace to avoid conflicts with standard NixOS options.
All UGA Innovation Factory-specific options are under the `athenix` namespace to avoid conflicts with standard NixOS options.
## Table of Contents
- [Host Configuration (`athenix.host`)](#host-configuration-athenixhost)
- [Software Configuration (`athenix.sw`)](#software-configuration-athenixsw)
- [User Management (`athenix.users`)](#user-management-athenixusers)
- [System Configuration (`athenix.system`)](#system-configuration-athenixsystem)
- [Convenience Options](#convenience-options)
## Host Configuration (`athenix.host`)
Hardware and boot-related settings.
Hardware and host-specific settings.
### `athenix.host.filesystem.device`
### `athenix.host.filesystem`
Boot disk device path.
Disk and storage configuration.
**Type:** String or null
**Default:** `null` (must be set by hardware type or per-host)
**Options:**
- `athenix.host.filesystem.device` - Boot disk device (default: `/dev/sda`)
- `athenix.host.filesystem.swapSize` - Swap file size (default: `"32G"`)
**Example:**
```nix
athenix.host.filesystem.device = "/dev/nvme0n1";
```
### `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.filesystem = {
device = "/dev/nvme0n1";
swapSize = "64G";
};
```
### `athenix.host.buildMethods`
Artifact types to build for this host.
List of supported build artifact types for this host.
**Type:** List of strings
**Options:** `"installer-iso"`, `"iso"`, `"ipxe"`, `"lxc"`, `"proxmox"`
**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
**Default:** `["installer-iso"]`
**Example:**
```nix
athenix.host.buildMethods = [ "installer-iso" "lxc" ];
athenix.host.buildMethods = [ "lxc" "proxmox" ];
```
### `athenix.host.useHostPrefix`
Whether to prepend the host type prefix to the generated hostname.
Whether to prepend the host type prefix to the hostname (used in inventory generation).
**Type:** Boolean
@@ -71,19 +55,15 @@ Whether to prepend the host type prefix to the generated hostname.
**Example:**
```nix
# 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.useHostPrefix = false; # "builder" instead of "nix-lxc-builder"
```
### `athenix.host.wsl.user`
### `athenix.host.wsl`
Default WSL user account (only for `nix-wsl` type).
WSL-specific configuration options.
**Type:** String (username)
**Options:**
- `athenix.host.wsl.user` - Default WSL user for this instance
**Example:**
```nix
@@ -92,11 +72,11 @@ athenix.host.wsl.user = "myusername";
## Software Configuration (`athenix.sw`)
System type, packages, and application configuration.
System software and application configuration.
### `athenix.sw.enable`
Enable software configuration.
Enable the software configuration module.
**Type:** Boolean
@@ -104,32 +84,28 @@ Enable software configuration.
### `athenix.sw.type`
System profile/type. Determines which software packages and services are installed.
System type that determines the software profile.
**Type:** String or list of strings
**Type:** Enum
**Options:**
- `"desktop"` - Full GNOME desktop environment with development tools
- `"tablet-kiosk"` - Surface tablets with Firefox kiosk browser
- `"stateless-kiosk"` - Diskless PXE-booted ephemeral systems
- `"desktop"` - Full desktop environment (GNOME)
- `"tablet-kiosk"` - Surface tablets with kiosk mode browser
- `"stateless-kiosk"` - Diskless PXE boot kiosks
- `"headless"` - Servers and containers without GUI
- `"builders"` - Build servers with build dependencies
**Default:** `"desktop"`
**Example:**
```nix
athenix.sw.type = "desktop";
# Multiple types supported
athenix.sw.type = [ "desktop" "headless" ];
athenix.sw.type = "headless";
```
### `athenix.sw.kioskUrl`
URL to display in kiosk browser (for `tablet-kiosk` and `stateless-kiosk` types).
URL to display in kiosk mode browsers (for `tablet-kiosk` and `stateless-kiosk` types).
**Type:** String (URL)
**Type:** String
**Default:** `"https://ha.factory.uga.edu"`
@@ -138,13 +114,12 @@ URL to display in kiosk browser (for `tablet-kiosk` and `stateless-kiosk` types)
athenix.sw.kioskUrl = "https://dashboard.example.com";
```
### `athenix.sw.python.enable`
### `athenix.sw.python`
Enable Python development tools (pixi, uv, etc.).
Python development tools configuration.
**Type:** Boolean
**Default:** `true`
**Options:**
- `athenix.sw.python.enable` - Enable Python tools (pixi, uv) (default: `true`)
**Example:**
```nix
@@ -153,13 +128,11 @@ athenix.sw.python.enable = true;
### `athenix.sw.remoteBuild`
Configure remote build servers for offloading builds.
**Type:** Attribute set
Remote build server configuration for offloading builds.
**Options:**
- `enable` - Enable remote builders (Boolean, default: `true` for tablets)
- `hosts` - List of remote builder hostnames (List of strings)
- `athenix.sw.remoteBuild.enable` - Use remote builders (default: enabled on tablets)
- `athenix.sw.remoteBuild.hosts` - List of build server hostnames
**Example:**
```nix
@@ -171,187 +144,104 @@ athenix.sw.remoteBuild = {
### `athenix.sw.extraPackages`
Additional system packages beyond the type defaults.
Additional system packages to install beyond the type defaults.
**Type:** List of packages
**Default:** `[ ]`
**Default:** `[]`
**Example:**
```nix
athenix.sw.extraPackages = with pkgs; [
vim
docker
htop
ripgrep
docker
];
```
### `athenix.sw.excludePackages`
Packages to remove from the default list for this system type.
Packages to exclude from the default list for this system type.
**Type:** List of packages
**Default:** `[ ]`
**Default:** `[]`
**Example:**
```nix
athenix.sw.excludePackages = with pkgs; [
firefox # Don't install Firefox on this system
firefox # Remove Firefox from default desktop packages
];
```
## User Management (`athenix.users`)
User account configuration and access control.
User account configuration and management.
### `athenix.users.<username>.enable`
Enable a user account on this system.
Enable a specific user account on this system.
**Type:** Boolean
**Default:** `false` (except `root` and `engr-ugaif` which are `true`)
**Default:** `false` (except `root` and `engr-ugaif` which default to `true`)
**Example:**
```nix
# In inventory.nix
nix-laptop = {
devices = 5;
overrides.athenix.users.myuser.enable = true;
athenix.users = {
myuser.enable = true;
student.enable = true;
};
```
### User Account Options (in `users.nix`)
### User Account Options
Define user accounts in `users.nix` with these options:
#### `description`
Full name or description of the user.
**Type:** String
Each user in `users.nix` can be configured with:
```nix
athenix.users.myuser.description = "John Doe";
```
# Option 1: Define inline in users.nix
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
#### `extraGroups`
enable = false; # Enable per-system in inventory.nix
};
Additional Unix groups for the user.
**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 {
# Option 2: Use external configuration (recommended)
# The external user.nix can set athenix.users.myuser options directly
athenix.users.anotheruser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
rev = "abc123...";
};
```
See [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) for detailed external module usage.
## System Configuration (`athenix.system`)
### Enabling Users on Systems
System-wide settings and services.
Users defined in `users.nix` are **not enabled by default**. Enable them in `inventory.nix`:
### `athenix.system.gc`
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
# Option 1: Enable on all devices in a group
nix-laptop = {
devices = 5;
overrides.athenix.users.student.enable = true;
};
# Option 2: Enable on specific devices
nix-surface = {
devices = {
"1".athenix.users.admin.enable = true;
"2".athenix.users.admin.enable = true;
};
athenix.system.gc = {
enable = true;
frequency = "daily";
retentionDays = 14;
optimise = true;
};
```
@@ -359,7 +249,7 @@ nix-surface = {
### `athenix.forUser`
Quick setup for single-user systems. Automatically enables a user and sets it as the default.
Quick setup option that enables a user account in one line.
**Type:** String (username) or null
@@ -367,7 +257,11 @@ Quick setup for single-user systems. Automatically enables a user and sets it as
**Example:**
```nix
# In inventory.nix - enables the user automatically
athenix.forUser = "myusername"; # Equivalent to athenix.users.myusername.enable = true
```
**Usage in inventory.nix:**
```nix
nix-wsl = {
devices = {
"alice".athenix.forUser = "alice-uga";
@@ -375,17 +269,9 @@ nix-wsl = {
};
```
Equivalent to:
```nix
"alice" = {
athenix.users.alice-uga.enable = true;
athenix.host.wsl.user = "alice-uga";
};
```
## See Also
- [INVENTORY.md](INVENTORY.md) - Host configuration examples
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User account management guide
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External module integration
- [INVENTORY.md](INVENTORY.md) - Host inventory configuration guide
- [USER_CONFIGURATION.md](USER_CONFIGURATION.md) - User management guide
- [EXTERNAL_MODULES.md](EXTERNAL_MODULES.md) - External configuration modules
- [README.md](../README.md) - Main documentation
+63 -545
View File
@@ -1,585 +1,103 @@
# User Configuration Guide
Comprehensive guide to managing user accounts in Athenix.
Complete guide to managing user accounts in nixos-systems.
## Table of Contents
- [Overview](#overview)
- [Quick Start](#quick-start)
- [Defining Users](#defining-users)
- [Enabling Users on Hosts](#enabling-users-on-hosts)
- [User Account Options](#user-account-options)
- [External User Configurations](#external-user-configurations)
- [Enabling Users on Hosts](#enabling-users-on-hosts)
- [Password Management](#password-management)
- [SSH Keys](#ssh-keys)
- [User Groups](#user-groups)
- [Examples](#examples)
## Overview
User accounts are defined in `users.nix` but are **not enabled by default**. Each host must explicitly enable users in `inventory.nix`.
Users are defined in `users.nix` but are **not enabled by default** on all systems. Each system must explicitly enable users in `inventory.nix`.
**Always-enabled users:**
- `root` - System administrator (enable: true)
- `engr-ugaif` - Innovation Factory default account (enable: true)
All other users are disabled by default and must be explicitly enabled per-host.
**Default enabled users:**
- `root` - System administrator
- `engr-ugaif` - Innovation Factory default account
## Quick Start
### 1. Define User in users.nix
```nix
athenix.users.myuser = {
description = "John Doe";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512
opensshKeys = [ "ssh-ed25519 AAAA..." ];
athenix.users = {
# Option 1: Inline definition
myuser = {
description = "My Full Name";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$..."; # Generate with: mkpasswd -m sha-512
opensshKeys = [
"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 on Hosts in inventory.nix
### 2. Enable User on Hosts
In `inventory.nix`:
```nix
nix-laptop = {
devices = 5;
overrides.athenix.users.myuser.enable = true;
devices = 2;
overrides.athenix.users.myuser.enable = true; # Enables on all nix-laptop hosts
};
# 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
};
```
### 3. Users can now log in
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
athenix.users.myuser = {
description = "My Full Name";
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
hashedPassword = "$6$...";
opensshKeys = [ "ssh-ed25519 AAAA..." ];
useZshTheme = true;
useNvimPlugins = true;
};
```
### External User Configuration
Reference an external Git repository (recommended for personal dotfiles):
```nix
athenix.users.myuser.external = builtins.fetchGit {
url = "https://git.factory.uga.edu/username/dotfiles";
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
Each user in `users.nix` can have the following options:
```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`
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"
];
useZshTheme = true;
useNvimPlugins = true;
};
# ========== 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"
username = {
# === Identity ===
description = "Full Name"; # User's full name
# === System Access ===
isNormalUser = true; # Default: true (false for root)
extraGroups = [ # Additional Unix groups
"wheel" # Sudo access
"networkmanager" # Network configuration
"docker" # Docker access
"video" # Video device access
"audio" # Audio device access
];
useZshTheme = true;
useNvimPlugins = true;
};
shell = pkgs.zsh; # Login shell (default: pkgs.bash)
hashedPassword = "$6$..."; # Hashed password (see below)
# inventory.nix
nix-desktop = {
devices = 3;
overrides.athenix.users.developer.enable = true;
};
```
# === SSH Access ===
opensshKeys = [ # SSH public keys
"ssh-ed25519 AAAA... user@host"
"ssh-rsa AAAA... user@otherhost"
];
### 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 Configuration ===
external = builtins.fetchGit { ... }; # External user module (see below)
# === Theme Integration ===
Generated
+17 -93
View File
@@ -115,11 +115,11 @@
]
},
"locked": {
"lastModified": 1766150702,
"narHash": "sha256-P0kM+5o+DKnB6raXgFEk3azw8Wqg5FL6wyl9jD+G5a4=",
"lastModified": 1765794845,
"narHash": "sha256-YD5QWlGnusNbZCqR3pxG8tRxx9yUXayLZfAJRWspq2s=",
"owner": "nix-community",
"repo": "disko",
"rev": "916506443ecd0d0b4a0f4cf9d40a3c22ce39b378",
"rev": "7194cfe5b7a3660726b0fe7296070eaef601cae9",
"type": "github"
},
"original": {
@@ -159,26 +159,6 @@
}
},
"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": {
"nixpkgs-lib": [
"lazyvim-nixvim",
@@ -239,24 +219,6 @@
"inputs": {
"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": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
@@ -356,11 +318,11 @@
]
},
"locked": {
"lastModified": 1767910483,
"narHash": "sha256-MOU5YdVu4DVwuT5ztXgQpPuRRBjSjUGIdUzOQr9iQOY=",
"lastModified": 1765979862,
"narHash": "sha256-/r9/1KamvbHJx6I40H4HsSXnEcBAkj46ZwibhBx9kg0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "82fb7dedaad83e5e279127a38ef410bcfac6d77c",
"rev": "d3135ab747fd9dac250ffb90b4a7e80634eacbe9",
"type": "github"
},
"original": {
@@ -424,7 +386,7 @@
},
"lazyvim-nixvim": {
"inputs": {
"flake-parts": "flake-parts_2",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs",
"nixvim": "nixvim"
},
@@ -502,11 +464,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1767185284,
"narHash": "sha256-ljDBUDpD1Cg5n3mJI81Hz5qeZAwCGxon4kQW3Ho3+6Q=",
"lastModified": 1764440730,
"narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "40b1a28dce561bea34858287fbb23052c3ee63fe",
"rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3",
"type": "github"
},
"original": {
@@ -556,11 +518,11 @@
},
"nixpkgs-old-kernel": {
"locked": {
"lastModified": 1767313136,
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
"lastModified": 1765687488,
"narHash": "sha256-7YAJ6xgBAQ/Nr+7MI13Tui1ULflgAdKh63m1tfYV7+M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
"rev": "d02bcc33948ca19b0aaa0213fe987ceec1f4ebe1",
"type": "github"
},
"original": {
@@ -572,11 +534,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1768242861,
"narHash": "sha256-F4IIxa5xDHjtrmMcayM8lHctUq1oGltfBQu2+oqDWP4=",
"lastModified": 1765838191,
"narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1327e798cb055f96f92685df444e9a2c326ab5ed",
"rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6",
"type": "github"
},
"original": {
@@ -646,7 +608,6 @@
"inputs": {
"agenix": "agenix",
"disko": "disko",
"flake-parts": "flake-parts",
"home-manager": "home-manager_2",
"lazyvim-nixvim": "lazyvim-nixvim",
"nixos-generators": "nixos-generators",
@@ -654,7 +615,6 @@
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_2",
"nixpkgs-old-kernel": "nixpkgs-old-kernel",
"usda-vision": "usda-vision",
"vscode-server": "vscode-server"
}
},
@@ -739,21 +699,6 @@
"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": {
"inputs": {
"nixpkgs": [
@@ -776,30 +721,9 @@
"type": "github"
}
},
"usda-vision": {
"inputs": {
"flake-utils": "flake-utils_3",
"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",
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixpkgs"
]
+37 -31
View File
@@ -4,7 +4,7 @@
# ============================================================================
# This file defines the inputs (dependencies) and outputs (configurations)
# for Athenix. It ties together the hardware, software, and user
# configurations into deployable systems using flake-parts.
# configurations into deployable systems.
inputs = {
# Core NixOS package repository (Release 25.11)
@@ -13,12 +13,6 @@
# Older kernel packages for Surface compatibility if needed
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 = {
url = "github:nix-community/home-manager/release-25.11";
@@ -59,37 +53,49 @@
url = "github:nix-community/NixOS-WSL/main";
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 =
inputs@{ self, flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
# Support all common systems
systems = [
inputs@{
self,
nixpkgs,
nixpkgs-old-kernel,
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"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
in
{
# Formatter for 'nix fmt'
formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style);
# Import flake-parts modules
imports = [
./parts/formatter.nix
./parts/lib.nix
./parts/nixos-configurations.nix
./parts/nixos-modules.nix
./parts/packages.nix
./parts/templates.nix
./parts/docs.nix
./inventory.nix
./users.nix
];
# Generate NixOS configurations from hosts/default.nix
nixosConfigurations = hosts.nixosConfigurations;
# Expose artifacts to all systems, but they are always built for x86_64-linux
packages = forAllSystems (_: artifacts);
# Expose modules for external use
nixosModules = import ./installer/modules.nix { inherit inputs; };
# Templates for external configurations
templates = import ./templates;
};
}
-64
View File
@@ -1,64 +0,0 @@
# ============================================================================
# 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 = lib.mkDefault ''
SuspendState=freeze
HibernateDelaySec=30m
'';
};
}
-196
View File
@@ -1,196 +0,0 @@
# ============================================================================
# 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"
];
}
];
}
-227
View File
@@ -1,227 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,132 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,151 +0,0 @@
{
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;
};
};
}
+189
View File
@@ -0,0 +1,189 @@
# ============================================================================
# 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?
}
];
}
+47
View File
@@ -0,0 +1,47 @@
# ============================================================================
# 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;
}
+75 -100
View File
@@ -1,25 +1,36 @@
{
inputs,
lib,
config,
self ? null,
users ? { },
hosts ? import ../inventory.nix,
...
}:
# ============================================================================
# Fleet Generator
# Host Generator
# ============================================================================
# This file contains the logic to generate NixOS configurations for all hosts
# defined in inventory.nix. It supports both hostname-based and count-based
# configurations with flexible type associations.
#
# 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
# 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
nixpkgs = inputs.nixpkgs;
lib = nixpkgs.lib;
# Helper to create a single NixOS system configuration
mkHost =
{
@@ -27,42 +38,13 @@ let
system ? "x86_64-linux",
hostType,
configOverrides ? { },
externalModuleThunk ? null,
externalModulePath ? null,
}:
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
accounts = config.athenix.users or { };
pkgs = nixpkgs.legacyPackages.${system};
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.athenix.users or { };
# Build a map of user names to their nixos module paths (if they exist)
# We'll use this to conditionally import modules based on user.enable
@@ -71,19 +53,10 @@ let
name: user:
if (user ? external && user.external != null) then
let
# Resolve external path (lazy fetchGit if needed)
externalPath =
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
if builtins.isAttrs user.external && user.external ? outPath then
user.external.outPath
else
# Direct path
user.external;
nixosModulePath = externalPath + "/nixos.nix";
in
@@ -121,6 +94,14 @@ let
}
) 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
externalPathModule =
if externalModulePath != null then import externalModulePath { inherit inputs; } else { };
@@ -148,35 +129,19 @@ let
];
};
# Hardware-specific external modules
hwSpecificModules =
lib.optional (hostType == "nix-lxc")
"${inputs.nixpkgs.legacyPackages.${system}.path}/nixos/modules/virtualisation/proxmox-lxc.nix";
allModules =
userNixosModules
++ [
./common.nix
typeModule
overrideModule
{ 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;
in
{
system = lib.nixosSystem {
inherit system;
specialArgs = {
inputs = if self != null then inputs // { inherit self; } else inputs;
};
specialArgs = { inherit inputs; };
modules = allModules;
};
modules = allModules;
@@ -188,6 +153,8 @@ let
let
hostType = config.type or prefix;
system = config.system or "x86_64-linux";
devices = config.devices or { };
hasCount = config ? count;
# Helper to generate hostname from prefix and suffix
# Numbers get no dash: "nix-surface1", "nix-surface2"
@@ -225,38 +192,48 @@ let
lib.mapAttrsToList (
deviceKey: deviceConfig:
let
# Check if deviceConfig has an 'external' field for lazy evaluation
hasExternalField = builtins.isAttrs deviceConfig && deviceConfig ? external;
# Check if deviceConfig is a path/derivation (from fetchGit, fetchurl, etc.)
# fetchGit/fetchTarball return an attrset with outPath attribute
isExternalModule =
(builtins.isPath deviceConfig)
|| (builtins.isString deviceConfig && lib.hasPrefix "/" deviceConfig)
|| (lib.isDerivation deviceConfig)
|| (builtins.isAttrs deviceConfig && deviceConfig ? outPath);
# Extract external module spec (don't evaluate fetchGit yet!)
externalModuleThunk =
if hasExternalField then
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
ext
# Extract the actual path from fetchGit/fetchTarball results
extractedPath =
if builtins.isAttrs deviceConfig && deviceConfig ? outPath then
deviceConfig.outPath
else
null;
deviceConfig;
# Remove 'external' from config to avoid conflicts
cleanDeviceConfig =
if hasExternalField then lib.removeAttrs deviceConfig [ "external" ] else deviceConfig;
# 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
mergedConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) cleanDeviceConfig;
# Merge: base config -> overrides -> device-specific config (only if not external module)
mergedConfig =
if isExternalModule then
actualConfig
else
lib.recursiveUpdate (lib.recursiveUpdate baseConfig overrides) deviceConfig;
# Check useHostPrefix from the merged config
usePrefix = mergedConfig.athenix.host.useHostPrefix or true;
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
{
name = hostName;
@@ -265,7 +242,7 @@ let
hostName
system
hostType
externalModuleThunk
externalModulePath
;
configOverrides = mergedConfig;
};
@@ -312,12 +289,10 @@ let
{ };
in
lib.recursiveUpdate deviceHosts countHosts
);
fleetData = config.athenix.fleet;
) hosts;
# Flatten the nested structure
allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues (processInventory fleetData));
allHosts = lib.foldl' lib.recursiveUpdate { } (lib.attrValues processInventory);
in
{
nixosConfigurations = lib.mapAttrs (n: v: v.system) allHosts;
+52
View File
@@ -0,0 +1,52 @@
# ============================================================================
# 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";
}
+68
View File
@@ -0,0 +1,68 @@
# ============================================================================
# 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";
}
+65
View File
@@ -0,0 +1,65 @@
# ============================================================================
# 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";
}
+62
View File
@@ -0,0 +1,62 @@
# ============================================================================
# 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";
}
+70
View File
@@ -0,0 +1,70 @@
# ============================================================================
# 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
}
+10 -32
View File
@@ -4,46 +4,27 @@
# Configuration for NixOS running in WSL2 on Windows.
# Integrates with nixos-wsl for WSL-specific functionality.
{ inputs, ... }:
{
lib,
config,
...
}:
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.";
};
imports = [
(import ../common.nix { inherit inputs; })
inputs.nixos-wsl.nixosModules.default
inputs.vscode-server.nixosModules.default
];
# WSL user option (at module level, not inside config)
# ========== Options ==========
options.athenix.host.wsl.user = lib.mkOption {
type = lib.types.str;
default = "engr-ugaif";
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";
description = "The default user to log in as in WSL.";
};
config = mkIf cfg.enable {
config = {
# ========== WSL Configuration ==========
wsl.enable = true;
# Use forUser if set, otherwise fall back to wsl.user option
@@ -52,7 +33,7 @@ in
# ========== Software Profile ==========
athenix.sw.enable = lib.mkDefault true;
athenix.sw.headless.enable = lib.mkDefault true;
athenix.sw.type = lib.mkDefault "headless";
# ========== Remote Development ==========
services.vscode-server.enable = true;
@@ -69,8 +50,5 @@ in
# Provide dummy values for required options from boot.nix
athenix.host.filesystem.device = "/dev/null";
athenix.host.filesystem.swapSize = "0G";
# WSL doesn't use installer ISOs
athenix.host.buildMethods = lib.mkDefault [ ];
};
}
+276
View File
@@ -0,0 +1,276 @@
{
pkgs,
config,
lib,
inputs,
...
}:
# ============================================================================
# User Configuration Module
# ============================================================================
# This module defines the schema for user accounts and handles their creation.
# It bridges the gap between the data in 'users.nix' and the actual NixOS
# and Home Manager configuration.
let
# Load users.nix to get account definitions
usersData = import ../users.nix { inherit pkgs; };
accounts = usersData.athenix.users or { };
# Helper: Resolve external module path from fetchGit/fetchTarball/path
resolveExternalPath =
external:
if external == null then
null
else if builtins.isAttrs external && external ? outPath then
external.outPath
else
external;
# Helper: Check if path exists and is valid
isValidPath =
path:
path != null
&& (builtins.isPath path || (builtins.isString path && lib.hasPrefix "/" path))
&& builtins.pathExists path;
# Extract athenix.users options from external user.nix modules
# First, build a cache of options per user from their external user.nix (if any).
externalUserModuleOptions = lib.genAttrs (lib.attrNames accounts) (
name:
let
user = accounts.${name};
externalPath = resolveExternalPath (user.external or null);
userNixPath = if externalPath != null then externalPath + "/user.nix" else null;
in
if isValidPath userNixPath then
let
# Import and evaluate the module with minimal args
outerModule = import userNixPath { inherit inputs; };
evaluatedModule = outerModule {
config = { };
inherit lib pkgs;
osConfig = null;
};
# Extract just the athenix.users.<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
View File
@@ -1,23 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,73 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,88 +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.
{
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;
};
}
-130
View File
@@ -1,130 +0,0 @@
# ============================================================================
# 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
"mem_sleep_default=deep" # Use deep sleep (S3) by default
];
# ========== 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
boot.resumeDevice = "/dev/nvme0n1p2"; # Resume from swap partition
athenix.host.buildMethods = lib.mkDefault [ "installer-iso" ];
# ========== Power Management ==========
systemd.sleep.extraConfig = ''
SuspendState=mem
HibernateDelaySec=30m
'';
powerManagement.enable = lib.mkDefault true;
powerManagement.powertop.enable = lib.mkDefault true;
services = {
upower.enable = lib.mkDefault true;
logind.settings = {
Login = {
HandleLidSwitch = "suspend";
HandleLidSwitchExternalPower = "suspend";
HandleLidSwitchDocked = "ignore";
PowerKey = "hibernate";
PowerKeyLongPress = "poweroff";
};
};
power-profiles-daemon.enable = false;
tlp = {
enable = true;
settings = {
CPU_SCALING_GOVERNOR_ON_AC = "performance";
CPU_SCALING_GOVERNOR_ON_BAT = "powersave";
CPU_ENERGY_PERF_POLICY_ON_BAT = "power";
CPU_ENERGY_PERF_POLICY_ON_AC = "performance";
CPU_MIN_PERF_ON_AC = 0;
CPU_MAX_PERF_ON_AC = 100;
CPU_MIN_PERF_ON_BAT = 0;
CPU_MAX_PERF_ON_BAT = 20;
#Optional helps save long term battery health
START_CHARGE_THRESH_BAT0 = 40; # 40 and below it starts to charge
STOP_CHARGE_THRESH_BAT0 = 80; # 80 and above it stops charging
};
};
auto-cpufreq = {
enable = lib.mkDefault true;
settings = {
battery = {
governor = "powersave";
turbo = "never";
};
charger = {
governor = "performance";
turbo = "auto";
};
};
};
};
athenix.sw.enable = lib.mkDefault true;
athenix.sw.desktop.enable = lib.mkDefault true;
};
}
-79
View File
@@ -1,79 +0,0 @@
# ============================================================================
# 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;
};
}
-88
View File
@@ -1,88 +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.
{
config,
lib,
pkgs,
modulesPath,
inputs,
...
}:
with lib;
let
cfg = config.athenix.hw.nix-surface;
in
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
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
# Evaluated lazily - only when this module is enabled
boot.kernelPackages = lib.mkForce (
(inputs.nixpkgs-old-kernel.lib.nixosSystem {
system = pkgs.stdenv.hostPlatform.system;
modules = [ inputs.nixos-hardware.nixosModules.microsoft-surface-go ];
}).config.boot.kernelPackages
);
# ========== 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
};
}
-71
View File
@@ -1,71 +0,0 @@
# ============================================================================
# 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.bigscreen.enable = lib.mkDefault true;
};
}
+1 -1
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 (`hw/nix-lxc.nix`) should include:
Your host type configuration (`hosts/types/nix-lxc.nix`) should include:
```nix
{
+11 -19
View File
@@ -1,9 +1,8 @@
{
inputs,
fleet,
hosts,
self,
system,
users ? { },
}:
# 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>`
@@ -19,7 +18,7 @@ let
hostName:
let
targetConfig = self.nixosConfigurations.${hostName}.config;
targetSystemBuild = targetConfig.system.build;
targetSystem = targetConfig.system.build.toplevel;
diskoScript = targetConfig.system.build.diskoScript;
in
nixpkgs.lib.nixosSystem {
@@ -28,9 +27,8 @@ let
inherit
inputs
hostName
targetSystemBuild
targetSystem
diskoScript
users
;
hostPlatform = system;
};
@@ -47,10 +45,7 @@ let
nixos-generators.nixosGenerate {
inherit system;
specialArgs = { inherit inputs; };
modules = fleet.modules.${hostName} ++ [
{
config.athenix.users = lib.mapAttrs (_: user: lib.mapAttrs (_: lib.mkDefault) user) users;
}
modules = hosts.modules.${hostName} ++ [
{
disko.enableConfig = lib.mkForce false;
services.upower.enable = lib.mkForce false;
@@ -66,11 +61,8 @@ let
nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = { inherit inputs; };
modules = fleet.modules.${hostName} ++ [
modules = hosts.modules.${hostName} ++ [
"${nixpkgs}/nixos/modules/installer/netboot/netboot.nix"
{
config.athenix.users = lib.mapAttrs (_: user: lib.mapAttrs (_: lib.mkDefault) user) users;
}
{
disko.enableConfig = lib.mkForce false;
services.upower.enable = lib.mkForce false;
@@ -78,14 +70,14 @@ let
];
};
hostNames = builtins.attrNames fleet.nixosConfigurations;
hostNames = builtins.attrNames hosts.nixosConfigurations;
# Generate installer ISOs for hosts that have "installer-iso" in their buildMethods
installerPackages = lib.listToAttrs (
lib.concatMap (
name:
let
cfg = fleet.nixosConfigurations.${name};
cfg = hosts.nixosConfigurations.${name};
in
if lib.elem "installer-iso" cfg.config.athenix.host.buildMethods then
[
@@ -104,7 +96,7 @@ let
lib.concatMap (
name:
let
cfg = fleet.nixosConfigurations.${name};
cfg = hosts.nixosConfigurations.${name};
in
if lib.elem "iso" cfg.config.athenix.host.buildMethods then
[
@@ -123,7 +115,7 @@ let
lib.concatMap (
name:
let
cfg = fleet.nixosConfigurations.${name};
cfg = hosts.nixosConfigurations.${name};
in
if lib.elem "ipxe" cfg.config.athenix.host.buildMethods then
[
@@ -153,7 +145,7 @@ let
lib.concatMap (
name:
let
cfg = fleet.nixosConfigurations.${name};
cfg = hosts.nixosConfigurations.${name};
in
if lib.elem "lxc" cfg.config.athenix.host.buildMethods then
[
@@ -172,7 +164,7 @@ let
lib.concatMap (
name:
let
cfg = fleet.nixosConfigurations.${name};
cfg = hosts.nixosConfigurations.${name};
in
if lib.elem "proxmox" cfg.config.athenix.host.buildMethods then
[
+7 -18
View File
@@ -2,10 +2,13 @@
# It is intended to be used in an installation ISO.
# It expects `targetSystem` (the closure to install) and `diskoScript` (the partitioning script) to be passed as arguments.
{
config,
lib,
pkgs,
inputs,
hostName,
hostPlatform,
targetSystemBuild,
targetSystem,
diskoScript,
...
}:
@@ -14,21 +17,11 @@
pkgs.git
pkgs.bashInteractive
pkgs.curl
targetSystemBuild.toplevel
targetSystem
];
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 = {
description = "Automatic NixOS install for ${hostName}";
after = [
@@ -51,12 +44,8 @@
echo ">>> Running disko script..."
${diskoScript}
echo ">>> Setting up NixOS..."
nixos-install \
--system ${targetSystemBuild.toplevel} \
--no-root-passwd \
--no-channel-copy \
--substituters ""
echo ">>> Running nixos-install..."
nixos-install --no-root-passwd --system ${targetSystem}
echo ">>> Done. Rebooting."
systemctl reboot
+32 -18
View File
@@ -6,26 +6,40 @@
#
# Usage in another flake:
# # Full host type configurations (includes hardware + software + system config)
# inputs.athenix.nixosModules.nix-desktop
# inputs.athenix.nixosModules.nix-laptop
# inputs.nixos-systems.nixosModules.nix-desktop
# inputs.nixos-systems.nixosModules.nix-laptop
#
# # Software-only configuration (for custom hardware setups)
# inputs.athenix.nixosModules.sw
# # Software-only configurations (for custom hardware setups)
# # Note: These include theme.nix in home-manager.sharedModules automatically
# 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 }:
# 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
{
# Software configuration module - main module with all athenix.sw options
# Use athenix.sw.<type>.enable to enable software profiles: desktop, tablet-kiosk, headless, stateless-kiosk, builders
hw = hostTypes;
sw =
{
inputs,
...
}@args:
(import ../sw/default.nix (args // { inherit inputs; }));
# ========== Full Host Type Modules ==========
# Complete system configurations including hardware, boot, and software
nix-desktop = import ../hosts/types/nix-desktop.nix { inherit inputs; }; # Desktop workstations
nix-laptop = import ../hosts/types/nix-laptop.nix { inherit inputs; }; # Laptop systems
nix-surface = import ../hosts/types/nix-surface.nix { inherit inputs; }; # Surface tablets
nix-lxc = import ../hosts/types/nix-lxc.nix { inherit inputs; }; # Proxmox containers
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
# ========== 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; }));
}
+162 -144
View File
@@ -1,157 +1,175 @@
# ============================================================================
# Fleet Inventory
# ============================================================================
# Top-level keys are ALWAYS hostname prefixes. Actual hostnames are generated
# from the devices map or count.
#
# Hostname generation rules:
# - Numeric suffixes: no dash (e.g., "nix-surface1", "nix-surface2")
# - Non-numeric suffixes: add dash (e.g., "nix-surface-alpha", "nix-surface-beta")
# - Set athenix.host.useHostPrefix = false to use suffix as full hostname
#
# Format:
# "prefix" = {
# type = "nix-desktop"; # Optional: defaults to prefix name
# system = "x86_64-linux"; # Optional: default is x86_64-linux
#
# # Option 1: Simple count (quick syntax)
# devices = 5; # Creates: prefix1, prefix2, ..., prefix5
#
# # Option 2: Explicit count
# count = 5; # Creates: prefix1, prefix2, ..., prefix5
#
# # Option 3: Default count (for groups with mixed devices)
# defaultCount = 3; # Creates default numbered hosts
#
# # Option 4: Named device configurations
# devices = {
# "1" = { ... }; # Creates: prefix1
# "alpha" = { ... }; # Creates: prefix-alpha
# "custom" = { # Creates: custom (no prefix)
# athenix.host.useHostPrefix = false;
# };
# };
#
# # Common config for all devices in this group
# overrides = {
# athenix.users.user1.enable = true; # Applied to all devices in this group
# # ... any other config
# };
# };
#
# Convenience options:
# athenix.forUser = "username"; # Automatically enables user (sets athenix.users.username.enable = true)
#
# External modules (instead of config):
# Device values can be a config attrset with an optional 'external' field:
# 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:
# "lab" = { devices = 3; }; # Quick: lab1, lab2, lab3
# "lab" = { count = 3; }; # Same as above
# "kiosk" = {
# defaultCount = 2; # kiosk1, kiosk2 (default)
# devices."special" = {}; # kiosk-special (custom)
# };
# "laptop" = {
# devices = 5;
# overrides.athenix.users.student.enable = true; # All 5 laptops get this user
# };
# "wsl" = {
# devices."alice".athenix.forUser = "alice123"; # Sets up for user alice123
# };
# "external" = {
# devices."remote".external = { url = "..."; rev = "..."; }; # External module via Git (lazy)
# url = "https://github.com/example/config";
# rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014";
# };
# };
{ ... }:
{
athenix.fleet = {
# ========== Lab Laptops ==========
# Creates: nix-laptop1, nix-laptop2
# Both get hdh20267 user via overrides
nix-laptop = {
devices = 2;
overrides.athenix.users.hdh20267.enable = true;
};
# ============================================================================
# Fleet Inventory
# ============================================================================
# Top-level keys are ALWAYS hostname prefixes. Actual hostnames are generated
# from the devices map or count.
#
# Hostname generation rules:
# - Numeric suffixes: no dash (e.g., "nix-surface1", "nix-surface2")
# - Non-numeric suffixes: add dash (e.g., "nix-surface-alpha", "nix-surface-beta")
# - Set athenix.host.useHostPrefix = false to use suffix as full hostname
#
# Format:
# "prefix" = {
# type = "nix-desktop"; # Optional: defaults to prefix name
# system = "x86_64-linux"; # Optional: default is x86_64-linux
#
# # Option 1: Simple count (quick syntax)
# devices = 5; # Creates: prefix1, prefix2, ..., prefix5
#
# # Option 2: Explicit count
# count = 5; # Creates: prefix1, prefix2, ..., prefix5
#
# # Option 3: Default count (for groups with mixed devices)
# defaultCount = 3; # Creates default numbered hosts
#
# # Option 4: Named device configurations
# devices = {
# "1" = { ... }; # Creates: prefix1
# "alpha" = { ... }; # Creates: prefix-alpha
# "custom" = { # Creates: custom (no prefix)
# athenix.host.useHostPrefix = false;
# };
# };
#
# # Common config for all devices in this group
# overrides = {
# athenix.users.user1.enable = true; # Applied to all devices in this group
# # ... any other config
# };
# };
#
# Convenience options:
# athenix.forUser = "username"; # Automatically enables user (sets athenix.users.username.enable = true)
#
# External modules (instead of config):
# Device values can be either a config attrset OR a fetchGit/fetchurl call
# that points to an external Nix module. The module will be imported and evaluated.
#
# Examples:
# "lab" = { devices = 3; }; # Quick: lab1, lab2, lab3
# "lab" = { count = 3; }; # Same as above
# "kiosk" = {
# defaultCount = 2; # kiosk1, kiosk2 (default)
# devices."special" = {}; # kiosk-special (custom)
# };
# "laptop" = {
# devices = 5;
# overrides.athenix.users.student.enable = true; # All 5 laptops get this user
# };
# "wsl" = {
# devices."alice".athenix.forUser = "alice123"; # Sets up for user alice123
# };
# "external" = {
# devices."remote" = builtins.fetchGit { # External module via Git
# url = "https://github.com/example/config";
# rev = "e1ccd7cc3e709afe4f50b0627e1c4bde49165014";
# };
# }; # ========== Lab Laptops ==========
# Creates: nix-laptop1, nix-laptop2
# Both get hdh20267 user via overrides
nix-laptop = {
devices = 2;
overrides.athenix.users.hdh20267.enable = true;
};
# ========== Desktop ==========
# Creates: nix-desktop1
nix-desktop = {
devices = 1;
};
# ========== Desktop ==========
# Creates: nix-desktop1
nix-desktop = {
devices = 1;
};
# ========== Surface Tablets (Kiosk Mode) ==========
# Creates: nix-surface1 (custom), nix-surface2, nix-surface3 (via defaultCount)
nix-surface = {
defaultCount = 3;
devices = {
"1".athenix.sw.tablet-kiosk.kioskUrl = "https://google.com";
};
overrides = {
athenix.sw.tablet-kiosk.kioskUrl = "https://yahoo.com";
};
# ========== Surface Tablets (Kiosk Mode) ==========
# Creates: nix-surface1 (custom), nix-surface2, nix-surface3 (via defaultCount)
nix-surface = {
defaultCount = 3;
devices = {
"1".athenix.sw.kioskUrl = "https://google.com";
};
overrides = {
athenix.sw.kioskUrl = "https://yahoo.com";
};
};
# ========== LXC Containers ==========
# Creates: nix-builder (without lxc prefix)
nix-lxc = {
devices = {
"nix-builder" = {
# Gitea Actions self-hosted runner configuration
athenix.sw = {
headless.enable = true;
builders = {
enable = true;
giteaRunner = {
enable = true;
url = "https://git.factory.uga.edu";
# Token file must be created manually at this path with a Gitea runner token
# Generate in repository settings: Settings > Actions > Runners > Create new Runner
# echo "TOKEN=YOUR_TOKEN_HERE" | sudo tee /var/lib/gitea-runner-token > /dev/null
tokenFile = "/var/lib/gitea-runner-token";
# Labels to identify this runner in workflows
extraLabels = [
"self-hosted"
"nix-builder"
];
# Runner service name
name = "athenix";
};
};
# ========== LXC Containers ==========
# Creates: nix-builder (without lxc prefix)
nix-lxc = {
devices = {
"nix-builder" = {
# Gitea Actions self-hosted runner configuration
athenix.sw = {
type = [
"headless"
"builders"
];
builders.giteaRunner = {
enable = true;
url = "https://git.factory.uga.edu";
# Token file must be created manually at this path with a Gitea runner token
# Generate in repository settings: Settings > Actions > Runners > Create new Runner
# echo "TOKEN=YOUR_TOKEN_HERE" | sudo tee /var/lib/gitea-runner-token > /dev/null
tokenFile = "/var/lib/gitea-runner-token";
# Labels to identify this runner in workflows
extraLabels = [
"self-hosted"
"nix-builder"
];
# Runner service name
name = "athenix";
};
};
"usda-dash".external = {
url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git";
rev = "ce2700b0196e106f7c013bbcee851a5f96b146a3";
submodules = false;
};
};
overrides = {
athenix.host.useHostPrefix = false;
"usda-dash" = builtins.fetchGit {
url = "https://git.factory.uga.edu/MODEL/usda-dash-config.git";
rev = "a52962b08d21a4c0b1f4f07dcb55146f2078a5d8";
submodules = true;
};
};
# ========== WSL Instances ==========
# Creates: nix-wsl-alireza
nix-wsl = {
devices = {
"alireza".athenix.forUser = "sv22900";
};
overrides = {
athenix.host.useHostPrefix = false;
};
# ========== ZimaBoard Desktops ==========
# Creates: nix-zima1, nix-zima2, nix-zima3
nix-zima.devices = 3;
# ========== Ephemeral/Netboot System ==========
# Creates: nix-ephemeral1
nix-ephemeral.devices = 1;
};
# ========== WSL Instances ==========
# Creates: nix-wsl-alireza
nix-wsl = {
devices = {
"alireza".athenix.forUser = "sv22900";
};
};
# ========== Ephemeral/Netboot System ==========
# Creates: nix-ephemeral1
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
View File
@@ -1,8 +0,0 @@
{
lib,
...
}:
{
mkFleet = import ./mkFleet.nix;
macCaseBuilder = import ./macCaseBuilder.nix { inherit lib; };
}
-33
View File
@@ -1,33 +0,0 @@
{ 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
View File
@@ -1,18 +0,0 @@
# 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
View File
@@ -1,194 +0,0 @@
# 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
View File
@@ -1,9 +0,0 @@
# Formatter configuration for flake-parts
{ ... }:
{
perSystem =
{ pkgs, ... }:
{
formatter = pkgs.nixfmt-rfc-style;
};
}
-8
View File
@@ -1,8 +0,0 @@
# Library functions for flake-parts
{ inputs, ... }:
{
flake.lib = import ../lib {
inherit inputs;
lib = inputs.nixpkgs.lib;
};
}
-28
View File
@@ -1,28 +0,0 @@
# 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
View File
@@ -1,5 +0,0 @@
# Expose host type modules and installer modules for external use
{ inputs, ... }:
{
flake.nixosModules = import ../installer/modules.nix { inherit inputs; };
}
-37
View File
@@ -1,37 +0,0 @@
# 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
View File
@@ -1,5 +0,0 @@
# Templates for external configurations
{ ... }:
{
flake.templates = import ../templates;
}
-24
View File
@@ -1,24 +0,0 @@
{ pkgs, ... }:
{
plasma-bigscreen = pkgs.callPackage ./plasma-bigscreen.nix {
inherit (pkgs.kdePackages)
kcmutils
kdeclarative
ki18n
kio
knotifications
kwayland
kwindowsystem
mkKdeDerivation
qtmultimedia
plasma-workspace
bluez-qt
qtwebengine
plasma-nano
plasma-nm
milou
kscreen
kdeconnect-kde
;
};
}
-112
View File
@@ -1,112 +0,0 @@
# NOTE: from https://github.com/NixOS/nixpkgs/pull/428353#issuecomment-3498917203
# more info in tv.nix nixos config
{
mkKdeDerivation,
lib,
fetchFromGitLab,
pkg-config,
ki18n,
kdeclarative,
kcmutils,
knotifications,
kio,
kwayland,
kwindowsystem,
plasma-workspace,
qtmultimedia,
bluez-qt,
qtwebengine,
plasma-nano,
plasma-nm,
milou,
kscreen,
kdeconnect-kde,
sdl3,
libcec ? null
}:
mkKdeDerivation {
pname = "plasma-bigscreen";
version = "unstable-2026-02-13";
src = fetchFromGitLab {
domain = "invent.kde.org";
owner = "plasma";
repo = "plasma-bigscreen";
rev = "1db19cdfc2ac1653cb129f403702addec2caf4c7";
hash = "sha256-dfsR20dY1jtLmEpGRxvp9xwOHJFn3mVw+1RUGBJRLpQ=";
};
extraNativeBuildInputs = [
pkg-config
];
buildInputs = [
ki18n
kdeclarative
kcmutils
knotifications
kio
kwayland
kwindowsystem
plasma-workspace
qtmultimedia
bluez-qt
qtwebengine
plasma-nano
plasma-nm
milou
kscreen
kdeconnect-kde
sdl3
] ++ lib.optionals (libcec != null) [ libcec ];
postPatch = ''
substituteInPlace bin/plasma-bigscreen-wayland.in \
--replace-fail @KDE_INSTALL_FULL_LIBEXECDIR@ "${plasma-workspace}/libexec"
# Plasma version numbers are required to match, but we are building an
# unreleased package against a stable Plasma release.
substituteInPlace CMakeLists.txt \
--replace-fail 'set(PROJECT_VERSION "6.5.80")' 'set(PROJECT_VERSION "${plasma-workspace.version}")'
# Fix for Qt 6.10+ which requires explicit find_package of private targets
# Reference: https://github.com/NixOS/nixpkgs/pull/461599/changes
substituteInPlace CMakeLists.txt \
--replace-fail \
'find_package(Qt6 ''${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Quick
Core
Qml
DBus
Network
Multimedia
WebEngineCore
WebEngineQuick
WaylandClient
)' \
'find_package(Qt6 ''${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Quick
Core
Qml
QmlPrivate
DBus
Network
Multimedia
WebEngineCore
WebEngineQuick
WaylandClient
)'
'';
preFixup = ''
wrapQtApp $out/bin/plasma-bigscreen-wayland
'';
passthru.providedSessions = [
"plasma-bigscreen-wayland"
];
meta = {
license = lib.licenses.gpl2Plus;
};
}
-187
View File
@@ -1,187 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,174 +0,0 @@
# 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
View File
@@ -1,250 +0,0 @@
# 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/)
-1
View File
@@ -1 +0,0 @@
age14emzyraytqzmre58c452t07rtcj87cwqwmd9z3gj7upugtxk8s3sda5tju
BIN
View File
Binary file not shown.
-121
View File
@@ -1,121 +0,0 @@
#!/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
@@ -1 +0,0 @@
age1udmpqkedupd33gyut85ud3nvppydzeg04kkuneymkvxcjjej244s4v8xjc
-10
View File
@@ -1,10 +0,0 @@
# 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";
};
}
@@ -1 +0,0 @@
age1u5tczg2sx90n03uuz9h549f4h3h7sq5uehhqpampzs7vj8ew7y6s2mjwz0
-176
View File
@@ -1,176 +0,0 @@
# ============================================================================
# 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
View File
@@ -1,36 +0,0 @@
#!/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"
-8
View File
@@ -1,8 +0,0 @@
# Host-specific secret configuration for usda-dash
{
usda-vision-azure-env = {
mode = "0600";
owner = "root";
group = "root";
};
}
@@ -1 +0,0 @@
age1lr24yvk7rdfh5wkle7h32jpxqxm2e8vk85mc4plv370u2sh4yfmszaaejx
@@ -1 +0,0 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHI73LOEK2RgfjhZWpryntlLbx0LouHrhQ6v0vZu4Etr root@usda-dash
Binary file not shown.
-63
View File
@@ -1,63 +0,0 @@
# ============================================================================
# Bigscreen Software Configuration
# ============================================================================
# Imports desktop-specific programs and services (KDE Plasma, CUPS, etc.)
{
config,
lib,
pkgs,
inputs,
...
}:
with lib;
let
cfg = config.athenix.sw.bigscreen;
in
{
options.athenix.sw.bigscreen = mkOption {
type = lib.types.submodule {
options = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable full desktop environment with KDE Plasma 6 Bigscreen.
Includes:
- KDE Plasma 6 desktop with SDDM display manager
- Printing and scanning support (CUPS)
- Virtualization (libvirt, virt-manager)
- Bluetooth and audio (PipeWire)
Recommended for: home theater PCs, media centers, and large-screen desktops.
'';
example = true;
};
};
};
default = { };
description = "Desktop environment configuration (KDE Plasma 6).";
};
config = mkIf cfg.enable (mkMerge [
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
-31
View File
@@ -1,31 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.athenix.sw;
basePackages = with pkgs; [
tmux
man
(chromium.override {
commandLineArgs = [ "--enable-features=TouchpadOverscrollHistoryNavigation" ];
})
lm_sensors
];
in
{
environment.systemPackages = subtractLists cfg.excludePackages (basePackages ++ cfg.extraPackages);
programs.mtr.enable = true;
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
};
programs.virt-manager.enable = true;
}
-74
View File
@@ -1,74 +0,0 @@
{
lib,
pkgs,
...
}:
let
xelpkgs = pkgs: import ../../pkgs pkgs;
bigscreenpkgs = xelpkgs pkgs;
in
{
athenix.sw.python.enable = lib.mkDefault true;
services.displayManager.sddm = {
enable = true;
theme = "breeze";
wayland.enable = true;
enableHidpi = true;
settings = {
Autologin = {
Session = "plasma-bigscreen-wayland";
User = "engr-ugaif";
};
};
};
services.desktopManager.plasma6.enable = true;
services.displayManager.sessionPackages = with bigscreenpkgs; [
plasma-bigscreen
];
services.printing.enable = true;
networking.networkmanager.enable = true;
services.pulseaudio.enable = false;
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
fonts.packages = with pkgs; [
nerd-fonts.fira-code
corefonts
noto-fonts
];
fonts.fontconfig = {
enable = true;
defaultFonts.monospace = [ "FiraCode Nerd Font Mono" ];
};
hardware.bluetooth.enable = true;
services.blueman.enable = true;
networking.firewall.enable = true;
services.flatpak.enable = true;
xdg.portal.enable = true;
xdg.portal.extraPortals = [ pkgs.kdePackages.xdg-desktop-portal-kde ];
virtualisation.libvirtd.enable = true;
services.thermald.enable = true;
services.fwupd.enable = true;
services.openssh = {
enable = true;
settings.PermitRootLogin = "no";
};
}
+18 -120
View File
@@ -11,123 +11,21 @@
...
}:
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 {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
lib.mkMerge [
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]
+3 -1
View File
@@ -1,6 +1,8 @@
{
config,
lib,
pkgs,
inputs,
...
}:
@@ -8,7 +10,7 @@ with lib;
let
cfg = config.athenix.sw;
basePackages = [
basePackages = with pkgs; [
# Build-related packages can be added here if needed
];
in
+150 -66
View File
@@ -10,99 +10,183 @@
# Software Module Entry Point
# ============================================================================
# This module manages the software configuration for the system. It provides
# enable options for each system type (desktop, headless, builders, etc.)
# that can be enabled independently or in combination. Each type is a proper
# NixOS submodule with its own enable flag and type-specific options.
# options to select the system type ('desktop' or 'kiosk') and handles
# the conditional importation of the appropriate sub-modules.
with lib;
let
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
{
imports = [
./python.nix
./ghostty.nix
./gc.nix
./updater.nix
./update-ref.nix
./secrets.nix
./desktop
./headless
./builders
./tablet-kiosk
./stateless-kiosk
./bigscreen
inputs.home-manager.nixosModules.home-manager
inputs.agenix.nixosModules.default
inputs.disko.nixosModules.disko
];
options.athenix.sw = {
enable = mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable standard workstation configuration with base packages.
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.
'';
};
enable = mkEnableOption "Standard Workstation Configuration";
type = mkOption {
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;
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 = lib.types.listOf lib.types.package;
type = types.listOf types.package;
default = [ ];
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 ]";
description = "Extra packages to install.";
};
excludePackages = mkOption {
type = lib.types.listOf lib.types.package;
type = types.listOf types.package;
default = [ ];
description = ''
Packages to exclude from the default package list.
Useful for removing unwanted default packages.
'';
example = lib.literalExpression "[ pkgs.htop ]";
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 = ''
Path to file containing Gitea runner token.
Generate in Gitea repository settings under Actions > Runners.
The token must have runner registration access.
'';
};
extraLabels = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Extra labels to identify this runner in workflows";
};
name = mkOption {
type = types.str;
default = "athenix";
description = "Name of the Gitea runner service";
};
};
};
};
default = { };
description = "Builder-specific configuration options";
};
};
config = mkIf cfg.enable {
# ========== System-Wide Configuration ==========
nixpkgs.config.allowUnfree = true;
config = mkIf cfg.enable (mkMerge [
{
# ========== System-Wide Configuration ==========
nixpkgs.config.allowUnfree = true;
# ========== Shell Configuration ==========
programs.zsh.enable = true;
programs.nix-ld.enable = true; # Allow running non-NixOS binaries
# ========== Shell Configuration ==========
programs.zsh.enable = true;
programs.nix-ld.enable = true; # Allow running non-NixOS binaries
# ========== Base Packages ==========
environment.systemPackages =
with pkgs;
subtractLists cfg.excludePackages [
htop # System monitor
binutils # Binary utilities
zsh # Z shell
git # Version control
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
];
};
# ========== Base Packages ==========
environment.systemPackages =
with pkgs;
subtractLists cfg.excludePackages [
htop # System monitor
binutils # Binary utilities
zsh # Z shell
git # Version control
oh-my-posh # Shell prompt theme
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
;
}
))
]);
}
+18 -53
View File
@@ -10,56 +10,21 @@
inputs,
...
}:
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 {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
lib.mkMerge [
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]
+1
View File
@@ -2,6 +2,7 @@
config,
lib,
pkgs,
inputs,
...
}:
+1
View File
@@ -1,4 +1,5 @@
{
config,
lib,
pkgs,
...
-75
View File
@@ -1,75 +0,0 @@
{
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;
};
}
+1 -7
View File
@@ -12,11 +12,7 @@
# It reconstructs the terminfo database from the provided definition and
# adds it to the system packages.
with lib;
let
cfg = config.athenix.sw;
ghostty-terminfo = pkgs.runCommand "ghostty-terminfo" { } ''
mkdir -p $out/share/terminfo
cat > ghostty.info <<'EOF'
@@ -105,7 +101,5 @@ let
'';
in
{
config = mkIf cfg.enable {
environment.systemPackages = [ ghostty-terminfo ];
};
environment.systemPackages = [ ghostty-terminfo ];
}
+18 -50
View File
@@ -11,53 +11,21 @@
...
}:
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 {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
lib.mkMerge [
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
]
+1
View File
@@ -2,6 +2,7 @@
config,
lib,
pkgs,
inputs,
...
}:
+3
View File
@@ -1,4 +1,7 @@
{
config,
lib,
pkgs,
...
}:
+3 -20
View File
@@ -18,27 +18,10 @@ let
cfg = config.athenix.sw.python;
in
{
options.athenix.sw.python = lib.mkOption {
type = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
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.
'';
};
};
options.athenix.sw.python = {
enable = mkEnableOption "Python development tools (pixi, uv)" // {
default = true;
};
default = { };
description = "Python development environment configuration.";
};
config = mkIf cfg.enable {
-230
View File
@@ -1,230 +0,0 @@
# ============================================================================
# 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}/";
};
}
+34 -80
View File
@@ -7,83 +7,37 @@
inputs,
...
}:
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 {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./net.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
lib.mkMerge [
(import ./kiosk-browser.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./net.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
]
+3 -2
View File
@@ -1,13 +1,14 @@
# This module configures Chromium for kiosk mode under Sway.
# It includes a startup script that determines the kiosk URL based on the machine's MAC address.
{
config,
lib,
pkgs,
inputs,
...
}:
let
macCaseBuilder = inputs.self.lib.macCaseBuilder;
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder;
macCases = macCaseBuilder {
varName = "STATION";
};
+28
View File
@@ -0,0 +1,28 @@
# 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;
}
+1 -1
View File
@@ -26,5 +26,5 @@
};
# Disable systemd-networkd and systemd-hostnamed
systemd.network.enable = lib.mkForce false;
systemd.network.enable = false;
}
+3
View File
@@ -1,4 +1,7 @@
{
config,
lib,
pkgs,
...
}:
{
+3 -2
View File
@@ -1,10 +1,11 @@
{
config,
lib,
pkgs,
inputs,
...
}:
let
macCaseBuilder = inputs.self.lib.macCaseBuilder;
macCaseBuilder = (import ./mac-hostmap.nix { inherit lib; }).macCaseBuilder;
shellCases = macCaseBuilder {
varName = "NEW_HOST";
prefix = "nix-station";
+26 -71
View File
@@ -5,74 +5,29 @@
inputs,
...
}:
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 {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./gsettings.nix {
inherit
config
lib
pkgs
inputs
;
})
]);
}
lib.mkMerge [
(import ./programs.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./services.nix {
inherit
config
lib
pkgs
inputs
;
})
(import ./gsettings.nix {
inherit
config
lib
pkgs
inputs
;
})
]
+1 -1
View File
@@ -155,7 +155,7 @@
--noerrdialogs \
--disable-session-crashed-bubble \
--disable-infobars \
${config.athenix.sw.tablet-kiosk.kioskUrl}
${config.athenix.sw.kioskUrl}
'';
};
};
+1 -12
View File
@@ -1,6 +1,7 @@
{
pkgs,
config,
osConfig,
lib,
...
}:
@@ -32,18 +33,6 @@ in
programs.zsh = {
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
historySubstringSearch = {
enable = true;
-524
View File
@@ -1,524 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib;
let
cfg = config.athenix.sw;
in
{
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [
python3
git
(pkgs.writeShellScriptBin "update-ref" ''
set -euo pipefail
RED='\033[31m'; YEL='\033[33m'; NC='\033[0m'
die() { printf "''${RED}error:''${NC} %s\n" "$*" >&2; exit 2; }
warn() { printf "''${YEL}warning:''${NC} %s\n" "$*" >&2; }
usage() {
cat >&2 <<'EOF'
usage:
update-ref [-R PATH|--athenix-repo=PATH] [-b BRANCH|--athenix-branch=BRANCH]
[-m "msg"|--message "msg"]
[-p[=false] [remote[=URL]]|--push[=false] [remote[=URL]]]
[--make-local|-l] [--make-remote|-r] [--ssh]
user=<username> | system=<device-type>:<hostkey>
EOF
exit 2
}
# --- must be in a git repo (current dir) ---
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "This directory is not a git project"
CUR_REPO_ROOT="$(git rev-parse --show-toplevel)"
CUR_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
# --- athenix checkout (working tree) ---
ATHENIX_DIR="$HOME/athenix"
ATHENIX_BRANCH=""
# --- current repo automation ---
COMMIT_MSG=""
PUSH_SPEC=""
# --- push / url mode ---
PUSH_SET=0
DO_PUSH=0
MODE_FORCE="" # "", local, remote
TARGET=""
is_remote_url() {
# https://, http://, ssh://, or scp-style git@host:org/repo
printf "%s" "$1" | grep -qE '^(https?|ssh)://|^[^/@:]+@[^/:]+:'
}
derive_full_hostname() {
devtype="$1"; hostkey="$2"
if printf "%s" "$hostkey" | grep -q '-' || printf "%s" "$hostkey" | grep -q "^$devtype"; then
printf "%s" "$hostkey"
elif printf "%s" "$hostkey" | grep -qE '^[0-9]+$'; then
printf "%s" "$devtype$hostkey"
else
printf "%s" "$devtype-$hostkey"
fi
}
extract_existing_fetch_url() {
# args: mode file username key
python3 - "$1" "$2" "$3" "$4" "$5"<<'PY'
import sys, re, pathlib
mode, file, username, key, use_ssh = sys.argv[1:5]
t = pathlib.Path(file).read_text()
def url_from_block(block: str) -> str:
if not block:
return ""
m = re.search(r'url\s*=\s*"([^"]+)"\s*;', block)
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":
m = re.search(r'(?s)\n\s*' + re.escape(username) + r'\.external\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t)
block = m.group(1) if m else ""
print(url_from_block(block))
else:
m = re.search(r'(?s)\n\s*"' + re.escape(key) + r'"\s*=\s*builtins\.fetchGit\s*\{(.*?)\n\s*\};', t)
block = m.group(1) if m else ""
print(url_from_block(block))
PY
}
# --- parse args ---
while [ "$#" -gt 0 ]; do
case "$1" in
user=*|system=*)
[ -z "$TARGET" ] || die "Only one subcommand allowed (user=... or system=...)"
TARGET="$1"; shift
;;
--athenix-repo=*)
ATHENIX_DIR="''${1#*=}"; shift
;;
-R)
[ "$#" -ge 2 ] || usage
ATHENIX_DIR="$2"; shift 2
;;
--athenix-branch=*)
ATHENIX_BRANCH="''${1#*=}"; shift
;;
-b)
[ "$#" -ge 2 ] || usage
ATHENIX_BRANCH="$2"; shift 2
;;
-m|--message)
[ "$#" -ge 2 ] || usage
COMMIT_MSG="$2"; shift 2
;;
-p|--push)
PUSH_SET=1
DO_PUSH=1
PUSH_SPEC=""
# If there is a next token, only consume it if it is a remote spec
# and not another flag or the subcommand.
if [ "$#" -ge 2 ]; then
nxt="$2"
if printf "%s" "$nxt" | grep -qE '^(user=|system=)'; then
# next token is the subcommand; don't consume it
shift
elif printf "%s" "$nxt" | grep -qE '^-'; then
# next token is another flag; don't consume it
shift
elif printf "%s" "$nxt" | grep -qE '^[A-Za-z0-9._-]+$'; then
# remote name
PUSH_SPEC="$nxt"
shift 2
elif printf "%s" "$nxt" | grep -qE '^[A-Za-z0-9._-]+=.+$'; then
# remote=URL
PUSH_SPEC="$nxt"
shift 2
else
# unknown token; treat as not-a-push-spec and don't consume it
shift
fi
else
shift
fi
;;
-p=*|--push=*)
PUSH_SET=1
val="''${1#*=}"
case "$val" in
false|0|no|off) DO_PUSH=0 ;;
true|1|yes|on|"") DO_PUSH=1 ;;
*) die "Invalid value for --push: $val (use true/false)" ;;
esac
shift
;;
--make-local|-l) MODE_FORCE="local"; shift ;;
--make-remote|-r) MODE_FORCE="remote"; shift ;;
--ssh) USE_SSH="true"; shift ;;
-h|--help) usage ;;
*) die "Unknown argument: $1" ;;
esac
done
[ -n "$TARGET" ] || die "Missing required subcommand: user=<username> or system=<device-type>:<hostkey>"
# --- validate athenix working tree path ---
[ -d "$ATHENIX_DIR" ] || die "$ATHENIX_DIR does not exist"
git -C "$ATHENIX_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "$ATHENIX_DIR is not a git project (athenix checkout)"
# --- -b behavior: fork/switch athenix working tree into branch ---
if [ -n "$ATHENIX_BRANCH" ]; then
ATH_CUR_BRANCH="$(git -C "$ATHENIX_DIR" rev-parse --abbrev-ref HEAD)"
if [ "$ATH_CUR_BRANCH" != "$ATHENIX_BRANCH" ]; then
if git -C "$ATHENIX_DIR" show-ref --verify --quiet "refs/heads/$ATHENIX_BRANCH"; then
warn "Branch '$ATHENIX_BRANCH' already exists in $ATHENIX_DIR."
warn "Delete and recreate it from current branch '$ATH_CUR_BRANCH' state? [y/N] "
read -r ans || true
case "''${ans:-N}" in
y|Y|yes|YES)
git -C "$ATHENIX_DIR" branch -D "$ATHENIX_BRANCH"
git -C "$ATHENIX_DIR" switch -c "$ATHENIX_BRANCH"
;;
*)
git -C "$ATHENIX_DIR" switch "$ATHENIX_BRANCH"
;;
esac
else
git -C "$ATHENIX_DIR" switch -c "$ATHENIX_BRANCH"
fi
fi
fi
# --- target file + identifiers ---
MODE=""; FILE=""; USERNAME=""; DEVTYPE=""; HOSTKEY=""
case "$TARGET" in
user=*)
MODE="user"
USERNAME="''${TARGET#user=}"
[ -n "$USERNAME" ] || die "user=<username>: username missing"
FILE="$ATHENIX_DIR/users.nix"
;;
system=*)
MODE="system"
RHS="''${TARGET#system=}"
printf "%s" "$RHS" | grep -q ':' || die "system=... must be system=<device-type>:<hostkey>"
DEVTYPE="''${RHS%%:*}"
HOSTKEY="''${RHS#*:}"
[ -n "$DEVTYPE" ] || die "system=<device-type>:<hostkey>: device-type missing"
[ -n "$HOSTKEY" ] || die "system=<device-type>:<hostkey>: hostkey missing"
FILE="$ATHENIX_DIR/inventory.nix"
;;
esac
[ -f "$FILE" ] || die "File not found: $FILE"
# --- push default based on existing entry url in the target file ---
EXISTING_URL=""
ENTRY_EXISTS=0
if [ "$MODE" = "user" ]; then
EXISTING_URL="$(extract_existing_fetch_url user "$FILE" "$USERNAME" "" "false")"
[ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true
else
FULL="$(derive_full_hostname "$DEVTYPE" "$HOSTKEY")"
EXISTING_URL="$(extract_existing_fetch_url system "$FILE" "" "$HOSTKEY")"
if [ -n "$EXISTING_URL" ]; then
ENTRY_EXISTS=1
elif [ "$FULL" != "$HOSTKEY" ]; then
EXISTING_URL="$(extract_existing_fetch_url system "$FILE" "" "$FULL")"
[ -n "$EXISTING_URL" ] && ENTRY_EXISTS=1 || true
fi
fi
if [ "$PUSH_SET" -eq 0 ]; then
if [ "$ENTRY_EXISTS" -eq 1 ] && is_remote_url "$EXISTING_URL"; then
DO_PUSH=1
else
DO_PUSH=0
[ "$MODE_FORCE" = "remote" ] && DO_PUSH=1 || true
fi
fi
if [ "$MODE_FORCE" = "local" ] && [ "$PUSH_SET" -eq 0 ]; then
DO_PUSH=0
fi
# --- if current repo dirty, prompt ---
if [ -n "$(git status --porcelain)" ]; then
warn "This branch has untracked or uncommitted changes. Would you like to add, commit''${DO_PUSH:+, and push}? [y/N] "
read -r ans || true
case "''${ans:-N}" in
y|Y|yes|YES)
git add -A
if ! git diff --cached --quiet; then
if [ -n "$COMMIT_MSG" ]; then git commit -m "$COMMIT_MSG"; else git commit; fi
else
warn "No staged changes to commit."
fi
;;
*) warn "Proceeding without committing. (rev will be last committed HEAD.)" ;;
esac
fi
# --- push current repo if requested ---
PUSH_REMOTE_URL=""
if [ "$DO_PUSH" -eq 1 ]; then
if [ -n "$PUSH_SPEC" ]; then
if printf "%s" "$PUSH_SPEC" | grep -q '='; then
REM_NAME="''${PUSH_SPEC%%=*}"
REM_URL="''${PUSH_SPEC#*=}"
[ -n "$REM_NAME" ] || die "--push remote-name=URL: remote-name missing"
[ -n "$REM_URL" ] || die "--push remote-name=URL: URL missing"
if git remote get-url "$REM_NAME" >/dev/null 2>&1; then
git remote set-url "$REM_NAME" "$REM_URL"
else
git remote add "$REM_NAME" "$REM_URL"
fi
git push -u "$REM_NAME" "$CUR_BRANCH"
PUSH_REMOTE_URL="$REM_URL"
else
REM_NAME="$PUSH_SPEC"
git push -u "$REM_NAME" "$CUR_BRANCH"
PUSH_REMOTE_URL="$(git remote get-url "$REM_NAME")"
fi
else
if ! git rev-parse --abbrev-ref --symbolic-full-name @{u} >/dev/null 2>&1; then
die "No upstream is set. Set a default upstream with \"git branch -u <remote>/<remote_branch_name>\""
fi
git push
UPSTREAM_REMOTE="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} | cut -d/ -f1)"
PUSH_REMOTE_URL="$(git remote get-url "$UPSTREAM_REMOTE")"
fi
fi
CUR_REV="$(git -C "$CUR_REPO_ROOT" rev-parse HEAD)"
# --- choose URL to write into fetchGit ---
if [ "$MODE_FORCE" = "local" ]; then
FETCH_URL="file://$CUR_REPO_ROOT"
elif [ "$MODE_FORCE" = "remote" ]; then
if [ "$DO_PUSH" -eq 1 ]; then
FETCH_URL="$PUSH_REMOTE_URL"
elif [ "$ENTRY_EXISTS" -eq 1 ] && [ -n "$EXISTING_URL" ] && is_remote_url "$EXISTING_URL"; then
FETCH_URL="$EXISTING_URL"
else
CUR_ORIGIN="$(git remote get-url origin 2>/dev/null || true)"
[ -n "$CUR_ORIGIN" ] && is_remote_url "$CUR_ORIGIN" || die "--make-remote requires a remote url (set origin or use -p remote=URL)"
FETCH_URL="$CUR_ORIGIN"
fi
else
if [ "$DO_PUSH" -eq 1 ]; then FETCH_URL="$PUSH_REMOTE_URL"; else FETCH_URL="file://$CUR_REPO_ROOT"; fi
fi
# --- rewrite users.nix or inventory.nix ---
python3 - "$MODE" "$FILE" "$FETCH_URL" "$CUR_REV" "$USERNAME" "$DEVTYPE" "$HOSTKEY" <<'PY'
import sys, re, pathlib
mode = sys.argv[1]
path = pathlib.Path(sys.argv[2])
fetch_url = sys.argv[3]
rev = sys.argv[4]
username = sys.argv[5]
devtype = sys.argv[6]
hostkey = sys.argv[7]
text = path.read_text()
def find_matching_brace(s: str, start: int) -> int:
depth = 0
i = start
in_str = False
while i < len(s):
ch = s[i]
if in_str:
if ch == '\\':
i += 2
continue
if ch == '"':
in_str = False
i += 1
continue
if ch == '"':
in_str = True
i += 1
continue
if ch == '{':
depth += 1
elif ch == '}':
depth -= 1
if depth == 0:
return i
i += 1
raise ValueError("Could not find matching '}'")
def mk_fetch(entry_indent: str) -> str:
# entry_indent is indentation for the whole `"key" = <here>;` line.
# The attrset contents should be indented one level deeper.
inner = entry_indent + " "
return (
'builtins.fetchGit {\n'
f'{inner}url = "{fetch_url}";\n'
f'{inner}rev = "{rev}";\n'
f'{inner}submodules = true;\n'
f'{entry_indent}}}'
)
def full_hostname(devtype: str, hostkey: str) -> str:
if hostkey.startswith(devtype) or "-" in hostkey:
return hostkey
if hostkey.isdigit():
return f"{devtype}{hostkey}"
return f"{devtype}-{hostkey}"
def update_user(t: str) -> str:
mblock = re.search(r"(?s)athenix\.users\s*=\s*\{(.*?)\n\s*\};", t)
if not mblock:
raise SystemExit("error: could not locate `athenix.users = { ... };` block")
# locate the full span of the users block to edit inside it
# (re-find with groups for reconstruction)
m2 = re.search(r"(?s)(athenix\.users\s*=\s*\{)(.*?)(\n\s*\};)", t)
head, body, tail = m2.group(1), m2.group(2), m2.group(3)
entry_re = re.search(
r"(?s)(\n[ \t]*" + re.escape(username) + r"\.external\s*=\s*)builtins\.fetchGit\s*\{",
body
)
if entry_re:
brace = body.rfind("{", 0, entry_re.end())
end = find_matching_brace(body, brace)
semi = re.match(r"\s*;", body[end+1:])
if not semi:
raise SystemExit("error: expected ';' after fetchGit attrset")
semi_end = end + 1 + semi.end()
line_start = body.rfind("\n", 0, entry_re.start()) + 1
indent = re.match(r"[ \t]*", body[line_start:entry_re.start()]).group(0)
new_body = body[:entry_re.start()] + entry_re.group(1) + mk_fetch(indent) + ";" + body[semi_end:]
else:
indent = " "
new_body = body + f"\n{indent}{username}.external = {mk_fetch(indent)};\n"
return t[:m2.start()] + head + new_body + tail + t[m2.end():]
def update_system(t: str) -> str:
# Find devtype block robustly: start-of-file or newline.
m = re.search(r"(?s)(^|\n)[ \t]*" + re.escape(devtype) + r"\s*=\s*\{", t)
if not m:
raise SystemExit(f"error: could not locate `{devtype} = {{ ... }};` block")
dev_open = t.find("{", m.end() - 1)
dev_close = find_matching_brace(t, dev_open)
dev = t[dev_open:dev_close+1]
# Find devices attrset inside dev
dm = re.search(r"(?s)(^|\n)[ \t]*devices\s*=\s*\{", dev)
if not dm:
raise SystemExit(f"error: could not locate `devices = {{ ... }};` inside `{devtype}`")
devices_open = dev.find("{", dm.end() - 1)
devices_close = find_matching_brace(dev, devices_open)
devices = dev[devices_open:devices_close+1]
# indentation for entries in devices
# find indent of the 'devices' line, then add 2 spaces
candidates = [hostkey, full_hostname(devtype, hostkey)]
seen = set()
candidates = [c for c in candidates if not (c in seen or seen.add(c))]
for key in candidates:
entry = re.search(
r'(?s)\n([ ]*)"' + re.escape(key) + r'"\s*=\s*builtins\.fetchGit\s*\{',
devices
)
if entry:
entry_indent = entry.group(1)
# find the '{' we matched
brace = devices.find("{", entry.end() - 1)
end = find_matching_brace(devices, brace)
semi = re.match(r"\s*;", devices[end+1:])
if not semi:
raise SystemExit("error: expected ';' after fetchGit attrset in devices")
semi_end = end + 1 + semi.end()
# Reconstruct the prefix: newline + indent + "key" =
prefix = f'\n{entry_indent}"{key}" = '
new_devices = (
devices[:entry.start()]
+ prefix
+ mk_fetch(entry_indent)
+ ";"
+ devices[semi_end:]
)
new_dev = dev[:devices_open] + new_devices + dev[devices_close+1:]
return t[:dev_open] + new_dev + t[dev_close+1:]
# Not found: append into devices (exact hostkey)
# Indent for new entries: take indent of the closing '}' of devices, add 2 spaces.
close_line_start = devices.rfind("\n", 0, len(devices)-1) + 1
close_indent = re.match(r"[ ]*", devices[close_line_start:]).group(0)
entry_indent = close_indent + " "
insertion = f'\n{entry_indent}"{hostkey}" = {mk_fetch(entry_indent)};\n'
new_devices = devices[:-1].rstrip() + insertion + close_indent + "}"
new_dev = dev[:devices_open] + new_devices + dev[devices_close+1:]
return t[:dev_open] + new_dev + t[dev_close+1:]
if mode == "user":
out = update_user(text)
elif mode == "system":
out = update_system(text)
else:
raise SystemExit("error: unknown mode")
path.write_text(out)
PY
cd $ATHENIX_DIR
nix fmt **/*.nix
cd $CUR_REPO_ROOT
printf "updated %s\n" "$FILE" >&2
printf " url = %s\n" "$FETCH_URL" >&2
printf " rev = %s\n" "$CUR_REV" >&2
'')
];
};
}
+19 -180
View File
@@ -9,210 +9,51 @@ with lib;
{
options.athenix.sw.remoteBuild = lib.mkOption {
type = lib.types.submodule {
type = types.submodule {
options = {
hosts = mkOption {
type = lib.types.listOf lib.types.str;
type = types.listOf types.str;
default = [ "engr-ugaif@192.168.11.133 x86_64-linux" ];
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"
]'';
description = "List of remote build hosts for system rebuilding.";
};
enable = mkOption {
type = lib.types.bool;
type = types.bool;
default = false;
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.
'';
description = "Whether to enable remote build for 'update-system' command.";
};
};
};
default = { };
description = "Remote build configuration for system updates.";
description = "Remote build configuration";
};
config = {
athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.tablet-kiosk.enable);
athenix.sw.remoteBuild.enable = lib.mkDefault (config.athenix.sw.type == "tablet-kiosk");
environment.systemPackages = [
(pkgs.writeShellScriptBin "update-system" ''
set -euo pipefail
RED='\033[31m'; NC='\033[0m'
is_root() { [ "''${EUID:-$(id -u)}" -eq 0 ]; }
in_wheel() { id -nG 2>/dev/null | tr ' ' '\n' | grep -qx wheel; }
# Service path for unprivileged (no flags)
UNIT="update-system.service"
# Figure out the "real" invoking user, even under sudo.
INVOKER_USER="''${SUDO_USER:-$(id -un)}"
INVOKER_HOME="$(getent passwd "$INVOKER_USER" | cut -d: -f6)"
if [ -z "$INVOKER_HOME" ]; then
# fallback if getent is weird in some containers
INVOKER_HOME="''${HOME:-/home/$INVOKER_USER}"
fi
# Start following logs in the background
journalctl -fu "$UNIT" -n 0 --output=cat &
JPID=$!
# Defaults for flagged mode
DEFAULT_REMOTE_URL="https://git.factory.uga.edu/UGA-Innovation-Factory/athenix"
REPO_MODE="default" # default | local | remote
LOCAL_PATH=""
REMOTE_URL=""
BRANCH=""
IMPURE=0
usage() {
cat >&2 <<'EOF'
usage:
update-system
update-system [--local-repo[=PATH]] [--remote-repo=URL] [--branch=BRANCH] [--impure]
notes:
- No flags: runs the systemd service (works for unprivileged users via polkit).
- Any flags: only allowed for root or wheel (runs nixos-rebuild directly).
EOF
exit 2
}
# No flags -> polkit-friendly systemd service route
if [ "$#" -eq 0 ]; then
journalctl -fu "$UNIT" -n 0 --output=cat &
JPID=$!
if systemctl start --wait --no-ask-password "$UNIT"; then
STATUS=$?
else
STATUS=$?
fi
sleep 2
kill "$JPID" 2>/dev/null || true
exit "$STATUS"
fi
# Flags -> require root or wheel
if ! is_root && ! in_wheel; then
printf "''${RED}error:''${NC} flags are only allowed for root or wheel. Run without flags (service path), or use sudo / add yourself to wheel.\n" >&2
exit 2
fi
# Parse flags
while [ "$#" -gt 0 ]; do
case "$1" in
--local-repo)
REPO_MODE="local"
LOCAL_PATH="$INVOKER_HOME/athenix"
shift
;;
--local-repo=*)
REPO_MODE="local"
LOCAL_PATH="''${1#*=}"
shift
;;
--remote-repo=*)
REPO_MODE="remote"
REMOTE_URL="''${1#*=}"
shift
;;
--branch)
[ "$#" -ge 2 ] || usage
BRANCH="$2"
shift 2
;;
--branch=*)
BRANCH="''${1#*=}"
shift
;;
--impure)
IMPURE=1
shift
;;
-h|--help) usage ;;
*)
printf "''${RED}error:''${NC} unknown argument: %s\n" "$1" >&2
usage
;;
esac
done
if [ "$REPO_MODE" = "local" ] && [ -n "$REMOTE_URL" ]; then
printf "''${RED}error:''${NC} can't use --local-repo and --remote-repo together.\n" >&2
exit 2
fi
host="''${HOSTNAME:-$(hostname)}"
# Build flake ref
if [ "$REPO_MODE" = "local" ]; then
[ -n "$LOCAL_PATH" ] || LOCAL_PATH="$INVOKER_HOME/athenix"
# Clone default repo if missing
if [ ! -d "$LOCAL_PATH" ]; then
printf "local repo not found at %s, cloning %s...\n" "$LOCAL_PATH" "$DEFAULT_REMOTE_URL" >&2
if [ -n "$BRANCH" ]; then
git clone --branch "$BRANCH" "$DEFAULT_REMOTE_URL" "$LOCAL_PATH"
else
git clone "$DEFAULT_REMOTE_URL" "$LOCAL_PATH"
fi
fi
flakeRef="''${LOCAL_PATH}#''${host}"
# Start the service and wait for it to finish
if systemctl start --wait --no-ask-password "$UNIT"; then
STATUS=$?
else
url="''${REMOTE_URL:-$DEFAULT_REMOTE_URL}"
if echo "$url" | grep -qE '^(https?|ssh)://'; then
base="git+''${url}"
elif echo "$url" | grep -qE '^[^/@:]+@[^/:]+:'; then
# scp-style: git@host:owner/repo(.git)
userhost="''${url%%:*}"
path="''${url#*:}"
base="git+ssh://''${userhost}/''${path}"
else
base="''${url}"
fi
if [ -n "$BRANCH" ]; then
if echo "$base" | grep -q '?'; then
base="''${base}&ref=''${BRANCH}"
else
base="''${base}?ref=''${BRANCH}"
fi
fi
flakeRef="''${base}#''${host}"
STATUS=$?
fi
impureFlag=""
if [ "$IMPURE" -eq 1 ]; then
impureFlag="--impure"
fi
sleep 2
# If not root, re-exec via sudo to do the actual switch.
# Preserve our computed invoker context so sudo doesn't "helpfully" change it.
if ! is_root; then
exec sudo --preserve-env=HOME,USER,LOGNAME \
nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "$flakeRef"
else
exec nixos-rebuild switch --refresh --print-build-logs $impureFlag --flake "$flakeRef"
fi
# Kill the log follower
kill "$JPID" 2>/dev/null || true
exit "$STATUS"
'')
];
@@ -221,10 +62,8 @@ with lib;
description = "System daemon to one-shot run the Nix updater from fleet flake as root";
path = with pkgs; [
git
openssh
nixos-rebuild
nix
coreutils
];
serviceConfig = {
Type = "oneshot";
+3 -2
View File
@@ -1,4 +1,4 @@
{ ... }:
{ inputs, ... }:
# ============================================================================
# User Configuration
@@ -15,6 +15,7 @@
# nixos-systems configuration (nixpkgs, home-manager, etc.).
{
config,
lib,
pkgs,
osConfig ? null, # Only available in home-manager context
@@ -59,7 +60,7 @@
fd
bat
]
++ lib.optional (osConfig.athenix.sw.desktop.enable or false) firefox;
++ lib.optional (osConfig.athenix.sw.type or null == "desktop") firefox;
# Conditionally add packages based on system type
# ========== Programs ==========
+6 -12
View File
@@ -1,4 +1,4 @@
{ ... }:
{ pkgs, ... }:
{
# ============================================================================
# User Definitions
@@ -13,9 +13,8 @@
#
# External User Configuration:
# Users can specify external configuration modules via the 'external' attribute:
# external = { url = "..."; rev = "..."; submodules? = false; };
# external = builtins.fetchGit { url = "..."; rev = "..."; };
# external = /path/to/local/config;
# external = builtins.fetchGit { ... }; # legacy, still supported
#
# External repositories should contain:
# - user.nix (required): Defines athenix.users.<name> options AND home-manager config
@@ -27,7 +26,7 @@
#
# 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.
config.athenix.users = {
athenix.users = {
root = {
isNormalUser = false;
hashedPassword = "!";
@@ -48,15 +47,10 @@
enable = true; # Default user, enabled everywhere
};
hdh20267 = {
external = {
external = builtins.fetchGit {
url = "https://git.factory.uga.edu/hdh20267/hdh20267-nix";
rev = "dbdf65c7bd59e646719f724a3acd2330e0c922ec";
# submodules = false; # optional, defaults to false
rev = "c538e0c0510045b58264627bb897fc499dc7c490";
};
extraGroups = [
"networkmanager"
"wheel"
];
};
sv22900 = {
description = "Alireza Vaezi";
@@ -64,7 +58,7 @@
"networkmanager"
"wheel"
];
shell = "zsh";
shell = pkgs.zsh;
# enable = false by default, set to true per-system
};
};