497 Commits

Author SHA1 Message Date
mjallen18
91ec603b62 spec 2026-03-24 16:24:23 -05:00
mjallen18
a4c2cbdf7b ntfy crowdsec 2026-03-24 16:11:07 -05:00
mjallen18
f8a86f9b29 sdcard 2026-03-24 14:42:42 -05:00
mjallen18
84eb2e3734 ntfy 2026-03-24 14:41:22 -05:00
mjallen18
4cc58ab381 ntfy 2026-03-24 14:41:07 -05:00
mjallen18
661c7c7771 restic browser 2026-03-24 13:27:40 -05:00
mjallen18
35ac45f5ce restic 2026-03-24 13:23:38 -05:00
mjallen18
540dabcb5d grafana dashboard fixes 2026-03-24 13:02:17 -05:00
mjallen18
7798684d29 grafana 2026-03-24 10:20:46 -05:00
mjallen18
d1960837a0 prometheus 2026-03-24 09:36:36 -05:00
99452eb470 sops 2026-03-24 09:25:42 -05:00
d75c05f74f rpi 7-rc5 2026-03-24 09:11:06 -05:00
mjallen18
2ad3e050fc allyx neb 2026-03-24 09:09:23 -05:00
mjallen18
da1cd27482 sops 2026-03-24 09:07:09 -05:00
mjallen18
0f2239af05 nebula 2026-03-24 08:59:13 -05:00
mjallen18
0ffbeaaea1 idk 2026-03-24 08:52:01 -05:00
mjallen18
cd6ea07e88 nebula cert 2026-03-23 18:13:10 -05:00
mjallen18
72014609a0 nebula cert 2026-03-23 18:02:53 -05:00
mjallen18
01d1086580 nebula 2026-03-23 17:49:38 -05:00
mjallen18
5952eddecb upd ext 2026-03-23 17:46:48 -05:00
mjallen18
309e224a72 test 2026-03-23 17:42:47 -05:00
mjallen18
ecce28b498 iface 2026-03-23 17:36:25 -05:00
mjallen18
bd569962ca log 2026-03-23 17:33:28 -05:00
mjallen18
068d6c8f94 ext 2026-03-23 17:26:01 -05:00
mjallen18
0b9a301a92 neb 2026-03-23 16:37:34 -05:00
mjallen18
23f29b6ca1 fixes and docs 2026-03-23 15:17:10 -05:00
mjallen18
2c0b26ced0 fixes and docs 2026-03-23 15:17:09 -05:00
mjallen18
e647794a0f couple fixes 2026-03-23 14:07:48 -05:00
mjallen18
6f77344d42 fixes 2026-03-20 18:24:51 -05:00
mjallen18
27790713be upds 2026-03-20 17:24:20 -05:00
93aaf52b7e bcachefs 1.37.2 2026-03-20 15:37:56 -05:00
659cc20e38 idk 2026-03-20 10:43:32 -05:00
mjallen18
3ee33e4bfd couple fixes 2026-03-19 16:34:27 -05:00
mjallen18
d229cdbf6a assertions 2026-03-19 16:17:20 -05:00
dd04320fe7 lmfao 2026-03-19 16:01:23 -05:00
5d14db352e lmfao 2026-03-19 08:10:06 -05:00
mjallen18
af840f242b cleanup 2026-03-18 22:43:29 -05:00
mjallen18
d9f17670e1 cleanup 2026-03-18 21:24:58 -05:00
mjallen18
c1028fc0f0 cleanup 2026-03-18 21:15:20 -05:00
mjallen18
5466c59dde cleanup 2026-03-18 21:06:43 -05:00
mjallen18
d53093a6c1 cleanup 2026-03-18 21:05:20 -05:00
mjallen18
5d1d3dc850 cleanup 2026-03-18 20:59:27 -05:00
mjallen18
f4fd826c90 cleanup 2026-03-18 20:51:39 -05:00
mjallen18
46b249560f cleanup 2026-03-18 20:39:25 -05:00
mjallen18
4a7b2f835e gnome 2026-03-18 16:57:17 -05:00
mjallen18
cb82c6a6d7 proxy 2026-03-18 10:19:52 -05:00
mjallen18
b6ed51da26 iwd 2026-03-17 20:00:45 -05:00
mjallen18
23f39ffe6f hm 2026-03-17 19:50:16 -05:00
mjallen18
676ec990e1 gpg 2026-03-17 19:43:11 -05:00
mjallen18
297f23fac8 couple fixes 2026-03-17 19:16:31 -05:00
mjallen18
a925fccda1 couple fixes 2026-03-17 19:16:31 -05:00
mjallen18
0346094f8e hass esphome 2026-03-17 19:11:22 -05:00
mjallen18
2c6ea8b9a4 samba 2026-03-17 14:18:57 -05:00
mjallen18
cf40c72e7e reverse proxy 2026-03-17 14:05:50 -05:00
mjallen18
436cc7ccc9 nuc 2026-03-16 17:46:40 -05:00
mjallen18
4db098206f nuc 2026-03-16 17:45:46 -05:00
mjallen18
cbfe855853 nuc 2026-03-16 17:28:58 -05:00
mjallen18
9ad581f0e5 nuc 2026-03-16 17:20:16 -05:00
mjallen18
ad348a0f9e nuc 2026-03-16 17:13:13 -05:00
mjallen18
001b54a620 nuc 2026-03-16 17:13:12 -05:00
mjallen18
1d2d031609 nuc 2026-03-16 17:08:37 -05:00
mjallen18
dad0dbf6b5 hash 2026-03-16 17:04:37 -05:00
mjallen18
8c93a6473d nuc 2026-03-16 17:03:19 -05:00
mjallen18
5b3e625335 nuc 2026-03-16 16:57:36 -05:00
mjallen18
ff5cfe0f97 nuc 2026-03-16 16:51:56 -05:00
mjallen18
1ae9c47afe lib 2026-03-16 16:46:29 -05:00
mjallen18
7538f734f1 sf 2026-03-16 16:41:46 -05:00
mjallen18
742e1703d8 lib 2026-03-16 14:32:54 -05:00
mjallen18
7dbd958c73 lib 2026-03-16 14:31:26 -05:00
mjallen18
3e232c3474 overlays lol 2026-03-16 14:25:26 -05:00
mjallen18
e6c9e21c62 termix 2026-03-16 14:06:22 -05:00
de8ec35cbb bruh 2026-03-16 12:14:02 -05:00
3e6e975ce5 ally 2026-03-16 12:12:02 -05:00
897ca32919 grrr 2026-03-16 12:00:43 -05:00
8d2b16825a up 2026-03-16 11:57:25 -05:00
33f0a085ef iwd 2026-03-16 11:38:44 -05:00
mjallen18
798f233ff2 asd 2026-03-15 20:20:14 -05:00
mjallen18
f968cc0e8d srm 2026-03-15 20:17:53 -05:00
mjallen18
cd6188775f srm 2026-03-15 20:08:20 -05:00
mjallen18
e13c19e698 asd 2026-03-15 20:07:41 -05:00
mjallen18
56c2ef7b96 srm 2026-03-15 20:06:27 -05:00
mjallen18
829b369ebe srm 2026-03-15 19:52:22 -05:00
mjallen18
9891b3abff a 2026-03-15 19:04:38 -05:00
mjallen18
8fc34adfc6 deck 2026-03-15 19:00:33 -05:00
mjallen18
56c3bfdd91 hass esphome 2026-03-14 13:52:54 -05:00
mjallen18
21a9b476e2 sleep? 2026-03-14 13:50:28 -05:00
mjallen18
2314dd3225 hhd 2026-03-14 13:29:40 -05:00
mjallen18
9cd0677ba0 hhd 2026-03-14 13:28:02 -05:00
mjallen18
2699bc0f0d drwin 2026-03-14 13:26:56 -05:00
mjallen18
996571a544 hass esphome 2026-03-14 12:36:49 -05:00
mjallen18
4f4b7865eb kek 2026-03-13 20:01:27 -05:00
mjallen18
c5e97ac853 kek 2026-03-13 19:11:58 -05:00
mjallen18
778e9ce02b lol 2026-03-13 19:11:43 -05:00
mjallen18
39b898bcf2 lol 2026-03-13 14:45:00 -05:00
mjallen18
9737b3af9f idk man 2026-03-13 13:38:53 -05:00
mjallen18
3862a6b651 up 2026-03-13 12:18:59 -05:00
mjallen18
2500f5ffa0 lol 2026-03-13 11:15:07 -05:00
mjallen18
8d3574e183 kek 2026-03-10 12:32:49 -05:00
mjallen18
631d7336f6 kek 2026-03-10 11:16:16 -05:00
mjallen18
01f6eeff7c lol 2026-03-10 11:13:25 -05:00
2fbfd9c2d3 lmfao 2026-03-10 11:05:14 -05:00
89196706c7 lmfao 2026-03-10 11:02:13 -05:00
mjallen18
1616c6766e lmao perhaps 2026-03-10 10:51:32 -05:00
mjallen18
c13ce86810 lol 2026-03-10 10:27:51 -05:00
mjallen18
cce8f2ea03 bring back allyx maybe 2026-03-10 10:06:55 -05:00
mjallen18
75c64f2499 bruh 2026-03-10 09:56:06 -05:00
mjallen18
9daf43e3f3 upd 2026-03-08 20:23:35 -05:00
mjallen18
f369128cb7 auth 2026-03-06 10:02:19 -06:00
mjallen18
6210605ac2 net 2026-03-06 09:26:24 -06:00
mjallen18
f1493146ab auth 2026-03-06 09:25:58 -06:00
mjallen18
86acd96e07 hasp 2026-03-05 16:01:07 -06:00
mjallen18
e5b7c403a3 hasp 2026-03-05 15:44:44 -06:00
mjallen18
805699d9d0 hasp 2026-03-05 15:41:45 -06:00
mjallen18
bcc7214c43 hasp ids 2026-03-05 15:37:19 -06:00
mjallen18
52bc808a0a fixes 2026-03-05 14:40:48 -06:00
mjallen18
0beecf9cce hasp? 2026-03-05 14:33:30 -06:00
mjallen18
eac037caf8 hasp? 2026-03-05 14:27:21 -06:00
mjallen18
56c4e1fcad conver 2026-03-05 10:34:34 -06:00
mjallen18
8d299980fb conver 2026-03-05 10:29:06 -06:00
a2ee0e2a55 fix pi5 2026-03-05 09:30:37 -06:00
mjallen18
401e5f7bd8 upd 2026-03-04 21:26:10 -06:00
mjallen18
2398b810dc upd 2026-03-04 21:18:59 -06:00
79b0fdf4c4 pi 7.0 2026-03-04 15:58:34 -06:00
mjallen18
46d35ab70e versions 2026-03-04 15:55:38 -06:00
mjallen18
0dc60c261b versions 2026-03-04 15:52:24 -06:00
mjallen18
23f5f6c3b3 versions 2026-03-04 15:31:10 -06:00
mjallen18
d17d096a97 versions 2026-03-04 13:43:18 -06:00
5f79421d9e code 2026-03-04 10:39:37 -06:00
5eaf1cce2a gnome notch 2026-03-04 10:33:51 -06:00
85b35eaedc gnome notch 2026-03-04 10:33:27 -06:00
mjallen18
3c4856ddca apps 2026-03-03 16:32:55 -06:00
ff92934cc4 port lol 2026-03-03 16:31:35 -06:00
c304fefb54 tunarr 2026-03-03 16:25:09 -06:00
mjallen18
9b1c9a6371 stuff 2026-03-03 15:21:30 -06:00
mjallen18
b0ecedaf2a stuff 2026-03-03 15:21:30 -06:00
mjallen18
8d7a112365 bcachefs 2026-03-03 14:50:56 -06:00
5994b99288 disks 2026-03-03 14:48:32 -06:00
mjallen18
9e755ab926 bcachefs 2026-03-03 14:41:24 -06:00
e277a56233 versions 2026-03-03 14:36:47 -06:00
41361e5bc1 versions 2026-03-03 14:33:17 -06:00
mjallen18
85b69d69ff idk 2026-03-03 14:18:44 -06:00
9a962363e8 asahi 2026-03-03 13:56:59 -06:00
mjallen18
d856b57f13 fix 2026-03-03 11:21:33 -06:00
mjallen18
7e85953133 up 2026-03-03 10:42:08 -06:00
mjallen18
3655680e7c drwin 2026-03-03 10:08:23 -06:00
mjallen18
1fc2a7a89f temp 2026-03-03 09:51:12 -06:00
mjallen18
ddd99ef396 upd 2026-03-03 09:49:00 -06:00
mjallen18
ff9aea7a58 cleanup 2026-02-24 14:18:47 -06:00
mjallen18
4c5118c181 cachy 2026-02-23 18:13:56 -06:00
mjallen18
2e0d6aebb5 sparky but broken testing 2026-02-21 11:36:50 -06:00
mjallen18
974b696ef8 cachy 2026-02-20 20:53:19 -06:00
afccded50c attempt omnissa on arm 2026-02-20 14:06:42 -06:00
mjallen18
0d835df1aa retire pi4 2026-02-19 18:47:05 -06:00
mjallen18
d7958927b5 retire pi4 2026-02-19 18:35:13 -06:00
a5162e9e76 rpi 2026-02-17 10:37:07 -06:00
ed19748358 asahi: 2026-02-17 08:49:24 -06:00
mjallen18
2efe3fa067 up 2026-02-16 14:28:22 -06:00
mjallen18
c7a3aa6897 cleanup 2026-02-16 12:07:05 -06:00
mjallen18
f7ecb901db nebula 2026-02-13 20:38:19 -06:00
mjallen18
66ee83167f stuff 2026-02-13 19:20:25 -06:00
mjallen18
1740116e6b bruh 2026-02-13 18:55:12 -06:00
mjallen18
80204acfb6 nbula 2026-02-13 18:27:31 -06:00
mjallen18
c50aa6d68e trust 2026-02-13 18:20:49 -06:00
mjallen18
99a6b40776 idk 2026-02-13 18:18:40 -06:00
mjallen18
d855e75e48 dns 2026-02-13 15:18:07 -06:00
mjallen18
ae9075e795 pi5 2026-02-13 15:11:57 -06:00
mjallen18
3b3ec68a3c sp 2026-02-13 14:28:28 -06:00
mjallen18
e771770ae2 neb2 2026-02-13 14:27:59 -06:00
mjallen18
17a1307343 neb 2026-02-13 14:27:00 -06:00
mjallen18
fe77b68446 bro 2026-02-13 14:22:12 -06:00
mjallen18
6ebc1bb103 nebula 2026-02-13 13:14:35 -06:00
mjallen18
869a320ede collabora 2026-02-12 21:57:00 -06:00
mjallen18
fd437ec528 nextcloud broke lmao 2026-02-12 20:12:58 -06:00
mjallen18
9a63c609fa nextcloud broke lmao 2026-02-12 14:54:05 -06:00
mjallen18
b46628a747 nextcloud 2026-02-12 14:31:22 -06:00
mjallen18
48d864bb64 caddy 2026-02-11 22:26:34 -06:00
mjallen18
92b6e7a822 caddy 2026-02-11 22:23:00 -06:00
mjallen18
89275509f3 uns-sm 2026-02-10 20:12:21 -06:00
mjallen18
28c6306182 kernels 2026-02-10 20:07:40 -06:00
mjallen18
535fdc2f86 ha 2026-02-10 19:44:41 -06:00
mjallen18
09d9b010b7 dummy 2026-02-10 13:51:19 -06:00
mjallen18
ec76404122 ha 2026-02-10 12:39:24 -06:00
5c94bd7fcb ha up 2026-02-10 12:34:05 -06:00
07e8fc704f bruh 2026-02-09 21:18:53 -06:00
mjallen18
27e0a448b8 secrets 2026-02-09 21:15:42 -06:00
50773eda54 for test 2026-02-09 21:15:31 -06:00
c8f8fce410 perms lol 2026-02-09 21:07:18 -06:00
2184135c7c perms? 2026-02-09 21:06:34 -06:00
60df52b090 books2 2026-02-09 21:05:25 -06:00
5e6edd00bb books 2026-02-09 21:05:02 -06:00
319923c57c m 2026-02-09 21:00:12 -06:00
eb2ebdca84 mariadb 2026-02-09 20:58:55 -06:00
mjallen18
d9c801530d idk 2026-02-09 18:31:19 -06:00
f5b0b16c22 cache 2026-02-09 18:23:37 -06:00
a95bc4db39 cache 2026-02-09 18:04:24 -06:00
b5b5bf43e2 mac key 2026-02-09 17:58:47 -06:00
49e60a0d27 cache 2026-02-09 17:55:30 -06:00
3d21a345c3 idk 2026-02-09 17:50:26 -06:00
a740e1c33a idk 2026-02-09 16:45:56 -06:00
mjallen18
bc16420869 secrets 2026-02-09 16:45:11 -06:00
mjallen18
1731647367 sab 2026-02-09 16:35:55 -06:00
mjallen18
9ad06425c8 idk 2026-02-09 16:35:55 -06:00
525cc60739 plymouth apple 2026-02-09 16:01:50 -06:00
mjallen18
3595428e02 upd 20260209 2026-02-09 08:54:19 -06:00
4ed90347f8 boxbuddy 2026-02-09 08:21:41 -06:00
b92dda099b gnome caffeine 2026-02-06 13:36:32 -06:00
mjallen18
f61dbc7190 sops sync 2026-02-06 09:34:43 -06:00
a48fc8fffe ke 2026-02-06 09:34:00 -06:00
mjallen18
4d107533ea ppd 2026-02-06 09:26:20 -06:00
mjallen18
2c3cc8ed18 sadasdad 2026-02-06 09:25:41 -06:00
mjallen18
334d69894e stuff 2026-02-06 09:25:05 -06:00
mjallen18
31a9957877 idk 2026-02-06 09:11:49 -06:00
mjallen18
dc25f02010 config 2026-02-06 08:56:16 -06:00
mjallen18
ef8e52b93f sops 2026-02-06 08:50:23 -06:00
mjallen18
bf48d8fcf0 nix 2026-02-06 08:48:46 -06:00
mjallen18
a68c91264f ice 2026-02-05 17:39:47 -06:00
mjallen18
75d7097e23 fix nuc stuff 2026-02-05 17:05:30 -06:00
mjallen18
b738f38267 idk 2026-02-05 16:59:54 -06:00
mjallen18
32b0e67a7a idk 2026-02-05 16:48:27 -06:00
mjallen18
f352ef1c05 idk 2026-02-05 16:14:35 -06:00
mjallen18
1ea8c8b6a9 mac 2026-02-04 22:38:54 -06:00
mjallen18
eac9c4b67d http1 traefik 2026-02-04 22:05:56 -06:00
mjallen18
ce4047b62f sad 2026-02-04 21:15:28 -06:00
mjallen18
aff6fa9ec6 fix nuc stuff 2026-02-04 21:11:23 -06:00
mjallen18
a9c1d71495 fmt 2026-02-04 20:40:34 -06:00
mjallen18
4d4808490b fix traefik stuff 2026-02-04 20:02:32 -06:00
mjallen18
1f99318fcd stuffs like bruh wtf 2026-02-04 19:40:00 -06:00
mjallen18
87fb1c96e5 stuffs 2026-02-04 13:08:39 -06:00
mjallen18
aa7fc9c228 rpi lin 2026-02-03 10:21:00 -06:00
mjallen18
f856076452 stuffs 2026-02-02 19:33:04 -06:00
mjallen18
d9d7760e58 nix fmt 2026-02-02 18:14:04 -06:00
mjallen18
bd8d1e6485 cleanup 2026-02-02 18:11:45 -06:00
mjallen18
8fa82bce90 stuffs 2026-02-02 11:13:19 -06:00
mjallen18
187b478c5d hyprland rules 2026-01-31 10:19:49 -06:00
mjallen18
25ef179d3a kerns 2026-01-30 23:46:05 -06:00
mjallen18
57d1a8d864 what 2026-01-30 23:38:24 -06:00
mjallen18
c83cfcf06b stuff 2026-01-30 23:37:53 -06:00
mjallen18
59268d1da3 what 2026-01-30 22:47:46 -06:00
mjallen18
bca4a13614 bruh 2026-01-30 22:45:46 -06:00
mjallen18
044bac7464 fix mount 2026-01-30 08:43:19 -06:00
mjallen18
10fa5498ee idk shits so fucked lol 2026-01-30 08:36:17 -06:00
mjallen18
1331e69b58 idk shits so fucked lol 2026-01-29 19:15:22 -06:00
mjallen18
9c8733431d idk 2026-01-28 10:18:25 -06:00
mjallen18
ad15679eb4 idk 2026-01-27 18:21:37 -06:00
mjallen18
01ae622391 perms 2026-01-27 13:01:36 -06:00
mjallen18
defbd725dd secrets 2026-01-27 12:44:16 -06:00
mjallen18
d2c1cbf987 what 2026-01-27 12:34:40 -06:00
mjallen18
14db694b90 testing 2026-01-27 11:21:42 -06:00
mjallen18
10fc7820e1 testing 2026-01-27 11:13:58 -06:00
mjallen18
176d0b7311 testing 2026-01-26 18:01:56 -06:00
mjallen18
1fc2ce66cf testing 2026-01-26 17:39:52 -06:00
mjallen18
f93d69a259 testing 2026-01-26 17:30:16 -06:00
mjallen18
d7afac2fb9 testing 2026-01-26 17:20:27 -06:00
mjallen18
3aa0407532 testing 2026-01-26 17:18:18 -06:00
mjallen18
c29ff231f9 testing 2026-01-26 17:16:33 -06:00
c5481909a1 isk 2026-01-26 23:08:34 +00:00
mjallen18
8b4489cf8f testing 2026-01-26 15:38:24 -06:00
mjallen18
d1d68e154d tpm unlock+moondeck 2026-01-23 23:26:02 -06:00
mjallen18
3ff7bed41d tpm unlock 2026-01-23 16:05:44 -06:00
mjallen18
19acd60ed7 flake 2026-01-23 14:40:41 -06:00
mjallen18
6cfb31fdc9 remove sd 2026-01-23 14:33:59 -06:00
mjallen18
e5dbeadbb6 upd 2026-01-23 14:21:03 -06:00
mjallen18
302d7f5af5 build2 2026-01-23 13:30:48 -06:00
mjallen18
148229f551 proton-cachy rename for convenience 2026-01-22 21:41:00 -06:00
mjallen18
5a22ad0f88 fmt 2026-01-22 16:08:24 -06:00
mjallen18
5b96b59e60 edk 2026-01-22 15:37:42 -06:00
mjallen18
bfd5e90613 what a mess 2026-01-22 13:57:21 -06:00
mjallen18
fdaa34191c build2 2026-01-22 13:54:07 -06:00
mjallen18
fde852b29b fixes 2026-01-22 13:39:46 -06:00
mjallen18
d4e0b93e4e fix cachy kernel 2026-01-22 13:25:34 -06:00
mjallen18
1410f0fe25 package upd tui 2026-01-22 12:55:23 -06:00
mjallen18
5fed9a649f pi5d0 2026-01-22 11:54:53 -06:00
mjallen18
9b87aa9d3e fixes 2026-01-22 11:54:10 -06:00
mjallen18
e7a6fdb644 cleanup 2026-01-22 11:28:43 -06:00
mjallen18
871f099dac start dynamic dt overlays 2026-01-22 09:42:16 -06:00
mjallen18
a07f694806 pi 2026-01-22 09:12:36 -06:00
mjallen18
45f40e45d8 stuff 2026-01-22 09:06:45 -06:00
mjallen18
7c7c1dc3f4 cleanup 2026-01-22 08:53:24 -06:00
mjallen18
bd44913ccc cleanup 2026-01-22 08:51:34 -06:00
mjallen18
a5b53e66e7 cleanup 2026-01-22 08:49:09 -06:00
mjallen18
5d9613ee13 stuff 2026-01-22 08:45:26 -06:00
mjallen18
3dea6e0a9e test 2026-01-22 08:38:50 -06:00
mjallen18
9ab1c99914 move 2026-01-22 08:37:01 -06:00
mjallen18
62ff7b829a aarch maybe 2026-01-22 08:34:47 -06:00
mjallen18
477b79bfb8 move 2026-01-21 21:47:08 -06:00
mjallen18
e2e2d814fe nix fmt 2026-01-21 21:43:31 -06:00
mjallen18
94c3d6d6ff packages 2026-01-21 21:41:01 -06:00
mjallen18
a336b0cf60 packages 2026-01-21 21:17:36 -06:00
mjallen18
a94e68514a scripts 2026-01-21 20:48:12 -06:00
mjallen18
fb68d25008 bulk versions.json 2026-01-21 20:47:21 -06:00
mjallen18
2b9908e760 bulk versions.json 2026-01-21 12:53:13 -06:00
mjallen18
7cc4e8c99e what a mess 2026-01-20 13:53:10 -06:00
mjallen18
fbdbbdf328 aarch maybe 2026-01-20 13:50:46 -06:00
mjallen18
d4799b6598 what a mess 2026-01-20 13:50:33 -06:00
mjallen18
6a9ec8f933 what a mess 2026-01-20 12:41:45 -06:00
mjallen18
fa236b7076 aarch maybe 2026-01-20 12:38:59 -06:00
mjallen18
05bd3f1a60 bruh 2026-01-20 12:34:23 -06:00
mjallen18
a3ade57500 what a mess 2026-01-20 12:25:06 -06:00
mjallen18
e40181ba04 bruh 2026-01-20 12:18:46 -06:00
mjallen18
cedd4051f7 what a mess 2026-01-20 12:17:57 -06:00
mjallen18
5a992567b8 bcachefs-tools for 6.19 2026-01-20 09:04:49 -06:00
mjallen18
631bdd3c2c idk 2026-01-19 12:35:11 -06:00
mjallen18
a968cb4b4f idk 2026-01-19 12:11:19 -06:00
mjallen18
d76fdcbab6 idk 2026-01-19 12:11:19 -06:00
mjallen18
e2dfca0245 nothing n 2026-01-19 10:54:27 -06:00
mjallen18
59500f3e50 update cachy kernel 2026-01-19 10:44:49 -06:00
mjallen18
a74305a444 ally keys 2026-01-16 21:43:46 -06:00
mjallen18
e39614a9c8 pw 2026-01-16 18:52:08 -06:00
mjallen18
b5de056631 force 2026-01-16 18:27:53 -06:00
mjallen18
49680a9188 greeter 2026-01-16 18:27:22 -06:00
mjallen18
c3865f2742 ally 2026-01-16 18:25:49 -06:00
mjallen18
341d522fd4 steam stuff 2026-01-16 17:49:46 -06:00
mjallen18
51f4b335b1 cosmic? 2026-01-16 17:41:18 -06:00
mjallen18
77dc15e0e1 maybe 2026-01-16 14:14:18 -06:00
mjallen18
a0c2753f37 fix sd 2026-01-16 12:40:35 -06:00
mjallen18
7320b0ee87 sd 2026-01-16 12:36:28 -06:00
mjallen18
10268e7677 kde 2026-01-16 12:25:36 -06:00
mjallen18
f57617e268 stuff 2026-01-16 10:47:07 -06:00
mjallen18
b613f941a6 idk: 2026-01-16 08:45:50 -06:00
mjallen18
b3138d0fca stuff 2026-01-16 08:45:27 -06:00
mjallen18
9376005a21 stuff 2026-01-15 13:53:59 -06:00
mjallen18
549580395f avizo + kbd backlight 2026-01-15 12:50:25 -06:00
mjallen18
3f5634317b ver 2026-01-15 09:42:21 -06:00
mjallen18
26746873f3 naming 2026-01-15 09:41:43 -06:00
mjallen18
a58a9da64e rev 2026-01-15 09:33:49 -06:00
mjallen18
8e7a0343c2 some versions 2026-01-15 09:29:21 -06:00
mjallen18
d47b092a8c nothing n 2026-01-14 18:45:49 -06:00
mjallen18
4de54dee30 lol 2026-01-14 18:39:33 -06:00
mjallen18
455ea26cdc cleanup 2026-01-14 18:38:22 -06:00
mjallen18
7306b08762 fmt 2026-01-14 18:00:52 -06:00
mjallen18
59b4729636 fix warning 2026-01-14 16:38:42 -06:00
mjallen18
afe04b88b0 lol 2026-01-14 15:34:47 -06:00
mjallen18
b1215da790 wtf 2026-01-14 10:37:22 -06:00
mjallen18
b1f2521260 edk2 pi5 building 2026-01-13 19:29:42 -06:00
mjallen18
57add82f95 edk2 pi5 building 2026-01-13 19:28:08 -06:00
mjallen18
49485c91a6 idk 2026-01-13 18:38:20 -06:00
mjallen18
a7c25fe70d pi4 edk2 builds 2026-01-13 14:54:55 -06:00
mjallen18
1dc075f405 fix 2026-01-12 20:22:09 -06:00
mjallen18
2a17112d50 idk man 2026-01-12 20:11:49 -06:00
mjallen18
a59d5ce3b1 hass stuff 2026-01-12 15:26:41 -06:00
mjallen18
639ce36cb0 otbr 2026-01-12 15:13:02 -06:00
mjallen18
42e771ef1f otbr 2026-01-12 15:11:47 -06:00
mjallen18
37d472c7db hass 2026-01-12 14:11:04 -06:00
mjallen18
382f059188 upd full 2026-01-12 12:08:36 -06:00
mjallen18
dfd8d9a1a8 tailscale 2026-01-10 13:36:57 -06:00
mjallen18
fcddde0c06 temp sunshine 2026-01-09 16:17:22 -06:00
mjallen18
ad16b47827 librepods rust 2026-01-09 15:49:19 -06:00
mjallen18
994fddba66 set uefi nvram for convenience 2026-01-09 13:19:29 -06:00
mjallen18
0483ea8a67 cleanup dsc 2026-01-09 13:09:14 -06:00
mjallen18
b6e5223d96 nvram 2026-01-09 13:08:02 -06:00
mjallen18
ed7656cf3e up kern 2026-01-09 12:10:37 -06:00
mjallen18
76b6255169 edk 2026-01-09 12:10:25 -06:00
mjallen18
5f5972f10f pi5 2026-01-09 10:44:17 -06:00
mjallen18
82dffdf505 pi5 kernel 2026-01-09 10:40:07 -06:00
mjallen18
741ab5c7ec config 2026-01-09 09:03:39 -06:00
mjallen18
3d0d5c878c darwin 2026-01-09 09:02:20 -06:00
mjallen18
0bb71bbb4f pi stufF 2026-01-09 08:54:17 -06:00
mjallen18
741de00ef7 edk 2026-01-08 23:04:40 -06:00
mjallen18
c7c5192c0c pi5 2026-01-08 11:50:24 -06:00
mjallen18
052f7a2838 pi stufF 2026-01-07 22:38:59 -06:00
mjallen18
ca17f0fe18 uefi stuff 2026-01-07 21:28:20 -06:00
mjallen18
e76f74e63a uefi stuff 2026-01-07 19:06:52 -06:00
mjallen18
88e81c9aaa pi stufF 2026-01-07 15:32:50 -06:00
mjallen18
8406714527 16k 2026-01-07 15:27:39 -06:00
mjallen18
97e4060b52 uefi stuff 2026-01-07 12:44:59 -06:00
mjallen18
59121cf43b uefi stuff 2026-01-07 12:44:33 -06:00
mjallen18
e15d62d03a oops 2026-01-07 09:57:56 -06:00
mjallen18
3268c28cc8 openrgb 2026-01-07 09:46:13 -06:00
mjallen18
e4ac3b99cd mac stuff 2026-01-07 09:18:26 -06:00
mjallen18
dabd791fac nix format 2026-01-07 09:17:34 -06:00
mjallen18
98c627531c hass unstable 2026-01-06 14:10:11 -06:00
mjallen18
c8f41cf7d8 disable cache, stable 2026-01-06 13:44:05 -06:00
mjallen18
78f8d1a733 idk man 2026-01-06 10:21:50 -06:00
mjallen18
b05067a3e1 kernels 2026-01-05 23:57:21 -06:00
mjallen18
3b780d4d78 bruh 2026-01-05 23:31:06 -06:00
mjallen18
77c76e6483 chack 2026-01-05 14:54:52 -06:00
mjallen18
cd03862e4b pi stuff 2026-01-03 11:22:12 -06:00
mjallen18
e6a69cc21f user 2026-01-03 11:14:49 -06:00
mjallen18
c6317cdd18 fixes 2026-01-03 11:10:23 -06:00
mjallen18
ca73743483 lock update 2026-01-03 11:04:58 -06:00
mjallen18
105ed3bcb8 nix format 2026-01-03 09:28:28 -06:00
mjallen18
4a6a68dad4 temp fix glance 2026-01-03 09:27:28 -06:00
mjallen18
56154fe941 check 2026-01-02 20:30:05 -06:00
mjallen18
b7380317b9 cleanup 2026-01-02 14:46:36 -06:00
mjallen18
579c83164e fix 2026-01-02 14:45:58 -06:00
mjallen18
56fb195967 upd 2026-01-02 14:41:08 -06:00
mjallen18
451f5aa726 pi4 2026-01-02 14:31:10 -06:00
mjallen18
5c359dca81 ccache 2026-01-02 12:22:51 -06:00
mjallen18
6b28a8df9c lto 2026-01-02 12:21:51 -06:00
mjallen18
dfc2c8faa4 oop 2026-01-02 11:56:31 -06:00
mjallen18
0f42ffeebd arm 2026-01-02 11:55:52 -06:00
mjallen18
1d5b1cf5e0 check 2026-01-02 11:05:48 -06:00
mjallen18
8fc40f265b pi stuff 2026-01-02 09:47:24 -06:00
mjallen18
cdf388cf17 mbp 2026-01-02 09:14:09 -06:00
mjallen18
8088ea0933 mbp 2026-01-02 09:14:09 -06:00
mjallen18
9305483e09 lol 2026-01-02 00:06:48 -06:00
mjallen18
687a3a6446 cachy stuff 2026-01-01 23:11:27 -06:00
mjallen18
1e5ef59d56 fix overlay and upd 2026-01-01 22:00:31 -06:00
mjallen18
a7389ac9b2 temp 2026-01-01 21:47:14 -06:00
mjallen18
501329def1 uefi testing 2026-01-01 19:18:20 -06:00
mjallen18
3063d6b161 uefi testing 2026-01-01 19:15:41 -06:00
mjallen18
c4ea874ae7 parted 2025-12-31 09:23:43 -06:00
mjallen18
68dbe98370 uh 2025-12-30 20:40:45 -06:00
mjallen18
374bd4348f pi4 stuff 2025-12-30 20:40:22 -06:00
mjallen18
5ee4da5b08 dtbs 2025-12-30 17:35:51 -06:00
mjallen18
bf31786735 tree 2025-12-30 17:30:41 -06:00
mjallen18
b216fe5215 bruh 2025-12-30 14:28:48 -06:00
mjallen18
b36ae0914c sunshine apps 2025-12-30 11:11:30 -06:00
mjallen18
edd6291176 sunshine apps 2025-12-30 09:48:36 -06:00
mjallen18
b91afbc323 sunshine ports 2025-12-30 09:05:00 -06:00
mjallen18
450dc1ad91 sd discord 2025-12-30 08:43:43 -06:00
mjallen18
eed1d28bd8 sunshine 2025-12-30 08:41:48 -06:00
mjallen18
ae8bcdf690 idk 2025-12-29 21:43:09 -06:00
mjallen18
4ac6de479c uh 2025-12-29 19:46:31 -06:00
mjallen18
14990b28e4 overlay 2025-12-29 19:44:53 -06:00
mjallen18
ede0bf5c10 maybe 2025-12-29 19:43:26 -06:00
mjallen18
ae95786d8e comment 2025-12-29 19:26:22 -06:00
mjallen18
209e776640 dtsoverlays 2025-12-29 19:22:11 -06:00
mjallen18
82e062a7e8 checkpoint lol 2025-12-29 15:18:59 -06:00
mjallen18
67fb7d7323 'building' 2025-12-29 15:18:05 -06:00
mjallen18
462494277a idk 2025-12-29 10:49:59 -06:00
mjallen18
8fbf8c54eb cache 2025-12-27 11:45:28 -06:00
mjallen18
f549723a61 'building' 2025-12-26 22:47:11 -06:00
mjallen18
c8aebb9e4a ugh 2025-12-26 18:22:49 -06:00
mjallen18
afb60fc031 stuff 2025-12-26 16:20:02 -06:00
mjallen18
4604e24ee5 stuff 2025-12-26 14:54:03 -06:00
mjallen18
3233955690 kernel test 2025-12-26 14:53:27 -06:00
mjallen18
c9f75a053c upd and cache 2025-12-26 11:45:05 -06:00
mjallen18
f7cb1cb217 nuc 2025-12-23 22:14:22 -06:00
mjallen18
d981fb20c2 fmt ++ 2025-12-23 21:04:21 -06:00
mjallen18
8f08f24761 idk 2025-12-23 18:41:22 -06:00
mjallen18
74e772582d stuff and thangs 2025-12-23 10:41:21 -06:00
mjallen18
15f186e3ce darwin 2025-12-23 10:37:03 -06:00
mjallen18
7d92e9b1cc fix linux-builder ccache 2025-12-22 15:28:42 -06:00
mjallen18
53a2d01a83 up 2025-12-22 14:55:32 -06:00
mjallen18
abdeddf751 darwin 2025-12-19 17:05:58 -06:00
mjallen18
1280cf9939 darwin 2025-12-19 16:59:18 -06:00
mjallen18
76265f9b1b fix updates lol 2025-12-19 16:33:44 -06:00
mjallen18
66fe87dd23 update packages 2025-12-19 14:49:44 -06:00
mjallen18
e8fcf96253 services require storage 2025-12-19 14:06:18 -06:00
mjallen18
479ac18f20 some sops 2025-12-19 13:32:07 -06:00
mjallen18
ba446f408a matrix sops 2025-12-19 13:02:58 -06:00
mjallen18
05486efb75 mkModule various + fixes 2025-12-18 17:20:21 -06:00
mjallen18
e0b1e72431 mkModule various + fixes 2025-12-18 16:47:12 -06:00
mjallen18
63bd725d64 mkModule various 2025-12-17 14:11:49 -06:00
mjallen18
96ce0001c5 mkModule various 2025-12-17 12:52:42 -06:00
mjallen18
50345adeb5 mkModule gitea 2025-12-15 20:07:48 -06:00
mjallen18
e74ea5f13b teml 2025-12-15 19:51:47 -06:00
mjallen18
367c3a16c5 nuc 2025-12-15 17:44:30 -06:00
mjallen18
4c784f5f33 pi stuff 2025-12-15 15:06:54 -06:00
mjallen18
fa0210e937 pi stuff 2025-12-15 15:00:11 -06:00
mjallen18
f803c37105 t 2025-12-15 14:59:12 -06:00
mjallen18
03b00c59b2 mkModule code-server 2025-12-15 09:17:26 -06:00
mjallen18
5c9a42fe71 mkModule calibre 2025-12-15 09:00:04 -06:00
mjallen18
3b95a97921 mkModule migration begin 2025-12-14 22:58:07 -06:00
mjallen18
2d03954a9f mkModule migration begin 2025-12-14 22:54:01 -06:00
mjallen18
3dc1055b54 mkModule migration begin 2025-12-14 22:47:59 -06:00
mjallen18
fedba849a7 mkModule migration begin 2025-12-14 22:47:51 -06:00
mjallen18
34539045e5 mkModule 2025-12-14 21:50:50 -06:00
mjallen18
0012a019fc upd 2025-12-14 20:33:39 -06:00
mjallen18
f09246dcba test 2025-12-14 10:19:28 -06:00
mjallen18
90daf80a88 icons 2025-12-14 09:50:53 -06:00
mjallen18
3c85ea0515 winboat 2025-12-13 14:31:21 -06:00
mjallen18
06e26e3be2 t 2025-12-12 15:08:26 -06:00
mjallen18
d76eff6f68 cleanup 2025-12-12 13:39:40 -06:00
mjallen18
ce39a330b1 cachy kernel naming 2025-12-12 13:25:50 -06:00
mjallen18
41063a1ef7 upd-scr 2025-12-11 22:06:40 -06:00
mjallen18
35206c8ed5 syntax 2025-12-11 22:01:11 -06:00
mjallen18
201ba0b780 checker 2025-12-11 21:39:37 -06:00
mjallen18
a273c701ab server-lto 2025-12-11 21:25:50 -06:00
mjallen18
2e4b629805 lto server 2025-12-11 21:19:40 -06:00
mjallen18
3364ae8fda nuc 2025-12-11 21:00:17 -06:00
mjallen18
15e0b9563f lto server 2025-12-11 20:45:11 -06:00
mjallen18
64f34892b7 server-lto 2025-12-11 19:55:17 -06:00
mjallen18
8d1a9312cb upd 2025-12-11 19:14:52 -06:00
mjallen18
31202f8b0e upd 2025-12-11 17:41:05 -06:00
mjallen18
82ecfba7db libre 2025-12-11 09:50:21 -06:00
mjallen18
f86808d86e discord w/ krisp 2025-12-11 09:38:39 -06:00
mjallen18
89c5d60be3 update cachy kernel 2025-12-10 13:53:35 -06:00
mjallen18
96c3a34449 cachy test 2025-12-10 10:49:41 -06:00
mjallen18
a5eba8ec68 init cachy kernel 2025-12-10 09:30:35 -06:00
mjallen18
764ce12aea clouds 2025-12-09 20:26:18 -06:00
mjallen18
989e717e4e opencloud fucked still lmao 2025-12-09 17:15:49 -06:00
mjallen18
4b53a89030 upd 2025-12-09 16:05:59 -06:00
mjallen18
70a54b208e upd 2025-12-08 10:20:27 -06:00
mjallen18
0b4d63fd09 update ha components 2025-12-05 17:22:51 -06:00
mjallen18
f54285bfe6 up 2025-12-05 12:30:46 -06:00
mjallen18
a94f5c20aa ld 2025-12-05 10:23:34 -06:00
mjallen18
adf1cc7ca6 yubi 2025-12-05 09:35:01 -06:00
mjallen18
d32826dc36 ccache? 2025-12-04 20:09:27 -06:00
mjallen18
3c1c5b6292 stylix 2025-12-04 19:36:22 -06:00
mjallen18
73a11e1ac8 stylix 2025-12-04 19:35:47 -06:00
mjallen18
88e29f74de lower refresh cause artifact 2025-12-04 19:16:49 -06:00
mjallen18
59702e15cf darwin 2025-12-04 14:00:20 -06:00
mjallen18
6c8d306d38 kmscon theme 2025-12-01 17:29:31 -06:00
f172707b15 stylix (#3)
Co-authored-by: mjallen18 <matt.l.jallen@gmail.com>
Reviewed-on: #3
2025-12-01 17:26:26 -06:00
mjallen18
672221f471 pi stuff 2025-12-01 12:06:00 -06:00
414 changed files with 27771 additions and 12270 deletions

3
.gitignore vendored
View File

@@ -10,4 +10,5 @@ shell.nix
.envrc
.DS_Store
*.qcow2
keys
keys
iso-*

View File

@@ -2,19 +2,19 @@
keys:
- &matt-pgp CBCB9B18A6B8930B0B6ABFD1CCB8CBEB30633684
- &matt age157jemphjzg6zmk373vpccuguyw6e75qnkqmz8pcnn2yue85p939swqqhy0
- &matt_pi4 age13g9a4d4jrvckfddpgn8sm4kjtzajr67le56pfdg78ktr5pd09phq32j89u
- &matt_pi5 age1wpvfpv5n32lruk7c0da4uaeapsmhjxdvg8z4ljehn06l6g2y0e0sum404l
- &desktop age1jv8ap5zwa49ftv0gg7wqf5ps0e68uuwxe2fekjsn0zkyql964unqyc58rf
- &admin age1pm3fehmmk0vmnrscz9vm96rakn46aaldr5ydpscmde3v9x0k3faswwdzxs
- &jallen-nas age1mn2afyp9my7y7hcyzum0wdwt49zufnkt8swnyy8pj30cwzs4zvgsthj0lt
- &pi4 age1ykkjw57t3z3deup3gtp7dujyaslskn74e0d9hsmqaha2pj3rvazqgndw5a
- &pi5 age1t2d5scrukk0guva5sr97a8tge5j8kd865adezrcru7p269pzwvpsamkgje
- &deck age1c8qw59ffcq9l77gfmtyc3djtvt3md0u6dwhrjcgsm98ntyf72ufqugj7cg
- &steamdeck age1er5qucsc2mugrzrr7n3xhzv7kemkrqrw4m84r544fkk7nkg5g5eswxkqj0
- &matt_macbook-pro age19daqsncuzeh3j6cwk8uxp6yfj8h0qtz02jxlwwy4v8j0mfgznsvq30440g
- &macbook-pro age19w4zafpwnq9yhzuf8r5te2yhq7xlqj76rcgzcz935hllyrz4yvws4jn6ca
- &matt_macbook-pro age12gu9hqhd56yl5x3t5yenkn9yg57du08h77vzjqsmnu5hdppne38qcur5a0
- &macbook-pro age1t7378n8kmd3f32fkye2gw3jj6qswv3exjdx0dq8kl0xra3tmcdnsvddq3u
- &nuc age102el4snus37dj807rwvsmlvwu2sg2d8rw3vfmtntgczfkz04l9nshetcq0
- &admin_nuc age102el4snus37dj807rwvsmlvwu2sg2d8rw3vfmtntgczfkz04l9nshetcq0
- &admin_nuc age1yn82e39pxt0d0pgny34ux4lkge4ff7wxvsye8ragvwngehemt4ps27phyw
- &matt_allyx age18z4ctyyj7eq0cmt23eelfzjuacq4fa6hsplyg779d3rdg7ac2q5q2njxqh
- &allyx age1er5qucsc2mugrzrr7n3xhzv7kemkrqrw4m84r544fkk7nkg5g5eswxkqj0
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
@@ -22,12 +22,10 @@ creation_rules:
- *matt-pgp
age:
- *matt
- *matt_pi4
- *matt_pi5
- *desktop
- *admin
- *jallen-nas
- *pi4
- *pi5
- *deck
- *steamdeck
@@ -35,6 +33,8 @@ creation_rules:
- *macbook-pro
- *admin_nuc
- *nuc
- *matt_allyx
- *allyx
- path_regex: nas-secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
@@ -53,7 +53,7 @@ creation_rules:
- *desktop
- *admin
- *jallen-nas
- path_regex: steamdeck-secrets/[^/]+\.(yaml|json|env|ini)$
- path_regex: allyx-secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *matt-pgp
@@ -64,29 +64,16 @@ creation_rules:
- *steamdeck
- *admin
- *jallen-nas
- path_regex: pi4-secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *matt-pgp
age:
- *matt
- *matt_pi4
- *matt_pi5
- *desktop
- *pi4
- *pi5
- *admin
- *jallen-nas
- *matt_allyx
- *allyx
- path_regex: pi5-secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- pgp:
- *matt-pgp
age:
- *matt
- *matt_pi4
- *matt_pi5
- *desktop
- *pi4
- *pi5
- *admin
- *jallen-nas

208
AGENTS.md Normal file
View File

@@ -0,0 +1,208 @@
# Agent Guide
## Directory Structure
```
/etc/nixos/
├── flake.nix # Main flake configuration
├── flake.lock # Locked versions
├── AGENTS.md # This file
├── treefmt.nix # Code formatting config
├── qemu.nix # QEMU testing config
├── systems/ # System configurations by architecture
│ ├── aarch64-linux/
│ │ ├── macbook-pro-nixos/ # Apple Silicon MacBook
│ │ │ ├── default.nix
│ │ │ ├── boot.nix
│ │ │ ├── services.nix # logind, gdm, gnome, flatpak, etc.
│ │ │ ├── filesystems.nix
│ │ │ ├── hardware-configuration.nix
│ │ │ └── firmware/ # Asahi firmware
│ │ └── pi5/ # Raspberry Pi 5
│ │ ├── default.nix
│ │ ├── boot.nix
│ │ ├── adguard.nix
│ │ └── sops.nix
│ ├── x86_64-linux/
│ │ ├── matt-nixos/ # Desktop AMD system
│ │ │ ├── default.nix
│ │ │ ├── boot.nix
│ │ │ ├── filesystems.nix
│ │ │ ├── sops.nix
│ │ │ └── services/
│ │ │ ├── lsfg-vk/
│ │ │ ├── ratbagd/
│ │ │ └── restic/
│ │ ├── allyx/ # ASUS ROG Ally X
│ │ │ ├── default.nix
│ │ │ └── boot.nix
│ │ ├── nuc-nixos/ # Intel NUC
│ │ ├── jallen-nas/ # NAS server
│ │ └── iso-minimal/
│ └── aarch64-darwin/
│ └── macbook-pro/ # macOS (nix-darwin)
├── homes/ # Home-manager configurations
│ ├── aarch64-linux/
│ │ └── matt@macbook-pro-nixos/
│ │ └── default.nix
│ ├── x86_64-linux/
│ └── aarch64-darwin/
├── modules/ # Shared modules
│ ├── nixos/ # NixOS system modules
│ ├── home/ # Home-manager modules
│ └── darwin/ # nix-darwin modules
├── packages/ # Custom package overlays
│ ├── omnissa/
│ ├── bcachefs/
│ ├── raspberrypi/
│ ├── comfyui/
│ ├── homeassistant/
│ ├── librepods-beta/
│ └── ...
└── secrets/ # SOPS secrets
├── secrets.yaml # Master key config
└── *-secrets.yaml # Per-host secrets
```
## System Configurations
### macbook-pro-nixos (Apple Silicon MacBook)
- **Path**: `systems/aarch64-linux/macbook-pro-nixos/`
- **Key files**:
- `services.nix:72-81` - logind/sleep settings
- `default.nix` - main config, imports all parts
- `boot.nix` - systemd-boot, kernel params
- **Features**: Asahi Linux, GNOME, Hyprland option, battery management
### matt-nixos (AMD Desktop)
- **Path**: `systems/x86_64-linux/matt-nixos/`
- **Features**: AMD GPU (LACT), GNOME, gaming, Lanzaboote
### allyx (ASUS ROG Ally X)
- **Path**: `systems/x86_64-linux/allyx/`
- **Features**: Jovian NixOS, Steam, handheld-daemon, AMD GPU
### pi5 (Raspberry Pi 5)
- **Path**: `systems/aarch64-linux/pi5/`
- **Features**: Headless, AdGuard, Docker, static IP, UEFI boot
### jallen-nas (NAS Server)
- **Path**: `systems/x86_64-linux/jallen-nas/`
- **Features**: Headless, VPN, bcachefs, restic backups
## NixOS Modules (`modules/nixos/`)
### Desktop Environments
- `desktop/gnome/default.nix` - GNOME configuration
- `desktop/hyprland/default.nix` - Hyprland configuration
- `desktop/cosmic/default.nix` - Cosmic DE configuration
### Hardware
- `hardware/amd/default.nix` - AMD GPU (LACT)
- `hardware/nvidia/default.nix` - NVIDIA GPU
- `hardware/battery/default.nix` - Battery management
- `hardware/raspberry-pi/` - Raspberry Pi support
### Boot & System
- `boot/common/` - Common boot settings
- `boot/lanzaboote/` - Lanzaboote (secure boot)
- `boot/systemd-boot/` - Systemd-boot config
- `boot/plymouth/` - Plymouth splash screen
### Networking
- `network/default.nix` - Network configuration (hostname, firewall, NM)
- `network/options.nix` - Network module options
### Other Services
- `headless/default.nix` - Headless server config (watchdog, no suspend)
- `gaming/default.nix` - Steam, Gamescope, Gamemode
- `programs/default.nix` - System programs (nix-index, gnupg, etc.)
## Home-Manager Modules (`modules/home/`)
### Programs
- `programs/waybar/` - Wayland bar
- `programs/hyprland/` - Hyprland config
- `programs/kitty/` - Kitty terminal
- `programs/wofi/` - Wofi launcher
- `programs/wlogout/` - Logout menu
- `programs/btop/` - System monitor
- `programs/git/` - Git configuration
- `programs/zsh/` - Zsh configuration
- `programs/mako/` - Notification daemon
### Desktop
- `desktop/gnome/` - GNOME settings
- `desktop/stylix/` - Stylix theming
### Services
- `services/sops/` - SOPS integration
## Custom Packages (`packages/`)
- `omnissa/` - Omnissa Horizon client
- `bcachefs/` - Bcachefs tools
- `raspberrypi/` - Raspberry Pi firmware/tools
- `comfyui/` - ComfyUI packages
- `homeassistant/` - Home Assistant components
- `librepods-beta/` - LibrePODS beta (AirPods support)
## Common Patterns
### Enable a desktop environment
```nix
${namespace}.desktop.gnome.enable = true;
${namespace}.desktop.hyprland.enable = true;
```
### Enable SOPS
```nix
${namespace}.sops.enable = true;
```
### Enable headless mode
```nix
${namespace}.headless.enable = true;
```
### System imports
```nix
imports = [
./boot.nix
./filesystems.nix
./hardware-configuration.nix
./services.nix
];
```
### Namespace options (flake.nix:253)
```nix
namespace = "mjallen";
```
## SOPS Secrets
Secrets are encrypted with SOPS. Each system has its own secrets file:
- `secrets/mac-secrets.yaml` - macbook-pro-nixos
- `secrets/pi5-secrets.yaml` - pi5
- `secrets/allyx-secrets.yaml` - allyx
- `secrets/nuc-secrets.yaml` - nuc-nixos
- `secrets/nas-secrets.yaml` - jallen-nas
## Flake Inputs (flake.nix)
Key inputs:
- `nixpkgs-unstable` - Unstable channel
- `nixpkgs-stable` - Stable channel (25.11)
- `home-manager-unstable` - Home-manager
- `nixos-apple-silicon` - Apple Silicon support
- `nixos-hardware` - Common hardware configs
- `disko` - Disk partitioning
- `sops-nix` - Secrets management
- `lanzaboote` - Secure boot
- `jovian` - Steam Deck support (allyx)

127
README.md
View File

@@ -113,6 +113,133 @@ sudo nixos-rebuild switch --flake .#hostname
home-manager switch --flake .#username@hostname
```
## Secrets Management
Secrets are managed with [sops-nix](https://github.com/Mic92/sops-nix). Each secret file is encrypted with [age](https://age-encryption.org/), using the SSH host key (`/etc/ssh/ssh_host_ed25519_key`) of each machine as a recipient, so that machine can decrypt its own secrets at boot without any passphrase.
### How age keys work
sops-nix derives an age key from the machine's ed25519 SSH host key automatically. The corresponding age **public key** must be added to `.sops.yaml` before you can encrypt secrets for that machine.
To get the age public key for a machine:
```bash
# On the target machine (or from its host key file):
nix-shell -p ssh-to-age --run \
'ssh-keyscan localhost 2>/dev/null | ssh-to-age'
# Or directly from the key file:
nix-shell -p ssh-to-age --run \
'ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub'
```
### Adding a new machine
1. **Get the age public key** for the new machine using the command above.
2. **Add it to `.sops.yaml`**:
```yaml
keys:
- &new-machine age1<public-key-here>
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *new-machine
# ... existing recipients
```
3. **Re-encrypt all secret files** so the new machine becomes a recipient:
```bash
find secrets/ -name '*.yaml' -exec sops updatekeys {} \;
```
### Adding a new secret
To add a secret to an existing file:
```bash
# Edit the file interactively (sops decrypts, opens $EDITOR, re-encrypts on save)
sops secrets/nas-secrets.yaml
```
To create a new secrets file:
```bash
sops secrets/mymachine-secrets.yaml
```
The `.sops.yaml` `creation_rules` determine which keys encrypt the file based on its path.
### Generating Nebula VPN certificates
The Nebula module (`mjallen.services.nebula`) expects three secrets per host under a configurable prefix:
- `<prefix>/ca-cert` — the CA certificate (shared across all nodes)
- `<prefix>/host-cert` — this node's signed certificate
- `<prefix>/host-key` — this node's private key
**Step 1 — Create the CA** (once per network, on a trusted machine):
```bash
nebula-cert ca -name "jallen-nebula"
# Produces: ca.crt, ca.key
```
**Step 2 — Sign a certificate for each node**:
```bash
# Lighthouse (assign an overlay IP, e.g. 10.1.1.1)
nebula-cert sign -name "pi5" -ip "10.1.1.1/24" \
-ca-crt ca.crt -ca-key ca.key \
-out-crt lighthouse.crt -out-key lighthouse.key
# Regular node (assign a unique overlay IP, e.g. 10.1.1.2)
nebula-cert sign -name "nas" -ip "10.1.1.2/24" \
-ca-crt ca.crt -ca-key ca.key \
-out-crt nas.crt -out-key nas.key
```
**Step 3 — Add the secrets to SOPS**:
```bash
# Edit the target host's secrets file
sops secrets/pi5-secrets.yaml
```
Add the certificate contents under the configured prefix (e.g. `pi5/nebula`):
```yaml
pi5:
nebula:
ca-cert: |
<contents of ca.crt>
lighthouse-cert: |
<contents of lighthouse.crt>
lighthouse-key: |
<contents of lighthouse.key>
```
The key name for the cert/key pair matches the `hostSecretName` option (e.g. `hostSecretName = "lighthouse"` → looks for `lighthouse-cert` / `lighthouse-key`).
**Step 4 — Shred the plaintext key files** once they are in SOPS:
```bash
shred -u ca.key lighthouse.key nas.key
```
> Keep `ca.crt` accessible if you need to sign more nodes later, but store `ca.key` only in SOPS.
## Documentation
Comprehensive documentation is available in the [docs](./docs) directory:
- [Getting Started](./docs/getting-started.md) - Instructions for setting up new systems
- [Architecture](./docs/architecture.md) - Overview of the repository structure
- [System Configurations](./docs/systems/README.md) - Details about each system
- [Home Assistant](./docs/home-assistant/README.md) - Home Assistant setup and automations
- [Custom Modules](./docs/modules/README.md) - Details about reusable configuration modules
- [Troubleshooting](./docs/troubleshooting.md) - Common issues and solutions
## License
This project is licensed under the MIT License - see the LICENSE file for details.

383
WORKAROUNDS.md Normal file
View File

@@ -0,0 +1,383 @@
# Workarounds, Overrides & Temporary Fixes
This document tracks all known workarounds, patches, and temporary overrides in this flake.
Each entry includes the file location, reason, and whether it is still required.
**Status legend:**
- `ACTIVE` — still required, upstream fix not available
- `REDUNDANT` — upstream has fixed the issue; this override can be removed
- `UPSTREAM PENDING` — waiting on an upstream PR/issue
- `INTENTIONAL` — permanent design decision, not a workaround
---
## Overlays (upstream package overrides)
### `overlays/cosmic-settings-daemon/default.nix`
**Status:** `ACTIVE — UPSTREAM PENDING`
`cosmic-settings-daemon 1.0.8` has a buggy `Cargo.lock` that references
`https://github.com/pop-os/dbus-settings-bindings` at two different commits
(`3b86984` for `cosmic-dbus-a11y`/`locale1`/`upower_dbus`, and `0fa672f8`
for the `cosmic-settings-daemon` subcrate). `cargoSetupHook` (used by
`fetchCargoVendor`/`cargoHash`) rejects this: *"Sources are not allowed to be
defined multiple times."*
The fix overrides `cargoDeps` with `rustPlatform.importCargoLock`, which uses
a different vendoring strategy that handles multiple commits from the same repo.
**Removal condition:** When nixpkgs updates `cosmic-settings-daemon` past 1.0.8
with a fixed `Cargo.lock`, or applies `cargoLock` in its own package definition.
---
### `overlays/cosmic-applets/default.nix`
**Status:** `ACTIVE — UPSTREAM PENDING`
`cosmic-applets 1.0.8` has the same class of bug: its `Cargo.lock` references
`https://github.com/pop-os/cosmic-settings` at two different commits (`b46a55d`
for `cosmic-pipewire` and `cosmic-settings-sound-subscription`, and `55b502d`
for `cosmic-settings-a11y-manager-subscription` and several other crates).
`cargoSetupHook` rejects this with the same "Sources are not allowed to be
defined multiple times" error.
Same fix as `cosmic-settings-daemon`: overrides `cargoDeps` with
`rustPlatform.importCargoLock`.
**Removal condition:** When nixpkgs updates `cosmic-applets` past 1.0.8 with a
fixed `Cargo.lock`, or applies `cargoLock` in its own package definition.
---
### ~~`overlays/waybar/default.nix`~~ — REMOVED
**Status:** `REMOVED`
Previously added `-Dexperimental=true` to waybar's meson flags. nixpkgs now
includes `-Dexperimental=true` in its waybar definition, making the overlay
redundant. Removed.
---
### `overlays/radios/default.nix`
**Status:** `ACTIVE` (protective — needed after next `flake update`)
`radios` requires `pycountry>=24.0.0,<25.0.0` (PEP 440: `^24.0.0`). The
current locked nixpkgs has `pycountry 24.6.1` (in range), but nixpkgs HEAD
has already bumped `pycountry` to `26.2.16`, which will break `radios` after
the next `flake update`. The overlay applies `pythonRelaxDepsHook` to loosen
the upper bound.
**Removal condition:** When the upstream `radios` package (`frenck/python-radios`)
or nixpkgs relaxes the pycountry version constraint.
---
### `overlays/redis/default.nix`
**Status:** `INTENTIONAL`
Replaces `redis` with `valkey` (the Redis community fork) globally. This is a
deliberate preference for the open-source fork over the Redis 7.x+ license change.
---
### `overlays/stable/default.nix`
**Status:** `INTENTIONAL`
Injects `pkgs.stable` as an attribute pointing to the stable nixpkgs channel,
so modules can selectively pull in stable packages. Not a workaround.
---
## Flake Inputs (forks and custom branches)
### `nixpkgs-otbr` — `github:mrene/nixpkgs/openthread-border-router`
**File:** `flake.nix:8`
**Status:** `ACTIVE — UPSTREAM PENDING`
`openthread-border-router` is not yet packaged in nixpkgs-unstable. A community
member's nixpkgs fork provides the package, used by
`modules/nixos/homeassistant/services/thread/default.nix`.
The fork is ~52,000 commits behind `nixos-unstable`, so it is pulled
only via `pkgs.callPackage` from the fork's path, not as a full channel overlay.
**Removal condition:** When `openthread-border-router` is merged into nixpkgs.
Check: https://github.com/NixOS/nixpkgs/pulls?q=openthread-border-router
---
### `snowfall-lib` — `github:mjallen18/snowfall-lib`
**File:** `flake.nix:26`
**Status:** `INTENTIONAL`
Personal fork of `snowfallorg/lib` with 46 commits ahead of upstream, including:
- `fix: pass namespace argument to overlays`
- `fix: pass namespace argument to home-manager modules`
- `feat: support same username across multiple targets`
- `feat: enable per-channel configuration and fix pkgs selection`
- Performance improvements and additional features
These are custom changes required by this flake's structure that have not been
upstreamed.
---
### `steam-rom-manager` — `github:mjallen18/nix-steam-rom-manager`
**File:** `flake.nix:41`
**Status:** `INTENTIONAL`
Personal fork/packaging of nix-steam-rom-manager. The upstream
(`nix-community/nix-steam-rom-manager`) may or may not exist; this is a
maintained fork.
---
### Commented-out: `nvmd/disko` fork
**File:** `flake.nix:59-61`
**Status:** `REDUNDANT` (already disabled)
```nix
# the fork is needed for partition attributes support
# url = "github:nvmd/disko/gpt-attrs";
```
A community fork of disko with GPT partition attribute support was previously
used but has since been switched back to upstream `nix-community/disko`. The
comment can be cleaned up if the feature is no longer needed.
---
## Build Fixes & postPatch
### `packages/edk2-basetools/default.nix` — OpenSSL vendoring FIXME
**File:** `packages/edk2-basetools/default.nix:50-52`
**Status:** `UPSTREAM PENDING` (verify PR reference)
```nix
# FIXME: unvendor OpenSSL again once upstream updates
# to a compatible version.
# Upstream PR: https://github.com/tianocore/edk2/pull/10946
```
The comment references tianocore/edk2 PR #10946, but that PR's title is
*"update to openssl 3.5.1"* (now merged), not an unvendoring PR. The FIXME
comment may be referencing the wrong PR number or the issue may have evolved.
The edk2 build still vendor-patches OpenSSL compatibility; this should be
re-evaluated against the current edk2 upstream.
**Action:** Verify whether the OpenSSL vendoring is still needed with current
edk2, and update or remove the FIXME comment.
---
### `packages/bcachefs/default.nix` — Tests disabled
**File:** `packages/bcachefs/default.nix:100`
**Status:** `ACTIVE`
```nix
# FIXME: Try enabling this once the default linux kernel is at least 6.7
doCheck = false; # needs bcachefs module loaded on builder
```
Tests require a live bcachefs kernel module in the build sandbox, which is
not available. The comment mentions kernel ≥ 6.7, which is now the case
(nixpkgs is on 6.12+), but the underlying sandbox limitation still applies —
the build sandbox cannot load kernel modules regardless of kernel version.
**Action:** The `6.7` condition is now met but irrelevant; update the comment
to reflect that the real blocker is sandbox access to kernel modules.
---
### `packages/raspberrypi/linux-rpi/default.nix` — Failed kernel attempts
**File:** `packages/raspberrypi/linux-rpi/default.nix:25-43`
**Status:** `ACTIVE` (informational)
Four newer RPi kernel versions (6.15.11 through 6.19.0-rc5) are commented out
because they "fail for various reasons." The active version is pinned to an
older working commit.
**Action:** Periodically attempt to enable a newer kernel tag. The comments
serve as a history of failed attempts.
---
### `packages/raspberrypi/linux-rpi/default.nix` — DTB aliasing hack
**File:** `packages/raspberrypi/linux-rpi/default.nix:110-148`
**Status:** `ACTIVE`
```nix
# Make copies of the DTBs named after the upstream names so that U-Boot finds them.
# This is ugly as heck, but I don't know a better solution so far.
```
RPi's kernel ships DTBs with non-standard names (e.g. `bcm2708-rpi-zero-w.dtb`);
U-Boot expects canonical upstream names (e.g. `bcm2835-rpi-zero.dtb`). DTBs
are duplicated in `postFixup`.
---
### `packages/homeassistant/ha-mail-and-packages/default.nix` — Hardcoded paths
**File:** `packages/homeassistant/ha-mail-and-packages/default.nix:25-37`
**Status:** `ACTIVE`
The upstream HA integration hard-codes paths relative to its source directory,
which breaks in the Nix store. `postPatch` rewrites them to
`/var/lib/homeassistant/images/`.
---
### `packages/homeassistant/ha-wyzeapi/default.nix` — Version pin relaxation
**File:** `packages/homeassistant/ha-wyzeapi/default.nix:24-27`
**Status:** `ACTIVE`
Relaxes the minimum `wyzeapy` version pin from `0.5.28` to `0.5.27` to match
the version packaged in this flake.
---
## Raspberry Pi — Structural Overrides
### `modules/nixos/hardware/raspberry-pi/default.nix` — jemalloc 16K pages
**File:** `modules/nixos/hardware/raspberry-pi/default.nix:458-470`
**Status:** `ACTIVE` (structural)
```nix
# https://github.com/nvmd/nixos-raspberrypi/issues/64
jemalloc = prev.jemalloc.overrideAttrs (old: {
configureFlags = ... ++ [ "${pageSizeFlag}=14" ];
});
```
RPi5 uses 16K memory pages (2^14). jemalloc must be compiled with
`--with-lg-page=14`, otherwise it will use incorrect page size assumptions
and likely crash or corrupt memory.
**References:** https://github.com/nvmd/nixos-raspberrypi/issues/64
---
### `systems/aarch64-linux/pi5/boot.nix` — 16K page kernel
**File:** `systems/aarch64-linux/pi5/boot.nix:22-35`
**Status:** `ACTIVE` (structural)
Forces `CONFIG_ARM64_16K_PAGES=y` in the kernel config via `linux_6_19.override`.
`ignoreConfigErrors = true` is required because some kernel config options are
unavailable and would otherwise fail validation.
---
### `packages/raspberrypi/ffmpeg-rpi/default.nix` — RPi hardware codec ffmpeg
**File:** `packages/raspberrypi/ffmpeg-rpi/default.nix`
**Status:** `ACTIVE` (structural)
Custom ffmpeg build from `jc-kynesim/rpi-ffmpeg` fork with RPi hardware codec
support (`--enable-v4l2-request`, `--enable-sand`, etc.). Tests disabled
(`doCheck = false`) because the `imgutils` test fails on this build.
---
## systemd Service Overrides
### `systems/x86_64-linux/matt-nixos/default.nix` — networkd-wait-online
**File:** `systems/x86_64-linux/matt-nixos/default.nix:78`
**Status:** `INTENTIONAL`
```nix
systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
```
The `systemd-networkd-wait-online` service times out on this desktop,
blocking boot. Standard workaround for desktop systems that don't require
all interfaces to be up before proceeding.
---
### `systems/x86_64-linux/allyx/default.nix` — Jovian NixOS conflicts
**File:** `systems/x86_64-linux/allyx/default.nix:121-123`
**Status:** `ACTIVE`
```nix
systemd-networkd-wait-online.enable = lib.mkForce false;
power-profiles-daemon.enable = lib.mkForce false;
inputplumber.enable = lib.mkForce false;
```
On the ASUS ROG Ally X with Jovian NixOS and `handheld-daemon`:
- `power-profiles-daemon` conflicts with `handheld-daemon`'s power management
- `inputplumber` conflicts with `handheld-daemon`'s input handling
- `systemd-networkd-wait-online` times out as on matt-nixos
---
### `modules/nixos/services/crowdsec/default.nix` — DynamicUser conflict
**File:** `modules/nixos/services/crowdsec/default.nix:133-143`
**Status:** `ACTIVE — UPSTREAM PENDING`
```nix
systemd.services.crowdsec.serviceConfig.DynamicUser = lib.mkForce false;
systemd.services.crowdsec-firewall-bouncer.serviceConfig.DynamicUser = lib.mkForce false;
systemd.services.crowdsec-firewall-bouncer-register.serviceConfig.DynamicUser = lib.mkForce false;
```
The upstream NixOS crowdsec module uses `ReadWritePaths` (not `StateDirectory`)
on the main `crowdsec.service`, expecting `/var/lib/crowdsec` to be a real
directory. However, `crowdsec-firewall-bouncer-register` declares
`StateDirectory=crowdsec` with `DynamicUser=true`, which conflicts — it tries
to create `/var/lib/private/crowdsec` and symlink `/var/lib/crowdsec` to it,
but the directory already exists as a real path. Disabling `DynamicUser` on
all three services resolves the conflict by using the real `crowdsec` user.
Additionally, `crowdsec-firewall-bouncer-register` calls `cscli` without `-c`,
expecting `/etc/crowdsec/config.yaml` to exist, but the upstream NixOS module
uses a Nix store path via `-c` and never creates that file. The module works
around this by extracting the store path at eval time.
**Removal condition:** When the NixOS crowdsec module is fixed upstream to
handle state directory ownership consistently.
---
## Incomplete / TODO Items
These are not workarounds but known incomplete configurations:
| File | Line | Description |
|------|------|-------------|
| `systems/x86_64-linux/jallen-nas/sops.nix` | 89, 113 | Collabora and MariaDB secrets not configured |
| `systems/x86_64-linux/jallen-nas/apps.nix` | 47 | Authentik environment secrets file not wired up |
| `modules/nixos/services/sparky-fitness/default.nix` | — | ~~DB passwords not yet moved to SOPS~~ — resolved; secrets now via `mkSopsEnvFile`; run `sops secrets/nas-secrets.yaml` to add real values for `jallen-nas/sparky-fitness/{db-password,api-encryption-key,auth-secret}` |
| `modules/nixos/services/your-spotify/default.nix` | 36 | Spotify API keys not yet moved to SOPS |
| `modules/nixos/services/booklore/default.nix` | 25 | Database password not yet a SOPS secret |
| `packages/raspberrypi/udev-rules/default.nix` | 33 | `15-i2c-modprobe.rules` disabled; `i2cprobe` script not ported |
| `modules/nixos/homeassistant/services/homeassistant/default.nix` | 214 | `roborock` integration marked broken |
---
## Kernel Boot Parameters
### `systems/x86_64-linux/matt-nixos/boot.nix` — NVMe power saving
**File:** `systems/x86_64-linux/matt-nixos/boot.nix:46-48`
**Status:** `ACTIVE`
```nix
"nvme_core.default_ps_max_latency_us=0"
"pcie_aspm=off"
```
NVMe SSD power-saving states cause latency/stability issues on this machine.
Disabling ASPM and NVMe power states is a standard workaround for affected
hardware.
---
### `systems/aarch64-linux/macbook-pro-nixos/boot.nix` — Fan control
**File:** `systems/aarch64-linux/macbook-pro-nixos/boot.nix:28`
**Status:** `ACTIVE`
```nix
"melt_my_mac=1"
```
Undocumented Asahi Linux kernel parameter that enables fan control on Apple
Silicon Macs. The name is intentional (set by the Asahi kernel developers).

73
checks/disksnstuff.sh Normal file → Executable file
View File

@@ -1,15 +1,62 @@
mount -t tmpfs -o mode=755 none /mnt
mkdir -p /mnt/{boot,home,root,etc,nix,var/log}
mount /dev/sdb1 /mnt/boot
mount /dev/sdb3 -o compress=zstd,subvol=home /mnt/home
mount /dev/sdb3 -o compress=zstd,noatime,subvol=root /mnt/root
mount /dev/sdb3 -o compress=zstd,noatime,subvol=etc /mnt/etc
mount /dev/sdb3 -o compress=zstd,noatime,subvol=nix /mnt/nix
mount /dev/sdb3 -o compress=zstd,noatime,subvol=log /mnt/var/log
#!/usr/bin/env bash
wpa_passphrase "Joey's Jungle 5G" "kR8v&3Qd" > 5g.conf
wpa_supplicant -i wlp6s0 -c 5g.conf -B
dhcpcd
disk=/dev/mapper/nuc-nixos-cryptroot
keyctl link @u @s
clevis decrypt < "/etc/clevis/nas_pool.jwe" | bcachefs unlock /dev/disk/by-label/nas_pool
# sudo mkfs.vfat "$disk"1
# sudo bcachefs format --label ssd.ssd1 --compression=zstd --discard "$disk"
sudo mount -t tmpfs -o mode=755 none /mnt
sudo mkdir -p /mnt/{boot,home,root,etc,nix,var/log,tmp,persist}
sudo mount /dev/disk/by-partlabel/disk-main-nuc-nixos-EFI /mnt/boot
# sudo mkdir -p /mnt/boot/firmware
# sudo mount "$disk"2 /mnt/boot/firmware
# sudo mount "$disk"2 -o compress=zstd,subvol=home /mnt/home
# sudo mount "$disk"2 -o compress=zstd,noatime,subvol=root /mnt/root
# sudo mount "$disk"2 -o compress=zstd,noatime,subvol=etc /mnt/etc
# sudo mount "$disk"2 -o compress=zstd,noatime,subvol=nix /mnt/nix
# sudo mount "$disk"2 -o compress=zstd,noatime,subvol=log /mnt/var/log
# bcachefs unlock -k session /dev/disk/by-partlabel/disk-main-nuc-nixos-bcachefs-root
sudo cryptsetup open /dev/disk/by-partlabel/disk-main-nuc-nixos-cryptroot nuc-nixos-cryptroot
# sudo bcachefs unlock -k session "$disk"2
# sudo mount "$disk" /mnt/tmp
# cd /mnt/tmp
# ls -alh
# sudo bcachefs subvolume create nix
# sudo bcachefs subvolume create etc
# sudo bcachefs subvolume create log
# sudo bcachefs subvolume create root
# sudo bcachefs subvolume create persist
# sudo bcachefs subvolume create home
# ls -alh
# cd /etc/nixos
# sudo umount /mnt/tmp
sudo mount -o noatime,X-mount.subdir=nix "$disk" /mnt/nix
sudo mount -o noatime,X-mount.subdir=etc "$disk" /mnt/etc
sudo mount -o noatime,X-mount.subdir=log "$disk" /mnt/var/log
sudo mount -o noatime,X-mount.subdir=root "$disk" /mnt/root
sudo mount -o noatime,X-mount.subdir=persist "$disk" /mnt/persist
sudo mount -o X-mount.subdir=home "$disk" /mnt/home
# tree /mnt
# sudo nixos-install --flake /etc/nixos#nuc-nixos
# sudo umount /mnt/boot
# sudo umount /mnt/var/log
# sudo umount /mnt/persist
# sudo umount /mnt/home
# sudo umount /mnt/root
# sudo umount /mnt/etc
# sudo umount /mnt/nix
# sudo umount /mnt
# wpa_passphrase "Joey's Jungle 5G" "kR8v&3Qd" > 5g.conf
# wpa_supplicant -i wlp6s0 -c 5g.conf -B
# dhcpcd
# keyctl link @u @s
# clevis decrypt < "/etc/clevis/nas_pool.jwe" | bcachefs unlock /dev/disk/by-label/nas_pool

12
docs/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Documentation
This directory contains comprehensive documentation for the NixOS configuration.
## Contents
- [Getting Started](./getting-started.md) - Instructions for setting up new systems
- [System Configurations](./systems/README.md) - Detailed information about each system
- [Home Assistant](./home-assistant/README.md) - Documentation for the Home Assistant setup
- [Custom Modules](./modules/README.md) - Information about reusable modules
- [Architecture](./architecture.md) - Overview of the repository architecture
- [Troubleshooting](./troubleshooting.md) - Common issues and solutions

181
docs/architecture.md Normal file
View File

@@ -0,0 +1,181 @@
# Repository Architecture
This document provides an overview of the repository architecture, explaining how the various components fit together.
## Overview
This NixOS configuration repository is built using [Nix Flakes](https://nixos.wiki/wiki/Flakes) and [Snowfall Lib](https://github.com/snowfallorg/lib) to provide a modular, maintainable configuration for multiple systems. The Snowfall namespace is `mjallen`, so all custom options are accessed as `mjallen.<domain>.<name>`.
## Directory Structure
```
.
├── flake.nix # Main flake — inputs, outputs, Snowfall config
├── flake.lock # Locked dependency versions
├── .sops.yaml # SOPS key management rules
├── treefmt.nix # Code formatter configuration
├── qemu.nix # QEMU VM testing config
├── checks/ # Pre-commit hooks and CI checks
├── docs/ # Documentation (this directory)
├── homes/ # Home Manager configurations
│ ├── aarch64-darwin/ # macOS user configs
│ ├── aarch64-linux/ # ARM Linux user configs
│ └── x86_64-linux/ # x86 Linux user configs
├── lib/ # Custom Nix library utilities
│ ├── module/ # mkModule, mkOpt, mkBoolOpt helpers
│ ├── file/ # File/path utilities
│ └── versioning/ # Package version pinning helpers
├── modules/ # Reusable configuration modules
│ ├── home/ # Home Manager modules
│ ├── nixos/ # NixOS system modules
│ └── darwin/ # nix-darwin modules (macOS)
├── overlays/ # Nixpkgs overlays
├── packages/ # Custom package definitions
├── secrets/ # SOPS-encrypted secret files
└── systems/ # Per-host system configurations
├── aarch64-darwin/ # macOS (nix-darwin) hosts
├── aarch64-linux/ # ARM Linux hosts
├── x86_64-install-iso/# Install ISO configurations
└── x86_64-linux/ # x86_64 Linux hosts
```
## Flake Inputs
| Input | Source | Purpose |
|---|---|---|
| `nixpkgs-unstable` | `github:NixOS/nixpkgs/nixos-unstable` | Primary package set |
| `nixpkgs-stable` | `github:NixOS/nixpkgs/nixos-25.11` | Stable package set |
| `nixpkgs-otbr` | `github:mrene/nixpkgs` (fork) | OpenThread Border Router packages |
| `home-manager-unstable` | `github:nix-community/home-manager` | User environment management |
| `home-manager-stable` | `github:nix-community/home-manager/release-25.11` | Stable home-manager |
| `snowfall-lib` | `github:mjallen18/snowfall-lib` | Flake structure library (personal fork) |
| `impermanence` | `github:nix-community/impermanence` | Ephemeral root filesystem support |
| `lanzaboote` | `github:nix-community/lanzaboote/v1.0.0` | Secure Boot |
| `nixos-hardware` | `github:NixOS/nixos-hardware` | Hardware-specific NixOS configs |
| `sops-nix` | `github:Mic92/sops-nix` | Secret management |
| `disko` | `github:nix-community/disko` | Declarative disk partitioning |
| `cosmic` | `github:lilyinstarlight/nixos-cosmic` | COSMIC desktop environment |
| `jovian` | `github:Jovian-Experiments/Jovian-NixOS` | Steam Deck / handheld support |
| `nixos-apple-silicon` | `github:nix-community/nixos-apple-silicon` | Asahi Linux / Apple Silicon |
| `darwin` | `github:nix-darwin/nix-darwin` | macOS system configuration |
| `nix-homebrew` | `github:zhaofengli/nix-homebrew` | Declarative Homebrew (macOS) |
| `stylix` | `github:nix-community/stylix` | System-wide theming |
| `nix-vscode-extensions` | `github:nix-community/nix-vscode-extensions` | VS Code extension packages |
| `authentik-nix` | `github:nix-community/authentik-nix` | Authentik SSO |
| `nix-cachyos-kernel` | `github:xddxdd/nix-cachyos-kernel` | CachyOS optimised kernels |
| `lsfg-vk` | `github:pabloaul/lsfg-vk-flake` | Lossless Scaling frame generation (Linux) |
| `nix-index-database` | `github:nix-community/nix-index-database` | Pre-built nix-index database |
| `steam-rom-manager` | `github:mjallen18/nix-steam-rom-manager` | Steam ROM Manager package |
| `nix-plist-manager` | `github:sushydev/nix-plist-manager` | macOS plist management |
| `nix-rosetta-builder` | `github:cpick/nix-rosetta-builder` | Rosetta build support (macOS) |
| `pre-commit-hooks-nix` | `github:cachix/pre-commit-hooks.nix` | Pre-commit hooks |
| `treefmt-nix` | `github:numtide/treefmt-nix` | Code formatting |
`nixpkgs` and `home-manager` are aliases pointing to the unstable variants.
## Module System
### Structure
All modules follow a standard Snowfall Lib pattern and are automatically discovered. Each module exposes options under the `mjallen` namespace:
```nix
# Enable a module
mjallen.services.jellyfin.enable = true;
mjallen.desktop.gnome.enable = true;
mjallen.hardware.amd.enable = true;
```
### `mkModule` helper
Most service modules are built with `lib.mjallen.mkModule` (`lib/module/default.nix`), which provides a standard set of options:
| Option | Default | Description |
|---|---|---|
| `enable` | `false` | Enable/disable the module |
| `port` | `80` | Service listen port |
| `listenAddress` | `"0.0.0.0"` | Bind address |
| `openFirewall` | `true` | Open firewall ports |
| `configDir` | `/var/lib/<name>` | Config directory |
| `dataDir` | `/var/lib/<name>/data` | Data directory |
| `createUser` | `false` | Create a dedicated system user |
| `configureDb` | `false` | Create a PostgreSQL database |
| `environmentFile` | `null` | Path to an env-file |
| `reverseProxy.enable` | `false` | Add a Caddy reverse proxy block |
| `reverseProxy.subdomain` | `<name>` | Caddy subdomain |
| `redis.enable` | `false` | Create a dedicated Redis instance |
### NixOS modules (`modules/nixos/`)
| Category | Paths | Description |
|---|---|---|
| Boot | `boot/common/`, `boot/lanzaboote/`, `boot/plymouth/`, `boot/systemd-boot/` | Bootloader configurations |
| Desktop | `desktop/gnome/`, `desktop/hyprland/`, `desktop/cosmic/` | Desktop environments |
| Development | `development/` | Dev tools, language support, containers |
| Hardware | `hardware/amd/`, `hardware/nvidia/`, `hardware/battery/`, `hardware/raspberry-pi/`, `hardware/openrgb/`, ... | Hardware-specific configs |
| Headless | `headless/` | Headless server profile (watchdog, no suspend) |
| Home Assistant | `homeassistant/` | Smart home automation suite |
| Impermanence | `impermanence/` | Ephemeral root + persistent state |
| Monitoring | `monitoring/` | Prometheus/Grafana metrics |
| Network | `network/` | Hostname, firewall, NetworkManager, static IP |
| Power | `power/` | UPS support |
| Programs | `programs/` | System-wide programs (nix-index, gnupg, etc.) |
| Security | `security/common/`, `security/tpm/` | Common hardening, TPM unlock |
| Services | `services/<name>/` | ~50 self-hosted service modules (see below) |
| SOPS | `sops/` | Secret management setup |
| System | `system/` | Miscellaneous system settings |
| User | `user/` | User account management |
| Virtualization | `virtualization/` | libvirt, containers |
### Home Manager modules (`modules/home/`)
| Category | Paths | Description |
|---|---|---|
| Desktop | `desktop/gnome/`, `desktop/theme/` | GNOME and theming |
| GPG | `gpg/` | GPG agent configuration |
| Programs | `programs/btop/`, `programs/git/`, `programs/zsh/`, `programs/kitty/`, `programs/waybar/`, `programs/hyprland/`, `programs/wofi/`, `programs/mako/`, `programs/wlogout/`, `programs/librewolf/`, `programs/opencode/`, `programs/update-checker/`, ... | User applications |
| Services | `services/pass/` | Password store integration |
| Shell | `shell-aliases/` | Common shell aliases |
| SOPS | `sops/` | User-level secret integration |
| Stylix | `stylix/` | System-wide theming |
| User | `user/` | User environment defaults |
## Secrets Management
Secrets are encrypted with [SOPS](https://github.com/getsops/sops) using age keys derived from each machine's SSH host key (`/etc/ssh/ssh_host_ed25519_key`). The `.sops.yaml` file maps secret file path patterns to the set of age recipients that can decrypt them.
Each host has its own secrets file:
| File | Host |
|---|---|
| `secrets/secrets.yaml` | Shared (all hosts) |
| `secrets/nas-secrets.yaml` | jallen-nas |
| `secrets/pi5-secrets.yaml` | pi5 |
| `secrets/allyx-secrets.yaml` | allyx |
| `secrets/nuc-secrets.yaml` | nuc-nixos |
| `secrets/mac-secrets.yaml` | macbook-pro-nixos |
| `secrets/desktop-secrets.yaml` | matt-nixos |
See the [Secrets Management](../README.md#secrets-management) section of the root README for full details on generating keys and adding secrets.
## Deployment
```bash
# NixOS system
sudo nixos-rebuild switch --flake .#hostname
# macOS (nix-darwin)
darwin-rebuild switch --flake .#hostname
# Home Manager only
home-manager switch --flake .#username@hostname
```

175
docs/getting-started.md Normal file
View File

@@ -0,0 +1,175 @@
# Getting Started
This guide will help you get started with this NixOS configuration repository.
## Prerequisites
- Basic knowledge of NixOS and the Nix language
- Git installed on your system
- Physical or SSH access to the target machine
## Cloning the Repository
```bash
git clone ssh://nix-apps@localhost:2222/mjallen/nix-config.git
cd nix-config
```
## Installing on a New Machine
### Option 1: Using an existing system configuration
If the machine matches an existing configuration (e.g. reinstalling `jallen-nas`):
1. Boot from a NixOS installation ISO
2. Partition and mount disks (or use `disko`):
```bash
nix run github:nix-community/disko -- --mode disko /path/to/disko-config.nix
```
3. Clone this repo into the target:
```bash
mkdir -p /mnt/etc/nixos
git clone <repo-url> /mnt/etc/nixos
```
4. Install:
```bash
nixos-install --flake /mnt/etc/nixos#hostname
```
### Option 2: Adding a new system configuration
1. **Create the system directory** under the appropriate architecture:
```bash
mkdir -p systems/x86_64-linux/new-hostname
```
2. **Write the configuration** — at minimum a `default.nix`:
```nix
{ namespace, ... }:
{
mjallen = {
sops.enable = true;
network.hostName = "new-hostname";
user.name = "admin";
};
}
```
3. **Generate hardware configuration** (on the target machine):
```bash
nixos-generate-config --no-filesystems --dir systems/x86_64-linux/new-hostname/
```
4. **Add SOPS secrets** for the new host — see [Secrets Management](../README.md#secrets-management).
5. **Build and switch**:
```bash
sudo nixos-rebuild switch --flake .#new-hostname
```
## Day-to-Day Usage
### Applying configuration changes
```bash
# On the local machine
sudo nixos-rebuild switch --flake .#$(hostname)
# On a remote machine
nixos-rebuild switch --flake .#hostname --target-host user@host --use-remote-sudo
```
### Updating flake inputs
```bash
# Update all inputs
nix flake update
# Update a single input
nix flake lock --update-input nixpkgs
# Apply after updating
sudo nixos-rebuild switch --flake .#$(hostname)
```
### Garbage collection
```bash
# Remove old generations and unreferenced store paths
sudo nix-collect-garbage -d
# Keep the last N generations
sudo nix-collect-garbage --delete-older-than 30d
```
## Enabling a Module
Most functionality is exposed through the `mjallen` namespace. To enable a module, set it in the system's `default.nix` (or a relevant sub-file):
```nix
mjallen = {
desktop.gnome.enable = true;
hardware.amd.enable = true;
gaming.enable = true;
services.jellyfin = {
enable = true;
port = 8096;
reverseProxy.enable = true;
};
};
```
See [Custom Modules](./modules/README.md) for the full list of available modules and options.
## Adding a New Service Module
1. **Create the module directory**:
```bash
mkdir -p modules/nixos/services/my-service
```
2. **Write `default.nix`** using the `mkModule` helper:
```nix
{ config, lib, namespace, pkgs, ... }:
let
name = "my-service";
nebulaConfig = lib.${namespace}.mkModule {
inherit config name;
description = "my service description";
options = { };
moduleConfig = {
services.my-service = {
enable = true;
port = config.${namespace}.services.${name}.port;
};
};
};
in
{ imports = [ nebulaConfig ]; }
```
3. **Enable it** in a system configuration:
```nix
mjallen.services.my-service = {
enable = true;
port = 1234;
};
```
## Adding a New Package
1. Create a directory under `packages/`:
```bash
mkdir packages/my-package
```
2. Write a `default.nix` that returns a derivation. The package will be available as `pkgs.mjallen.my-package` in all configurations.
## Secrets
See the [Secrets Management](../README.md#secrets-management) section of the root README for:
- How age keys are derived from SSH host keys
- Adding a new machine as a SOPS recipient
- Adding/editing secrets
- Generating Nebula VPN certificates

View File

@@ -0,0 +1,188 @@
# Home Assistant Configuration
This document provides comprehensive information about the Home Assistant setup in this NixOS configuration.
## Overview
Home Assistant is configured as a NixOS service with custom components, integrations, and automations. The configuration uses a modular approach with separate files for different aspects of the setup.
## Module Structure
The Home Assistant configuration is organized in the following structure:
```
modules/nixos/homeassistant/
├── automations/ # Automation configurations
│ ├── lightswitch/ # Light switch automations
│ └── motion-light/ # Motion-activated light automations
├── default.nix # Main module configuration
├── options.nix # Module options definition
└── services/ # Related service configurations
├── govee2mqtt/ # Govee integration via MQTT
├── homeassistant/ # Core Home Assistant service
├── music-assistant/ # Music Assistant integration
├── thread/ # Thread border router
└── zigbee2mqtt/ # Zigbee to MQTT bridge
```
## Installation
The Home Assistant module is enabled in the system configuration by setting:
```nix
mjallen.services.home-assistant.enable = true;
```
This activates Home Assistant and related services such as MQTT, Zigbee2MQTT, and the Matter server.
## Configuration Options
The module provides several configuration options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enable` | boolean | `false` | Enable Home Assistant and related services |
| `mosquittoPort` | integer | `1883` | Port for the MQTT broker |
| `zigbee2mqttPort` | integer | `8080` | Port for the Zigbee2MQTT web interface |
| `zigbeeDevicePath` | string | `/dev/ttyUSB0` | Path to the Zigbee USB device |
## Core Services
### Home Assistant
The main Home Assistant service is configured in `services/homeassistant/default.nix` with:
- PostgreSQL database backend
- Custom components
- Custom Lovelace modules
- HTTPS access with authentication
- Integration with other services
### MQTT
MQTT is used as a messaging protocol for various smart home devices. The Mosquitto MQTT broker is automatically configured when Home Assistant is enabled.
### Zigbee2MQTT
Zigbee2MQTT allows integration with Zigbee devices. It's configured with:
- Automatic discovery for Home Assistant
- OTA updates for Zigbee devices
- Web interface for management
### Thread Border Router
The Thread Border Router provides integration with Thread-based devices like Matter devices.
## Custom Components
The following custom components are included:
- `ha-anycubic` - Anycubic 3D printer integration
- `ha-bambulab` - Bambu Lab 3D printer integration
- `ha-bedjet` - BedJet climate control integration
- `ha-gehome` - GE Home appliance integration
- `ha-icloud3` - Enhanced iCloud device tracking
- `ha-local-llm` - Local LLM integration
- `ha-mail-and-packages` - Mail and package delivery tracking
- `ha-nanokvm` - NanoKVM integration
- `ha-openhasp` - openHASP integration for DIY displays
- `ha-overseerr` - Overseerr media request integration
- `ha-petlibro` - PetLibro pet feeder integration
- `ha-wyzeapi` - Wyze device integration
## Automations
### Light Switch Automations
The light switch automations handle physical switch inputs for controlling smart lights.
### Motion Light Automations
Motion light automations turn lights on when motion is detected and off after a period of inactivity.
### Custom Automations
Additional automations are placed in the `/etc/hass` directory and are included in the Home Assistant configuration. These include:
- `fountain_automation.yaml` - Toggles the water dispensing mode on the Dockstream Smart RFID Fountain every 15 minutes between constant and intermittent flow.
## Smart Home Devices
The configuration includes support for various smart home devices:
### Lighting
- Various smart lights throughout the home
### Climate
- Smart thermostat
- Humidifier control
### Pet Care
- Dockstream Smart RFID Fountain with scheduling
- Smart pet feeders for pets named Joey and Luci
- Litter-Robot 4 smart litter box
### Media
- Google Cast devices
- Smart TVs
- Media players
### Sensors
- Temperature, humidity, and motion sensors
- Door and window sensors
- Presence detection
## Integration with Other Services
Home Assistant is integrated with:
- **Music Assistant** - For enhanced music streaming capabilities
- **Govee Integration** - For Govee smart devices
- **Matter** - For Matter-compatible devices
## Adding New Automations
To add a new automation:
1. Create a YAML file with the automation definition
2. Place it in `/etc/hass`
3. The automation will be automatically included in Home Assistant
Example automation format:
```yaml
alias: "Automation Name"
description: "Description of what the automation does"
trigger:
- platform: state
entity_id: binary_sensor.motion_sensor
to: "on"
condition: []
action:
- service: light.turn_on
target:
entity_id: light.living_room
mode: single
```
## Troubleshooting
### Common Issues
1. **Zigbee Device Pairing Issues**
- Make sure the Zigbee coordinator is properly connected
- Check the Zigbee2MQTT logs for errors
2. **Service Unavailable**
- Check if all related services are running
- Verify firewall rules allow access to the services
3. **Database Issues**
- Check PostgreSQL service status
- Verify database connection settings

View File

@@ -0,0 +1,148 @@
# Home Assistant Automations
This document details the automations configured in the Home Assistant setup.
## Automation Types
Automations in this configuration are managed in several ways:
1. **Module-Based Automations**: Defined in Nix modules within the `modules/nixos/homeassistant/automations/` directory
2. **YAML Automations**: Defined in YAML files and included via the `automation manual` directive
3. **UI-Created Automations**: Created through the Home Assistant UI and stored in `automations.yaml`
## Module-Based Automations
### Light Switch Automations
**Location**: `modules/nixos/homeassistant/automations/lightswitch/`
These automations link physical light switches to smart lights:
- **Bedroom Light Switch**: Controls the bedroom lights
- **Living Room Light Switch**: Controls the living room lights
- **Bedroom Closet Lights**: Controls the closet lights
### Motion-Activated Light Automations
**Location**: `modules/nixos/homeassistant/automations/motion-light/`
These automations turn lights on when motion is detected and off after a period of inactivity.
## YAML Automations
### Fountain Cycling Automation
**Location**: `/etc/nixos/fountain_automation.yaml`
This automation toggles the water dispensing mode on the Dockstream Smart RFID Fountain every 15 minutes:
```yaml
alias: "Fountain Cycle Mode"
description: "Toggles fountain water mode every 15 minutes between constant and intermittent flow"
trigger:
- platform: time_pattern
minutes: "/15" # Every 15 minutes
condition: []
action:
- service: select.select_next
target:
entity_id: select.dockstream_smart_rfid_fountain_water_dispensing_mode
mode: single
id: fountain_cycle_mode
```
This automation:
1. Triggers every 15 minutes
2. Uses the `select.select_next` service to toggle between the two available options:
- "Flowing Water (Constant)"
- "Intermittent Water (Scheduled)"
The fountain is also configured with:
- Water Interval: 10 minutes
- Water Dispensing Duration: 15 minutes
## Creating New Automations
### Method 1: Module-Based Automation
For reusable, complex automations that should be managed in code:
1. Create a new directory in `modules/nixos/homeassistant/automations/`
2. Create a `default.nix` file with the automation logic
Example:
```nix
{ config, lib, ... }:
{
config = {
services.home-assistant.config."automation manual" = [
{
alias = "Example Automation";
description = "Example automation created via Nix module";
trigger = [
{
platform = "state";
entity_id = "binary_sensor.example_sensor";
to = "on";
}
];
action = [
{
service = "light.turn_on";
target.entity_id = "light.example_light";
}
];
mode = "single";
}
];
};
}
```
### Method 2: YAML Automation
For simpler automations:
1. Create a YAML file with the automation definition
2. Place it in `/etc/hass/`
Example:
```yaml
alias: "Example Automation"
description: "Example automation in YAML"
trigger:
- platform: state
entity_id: binary_sensor.example_sensor
to: "on"
action:
- service: light.turn_on
target:
entity_id: light.example_light
mode: single
```
### Method 3: UI Creation
For quick prototyping or simple automations:
1. Go to Home Assistant UI > Settings > Automations & Scenes
2. Click "+ Add Automation"
3. Configure using the UI editor
## Testing Automations
To test an automation:
1. In the Home Assistant UI, go to Developer Tools > Services
2. Select `automation.trigger` as the service
3. Enter the entity_id of your automation in the service data field
4. Click "Call Service" to trigger the automation manually
## Troubleshooting
If an automation isn't working as expected:
1. Check the Home Assistant logs for errors
2. Verify entity names and service calls are correct
3. Test individual triggers and actions separately
4. Use the "Debug" section in the automation editor to trace execution

View File

@@ -0,0 +1,96 @@
# Pet Fountain Automation
This document details the automation for the Dockstream Smart RFID Fountain device.
## Overview
The Dockstream Smart RFID Fountain is a smart pet fountain controlled through Home Assistant. A custom automation has been created to toggle the water dispensing mode between constant flow and intermittent flow every 15 minutes. This cycling helps keep the water fresh while reducing energy consumption.
## Fountain Configuration
The Dockstream Smart RFID Fountain has the following settings in Home Assistant:
| Setting | Entity ID | Value | Description |
|---------|-----------|-------|-------------|
| Water Dispensing Mode | `select.dockstream_smart_rfid_fountain_water_dispensing_mode` | Toggles between modes | Controls how water flows |
| Water Interval | `number.dockstream_smart_rfid_fountain_water_interval` | 10 minutes | Time between water dispensing in intermittent mode |
| Water Dispensing Duration | `number.dockstream_smart_rfid_fountain_water_dispensing_duration` | 15 minutes | How long water flows in intermittent mode |
| Cleaning Cycle | `number.dockstream_smart_rfid_fountain_cleaning_cycle` | 14 days | Reminder interval for cleaning |
## Available Modes
The fountain supports two water dispensing modes:
1. **Flowing Water (Constant)** - Water flows continuously
2. **Intermittent Water (Scheduled)** - Water flows according to the interval and duration settings
## Automation Details
The fountain cycling automation is defined in `/etc/nixos/fountain_automation.yaml`:
```yaml
alias: "Fountain Cycle Mode"
description: "Toggles fountain water mode every 15 minutes between constant and intermittent flow"
trigger:
- platform: time_pattern
minutes: "/15" # Every 15 minutes
condition: []
action:
- service: select.select_next
target:
entity_id: select.dockstream_smart_rfid_fountain_water_dispensing_mode
mode: single
id: fountain_cycle_mode
```
### How It Works
1. **Trigger**: The automation runs every 15 minutes based on the time pattern trigger
2. **Action**: It uses the `select.select_next` service to toggle to the next available option
3. **Mode**: Set to "single" to prevent multiple executions if triggers overlap
## Installation
The automation is included in Home Assistant via the `automation manual` directive in the Home Assistant configuration:
```yaml
"automation manual" = "!include_dir_merge_list /etc/hass";
```
The YAML file needs to be placed in the `/etc/hass` directory to be loaded.
## Testing
To manually test the automation:
1. In Home Assistant UI, go to Developer Tools > Services
2. Select `automation.trigger` as the service
3. Enter the following service data:
```yaml
entity_id: automation.fountain_cycle_mode
```
4. Click "Call Service" to trigger the automation
## Customizing
To adjust the cycling interval:
1. Edit the YAML file at `/etc/nixos/fountain_automation.yaml`
2. Change the `minutes` value in the trigger section (e.g., from `"/15"` to `"/30"` for every 30 minutes)
3. Save the file
4. Restart Home Assistant or reload automations
To adjust fountain settings:
1. In Home Assistant UI, go to Settings > Devices & Services
2. Find the Dockstream Smart RFID Fountain device
3. Adjust the water interval or dispensing duration settings
## Troubleshooting
If the automation is not working as expected:
1. Check that the entity ID is correct and the fountain is online
2. Verify that Home Assistant is including the automation file correctly
3. Look for errors in the Home Assistant logs related to the automation or the fountain
4. Try manually controlling the fountain to ensure it responds to commands

295
docs/modules/README.md Normal file
View File

@@ -0,0 +1,295 @@
# Custom Modules
This directory contains documentation for the custom modules used in this NixOS configuration.
## Overview
Modules are split into three categories:
- **NixOS modules** (`modules/nixos/`) — system-level configuration
- **Home Manager modules** (`modules/home/`) — user-level configuration
- **Darwin modules** (`modules/darwin/`) — macOS-specific configuration
All modules are auto-discovered by Snowfall Lib and expose options under the `mjallen` namespace.
## NixOS Modules
### Boot (`modules/nixos/boot/`)
| Module | Description |
|---|---|
| `boot/common/` | Shared boot defaults (quiet boot, Plymouth) |
| `boot/lanzaboote/` | Secure Boot via Lanzaboote |
| `boot/systemd-boot/` | systemd-boot (non-secure-boot systems) |
| `boot/plymouth/` | Plymouth splash screen |
### Desktop (`modules/nixos/desktop/`)
| Module | Description |
|---|---|
| `desktop/gnome/` | GNOME desktop environment |
| `desktop/hyprland/` | Hyprland compositor |
| `desktop/cosmic/` | COSMIC desktop environment |
### Development (`modules/nixos/development/`)
Enables development tools and language support. Options:
```nix
mjallen.development = {
enable = true;
includeLanguages = [ "python" "c" ];
includeContainers = true;
};
```
### Hardware (`modules/nixos/hardware/`)
| Module | Description |
|---|---|
| `hardware/amd/` | AMD GPU (AMDGPU driver, LACT) |
| `hardware/nvidia/` | NVIDIA GPU |
| `hardware/battery/` | Battery charge threshold management |
| `hardware/raspberry-pi/` | Raspberry Pi hardware support and DT overlays |
| `hardware/openrgb/` | OpenRGB for LED control |
| `hardware/btrfs/` | btrfs-specific settings |
| `hardware/common/` | Common hardware defaults |
### Headless (`modules/nixos/headless/`)
Server profile — disables suspend/hibernate, enables systemd watchdog, no display manager.
```nix
mjallen.headless.enable = true;
```
### Home Assistant (`modules/nixos/homeassistant/`)
Full smart home stack. See [Home Assistant docs](../home-assistant/README.md) for details.
```nix
mjallen.services.home-assistant.enable = true;
```
### Impermanence (`modules/nixos/impermanence/`)
Ephemeral root filesystem with explicit persistence declarations.
```nix
mjallen.impermanence = {
enable = true;
extraDirectories = [ { directory = "/var/lib/myapp"; user = "myapp"; } ];
};
```
### Monitoring (`modules/nixos/monitoring/`)
Prometheus metrics and Grafana dashboards.
```nix
mjallen.monitoring.enable = true;
```
### Network (`modules/nixos/network/`)
Hostname, firewall, NetworkManager profiles, static IP configuration.
```nix
mjallen.network = {
hostName = "my-host";
ipv4 = {
method = "manual";
address = "10.0.1.5/24";
gateway = "10.0.1.1";
dns = "1.1.1.1";
interface = "eth0";
};
firewall = {
enable = true;
allowedTCPPorts = [ 80 443 ];
};
};
```
### Power (`modules/nixos/power/`)
UPS (NUT) support.
```nix
mjallen.power.ups.enable = true;
```
### Security (`modules/nixos/security/`)
| Module | Description |
|---|---|
| `security/common/` | Common hardening (kernel params, etc.) |
| `security/tpm/` | TPM2 — Clevis disk unlock |
### Services (`modules/nixos/services/`)
~50 self-hosted service modules, all built with `mkModule`. Each exposes at minimum `enable`, `port`, `reverseProxy`, and `openFirewall`. Common usage pattern:
```nix
mjallen.services.jellyfin = {
enable = true;
port = 8096;
reverseProxy.enable = true;
};
```
Available services:
`actual`, `ai`, `appimage`, `arrs`, `attic`, `authentik`, `authentikRac`, `booklore`, `caddy`, `calibre`, `calibre-web`, `cockpit`, `code-server`, `collabora`, `coturn`, `crowdsec`, `dispatcharr`, `free-games-claimer`, `gitea`, `glance`, `glances`, `grafana`, `guacd`, `headscale`, `immich`, `jellyfin`, `jellyseerr`, `lubelogger`, `manyfold`, `matrix`, `minecraft`, `mongodb`, `nebula`, `netbootxyz`, `nextcloud`, `ntfy`, `onlyoffice`, `opencloud`, `orca`, `paperless`, `paperless-ai`, `protonmail-bridge`, `restic`, `samba`, `sparky-fitness`, `sparky-fitness-server`, `sunshine`, `tdarr`, `termix`, `tunarr`, `unmanic`, `uptime-kuma`, `wyoming`, `your-spotify`
#### Nebula VPN (`services/nebula/`)
Unified module for both lighthouse and node roles:
```nix
# Lighthouse
mjallen.services.nebula = {
enable = true;
isLighthouse = true;
port = 4242;
secretsPrefix = "pi5/nebula";
secretsFile = lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml";
hostSecretName = "lighthouse";
};
# Node
mjallen.services.nebula = {
enable = true;
port = 4242;
lighthouses = [ "10.1.1.1" ];
staticHostMap = { "10.1.1.1" = [ "mjallen.dev:4242" ]; };
secretsPrefix = "mymachine/nebula";
secretsFile = lib.snowfall.fs.get-file "secrets/mymachine-secrets.yaml";
hostSecretName = "mymachine";
};
```
See [Secrets Management](../../README.md#generating-nebula-vpn-certificates) for how to generate the required certificates.
### SOPS (`modules/nixos/sops/`)
Configures sops-nix to decrypt secrets using the machine's SSH host key as an age key.
```nix
mjallen.sops = {
enable = true;
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; # default
};
```
### User (`modules/nixos/user/`)
System user account management.
```nix
mjallen.user = {
name = "matt";
mutableUsers = false;
extraGroups = [ "docker" "video" ];
};
```
---
## Home Manager Modules
### Desktop
| Module | Description |
|---|---|
| `desktop/gnome/` | GNOME user settings (extensions, keybindings, etc.) |
| `desktop/theme/` | Theme configuration |
### Programs
| Module | Description |
|---|---|
| `programs/btop/` | btop system monitor |
| `programs/code/` | VS Code / VSCodium settings |
| `programs/git/` | Git user config |
| `programs/hyprland/` | Hyprland compositor config |
| `programs/kitty/` | Kitty terminal config |
| `programs/librewolf/` | LibreWolf browser settings |
| `programs/mako/` | Mako notification daemon |
| `programs/nwg-dock/` | nwg-dock panel |
| `programs/nwg-drawer/` | nwg-drawer app launcher |
| `programs/nwg-panel/` | nwg-panel bar |
| `programs/opencode/` | OpenCode AI coding assistant |
| `programs/update-checker/` | Automatic flake update checker |
| `programs/waybar/` | Waybar status bar |
| `programs/wlogout/` | Logout menu |
| `programs/wofi/` | Wofi launcher |
| `programs/zsh/` | Zsh shell config |
### Other
| Module | Description |
|---|---|
| `gpg/` | GPG agent configuration |
| `services/pass/` | Password store |
| `shell-aliases/` | Common shell aliases |
| `sops/` | User-level SOPS secrets |
| `stylix/` | System-wide theming (colours, fonts, wallpaper) |
| `user/` | User environment defaults |
---
## Module Development
### Using `mkModule`
The `lib.mjallen.mkModule` helper (`lib/module/default.nix`) creates a fully-featured NixOS module from a minimal spec:
```nix
{ config, lib, namespace, pkgs, ... }:
let
name = "my-service";
cfg = config.${namespace}.services.${name};
serviceConfig = lib.${namespace}.mkModule {
inherit config name;
description = "my service";
options = {
# extra options beyond the standard set
myOption = lib.${namespace}.mkOpt lib.types.str "default" "Description";
};
moduleConfig = {
services.my-service = {
enable = true;
port = cfg.port;
};
};
};
in
{ imports = [ serviceConfig ]; }
```
Standard options provided by `mkModule` for free: `enable`, `port`, `listenAddress`, `openFirewall`, `configDir`, `dataDir`, `createUser`, `configureDb`, `environmentFile`, `reverseProxy.*`, `redis.*`, `extraEnvironment`, `hashedPassword`, `puid`, `pgid`, `timeZone`.
### Using `mkContainerService`
For Podman/OCI container services, use `mkContainerService` instead:
```nix
lib.${namespace}.mkContainerService {
inherit config name;
image = "ghcr.io/example/my-app:latest";
internalPort = 8080;
volumes = [ "${cfg.configDir}:/config" ];
};
```
### Option helpers
```nix
lib.mjallen.mkOpt types.str "default" "description"
lib.mjallen.mkBoolOpt false "description"
lib.mjallen.mkOpt' types.int 80 # no description
lib.mjallen.enabled # { enable = true; }
lib.mjallen.disabled # { enable = false; }
```

View File

@@ -0,0 +1,190 @@
# Home Assistant Module
This document details the Home Assistant module configuration.
## Module Structure
The Home Assistant module is organized in the following structure:
```
modules/nixos/homeassistant/
├── automations/ # Automation configurations
│ ├── lightswitch/ # Light switch automations
│ └── motion-light/ # Motion-activated light automations
├── default.nix # Main module configuration
├── options.nix # Module options definition
└── services/ # Related service configurations
├── govee2mqtt/ # Govee integration via MQTT
├── homeassistant/ # Core Home Assistant service
├── music-assistant/ # Music Assistant integration
├── thread/ # Thread border router
└── zigbee2mqtt/ # Zigbee to MQTT bridge
```
## Module Options
The module is configured through options defined in `options.nix`:
```nix
options.${namespace}.services.home-assistant = {
enable = mkEnableOption "enable home-assistant";
mosquittoPort = mkOpt types.int 1883 "Port for MQTT";
zigbee2mqttPort = mkOpt types.int 8080 "Port for zigbee2mqtt web interface";
zigbeeDevicePath = mkOpt types.str "/dev/ttyUSB0" "Path to zigbee usb device";
};
```
## Main Configuration
The main module configuration in `default.nix` includes:
1. **Activation Scripts** - For setting up custom components
2. **Service Configurations** - For Matter, PostgreSQL, etc.
3. **Firewall Rules** - For allowing required ports
```nix
config = lib.mkIf cfg.enable {
# Activation script for custom components
system.activationScripts.installCustomComponents = ''
chown -R hass:hass ${config.services.home-assistant.configDir}
chmod -R 750 ${config.services.home-assistant.configDir}
'';
# Service configurations
services = {
matter-server.enable = true;
postgresql = {
enable = false;
ensureDatabases = [ "hass" ];
ensureUsers = [
{
name = "hass";
ensureDBOwnership = true;
}
];
};
};
# Firewall rules
networking.firewall.allowedTCPPorts = [
cfg.mosquittoPort
cfg.zigbee2mqttPort
8095 # music-assistant
8097 # home-assistant
5580 # matter-server
];
};
```
## Home Assistant Service
The core Home Assistant service configuration in `services/homeassistant/default.nix` includes:
1. **Package Selection** - Using the standard Home Assistant package
2. **Component Configuration** - Enabling required components
3. **Custom Components** - Adding custom components from packages
4. **Lovelace Modules** - Adding custom UI components
5. **Integration Configuration** - Setting up integrations with other systems
```nix
services.home-assistant = {
enable = true;
package = pkgs.home-assistant;
openFirewall = true;
configDir = "/var/lib/homeassistant";
configWritable = true;
# Components
extraComponents = [
"mqtt"
"zha"
"homekit"
# ... many more components
];
# Custom components
customComponents = [
# ... custom components
];
# Lovelace modules
customLovelaceModules = [
# ... custom UI modules
];
# Configuration
config = {
# ... Home Assistant configuration
};
};
```
## Related Services
### Zigbee2MQTT
The Zigbee2MQTT service in `services/zigbee2mqtt/default.nix` connects Zigbee devices to MQTT:
```nix
services.zigbee2mqtt = {
enable = true;
settings = {
mqtt = {
server = "mqtt://localhost:${toString cfg.mosquittoPort}";
};
serial = {
port = cfg.zigbeeDevicePath;
};
# ... additional settings
};
};
```
### MQTT
MQTT is configured as a dependency for the Home Assistant module.
### Thread Border Router
The Thread Border Router in `services/thread/default.nix` provides Thread network connectivity for Matter devices.
## Automations
The module includes predefined automations in the `automations/` directory:
1. **Light Switch Automations** - For controlling lights via physical switches
2. **Motion Light Automations** - For motion-activated lighting
## Using the Module
To use this module in a system configuration:
```nix
{ config, ... }:
{
mjallen.services.home-assistant = {
enable = true;
# Optional: customize ports and device paths
mosquittoPort = 1883;
zigbee2mqttPort = 8080;
zigbeeDevicePath = "/dev/ttyUSB0";
};
}
```
## Extending the Module
### Adding Custom Components
To add a custom component:
1. Add the package to `packages/`
2. Add it to the `customComponents` list in `services/homeassistant/default.nix`
### Adding Custom Automations
To add a custom automation:
1. Create a new directory in `automations/`
2. Implement the automation in `default.nix`
3. Import it in the system configuration

37
docs/systems/README.md Normal file
View File

@@ -0,0 +1,37 @@
# System Configurations
This directory contains documentation for each system configuration in this repository.
## Systems
| Host | Architecture | OS | Role |
|---|---|---|---|
| [matt-nixos](./matt-nixos.md) | x86_64-linux | NixOS | Primary AMD desktop |
| [jallen-nas](./jallen-nas.md) | x86_64-linux | NixOS | Home server / NAS |
| [nuc-nixos](./nuc-nixos.md) | x86_64-linux | NixOS | Intel NUC — Home Assistant hub |
| [allyx](./allyx.md) | x86_64-linux | NixOS | ASUS ROG Ally X handheld |
| [pi5](./pi5.md) | aarch64-linux | NixOS | Raspberry Pi 5 — network services |
| [macbook-pro-nixos](./macbook-pro-nixos.md) | aarch64-linux | NixOS (Asahi) | Apple Silicon MacBook Pro |
| [macbook-pro](./macbook-pro.md) | aarch64-darwin | nix-darwin | macOS on the same MacBook Pro |
There are also two ISO targets (`x86_64-install-iso/graphical`, `x86_64-linux/iso-minimal`) used for installation media builds.
## Network
All hosts are on the `10.0.1.0/24` LAN with static IPs:
| Host | LAN IP | Overlay (Nebula) |
|---|---|---|
| pi5 | 10.0.1.2 | 10.1.1.1 (lighthouse) |
| jallen-nas | 10.0.1.3 | 10.1.1.x (node) |
| nuc-nixos | 10.0.1.4 | — |
## Common Configuration
All systems share:
- SOPS secret management (age keys from SSH host keys)
- Impermanence (ephemeral root, explicit persistence)
- Nix flake-based configuration via Snowfall Lib
- The `mjallen` module namespace
Each system then layers its own modules and hardware configuration on top.

57
docs/systems/allyx.md Normal file
View File

@@ -0,0 +1,57 @@
# ASUS ROG Ally X (allyx)
`systems/x86_64-linux/allyx/`
## Hardware
- **Device**: ASUS ROG Ally X handheld gaming PC
- **CPU/GPU**: AMD (LACT, CoolerControl)
- **Disk**: NVMe with LUKS encryption
- **Security**: Lanzaboote (Secure Boot)
## Key Features
- Jovian NixOS for Steam Deck-compatible experience
- Steam auto-starts into Game Mode on boot
- Decky Loader for Steam Deck plugins
- Handheld Daemon for power/TDP/fan control
- GNOME available as a desktop session (selectable from Steam)
- SDDM (Wayland) as display manager — GDM disabled
- Gaming enabled (Gamemode, Gamescope, etc.)
- AMD GPU management via LACT
- CoolerControl for fan curves
- iwd as the Wi-Fi backend
- Impermanence (ephemeral root)
## Jovian NixOS
The allyx uses [Jovian NixOS](https://github.com/Jovian-Experiments/Jovian-NixOS) to provide Steam Deck compatibility:
```nix
jovian.steam = {
enable = true;
autoStart = true;
desktopSession = "gnome"; # fall-through desktop session
};
jovian.decky-loader = {
enable = true;
extraPackages = [ pkgs.python3 pkgs.systemd ];
};
```
## Network
- **Hostname**: allyx
- **Wi-Fi backend**: iwd (via NetworkManager)
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config — Jovian, gaming, hardware |
| `boot.nix` | Lanzaboote, kernel |
## Secrets
Secrets are in `secrets/allyx-secrets.yaml`, encrypted for: `matt`, `desktop`, `deck`, `steamdeck`, `admin`, `jallen-nas`, `matt_allyx`, `allyx`.

104
docs/systems/jallen-nas.md Normal file
View File

@@ -0,0 +1,104 @@
# NAS Server (jallen-nas)
`systems/x86_64-linux/jallen-nas/`
## Hardware
- **CPU**: AMD (x86_64)
- **GPU**: AMD (LACT for fan/power control)
- **Disk**: NVMe system drive + bcachefs NAS pool
- **Security**: TPM2 (Clevis disk unlock), Lanzaboote (Secure Boot)
## Key Features
- bcachefs storage pool mounted at `/media/nas/main`
- Clevis-based TPM disk unlock at boot (no passphrase required)
- Impermanence — root is ephemeral; state persists to `/media/nas/main/persist`
- Samba shares (Windows file sharing, Time Machine)
- Nebula VPN node (overlay peer, lighthouse at pi5)
- ~40 self-hosted services behind a Caddy reverse proxy
- Authentik SSO protecting most web UIs
- CrowdSec for intrusion detection
- Restic backups
## Network
- **LAN IP**: 10.0.1.3 (static, `enp197s0`)
- **Gateway**: 10.0.1.1
- **Nebula**: overlay peer, lighthouse at `mjallen.dev:4242`
## Storage
| Mount | Filesystem | Description |
|---|---|---|
| `/media/nas/main` | bcachefs | Primary NAS pool (media, appdata, documents) |
| `/media/nas/test` | bcachefs | Secondary test pool |
### Samba Shares
| Share | Time Machine |
|---|---|
| `3d_printer` | no |
| `Backup` | no |
| `Documents` | no |
| `isos` | no |
| `app_data` | no |
| `TimeMachine` | yes (max 1 TB) |
## Enabled Services
| Service | Port | Notes |
|---|---|---|
| Caddy | 443/80 | Reverse proxy for all services |
| Authentik | 9000 | SSO / identity provider |
| Attic | 9012 | Nix binary cache (`cache.mjallen.dev`) |
| Immich | 2283 | Photo management |
| Jellyfin | 8096 | Media server |
| Jellyseerr | 5055 | Media request manager |
| Nextcloud | 9988 | Cloud storage |
| Paperless | 28981 | Document management |
| Paperless AI | 28982 | AI-assisted document tagging |
| Gitea | 3000 | Self-hosted Git |
| Matrix | 8448 | Matrix homeserver |
| Ntfy | 2586 | Push notifications |
| Glance | 5555 | Dashboard |
| Immich | 2283 | Photo library |
| Uptime Kuma | 3001 | Uptime monitoring |
| Code Server | 4444 | VS Code in the browser |
| Cockpit | 9090 | System management UI |
| Collabora | 9980 | Online office suite |
| CrowdSec | 8181 | Intrusion detection |
| Glances | 61208 | System stats |
| Coturn | 3478 | TURN/STUN server |
| Nebula | 4242 | Overlay VPN node |
| Restic | 8008 | Backup service |
| Sunshine | 47989 | Remote desktop (Moonlight) |
| Unmanic | 8265 | Media transcoding |
| Lubelogger | 6754 | Vehicle maintenance log |
| Manyfold | 3214 | 3D model library |
| Booklore | 6066 | Book library |
| Tunarr | 8000 | Virtual TV channels |
| Termix | 7777 | Web terminal |
| Sparky Fitness | 3004/3010 | Fitness tracking |
| Protonmail Bridge | 1025/1143 | SMTP/IMAP bridge |
| Arrs | various | Sonarr, Radarr, etc. |
| AI | various | Ollama, etc. |
| Wyoming | various | Voice assistant pipeline |
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config — network, hardware, filesystems, packages |
| `apps.nix` | All service enable/disable declarations |
| `nas-defaults.nix` | Sets `configDir`/`dataDir` defaults for all services |
| `boot.nix` | Lanzaboote, kernel, initrd |
| `services.nix` | Home Assistant, samba, and other platform services |
| `users.nix` | User accounts (`admin`, `nix-apps`) |
| `sops.nix` | Secret declarations |
| `vpn.nix` | Nebula VPN configuration |
| `disabled.nix` | Services explicitly disabled |
## Secrets
Secrets are in `secrets/nas-secrets.yaml`, encrypted for: `matt`, `desktop`, `admin`, `jallen-nas`.

View File

@@ -0,0 +1,69 @@
# MacBook Pro — NixOS / Asahi Linux (macbook-pro-nixos)
`systems/aarch64-linux/macbook-pro-nixos/`
## Hardware
- **Device**: Apple Silicon MacBook Pro (M-series)
- **OS**: NixOS via [Asahi Linux](https://asahilinux.org/) (`nixos-apple-silicon`)
- **Boot**: Asahi boot chain (not traditional EFI)
## Key Features
- Asahi Linux kernel with full Apple Silicon support (sound, GPU, etc.)
- GNOME as the primary desktop; Hyprland available but disabled
- x86_64 emulation via binfmt (enables running x86 binaries)
- Waydroid and libvirtd available (Waydroid disabled by default)
- Battery management — charge threshold set via `macsmc-battery`
- Omnissa Horizon client (custom package) for remote desktop
- Distrobox for containerised Linux environments
- iwd as the Wi-Fi backend
## x86_64 Emulation
```nix
nix.settings.extra-platforms = [ "x86_64-linux" ];
boot.binfmt.emulatedSystems = [ "x86_64-linux" ];
```
This allows building and running x86_64 packages on the ARM host.
## Asahi Hardware
The Asahi hardware module provides:
- Firmware loading from `./firmware/`
- Sound setup (`setupAsahiSound = true`)
- Apple-specific kernel patches and device drivers
Useful packages installed:
`asahi-bless`, `asahi-btsync`, `asahi-nvram`, `asahi-wifisync`, `apfs-fuse`, `apfsprogs`, `muvm`, `fex`
## Network
- **Hostname**: macbook-pro-nixos
- **Wi-Fi backend**: iwd (via NetworkManager)
- Firewall: extra rules for multicast (ports 1990, 2021)
## Battery Management
```nix
mjallen.hardware.battery = {
enable = true;
chargeLimitPath = "/sys/class/power_supply/macsmc-battery/charge_control_end_threshold";
};
```
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config — Asahi hardware, users, network |
| `boot.nix` | Asahi boot configuration |
| `filesystems.nix` | Disk layout |
| `hardware-configuration.nix` | Generated hardware config |
| `services.nix` | logind, GDM, GNOME, Flatpak, power settings |
| `firmware/` | Asahi firmware blobs |
## Secrets
Secrets are in `secrets/mac-secrets.yaml`, encrypted for: `matt`, `matt_pi5`, `desktop`, `pi5`, `admin`, `jallen-nas`, `matt_macbook-pro`, `macbook-pro`.

View File

@@ -0,0 +1,40 @@
# MacBook Pro — macOS / nix-darwin (macbook-pro)
`systems/aarch64-darwin/macbook-pro/`
## Overview
This is the [nix-darwin](https://github.com/nix-darwin/nix-darwin) configuration for the same MacBook Pro running macOS. It provides declarative macOS system management alongside Homebrew.
## Key Features
- Touch ID for `sudo`
- Declarative Homebrew (casks and formulae managed via `nix-homebrew`)
- `nh` for easy NixOS/darwin rebuilds
- `attic-client` for accessing the Nix binary cache
- `macpm` for Apple Silicon power monitoring
- Rosetta builder available (disabled, on-demand)
- Linux builder available (disabled)
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config — packages, users, environment |
| `homebrew.nix` | Declarative Homebrew casks and formulae |
| `programs.nix` | macOS program settings |
| `system.nix` | System defaults (dock, finder, etc.) |
## User
- **Username**: `mattjallen`
- **Home**: `/Users/mattjallen`
- **Flake path**: `/Users/mattjallen/nix-config` (set via `NH_OS_FLAKE`)
## Rebuilding
```bash
darwin-rebuild switch --flake .#macbook-pro
# or using nh:
nh darwin switch
```

View File

@@ -0,0 +1,50 @@
# Desktop (matt-nixos)
`systems/x86_64-linux/matt-nixos/`
## Hardware
- **CPU**: AMD
- **GPU**: AMD (LACT for fan/power control, OpenRGB)
- **Disk**: NVMe with LUKS encryption (disko)
- **Security**: TPM2, Lanzaboote (Secure Boot)
## Key Features
- GNOME as the primary desktop (Hyprland available but disabled)
- COSMIC available as a specialisation (`nixos-rebuild switch --specialisation cosmic`)
- Gaming — Steam, Gamemode, Gamescope, Lossless Scaling (`lsfg-vk`)
- AMD GPU management via LACT
- CoolerControl for fan curves
- Impermanence (ephemeral root)
- iwd as the Wi-Fi backend
- VSCodium as `$EDITOR`/`$VISUAL`
## Desktop Specialisations
| Specialisation | Description |
|---|---|
| *(default)* | GNOME |
| `cosmic` | COSMIC DE (enables `mjallen.desktop.cosmic`, disables GNOME/Hyprland) |
## Network
- **Hostname**: matt-nixos
- **Wi-Fi backend**: iwd (via NetworkManager)
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config |
| `boot.nix` | Lanzaboote, kernel |
| `filesystems.nix` | Disk layout |
| `sops.nix` | Secret declarations |
| `wifi-fixer.nix` | NetworkManager Wi-Fi workaround |
| `services/lsfg-vk/` | Lossless Scaling frame generation |
| `services/ratbagd/` | Gaming mouse config (libratbag) |
| `services/restic/` | Restic backup jobs |
## Secrets
Secrets are in `secrets/desktop-secrets.yaml`, encrypted for: `matt`, `desktop`, `admin`, `jallen-nas`.

57
docs/systems/nuc-nixos.md Normal file
View File

@@ -0,0 +1,57 @@
# Intel NUC (nuc-nixos)
`systems/x86_64-linux/nuc-nixos/`
## Hardware
- **Device**: Intel NUC
- **Disk**: btrfs with LUKS encryption
- **Security**: TPM2, Lanzaboote (Secure Boot)
- **Kernel**: CachyOS `linux-cachyos-lto` (x86_64-v4 build)
## Key Features
- Headless server (no display manager, watchdog enabled)
- Home Assistant — the primary smart home controller
- OpenThread Border Router (OTBR) for Matter/Thread devices
- Impermanence (ephemeral root, persistent state for HA and related services)
- btrfs filesystem (unlike the bcachefs-based NAS and Pi5)
## Network
- **LAN IP**: 10.0.1.4 (static, `enp2s0`)
- **Gateway / DNS**: 10.0.1.1
- **Firewall**: 1883 (MQTT), 8880/8881 (OTBR), 8192
## Services
| Service | Port | Description |
|---|---|---|
| Home Assistant | 8097 | Smart home controller |
| Mosquitto (MQTT) | 1883 | IoT message broker |
| Zigbee2MQTT | 8080 | Zigbee device bridge |
| Music Assistant | 8095 | Music streaming |
| OTBR | 8880/8881 | OpenThread Border Router (Matter/Thread) |
| ESPHome | — | ESP microcontroller firmware |
| PostgreSQL | — | HA database backend |
## Persistent Directories
The following directories survive reboots via impermanence:
- `/esphome`
- `/var/lib/homeassistant`
- `/var/lib/mosquitto`
- `/var/lib/music-assistant`
- `/var/lib/postgresql`
- `/var/lib/zigbee2mqtt`
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | All config in one file — HA, OTBR, network, hardware, impermanence |
## Secrets
Secrets are in `secrets/nuc-secrets.yaml`, encrypted for: `nuc`, `admin_nuc`, `matt`, `admin`, `jallen-nas`.

62
docs/systems/pi5.md Normal file
View File

@@ -0,0 +1,62 @@
# Raspberry Pi 5 (pi5)
`systems/aarch64-linux/pi5/`
## Hardware
- **Board**: Raspberry Pi 5
- **Boot**: UEFI (via `rpi5-uefi`)
- **Storage**: bcachefs
- **Connectivity**: Ethernet (`end0`); Wi-Fi and Bluetooth disabled via device tree overlays
## Key Features
- Headless server (no display, no desktop)
- Nebula VPN **lighthouse** — the central relay for the `jallen-nebula` overlay network
- AdGuard Home DNS server (port 53)
- Docker
- Impermanence (ephemeral root)
- Extensive Raspberry Pi device tree overlays configured (I²C, SPI, UART, SDIO, etc.)
## Network
- **LAN IP**: 10.0.1.2 (static, `end0`)
- **Gateway**: 10.0.1.1
- **DNS**: 1.1.1.1
- **Nebula**: lighthouse at `10.1.1.1`, listening on UDP 4242 (public: `mjallen.dev:4242`)
- Firewall: TCP/UDP 53 open (DNS)
## Nebula Lighthouse
The pi5 acts as the Nebula VPN lighthouse for the whole network. All other Nebula nodes connect to it to discover peers.
```nix
mjallen.services.nebula = {
enable = true;
isLighthouse = true;
port = 4242;
secretsPrefix = "pi5/nebula";
secretsFile = lib.snowfall.fs.get-file "secrets/pi5-secrets.yaml";
hostSecretName = "lighthouse";
};
```
## Services
| Service | Port | Description |
|---|---|---|
| AdGuard Home | 53 | DNS ad-blocking |
| Nebula | 4242 (UDP) | VPN lighthouse |
## Configuration Files
| File | Purpose |
|---|---|
| `default.nix` | Main config |
| `boot.nix` | UEFI boot, kernel |
| `adguard.nix` | AdGuard Home configuration |
| `sops.nix` | Secret declarations (SSH keys, system keys) |
## Secrets
Secrets are in `secrets/pi5-secrets.yaml`, encrypted for: `matt`, `matt_pi5`, `desktop`, `pi5`, `admin`, `jallen-nas`.

217
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,217 @@
# Troubleshooting Guide
Common issues and solutions for this NixOS configuration.
## Build Failures
### `nixos-rebuild switch` fails
1. **Syntax error** — the error message includes the file and line number. Common causes: missing `;`, unmatched `{`, wrong type passed to an option.
2. **Evaluation error** — read the full error trace. Often caused by a module option receiving the wrong type, or a missing `cfg.enable` guard.
3. **Fetch failure** — a flake input or package source can't be downloaded. Check network connectivity, or try:
```bash
nix flake update --update-input <input-name>
```
4. **Disk space** — build sandbox fills up. Free space:
```bash
sudo nix-collect-garbage -d
df -h /nix
```
### Assertion failures
If you see `assertion failed`, read the `message` field. For example:
```
error: assertion failed at …/nebula/sops.nix
mjallen.services.nebula.secretsPrefix must be set
```
Set the required option in the system configuration.
## Boot Issues
### System won't boot after a config change
1. At the boot menu, select a previous generation.
2. Once booted, revert the change:
```bash
cd /etc/nixos
git revert HEAD
sudo nixos-rebuild switch --flake .#$(hostname)
```
### Booting from installation media to recover
```bash
# Mount the system (adjust device paths as needed)
sudo mount /dev/disk/by-label/nixos /mnt
sudo mount /dev/disk/by-label/boot /mnt/boot
# Chroot in
sudo nixos-enter --root /mnt
cd /etc/nixos
# Revert and rebuild
git revert HEAD
nixos-rebuild switch --flake .#hostname --install-bootloader
```
### Lanzaboote / Secure Boot issues
If Secure Boot enrolment fails or the system won't verify:
```bash
# Check enrolled keys
sbctl status
# Re-enrol if needed (run as root)
sbctl enrol-keys --microsoft
# Sign bootloader files manually
sbctl sign -s /boot/EFI/systemd/systemd-bootx64.efi
```
## SOPS / Secrets Issues
### `secret not found` or permission denied at boot
1. Verify the secret key path matches what's declared in the module's `sops.nix`.
2. Check the secret exists in the SOPS file:
```bash
sops --decrypt secrets/nas-secrets.yaml | grep "the-key"
```
3. Check the `owner`/`group` set on the secret matches the service user.
### Can't decrypt — wrong age key
The machine's age key is derived from `/etc/ssh/ssh_host_ed25519_key`. If the host key was regenerated, the age key changed and existing secrets can no longer be decrypted.
To fix: re-encrypt the secrets file with the new public key:
```bash
# Get the new public key
nix-shell -p ssh-to-age --run 'ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub'
# Update .sops.yaml with the new key, then:
sops updatekeys secrets/nas-secrets.yaml
```
### Adding a new secret to an existing file
```bash
sops secrets/nas-secrets.yaml
# Editor opens with decrypted YAML — add your key, save, sops re-encrypts
```
## Nebula VPN Issues
### Peers can't connect
1. Verify the lighthouse is reachable on its public address:
```bash
nc -zvu mjallen.dev 4242
```
2. Check the nebula service on both hosts:
```bash
systemctl status nebula@jallen-nebula
journalctl -u nebula@jallen-nebula -n 50
```
3. Confirm the CA cert, host cert, and host key are all present and owned by the `nebula-jallen-nebula` user:
```bash
ls -la /run/secrets/pi5/nebula/
```
4. Verify the host cert was signed by the same CA as the other nodes:
```bash
nebula-cert verify -ca ca.crt -crt host.crt
```
### Certificate expired
Re-sign the host certificate:
```bash
nebula-cert sign -name "hostname" -ip "10.1.1.x/24" \
-ca-crt ca.crt -ca-key ca.key \
-out-crt host.crt -out-key host.key
# Update SOPS, rebuild
```
## Impermanence Issues
### Service fails because its data directory is missing after reboot
If a service stores state in a path that isn't in the persistence list, it will be wiped on reboot. Add it to `impermanence.extraDirectories`:
```nix
mjallen.impermanence.extraDirectories = [
{ directory = "/var/lib/my-service"; user = "my-service"; group = "my-service"; mode = "0750"; }
];
```
Then move the existing data if needed:
```bash
cp -a /var/lib/my-service /persist/var/lib/my-service
```
## Flake Input Issues
### Input update breaks a build
Roll back the specific input:
```bash
git checkout HEAD^ -- flake.lock
```
Or pin the input to a specific revision in `flake.nix`:
```nix
nixpkgs-unstable.url = "github:NixOS/nixpkgs/abc123def";
```
## Service Issues
### Service won't start
```bash
systemctl status <service>
journalctl -u <service> -n 100 --no-pager
```
### Caddy reverse proxy not routing
1. Check that `reverseProxy.enable = true` is set on the service.
2. Verify the subdomain matches: `reverseProxy.subdomain = "myapp"` → `myapp.mjallen.dev`.
3. Check Caddy logs:
```bash
journalctl -u caddy -n 50
```
### PostgreSQL database missing for a service
If `configureDb = true` is set, the database is created automatically. If it's missing:
```bash
sudo -u postgres createdb my-service
sudo -u postgres psql -c "GRANT ALL ON DATABASE my-service TO my-service;"
```
## Network Issues
### Firewall blocking a service
Check which ports are open:
```bash
sudo nft list ruleset | grep accept
```
Add ports in the system config:
```nix
mjallen.network.firewall.allowedTCPPorts = [ 8080 ];
```
Or if using `mkModule`, set `openFirewall = true` (it's the default).
## Getting Help
- NixOS manual: `nixos-help` or https://nixos.org/manual/nixos/stable/
- NixOS Wiki: https://nixos.wiki/
- NixOS Discourse: https://discourse.nixos.org/
- Nix package search: https://search.nixos.org/packages

208
docs/version.schema.json Normal file
View File

@@ -0,0 +1,208 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.invalid/version.schema.json",
"title": "Unified Package Version Schema",
"description": "Schema for a unified version.json used by packages/",
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"sources"
],
"properties": {
"schemaVersion": {
"type": "integer",
"enum": [1],
"description": "Schema version. Start at 1; bump on breaking changes."
},
"variables": {
"type": "object",
"description": "Common variables available for template substitution in string fields.",
"additionalProperties": {
"type": "string"
}
},
"defaultVariant": {
"type": "string",
"description": "Optional default variant name for consumers."
},
"sources": {
"type": "object",
"description": "Base component sources keyed by component name.",
"minProperties": 1,
"additionalProperties": {
"$ref": "#/$defs/SourceSpec"
}
},
"variants": {
"type": "object",
"description": "Optional variants/channels/flavors; each overlays the base.",
"additionalProperties": {
"$ref": "#/$defs/VariantSpec"
}
},
"notes": {
"type": "object",
"description": "Optional free-form human notes/documentation.",
"additionalProperties": true
}
},
"$defs": {
"SourceSpecBase": {
"type": "object",
"additionalProperties": false,
"properties": {
"fetcher": {
"type": "string",
"enum": ["github", "git", "url", "pypi", "none"],
"description": "Fetcher type for this source."
},
"hash": {
"type": "string",
"pattern": "^sha[0-9]+-",
"description": "SRI hash for the fetched artifact. Required unless fetcher is 'none'."
},
"version": {
"type": "string",
"description": "Optional version string metadata for this component."
},
"extra": {
"type": "object",
"description": "Optional free-form metadata for consumer logic.",
"additionalProperties": true
},
"owner": { "type": "string", "description": "GitHub owner/org (github fetcher)." },
"repo": { "type": "string", "description": "GitHub repository (github fetcher)." },
"tag": { "type": "string", "description": "Git tag (github fetcher). Mutually exclusive with 'rev'." },
"rev": { "type": "string", "description": "Commit revision (github/git fetchers)." },
"submodules": { "type": "boolean", "description": "Whether to fetch submodules (github/git fetchers)." },
"url": { "type": "string", "description": "Final URL (url fetcher). May be templated." },
"urlTemplate": { "type": "string", "description": "Template for URL (url fetcher); supports ${var}." },
"name": { "type": "string", "description": "PyPI dist name (pypi fetcher)." }
}
},
"SourceSpec": {
"allOf": [
{ "$ref": "#/$defs/SourceSpecBase" },
{
"if": {
"properties": { "fetcher": { "const": "github" } },
"required": ["fetcher"]
},
"then": {
"required": ["owner", "repo"],
"oneOf": [
{ "required": ["tag"] },
{ "required": ["rev"] }
]
}
},
{
"if": {
"properties": { "fetcher": { "const": "git" } },
"required": ["fetcher"]
},
"then": {
"required": ["url", "rev"]
}
},
{
"if": {
"properties": { "fetcher": { "const": "url" } },
"required": ["fetcher"]
},
"then": {
"oneOf": [
{ "required": ["url"] },
{ "required": ["urlTemplate"] }
]
}
},
{
"if": {
"properties": { "fetcher": { "const": "pypi" } },
"required": ["fetcher"]
},
"then": {
"required": ["name", "version"]
}
},
{
"if": {
"properties": { "fetcher": { "enum": ["github", "git", "url", "pypi"] } },
"required": ["fetcher"]
},
"then": {
"required": ["hash"]
}
}
]
},
"SourceOverride": {
"type": "object",
"additionalProperties": false,
"description": "Partial override of a source within a variant. All fields optional.",
"properties": {
"fetcher": { "type": "string", "enum": ["github", "git", "url", "pypi", "none"] },
"hash": { "type": "string", "pattern": "^sha[0-9]+-" },
"version": { "type": "string" },
"extra": { "type": "object", "additionalProperties": true },
"owner": { "type": "string" },
"repo": { "type": "string" },
"tag": { "type": "string" },
"rev": { "type": "string" },
"submodules": { "type": "boolean" },
"url": { "type": "string" },
"urlTemplate": { "type": "string" },
"name": { "type": "string" }
}
},
"VariantSpec": {
"type": "object",
"additionalProperties": false,
"properties": {
"inherits": {
"type": "string",
"description": "Optional base variant to inherit from."
},
"variables": {
"type": "object",
"description": "Variant-level variables that overlay top-level variables.",
"additionalProperties": { "type": "string" }
},
"sources": {
"type": "object",
"description": "Per-component overrides for this variant.",
"additionalProperties": { "$ref": "#/$defs/SourceOverride" }
},
"platforms": {
"type": "object",
"description": "Optional per-system overrides to support differing hashes/fields by platform.",
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"sources": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/SourceOverride" }
},
"variables": {
"type": "object",
"additionalProperties": { "type": "string" }
}
}
}
}
}
}
}
}

1
echo
View File

@@ -1 +0,0 @@
{"text": "\ue312 49\u00b0F", "tooltip": " Overcast 49\u00b0\n<span foreground=\"#585858\" font-weight=\"bold\"> .--. </span>Feels like: 49\u00b0\n<span foreground=\"#585858\" font-weight=\"bold\"> .-( ). </span>Wind: 2mph \u2199\n<span foreground=\"#585858\" font-weight=\"bold\"> (___.__)__) </span>Humidity: 80%\n Moon phase: Waxing Crescent \ud83c\udf12\n\nToday, <b>Mon Nov 24 2025</b>\n\uf2c7 53\u00b0F \uf2ca 38\u00b0F\ue34c 07:23 AM \ue34d 04:36 PM\n03 PM \udb81\udd95 52\u00b0 Partly Cloudy , Overcast 33%, Sunshine 73%\n06 PM \ue313 44\u00b0 Mist, Overcast 83%, Sunshine 8%\n09 PM \ue313 43\u00b0 Fog, Overcast 93%, Sunshine 5%\nTomorrow, <b>Tue Nov 25 2025</b>\n\uf2c7 43\u00b0F \uf2ca 34\u00b0F\ue34c 07:24 AM \ue34d 04:36 PM\n12 AM \ue313 43\u00b0 Fog, Fog 6%, Overcast 81%, Sunshine 19%\n03 AM \ue313 42\u00b0 Fog, Overcast 89%, Sunshine 8%\n06 AM \ue313 41\u00b0 Fog, Fog 6%, Overcast 92%, Sunshine 11%\n09 AM \ue313 40\u00b0 Fog, Fog 6%, Overcast 88%, Sunshine 5%\n12 PM \ue317 39\u00b0 Moderate rain at times, Overcast 90%, Rain 100%\n03 PM \ue308 34\u00b0 Light rain, Overcast 93%, Rain 100%\n06 PM \ue318 31\u00b0 Moderate rain, Overcast 88%, Rain 100%\n09 PM \ue31a 24\u00b0 Moderate snow, Overcast 89%, Rain 100%, Snow 100%\n<b>Wed Nov 26 2025</b>\n\uf2c7 36\u00b0F \uf2ca 25\u00b0F\ue34c 07:26 AM \ue34d 04:35 PM\n12 AM \ue312 21\u00b0 Overcast , Overcast 87%, Sunshine 8%\n03 AM \ue312 14\u00b0 Overcast , Frost 25%, Overcast 94%, Sunshine 13%\n06 AM \ue312 11\u00b0 Overcast , Frost 80%, Overcast 89%, Sunshine 8%\n09 AM \ue312 13\u00b0 Overcast , Frost 79%, Overcast 80%, Sunshine 5%\n12 PM \ue33d 18\u00b0 Cloudy , Frost 77%, Overcast 89%, Sunshine 17%\n03 PM \ue30d 24\u00b0 Sunny, Frost 29%, Sunshine 90%\n06 PM \udb81\udd94 22\u00b0 Clear , Frost 78%, Sunshine 94%\n09 PM \udb83\udf31 15\u00b0 Partly Cloudy , Frost 85%, Overcast 39%, Sunshine 83%\n"}

1185
flake.lock generated

File diff suppressed because it is too large Load Diff

174
flake.nix
View File

@@ -1,43 +1,59 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs = rec {
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11";
# The name "snowfall-lib" is required due to how Snowfall Lib processes your
# flake's inputs.
snowfall-lib = {
url = "github:mjallen18/lib";
inputs.nixpkgs.follows = "nixpkgs";
# Fork required: openthread-border-router is not yet in nixpkgs-unstable.
# Used by modules/nixos/homeassistant/services/thread/default.nix
nixpkgs-otbr.url = "github:mrene/nixpkgs/openthread-border-router";
home-manager-stable = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs-stable";
};
chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable";
home-manager = {
home-manager-unstable = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
nixpkgs = nixpkgs-unstable;
home-manager = home-manager-unstable;
# The name "snowfall-lib" is required due to how Snowfall Lib processes your
# flake's inputs. Using a personal fork for custom changes.
snowfall-lib = {
url = "github:mjallen18/snowfall-lib";
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
lanzaboote.url = "github:nix-community/lanzaboote/v0.4.3";
lanzaboote.url = "github:nix-community/lanzaboote/v1.0.0";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
sops-nix.url = "github:Mic92/sops-nix";
nix-cachyos-kernel.url = "github:xddxdd/nix-cachyos-kernel/release";
steam-rom-manager = {
url = "github:mjallen18/nix-steam-rom-manager";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
};
cosmic.url = "github:lilyinstarlight/nixos-cosmic";
cosmic = {
url = "github:lilyinstarlight/nixos-cosmic";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-vscode-extensions.url = "github:nix-community/nix-vscode-extensions";
authentik-nix.url = "github:nix-community/authentik-nix";
nixai.url = "github:olafkfreund/nix-ai-help";
authentik-nix = {
url = "github:nix-community/authentik-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
disko = {
# the fork is needed for partition attributes support
@@ -46,11 +62,7 @@
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-raspberrypi.url = "github:mjallen18/nixos-raspberrypi";
jovian.url = "github:Jovian-Experiments/Jovian-NixOS";
darwin.url = "github:LnL7/nix-darwin";
darwin.url = "github:nix-darwin/nix-darwin/master";
nix-homebrew.url = "github:zhaofengli/nix-homebrew";
@@ -66,7 +78,10 @@
nixos-apple-silicon.url = "github:nix-community/nixos-apple-silicon";
pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix";
pre-commit-hooks-nix = {
url = "github:cachix/pre-commit-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
treefmt-nix = {
url = "github:numtide/treefmt-nix";
@@ -91,6 +106,16 @@
url = "github:cpick/nix-rosetta-builder";
inputs.nixpkgs.follows = "nixpkgs";
};
stylix = {
url = "github:nix-community/stylix";
inputs.nixpkgs.follows = "nixpkgs";
};
jovian = {
url = "github:Jovian-Experiments/Jovian-NixOS";
inputs.nixpkgs.follows = "nixpkgs";
};
};
# We will handle this in the next section.
@@ -105,18 +130,28 @@
# Nix files to a separate directory.
src = ./.;
overlays = with inputs; [
nix-vscode-extensions.overlays.default
nix-cachyos-kernel.overlays.default
];
# Add a module to a specific host.
systems = {
# common modules
modules.nixos = with inputs; [
authentik-nix.nixosModules.default
chaotic.nixosModules.default
disko.nixosModules.disko
impermanence.nixosModules.impermanence
lanzaboote.nixosModules.lanzaboote
sops-nix.nixosModules.sops
home-manager.nixosModules.home-manager
nix-index-database.nixosModules.nix-index
stylix.nixosModules.stylix
];
modules.home = with inputs; [
nix-index-database.homeManagerModules.nix-index
steam-rom-manager.homeManagerModules.default
];
# common darwin modules
@@ -126,6 +161,7 @@
nix-plist-manager.darwinModules.default
nix-rosetta-builder.darwinModules.default
nix-index-database.darwinModules.nix-index
stylix.darwinModules.stylix
];
# Host config
@@ -137,7 +173,7 @@
modules = with inputs; [
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-cpu-amd-pstate
nixos-hardware.nixosModules.common-cpu-amd-zenpower
# nixos-hardware.nixosModules.common-cpu-amd-zenpower
nixos-hardware.nixosModules.common-gpu-amd
nixos-hardware.nixosModules.common-hidpi
nixos-hardware.nixosModules.common-pc
@@ -149,30 +185,13 @@
# NAS #
# ######################################################
jallen-nas = {
# home-manager is already in systems.modules.nixos above
modules = with inputs; [
nixos-hardware.nixosModules.common-pc
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-cpu-amd-pstate
nixos-hardware.nixosModules.common-cpu-amd-zenpower
# nixos-hardware.nixosModules.common-cpu-amd-zenpower
nixos-hardware.nixosModules.common-hidpi
home-manager.nixosModules.home-manager
];
};
# ######################################################
# Steamdeck #
# ######################################################
steamdeck = {
modules = with inputs; [
disko.nixosModules.disko
jovian.nixosModules.jovian
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-cpu-amd-pstate
nixos-hardware.nixosModules.common-cpu-amd-zenpower
nixos-hardware.nixosModules.common-gpu-amd
nixos-hardware.nixosModules.common-hidpi
nixos-hardware.nixosModules.common-pc
lsfg-vk.nixosModules.default
];
};
@@ -180,53 +199,23 @@
# NUC #
# ######################################################
nuc-nixos = {
# disko is already in systems.modules.nixos above
modules = with inputs; [
disko.nixosModules.disko
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-cpu-amd-pstate
nixos-hardware.nixosModules.common-cpu-amd-zenpower
# nixos-hardware.nixosModules.common-cpu-amd-zenpower
nixos-hardware.nixosModules.common-gpu-amd
nixos-hardware.nixosModules.common-hidpi
nixos-hardware.nixosModules.common-pc
];
};
# ######################################################
# Pi4 #
# ######################################################
pi4 = {
specialArgs = {
nixpkgs = inputs.nixpkgs-stable;
};
modules = with inputs; [
disko.nixosModules.disko
nixos-raspberrypi.nixosModules.raspberry-pi-4.base
nixos-raspberrypi.nixosModules.raspberry-pi-4.display-vc4
nixos-raspberrypi.nixosModules.nixpkgs-rpi
nixos-raspberrypi.nixosModules.trusted-nix-caches
nixos-raspberrypi.lib.inject-overlays
nixos-raspberrypi.lib.inject-overlays-global
];
};
# ######################################################
# Pi5 #
# ######################################################
pi5 = {
specialArgs = {
nixpkgs = inputs.nixpkgs-stable;
};
modules = with inputs; [
disko.nixosModules.disko
nixos-raspberrypi.nixosModules.raspberry-pi-5.base
nixos-raspberrypi.nixosModules.raspberry-pi-5.display-vc4
nixos-raspberrypi.nixosModules.raspberry-pi-5.bluetooth
nixos-raspberrypi.nixosModules.raspberry-pi-5.page-size-16k
nixos-raspberrypi.nixosModules.nixpkgs-rpi
nixos-raspberrypi.nixosModules.trusted-nix-caches
nixos-raspberrypi.lib.inject-overlays
nixos-raspberrypi.lib.inject-overlays-global
];
# disko is already in systems.modules.nixos above
modules = [ ];
};
# ######################################################
@@ -238,11 +227,23 @@
lsfg-vk.nixosModules.default
];
};
};
overlays = with inputs; [
nix-vscode-extensions.overlays.default
];
# ######################################################
# AllyX #
# ######################################################
allyx = {
modules = with inputs; [
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-cpu-amd-pstate
# nixos-hardware.nixosModules.common-cpu-amd-zenpower
nixos-hardware.nixosModules.common-gpu-amd
nixos-hardware.nixosModules.common-hidpi
nixos-hardware.nixosModules.common-pc
lsfg-vk.nixosModules.default
jovian.nixosModules.jovian
];
};
};
};
# Configure Snowfall Lib, all of these settings are optional.
@@ -263,22 +264,17 @@
channels-config = {
allowUnfree = true;
allowUnsupportedSystem = true;
permittedInsecurePackages = [
# ...
"libsoup-2.74.3"
"mbedtls-2.28.10"
# "libsoup-2.74.3"
# "mbedtls-2.28.10"
];
};
outputs-builder = channels: {
formatter = inputs.treefmt-nix.lib.mkWrapper channels.nixpkgs ./treefmt.nix;
# Add mjallen-lib to the flake outputs
overlays = {
mjallen-lib = _final: _prev: {
mjallen-lib = (import ./lib { inherit inputs; }).mjallen-lib;
};
};
# mjallen-lib overlay is auto-discovered from overlays/mjallen-lib/default.nix
};
};
}

View File

@@ -1,9 +1,11 @@
{
lib,
pkgs,
namespace,
...
}:
let
inherit (lib.${namespace}) enabled disabled;
shellAliases = {
update-switch = "darwin-rebuild switch --flake ~/nix-config";
update-flake = "nix flake update ~/nix-config";
@@ -13,7 +15,9 @@ let
age
cpufetch
deadnix
nixfmt-rfc-style
iproute2mac
nebula
nixfmt
nodePackages.nodejs
uv
sops
@@ -39,226 +43,12 @@ in
};
};
programs.nix-plist-manager = {
enable = true;
options = {
applications = {
finder = {
settings = {
general = {
showTheseItemsOnTheDesktop = {
hardDisks = false;
externalDisks = true;
cdsDvdsAndiPods = false;
connectedServers = false;
};
openFoldersInTabsInsteadOfNewWindows = true;
};
sidebar = {
recentTags = true;
};
advanced = {
removeItemsFromTheTrashAfter30Days = true;
showAllFilenameExtensions = true;
showWarningBeforeChangingAnExtension = true;
showWarningBeforeRemovingFromiCloudDrive = true;
showWarningBeforeEmptyingTheTrash = true;
keepFoldersOnTop = {
inWindowsWhenSortingByName = true;
onDesktop = true;
};
whenPerformingASearch = "Search This Mac";
};
};
menuBar = {
view = {
showTabBar = true;
showSidebar = true;
showPathBar = true;
showStatusBar = true;
};
};
};
systemSettings = {
appearance = {
appearance = "Dark";
accentColor = "Multicolor";
# clickInTheScrollBarTo = "Jump to the next page";
sidebarIconSize = "Medium";
showScrollBars = "When scrolling";
};
controlCenter = {
wifi = true;
bluetooth = true;
airdrop = true;
stageManager = true;
focusModes = "active";
screenMirroring = "active";
display = "never";
sound = "always";
nowPlaying = "active";
accessibilityShortcuts = "unset";
musicRecognition = {
showInMenuBar = false;
showInControlCenter = true;
};
hearing = "unset";
fastUserSwitching = {
showInMenuBar = false;
showInControlCenter = true;
};
keyboardBrightness = {
showInMenuBar = false;
showInControlCenter = true;
};
battery = {
showInMenuBar = false;
showInControlCenter = false;
};
batteryShowPercentage = true;
# menuBarOnly = {
# spotlight = false;
# siri = true;
# };
# automaticallyHideAndShowTheMenuBar = "In Full Screen Only";
};
desktopAndDock = {
desktopAndStageManager = {
showItems = {
onDesktop = true;
inStageManager = true;
};
clickWallpaperToRevealDesktop = "Always";
stageManager = false;
showRecentAppsInStageManager = true;
showWindowsFromAnApplication = "All at Once";
};
dock = {
animateOpeningApplications = true;
automaticallyHideAndShowTheDock.enabled = false;
doubleClickAWindowsTitleBarTo = "Minimize";
magnification.enabled = false;
minimizeWindowsIntoApplicationIcon = true;
minimizeWindowsUsing = "Genie Effect";
positionOnScreen = "Bottom";
showIndicatorsForOpenApplications = true;
showSuggestedAndRecentAppsInDock = false;
size = 64; # 16 - 128
# persistentApps = [
# { app = "/Applications/Clock.app"; }
# { folder = "/Applications"; }
# { app = "/Applications/Safari.app"; }
# { app = "/Applications/Firefox.app"; }
# { app = "/Applications/Tabby.app"; }
# { app = "/Applications/Termius.app"; }
# { app = "/Applications/Muic.app"; }
# { app = "/Applications/Vesktop.app"; }
# { app = "/Applications/Messages.app"; }
# { app = "/Applications/Calendar.app"; }
# { app = "/Applications/Reminders.app"; }
# { app = "/Applications/Notes.app"; }
# { app = "/Applications/Weather.app"; }
# { app = "/Applications/Maps.app"; }
# { app = "/Applications/App Store.app"; }
# { app = "/Applications/System Settings.app"; }
# { app = "/Applications/ChatGPT.app"; }
# { app = "/Applications/Nextcloud.app"; }
# { app = "/Applications/VSCodium.app"; }
# { app = "/Applications/Omnissa Horizon Client.app"; }
# { app = "/Applications/Proton Pass.app"; }
# { app = "/Applications/OrcaSlicer.app"; }
# { app = "/Applications/AlDente.app"; }
# ];
# persistentOthers = [
# "~/Downloads"
# ];
};
hotCorners = {
# ["-" "Mission Control" "Application Windows" "Desktop" "Start Screen Saver" "Disable Screen Saver" "Dashboard" "Put Display to Sleep" "Launchpad" "Notification Center" "Lock Screen" "Quick Note"]
topLeft = "-";
topRight = "-";
bottomLeft = "-";
bottomRight = "-";
};
missionControl = {
automaticallyRearrangeSpacesBasedOnMostRecentUse = true;
displaysHaveSeparateSpaces = true;
dragWindowsToTopOfScreenToEnterMissionControl = true;
groupWindowsByApplication = true;
whenSwitchingToAnApplicationSwitchToAspaceWithOpenWindowsForTheApplication = true;
};
widgets = {
showWidgets = {
onDesktop = true;
inStageManager = true;
};
widgetStyle = "Automatic";
useIphoneWidgets = true;
};
windows = {
askToKeepChangesWhenClosingDocuments = true;
closeWindowsWhenQuittingAnApplication = true;
dragWindowsToScreenEdgesToTile = true;
dragWindowsToMenuBarToFillScreen = true;
holdOptionKeyWhileDraggingWindowsToTile = true;
preferTabsWhenOpeningDocuments = "In Full Screen";
tiledWindowsHaveMargin = false;
};
};
focus = {
shareAcrossDevices = true;
};
# general.dateAndTime."24HourTime" = false;
notifications = {
notificationCenter = {
showPreviews = "When Unlocked";
summarizeNotifications = true;
};
};
sound = {
soundEffects = {
alertSound = "Boop";
alertVolume = 0.7;
playFeedbackWhenVolumeIsChanged = true;
playUserInterfaceSoundEffects = true;
};
};
spotlight = {
helpAppleImproveSearch = false;
# searchResults = {
# applications = true;
# calculator = true;
# contacts = true;
# conversion = true;
# definition = true;
# developer = true;
# documents = true;
# eventsAndReminders = true;
# folders = true;
# fonts = false;
# images = true;
# mailAndMessages = true;
# movies = true;
# music = true;
# other = false;
# pdfDocuments = true;
# presentations = true;
# siriSuggestions = false;
# systemSettings = true;
# tips = false;
# websites = true;
};
};
};
};
};
# Manage bug in compilations - who uses manpages in 2024 anyways? :P
manual.manpages.enable = false;
manual.manpages = enabled;
# Override defaults that arent supported
programs = {
mangohud.enable = lib.mkForce false;
mangohud = lib.mkForce disabled;
nh = {
flake = lib.mkForce "/Users/mattjallen/nix-config";
@@ -266,8 +56,8 @@ in
};
services = {
pass-secret-service.enable = lib.mkForce false;
nextcloud-client.enable = lib.mkForce false;
pass-secret-service = lib.mkForce disabled;
nextcloud-client = lib.mkForce disabled;
kdeconnect = {
enable = false;
indicator = false;

View File

@@ -1,19 +1,11 @@
{
lib,
pkgs,
namespace,
config,
...
}:
let
theme = config.mjallen.theme.palette;
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10 --build-host admin@10.0.1.3";
update-switch = "sudo nixos-rebuild switch --max-jobs 10 --build-host admin@10.0.1.3";
update-flake = "nix flake update mac-nixpkgs mac-nixos-apple-silicon mac-home-manager mac-impermanence mac-sops-nix --flake /etc/nixos";
update-nas = "nixos-rebuild switch --use-remote-sudo --target-host admin@10.0.1.3 --build-host admin@10.0.1.3 --flake ~/nix-config#jallen-nas";
};
fontName = "JetBrainsMono NFM";
fontPackage = pkgs.nerd-fonts.jetbrains-mono;
inherit (lib.${namespace}) enabled disabled;
# Displays
display = {
input = "eDP-1";
@@ -28,8 +20,9 @@ in
home.stateVersion = "23.11";
${namespace} = {
desktop.gnome = enabled;
programs.hyprland = {
enable = true;
enable = false;
primaryDisplay = "eDP-1";
debug.disableScaleChecks = true;
@@ -59,7 +52,7 @@ in
];
windowRule = [
"size 2160 3356, tag:horizonrdp"
# "size 2160 3356, tag:horizonrdp"
];
hyprpaper = {
@@ -69,42 +62,36 @@ in
keybinds = {
bind = [
"$mod, A, exec, chromium --app=\"https://music.apple.com\""
"SHIFT, XF86MonBrightnessUp, exec, lightctl -D kbd_backlight up"
"SHIFT, XF86MonBrightnessDown, exec, lightctl -D kbd_backlight down"
];
};
defaultApps = {
browser = pkgs.firefox;
};
extraConfig = ''
exec-once = brightnessctl -d kbd_backlight s 50%
'';
};
programs = {
btop.enable = true;
kitty = {
enable = true;
font = {
name = fontName;
package = fontPackage;
};
};
mako = {
enable = true;
fontName = fontName;
};
nwg-dock.enable = true;
nwg-drawer.enable = true;
nwg-panel = {
enable = true;
defaultApps = {
browser = pkgs.firefox;
};
};
btop = enabled;
kitty = disabled;
mako = disabled;
nwg-dock = disabled;
nwg-drawer = disabled;
nwg-panel = disabled;
opencode = enabled;
waybar = {
enable = true;
enable = false;
layer = "bottom";
temperature = {
cpu.enable = true;
gpu.enable = true;
cpu = enabled;
gpu = enabled;
};
extraModules = {
@@ -120,37 +107,76 @@ in
extraModulesStyle = ''
#custom-lights {
color: ${theme.frost.nord8};
background-color: ${theme.polarNight.nord0};
${theme.defaultOpacity}
${theme.borderLeft}
color: @base0C;
opacity: 0.85;
background-color: @base00;
}
#custom-lights:hover {
background: ${theme.polarNight.nord3};
background: @base03;
}
'';
windowOffset = 75;
};
wlogout.enable = true;
wofi.enable = true;
wlogout = disabled;
wofi = disabled;
};
};
home.packages = with pkgs; [
pkgs.${namespace}.bolt-launcher
pkgs.${namespace}.librepods
home.packages =
with pkgs.${namespace};
[
# librepods
librepods-beta
]
++ (with pkgs; [
bolt-launcher
iw
iwd
orca-slicer
vscodium
iw
iwd
orca-slicer
vscodium
];
gnomeExtensions.notch-clock-offset
]);
services = {
kdeconnect = {
enable = lib.mkForce true;
indicator = lib.mkForce true;
};
};
programs = {
password-store.enable = true;
password-store = enabled;
};
zsh.shellAliases = shellAliases;
dconf = {
enable = true;
settings = {
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0".name =
"Keyboard Backlight +";
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0".binding =
"<Super>MonBrightnessUp";
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0".command =
"brightnessctl -d kbd_backlight s +10";
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1".name =
"Keyboard Backlight -";
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1".binding =
"<Super>MonBrightnessDown";
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1".command =
"brightnessctl -d kbd_backlight s 10-";
"org/gnome/shell".enabled-extensions = [
"notch-clock-offset@christophbrill.de"
];
"org/gnome/shell/extensions/notch-clock-offset".percent = 40;
"org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/"
];
};
};
}

View File

@@ -1,66 +0,0 @@
{ lib, namespace, ... }:
{
home.username = "matt";
${namespace} = {
shell-aliases = {
enable = true;
flakeInputs = [
"pi4-nixpkgs"
"pi4-home-manager"
"pi4-impermanence"
"pi4-sops-nix"
"pi4-nixos-hardware"
"pi4-nixos-raspberrypi"
"pi4-disko"
];
};
};
sops = {
age.keyFile = "/home/matt/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
validateSopsFiles = false;
secrets = {
"ssh-keys-public/pi4" = {
path = "/home/matt/.ssh/id_ed25519.pub";
mode = "0644";
};
"ssh-keys-private/pi4" = {
path = "/home/matt/.ssh/id_ed25519";
mode = "0600";
};
# "ssh-keys-public/desktop-nixos" = {
# path = "/home/matt/.ssh/authorized_keys";
# mode = "0600";
# };
# "ssh-keys-public/desktop-nixos-root" = {
# path = "/home/matt/.ssh/authorized_keys2";
# mode = "0600";
# };
# "ssh-keys-public/desktop-windows" = {
# path = "/home/matt/.ssh/authorized_keys3";
# mode = "0600";
# };
# "ssh-keys-public/macbook-macos" = {
# path = "/home/matt/.ssh/authorized_keys4";
# mode = "0600";
# };
};
};
programs = {
mangohud.enable = lib.mkForce true;
};
services = {
nextcloud-client.enable = lib.mkForce false;
kdeconnect = {
enable = false;
indicator = false;
};
};
}

View File

@@ -1,15 +1,11 @@
{
config,
lib,
namespace,
...
}:
let
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10 --build-host admin@10.0.1.3";
update-switch = "sudo nixos-rebuild switch --max-jobs 10 --build-host admin@10.0.1.3";
update-flake = "nix flake update pi5-nixpkgs pi5-home-manager pi5-impermanence pi5-nixos-hardware pi5-sops-nix nixos-raspberrypi --flake /etc/nixos";
update-nas = "nixos-rebuild switch --use-remote-sudo --target-host admin@10.0.1.3 --build-host admin@10.0.1.3 --flake ~/nix-config#jallen-nas";
nas-ssh = "kitten ssh admin@10.0.1.3";
};
inherit (lib.${namespace}) disabled;
in
{
@@ -54,12 +50,8 @@ in
};
};
programs = {
zsh.shellAliases = shellAliases;
};
services = {
nextcloud-client.enable = false;
nextcloud-client = lib.mkForce disabled;
kdeconnect = {
enable = false;
indicator = false;

View File

@@ -1,22 +1,41 @@
{ pkgs, namespace, ... }:
{
home.username = "admin";
pkgs,
config,
lib,
inputs,
namespace,
...
}:
let
inherit (lib.${namespace}) enabled;
in
{
# steam-rom-manager HM module is needed for the steam-rom-manager program
# options. On NixOS hosts it's provided via sharedModules; here we add it
# explicitly so the standalone homeConfiguration build also includes it.
imports = [
inputs.steam-rom-manager.homeManagerModules.default
];
home = {
username = "admin";
packages =
with pkgs;
[
heroic
python3
python3Packages.requests
python3Packages.mcp
jq
]
++ (with pkgs.${namespace}; [
moondeck-buddy
]);
};
${namespace} = {
shell-aliases = {
enable = true;
buildHost = ""; # NAS builds locally
flakeInputs = [
"nas-nixpkgs"
"nas-authentik-nix"
"nas-cosmic"
"nas-home-manager"
"nas-impermanence"
"nas-lanzaboote"
"nas-nixos-hardware"
"nas-sops-nix"
];
};
sops.enable = true;
programs.opencode = enabled;
};
sops = {
@@ -55,6 +74,13 @@
};
programs = {
bash = {
shellAliases = {
"llama-status" =
"curl -s http://localhost:8127/health 2>/dev/null && echo 'LLaMA.cpp server is running' || echo 'LLaMA.cpp server is not responding'";
};
};
neovim = {
enable = true;
viAlias = true;
@@ -68,6 +94,23 @@
}
];
};
};
steam-rom-manager = {
enable = true;
steamUsername = "mjallen18";
# Optional: override default paths if needed
environmentVariables = {
romsDirectory = "/home/admin/Emulation/roms";
steamDirectory = "/home/admin/.local/share/Steam";
};
emulators = {
"Non-SRM Shortcuts" = {
enable = true;
parserType = "Non-SRM Shortcuts";
extraArgs = "";
};
};
};
};
}

View File

@@ -1,18 +1,15 @@
{ pkgs, ... }:
{
lib,
pkgs,
namespace,
...
}:
let
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10";
update-switch = "sudo nixos-rebuild switch --max-jobs 10";
update-flake = "nix flake update nas-nixpkgs nas-authentik-nix nas-cosmic nas-crowdsec nas-home-manager nas-impermanence nas-lanzaboote nas-nixos-hardware nas-sops-nix --flake /etc/nixos";
};
inherit (lib.${namespace}) disabled;
in
{
home.username = "admin";
programs = {
zsh.shellAliases = shellAliases;
};
# Configure systemd user service for protonmail-bridge
systemd.user.services.protonmail-bridge = {
Service = {
@@ -24,9 +21,17 @@ in
};
services = {
nextcloud-client = lib.mkForce disabled;
kdeconnect = {
enable = false;
indicator = false;
};
protonmail-bridge = {
enable = true;
extraPackages = with pkgs; [ pass libsecret ];
extraPackages = with pkgs; [
pass
libsecret
];
};
};
}

View File

@@ -1,83 +0,0 @@
{ pkgs, namespace, ... }:
let
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10 --build-host admin@10.0.1.3";
update-switch = "sudo nixos-rebuild switch --max-jobs 10";
update-flake = "nix flake update steamdeck-nixpkgs steamdeck-chaotic steamdeck-home-manager steamdeck-impermanence steamdeck-jovian steamdeck-lanzaboote steamdeck-nixos-hardware steamdeck-sops-nix steamdeck-steam-rom-manager --flake /etc/nixos";
nas-ssh = "ssh admin@10.0.1.3";
};
in
{
home.username = "deck";
${namespace}.desktop.gnome.enable = true;
sops = {
age.keyFile = "/home/deck/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
validateSopsFiles = false;
secrets = {
"ssh-keys-public/deck" = {
path = "/home/deck/.ssh/id_ed25519.pub";
mode = "0644";
};
"ssh-keys-private/deck" = {
path = "/home/deck/.ssh/id_ed25519";
mode = "0600";
};
};
};
programs = {
steam-rom-manager = {
enable = true;
steamUsername = "mjallen18";
# Optional: override default paths if needed
environmentVariables = {
romsDirectory = "/home/deck/Emulation/roms";
steamDirectory = "/home/deck/.local/share/Steam";
};
emulators = {
ryujinx.enable = true;
dolphin-gamecube = {
enable = true;
package = pkgs.dolphin-emu;
romFolder = "gc";
fileTypes = [
".iso"
".ISO"
".gcm"
".GCM"
".ciso"
".CISO"
"rvz"
];
extraArgs = "-b -e \"\${filePath}\"";
};
pcsx2.enable = true;
mgba.enable = true;
"Non-SRM Shortcuts" = {
enable = true;
parserType = "Non-SRM Shortcuts";
extraArgs = "";
};
};
};
zsh.shellAliases = shellAliases;
};
home.packages = with pkgs; [
dolphin-emu
heroic
mgba
prismlauncher
ryubing
omnissa-horizon-client
];
}

View File

@@ -0,0 +1,92 @@
{
lib,
pkgs,
namespace,
...
}:
let
inherit (lib.${namespace}) enabled;
in
{
home.username = "matt";
${namespace}.desktop.gnome = enabled;
sops = {
age.keyFile = "/home/matt/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
validateSopsFiles = false;
secrets = {
"ssh-keys-public/matt" = {
path = "/home/matt/.ssh/id_ed25519.pub";
mode = "0644";
};
"ssh-keys-private/matt" = {
path = "/home/matt/.ssh/id_ed25519";
mode = "0600";
};
};
};
programs = {
steam-rom-manager = {
enable = true;
steamUsername = "mjallen18";
environmentVariables = {
romsDirectory = "/media/sdcard/Emulation/roms";
steamDirectory = "/home/matt/.local/share/Steam";
};
enabledProviders = [
"sgdb"
"steamCDN"
];
imageProviderSettings.sgdb = {
nsfw = false;
humor = false;
imageMotionTypes = [ "static" ];
};
emulators = {
# --- Nintendo ---
ryujinx.enable = true; # Switch (ryubing fork)
yuzu.enable = true; # Switch (eden fork)
dolphin-emu.enable = true; # GameCube / Wii
cemu.enable = true; # Wii U
melonDS.enable = true; # DS
citra.enable = true; # 3DS (azahar fork)
mgba.enable = true; # Game Boy / GBC
mgba-gba.enable = true; # Game Boy Advance
# --- Sony ---
duckstation.enable = false; # PS1
pcsx2.enable = true; # PS2
rpcs3.enable = true; # PS3
ppsspp.enable = true; # PSP
# --- Microsoft ---
xemu.enable = true; # Xbox
# --- Platform parsers (no ROM scanning; artwork only / launcher integration) ---
"Non-SRM Shortcuts".enable = true;
};
};
};
home.packages =
with pkgs;
[
dolphin-emu
heroic
mgba
moonlight-qt
prismlauncher
ryubing
omnissa-horizon-client
]
++ (with pkgs.${namespace}; [
discord-krisp
# librepods-beta
]);
}

View File

@@ -2,7 +2,6 @@
lib,
pkgs,
namespace,
config,
...
}:
let
@@ -10,14 +9,13 @@ let
displayLeft = {
input = "DP-1";
resolution = "3840x2160";
refreshRate = "240.00000";
refreshRate = "120.00000";
};
displayRight = {
input = "DP-2";
resolution = "3840x2160";
refreshRate = "240.00000";
};
theme = config.mjallen.theme.palette;
in
{
home.username = "matt";
@@ -30,9 +28,11 @@ in
enable = true;
};
desktop.gnome = enabled;
programs = {
hyprland = {
enable = true;
enable = false;
primaryDisplay = "DP-1";
monitorv2 = [
@@ -42,14 +42,14 @@ in
position = "0x0";
scale = 1.0;
extra = [
"bitdepth"
"10"
"cm"
"hdr"
"sdrbrightness"
"1.2"
"sdrsaturation"
"0.98"
# "bitdepth"
# "10"
# "cm"
# "hdredid"
# "sdrbrightness"
# "1.2"
# "sdrsaturation"
# "0.98"
];
}
{
@@ -58,14 +58,14 @@ in
position = "3840x0";
scale = 1.0;
extra = [
"bitdepth"
"10"
"cm"
"hdr"
"sdrbrightness"
"1.5"
"sdrsaturation"
"0.98"
# "bitdepth"
# "10"
# "cm"
# "hdredid"
# "sdrbrightness"
# "1.5"
# "sdrsaturation"
# "0.98"
];
}
];
@@ -77,7 +77,7 @@ in
];
windowRule = [
"size 2160 7680, tag:horizonrdp"
"match:tag horizonrdp, size 2160 7680"
];
autostartCommands = [
@@ -104,34 +104,20 @@ in
};
};
btop = enabled;
kitty = {
enable = true;
font = {
name = "JetBrainsMono NFM";
package = pkgs.nerd-fonts.jetbrains-mono;
};
};
mako = {
enable = true;
fontName = "JetBrainsMono NFM";
};
nwg-dock = enabled;
nwg-drawer = enabled;
nwg-panel = {
enable = true;
defaultApps = {
browser = pkgs.firefox;
};
};
kitty = disabled;
mako = disabled;
nwg-dock = disabled;
nwg-drawer = disabled;
nwg-panel = disabled;
waybar = {
enable = true;
enable = false;
layer = "bottom";
network.interface = "wlp9s0";
temperature = {
cpu.enable = true;
gpu.enable = true;
cpu = enabled;
gpu = enabled;
};
extraModules = {
@@ -147,19 +133,19 @@ in
extraModulesStyle = ''
#custom-lights {
color: ${theme.frost.nord8};
background-color: ${theme.polarNight.nord0};
${theme.defaultOpacity}
border-left: 5px solid ${theme.frost.nord8};
color: @base0C;
background-color: @base00;
opacity: 0.85;
border-left: 5px solid @base0C;
}
#custom-lights:hover {
background: ${theme.polarNight.nord3};
background: @base03;
}
'';
};
wlogout = enabled;
wofi = enabled;
wlogout = disabled;
wofi = disabled;
};
};
@@ -174,34 +160,37 @@ in
password-store = enabled;
};
home.packages = with pkgs; [
pkgs.${namespace}.bolt-launcher
pkgs.${namespace}.librepods
bottles
compose2nix
discord
distrobox
heroic
omnissa-horizon-client
jq
lutris
lzip
morph
orca-slicer
piper
prismlauncher
protontricks
protonvpn-gui
python3
runelite
smile
unigine-heaven
via
virt-manager
vorta
waydroid-helper
];
home.packages =
with pkgs;
[
bolt-launcher
clevis
compose2nix
distrobox
goverlay
heroic
home-manager
omnissa-horizon-client
jq
lzip
morph
orca-slicer
piper
prismlauncher
protontricks
protonvpn-gui
runelite
smile
via
virt-manager
vorta
waydroid-helper
winboat
]
++ (with pkgs.${namespace}; [
discord-krisp
# librepods
]);
specialisation = {
"cosmic".configuration = {

View File

@@ -1,130 +1,189 @@
# mjallen-lib Utility Functions
This directory contains utility functions that can be used to enhance your Nix configuration. These functions are inspired by the khanelinix repository and provide a more explicit and modular approach to building Nix configurations.
Utility functions for the NixOS/nix-darwin configuration. Exposed via Snowfall Lib as `lib.mjallen.*`.
## Directory Structure
- `default.nix`: Main entry point that imports and exposes all utility functions
- `module/`: Utilities for module creation and option handling
- `file/`: Utilities for file handling and module discovery
- `system/`: Utilities for system configuration building
- `default.nix` Main entry point; exports `module`, `file`, and `versioning`
- `module/` — Module creation helpers (`mkModule`, `mkContainerService`, `mkSopsEnvFile`, `mkOpt`, etc.)
- `file/` — File and path utilities
- `versioning/` — Multi-source version pinning helpers (used by packages)
## How to Use
---
### 1. Import the Library
## Module Utilities (`lib.mjallen.module`)
The library is already imported in your flake.nix file through the outputs-builder:
### `mkModule`
Creates a NixOS service module with a standard set of options. All config is gated behind `cfg.enable`.
```nix
outputs-builder = channels: {
formatter = inputs.treefmt-nix.lib.mkWrapper channels.nixpkgs ./treefmt.nix;
# Add mjallen-lib to the flake outputs
overlays = {
mjallen-lib = final: prev: {
mjallen-lib = (import ./lib { inherit inputs; }).mjallen-lib;
};
};
};
```
This makes the mjallen-lib available to all your modules through the extended lib.
### 2. Use the Module Utilities
The module utilities provide functions for creating modules with consistent options:
```nix
{ lib, ... }:
let
inherit (lib.mjallen.module) mkModule mkOpt mkBoolOpt;
in
mkModule {
name = "mymodule";
description = "My awesome module";
options = {
setting1 = mkOpt lib.types.str "default" "Description of setting1";
setting2 = mkBoolOpt false "Description of setting2";
};
config = {
# Module implementation
};
lib.mjallen.mkModule {
config # NixOS config attrset (pass-through from module args)
name # Service name — used for option path and systemd unit
description # Text for mkEnableOption (defaults to name)
options # Extra options merged into the submodule
moduleConfig # NixOS config body (applied when cfg.enable = true)
domain # Option namespace domain (default: "services")
serviceName # Systemd service name (default: name)
}
```
### 3. Use the File Utilities
**Standard options provided for free:**
The file utilities provide functions for file handling and module discovery:
| Option | Type | Default | Description |
|---|---|---|---|
| `enable` | bool | `false` | Enable/disable the service |
| `port` | int | `80` | Service listen port |
| `listenAddress` | str | `"0.0.0.0"` | Bind address |
| `openFirewall` | bool | `true` | Open TCP+UDP firewall ports |
| `configDir` | str | `/var/lib/<name>` | Config directory |
| `dataDir` | str | `/var/lib/<name>/data` | Data directory |
| `createUser` | bool | `false` | Create a dedicated system user |
| `configureDb` | bool | `false` | Create a PostgreSQL database |
| `environmentFile` | str\|null | `null` | Path to an env-file |
| `extraEnvironment` | attrs | `{}` | Extra environment variables |
| `hashedPassword` | str\|null | `null` | Hashed password for web auth |
| `puid` / `pgid` | str | `"911"` / `"100"` | UID/GID for container services |
| `timeZone` | str | `"UTC"` | Timezone for container services |
| `redis.enable` | bool | `false` | Create a Redis instance for this service |
| `redis.port` | int | `6379` | Redis port |
| `reverseProxy.enable` | bool | `false` | Add a Caddy reverse proxy block |
| `reverseProxy.subdomain` | str | `<name>` | Caddy subdomain |
| `reverseProxy.domain` | str | `"mjallen.dev"` | Caddy base domain |
| `reverseProxy.upstreamUrl` | str\|null | `null` | Override upstream URL |
| `reverseProxy.extraCaddyConfig` | lines | `""` | Extra Caddyfile directives |
**Default behaviour when enabled:**
- Adds Caddy reverse proxy block (if `reverseProxy.enable = true`)
- Opens firewall ports (if `openFirewall = true`)
- Creates system user/group (if `createUser = true`)
- Creates PostgreSQL database (if `configureDb = true`)
- Creates Redis instance (if `redis.enable = true`)
- Adds `RequiresMountsFor` systemd dependency for `configDir` and `dataDir`
---
### `mkContainerService`
Wraps `mkModule` for Podman/OCI container services. Generates the full container definition automatically.
```nix
{ lib, ... }:
let
inherit (lib.mjallen.file) safeImport importModulesRecursive;
in
{
# Import a file with error handling
myConfig = safeImport ./my-config.nix {};
# Import all modules recursively
imports = importModulesRecursive ./modules;
lib.mjallen.mkContainerService {
config # NixOS config attrset
name # Service/container name
image # OCI image reference (e.g. "ghcr.io/example/app:latest")
internalPort # Port the container listens on internally
description # Human-readable description (defaults to name)
options # Extra mkModule options
volumes # Extra volume mount strings
environment # Extra environment variables (merged with PUID/PGID/TZ)
environmentFiles # List of env-file paths (e.g. SOPS template paths)
extraOptions # Extra --opt strings for the container runtime
devices # Device mappings
extraConfig # Extra NixOS config merged into moduleConfig
}
```
### 4. Use the System Utilities
The systemd service is named `podman-<name>`, and the port binding is `<cfg.port>:<internalPort>`.
The system utilities provide functions for building system configurations:
---
### `mkSopsEnvFile`
Generates a SOPS secrets block and a SOPS template env-file in a single call. Useful for services that need secrets injected as environment variables.
```nix
{ lib, ... }:
let
inherit (lib.mjallen.system.common) mkHomeManagerConfig;
in
{
# Build home-manager configurations
homeManagerConfig = mkHomeManagerConfig {
extendedLib = lib;
inputs = inputs;
system = "x86_64-linux";
matchingHomes = { ... };
};
lib.mjallen.mkSopsEnvFile {
secrets # attrset: sops-key → extra attrs (owner, group, etc.)
name # Template file name, e.g. "myapp.env"
content # Template body (use config.sops.placeholder."key")
restartUnit # Systemd unit to restart when secrets change
owner # File owner (default: "nix-apps")
group # File group (default: "jallen-nas")
mode # File permissions (default: "660")
sopsFile # Default SOPS file for all secrets
}
```
## Available Functions
---
### Module Utilities
### Option helpers
- `mkModule`: Create a module with common options
- `mkOpt`: Create an option with a type, default value, and description
- `mkOpt'`: Create an option with a type and default value (no description)
- `mkBoolOpt`: Create a boolean option with a default value and description
- `mkBoolOpt'`: Create a boolean option with a default value (no description)
- `enabled`: Standard enable pattern
- `disabled`: Standard disable pattern
- `capitalize`: Capitalize a string
- `boolToNum`: Convert a boolean to a number
- `default-attrs`: Apply mkDefault to all attributes
- `force-attrs`: Apply mkForce to all attributes
- `nested-default-attrs`: Apply default-attrs to nested attributes
- `nested-force-attrs`: Apply force-attrs to nested attributes
| Function | Signature | Description |
|---|---|---|
| `mkOpt` | `type default description → mkOption` | Standard option shorthand |
| `mkOpt'` | `type → default → mkOption` | `mkOpt` without description |
| `mkBoolOpt` | `default → description → mkOption` | Boolean option shorthand |
| `mkBoolOpt'` | `default → mkOption` | Boolean option without description |
| `mkReverseProxyOpt` | `name → attrset` | Standard Caddy reverse proxy sub-options |
### File Utilities
---
- `readFile`: Read a file and return its contents
- `pathExists`: Check if a file exists
- `safeImport`: Import a nix file with error handling
- `scanDir`: Scan a directory and return directory names
- `getFile`: Get a file path relative to the flake root
- `importModulesRecursive`: Recursively discover and import all Nix modules in a directory tree
- `scanSystems`: Recursively scan systems directory structure
- `filterNixOSSystems`: Filter systems for NixOS (Linux)
- `filterDarwinSystems`: Filter systems for Darwin (macOS)
- `scanHomes`: Scan homes directory structure for home configurations
### Convenience shorthands
### System Utilities
| Value | Expands to |
|---|---|
| `enabled` | `{ enable = true; }` |
| `disabled` | `{ enable = false; }` |
- `mkExtendedLib`: Extend the nixpkgs lib with mjallen-lib
- `mkNixpkgsConfig`: Create a nixpkgs configuration
- `mkHomeConfigs`: Create home configurations for a system and hostname
- `mkHomeManagerConfig`: Create a home-manager configuration
- `mkSpecialArgs`: Create special arguments for a system configuration
---
### Attribute utilities
| Function | Description |
|---|---|
| `default-attrs` | Apply `lib.mkDefault` to every value in an attrset |
| `force-attrs` | Apply `lib.mkForce` to every value in an attrset |
| `nested-default-attrs` | Apply `default-attrs` one level deeper |
| `nested-force-attrs` | Apply `force-attrs` one level deeper |
---
### String utilities
| Function | Description |
|---|---|
| `capitalize` | Capitalise the first character of a string |
---
### Boolean utilities
| Function | Description |
|---|---|
| `boolToNum` | `true → 1`, `false → 0` |
---
## Home Manager Utilities (`lib.mjallen.module`)
### `mkHomeModule`
Creates a Home Manager module with a standard `enable` option.
```nix
lib.mjallen.mkHomeModule {
config # HM config attrset
domain # Option namespace domain, e.g. "programs" or "desktop"
name # Module name, e.g. "btop"
description # Text for mkEnableOption (defaults to name)
options # Extra options merged into the submodule
moduleConfig # HM config body (applied when cfg.enable = true)
}
```
---
## File Utilities (`lib.mjallen.file`)
Helpers for resolving paths relative to the flake root. Primarily used internally by modules and packages.
---
## Versioning Utilities (`lib.mjallen.versioning`)
Used by packages that track multiple upstream variants (e.g. `proton-cachyos`, `linux-rpi`). Reads a `version.json` file and resolves sources with optional variable substitution and per-variant overrides.
See `lib/versioning/default.nix` for the full API and `docs/version.schema.json` for the `version.json` schema.

View File

@@ -1,3 +0,0 @@

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ

View File

@@ -1,62 +0,0 @@
{ inputs }:
let
inherit (inputs.nixpkgs.lib)
concatLists
concatMapStrings
foldl'
genList
hasSuffix
imap0
length
mod
nameValuePair
stringToCharacters
sublist
substring
take
;
in
rec {
base64Table = builtins.listToAttrs (
imap0 (i: c: nameValuePair c i) (
# The '=' is included so the main algorithm doesn't fail before we can trim the result
stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
)
);
# Generated using python3:
# print(''.join([ chr(n) for n in range(1, 256) ]), file=open('ascii', 'w'))
ascii = builtins.readFile ./ascii;
decode =
str:
let
paddingCount =
if hasSuffix "==" str then
2
else if hasSuffix "=" str then
1
else
0;
numbers64 = map (c: base64Table.${c}) (stringToCharacters str);
allBytes = concatLists (
genList (
i:
let
v = foldl' (acc: el: acc * 64 + el) 0 (sublist (i * 4) 4 numbers64);
in
[
(mod (v / 256 / 256) 256)
(mod (v / 256) 256)
(mod v 256)
]
) (length numbers64 / 4)
);
finalBytes = take (length allBytes - paddingCount) allBytes;
in
concatMapStrings (n: substring (n - 1) 1 ascii) finalBytes;
}

View File

@@ -1,19 +1,11 @@
{ inputs, ... }:
{
mjallen-lib = {
# Import module utilities
module = import ./module { inherit inputs; };
# Import file utilities
file = import ./file { inherit inputs; };
# Import system utilities
system = import ./system { inherit inputs; };
# Import reverse proxy utilities
reverseproxy = import ./reverseproxy { inherit inputs; };
# Import examples
examples = import ./examples { inherit inputs; };
versioning = import ./versioning {
lib = inputs.nixpkgs.lib;
inherit inputs;
};
};
}

View File

@@ -1,8 +0,0 @@
{ ... }:
{
# Import all examples
sops = import ./sops.nix;
homeSops = import ./home-sops.nix;
fileUtils = import ./file-utils.nix;
systemUtils = import ./system-utils.nix;
}

View File

@@ -1,60 +0,0 @@
{ lib, ... }:
let
inherit (lib.mjallen.file)
readFile
pathExists
safeImport
scanDir
getFile
importModulesRecursive
scanSystems
filterNixOSSystems
filterDarwinSystems
scanHomes
;
in
{
# Example of reading a file
myFileContent = readFile ./example.txt;
# Example of checking if a file exists
fileExists = pathExists ./example.txt;
# Example of safely importing a file
myConfig = safeImport ./my-config.nix { };
# Example of scanning a directory
directoryContents = scanDir ./modules;
# Example of getting a file path relative to the flake root
flakeFile = getFile "flake.nix";
# Example of importing modules recursively
modules = importModulesRecursive ./modules;
# Example of scanning systems
allSystems = scanSystems ./systems;
# Example of filtering systems
nixosSystems = filterNixOSSystems allSystems;
darwinSystems = filterDarwinSystems allSystems;
# Example of scanning homes
allHomes = scanHomes ./homes;
# Example of using these functions together
nixosConfigurations = lib.mapAttrs' (
_name:
{ system, hostname, ... }:
{
name = hostname;
value = lib.nixosSystem {
inherit system;
modules = [
{ networking.hostName = hostname; }
]
++ importModulesRecursive ./modules/nixos;
};
}
) nixosSystems;
}

View File

@@ -1,36 +0,0 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib.mjallen.module) mkModule mkOpt;
in
mkModule {
name = "sops";
description = "SOPS secret management for home-manager";
options = {
defaultSopsFile = mkOpt lib.types.path null "Default sops file.";
sshKeyPaths = mkOpt (lib.types.listOf lib.types.str) [ ] "SSH Key paths to use.";
};
config = {
home.packages = with pkgs; [
age
sops
ssh-to-age
];
sops = {
inherit (config.mjallen.sops) defaultSopsFile;
defaultSopsFormat = "yaml";
age = {
generateKey = true;
keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ] ++ config.mjallen.sops.sshKeyPaths;
};
};
};
}

View File

@@ -1,117 +0,0 @@
# Example usage of the reverse proxy utilities
{ lib, ... }:
let
inherit (lib.mjallen-lib.reverseproxy)
mkReverseProxy
mkReverseProxies
templates
middlewares
urls
;
in
{
# Example 1: Simple reverse proxy for a local service
simpleProxy = mkReverseProxy {
name = "myapp";
subdomain = "myapp";
url = "http://127.0.0.1:3000";
};
# Example 2: Authenticated service with custom middlewares
authProxy = mkReverseProxy {
name = "admin-panel";
subdomain = "admin";
url = "http://127.0.0.1:8080";
middlewares = middlewares.authBasic;
};
# Example 3: Container-based service
containerProxy = mkReverseProxy {
name = "nextcloud";
subdomain = "cloud";
url = urls.container "nextcloud" 80;
middlewares = middlewares.basic;
};
# Example 4: Multiple proxies at once
multipleProxies = mkReverseProxies [
{
name = "grafana";
subdomain = "grafana";
url = urls.localhost 3000;
middlewares = middlewares.authBasic;
}
{
name = "prometheus";
subdomain = "prometheus";
url = urls.localhost 9090;
middlewares = middlewares.internal;
}
{
name = "alertmanager";
subdomain = "alerts";
url = urls.localhost 9093;
middlewares = middlewares.authBasic;
}
];
# Example 5: Using templates for common patterns
webappExample = templates.webapp {
name = "webapp";
subdomain = "app";
port = 8080;
};
authWebappExample = templates.authWebapp {
name = "secure-app";
subdomain = "secure";
port = 9000;
};
containerExample = templates.containerService {
name = "gitea";
subdomain = "git";
containerName = "gitea";
port = 3000;
};
internalExample = templates.internalService {
name = "internal-api";
subdomain = "api-internal";
port = 8000;
};
# Example 6: Custom domain and advanced configuration
customProxy = mkReverseProxy {
name = "custom-service";
subdomain = "custom";
url = "http://10.0.1.100:8080";
domain = "example.com";
priority = 20;
rule = "Host(`custom.example.com`) && PathPrefix(`/api`)";
middlewares = [
"crowdsec"
"whitelist-geoblock"
"rate-limit"
];
};
# Example usage in a Traefik configuration:
#
# mjallen.services.traefik = {
# enable = true;
# extraServices = multipleProxies.extraServices;
# extraRouters = multipleProxies.extraRouters;
# };
#
# Or for individual proxies:
#
# mjallen.services.traefik = {
# enable = true;
# extraServices = [ simpleProxy.service ];
# extraRouters = [{
# inherit (simpleProxy.router) subdomain entryPoints middlewares;
# service = simpleProxy.router.service;
# }];
# };
}

View File

@@ -1,40 +0,0 @@
{ config, lib, ... }:
let
inherit (lib.mjallen.module) mkModule mkOpt mkBoolOpt;
in
mkModule {
name = "sops";
description = "SOPS secret management";
options = {
defaultSopsFile = mkOpt lib.types.path null "Default sops file.";
generateAgeKey = mkBoolOpt true "Whether to automatically generate an age key if one doesn't exist.";
ageKeyPath =
mkOpt (lib.types.nullOr lib.types.str) null
"Custom path to the age key file. If null, will use the default path.";
sshKeyPaths = mkOpt (lib.types.listOf lib.types.str) [
"/etc/ssh/ssh_host_ed25519_key"
] "SSH Key paths to use.";
validateSopsFiles = mkBoolOpt false "Whether to validate that sops files exist.";
};
config = {
sops = {
inherit (config.mjallen.sops) defaultSopsFile validateSopsFiles;
age = {
inherit (config.mjallen.sops) generateAgeKey;
keyFile =
if config.mjallen.sops.ageKeyPath != null then
config.mjallen.sops.ageKeyPath
else
"${config.users.users.${config.mjallen.user.name}.home}/.config/sops/age/keys.txt";
sshKeyPaths = config.mjallen.sops.sshKeyPaths;
};
};
};
}

View File

@@ -1,132 +0,0 @@
{ inputs, ... }:
let
inherit (inputs.self.mjallen-lib.system.common)
mkExtendedLib
mkNixpkgsConfig
mkHomeConfigs
mkHomeManagerConfig
mkSpecialArgs
;
in
{
# Example of creating NixOS configurations
nixosConfigurations =
let
# Get all systems
allSystems = inputs.self.mjallen-lib.file.scanSystems ../systems;
# Filter for NixOS systems
nixosSystems = inputs.self.mjallen-lib.file.filterNixOSSystems allSystems;
in
inputs.nixpkgs.lib.mapAttrs' (
_name:
{ system, hostname, ... }:
let
# Create extended lib with mjallen-lib
extendedLib = mkExtendedLib inputs.self inputs.nixpkgs;
# Find matching home configurations for this system
matchingHomes = mkHomeConfigs {
flake = inputs.self;
inherit system hostname;
};
# Create home-manager configuration
homeManagerConfig = mkHomeManagerConfig {
inherit
extendedLib
inputs
system
matchingHomes
;
isNixOS = true;
};
in
{
name = hostname;
value = inputs.nixpkgs.lib.nixosSystem {
inherit system;
# Pass special arguments to modules
specialArgs = mkSpecialArgs {
inherit inputs hostname extendedLib;
username = "mjallen";
};
modules = [
# Set lib to extended lib
{ _module.args.lib = extendedLib; }
# Configure nixpkgs
{
nixpkgs = {
inherit system;
}
// mkNixpkgsConfig inputs.self;
}
# Import home-manager module
inputs.home-manager.nixosModules.home-manager
# Auto-inject home configurations
homeManagerConfig
# Import all nixos modules recursively
../${system}/${hostname}
]
++ (extendedLib.mjallen.file.importModulesRecursive ../modules/nixos);
};
}
) nixosSystems;
# Example of creating home-manager configurations
homeConfigurations =
let
# Get all homes
allHomes = inputs.self.mjallen-lib.file.scanHomes ../homes;
in
inputs.nixpkgs.lib.mapAttrs' (
_name:
{
system,
username,
hostname,
userAtHost,
path,
...
}:
let
# Create extended lib with mjallen-lib
extendedLib = mkExtendedLib inputs.self inputs.nixpkgs;
in
{
name = userAtHost;
value = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs {
inherit system;
inherit ((mkNixpkgsConfig inputs.self)) config overlays;
};
extraSpecialArgs = {
inherit
inputs
hostname
username
system
;
inherit (inputs) self;
lib = extendedLib;
};
modules = [
# Set lib to extended lib
{ _module.args.lib = extendedLib; }
# Import the home configuration
path
]
++ (extendedLib.mjallen.file.importModulesRecursive ../modules/home);
};
}
) allHomes;
}

View File

@@ -1,4 +1,8 @@
{ inputs }:
{
inputs,
lib,
namespace,
}:
let
inherit (inputs.nixpkgs.lib)
mapAttrs
@@ -10,29 +14,275 @@ let
mkDefault
mkForce
;
base64Lib = import ../base64 { inherit inputs; };
in
rec {
# Conditionally enable modules based on system
enableForSystem =
system: modules:
builtins.filter (
mod: mod.systems or [ ] == [ ] || builtins.elem system (mod.systems or [ ])
) modules;
# ---------------------------------------------------------------------------
# NixOS service module helpers
# ---------------------------------------------------------------------------
# Create a module with common options
# Create a NixOS module with standard options (enable, port, reverseProxy,
# firewall, user, postgresql, redis) and optional caller-supplied options and
# config. All config is gated behind `cfg.enable`.
mkModule =
{
name,
description ? "",
options ? { },
config ? { },
moduleConfig ? { },
domain ? "services",
config,
serviceName ? name,
}:
let
cfg = config.${namespace}.${domain}.${name};
upstreamUrl =
if cfg.reverseProxy.upstreamUrl != null then
cfg.reverseProxy.upstreamUrl
else
"http://127.0.0.1:${toString cfg.port}";
fqdn = "${cfg.reverseProxy.subdomain}.${cfg.reverseProxy.domain}";
defaultConfig = {
# Caddy reverse proxy: when reverseProxy.enable = true, contribute this
# service's named-matcher block into the shared wildcard virtual host.
services.caddy.virtualHosts."*.${cfg.reverseProxy.domain}" = lib.mkIf cfg.reverseProxy.enable {
extraConfig = ''
@${name} host ${fqdn}
handle @${name} {
reverse_proxy ${upstreamUrl}
${cfg.reverseProxy.extraCaddyConfig}
}
'';
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
allowedUDPPorts = [ cfg.port ];
};
users = lib.mkIf cfg.createUser {
users.${name} = {
isSystemUser = true;
group = name;
home = cfg.configDir;
};
groups.${name} = { };
};
# RequiresMountsFor is silently ignored when the paths live on the root
# filesystem, so this is safe on non-NAS hosts too.
systemd.services.${serviceName}.unitConfig.RequiresMountsFor = [
cfg.configDir
cfg.dataDir
];
services = {
postgresql = lib.mkIf cfg.configureDb {
enable = true;
ensureDatabases = [ name ];
ensureUsers = [
{
name = name;
ensureDBOwnership = true;
}
];
};
redis.servers.${name} = lib.mkIf cfg.redis.enable {
enable = true;
port = cfg.redis.port;
};
};
};
in
{ lib, ... }:
{
options.mjallen.${name} = lib.mkOption {
imports = [
{ config = lib.mkIf cfg.enable defaultConfig; }
{ config = lib.mkIf cfg.enable moduleConfig; }
];
options.${namespace}.${domain}.${name} = lib.mkOption {
type = lib.types.submodule {
options = {
enable = lib.mkEnableOption description;
port = mkOpt types.int 80 "Port for ${name} to listen on";
configDir = mkOpt types.str "/var/lib/${name}" "Path to the config directory";
dataDir = mkOpt types.str "/var/lib/${name}/data" "Path to the data directory";
createUser = mkBoolOpt false "Create a dedicated system user for this service";
configureDb = mkBoolOpt false "Manage a PostgreSQL database for this service";
environmentFile =
mkOpt (types.nullOr types.str) null
"Path to an environment file (EnvironmentFile=)";
puid = mkOpt types.str "911" "User ID for container-based services";
pgid = mkOpt types.str "100" "Group ID for container-based services";
timeZone = mkOpt types.str "UTC" "Timezone for container-based services";
listenAddress = mkOpt types.str "0.0.0.0" "Listen address";
openFirewall = mkBoolOpt true "Open firewall ports for this service";
redis = {
enable = lib.mkEnableOption "a dedicated Redis server for this service";
port = mkOpt types.int 6379 "Redis port for ${name}";
};
hashedPassword =
mkOpt (types.nullOr types.str) null
"Hashed password (e.g. for web-based authentication)";
extraEnvironment =
mkOpt (types.attrsOf types.str) { }
"Extra environment variables passed to the service";
reverseProxy = mkReverseProxyOpt name;
}
// options;
};
default = { };
};
};
# Wraps mkModule for Podman/OCI container services. Generates all the
# standard mkModule options plus the container definition. The serviceName
# is set to "podman-<name>" automatically.
#
# Required args:
# config — the NixOS config attrset (pass through from the module args)
# name — service name (used for the container name and option path)
# image — OCI image reference string
# internalPort — port the container listens on internally
#
# Optional args:
# description — human-readable description (defaults to name)
# options — extra mkModule options attrset
# volumes — extra volume strings (in addition to none)
# environment — extra environment variables (merged with PUID/PGID/TZ)
# environmentFiles — list of paths to env-files (e.g. sops template paths)
# extraOptions — list of extra --opt strings passed to the container runtime
# devices — list of device mappings
# extraConfig — extra NixOS config merged into moduleConfig
mkContainerService =
{
config,
name,
image,
internalPort,
description ? name,
options ? { },
volumes ? [ ],
environment ? { },
environmentFiles ? [ ],
extraOptions ? [ ],
devices ? [ ],
extraConfig ? { },
}:
let
cfg = config.${namespace}.services.${name};
in
mkModule {
inherit
config
name
description
options
;
serviceName = "podman-${name}";
moduleConfig = lib.recursiveUpdate {
virtualisation.oci-containers.containers.${name} = {
autoStart = true;
inherit
image
volumes
environmentFiles
extraOptions
devices
;
ports = [ "${toString cfg.port}:${toString internalPort}" ];
environment = {
PUID = cfg.puid;
PGID = cfg.pgid;
TZ = cfg.timeZone;
}
// environment;
};
} extraConfig;
};
# Generates a sops secrets block + a sops template env-file in a single call.
#
# secrets — attrset of sops secret keys → extra attrs (e.g. owner/group).
# The sopsFile is set automatically to nas-secrets.yaml unless
# overridden per-secret via { sopsFile = ...; }.
# name — template file name, e.g. "glance.env"
# content — the template body string (use config.sops.placeholder."key")
# restartUnit — systemd unit to restart when the secret changes
# owner, group, mode — file ownership/permissions (defaults match NAS convention)
# sopsFile — default sops file for all secrets (can be overridden per-secret)
mkSopsEnvFile =
{
secrets,
name,
content,
restartUnit,
owner ? "nix-apps",
group ? "jallen-nas",
mode ? "660",
sopsFile ? (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml"),
}:
{
sops.secrets = mapAttrs (_key: extra: { inherit sopsFile; } // extra) secrets;
sops.templates.${name} = {
inherit
mode
owner
group
content
;
restartUnits = [ restartUnit ];
};
};
# ---------------------------------------------------------------------------
# Home Manager module helper
# ---------------------------------------------------------------------------
# Create a Home Manager module with a standard enable option and optional
# extra options, gating all config behind `cfg.enable`.
#
# domain — option namespace domain, e.g. "programs" or "desktop"
# name — module name, e.g. "btop"
# description — text for mkEnableOption (defaults to name)
# options — attrset of extra options merged into the submodule
# config — the NixOS/HM config attrset passed through from module args
# moduleConfig — the Home Manager config body (already gated behind cfg.enable)
mkHomeModule =
{
config,
domain,
name,
description ? name,
options ? { },
moduleConfig,
}:
let
cfg = config.${namespace}.${domain}.${name};
in
{ lib, ... }:
{
options.${namespace}.${domain}.${name} = lib.mkOption {
type = lib.types.submodule {
options = {
enable = lib.mkEnableOption description;
@@ -42,83 +292,52 @@ rec {
default = { };
};
config = lib.mkIf config.mjallen.${name}.enable config;
config = lib.mkIf cfg.enable moduleConfig;
};
mkContainer =
{
name,
localAddress ? "127.0.0.1",
ports ? [ "80" ],
bindMounts ? { },
config ? { },
}:
{ lib, ... }:
{
containers.${name} = {
inherit localAddress bindMounts;
config = config // {
networking = {
firewall = {
enable = true;
allowedTCPPorts = ports;
};
# Use systemd-resolved inside the container
# Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686
useHostResolvConf = lib.mkForce false;
};
services.resolved.enable = true;
system.stateVersion = "23.11";
};
autoStart = lib.mkDefault true;
privateNetwork = lib.mkDefault true;
hostAddress = lib.mkDefault "10.0.1.3";
};
networking = {
nat.forwardPorts = map (port: {
destination = lib.mkDefault "${localAddress}:${toString port}";
sourcePort = lib.mkDefault port;
}) ports;
firewall = {
allowedTCPPorts = ports;
allowedUDPPorts = ports;
};
};
};
# Migrated mjallen utilities
# ---------------------------------------------------------------------------
# Option creation helpers
# ---------------------------------------------------------------------------
mkOpt =
type: default: description:
mkOption { inherit type default description; };
mkOpt' = type: default: mkOpt type default null;
mkOpt' = type: default: mkOpt type default "";
mkBoolOpt = mkOpt types.bool;
mkBoolOpt' = mkOpt' types.bool;
mkReverseProxyOpt = {
enable = mkBoolOpt false "Enable reverse proxy support";
mkReverseProxyOpt = name: {
enable = mkBoolOpt false "Enable Caddy reverse proxy for this service";
subdomain = mkOpt types.str "" "subdomain of the service";
subdomain = mkOpt types.str name "Subdomain for the service (default: service name)";
middlewares = mkOpt (types.listOf types.str) [ ] "List of middlewares to use";
domain = mkOpt types.str "mjallen.dev" "Base domain for the reverse proxy";
upstreamUrl =
mkOpt (types.nullOr types.str) null
"Override upstream URL (e.g. for a service on a different host). Defaults to http://127.0.0.1:<port>.";
extraCaddyConfig = mkOpt types.lines "" "Extra Caddyfile directives inside this virtual host block";
};
# Standard enable/disable patterns
# ---------------------------------------------------------------------------
# Convenience shorthands
# ---------------------------------------------------------------------------
enabled = {
enable = true;
};
disabled = {
enable = false;
};
# ---------------------------------------------------------------------------
# String utilities
# ---------------------------------------------------------------------------
capitalize =
s:
let
@@ -126,10 +345,16 @@ rec {
in
if len == 0 then "" else (toUpper (substring 0 1 s)) + (substring 1 len s);
# ---------------------------------------------------------------------------
# Boolean utilities
# ---------------------------------------------------------------------------
boolToNum = bool: if bool then 1 else 0;
# ---------------------------------------------------------------------------
# Attribute manipulation utilities
# ---------------------------------------------------------------------------
default-attrs = mapAttrs (_key: mkDefault);
force-attrs = mapAttrs (_key: mkForce);
@@ -138,4 +363,3 @@ rec {
nested-force-attrs = mapAttrs (_key: force-attrs);
}
// base64Lib

View File

@@ -1,220 +0,0 @@
{ inputs }:
let
inherit (inputs.nixpkgs.lib)
listToAttrs
nameValuePair
;
in
rec {
# Create a service configuration for Traefik
mkService =
{
name,
url,
loadBalancer ? { },
}:
{
inherit name url;
config = {
loadBalancer = {
servers = [ { inherit url; } ];
}
// loadBalancer;
};
};
# Create a router configuration for Traefik
mkRouter =
{
subdomain,
domain ? "mjallen.dev",
service,
entryPoints ? [ "websecure" ],
middlewares ? [
"crowdsec"
"whitelist-geoblock"
],
priority ? null,
rule ? null,
tls ? {
certResolver = "letsencrypt";
},
}:
{
inherit
subdomain
service
entryPoints
middlewares
;
config = {
inherit
entryPoints
service
middlewares
tls
;
rule = if rule != null then rule else "Host(`${subdomain}.${domain}`)";
}
// (if priority != null then { inherit priority; } else { });
};
# Create both service and router for a simple reverse proxy setup
mkReverseProxy =
{
name,
subdomain,
url,
domain ? "mjallen.dev",
entryPoints ? [ "websecure" ],
middlewares ? [
"crowdsec"
"whitelist-geoblock"
],
priority ? null,
rule ? null,
tls ? {
certResolver = "letsencrypt";
},
loadBalancer ? { },
}:
{
service = mkService {
inherit name url loadBalancer;
};
router = mkRouter {
inherit
subdomain
domain
entryPoints
middlewares
priority
rule
tls
;
service = name;
};
};
# Convert a list of services to the format expected by Traefik module
servicesToConfig =
services: listToAttrs (map (service: nameValuePair service.name service.config) services);
# Convert a list of routers to the format expected by Traefik module
routersToConfig =
routers: listToAttrs (map (router: nameValuePair router.subdomain router.config) routers);
# Helper to create multiple reverse proxies at once
mkReverseProxies =
proxies:
let
results = map mkReverseProxy proxies;
services = map (result: result.service) results;
routers = map (result: result.router) results;
in
{
services = servicesToConfig services;
routers = routersToConfig routers;
extraServices = services;
extraRouters = map (router: {
inherit (router) subdomain entryPoints middlewares;
service = router.service;
}) routers;
};
# Common middleware configurations
middlewares = {
# Authentication middleware
auth = [ "authentik" ];
# Basic security (default)
basic = [
"crowdsec"
"whitelist-geoblock"
];
# Internal only access
internal = [
"crowdsec"
"whitelist-geoblock"
"internal-ipallowlist"
];
# WebSocket support
websocket = [
"crowdsec"
"whitelist-geoblock"
"onlyoffice-websocket"
];
# Authenticated with basic security
authBasic = [
"crowdsec"
"whitelist-geoblock"
"authentik"
];
};
# Common service URL builders
urls = {
# Local container service
container =
containerName: port: "http://\${config.containers.${containerName}.localAddress}:${toString port}";
# Local host service
localhost = port: "http://127.0.0.1:${toString port}";
# Network service
network = ip: port: "http://${ip}:${toString port}";
# Server IP service (using your server IP pattern)
server = port: "http://\${serverIp}:${toString port}";
};
# Pre-configured reverse proxy templates
templates = {
# Standard web application
webapp =
{ port, ... }@args:
mkReverseProxy (
{
url = urls.localhost port;
middlewares = middlewares.basic;
}
// args
);
# Authenticated web application
authWebapp =
{ port, ... }@args:
mkReverseProxy (
{
url = urls.localhost port;
middlewares = middlewares.authBasic;
}
// args
);
# Container-based service
containerService =
{ containerName, port, ... }@args:
mkReverseProxy (
{
url = urls.container containerName port;
middlewares = middlewares.basic;
}
// args
);
# Internal-only service
internalService =
{ port, ... }@args:
mkReverseProxy (
{
url = urls.localhost port;
middlewares = middlewares.internal;
}
// args
);
};
}

View File

@@ -1,103 +0,0 @@
{ inputs }:
let
inherit (inputs.nixpkgs.lib) filterAttrs mapAttrs';
in
{
mkExtendedLib =
flake: nixpkgs:
nixpkgs.lib.extend (
_final: _prev: {
mjallen = flake.mjallen-lib;
}
);
mkNixpkgsConfig = flake: {
overlays = builtins.attrValues flake.overlays;
config = {
allowAliases = false;
allowUnfree = true;
permittedInsecurePackages = [
# Add any permitted insecure packages here
"mbedtls-2.28.10"
];
};
};
mkHomeConfigs =
{
flake,
system,
hostname,
}:
let
inherit (flake.mjallen-lib.file) scanHomes;
homesPath = ../../homes;
allHomes = scanHomes homesPath;
in
filterAttrs (
_name: homeConfig: homeConfig.system == system && homeConfig.hostname == hostname
) allHomes;
mkHomeManagerConfig =
{
extendedLib,
inputs,
system,
matchingHomes,
isNixOS ? true,
}:
if matchingHomes != { } then
{
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
inherit inputs system;
inherit (inputs) self;
lib = extendedLib;
};
sharedModules = [
{ _module.args.lib = extendedLib; }
]
++ (extendedLib.mjallen.file.importModulesRecursive ../../modules/home);
users = mapAttrs' (_name: homeConfig: {
name = homeConfig.username;
value = {
imports = [ homeConfig.path ];
home = {
inherit (homeConfig) username;
homeDirectory = inputs.nixpkgs.lib.mkDefault (
if isNixOS then "/home/${homeConfig.username}" else "/Users/${homeConfig.username}"
);
};
}
// (
if isNixOS then
{
_module.args.username = homeConfig.username;
}
else
{ }
);
}) matchingHomes;
};
}
else
{ };
mkSpecialArgs =
{
inputs,
hostname,
username,
extendedLib,
}:
{
inherit inputs hostname username;
inherit (inputs) self;
lib = extendedLib;
namespace = "mjallen";
format = "system";
host = hostname;
};
}

View File

@@ -1,5 +0,0 @@
{ inputs }:
{
# Common utilities used by system builders
common = import ./common.nix { inherit inputs; };
}

219
lib/versioning/default.nix Normal file
View File

@@ -0,0 +1,219 @@
{
lib,
inputs,
system ? "aarch64-linux",
}:
let
pkgs = inputs.nixpkgs.legacyPackages.${system};
in
let
inherit (builtins)
isAttrs
isList
isString
hasAttr
getAttr
attrNames
toString
replaceStrings
;
mapAttrs = lib.mapAttrs;
recursiveUpdate = lib.recursiveUpdate;
# Deep-merge attrsets (right-biased).
deepMerge = a: b: recursiveUpdate a b;
# Merge component sources: base.sources overlaid by overrides (component-wise deep merge).
mergeSources =
baseSources: overrides:
baseSources
// mapAttrs (
name: ov: if hasAttr name baseSources then deepMerge (getAttr name baseSources) ov else ov
) overrides;
# Apply a single variant overlay (variables + sources).
applyVariantOnce =
selected: variant:
let
vVars = if variant ? variables then variant.variables else { };
vSrcs = if variant ? sources then variant.sources else { };
in
{
variables = selected.variables // vVars;
sources = mergeSources selected.sources vSrcs;
};
# Apply platform-specific overrides if present for the given system.
applyPlatforms =
selected: variant: system:
if system == null || !(variant ? platforms) || !(hasAttr system variant.platforms) then
selected
else
let
p = variant.platforms.${system};
pVars = if p ? variables then p.variables else { };
pSrcs = if p ? sources then p.sources else { };
in
{
variables = selected.variables // pVars;
sources = mergeSources selected.sources pSrcs;
};
# Resolve variant chain via inherits (ancestor first), then apply platforms.
resolveVariant =
spec: baseSelected: variantName: system:
if variantName == null || !(spec ? variants) || !(hasAttr variantName spec.variants) then
baseSelected
else
let
v = spec.variants.${variantName};
parentSelected =
if v ? inherits then resolveVariant spec baseSelected v.inherits system else baseSelected;
withVariant = applyVariantOnce parentSelected v;
in
applyPlatforms withVariant v system;
# Render ${var} substitutions in any string within attrs/lists.
renderValue =
value: vars:
if isString value then
let
keys = attrNames vars;
patterns = map (k: "\${" + k + "}") keys;
replacements = map (k: toString (getAttr k vars)) keys;
in
replaceStrings patterns replacements value
else if isAttrs value then
mapAttrs (_: v: renderValue v vars) value
else if isList value then
map (v: renderValue v vars) value
else
value;
# Decide fetcher for URL type based on optional extra.unpack hint.
useFetchZip = comp: comp ? extra && comp.extra ? unpack && comp.extra.unpack == "zip";
# Build a single src from a rendered component spec, using the given pkgs for fetchers.
mkSrcFromRendered' =
pkgs': comp:
let
fetcher = if comp ? fetcher then comp.fetcher else "none";
in
if fetcher == "github" then
pkgs'.fetchFromGitHub (
{
owner = comp.owner;
repo = comp.repo;
# Allow tag as rev (ignore null/empty tag)
rev = if comp ? tag && comp.tag != null && comp.tag != "" then comp.tag else comp.rev;
fetchSubmodules = if comp ? submodules then comp.submodules else false;
hash = comp.hash;
}
// lib.optionalAttrs (comp ? name) { name = comp.name; }
)
else if fetcher == "git" then
pkgs'.fetchgit {
url = comp.url;
rev = comp.rev;
fetchSubmodules = if comp ? submodules then comp.submodules else false;
hash = comp.hash;
}
else if fetcher == "url" then
let
url = if comp ? url then comp.url else comp.urlTemplate;
in
if useFetchZip comp then
pkgs'.fetchzip (
{
inherit url;
hash = comp.hash;
}
// lib.optionalAttrs (comp ? extra && comp.extra ? stripRoot) { stripRoot = comp.extra.stripRoot; }
)
else
pkgs'.fetchurl {
inherit url;
hash = comp.hash;
}
else if fetcher == "pypi" then
pkgs'.python3Packages.fetchPypi {
pname = comp.name;
version = comp.version;
hash = comp.hash;
}
else
# fetcher == "none": pass-through (e.g., linux version/hash consumed by custom logic)
comp;
# Build a single src from a rendered component spec using the default (lib-level) pkgs.
# Deprecated: prefer mkSrcFromRendered' with an explicit pkgs argument.
mkSrcFromRendered = mkSrcFromRendered' pkgs;
in
rec {
/*
Select a variant from a loaded version.json specification.
Usage:
let selected = versioning.selectVariant spec variantName system;
- spec: attrset from lib.importJSON ./version.json
- variantName: string or null (when null, uses spec.defaultVariant if present)
- system: string like "x86_64-linux" or null (to apply platforms overrides)
*/
selectVariant =
spec: variantName: system:
let
chosen =
if variantName != null then
variantName
else
(if spec ? defaultVariant then spec.defaultVariant else null);
baseSelected = {
variables = if spec ? variables then spec.variables else { };
sources = if spec ? sources then spec.sources else { };
};
in
resolveVariant spec baseSelected chosen system;
/*
Render ${var} template substitutions across any value using provided variables.
Strings, attrsets, and lists are traversed.
*/
render = value: variables: renderValue value variables;
/*
Render a component with variables and then build its src (or pass-through for fetcher "none").
Prefer using mkAllSources, which handles rendering for all components.
pkgs: the nixpkgs instance to use for fetchers (must match the target system).
*/
mkSrc =
pkgs': comp: variables:
let
rendered = renderValue comp variables;
in
mkSrcFromRendered' pkgs' rendered;
/*
Produce an attrset of all sources for a selected spec:
mkAllSources pkgs selected
Where:
pkgs: the nixpkgs instance to use for fetchers (must match the target system).
selected = selectVariant spec variantName system
Returns:
{ componentName = src | renderedComp (for "none"); ... }
*/
mkAllSources =
pkgs': selected:
mapAttrs (
_name: comp:
if comp ? fetcher && comp.fetcher == "none" then
renderValue comp selected.variables
else
mkSrc pkgs' (renderValue comp selected.variables) selected.variables
) selected.sources;
# Expose deepMerge for convenience (right-biased).
inherit deepMerge;
}

View File

@@ -35,9 +35,6 @@
# Pass inputs so external modules can access them
extraSpecialArgs = {
inherit inputs namespace;
overlays = with inputs; [
nix-vscode-extensions.overlays.default
];
};
# Make ALL external HM modules available globally
@@ -45,6 +42,7 @@
sops-nix.homeManagerModules.sops
nix-plist-manager.homeManagerModules.default
nix-index-database.homeModules.nix-index
stylix.homeModules.stylix
# Add any other external HM modules here
];

View File

@@ -0,0 +1,57 @@
{
lib,
...
}:
{
nix = {
settings = {
# extra-sandbox-paths = [ config.programs.ccache.cacheDir ];
substituters = [
"http://jallen-nas.local:9012/nas-cache"
"https://nixos-apple-silicon.cachix.org"
"https://nixos-raspberrypi.cachix.org"
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
trusted-public-keys = [
"nas-cache:eK0eRVAt9QNwbkLIyOo9N5Z5+zi6ukI4mSlL196C7Yg="
"nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20="
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
warn-dirty = lib.mkForce false;
experimental-features = lib.mkForce [
"nix-command"
"flakes"
];
trusted-users = [
"@wheel"
"@admin"
];
builders-use-substitutes = true;
connect-timeout = lib.mkDefault 5;
fallback = true;
log-lines = lib.mkDefault 25;
max-free = lib.mkDefault (3000 * 1024 * 1024);
min-free = lib.mkDefault (512 * 1024 * 1024);
};
# Garbage collect automatically every week
gc = {
automatic = lib.mkDefault true;
options = lib.mkDefault "--delete-older-than 30d";
};
optimise.automatic = lib.mkDefault true;
};
nixpkgs = {
config = {
# CUDA and ROCm are not applicable on Darwin; those hardware modules are
# NixOS-only. Unfree allowance is handled globally via channels-config in
# the flake.
allowUnsupportedSystem = true;
};
};
}

View File

@@ -2,10 +2,11 @@
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.mjallen.desktop.gnome;
cfg = config.${namespace}.desktop.gnome;
in
{
imports = [ ./options.nix ];
@@ -22,29 +23,38 @@ in
gnomeExtensions.bing-wallpaper-changer
gnomeExtensions.dash-to-dock
gnomeExtensions.dash-to-panel
gnomeExtensions.caffeine
gnomeExtensions.gsconnect
gnomeExtensions.nasa-apod
gnomeExtensions.random-wallpaper
gnomeExtensions.tiling-assistant
gnomeExtensions.user-themes
gnomeExtensions.wikiart-wallpaper
gnomeExtensions.boatman-winboat-monitor
papirus-icon-theme
pop-gtk-theme
pkgs.mjallen.gnome-nebula-vpn
];
dconf = {
enable = true;
settings = {
"org/gnome/desktop/interface".clock-format = "12h";
"org/gnome/desktop/interface".color-scheme = "prefer-dark";
"org/gnome/desktop/interface".cursor-theme = lib.mkDefault "macOS";
"org/gnome/desktop/interface".enable-hot-corners = false;
"org/gnome/desktop/interface".font-antialiasing = "grayscale";
"org/gnome/desktop/interface".font-hinting = "slight";
"org/gnome/desktop/interface".gtk-theme = lib.mkDefault "Colloid-Dark";
"org/gnome/desktop/interface".icon-theme = lib.mkDefault "Colloid-Dark";
"io/missioncenter/MissionCenter".performance-page-cpu-graph = 2; # logical processors
"org/gnome/desktop/interface" = {
clock-format = "12h";
color-scheme = "prefer-dark";
cursor-theme = lib.mkDefault "macOS";
enable-hot-corners = false;
font-antialiasing = "grayscale";
font-hinting = "slight";
gtk-theme = lib.mkDefault "Colloid-Dark";
icon-theme = lib.mkDefault "Colloid-Dark";
};
"org/gnome/desktop/peripherals/mouse".accel-profile = "flat";
"org/gnome/desktop/peripherals/touchpad".two-finger-scrolling-enabled = true;
"org/gnome/desktop/peripherals/touchpad".tap-to-click = true;
"org/gnome/desktop/peripherals/touchpad" = {
two-finger-scrolling-enabled = true;
tap-to-click = true;
};
"org/gnome/mutter".experimental-features = [
"scale-monitor-framebuffer"
"variable-refresh-rate"
@@ -53,20 +63,37 @@ in
"org/gnome/shell".enabled-extensions = [
"allowlockedremotedesktop@kamens.us"
"appindicatorsupport@rgcjonas.gmail.com"
"caffeine@patapon.info"
"user-theme@gnome-shell-extensions.gcampax.github.com"
"tiling-assistant@leleat-on-github"
"dash-to-dock@micxgx.gmail.com"
"BingWallpaper@ineffable-gmail.com"
"gsconnect@andyholmes.github.io"
"nebula-vpn-status@mjallen"
];
"org/gnome/shell/extensions/bingwallpaper".override-lockscreen-blur = true;
"org/gnome/shell/extensions/bingwallpaper".random-mode-enabled = true;
"org/gnome/shell/extensions/bingwallpaper".revert-to-current-image = false;
"org/gnome/shell/extensions/dash-to-panel".primary-monitor = 1;
"org/gnome/shell/extensions/dash-to-panel".multi-monitors = false;
"org/gnome/shell/extensions/gsconnect".id = "4db35bd2-0dcd-42a3-9f77-ef3e8bb83182";
"org/gnome/shell/extensions/gsconnect".name = "matt-nixos";
"org/gnome/shell/extensions/bingwallpaper" = {
override-lockscreen-blur = true;
random-mode-enabled = false;
selected-image = "current";
revert-to-current-image = false;
};
"org/gnome/shell/extensions/caffeine" = {
enable-fullscreen = true;
enable-mpris = true;
inhibit-apps = [
"horizon-client.desktop"
];
};
"org/gnome/shell/extensions/dash-to-panel" = {
primary-monitor = 1;
multi-monitors = false;
};
"org/gnome/shell/extensions/gsconnect" = {
id = "4db35bd2-0dcd-42a3-9f77-ef3e8bb83182";
name = "matt-nixos";
};
"org/gnome/shell/extensions/user-theme".name = lib.mkDefault "Colloid-Dark";
"org/gnome/system/location".enabled = true;
"org/gtk/settings/file-chooser".clock-format = "12h";
};
};

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.mjallen.desktop.gnome = {
options.${namespace}.desktop.gnome = {
enable = mkEnableOption "enable gnome settings";
};
}

View File

@@ -1,191 +0,0 @@
{ config, lib, ... }:
with lib;
let
cfg = config.mjallen.theme;
mkPalettePath = name: lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/${name}.nix";
cap =
s:
let
len = builtins.stringLength s;
in
(lib.toUpper (builtins.substring 0 1 s)) + (builtins.substring 1 (len - 1) s);
in
{
options.mjallen.theme = {
name = mkOption {
type = types.enum [
"nord"
"dracula"
"everforest"
];
default = "nord";
description = "Global theme palette name.";
};
# This is the palette file other modules should import.
# It exports a normalized schema with colors, tokens, and compat maps.
paletteFile = mkOption {
type = types.path;
default = mkPalettePath "nord";
description = "Path to a palette nix file exporting a normalized schema (and compat maps).";
};
# Exposed tokens (derived from paletteFile)
tokens = mkOption {
type = types.attrs;
default = { };
description = "Derived tokens from the selected palette (set automatically).";
};
# Expose the imported palette (actual colors + compat groups)
palette = mkOption {
type = types.attrs;
default = { };
description = "Imported palette attrset from the selected paletteFile (set automatically).";
};
gtk = {
enable = mkOption {
type = types.bool;
default = true;
description = "Manage GTK theme using global theme settings.";
};
# e.g. Colloid-Dark-Compact-*
color = mkOption {
type = types.enum [
"dark"
"light"
];
default = "dark";
description = "GTK color variant.";
};
size = mkOption {
type = types.enum [
"standard"
"compact"
];
default = "compact";
description = "GTK size variant.";
};
accent = mkOption {
type = types.enum [
"default"
"purple"
"pink"
"red"
"orange"
"yellow"
"green"
"teal"
"grey"
"all"
];
default = "all";
description = "GTK accent (Colloid themeVariants).";
};
tweak = mkOption {
type = types.enum [
"normal"
"rimless"
"float"
"black"
];
default = "normal";
description = "GTK tweak (Colloid tweaks).";
};
themeName = mkOption {
type = types.nullOr types.str;
default = null;
description = "Explicit GTK theme name override. If null, computed from color/size/accent.";
};
};
icons = {
# Colloid icon scheme usually supports several named schemes. Default follows palette name.
scheme = mkOption {
type = types.enum [
"default"
"nord"
"dracula"
"gruvbox"
"everforest"
"catppuccin"
];
default = cfg.name;
description = "Icon scheme to use (Colloid schemeVariants).";
};
variant = mkOption {
type = types.enum [
"default"
"purple"
"pink"
"red"
"orange"
"yellow"
"green"
"teal"
"grey"
"all"
];
default = "all";
description = "Icon variant (Colloid colorVariants).";
};
themeName = mkOption {
type = types.nullOr types.str;
default = null;
description = "Explicit icon theme name override. If null, computed from scheme/variant.";
};
};
};
# Wire derived defaults that depend on other options.
config = {
# Keep paletteFile following the chosen name unless user overrides it explicitly.
mjallen.theme.paletteFile = mkDefault (mkPalettePath cfg.name);
# Pull tokens directly from the palette file for convenience
mjallen.theme.tokens = mkDefault (
let
pal = import cfg.paletteFile;
in
pal.tokens or { }
);
# Expose the imported palette for convenience
mjallen.theme.palette = mkDefault (import cfg.paletteFile);
# Default per-program palette path (can still be overridden per program)
mjallen.programs.waybar.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.kitty.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.mako.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.wofi.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.btop.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.nwg-dock.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.nwg-drawer.theme.file = mkDefault cfg.paletteFile;
mjallen.programs.wlogout.theme.file = mkDefault cfg.paletteFile;
# Computed GTK/Icon theme names if not overridden
_module.args.mjallenThemeComputed = {
gtkTheme =
if cfg.gtk.themeName != null then
cfg.gtk.themeName
else
"Colloid-${cap cfg.gtk.color}-${cap cfg.gtk.size}";
iconTheme =
if cfg.icons.themeName != null then
cfg.icons.themeName
else
"Colloid-${cap cfg.icons.scheme}-${cap cfg.gtk.color}";
};
};
}

View File

@@ -1,62 +0,0 @@
{
# Nord colors
# Opacity Hex alpha
# 100% FF
# 75% BF
# 50% 80
# 25% 40
# 10% 1A
# 0% 00
polarNight = {
nord0 = "#2e3440";
nord1 = "#3b4252";
nord2 = "#434c5e";
nord3 = "#4c566a";
};
snowStorm = {
nord4 = "#d8dee9";
nord5 = "#e5e9f0";
nord6 = "#eceff4";
};
frost = {
nord7 = "#8fbcbb";
nord8 = "#88c0d0";
nord9 = "#81a1c1";
nord10 = "#5e81ac";
};
aurora = {
nord11 = "#bf616a";
nord12 = "#d08770";
nord13 = "#ebcb8b";
nord14 = "#a3be8c";
nord15 = "#b48ead";
};
defaultOpacity = "opacity: 0.85;";
defaultBorderRadius = "border-radius: 1rem;";
defaultCenterOptions = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
'';
borderRight = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0rem 1rem 1rem 0rem;
margin-right: 0.5rem;
'';
borderLeft = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 1rem 0rem 0rem 1rem;
margin-left: 0.5rem;
'';
}

View File

@@ -1,90 +0,0 @@
rec {
# Normalized semantic colors (theme-agnostic) - Dracula
colors = {
bg = "#282a36";
bgAlt = "#343746";
surface = "#343746";
surfaceAlt = "#44475a";
border = "#44475a";
text = "#f8f8f2";
textMuted = "#e2e2dc";
primary = "#6272a4"; # dark blue
info = "#8be9fd"; # cyan
accent = "#bd93f9"; # purple
success = "#50fa7b"; # green
warning = "#f1fa8c"; # yellow
danger = "#ff5555"; # red
};
# Shared styling tokens for CSS consumers
tokens = {
opacity = "opacity: 0.90;";
borderRadius = "border-radius: 0.8rem;";
centerOptions = ''
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
'';
borderRight = ''
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0rem 0.8rem 0.8rem 0rem;
margin-right: 0.5rem;
'';
borderLeft = ''
padding-top: 0.25rem;
padding-bottom: 0.25rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0.8rem 0rem 0rem 0.8rem;
margin-left: 0.5rem;
'';
};
# Legacy token aliases for back-compat
defaultOpacity = tokens.opacity;
defaultBorderRadius = tokens.borderRadius;
defaultCenterOptions = tokens.centerOptions;
borderRight = tokens.borderRight;
borderLeft = tokens.borderLeft;
# Back-compat: Nord-shaped groups for existing modules (approximate mappings)
polarNight = {
nord0 = "#282a36";
nord1 = "#343746";
nord2 = "#3b3e4a";
nord3 = "#44475a";
};
snowStorm = {
nord4 = "#e2e2dc";
nord5 = "#f1f1ea";
nord6 = "#f8f8f2";
};
frost = {
nord7 = "#50fa7b"; # using green as one of the frost group
nord8 = "#8be9fd"; # cyan
nord9 = "#6272a4"; # blue
nord10 = "#bd93f9"; # purple (as highlight)
};
aurora = {
nord11 = "#ff5555"; # red
nord12 = "#ffb86c"; # orange
nord13 = "#f1fa8c"; # yellow
nord14 = "#50fa7b"; # green
nord15 = "#bd93f9"; # magenta/purple
};
}

View File

@@ -1,90 +0,0 @@
rec {
# Normalized semantic colors (Everforest - Dark)
colors = {
bg = "#2b3339";
bgAlt = "#323c41";
surface = "#323c41";
surfaceAlt = "#3a444a";
border = "#414b51";
text = "#d3c6aa";
textMuted = "#9da9a0";
primary = "#7fbbb3"; # blue/aqua
info = "#83c092"; # teal
accent = "#d699b6"; # magenta
success = "#a7c080"; # green
warning = "#dbbc7f"; # yellow
danger = "#e67e80"; # red
};
# Shared styling tokens for CSS consumers
tokens = {
opacity = "opacity: 0.85;";
borderRadius = "border-radius: 1rem;";
centerOptions = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
'';
borderRight = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0rem 1rem 1rem 0rem;
margin-right: 0.5rem;
'';
borderLeft = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 1rem 0rem 0rem 1rem;
margin-left: 0.5rem;
'';
};
# Legacy token aliases for back-compat
defaultOpacity = tokens.opacity;
defaultBorderRadius = tokens.borderRadius;
defaultCenterOptions = tokens.centerOptions;
borderRight = tokens.borderRight;
borderLeft = tokens.borderLeft;
# Back-compat: Nord-shaped groups for existing modules (approximate mappings)
polarNight = {
nord0 = "#2b3339";
nord1 = "#323c41";
nord2 = "#3a444a";
nord3 = "#414b51";
};
snowStorm = {
nord4 = "#c6d0b5";
nord5 = "#e0dcc7";
nord6 = "#d3c6aa";
};
frost = {
nord7 = "#a7c080"; # green
nord8 = "#83c092"; # teal
nord9 = "#7fbbb3"; # aqua/blue
nord10 = "#7fbbb3"; # reuse
};
aurora = {
nord11 = "#e67e80"; # red
nord12 = "#e69875"; # orange
nord13 = "#dbbc7f"; # yellow
nord14 = "#a7c080"; # green
nord15 = "#d699b6"; # magenta
};
}

View File

@@ -1,90 +0,0 @@
rec {
# Normalized semantic colors (theme-agnostic)
colors = {
bg = "#2e3440";
bgAlt = "#3b4252";
surface = "#3b4252";
surfaceAlt = "#434c5e";
border = "#4c566a";
text = "#eceff4";
textMuted = "#e5e9f0";
primary = "#5e81ac"; # blue
info = "#88c0d0"; # cyan/teal
accent = "#b48ead"; # purple
success = "#a3be8c"; # green
warning = "#ebcb8b"; # yellow
danger = "#bf616a"; # red
};
# Shared styling tokens for CSS consumers
tokens = {
opacity = "opacity: 0.85;";
borderRadius = "border-radius: 1rem;";
centerOptions = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
'';
borderRight = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0rem 1rem 1rem 0rem;
margin-right: 0.5rem;
'';
borderLeft = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 1rem 0rem 0rem 1rem;
margin-left: 0.5rem;
'';
};
# Legacy token aliases for back-compat
defaultOpacity = tokens.opacity;
defaultBorderRadius = tokens.borderRadius;
defaultCenterOptions = tokens.centerOptions;
borderRight = tokens.borderRight;
borderLeft = tokens.borderLeft;
# Back-compat: Nord-shaped groups for existing modules
polarNight = {
nord0 = "#2e3440";
nord1 = "#3b4252";
nord2 = "#434c5e";
nord3 = "#4c566a";
};
snowStorm = {
nord4 = "#d8dee9";
nord5 = "#e5e9f0";
nord6 = "#eceff4";
};
frost = {
nord7 = "#8fbcbb";
nord8 = "#88c0d0";
nord9 = "#81a1c1";
nord10 = "#5e81ac";
};
aurora = {
nord11 = "#bf616a";
nord12 = "#d08770";
nord13 = "#ebcb8b";
nord14 = "#a3be8c";
nord15 = "#b48ead";
};
}

View File

@@ -3,6 +3,7 @@
programs = {
gpg = {
enable = true;
scdaemonSettings = {
disable-ccid = true;
pcsc-shared = true;

View File

@@ -2,69 +2,96 @@
config,
lib,
pkgs,
namespace,
hasDestopEnvironment ? true,
system,
...
}:
let
inherit (lib.${namespace}) enabled;
isArm = ("aarch64-linux" == system) || ("aarch64-darwin" == system);
# Non-login / system accounts (root, nixos installer, etc.) should not get
# desktop packages, tmux, nh, kdeconnect, nextcloud-client, etc.
# Detect them by username so individual host home files are not needed.
isSystemUser = lib.elem config.home.username [
"root"
"nixos"
];
in
{
home = {
enableNixpkgsReleaseCheck = lib.mkDefault false;
homeDirectory = lib.mkDefault "/home/${config.home.username}";
homeDirectory = lib.mkDefault (
if config.home.username == "root" then "/root" else "/home/${config.home.username}"
);
packages =
with pkgs;
[
age
clinfo
cpufetch
deadnix
lm_sensors
nano
nixfmt-rfc-style
pciutils
protonup-ng
rsync
smartmontools
sops
tailscale
tree
usbutils
vim
vulkan-tools
wget
]
++ (
if hasDestopEnvironment then
[
chromium
firefox
gamescope
gamescope-wsi
gparted
goverlay
mission-center
vesktop
]
else
(
if isSystemUser then
[ ]
else
[
age
clinfo
cpufetch
dbus
deadnix
lm_sensors
nano
nebula
nix-prefetch-scripts
nixfmt
pciutils
protonup-ng
rsync
smartmontools
sops
tailscale
tree
usbutils
vim
vulkan-tools
wget
]
++ (
if (hasDestopEnvironment) then
[
boxbuddy
stable.chromium
firefox
gamescope
gamescope-wsi
gparted
mission-center
parted
vesktop
]
else
[ ]
)
);
stateVersion = lib.mkDefault "23.11";
};
programs = {
nix-index-database.comma.enable = true;
btop.enable = lib.mkDefault true;
fastfetch.enable = lib.mkDefault true;
home-manager.enable = lib.mkDefault true;
java = {
enable = lib.mkDefault true;
# nix-index-database is not available in all home configs (e.g. iso-minimal
# standalone homes don't load the nix-index-database HM module).
# Set it per-host in homes that explicitly load the module.
btop = {
enable = lib.mkDefault (!isSystemUser);
package = pkgs.btop;
};
mangohud.enable = lib.mkDefault hasDestopEnvironment;
password-store.enable = true;
fastfetch.enable = lib.mkDefault (!isSystemUser);
home-manager = lib.mkDefault enabled;
java.enable = lib.mkDefault (!isSystemUser);
mangohud.enable = lib.mkDefault (hasDestopEnvironment && !isSystemUser);
password-store.enable = lib.mkDefault (!isSystemUser);
nh = {
enable = true;
enable = lib.mkDefault (!isSystemUser);
flake = "/etc/nixos";
clean = {
enable = true;
enable = lib.mkDefault (!isSystemUser);
extraArgs = "--keep 5";
};
};
@@ -81,7 +108,7 @@
};
tmux = {
enable = lib.mkDefault true;
enable = lib.mkDefault (!isSystemUser);
terminal = "screen-256color";
sensibleOnTop = true;
focusEvents = true;
@@ -103,7 +130,7 @@
tmuxPlugins.better-mouse-mode
];
extraConfig = ''
set -g status-right '#[fg=black,bg=color15] #{cpu_percentage} %H:%M '
set -g status-right '#[fg=black,bg=color15] #{cpu_percentage} %H:%M '
run-shell ${pkgs.tmuxPlugins.cpu}/share/tmux-plugins/cpu/cpu.tmux
set -g default-terminal "xterm-256color"
set -ga terminal-overrides ",*256col*:Tc"
@@ -121,11 +148,14 @@
};
services = {
nextcloud-client.enable = lib.mkDefault hasDestopEnvironment;
pass-secret-service.enable = lib.mkDefault true;
# nextcloud-client is disabled by default for all users; systems that
# want it enabled must opt in explicitly in their home configuration.
nextcloud-client.enable = lib.mkDefault false;
pass-secret-service.enable = lib.mkDefault (!isSystemUser);
kdeconnect = {
enable = lib.mkDefault hasDestopEnvironment;
indicator = lib.mkDefault true;
enable = lib.mkDefault (hasDestopEnvironment && !isSystemUser);
indicator = lib.mkDefault (hasDestopEnvironment && !isSystemUser);
package = pkgs.kdePackages.kdeconnect-kde;
};
};
}

View File

@@ -1,126 +1,84 @@
{ config, lib, ... }:
with lib;
let
cfg = config.mjallen.programs.btop;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
config = mkIf cfg.enable {
programs.btop = {
enable = true;
settings = {
color_theme = "global";
theme_background = true;
truecolor = true;
force_tty = false;
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty";
vim_keys = true;
rounded_corners = true;
graph_symbol = "braille";
graph_symbol_cpu = "default";
graph_symbol_mem = "default";
graph_symbol_net = "default";
graph_symbol_proc = "default";
shown_boxes = "cpu mem net proc";
update_ms = 2000;
proc_sorting = "cpu lazy";
proc_reversed = false;
proc_tree = false;
proc_colors = true;
proc_gradient = true;
proc_per_core = false;
proc_mem_bytes = true;
proc_cpu_graphs = true;
proc_info_smaps = false;
proc_left = false;
proc_filter_kernel = false;
cpu_graph_upper = "total";
cpu_graph_lower = "total";
cpu_invert_lower = true;
cpu_single_graph = false;
cpu_bottom = false;
show_uptime = true;
check_temp = true;
cpu_sensor = "Auto";
show_coretemp = true;
cpu_core_map = "";
temp_scale = "celsius";
base_10_sizes = false;
show_cpu_freq = true;
clock_format = "%X";
background_update = true;
custom_cpu_name = "";
disks_filter = "";
mem_graphs = true;
mem_below_net = false;
zfs_arc_cached = true;
show_swap = true;
swap_disk = true;
show_disks = true;
only_physical = true;
use_fstab = true;
zfs_hide_datasets = false;
disk_free_priv = false;
show_io_stat = true;
io_mode = false;
io_graph_combined = false;
io_graph_speeds = "";
net_download = 100;
net_upload = 100;
net_auto = true;
net_sync = true;
net_iface = "";
show_battery = true;
selected_battery = "Auto";
log_level = "WARNING";
config,
lib,
namespace,
...
}:
{
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "btop";
moduleConfig = {
programs.btop = {
enable = true;
settings = {
truecolor = true;
force_tty = false;
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty";
vim_keys = true;
rounded_corners = true;
graph_symbol = "braille";
graph_symbol_cpu = "default";
graph_symbol_mem = "default";
graph_symbol_net = "default";
graph_symbol_proc = "default";
shown_boxes = "cpu mem net proc";
update_ms = 2000;
proc_sorting = "cpu lazy";
proc_reversed = false;
proc_tree = false;
proc_colors = true;
proc_gradient = true;
proc_per_core = false;
proc_mem_bytes = true;
proc_cpu_graphs = true;
proc_info_smaps = false;
proc_left = false;
proc_filter_kernel = false;
cpu_graph_upper = "total";
cpu_graph_lower = "total";
cpu_invert_lower = true;
cpu_single_graph = false;
cpu_bottom = false;
show_uptime = true;
check_temp = true;
cpu_sensor = "Auto";
show_coretemp = true;
cpu_core_map = "";
temp_scale = "celsius";
base_10_sizes = false;
show_cpu_freq = true;
clock_format = "%X";
background_update = true;
custom_cpu_name = "";
disks_filter = "";
mem_graphs = true;
mem_below_net = false;
zfs_arc_cached = true;
show_swap = true;
swap_disk = true;
show_disks = true;
only_physical = true;
use_fstab = true;
zfs_hide_datasets = false;
disk_free_priv = false;
show_io_stat = true;
io_mode = false;
io_graph_combined = false;
io_graph_speeds = "";
net_download = 100;
net_upload = 100;
net_auto = true;
net_sync = true;
net_iface = "";
show_battery = true;
selected_battery = "Auto";
log_level = "WARNING";
};
};
};
themes = {
global = ''
theme[main_bg]="${palette.colors.bg}"
theme[main_fg]="${palette.colors.text}"
theme[title]="${palette.colors.text}"
theme[hi_fg]="${palette.colors.info}"
theme[selected_bg]="${palette.colors.bgAlt}"
theme[selected_fg]="${palette.colors.info}"
theme[inactive_fg]="${palette.colors.surfaceAlt}"
theme[graph_text]="${palette.colors.text}"
theme[meter_bg]="${palette.colors.bgAlt}"
theme[proc_misc]="${palette.colors.text}"
theme[cpu_box]="${palette.colors.accent}"
theme[mem_box]="${palette.colors.success}"
theme[net_box]="${palette.colors.warning}"
theme[proc_box]="${palette.colors.danger}"
theme[div_line]="${palette.colors.bgAlt}"
theme[temp_start]="${palette.colors.success}"
theme[temp_mid]="${palette.colors.warning}"
theme[temp_end]="${palette.colors.danger}"
theme[cpu_start]="${palette.colors.accent}"
theme[cpu_mid]="${palette.colors.warning}"
theme[cpu_end]="${palette.colors.danger}"
theme[free_start]="${palette.colors.success}"
theme[free_mid]="${palette.colors.warning}"
theme[free_end]="${palette.colors.warning}"
theme[cached_start]="${palette.colors.success}"
theme[cached_mid]="${palette.colors.warning}"
theme[cached_end]="${palette.colors.warning}"
theme[available_start]="${palette.colors.text}"
theme[available_mid]="${palette.colors.danger}"
theme[available_end]="${palette.colors.danger}"
theme[used_start]="${palette.colors.success}"
theme[used_mid]="${palette.colors.warning}"
theme[used_end]="${palette.colors.danger}"
theme[download_start]="${palette.colors.info}"
theme[download_mid]="${palette.colors.info}"
theme[download_end]="${palette.colors.warning}"
theme[upload_start]="${palette.colors.info}"
theme[upload_mid]="${palette.colors.info}"
theme[upload_end]="${palette.colors.warning}"
theme[process_start]="${palette.colors.accent}"
theme[process_mid]="${palette.colors.warning}"
theme[process_end]="${palette.colors.danger}"
'';
};
};
};
})
];
}

View File

@@ -1,21 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.btop = {
enable = mkEnableOption "enable btop";
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "btop theme palette configuration.";
};
};
}

View File

@@ -12,7 +12,6 @@ let
x86_only = with pkgs; [
vscode-extensions.redhat.vscode-xml
];
open-remote-ssh = pkgs.${namespace}.open-remote-ssh;
in
{
home.packages = with pkgs; [
@@ -46,38 +45,37 @@ in
vscode-extensions.redhat.vscode-yaml
vscode-extensions.yy0931.vscode-sqlite3-editor
# open-remote-ssh
# nix-vscode-extensions.open-vsx.jeanp413.open-remote-ssh
open-remote-ssh
nix-vscode-extensions.open-vsx.jeanp413.open-remote-ssh
]
++ (if !isArm then x86_only else [ ])
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
{
name = "copilot-mcp";
publisher = "automatalabs";
version = "0.0.49";
sha256 = "sha256-+G2OQl5SCN7bh7MzGdYiRclIZefBE7lWnGg1kNpCvnA=";
}
{
name = "mcp-server-runner";
publisher = "zebradev";
version = "0.1.0";
sha256 = "sha256-StydVt3VzQUSS/pYp76jnIwtZlEj8gWAGzOARs93J+E=";
}
{
name = "claude-dev";
publisher = "saoudrizwan";
version = "3.17.9";
sha256 = "sha256-y3bFtMe5vZrO3DFb31KDvkzjD2jM76wK89mKhgJXC70=";
}
];
++ (if !isArm then x86_only else [ ]);
# ++ (if !isDarwin then [ open-remote-ssh ] else [ ]);
# ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
# {
# name = "copilot-mcp";
# publisher = "automatalabs";
# version = "0.0.49";
# sha256 = "sha256-+G2OQl5SCN7bh7MzGdYiRclIZefBE7lWnGg1kNpCvnA=";
# }
# {
# name = "mcp-server-runner";
# publisher = "zebradev";
# version = "0.1.0";
# sha256 = "sha256-StydVt3VzQUSS/pYp76jnIwtZlEj8gWAGzOARs93J+E=";
# }
# {
# name = "claude-dev";
# publisher = "saoudrizwan";
# version = "3.17.9";
# sha256 = "sha256-y3bFtMe5vZrO3DFb31KDvkzjD2jM76wK89mKhgJXC70=";
# }
# ];
userSettings = {
"database-client.autoSync" = true;
"editor" = {
"defaultFormatter" = "brettm12345.nixfmt-vscode";
"fontFamily" = "fira-code-nerd, FiraCode Nerd Font, Consolas, 'Courier New', monospace";
"fontLigatures" = true;
"renderWhitespace" = "all";
};
@@ -86,6 +84,10 @@ in
"confirmDragAndDrop" = false;
};
"extensions." = {
"autoCheckUpdates" = false;
};
"git" = {
"confirmSync" = false;
"enableSmartCommit" = true;
@@ -141,6 +143,8 @@ in
"security.workspace.trust.untrustedFiles" = "open";
"update.mode" = "none";
"workbench" = {
"colorCustomizations" = null;
"editorAssociations" = {
@@ -154,8 +158,8 @@ in
};
};
direnv = {
enable = false;
nix-direnv.enable = false;
enable = true;
nix-direnv.enable = true;
enableZshIntegration = true;
};
};

View File

@@ -0,0 +1,46 @@
# Shared defaultApps submodule options, used by both the hyprland and
# nwg-panel modules. Import this file and pass the result as the `options`
# argument to types.submodule to avoid duplicating the definition.
{ pkgs, lib, ... }:
{
browser = lib.mkOption {
type = lib.types.package;
default = pkgs.firefox;
description = "Default web browser";
};
editor = lib.mkOption {
type = lib.types.package;
default = pkgs.micro;
description = "Default text editor";
};
fileExplorer = lib.mkOption {
type = lib.types.package;
default = pkgs.nautilus;
description = "Default file explorer";
};
visual = lib.mkOption {
type = lib.types.package;
default = pkgs.vscodium;
description = "Default visual/IDE editor";
};
terminal = lib.mkOption {
type = lib.types.package;
default = pkgs.kitty;
description = "Default terminal emulator";
};
office = lib.mkOption {
type = lib.types.package;
default = pkgs.onlyoffice-desktopeditors;
description = "Default office suite";
};
video = lib.mkOption {
type = lib.types.package;
default = pkgs.vlc;
description = "Default video player";
};
imageViewer = lib.mkOption {
type = lib.types.package;
default = pkgs.nomacs;
description = "Default image viewer";
};
}

View File

@@ -0,0 +1,25 @@
{
config,
namespace,
lib,
...
}:
let
cfg = config.${namespace}.programs.hyprland;
in
{
config = lib.mkIf cfg.enable {
services.avizo = {
enable = true;
settings = {
default = {
time = 1.0;
y-offset = 0.5;
fade-in = 0.1;
fade-out = 0.2;
padding = 10;
};
};
};
};
}

View File

@@ -2,17 +2,18 @@
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.mjallen.programs.hyprland;
drawer = "nwg-drawer -fm nautilus -term kitty -mb 10 -mt 10 -ml 10 -mr 10 -pbuseicontheme -i ${config.gtk.iconTheme.name}";
cfg = config.${namespace}.programs.hyprland;
drawer = "nwg-drawer -fm nautilus -term kitty -mb 10 -mt 10 -ml 10 -mr 10 -pbuseicontheme -i ${config.stylix.icons.dark}";
in
{
imports = [
./avizo.nix
./options.nix
./theme.nix
];
config = mkIf cfg.enable {
@@ -63,14 +64,13 @@ in
wl-clipboard
wlogout
wlroots
xorg.xhost
xhost
xsettingsd
xwayland
pkgs.mjallen.pipewire-python
]
++ (if cfg.notificationDaemon == "mako" then [ mako ] else [ dunst ])
++ (if cfg.launcher == "wofi" then [ wofi ] else [ rofi ])
++ (with pkgs.${namespace}; [ pipewire-python ])
);
# Session variables
@@ -83,20 +83,20 @@ in
GTK_CSD = "0";
GTK_THEME = config.gtk.theme.name;
GTK_USE_PORTAL = "1";
HYPRCURSOR_THEME = config.home.pointerCursor.name;
HYPRCURSOR_SIZE = config.home.pointerCursor.size;
HYPRCURSOR_THEME = config.stylix.cursor.name;
HYPRCURSOR_SIZE = config.stylix.cursor.size;
MOZ_ENABLE_WAYLAND = "1";
NIXOS_OZONE_WL = "1";
NIXOS_XDG_OPEN_USE_PORTAL = "1";
QT_AUTO_SCREEN_SCALE_FACTOR = "1";
QT_QPA_PLATFORM = "wayland-egl";
QT_QPA_PLATFORMTHEME = "gtk3";
QT_QPA_PLATFORMTHEME = lib.mkDefault "gtk3";
QT_SCALE_FACTOR = "1";
QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
SDL_VIDEODRIVER = "wayland";
TERMINAL = "${cfg.defaultApps.terminal.pname}";
XCURSOR_THEME = config.home.pointerCursor.name;
XCURSOR_SIZE = config.home.pointerCursor.size;
XCURSOR_THEME = config.stylix.cursor.name;
XCURSOR_SIZE = config.stylix.cursor.size;
XDG_CACHE_HOME = "\${HOME}/.cache";
XDG_CONFIG_HOME = "\${HOME}/.config";
XDG_CURRENT_DESKTOP = "Hyprland";
@@ -112,22 +112,12 @@ in
enable = true;
settings = {
preload = [ cfg.hyprpaper.wallpaperPath ];
wallpaper =
let
useMonitorV2 = (lib.versionAtLeast pkgs.hyprland.version "0.40.0") && (cfg.monitorv2 != [ ]);
names =
if useMonitorV2 then
map (m: m.name) cfg.monitorv2
else
[
cfg.display1.input
cfg.display2.input
];
in
if cfg.hyprpaper.usePerMonitor then
map (n: "${n}, ${cfg.hyprpaper.wallpaperPath}") names
else
[ ", ${cfg.hyprpaper.wallpaperPath}" ];
wallpaper = [
{
monitor = "";
path = cfg.hyprpaper.wallpaperPath;
}
];
splash = false;
};
};
@@ -172,18 +162,18 @@ in
hyprlock = {
enable = true;
settings = {
background = [
background = mkForce [
{
monitor = "";
path = "/run/wallpaper.jpg"; # supports png, jpg, webp (no animations, though)
color = "rgba(25, 20, 20, 1.0)";
color = mkDefault "rgba(25, 20, 20, 1.0";
# all these options are taken from hyprland, see https://wiki.hyprland.org/Configuring/Variables/#blur for explanations
blur_passes = "3"; # 0 disables blurring
blur_size = "7";
blur_passes = mkDefault "3"; # 0 disables blurring
blur_size = mkDefault "7";
noise = "0.0117";
contrast = "0.8916";
brightness = "0.8172";
brightness = mkDefault "0.8172";
vibrancy = "0.1696";
vibrancy_darkness = "0.0";
}
@@ -193,9 +183,9 @@ in
{
monitor = cfg.primaryDisplay;
text = "cmd[update:1000] echo -e \"$(LC_TIME=en_US.UTF-8 date +\"%A, %B %d\")\"";
color = "#eceff4";
color = config.lib.stylix.colors.base06;
font_size = "25";
font_family = "JetBrainsMono NFM";
font_family = lib.mkDefault config.stylix.fonts.monospace.name;
position = "0, 350";
halign = "center";
valign = "center";
@@ -204,9 +194,9 @@ in
{
monitor = cfg.primaryDisplay;
text = "cmd[update:1000] echo \"<span>$(date +\"%I:%M\")</span>\"";
color = "#eceff4";
color = config.lib.stylix.colors.base06;
font_size = "120";
font_family = "JetBrainsMono NFM Bold";
font_family = lib.mkDefault "${config.stylix.fonts.monospace.name} Bold";
position = "0, 230";
halign = "center";
valign = "center";
@@ -214,35 +204,35 @@ in
{
monitor = cfg.primaryDisplay;
text = "$USER";
color = "#eceff4";
color = config.lib.stylix.colors.base06;
outline_thickness = 2;
dots_size = 0.2;
dots_spacing = 0.2;
dots_center = true;
font_size = 18;
font_family = "JetBrainsMono NFM Bold";
font_family = lib.mkDefault "${config.stylix.fonts.monospace.name} Bold";
position = "0, 0";
halign = "center";
valign = "center";
}
# weather
# weather
{
monitor = cfg.primaryDisplay;
text = "cmd[update:30000] waybar-weather --hyprlock";
color = "#eceff4";
color = config.lib.stylix.colors.base06;
font_size = "25";
font_family = "JetBrainsMono NFM";
font_family = lib.mkDefault config.stylix.fonts.monospace.name;
position = "-100, 100";
halign = "right";
valign = "bottom";
}
# media
# media
{
monitor = cfg.primaryDisplay;
text = "cmd[update:1000] waybar-media";
color = "#eceff4";
color = config.lib.stylix.colors.base06;
font_size = "15";
font_family = "JetBrainsMono NFM";
font_family = lib.mkDefault config.stylix.fonts.monospace.name;
position = "100, 100";
halign = "left";
valign = "bottom";
@@ -251,9 +241,9 @@ in
# user box
shape = [
{
monitor = "";
monitor = cfg.primaryDisplay;
size = "200, 50";
color = "rgba(46, 52, 64, .25)";
color = "rgba(46, 52, 64, .25";
rounding = -1;
border_size = "0";
position = "0, 0";
@@ -265,16 +255,16 @@ in
{
size = "200, 50";
position = "0, -80";
font_family = "JetBrainsMono NFM";
font_family = lib.mkDefault config.stylix.fonts.monospace.name;
monitor = cfg.primaryDisplay;
dots_center = true;
fade_on_empty = true;
font_color = "#eceff4";
inner_color = "#4c566a";
outer_color = "#2e3440";
font_color = config.lib.stylix.colors.base06;
inner_color = config.lib.stylix.colors.base03;
outer_color = config.lib.stylix.colors.base00;
bothlock_color = -1;
outline_thickness = 5;
placeholder_text = ''<span foreground="##2e3440">Password...</span>'';
placeholder_text = ''<span foreground="#${config.lib.stylix.colors.base00}">Password...</span>'';
shadow_passes = 2;
}
];
@@ -420,23 +410,20 @@ in
++ cfg.keybinds.bindm;
bindel = [
", XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+"
", XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
", XF86AudioRaiseVolume, exec, volumectl -u up"
", XF86AudioLowerVolume, exec, volumectl -u down"
]
++ cfg.keybinds.bindel;
bindl = [
", XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
", XF86AudioMute, exec, volumectl toggle-mute"
", XF86AudioPlay, exec, playerctl play-pause"
", XF86AudioPrev, exec, playerctl previous"
", XF86AudioNext, exec, playerctl next"
", XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
", XF86AudioMicMute, exec, volumectl -m toggle-mute"
", XF86MonBrightnessUp, exec, brightnessctl set +5%"
", XF86MonBrightnessDown, exec, brightnessctl set 5%-"
"$mod, XF86MonBrightnessUp, exec, brightnessctl -d kbd_backlight set +10%"
"$mod, XF86MonBrightnessDown, exec, brightnessctl -d kbd_backlight set 10%-"
", XF86MonBrightnessUp, exec, lightctl up"
", XF86MonBrightnessDown, exec, lightctl down"
]
++ cfg.keybinds.bindl;
@@ -486,8 +473,8 @@ in
gaps_in = 5;
gaps_out = 10;
border_size = 1;
"col.active_border" = "rgb(8aadf4) rgb(24273A) rgb(24273A) rgb(8aadf4) 45deg";
"col.inactive_border" = "rgb(24273A) rgb(24273A) rgb(24273A) rgb(24273A) 45deg";
# "col.active_border" = "rgb(8aadf4) rgb(24273A) rgb(24273A) rgb(8aadf4) 45deg";
# "col.inactive_border" = "rgb(24273A) rgb(24273A) rgb(24273A) rgb(24273A) 45deg";
layout = "dwindle";
allow_tearing = cfg.allowTearing;
};
@@ -529,80 +516,78 @@ in
workspace = cfg.workspace;
windowrule = [
"float, title:(file_progress)"
"float, title:(.*[Cc]onfirm.*)"
"float, title:(.*[Dd]ialog.*)"
"float, title:(.*[Dd]ownload.*)"
"float, title:(.*[Nn]otification.*)"
"float, title:(.*[Ee]rror.*)"
"float, title:(.*[Ss]plash.*)"
"float, title:(.*[Cc]onfirmreset.*)"
"float, title:(.*[Ss]ign [Ii]n - .*)"
"float, title:(.*[Oo]pen [Ff]ile.*)"
"float, title:(.*branchdialog.*)"
"float, class:(.*pavucontrol.*)"
"move onscreen cursor 0% 0%, class:(.*pavucontrol.*)"
"float, class:(.*[Oo]verskride.*)"
"float, class:(.*FileRoller.*)"
"float, class:(.*wlogout.*)"
"idleinhibit stayfocused, title:(.*mpv.*)"
"match:title file_progress, float 1"
"match:title .*[Cc]onfirm.*, float 1"
"match:title .*[Dd]ialog.*, float 1"
"match:title .*[Dd]ownload.*, float 1"
"match:title .*[Nn]otification.*, float 1"
"match:title .*[Ee]rror.*, float 1"
"match:title .*[Ss]plash.*, float 1"
"match:title .*[Cc]onfirmreset.*, float 1"
"match:title .*[Ss]ign [Ii]n - .*, float 1"
"match:title .*[Oo]pen [Ff]ile.*, float 1"
"match:title .*branchdialog.*, float 1"
"match:class .*pavucontrol.*, float 1"
"match:class .*pavucontrol.*, move onscreen cursor 0% 0%"
"match:class .*[Oo]verskride.*, float 1"
"match:class .*FileRoller.*, float 1"
"match:class .*wlogout.*, float 1"
"match:title .*mpv.*, idle_inhibit stayfocused"
"float, class:(.*nm-connection-editor.*)"
"move onscreen cursor 0% 0%, class:(.*nm-connection-editor.*)"
"match:class .*nm-connection-editor.*, float 1"
"match:class .*nm-connection-editor.*, move onscreen cursor 0% 0%"
"float, title:(Media viewer)"
"float, class:(it.mijorus.smile),title:(Smile)"
"float, class:(.blueman-manager-wrapped)$,title:(Bluetooth Devices)"
"match:title Media viewer, float 1"
"match:class it.mijorus.smile),match:title Smile, float 1"
"match:class .blueman-manager-wrapped)$,match:title Bluetooth Devices, float 1"
# Picture in picture windows
"float, title:(.*Picture-in-Picture.*)"
"pin, title::(.*Picture-in-Picture.*)"
"match:title .*Picture-in-Picture.*, float 1"
"match:title .*Picture-in-Picture.*, pin 1"
# discord/vesktop
# "workspace: name:discord, class:(.*vesktop)"
# "float, class:(.*vesktop),title:(.*Discord Popout.*)"
# "pin, class:(.*vesktop),title:(.*Discord Popout.*)"
# "workspace: name:discord, match:class .*vesktop"
# "match:class .*vesktop),match:title .*Discord Popout.*, float 1"
# "pin, match:class .*vesktop),match:title .*Discord Popout.*"
# Music
# "workspace: name:discord, class:(Apple Music.*)"
# "workspace: name:discord, match:class Apple Music.*"
# Steam
"float, class:(.*[Ss]team), title:(.*[Ss]team.*)$"
"workspace name:steam silent, class:(.*[Ss]team), title:(.*[Ss]team.*)$"
"tile, class:(.*[Ss]team), title:(.*[Ss]team.*)$"
"float, class:(.*steam),title:(.*Friends List.*)"
"match:class .*[Ss]team, match:title .*[Ss]team.*, float 1"
"match:class .*[Ss]team, match:title .*[Ss]team.*, workspace name:steam silent"
"match:class .*[Ss]team, match:title .*[Ss]team.*, tile 1"
"match:class .*steam,match:title .*Friends List.*, float 1"
# Code
"pin, class:(.*codium.*),title:(Save As)"
"float, class:(.*codium.*),title:(Save As)"
"float, class:(xdg-desktop-portal-gtk),title:(Open Workspace from File)"
"match:class .*codium.*, match:title Save As, pin 1"
"match:class .*codium.*, match:title Save As, float 1"
"match:class xdg-desktop-portal-gtk, match:title Open Workspace from File, float 1"
# Game Tearing??? https://wiki.hypr.land/Configuring/Tearing/
"immediate, class:(.*gamescope)"
"match:class .*gamescope.*, idle_inhibit fullscreen, content game, immediate 1"
"match:xdg_tag proton-game, idle_inhibit fullscreen, content game, immediate 1"
"match:class steam_app_.*, idle_inhibit fullscreen, content game, immediate 1"
# vmware
# this tag will set the below options to the vdi window
# this will have it auto open as a 2160x7680 window
# and makes multi-monitor work
"tag +horizonrdp, class:(.*[Hh]orizon-client),title:(USPS Next VDI)"
"match:class .*[Hh]orizon-client, match:title USPS Next VDI, tag +horizonrdp"
"noanim, tag:horizonrdp"
"noblur, tag:horizonrdp"
"norounding, tag:horizonrdp"
"noshadow, tag:horizonrdp"
"immediate, tag:horizonrdp"
"allowsinput, tag:horizonrdp"
"noborder, tag:horizonrdp"
"nodim, tag:horizonrdp"
"nomaxsize, tag:horizonrdp"
"renderunfocused, tag:horizonrdp"
"idleinhibit, tag:horizonrdp"
"float, tag:horizonrdp"
"match:tag horizonrdp, no_anim 1"
"match:tag horizonrdp, no_blur 1"
"match:tag horizonrdp, rounding 0"
"match:tag horizonrdp, no_shadow 1"
"match:tag horizonrdp, immediate 1"
"match:tag horizonrdp, allows_input 1"
"match:tag horizonrdp, border_size 0"
"match:tag horizonrdp, max_size 2160 7680"
"match:tag horizonrdp, min_size 1920 1080"
"match:tag horizonrdp, render_unfocused 1"
"match:tag horizonrdp, idle_inhibit 1"
"match:tag horizonrdp, float 1"
# float the vmware window cause its annoying to use in fullscreen
"float, class:(.*[Hh]orizon-client),title:([Oo]mnissa [Hh]orizon [Cc]lient)"
"tag +waydroid, class:([Ww]aydroid.*)"
"float, tag:waydroid"
"pin, tag:waydroid"
"match:class .*[Hh]orizon-client),match:title [Oo]mnissa [Hh]orizon [Cc]lient, float 1"
]
++ cfg.windowRule;
@@ -697,12 +682,12 @@ in
sensitivity = 0; # -1.0 - 1.0, 0 means no modification.
};
experimental = {
xx_color_management_v4 = true;
};
# experimental = {
# xx_color_management_v4 = true;
# };
debug = {
full_cm_proto = cfg.debug.fullCmProto;
# full_cm_proto = cfg.debug.fullCmProto;
disable_logs = cfg.debug.disableLogs;
disable_scale_checks = cfg.debug.disableScaleChecks;
};

View File

@@ -1,7 +1,11 @@
{ lib, pkgs, ... }:
{
lib,
namespace,
...
}:
with lib;
{
options.mjallen.programs.hyprland = {
options.${namespace}.programs.hyprland = {
enable = mkEnableOption "enable hyprland";
primaryDisplay = mkOption {
@@ -148,67 +152,12 @@ with lib;
extraConfig = mkOption {
type = with types; str;
default = '''';
default = "";
description = "Any extra configuration options";
};
iconThemeName = mkOption {
type = types.str;
default = "Colloid-Dark";
description = "Icon theme name";
};
gtkThemeName = mkOption {
type = types.str;
default = "Colloid-Dark";
description = "GTK theme name";
};
defaultApps = mkOption {
type = types.submodule {
options = {
browser = mkOption {
type = types.package;
default = pkgs.firefox;
description = "Default browser";
};
editor = mkOption {
type = types.package;
default = pkgs.micro;
description = "Default text editor";
};
fileExplorer = mkOption {
type = types.package;
default = pkgs.nautilus;
description = "Default file explorer";
};
visual = mkOption {
type = types.package;
default = pkgs.vscodium;
description = "Default visual editor";
};
terminal = mkOption {
type = types.package;
default = pkgs.kitty;
description = "Default terminal";
};
office = mkOption {
type = types.package;
default = pkgs.onlyoffice-desktopeditors;
description = "Default office suite";
};
video = mkOption {
type = types.package;
default = pkgs.vlc;
description = "Default video player";
};
imageViewer = mkOption {
type = types.package;
default = pkgs.nomacs;
description = "Default image viewer";
};
};
};
type = types.submodule (import ../common/default-apps.nix);
description = "Default applications used across the system";
};
@@ -216,7 +165,7 @@ with lib;
type = with types; listOf str;
default = [
"nwg-look -a"
"nwg-dock-hyprland -d"
"nwg-dock-hyprland -x"
];
description = "Commands to run via Hyprland exec-once";
};

View File

@@ -1,100 +0,0 @@
{
config,
lib,
pkgs,
mjallenThemeComputed,
...
}:
with lib;
let
cfg = config.mjallen.programs.hyprland;
# Pull from global theme options
themeSize = config.mjallen.theme.gtk.size; # "standard" | "compact"
themeAccent = config.mjallen.theme.gtk.accent; # "default" | ... | "all"
themeTweak = config.mjallen.theme.gtk.tweak; # "normal" | "rimless" | "float" | "black"
themeColor = config.mjallen.theme.gtk.color; # "light" | "dark"
iconThemeVariant = config.mjallen.theme.icons.variant; # "default" | ... | "all"
iconScheme = config.mjallen.theme.icons.scheme; # "default" | "nord" | "dracula" | ...
# Cursor
cursorTheme = "macOS";
cursorThemePkg = pkgs.apple-cursor;
cursorSize = 24;
# GTK
gtkTheme = mjallenThemeComputed.gtkTheme;
gtkThemePkg = pkgs.colloid-gtk-theme.override {
sizeVariants = [ themeSize ];
colorVariants = [ themeColor ];
themeVariants = [ themeAccent ];
tweaks = [ themeTweak ];
};
# Icons
iconTheme = mjallenThemeComputed.iconTheme;
iconThemePkg = pkgs.colloid-icon-theme.override {
schemeVariants = [ iconScheme ];
colorVariants = [ iconThemeVariant ];
};
# Fonts
fontName = "JetBrainsMono NFM";
fontPackage = pkgs.nerd-fonts.jetbrains-mono;
fontSize = 12;
in
{
config = mkIf cfg.enable {
home = {
pointerCursor = {
gtk.enable = true;
package = cursorThemePkg;
name = cursorTheme;
size = cursorSize;
};
};
dconf = {
enable = true;
settings = {
"org/gnome/desktop/interface".color-scheme = "prefer-dark";
"org/gnome/desktop/interface".cursor-theme = cursorTheme;
"org/gnome/desktop/interface".gtk-theme = gtkTheme;
"org/gnome/desktop/interface".icon-theme = iconTheme;
};
};
gtk = {
enable = true;
cursorTheme = {
name = cursorTheme;
package = cursorThemePkg;
};
theme = {
name = gtkTheme;
package = gtkThemePkg;
};
iconTheme = {
name = iconTheme;
package = iconThemePkg;
};
gtk3.extraConfig = {
gtk-application-prefer-dark-theme = true;
};
gtk4.extraConfig = {
gtk-application-prefer-dark-theme = true;
};
font = {
name = fontName;
package = fontPackage;
size = fontSize;
};
};
};
}

View File

@@ -1,106 +1,30 @@
{ lib, config, ... }:
with lib;
let
cfg = config.mjallen.programs.kitty;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
config = mkIf cfg.enable {
programs.kitty = {
enable = true;
shellIntegration.enableZshIntegration = true;
font = {
name = cfg.font.name;
package = cfg.font.package;
size = cfg.font.size;
config,
lib,
namespace,
...
}:
{
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "kitty";
moduleConfig = {
programs.kitty = {
enable = true;
shellIntegration.enableZshIntegration = true;
settings = {
bold_font = "auto";
italic_font = "auto";
bold_italic_font = "auto";
mouse_hide_wait = "2.0";
cursor_shape = "block";
url_style = "dotted";
confirm_os_window_close = "0";
};
};
};
settings = {
bold_font = "auto";
italic_font = "auto";
bold_italic_font = "auto";
mouse_hide_wait = "2.0";
cursor_shape = "block";
url_style = "dotted";
confirm_os_window_close = "0";
background_opacity = "0.85";
# The basic colors
foreground = palette.colors.text;
background = palette.colors.bg;
selection_foreground = palette.colors.bg;
selection_background = palette.colors.accent;
# Cursor colors
cursor = palette.colors.accent;
cursor_text_color = palette.colors.bg;
# URL underline color when hovering with mouse
url_color = palette.colors.accent;
# Kitty window border colors
active_border_color = palette.colors.primary;
inactive_border_color = palette.colors.bgAlt;
bell_border_color = palette.colors.warning;
# OS Window titlebar colors
wayland_titlebar_color = palette.colors.bg;
macos_titlebar_color = palette.colors.bg;
# Tab bar colors
active_tab_foreground = palette.colors.border;
active_tab_background = palette.colors.accent;
inactive_tab_foreground = palette.colors.text;
inactive_tab_background = palette.colors.bgAlt;
tab_bar_background = palette.colors.border;
# Colors for marks (marked text in the terminal)
mark1_foreground = palette.colors.bg;
mark1_background = palette.colors.primary;
mark2_foreground = palette.colors.bg;
mark2_background = palette.colors.accent;
mark3_foreground = palette.colors.bg;
mark3_background = palette.colors.info;
# The 16 terminal colors
# black
color0 = palette.colors.bg;
# Autosuggestion
color8 = palette.colors.primary;
# red
color1 = palette.colors.danger;
color9 = palette.colors.danger;
# green
color2 = palette.colors.success;
color10 = palette.colors.success;
# yellow
color3 = palette.colors.warning;
color11 = palette.colors.warning;
# blue
color4 = palette.colors.primary;
color12 = palette.colors.primary;
# magenta
color5 = palette.colors.accent;
color13 = palette.colors.accent;
# cyan
color6 = palette.colors.info;
color14 = palette.colors.info;
# white
color7 = palette.colors.textMuted;
color15 = palette.colors.text;
};
};
};
})
];
}

View File

@@ -1,39 +0,0 @@
{ lib, namespace, ... }:
with lib;
let
inherit (lib.${namespace}) mkOpt;
in
{
options.mjallen.programs.kitty = {
enable = mkEnableOption "enable kitty terminal";
font = {
name = mkOption {
type = types.str;
default = "DejaVu Sans";
};
package = mkOpt types.package pkgs.dejavu_fonts "Default font package";
size = mkOption {
type = with types; int;
default = 12;
};
};
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
# Fallback default; global theme module sets this via mkDefault
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "Kitty theme palette configuration.";
};
};
}

View File

@@ -1,32 +1,40 @@
{ config, lib, ... }:
with lib;
let
cfg = config.mjallen.programs.mako;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
config = mkIf cfg.enable {
services.mako = {
enable = true;
settings = {
font = cfg.fontName;
icons = true;
ignore-timeout = true;
sort = "-time";
width = 500;
height = 110;
layer = "overlay";
border-radius = 15;
border-size = 1;
max-icon-size = 64;
default-timeout = 5000;
background-color = palette.colors.bg;
text-color = palette.colors.text;
border-color = palette.colors.primary;
progress-color = "over ${palette.colors.info}";
config,
lib,
namespace,
...
}:
{
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "mako";
options = {
fontName = lib.mkOption {
type = lib.types.str;
default = "DejaVu Sans";
description = "Font name for mako notifications.";
};
};
};
};
moduleConfig = {
services.mako = {
enable = true;
settings = {
font = lib.mkDefault config.${namespace}.programs.mako.fontName;
icons = true;
ignore-timeout = true;
sort = "-time";
width = 500;
height = 110;
layer = "overlay";
border-radius = 15;
border-size = 1;
max-icon-size = 64;
default-timeout = 5000;
};
};
};
})
];
}

View File

@@ -1,26 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.mako = {
enable = mkEnableOption "enable mako";
fontName = mkOption {
type = types.str;
default = "DejaVu Sans";
};
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "Mako theme palette configuration.";
};
};
}

View File

@@ -2,64 +2,134 @@
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.mjallen.programs.nwg-dock;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "nwg-dock";
moduleConfig = {
home.packages = with pkgs; [ nwg-dock-hyprland ];
config = mkIf cfg.enable {
home.packages = with pkgs; [ nwg-dock-hyprland ];
home.file = {
".config/nwg-dock-hyprland/config.json".text = ''
{
"position": "bottom",
"anchor": "center",
"margin": 12,
"icon_size": 48,
"icon_size_hover": 64,
"spacing": 6,
"padding": 8,
"autohide": false,
"autohide_timeout": 0.3,
"exclusive": true,
"layer": "top",
"height": 72,
"background_alpha": 0.55,
"rounded_corners": 16,
"show_labels": false,
"show_running": true,
"show_pinned": true,
"pinned": [
"firefox.desktop",
"org.wezfurlong.wezterm.desktop",
"codium.desktop",
"org.gnome.Nautilus.desktop"
]
}
'';
home.file = {
".config/nwg-dock-hyprland/drawer.css".text = ''
window {
background: ${palette.colors.bg};
border-radius: 10px;
border-style: none;
border-width: 1px;
border-color: ${palette.colors.accent}b0
}
".config/nwg-dock-hyprland/style.css".text = ''
window {
background: #36364f;
border-radius: 10px;
border-style: none;
border-width: 1px;
border-color: rgba(156, 142, 122, 0.7)
}
#box {
/* Define attributes of the box surrounding icons here */
padding: 10px
}
#box {
padding: 10px
}
active {
/* This is to underline the button representing the currently active window */
border-bottom: solid 1px;
border-color: ${palette.colors.success}1a
}
#active {
border-bottom: solid 1px;
border-color: rgba(255, 255, 255, 0.3)
}
button, image {
background: none;
border-style: none;
box-shadow: none;
color: ${palette.colors.primary}
}
button, image {
background: none;
border-style: none;
box-shadow: none;
color: #999
}
button {
padding: 4px;
margin-left: 4px;
margin-right: 4px;
color: #eee;
font-size: 12px
}
button {
padding: 4px;
margin-left: 4px;
margin-right: 4px;
color: #eee;
font-size: 12px
}
button:hover {
background-color: ${palette.colors.bg}1a;
border-radius: 2px;
}
button:hover {
background-color: rgba(255, 255, 255, 0.15);
border-radius: 2px;
}
button:focus {
box-shadow: none
}
'';
};
};
button:focus {
box-shadow: none
}
'';
".config/nwg-dock-hyprland/drawer.css".text = ''
window {
background: ${config.lib.stylix.colors.base00};
border-radius: 10px;
border-style: none;
border-width: 1px;
border-color: ${config.lib.stylix.colors.base0E}b0
}
#box {
padding: 10px
}
active {
border-bottom: solid 1px;
border-color: ${config.lib.stylix.colors.base0B}1a
}
button, image {
background: none;
border-style: none;
box-shadow: none;
color: ${config.lib.stylix.colors.base0F}
}
button {
padding: 4px;
margin-left: 4px;
margin-right: 4px;
color: #eee;
font-size: 12px
}
button:hover {
background-color: ${config.lib.stylix.colors.base00}1a;
border-radius: 2px;
}
button:focus {
box-shadow: none
}
'';
};
};
})
];
}

View File

@@ -1,21 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.nwg-dock = {
enable = mkEnableOption "enable nwg-dock";
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "nwg-dock theme palette configuration.";
};
};
}

View File

@@ -2,56 +2,53 @@
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.mjallen.programs.nwg-drawer;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "nwg-drawer";
moduleConfig = {
home.packages = with pkgs; [ nwg-drawer ];
config = mkIf cfg.enable {
home.packages = with pkgs; [ nwg-drawer ];
home.file.".config/nwg-drawer/drawer.css".text = ''
window {
background-color: ${config.lib.stylix.colors.base00}bf;
color: ${config.lib.stylix.colors.base05}00
}
home.file = {
".config/nwg-drawer/drawer.css".text = ''
window {
background-color: ${palette.colors.bg}bf;
color: ${palette.colors.textMuted}00
}
entry {
background-color: ${config.lib.stylix.colors.base01}0f
}
/* search entry */
entry {
background-color: ${palette.colors.bgAlt}0f
}
button, image {
background: none;
border: none
}
button, image {
background: none;
border: none
}
button:hover {
background-color: ${config.lib.stylix.colors.base0F}1a
}
button:hover {
background-color: ${palette.colors.primary}1a
}
#category-button {
margin: 0 10px 0 10px
}
/* in case you wanted to give category buttons a different look */
#category-button {
margin: 0 10px 0 10px
}
#pinned-box {
padding-bottom: 5px;
border-bottom: 1px dotted ${config.lib.stylix.colors.base03}
}
#pinned-box {
padding-bottom: 5px;
border-bottom: 1px dotted ${palette.colors.border}
}
#files-box {
padding: 5px;
border: 1px dotted ${palette.colors.border};
border-radius: 15px
}
'';
};
};
#files-box {
padding: 5px;
border: 1px dotted ${config.lib.stylix.colors.base03};
border-radius: 15px
}
'';
};
})
];
}

View File

@@ -1,21 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.nwg-drawer = {
enable = mkEnableOption "enable nwg-drawer";
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "nwg-drawer theme palette configuration.";
};
};
}

View File

@@ -1,7 +1,12 @@
{ config, lib, ... }:
{
config,
lib,
namespace,
...
}:
with lib;
let
cfg = config.mjallen.programs.nwg-panel;
cfg = config.${namespace}.programs.nwg-panel;
in
{
imports = [ ./options.nix ];

View File

@@ -1,46 +1,14 @@
{ lib, pkgs, ... }:
with lib;
{
options.mjallen.programs.nwg-panel = {
enable = mkEnableOption "enable nwg-panel";
lib,
namespace,
...
}:
{
options.${namespace}.programs.nwg-panel = {
enable = lib.mkEnableOption "nwg-panel";
defaultApps = mkOption {
type = types.submodule {
options = {
browser = mkOption {
type = types.package;
default = pkgs.firefox;
};
editor = mkOption {
type = types.package;
default = pkgs.micro;
};
fileExplorer = mkOption {
type = types.package;
default = pkgs.nautilus;
};
visual = mkOption {
type = types.package;
default = pkgs.vscodium;
};
terminal = mkOption {
type = types.package;
default = pkgs.kitty;
};
office = mkOption {
type = types.package;
default = pkgs.onlyoffice-desktopeditors;
};
video = mkOption {
type = types.package;
default = pkgs.vlc;
};
imageViewer = mkOption {
type = types.package;
default = pkgs.gnome-photos;
};
};
};
defaultApps = lib.mkOption {
type = lib.types.submodule (import ../common/default-apps.nix);
description = "Default applications used across the system.";
};
};

View File

@@ -5,7 +5,7 @@
...
}:
let
isArm = builtins.match "aarch64*" system != null;
isArm = "aarch64-linux" == system;
in
{
programs.onlyoffice = {

View File

@@ -0,0 +1,87 @@
{
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.${namespace}.programs.opencode;
in
{
options.${namespace}.programs.opencode = {
enable = lib.mkEnableOption "opencode";
};
config = lib.mkIf cfg.enable {
sops.secrets."hass-mcp/token" = { };
sops.templates."hass-mcp.env" = {
mode = "0600";
content = ''
HA_URL=http://nuc-nixos.local:8123
HA_TOKEN=${config.sops.placeholder."hass-mcp/token"}
'';
};
programs.opencode = {
enable = true;
enableMcpIntegration = true;
settings = {
provider = {
nas = {
npm = "@ai-sdk/openai-compatible";
name = "llama-server (local)";
options = {
baseURL = "http://jallen-nas.local:8127/v1";
};
models = {
Qwen3-Coder-Next-Q4_0 = {
name = "Qwen3 Coder (local)";
modalities = {
input = [
"image"
"text"
];
output = [ "text" ];
};
limit = {
context = 262144;
output = 262144;
};
};
};
};
};
};
};
programs.mcp = {
enable = true;
servers = {
nixos = {
command = "nix";
args = [
"run"
"github:utensils/mcp-nixos"
"--"
];
};
hass-mcp = {
command = "bash";
args = [
"-c"
"set -a; source ${config.sops.templates."hass-mcp.env".path}; set +a; exec uvx hass-mcp"
];
};
mcp-server-code-runner = {
command = "${pkgs.nodejs_24}/bin/npm";
args = [
"-y"
"@iflow-mcp/mcp-server-code-runner"
];
};
};
};
};
}

View File

@@ -0,0 +1,290 @@
{
config,
namespace,
pkgs,
...
}:
let
git-token = config.sops.secrets."github-token".path;
update-checker = pkgs.writeScriptBin "update-checker" ''
#!/usr/bin/env nix-shell
#! nix-shell -i python3 --pure
#! nix-shell -p python3 python3Packages.pygithub python3Packages.feedparser python3Packages.requests nix-prefetch-scripts nix
import os
import json
import subprocess
from github import Github
from github import Auth
import feedparser
import requests
token = None
with open('${git-token}', 'r') as token_file:
token = token_file.readline()
auth = Auth.Token(token)
def check_github(owner, repo, version):
try:
release = None
result = None
prefetch = None
ghub = Github(auth=auth)
print(' getting repo ' + owner + '/' + repo)
repo = ghub.get_repo(owner + '/' + repo)
if '-b' in version:
release = repo.get_releases()[0]
latest_version = release.name
else:
try:
release = repo.get_latest_release()
latest_version = release.tag_name
except:
tags = repo.get_tags()
try:
if tags is not None:
latest_version = tags[0].name
except:
commits = repo.get_commits()
latest_version = commits[0].sha
if latest_version is not None:
if latest_version.replace('v',''\'') != version.replace('v',''\''):
print(' update found')
print(' Current version: ' + version)
print(' Latest version: ' + latest_version)
result = subprocess.check_output(['nix-prefetch-git', '--quiet', repo.clone_url, '--rev', latest_version])
prefetch = json.loads(result)
print(' New hash: ' + prefetch.get('hash'))
else:
print(' no update')
ghub.close()
except Exception as e:
print(e)
def check_codeberg(owner, repo, version):
feed = feedparser.parse('https://codeberg.org/{0}/{1}/releases.rss'.format(owner, repo))
if feed.status == 200:
entry = feed.entries[0]
if entry.title.replace('v',''\'') != version.replace('v',''\''):
print(' update found')
print(' Current version: ' + version)
print(' Latest version: ' + entry.title)
sha256 = subprocess.check_output(['nix-prefetch-url', url.replace(''\'''\${version}', entry.title.replace('v', ''\''))])
prefetch = subprocess.check_output(['nix', 'hash', 'convert', '--hash-algo', 'sha256', str(sha256.decode('utf-8').strip())])
print(' New hash: ' + prefetch.decode('utf-8').strip())
else:
print(' no update')
def check_open_vsx(publisher, name, version):
open_vsx = requests.get('https://open-vsx.org/api/' + publisher + '/' + name)
if open_vsx.status_code == 200:
extension = open_vsx.json()
latest_version = extension.get('version')
url = extension.get('files').get('download')
if latest_version.replace('v',''\'') != version.replace('v',''\''):
print(' update found')
print(' Current version: ' + version)
print(' Latest version: ' + latest_version)
sha256 = subprocess.check_output(['nix-prefetch-url', url])
prefetch = subprocess.check_output(['nix', 'hash', 'convert', '--hash-algo', 'sha256', str(sha256.decode('utf-8').strip())])
print(' New hash: ' + prefetch.decode('utf-8').strip())
else:
print(' no update')
def parse_nix(package_spec):
version = None
url = None
current_hash = None
owner = None
repo = None
pname = None
name = None
publisher = None
for line in package_spec.readlines():
if 'owner = "' in line and owner is None:
owner = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'repo = "' in line and repo is None:
repo = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'version = "' in line and version is None:
version = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'rev = "' in line and ''\'''\${version}' not in line:
version = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'url = "' in line and url is None:
url = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'sha256 = "' in line or ' hash = "' in line and current_hash is None:
current_hash = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'pname = "' in line and pname is None:
pname = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if ' name = "' in line and name is None:
name = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if 'publisher = "' in line and publisher is None:
publisher = line.split(' = ')[-1].replace('"', ''\'').replace(';\n', ''\'')
if url is None and repo is not None:
if 'pname' in repo:
repo = repo.replace(''\'''\${pname}', pname)
url = 'https://github.com/{0}/{1}/releases/tag/{2}'.format(owner, repo, version)
if url is not None and repo is None and 'github' in url:
owner = url.split('github.com/')[-1].split('/')[0]
repo = url.split('github.com/')[-1].split('/')[1]
if url is not None and repo is None and 'codeberg' in url:
owner = url.split('codeberg.org/')[-1].split('/')[0]
repo = url.split('codeberg.org/')[-1].split('/')[1]
if url is not None and version is None:
version = url.split('/')[-1].replace('.tar.gz', ''\'')
if url is not None and publisher is not None:
url = url.replace(''\'''\${publisher}', publisher).replace(''\'''\${name}', name)
return url, current_hash, owner, repo, pname, name, publisher, version
def parse_json(json_versions, flavor=''\''):
versions = json.load(json_versions)
linux_versions = versions.get('linux')
config_versions = versions.get('config')
patch_versions = versions.get('patches')
zfs_versions = versions.get('zfs')
check_kernel(linux_versions, flavor)
check_cachy_config(config_versions, flavor)
check_patch_versions(patch_versions, flavor)
check_zfs_versions(zfs_versions, flavor)
def check_kernel(linux_versions, flavor=''\''):
srcinfo = requests.get('https://raw.githubusercontent.com/CachyOS/linux-cachyos/master/linux-cachyos' + flavor + '/.SRCINFO')
for line in srcinfo.text.split('\n'):
if 'pkgver = ' in line:
kernel_version = line.split('=')[-1].strip()
if kernel_version[-2:] == '.0':
kernel_version = kernel_version[:-2]
if flavor in [''\'', '-lts', '-server', '-gcc', '-hardened']:
release_src = 'https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-' + kernel_version + '.tar.xz'
if flavor == '-rc':
release_src = 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/snapshot/linux-' + kernel_version.replace('.rc', '-rc') + '.tar.gz'
sha256 = subprocess.check_output(['nix-prefetch-url', release_src])
prefetch = subprocess.check_output(['nix', 'hash', 'convert', '--hash-algo', 'sha256', str(sha256.decode('utf-8').strip())])
current_version = linux_versions.get('version')
current_hash = linux_versions.get('hash')
latest_hash = prefetch.decode('utf-8').strip()
print(' Checking linux versions...')
if current_hash != latest_hash:
print(' Current rev: ' + current_version)
print(' Current hash: ' + current_hash)
print(' New rev: ' + kernel_version)
print(' New hash: ' + latest_hash)
else:
print(' no update')
def check_cachy_config(config_versions, flavor=''\''):
result = subprocess.check_output(['nix-prefetch-git', '--quiet', 'https://github.com/CachyOS/linux-cachyos.git'])
prefetch = json.loads(result)
current_version = config_versions.get('rev')
latest_version = prefetch.get('rev')
print(' Checking config versions...')
if current_version != latest_version:
print(' Current rev: ' + current_version)
print(' New rev: ' + latest_version)
print(' New hash: ' + prefetch.get('hash'))
else:
print(' no update')
def check_patch_versions(patch_versions, flavor=''\''):
result = subprocess.check_output(['nix-prefetch-git', '--quiet', 'https://github.com/CachyOS/kernel-patches.git'])
prefetch = json.loads(result)
current_version = patch_versions.get('rev')
latest_version = prefetch.get('rev')
print(' Checking patch versions...')
if current_version != latest_version:
print(' Current rev: ' + current_version)
print(' New rev: ' + latest_version)
print(' New hash: ' + prefetch.get('hash'))
else:
print(' no update')
def kconfig_to_nix(flavor=''\''):
kconfig_result = subprocess.check_output(['nix', 'build', '.#nixosConfigurations.jallen-nas.pkgs.linuxPackages_cachyos' + flavor + '.kernel.kconfigToNix', '--no-link', '--print-out-paths'])
config_file = kconfig_result.decode('utf-8').strip()
if flavor == ''\'':
cachy_flavor = '-gcc'
result = subprocess.check_output(['cat', config_file])
with open('/etc/nixos/packages/linux-cachyos/config-nix/cachyos' + cachy_flavor + '.x86_64-linux.nix', 'w') as config:
config.write(result.decode('utf-8').strip())
def check_zfs_versions(zfs_versions, flavor=''\''):
result = requests.get('https://raw.githubusercontent.com/CachyOS/linux-cachyos/master/linux-cachyos' + flavor + '/PKGBUILD')
for line in result.text.split('\n'):
if 'git+https://github.com/cachyos/zfs.git#commit=' in line:
zfs_rev = line.split('zfs.git#commit=')[-1].replace('")', ''\'')
result = subprocess.check_output(['nix-prefetch-git', '--quiet', 'https://github.com/CachyOS/zfs.git', '--rev', zfs_rev])
prefetch = json.loads(result)
current_version = zfs_versions.get('rev')
latest_version = prefetch.get('rev')
print(' Checking zfs versions...')
if current_version != latest_version:
print(' Current rev: ' + current_version)
print(' New rev: ' + latest_version)
print(' New hash: ' + prefetch.get('hash'))
else:
print(' no update')
for (root,dirs,files) in os.walk('/etc/nixos/packages',topdown=True):
if 'default.nix' in files and 'versions.json' not in files:
print(root.split('/')[-1])
with open(root + '/default.nix', 'r') as package_spec:
url, current_hash, owner, repo, pname, name, publisher, version = parse_nix(package_spec)
if owner is not None and repo is not None and 'codeberg' in url:
check_codeberg(owner, repo, version)
elif owner is not None and repo is not None and 'github' in url:
check_github(owner, repo, version)
elif publisher is not None and 'open-vsx' in url:
check_open_vsx(publisher, name, version)
else:
if url is not None:
print(url)
if 'default.nix' in files and 'versions.json' in files:
with open(root + '/versions.json', 'r') as json_versions:
print('Checking Linux CachyOS')
parse_json(json_versions)
with open(root + '/versions-rc.json', 'r') as json_versions:
print('Checking Linux CachyOS RC')
parse_json(json_versions, '-rc')
with open(root + '/versions-lts.json', 'r') as json_versions:
print('Checking Linux CachyOS LTS')
parse_json(json_versions, '-lts')
with open(root + '/versions-hardened.json', 'r') as json_versions:
print('Checking Linux CachyOS Hardened')
parse_json(json_versions, '-hardened')
'';
in
{
config = {
sops = {
age.keyFile = "/home/${config.${namespace}.user.name}/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
validateSopsFiles = false;
secrets = {
"github-token" = { };
};
templates = {
".env".content = ''
GITHUB_TOKEN = "${config.sops.placeholder.github-token}"
'';
};
};
home.packages = [ update-checker ];
};
}

View File

@@ -1,325 +1,239 @@
{ config, lib, ... }:
{
config,
lib,
namespace,
...
}:
with lib;
let
cfg = config.mjallen.programs.waybar;
palette = import cfg.theme.file;
defaultOpacity = palette.tokens.opacity or "opacity: 0.85;";
defaultBorderRadius = palette.tokens.borderRadius or "border-radius: 1rem;";
defaultCenterOptions =
palette.tokens.centerOptions or ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
'';
borderRight =
palette.tokens.borderRight or ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 0rem 1rem 1rem 0rem;
margin-right: 0.5rem;
'';
borderLeft = ''
padding-top: 0.2rem;
padding-bottom: 0.2rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
margin: 3px 0;
border-radius: 1rem 0rem 0rem 1rem;
margin-left: 0.5rem;
'';
cfg = config.${namespace}.programs.waybar;
baseStyle =
if cfg.style.file != null then
builtins.readFile cfg.style.file
else
''
.blink_me {
animation: blinker 1s linear infinite;
}
/* =============================================================================
ANIMATIONS & GLOBAL OVERRIDES
============================================================================= */
@keyframes blinker {
50% {
color: ${palette.colors.danger};
}
50% { color: @base08; }
}
/* Override Stylix font settings */
* {
font-family:
Jetbrains Mono Nerd Font,
monospace;
font-family: "Jetbrains Mono Nerd Font", monospace;
font-size: 14px;
min-height: 0;
}
/* =============================================================================
MAIN BAR & TOOLTIPS
============================================================================= */
window#waybar {
background: transparent;
}
#waybar {
background: transparent;
color: ${palette.colors.text};
color: @base06;
margin: 5px 5px;
}
#workspaces {
background-color: ${palette.colors.bg};
${defaultBorderRadius}
${defaultOpacity}
${defaultCenterOptions}
margin-left: 0.6rem;
}
#workspaces button {
color: ${palette.colors.primary};
${defaultBorderRadius}
padding: 0.4rem;
}
#workspaces button.active {
color: ${palette.colors.info};
${defaultBorderRadius}
}
#workspaces button:hover {
color: ${palette.colors.info};
${defaultBorderRadius}
}
#workspaces button.focused {
color: ${palette.colors.text};
background: ${palette.colors.warning};
${defaultBorderRadius}
}
#workspaces button.urgent {
color: ${palette.colors.bg};
background: ${palette.colors.text};
${defaultBorderRadius}
}
#tooltip {
background: ${palette.colors.bg};
border-color: ${palette.colors.bg};
${defaultBorderRadius}
border-width: 1rem;
background: @base00;
border-color: @base00;
border-radius: 1rem;
border-width: 2px; /* Reduced from 1rem which is usually too thick */
border-style: solid;
}
#window {
color: ${palette.colors.accent};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultBorderRadius}
${defaultCenterOptions}
margin-left: 4rem;
margin-right: ${toString cfg.windowOffset}rem;
}
/* make window module transparent when no windows present */
#window.empty {
background-color: transparent;
}
#custom-power {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
/* Right-click menu styling */
menu {
border-radius: 15px;
background: ${palette.colors.bg};
color: ${palette.colors.text};
background: @base00;
color: @base06;
}
menuitem {
border-radius: 15px;
}
#custom-weather {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
/* =============================================================================
WORKSPACES
============================================================================= */
#workspaces {
background-color: @base00;
border-radius: 1rem;
opacity: 0.85;
padding: 0.2rem 0.5rem;
margin: 3px 0 3px 0.6rem;
}
#custom-notifications {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
#workspaces button {
color: @base0F;
border-radius: 1rem;
padding: 0.4rem;
/* Border handled by stylix, but we add specific colors below */
}
#custom-notifications.notify {
color: ${palette.colors.danger};
#workspaces button.active,
#workspaces button:hover {
color: @base0C;
border-radius: 1rem;
}
#workspaces button.focused {
color: @base06;
background: @base0A;
border-radius: 1rem;
}
#workspaces button.urgent {
color: @base00;
background: @base06;
border-radius: 1rem;
}
/* =============================================================================
WINDOW TITLE
============================================================================= */
#window {
color: @base0E;
background-color: @base00;
opacity: 0.85;
border-radius: 1rem;
padding: 0.2rem 0.5rem;
margin: 3px 0;
margin-left: 4rem;
margin-right: 75rem; /* This is very large, likely a centering hack */
}
#window.empty {
background-color: transparent;
}
/* =============================================================================
SHARED MODULE STYLES
============================================================================= */
/* This block applies the standard "Nord Background" style to all modules
to avoid repeating code for every single ID. */
#custom-power,
#custom-weather,
#custom-notifications,
#battery,
#clock,
#idle_inhibitor,
#network,
#bluetooth,
#wireplumber,
#keyboard-state,
#temperature,
#custom-left-end,
#custom-right-end,
#custom-lights,
#tray {
background-color: @base00;
opacity: 0.85;
padding: 0.2rem 0.5rem;
margin: 3px 0;
border-radius: 0; /* Default to square, rounded manually below */
}
/* Common Hover Effect */
#idle_inhibitor:hover,
#network:hover,
#bluetooth:hover,
#wireplumber:hover,
#temperature:hover,
#custom-lights:hover {
background: @base02;
}
/* =============================================================================
MODULE SPECIFIC COLORS
============================================================================= */
#custom-power,
#custom-weather,
#custom-notifications {
color: @base0F;
}
#custom-notifications.notify { color: @base08; }
#custom-notifications.alert {
animation-name: blinker;
animation-duration: 3s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation: blinker 3s linear infinite;
}
#battery {
color: ${palette.colors.accent};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
color: @base0E;
min-width: 3rem;
}
#clock {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
/* ------------- */
#clock { color: @base0F; }
#idle_inhibitor {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
color: @base0F;
padding-right: 1rem;
}
#idle_inhibitor:hover {
background: ${palette.colors.surfaceAlt};
}
#network {
color: ${palette.colors.accent};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
color: @base0E;
padding-right: 15px;
}
#network:hover {
background: ${palette.colors.surfaceAlt};
}
#bluetooth { color: @base0F; }
#bluetooth {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#bluetooth:hover {
background: ${palette.colors.surfaceAlt};
}
#wireplumber.source {
color: ${palette.colors.info};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
/* --- Audio source/sink --- */
#wireplumber.source,
#wireplumber.sink {
color: @base0C;
}
#wireplumber.source.muted {
animation-name: blinker;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation: blinker 2s linear infinite;
padding-right: 1rem;
}
#wireplumber.source:hover {
background: ${palette.colors.surfaceAlt};
}
#wireplumber.sink {
color: ${palette.colors.info};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#wireplumber.sink.muted {
animation-name: blinker;
animation-duration: 5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation: blinker 5s linear infinite;
}
#wireplumber.sink:hover {
background: ${palette.colors.surfaceAlt};
}
#keyboard-state.numlock {
color: ${palette.colors.info};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#keyboard-state.capslock {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
/* --- Keyboard --- */
#keyboard-state.numlock { color: @base0C; }
#keyboard-state.capslock { color: @base0F; }
/* --- Temperature --- */
#temperature,
#temperature.gpu {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
color: @base0F;
}
#temperature.gpu:hover {
background: ${palette.colors.surfaceAlt};
}
#temperature {
color: ${palette.colors.primary};
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0
}
/* ------------- */
/* --- Tray --- */
#tray {
background-color: ${palette.colors.bg};
${defaultOpacity}
${defaultCenterOptions}
${defaultBorderRadius}
border-radius: 1rem;
margin-right: 0.6rem;
}
/* ------------- */
/* =============================================================================
DECORATIVE END CAPS & EXTRAS
============================================================================= */
#custom-left-end {
background-color: ${palette.colors.bg};
${defaultOpacity}
${borderLeft}
border-radius: 1rem 0 0 1rem;
margin-left: 0.5rem;
}
#custom-right-end {
background-color: ${palette.colors.bg};
${defaultOpacity}
${borderRight}
border-radius: 0 1rem 1rem 0;
margin-right: 0.5rem;
}
#custom-lights {
color: @base0C;
/* Re-declaring background here to ensure specific override logic if needed */
background-color: @base00;
border-radius: 1rem 0 0 1rem;
margin-left: 0.5rem;
}
'';
in
@@ -474,15 +388,15 @@ in
"custom/power" = {
format = " ";
tooltip = false;
tooltip = false;
menu = "on-click";
menu-file = "~/.config/waybar/power_menu.xml";
menu-actions = {
shutdown = "shutdown";
reboot = "reboot";
suspend = "systemctl suspend";
hibernate = "systemctl hibernate";
lock = "pidof hyprlock || hyprlock";
shutdown = "shutdown";
reboot = "reboot";
suspend = "systemctl suspend";
hibernate = "systemctl hibernate";
lock = "pidof hyprlock || hyprlock";
};
};
}
@@ -596,10 +510,10 @@ in
calendar = {
mode = "month";
format = {
months = "<span color='${palette.colors.primary}'><b>{}</b></span>";
days = "<span color='${palette.colors.primary}'><b>{}</b></span>";
weekdays = "<span color='${palette.colors.info}'><b>{}</b></span>";
today = "<span color='${palette.colors.success}'><b><u>{}</u></b></span>";
months = "<span color='@base0F'><b>{}</b></span>";
days = "<span color='@base0F'><b>{}</b></span>";
weekdays = "<span color='@base0C'><b>{}</b></span>";
today = "<span color='${config.lib.stylix.colors.base0B}'><b><u>{}</u></b></span>";
};
};
};

View File

@@ -1,4 +1,4 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
let
inherit (types)
@@ -13,7 +13,7 @@ let
;
in
{
options.mjallen.programs.waybar = {
options.${namespace}.programs.waybar = {
enable = mkEnableOption "Waybar status bar";
# Legacy/compat options (kept for backwards compatibility)
@@ -41,21 +41,6 @@ in
description = "Right margin offset for the hyprland/window module (in rem).";
};
# Theme
theme = mkOption {
type = submodule {
options = {
file = mkOption {
type = path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset (e.g., Nord).";
};
};
};
default = { };
description = "Theme configuration.";
};
# Layout
layout = mkOption {
type = submodule {

View File

@@ -6,11 +6,13 @@
...
}:
let
cfg = config.mjallen.programs.waybar;
cfg = config.${namespace}.programs.waybar;
pythonEnv = pkgs.python3.withPackages (_ps: [
pkgs.${namespace}.homeassistant-api
]);
pythonEnv = pkgs.python3.withPackages (
_ps: with pkgs.${namespace}; [
homeassistant-api
]
);
waybar-hass = pkgs.writeScriptBin "waybar-hass" ''
#!${pythonEnv}/bin/python

View File

@@ -2,10 +2,11 @@
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.mjallen.programs.waybar;
cfg = config.${namespace}.programs.waybar;
waybar-weather = pkgs.writeScriptBin "waybar-weather" ''
#!/usr/bin/env nix-shell

View File

@@ -1,104 +1,93 @@
{ config, lib, ... }:
with lib;
let
cfg = config.mjallen.programs.wlogout;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
config = mkIf cfg.enable {
programs.wlogout = {
enable = false;
layout = {
lock = {
label = "lock";
action = "hyprlock --immediate";
text = "Lock";
keybind = "l";
};
hibernate = {
label = "hibernate";
action = "systemctl hibernate";
text = "Hibernate";
keybind = "h";
};
logout = {
label = "logout";
action = "sleep 1; hyprctl dispatch exit";
text = "Logout";
keybind = "e";
};
shutdown = {
label = "shutdown";
action = "systemctl poweroff";
text = "Shutdown";
keybind = "s";
};
suspend = {
label = "suspend";
action = "systemctl suspend";
text = "Suspend";
keybind = "u";
};
reboot = {
label = "reboot";
action = "reboot";
text = "Reboot";
keybind = "r";
config,
lib,
namespace,
...
}:
{
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "wlogout";
moduleConfig = {
programs.wlogout = {
enable = false;
layout = {
lock = {
label = "lock";
action = "hyprlock --immediate";
text = "Lock";
keybind = "l";
};
hibernate = {
label = "hibernate";
action = "systemctl hibernate";
text = "Hibernate";
keybind = "h";
};
logout = {
label = "logout";
action = "sleep 1; hyprctl dispatch exit";
text = "Logout";
keybind = "e";
};
shutdown = {
label = "shutdown";
action = "systemctl poweroff";
text = "Shutdown";
keybind = "s";
};
suspend = {
label = "suspend";
action = "systemctl suspend";
text = "Suspend";
keybind = "u";
};
reboot = {
label = "reboot";
action = "reboot";
text = "Reboot";
keybind = "r";
};
};
style = ''
* {
background-image: none;
}
window {
background-color: ${config.lib.stylix.colors.base00}f0
}
button {
margin: 8px;
color: ${config.lib.stylix.colors.base0C};
background-color: ${config.lib.stylix.colors.base01};
border-style: solid;
border-width: 2px;
background-repeat: no-repeat;
background-position: center;
background-size: 25%;
}
button:active,
button:focus,
button:hover {
color: ${config.lib.stylix.colors.base0C};
background-color: ${config.lib.stylix.colors.base02Alt};
outline-style: none;
}
#lock { background-image: image(url("icons/lock.png")); }
#logout { background-image: image(url("icons/logout.png")); }
#suspend { background-image: image(url("icons/suspend.png")); }
#hibernate { background-image: image(url("icons/hibernate.png")); }
#shutdown { background-image: image(url("icons/shutdown.png")); }
#reboot { background-image: image(url("icons/reboot.png")); }
'';
};
};
style = ''
* {
background-image: none;
}
window {
background-color: ${palette.colors.bg}f0
}
button {
margin: 8px;
color: ${palette.colors.info};
background-color: ${palette.colors.bgAlt};
border-style: solid;
border-width: 2px;
background-repeat: no-repeat;
background-position: center;
background-size: 25%;
}
button:active,
button:focus,
button:hover {
color: ${palette.colors.info};
background-color: ${palette.colors.surfaceAlt};
outline-style: none;
}
#lock {
background-image: image(url("icons/lock.png"));
}
#logout {
background-image: image(url("icons/logout.png"));
}
#suspend {
background-image: image(url("icons/suspend.png"));
}
#hibernate {
background-image: image(url("icons/hibernate.png"));
}
#shutdown {
background-image: image(url("icons/shutdown.png"));
}
#reboot {
background-image: image(url("icons/reboot.png"));
}
'';
};
};
})
];
}

View File

@@ -1,26 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.wlogout = {
enable = mkEnableOption "enable wlogout";
fontName = mkOption {
type = types.str;
default = "Deja Vu Sans";
};
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "wlogout theme palette configuration.";
};
};
}

View File

@@ -1,102 +1,107 @@
{ config, lib, ... }:
with lib;
let
cfg = config.mjallen.programs.wofi;
palette = import cfg.theme.file;
in
{
imports = [ ./options.nix ];
config,
lib,
namespace,
...
}:
{
imports = [
(lib.${namespace}.mkHomeModule {
inherit config;
domain = "programs";
name = "wofi";
options = {
fontName = lib.mkOption {
type = lib.types.str;
default = "DejaVu Sans";
description = "Font name for wofi.";
};
};
moduleConfig = {
programs.wofi = {
enable = true;
style = ''
* {
font-family: "${config.${namespace}.programs.wofi.fontName}", monospace;
font-size: 14px;
}
config = mkIf cfg.enable {
programs.wofi = {
enable = true;
style = ''
* {
font-family: "${cfg.fontName}", monospace;
font-size: 14px;
}
window {
margin: 0px;
padding: 10px;
border: 0.16em solid ${config.lib.stylix.colors.base0E};
border-radius: 0.1em;
background-color: ${config.lib.stylix.colors.base00};
}
/* Window */
window {
margin: 0px;
padding: 10px;
border: 0.16em solid ${palette.colors.accent};
border-radius: 0.1em;
background-color: ${palette.colors.bg};
}
#inner-box {
margin: 5px;
padding: 10px;
border: none;
background-color: ${config.lib.stylix.colors.base00};
}
/* Inner Box */
#inner-box {
margin: 5px;
padding: 10px;
border: none;
background-color: ${palette.colors.bg};
}
#outer-box {
margin: 5px;
padding: 10px;
border: none;
background-color: ${config.lib.stylix.colors.base00};
}
/* Outer Box */
#outer-box {
margin: 5px;
padding: 10px;
border: none;
background-color: ${palette.colors.bg};
}
#scroll {
margin: 0px;
padding: 10px;
border: none;
background-color: ${config.lib.stylix.colors.base00};
}
/* Scroll */
#scroll {
margin: 0px;
padding: 10px;
border: none;
background-color: ${palette.colors.bg};
}
#input {
margin: 5px 20px;
padding: 10px;
border: none;
border-radius: 0.1em;
color: ${config.lib.stylix.colors.base06};
background-color: ${config.lib.stylix.colors.base00};
}
/* Input */
#input {
margin: 5px 20px;
padding: 10px;
border: none;
border-radius: 0.1em;
color: ${palette.colors.text};
background-color: ${palette.colors.bg};
}
#input image {
border: none;
color: ${config.lib.stylix.colors.base08};
}
#input image {
border: none;
color: ${palette.colors.danger};
}
#input * {
outline: 4px solid ${config.lib.stylix.colors.base08}!important;
}
#input * {
outline: 4px solid ${palette.colors.danger}!important;
}
#text {
margin: 5px;
border: none;
color: ${config.lib.stylix.colors.base06};
}
/* Text */
#text {
margin: 5px;
border: none;
color: ${palette.colors.text};
}
#entry {
background-color: ${config.lib.stylix.colors.base00};
}
#entry {
background-color: ${palette.colors.bg};
}
#entry arrow {
border: none;
color: ${config.lib.stylix.colors.base0E};
}
#entry arrow {
border: none;
color: ${palette.colors.accent};
}
#entry:selected {
border: 0.11em solid ${config.lib.stylix.colors.base0E};
}
/* Selected Entry */
#entry:selected {
border: 0.11em solid ${palette.colors.accent};
}
#entry:selected #text {
color: ${config.lib.stylix.colors.base0C};
}
#entry:selected #text {
color: ${palette.colors.info};
}
#entry:drop(active) {
background-color: ${palette.colors.accent}!important;
}
'';
};
};
#entry:drop(active) {
background-color: ${config.lib.stylix.colors.base0E}!important;
}
'';
};
};
})
];
}

View File

@@ -1,26 +0,0 @@
{ lib, ... }:
with lib;
{
options.mjallen.programs.wofi = {
enable = mkEnableOption "enable wofi";
fontName = mkOption {
type = types.str;
default = "Deja Vu Sans";
};
theme = mkOption {
type = types.submodule {
options = {
file = mkOption {
type = types.path;
default = lib.snowfall.fs.get-file "modules/home/desktop/theme/palettes/nord.nix";
description = "Nix file exporting a palette attrset.";
};
};
};
default = { };
description = "Wofi theme palette configuration.";
};
};
}

View File

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

View File

@@ -1,13 +1,14 @@
{
config,
lib,
namespace,
...
}:
let
cfg = config.mjallen.shell-aliases;
cfg = config.${namespace}.shell-aliases;
in
{
options.mjallen.shell-aliases = {
options.${namespace}.shell-aliases = {
enable = lib.mkEnableOption "Common shell aliases";
buildHost = lib.mkOption {

View File

@@ -1,29 +1,28 @@
{
config,
lib,
namespace,
...
}:
let
cfg = config.mjallen.sops;
cfg = config.${namespace}.sops;
in
{
imports = [ ./options.nix ];
config = lib.mkIf cfg.enable {
# sops = {
# age.keyFile = "/home/${user}/.config/sops/age/keys.txt";
# defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
# validateSopsFiles = false;
# secrets = {
# "ssh-keys-public/desktop-nixos" = {
# path = "/home/${user}/.ssh/id_ed25519.pub";
# mode = "0644";
# };
# "ssh-keys-private/desktop-nixos" = {
# path = "/home/${user}/.ssh/id_ed25519";
# mode = "0600";
# };
# };
# };
sops = {
age.keyFile = "/home/${config.${namespace}.user.name}/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
validateSopsFiles = false;
# secrets = {
# "github-token" = { };
# };
# templates = {
# ".env".content = ''
# GITHUB_TOKEN = "${config.sops.placeholder.github-token}"
# '';
# };
};
};
}

View File

@@ -1,12 +1,13 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.mjallen.sops = {
options.${namespace}.sops = {
enable = mkEnableOption "enable sops";
defaultSopsFile = mkOption {
type = types.str;
type = types.nullOr types.path;
default = null;
description = "Path to the default sops secrets file.";
};
};
}

View File

@@ -0,0 +1,105 @@
{
config,
pkgs,
system,
...
}:
let
# # Pull from global theme options
# themeSize = "standard"; # "standard" | "compact"
# themeAccent = "default"; # "default" | ... | "all"
# themeTweak = "normal"; # "normal" | "rimless" | "float" | "black"
# themeColor = "dark"; # "light" | "dark"
# iconThemeVariant = "default"; # "default" | ... | "all"
# iconScheme = "nord"; # "default" | "nord" | "dracula" | ...
# # GTK
# gtkTheme = "Colloid-dark-standard";
# gtkThemePkg = pkgs.colloid-gtk-theme.override {
# sizeVariants = [ themeSize ];
# colorVariants = [ themeColor ];
# themeVariants = [ themeAccent ];
# tweaks = [ themeTweak ];
# };
# # Icons
# iconTheme = "Colloid-nord-dark";
# iconThemePkg = pkgs.colloid-icon-theme.override {
# schemeVariants = [ iconScheme ];
# colorVariants = [ iconThemeVariant ];
# };
isDarwin = system == "aarch64-darwin";
in
{
stylix = {
enable = true;
overlays.enable = false;
enableReleaseChecks = false;
base16Scheme = "${pkgs.base16-schemes}/share/themes/nord.yaml";
polarity = "dark";
cursor = {
name = "macOS";
size = 24;
package = pkgs.apple-cursor;
};
fonts = {
serif = {
package = pkgs.dejavu_fonts;
name = "DejaVu Serif";
};
sansSerif = {
package = pkgs.dejavu_fonts;
name = "DejaVu Sans";
};
monospace = {
package = pkgs.nerd-fonts.jetbrains-mono;
name = "JetBrainsMono NFM";
};
emoji = {
package = pkgs.noto-fonts-color-emoji;
name = "Noto Color Emoji";
};
sizes = {
applications = if isDarwin then 10 else 12;
desktop = if isDarwin then 12 else 14;
popups = config.stylix.fonts.sizes.desktop;
terminal = config.stylix.fonts.sizes.applications;
};
};
icons = {
enable = true;
package = pkgs.colloid-icon-theme.override {
schemeVariants = [ "nord" ];
colorVariants = [ "default" ];
};
dark = "Colloid-Nord-Dark";
light = "Colloid-Nord-Light";
};
opacity = {
terminal = 0.85;
};
targets = {
hyprlock = {
enable = false;
useWallpaper = false;
};
kde.enable = false;
firefox = {
enable = false;
profileNames = [
"default"
"954lxlok.default"
];
};
};
};
}

View File

@@ -31,7 +31,7 @@ in
};
fullName = mkOption {
type = types.str;
default = "Austin Horstman";
default = "Matt Jallen";
description = "The full name of the user.";
};
home = mkOption {
@@ -41,8 +41,8 @@ in
};
icon = mkOption {
type = (types.nullOr types.package);
default = pkgs.${namespace}.user-icon;
description = "The profile picture to use for the user.";
default = null;
description = "The profile picture to use for the user. Set to a package whose output is the icon file (e.g. a derivation producing a PNG).";
};
name = mkOption {
type = (types.nullOr types.str);
@@ -115,8 +115,8 @@ in
tarnow = "${getExe pkgs.gnutar} -acf ";
untar = "${getExe pkgs.gnutar} -zxvf ";
wget = "${getExe pkgs.wget} -c ";
remove-empty = ''${getExe' pkgs.findutils "find"} . -type d --empty --delete'';
print-empty = ''${getExe' pkgs.findutils "find"} . -type d --empty --print'';
remove-empty = "${getExe' pkgs.findutils "find"} . -type d --empty --delete";
print-empty = "${getExe' pkgs.findutils "find"} . -type d --empty --print";
dfh = "${getExe' pkgs.coreutils "df"} -h";
duh = "${getExe' pkgs.coreutils "du"} -h";
usage = "${getExe' pkgs.coreutils "du"} -ah -d1 | sort -rn 2>/dev/null";
@@ -145,7 +145,7 @@ in
myip = "${getExe pkgs.curl} ifconfig.me";
# Cryptography
genpass = "${getExe pkgs.openssl} rand - base64 20"; # Generate a random, 20-character password
genpass = "${getExe pkgs.openssl} rand -base64 20"; # Generate a random, 20-character password
sha = "shasum -a 256"; # Test checksum
};

View File

@@ -22,8 +22,26 @@ in
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !cfg.yubikeyEncryption || config.${namespace}.hardware.disko.enableLuks;
message = "mjallen.boot.common.yubikeyEncryption requires mjallen.hardware.disko.enableLuks = true.";
}
{
assertion = !cfg.yubikeyEncryption || config.${namespace}.hardware.disko.enable;
message = "mjallen.boot.common.yubikeyEncryption requires mjallen.hardware.disko.enable = true (disko-managed disk layout).";
}
];
boot = {
kernelModules = [ "kvm" ];
kernelParams = lib.mkDefault [
"quiet"
"splash"
"udev.log_level=3"
];
binfmt = lib.mkIf isArm {
registrations."x86_64-linux" = {
magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00'';
@@ -36,12 +54,30 @@ in
};
};
supportedFilesystems = [ "bcachefs" ];
supportedFilesystems = {
bcachefs = lib.mkOverride 90 true;
btrfs = lib.mkOverride 90 true;
};
consoleLogLevel = lib.mkForce 3;
bcachefs.package = lib.mkOverride 90 pkgs.${namespace}.bcachefs;
consoleLogLevel = lib.mkDefault 0;
bootspec.enable = (!isArm);
initrd = {
verbose = lib.mkDefault false;
# availableKernelModules = {
# bcachefs = lib.mkOverride 90 true;
# btrfs = lib.mkOverride 90 true;
# };
# kernelModules = {
# bcachefs = lib.mkOverride 90 true;
# btrfs = lib.mkOverride 90 true;
# };
# systemd.storePaths = with pkgs; [
# bcachefs-tools
# ];
luks = mkIf cfg.yubikeyEncryption {
devices = {
"${config.disko.devices.disk.main.content.partitions.root.name}" = {
@@ -53,7 +89,7 @@ in
};
slot = 2;
twoFactor = false;
gracePeriod = yubikeyGracePeriod;
gracePeriod = cfg.yubikeyGracePeriod;
};
};
};

View File

@@ -17,6 +17,13 @@ in
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.configLimit > 0;
message = "mjallen.bootloader.lanzaboote.configLimit must be a positive integer (got ${toString cfg.configLimit}).";
}
];
boot = {
loader = {
efi = {

View File

@@ -6,31 +6,52 @@
...
}:
let
inherit (lib.${namespace}) enabled disabled;
cfg = config.${namespace}.desktop.gnome;
in
{
imports = [ ../../../home/desktop/gnome/options.nix ];
options.${namespace}.desktop.gnome = {
enable = lib.mkEnableOption "GNOME desktop environment";
};
config = lib.mkIf cfg.enable {
services = {
# Enable Desktop Environment.
desktopManager.gnome.enable = true;
# Enable Desktop Environment.
displayManager = {
gdm.enable = lib.mkDefault true;
gdm.wayland = lib.mkDefault true;
desktopManager.gnome = enabled;
displayManager.gdm = {
enable = lib.mkDefault true;
wayland = lib.mkDefault true;
};
gnome.gnome-remote-desktop.enable = true;
gnome = lib.mkOverride 90 {
at-spi2-core = disabled;
core-apps = enabled;
core-developer-tools = disabled;
core-os-services = enabled;
core-shell = enabled;
evolution-data-server = enabled;
games = disabled;
gcr-ssh-agent = enabled;
glib-networking = enabled;
gnome-browser-connector = enabled;
gnome-initial-setup = disabled;
gnome-keyring = enabled;
gnome-online-accounts = enabled;
gnome-remote-desktop = enabled;
gnome-settings-daemon = enabled;
gnome-software = disabled;
gnome-user-share = enabled;
localsearch = enabled;
rygel = enabled;
sushi = enabled;
tinysparql = enabled;
};
};
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
programs = {
kdeconnect = {
enable = false;
package = pkgs.gnomeExtensions.gsconnect;
};
programs.kdeconnect = {
enable = false;
package = pkgs.gnomeExtensions.gsconnect;
};
};
}

Some files were not shown because too many files have changed in this diff Show More