Compare commits
650 Commits
wip-emulat
...
wip-sxmo-s
| Author | SHA1 | Date | |
|---|---|---|---|
| b50ee5dcea | |||
| aaa8424c9e | |||
| 65123aa963 | |||
| 97d14f4c2c | |||
| 3e7fc56c86 | |||
| 346ca57c93 | |||
| 47e23c1ff3 | |||
| b6d2fbdf6d | |||
| cf553b1386 | |||
| e40cbaf1cf | |||
| 19b8c0c923 | |||
| 22e9a48edc | |||
| a6b1c23e2b | |||
| 4a498ef1a9 | |||
| 4909127ec7 | |||
| 7a75cad65f | |||
| 168fcce157 | |||
| 03d3ea4965 | |||
| e5125065d6 | |||
| bc3ad7dfa5 | |||
| 2097c3ad77 | |||
| 56838a4867 | |||
| d35fe126e3 | |||
| a6ea5da7a1 | |||
| 98a6671e95 | |||
| 243a4c6f0d | |||
| e84be3a7b2 | |||
| 5fdd6881a0 | |||
| 67192d89a9 | |||
| b6c8b1948b | |||
| 3a71d26638 | |||
| a586611aa0 | |||
| d7120a14f4 | |||
| 7db8dabf8f | |||
| d89287af11 | |||
| b14daac0f8 | |||
| f65aaf8852 | |||
| 5a84c9a585 | |||
| 464fca9679 | |||
| 6c6e1ee84b | |||
| 41d8c6681f | |||
| 4dbb656a34 | |||
| 8c4caab995 | |||
| 83586ce483 | |||
| e20c4d01e6 | |||
| 01cad7b702 | |||
| 48715546e2 | |||
| 00b59f6985 | |||
| d82d3e55cb | |||
| f2c3f9fe52 | |||
| 2de6c01262 | |||
| bbdc6f3aa9 | |||
| 16ee30b696 | |||
| 9c341c87d8 | |||
| 67a9134130 | |||
| fe6d2f04c5 | |||
| d138c99c61 | |||
| 290d6a8da5 | |||
| 09ed98c973 | |||
| bc7dee6a80 | |||
| 4c708baf63 | |||
| cc16fe85b0 | |||
| 7d63132c48 | |||
| 5acd704ae7 | |||
| 0c0948e8e1 | |||
| 6283384522 | |||
| b70fc6841f | |||
| 97dd84ed71 | |||
| 7a6981253b | |||
| 9e78ec221b | |||
| 4a8d7ca1c3 | |||
| b9f31c6f4b | |||
| cd3bed023a | |||
| 0ad6b2bc1b | |||
| 54b0c1bfcf | |||
| 285dd6a1c9 | |||
| 1c5e2843a1 | |||
| ce9b30767f | |||
| d26fa5bec1 | |||
| 832ca52ccf | |||
| c70176bfb2 | |||
| cb3cf57465 | |||
| dfaeb7b7de | |||
| d3818b5e44 | |||
| 5b8850404b | |||
| 38fa73cfb7 | |||
| 43fc050eed | |||
| f3423d45bd | |||
| 56866c1ac1 | |||
| 99ea6a59c5 | |||
| eb5ebf94a7 | |||
| bdf049d9e4 | |||
| 9205e076c5 | |||
| 60e5f6b41b | |||
| 3aa85c96b2 | |||
| 2f71d80c38 | |||
| 558a9f4cd0 | |||
| 9a6915a0ed | |||
| d0feca0d57 | |||
| 36e9f0bcde | |||
| a17fc1c76e | |||
| 84d8fb5339 | |||
| 2b9373e0fc | |||
| 2992d0db6b | |||
| 71b70712f8 | |||
| 10c7fc8e91 | |||
| 48971bb237 | |||
| 387b49a8b5 | |||
| bc9bacb08f | |||
| d44cf620c1 | |||
| 14cef8eb6c | |||
| 0bbe3e14c1 | |||
| 6df63d825a | |||
| 10e6436c34 | |||
| aa3ee802d2 | |||
| 9a16b1cda7 | |||
| ebbef901c1 | |||
| 1ef203ee07 | |||
| ca645ed23d | |||
| 742ed50960 | |||
| a60af4990a | |||
| d2890ecbba | |||
| 36d8158414 | |||
| 642afd6f34 | |||
| fad9c8f483 | |||
| 40a8fc50d9 | |||
| 21838afc0d | |||
| 8821c4edd7 | |||
| a265dd28dd | |||
| 14bc8a1732 | |||
| 10dd18a42a | |||
| 691f009656 | |||
| 68f1af090e | |||
| 6412778b98 | |||
| de12a2200e | |||
| 2600d6223c | |||
| 1ed1d8403d | |||
| 5e34d9e44d | |||
| 4f49c86d73 | |||
| 74309f8fa4 | |||
| 699c4301b4 | |||
| c7c90a9fa3 | |||
| e5d843b21f | |||
| 3ab943ab0b | |||
| e8d2aeb3a6 | |||
| 28220ea8b4 | |||
| 9f47a29b43 | |||
| 46bb39332f | |||
| e8bf83274f | |||
| 083bdad88f | |||
| 0e238ff2dd | |||
| d0cbfaed44 | |||
| 791dc59ba2 | |||
| 457197f85b | |||
| 07ee54af3a | |||
| 865777b7ba | |||
| 7b38ec3f8f | |||
| f8448d7d2f | |||
| ba638c1533 | |||
| 130901d7f7 | |||
| 07c3fd8941 | |||
| 2d98bbf4d6 | |||
| 08acd9714f | |||
| 57c3abf2e1 | |||
| 2f12fd8ae7 | |||
| 69ab1c1b8f | |||
| a2f4dc0b6c | |||
| 6d7ff7ea86 | |||
| 00d831e755 | |||
| 63d65a453c | |||
| 68e3bc932f | |||
| 6222998303 | |||
| 8d0678457e | |||
| c7c669b8d4 | |||
| e28cf3ebb5 | |||
| 4ea0256c56 | |||
| bf52b65dd5 | |||
| 6de9b87f16 | |||
| 2b48adfbef | |||
| 7f944ad4a1 | |||
| 50045432fa | |||
| cd4b700962 | |||
| b98934693c | |||
| e22fb7c6b7 | |||
| dfbe5c5210 | |||
| f3ed9a3452 | |||
| 57e35eeab1 | |||
| e3e2af46a1 | |||
| 3a30b891be | |||
| b69424983f | |||
| 37313183f5 | |||
| 86453b6873 | |||
| c1d62bdbc2 | |||
| bbe633ef2e | |||
| 201bfb922d | |||
| 9d1ebd38ce | |||
| 9dfcacf8a3 | |||
| 247b272986 | |||
| 072506c5d9 | |||
| 05bbc5d18f | |||
| e51ca61bfe | |||
| d3ad280731 | |||
| 85b043af37 | |||
| 0342594728 | |||
| 56e7484721 | |||
| cd61a530cb | |||
| f4c0e06b62 | |||
| b4d748d87f | |||
| 107c07915e | |||
| f493f005a9 | |||
| fbafbd0d52 | |||
| 9215da61a3 | |||
| 61428a5c8b | |||
| 77906fb58b | |||
| a79d021123 | |||
| d85f5d88cd | |||
| 518d63c08d | |||
| b254f0716b | |||
| 9e93a4cdce | |||
| 38f839fb60 | |||
| 09cee559eb | |||
| f64af6675b | |||
| 9d71a08841 | |||
| 321cc62ca0 | |||
| 92bf5c3be2 | |||
| 43db1fed84 | |||
| f81b76a975 | |||
| 81c16ec479 | |||
| 254da7e17b | |||
| 400739cd83 | |||
| 2f7655e1c1 | |||
| c3a6943b7e | |||
| fdc37c9f53 | |||
| c73246d7c6 | |||
| e03ae48ef6 | |||
| cd1cfdd5db | |||
| d87015836e | |||
| 71c01795f4 | |||
| 2291c89dbc | |||
| 1546304b4e | |||
| a0e6efb409 | |||
| bd18a6871c | |||
| 0f3f566d25 | |||
| 92451d1e28 | |||
| a0c2ed38e6 | |||
| 649e5a2cab | |||
| f2e51ef742 | |||
| cf4c27a74c | |||
| 4cff9f99cb | |||
| 741264ec48 | |||
| 9ad1be40b2 | |||
| 910d0fa59e | |||
| f54d5a68ff | |||
| a359350d7e | |||
| 7bef6b4089 | |||
| 8011e78e21 | |||
| 8a6fcd92ae | |||
| 3e33313bf0 | |||
| 6138291a8d | |||
| 6addf5a3b2 | |||
| 2ead0201ab | |||
| 56ad2370dc | |||
| 3157ceb88b | |||
| df2a2fe427 | |||
| c55ea59c4f | |||
| 9cb28e037d | |||
| 90eeb380ef | |||
| 9472a5c5d4 | |||
| d7884a9c8a | |||
| 3f10fbdf4d | |||
| c5ccc0ab34 | |||
| 664bd473c3 | |||
| 8ef0926614 | |||
| 2298d1bfaa | |||
| 08857dd143 | |||
| b26f7a5d2b | |||
| 4e997591dd | |||
| fad3972554 | |||
| 755f844294 | |||
| fd18da52a8 | |||
| cc78c3c36e | |||
| 75009f6816 | |||
| 59f82cea27 | |||
| 0da8d282fe | |||
| 6b4bd5ea28 | |||
| 93ceef0163 | |||
| eab0d656d3 | |||
| c2d99603a8 | |||
| f73b6b56a9 | |||
| b65eca7dcf | |||
| dec5826be8 | |||
| b2393d4715 | |||
| c86037e5d0 | |||
| d7751fb300 | |||
| 9582ea2e0a | |||
| d92b393f01 | |||
| ea26899735 | |||
| f8d807225f | |||
| 4c08609824 | |||
| ccb11a4ecf | |||
| 7f8ce68182 | |||
| edf936820a | |||
| c6ab274dcf | |||
| 4d0c1811a3 | |||
| ccb6f33b2f | |||
| 4484fd243e | |||
| 7f1cdae91a | |||
| b763009821 | |||
| f392c0c02b | |||
| 027086dd48 | |||
| 6eeca57694 | |||
| cc9ff2a2b0 | |||
| 507753b3dc | |||
| eaecb395cd | |||
| 6f5132633f | |||
| 1076289490 | |||
| 743f669b8c | |||
| c12fc4bd57 | |||
| 9ab82904e6 | |||
| 45df0954f4 | |||
| de685236a0 | |||
| 2aa8033a5f | |||
| 12b2fb6dfd | |||
| aa5eb3988d | |||
| 5efeb6ca50 | |||
| 18eaebb7fc | |||
| 9ed3dd4f22 | |||
| 51ecf1b54b | |||
| d1741c60dc | |||
| f62c844aaf | |||
| 409baf0321 | |||
| c3e37f7864 | |||
| 233a81c7d8 | |||
| aca67b997a | |||
| cddba3d35f | |||
| 14b0d1bd37 | |||
| 578162a266 | |||
| ab776d7fc8 | |||
| cd9f05b8e1 | |||
| 2bf978f845 | |||
| b89212bcbd | |||
| 5498694729 | |||
| 7b5bf2969a | |||
| e198c49a96 | |||
| 7f5811db9a | |||
| 5c3bb2293c | |||
| 59ac2061af | |||
| 905934cad2 | |||
| e89805cd17 | |||
| 680ab2c189 | |||
| 10095e3ce5 | |||
| a2b8e23eee | |||
| 0587c14af5 | |||
| 6a83e0ce6c | |||
| 72960aa963 | |||
| 5f4f047769 | |||
| a880ba254b | |||
| 4d75c3d97a | |||
| 90511ed765 | |||
| aa3b85511f | |||
| 5d90cbcc98 | |||
| 0525f99813 | |||
| 769019f2f5 | |||
| dcaba0f0ee | |||
| d33b6eec59 | |||
| 20aef83496 | |||
| 3cc4a1ea19 | |||
| a41fefa906 | |||
| c00bba3fcf | |||
| 63fab5899b | |||
| 357b6ef06e | |||
| 4fdf74fdbe | |||
| 15e09573d5 | |||
| d6479ca148 | |||
| cf9558f166 | |||
| 68bce9c8b7 | |||
| 913201b9cd | |||
| 3f748164e4 | |||
| ded5d94d69 | |||
| 815a8b52b6 | |||
| 639a4cfe50 | |||
| b2af4e8983 | |||
| ff39fc5d95 | |||
| 9fea007d4f | |||
| f44a094d1d | |||
| ec6f90eb44 | |||
| bbe583637f | |||
| 29eab151a1 | |||
| a7c5daf8a5 | |||
| a23dea03a9 | |||
| 45e5f3ecca | |||
| 8bcba8802f | |||
| 3e2e0ccc1c | |||
| c14d88f1ea | |||
| e72e847147 | |||
| 073879e523 | |||
| bf302f70f1 | |||
| a045eaa181 | |||
| b83b2ce0cc | |||
| 377aec7e07 | |||
| 9d50a6669a | |||
| bded6c9562 | |||
| 5520c74921 | |||
| 589c005bc4 | |||
| d64a213ec2 | |||
| 18c940962e | |||
| e01b1f35fc | |||
| 60030860e5 | |||
| 90894087e5 | |||
| bdcccbd894 | |||
| b64cf408fb | |||
| eaca5b9889 | |||
| 1c265b2073 | |||
| fa98ba86bc | |||
| 53aee9e651 | |||
| d4a305f5bb | |||
| fd39efe31f | |||
| 3b2f4b6f72 | |||
| 9a16942b16 | |||
| fe47d68fd3 | |||
| deaee833cf | |||
| 8d03881109 | |||
| e476adfdf5 | |||
| 4201aa7466 | |||
| a85d594c89 | |||
| 7b98cd3d50 | |||
| d256a0b647 | |||
| c87ba7f670 | |||
| e4e5df80f1 | |||
| 02f409451d | |||
| 9f2c7b90ce | |||
| 559c551752 | |||
| 304482cc9b | |||
| ad9db91812 | |||
| 1c7997e1ef | |||
| deefcaae9a | |||
| 562008f3c0 | |||
| 2584d62b28 | |||
| dc64193a62 | |||
| a7f8089ed8 | |||
| e8e63167d2 | |||
| c056191de1 | |||
| f2a597f698 | |||
| 7b637f976b | |||
| 39a378c517 | |||
| 0f9dfb9f8a | |||
| ab7f2fb1ec | |||
| a892c364c6 | |||
| a5c829fa96 | |||
| e844cf5970 | |||
| 999c6fd880 | |||
| 2aa4bdd5a6 | |||
| 05801f298f | |||
| 0fd1ec861b | |||
| 37d0473b7f | |||
| aaca46c485 | |||
| 30a6a1c1c2 | |||
| 2c39ac3015 | |||
| cc6a0dd8b3 | |||
| fbf62f0531 | |||
| c96b951895 | |||
| 34294341d7 | |||
| cdc8885e60 | |||
| 41416cd184 | |||
| 3c32246d9a | |||
| 6862d084ac | |||
| 6eb3626203 | |||
| 5f808eab5c | |||
| fe15c0b097 | |||
| e4fbe9d03c | |||
| de09d54c64 | |||
| 5bf117fc05 | |||
| f734797628 | |||
| 236470dc33 | |||
| 555627dad5 | |||
| 49c5ddd9f3 | |||
| a43ccaac64 | |||
| 91c02aec9a | |||
| 681d3d5520 | |||
| f945dc42fa | |||
| cc6f33b928 | |||
| 2f83e73139 | |||
| 53ccb96234 | |||
| a0d6139e50 | |||
| 90abadf7c4 | |||
| 7f1e959ece | |||
| 794df4d762 | |||
| d6b262a28e | |||
| 0cc518e523 | |||
| 8780dff794 | |||
| 0f881006e7 | |||
| 5d349ce042 | |||
| 940711878b | |||
| 75048efcf3 | |||
| 8cc5199d9b | |||
| 3f60bacd38 | |||
| 8fb705dde4 | |||
| 79777cd4ae | |||
| fabd1e3b64 | |||
| bcb6beef05 | |||
| 34336e4ade | |||
| a518e56cf1 | |||
| 6cc7655180 | |||
| 0a15aad6d7 | |||
| 1d8bee2856 | |||
| 6894d5828b | |||
| 35bc222552 | |||
| 16b5b6840f | |||
| 1a7837d740 | |||
| 607bfbe452 | |||
| c2b85bd6b8 | |||
| c3bc0ec645 | |||
| 89b5e8145d | |||
| 0edab7ed64 | |||
| c8a3814f6a | |||
| 9ddac508e2 | |||
| 3245f8f94c | |||
| 8be1f43c23 | |||
| e29e26605b | |||
| 7bd6c0c14d | |||
| d7c912386f | |||
| e7e86cae95 | |||
| b083ce87be | |||
| 17b90fc697 | |||
| 4fc59fa2ac | |||
| e87cda2e55 | |||
| 2c4d30b5ec | |||
| d0af645af8 | |||
| a1f79dc18a | |||
| ff65a697a9 | |||
| ef881b1392 | |||
| debea8fa5b | |||
| 8a9acbaeea | |||
| 8869ec7bca | |||
| dc0268736a | |||
| 6f9c2a846e | |||
| 3cb00840de | |||
| 6a2603a4ea | |||
| 69efecb2ef | |||
| 056e6d358e | |||
| 793baf0e0f | |||
| 721899258a | |||
| 4f9d84cd82 | |||
| a462180d3c | |||
| 58f2d87959 | |||
| a50b8e6373 | |||
| 4ec947d549 | |||
| 6751a74063 | |||
| 6118a18200 | |||
| d223d4be06 | |||
| ab7ec9bd74 | |||
| 7b70b5ec86 | |||
| db99043753 | |||
| 8f87e49606 | |||
| 5557107259 | |||
| 1b5c870798 | |||
| a5162651b7 | |||
| b9868512d6 | |||
| 8432d9c9ed | |||
| 5d4f94f218 | |||
| 7e9d5d99c7 | |||
| 487e64b09b | |||
| 5e350b810f | |||
| 5fb3a6be81 | |||
| dbec4b8f32 | |||
| f8b559bef1 | |||
| 7d9d0ce8b5 | |||
| 7857f123a4 | |||
| e3ba156fe1 | |||
| c824751682 | |||
| e5520437a5 | |||
| c6211fe48f | |||
| 54d6c9008d | |||
| 05e5edcce3 | |||
| 3249baccfa | |||
| 274682cf85 | |||
| 31a700f6a7 | |||
| 91a6fc32ef | |||
| 135b87a091 | |||
| 6b9484f611 | |||
| 7a612b701d | |||
| c69fb690f1 | |||
| 1ef73dd69d | |||
| 54afa1aec5 | |||
| 72c3c939e2 | |||
| 67d8e89556 | |||
| 07408813db | |||
| 436760a592 | |||
| 5c758df032 | |||
| d12a41bfa9 | |||
| 8ec22b6320 | |||
| 95d04467a8 | |||
| dd53de96fe | |||
| 8089334ea9 | |||
| 5bbb3678ed | |||
| 4e7ffe3140 | |||
| d2842484fd | |||
| a8932b5a72 | |||
| a283d1ee21 | |||
| d41d802d83 | |||
| 8a0efb3e40 | |||
| b013123669 | |||
| 264657d623 | |||
| 8ad4ee4341 | |||
| 664b21e5f1 | |||
| 163a7af328 | |||
| 4adeae6d85 | |||
| 9168803008 | |||
| 3f9c0d1b60 | |||
| cc35317b54 | |||
| 006070d08f | |||
| 19f6a98d31 | |||
| 301abbe155 | |||
| dab3a2d0a9 | |||
| 3a00e93dc3 | |||
| 4c4b0ce920 | |||
| b9259ff8b9 | |||
| e2ed37196f | |||
| b343d5d83a | |||
| 9bbff0d7a7 | |||
| 014fb5a633 | |||
| 76d8921bdf | |||
| b7f23a032c | |||
| 68be1ba2a1 | |||
| 5a6bb475c4 | |||
| 132e15cb9f | |||
| 07c0878d11 | |||
| b993479ada | |||
| 8dc568d52e | |||
| 44ee7cd3db | |||
| 2773cd8406 | |||
| 9bb26e0199 | |||
| 8d772074ba | |||
| 18c6ad0663 | |||
| 24a91acd0a | |||
| bec5241326 | |||
| 2b5365d774 | |||
| e377a9f05b | |||
| 2f53e94cc9 | |||
| 75770cd34b | |||
| a4860c3963 | |||
| 4b072ecbe5 | |||
| 3423f103fc | |||
| c3bb776149 | |||
| d31aac4d19 | |||
| 378e72ceb7 | |||
| d86be97ced | |||
| 44388b132a | |||
| cd6b112d33 | |||
| 8eb6be863a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
|||||||
|
/keep
|
||||||
result
|
result
|
||||||
|
result-*
|
||||||
/secrets/local.nix
|
/secrets/local.nix
|
||||||
|
/working
|
||||||
|
|||||||
34
README.md
34
README.md
@@ -4,17 +4,35 @@ this is the top-level repo from which i configure/deploy all my NixOS machines:
|
|||||||
- desktop
|
- desktop
|
||||||
- laptop
|
- laptop
|
||||||
- server
|
- server
|
||||||
- mobile phone
|
- mobile phone (Pinephone)
|
||||||
|
|
||||||
i enjoy a monorepo approach. this repo references [nixpkgs][nixpkgs], a couple 3rd party
|
everything outside of <./hosts/> and <./secrets/> is intended for export, to be importable for use by 3rd parties.
|
||||||
nix modules like [sops][sops], the sources for [uninsane.org][uninsane-org], and that's
|
the only hard dependency for my exported pkgs/modules should be [nixpkgs][nixpkgs].
|
||||||
about it. custom derivations and modules (some of which i try to upstream) live
|
building <./hosts/> will require [sops][sops].
|
||||||
directly here; even the sources for those packages is often kept here too.
|
|
||||||
|
you might specifically be interested in these files (elaborated further in #key-points-of-interest):
|
||||||
|
- [`sxmo-utils-latest`](./pkgs/additional/sxmo-utils/default.nix)
|
||||||
|
- [example SXMO deployment](./hosts/modules/gui/sxmo/default.nix)
|
||||||
|
- [my implementation of impermanence](./modules/persist/default.nix)
|
||||||
|
- my way of deploying dotfiles/configuring programs per-user:
|
||||||
|
- <./modules/fs/default.nix>
|
||||||
|
- <./modules/programs.nix>
|
||||||
|
- <./modules/users.nix>
|
||||||
|
|
||||||
[nixpkgs]: https://github.com/NixOS/nixpkgs
|
[nixpkgs]: https://github.com/NixOS/nixpkgs
|
||||||
[sops]: https://github.com/Mic92/sops-nix
|
[sops]: https://github.com/Mic92/sops-nix
|
||||||
[uninsane-org]: https://uninsane.org
|
[uninsane-org]: https://uninsane.org
|
||||||
|
|
||||||
|
## Using This Repo In Your Own Config
|
||||||
|
|
||||||
|
this should be a pretty "standard" flake. just reference it, and import either
|
||||||
|
- `nixosModules.sane` (for the modules)
|
||||||
|
- `overlays.pkgs` (for the packages)
|
||||||
|
|
||||||
|
or follow the instructions [here][NUR] to use it via the Nix User Repositories.
|
||||||
|
|
||||||
|
[NUR]: https://nur.nix-community.org/
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
- `doc/`
|
- `doc/`
|
||||||
- instructions for tasks i find myself doing semi-occasionally in this repo.
|
- instructions for tasks i find myself doing semi-occasionally in this repo.
|
||||||
@@ -90,12 +108,6 @@ them being factored out of my config, message me and we could work to make that
|
|||||||
|
|
||||||
[home-manager]: https://github.com/nix-community/home-manager
|
[home-manager]: https://github.com/nix-community/home-manager
|
||||||
|
|
||||||
## Using This Repo In Your Own Config
|
|
||||||
|
|
||||||
this should be a pretty "standard" flake. just reference it, and import either
|
|
||||||
- `nixosModules.sane` (for the modules)
|
|
||||||
- `overlays.pkgs` (for the packages)
|
|
||||||
|
|
||||||
## Mirrors
|
## Mirrors
|
||||||
|
|
||||||
this repo exists in a few known locations:
|
this repo exists in a few known locations:
|
||||||
|
|||||||
91
TODO.md
91
TODO.md
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
### roles
|
### roles
|
||||||
- allow any host to take the role of `uninsane.org`
|
- allow any host to take the role of `uninsane.org`
|
||||||
- will make it easier to test new services?
|
- will make it easier to test new services?
|
||||||
|
|
||||||
### upstreaming
|
### upstreaming
|
||||||
- split out a sxmo module usable by NUR consumers
|
- split out a sxmo module usable by NUR consumers
|
||||||
@@ -23,41 +23,78 @@
|
|||||||
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
- REVIEW/integrate jellyfin dataDir config: <https://github.com/NixOS/nixpkgs/pull/233617>
|
||||||
- remove `libsForQt5.callPackage` broadly: <https://github.com/NixOS/nixpkgs/issues/180841>
|
- remove `libsForQt5.callPackage` broadly: <https://github.com/NixOS/nixpkgs/issues/180841>
|
||||||
|
|
||||||
|
#### upstreaming to non-nixpkgs repos
|
||||||
|
- gtk: build schemas even on cross compilation: <https://github.com/NixOS/nixpkgs/pull/247844>
|
||||||
|
- sxmo: add new app entries
|
||||||
|
|
||||||
|
|
||||||
## IMPROVEMENTS:
|
## IMPROVEMENTS:
|
||||||
### security/resilience
|
### security/resilience
|
||||||
- validate duplicity backups!
|
- validate duplicity backups!
|
||||||
- encrypt more ~ dirs (~/archives, ~/records, ..?)
|
- encrypt more ~ dirs (~/archives, ~/records, ..?)
|
||||||
- best to do this after i know for sure i have good backups
|
- best to do this after i know for sure i have good backups
|
||||||
- have `sane.programs` be wrapped such that they run in a cgroup?
|
- have `sane.programs` be wrapped such that they run in a cgroup?
|
||||||
- at least, only give them access to the portion of the fs they *need*.
|
- at least, only give them access to the portion of the fs they *need*.
|
||||||
- Android takes approach of giving each app its own user: could hack that in here.
|
- Android takes approach of giving each app its own user: could hack that in here.
|
||||||
- **systemd-run** takes a command and runs it in a temporary scope (cgroup)
|
- **systemd-run** takes a command and runs it in a temporary scope (cgroup)
|
||||||
- presumably uses the same options as systemd services
|
- presumably uses the same options as systemd services
|
||||||
- see e.g. <https://github.com/NixOS/nixpkgs/issues/113903#issuecomment-857296349>
|
- see e.g. <https://github.com/NixOS/nixpkgs/issues/113903#issuecomment-857296349>
|
||||||
- flatpak does this, somehow
|
- flatpak does this, somehow
|
||||||
- apparmor? SElinux? (desktop) "portals"?
|
- apparmor? SElinux? (desktop) "portals"?
|
||||||
- see Spectrum OS; Alyssa Ross; etc
|
- see Spectrum OS; Alyssa Ross; etc
|
||||||
- bubblewrap-based sandboxing: <https://github.com/nixpak/nixpak>
|
- bubblewrap-based sandboxing: <https://github.com/nixpak/nixpak>
|
||||||
- canaries for important services
|
- canaries for important services
|
||||||
- e.g. daily email checks; daily backup checks
|
- e.g. daily email checks; daily backup checks
|
||||||
- integrate `nix check` into Gitea actions?
|
- integrate `nix check` into Gitea actions?
|
||||||
|
|
||||||
### user experience
|
### user experience
|
||||||
|
#### moby
|
||||||
|
- fix cpuidle (gets better power consumption): <https://xnux.eu/log/077.html>
|
||||||
|
- install apps:
|
||||||
|
- display QR codes for WiFi endpoints: <https://linuxphoneapps.org/apps/noappid.wisperwind.wifi2qr/>
|
||||||
|
- shopping list: <https://linuxphoneapps.org/apps/ro.hume.cosmin.shoppinglist/>
|
||||||
|
- offline Wikipedia
|
||||||
|
- SwayNC:
|
||||||
|
- don't show MPRIS if no players detected
|
||||||
|
- this is a problem of playerctld, i guess
|
||||||
|
- add option to change audio output
|
||||||
|
- fix colors (red alert) to match overall theme
|
||||||
|
- moby: tune GPS
|
||||||
|
- run only geoclue, and not gpsd, to save power?
|
||||||
|
- tune QGPS setting in eg25-control, for less jitter?
|
||||||
|
- direct mepo to prefer gpsd, with fallback to geoclue, for better accuracy?
|
||||||
|
- configure geoclue to do some smoothing?
|
||||||
|
- manually do smoothing, as some layer between mepo and geoclue/gpsd?
|
||||||
|
- moby: show battery state on ssh login
|
||||||
|
- moby: improve gPodder launch time
|
||||||
|
- sxmo: port to swaybar like i use on desktop
|
||||||
|
- users in #sxmo claim it's way better perf
|
||||||
|
- sxmo: fix youtube scripts (package youtube-cli)
|
||||||
|
- sxmo: don't put all deps on PATH
|
||||||
|
- maybe: use resholve to hard-code them
|
||||||
|
- this is the most "correct", but least patchable
|
||||||
|
- maybe: express each invocation as a function in sxmo_common.sh
|
||||||
|
- this will require some patching to handle `exec <foo>` style
|
||||||
|
- maybe: save original PATH and reset it before invoking user files
|
||||||
|
- moby: theme GTK apps (i.e. non-adwaita styles)
|
||||||
|
- combine multiple icon themes to get one which has the full icon set?
|
||||||
|
- get adwaita-icon-theme to ship everything even when cross-compiled?
|
||||||
|
- especially, make the menubar collapsible
|
||||||
|
- try Gradience tool specifically for theming adwaita? <https://linuxphoneapps.org/apps/com.github.gradienceteam.gradience/>
|
||||||
|
- phog: remove the gnome-shell runtime dependency to save hella closure size
|
||||||
|
|
||||||
|
#### non-moby
|
||||||
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
|
- neovim: set up language server (lsp; rnix-lsp; nvim-lspconfig)
|
||||||
- Helix: make copy-to-system clipboard be the default
|
- Helix: make copy-to-system clipboard be the default
|
||||||
- firefox/librewolf: persist history
|
- firefox/librewolf: persist history
|
||||||
- just not cookies or tabs
|
- just not cookies or tabs
|
||||||
- moby: improve gPodder launch time
|
|
||||||
- moby: theme GTK apps (i.e. non-adwaita styles)
|
|
||||||
- especially, make the menubar collapsible
|
|
||||||
- try Gradience tool specifically for theming adwaita? <https://linuxphoneapps.org/apps/com.github.gradienceteam.gradience/>
|
|
||||||
- package Nix/NixOS docs for Zeal
|
- package Nix/NixOS docs for Zeal
|
||||||
- install [doc-browser](https://github.com/qwfy/doc-browser)
|
- install [doc-browser](https://github.com/qwfy/doc-browser)
|
||||||
- this supports both dash (zeal) *and* the datasets from <https://devdocs.io> (which includes nix!)
|
- this supports both dash (zeal) *and* the datasets from <https://devdocs.io> (which includes nix!)
|
||||||
- install [devhelp](https://wiki.gnome.org/Apps/Devhelp) (gnome)
|
- install [devhelp](https://wiki.gnome.org/Apps/Devhelp) (gnome)
|
||||||
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
|
- have xdg-open parse `<repo:...> URIs (or adjust them so that it _can_ parse)
|
||||||
- sane-bt-search: show details like 5.1 vs stereo, h264 vs h265
|
- sane-bt-search: show details like 5.1 vs stereo, h264 vs h265
|
||||||
|
- maybe just color these "keywords" in all search results?
|
||||||
- uninsane.org: make URLs relative to allow local use (and as offline homepage)
|
- uninsane.org: make URLs relative to allow local use (and as offline homepage)
|
||||||
- email: fix so that local mail doesn't go to junk
|
- email: fix so that local mail doesn't go to junk
|
||||||
- git sendmail flow adds the DKIM signatures, but gets delivered locally w/o having the sig checked, so goes into Junk
|
- git sendmail flow adds the DKIM signatures, but gets delivered locally w/o having the sig checked, so goes into Junk
|
||||||
@@ -65,17 +102,19 @@
|
|||||||
|
|
||||||
### perf
|
### perf
|
||||||
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
- add `pkgs.impure-cached.<foo>` package set to build things with ccache enabled
|
||||||
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
- every package here can be auto-generated, and marked with some env var so that it doesn't pollute the pure package set
|
||||||
- would be super handy for package prototyping!
|
- would be super handy for package prototyping!
|
||||||
- why does nixos-rebuild switch take 5 minutes when net is flakey?
|
- why does nixos-rebuild switch take 5 minutes when net is flakey?
|
||||||
- trying to auto-mount servo?
|
- trying to auto-mount servo?
|
||||||
- something to do with systemd services restarting/stalling
|
- something to do with systemd services restarting/stalling
|
||||||
- maybe wireguard & its refresh operation, specifically?
|
- maybe wireguard & its refresh operation, specifically?
|
||||||
- get moby to build without binfmt emulation (i.e. make all emulation explicit)
|
- get moby to build without binfmt emulation (i.e. make all emulation explicit)
|
||||||
- then i can distribute builds across servo + desko, and also allow servo to pull packages from desko w/o worrying about purity
|
- then i can distribute builds across servo + desko, and also allow servo to pull packages from desko w/o worrying about purity
|
||||||
|
|
||||||
|
|
||||||
## NEW FEATURES:
|
## NEW FEATURES:
|
||||||
- migrate MAME cabinet to nix
|
- migrate MAME cabinet to nix
|
||||||
- boot it from PXE from servo?
|
- boot it from PXE from servo?
|
||||||
|
- deploy to new server, and use it as a remote builder
|
||||||
- enable IPv6
|
- enable IPv6
|
||||||
|
- package lemonade lemmy app: <https://linuxphoneapps.org/apps/ml.mdwalters.lemonade/>
|
||||||
|
|||||||
9
default.nix
Normal file
9
default.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# limited, non-flake interface to this repo.
|
||||||
|
# this file exposes the same view into `pkgs` which the flake would see when evaluated.
|
||||||
|
#
|
||||||
|
# the primary purpose of this file is so i can run `updateScript`s which expect
|
||||||
|
# the root to be `default.nix`
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.appendOverlays [
|
||||||
|
(import ./overlays/all.nix)
|
||||||
|
]
|
||||||
66
flake.lock
generated
66
flake.lock
generated
@@ -21,11 +21,11 @@
|
|||||||
"mobile-nixos": {
|
"mobile-nixos": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690059310,
|
"lastModified": 1696124168,
|
||||||
"narHash": "sha256-4zcoDp8wwZVfGSzXltC5x+eH4kDWC/eJpyQNgr7shAA=",
|
"narHash": "sha256-EzGHYAR7rozQQLZEHbKEcb5VpUFGoxwEsM0OWfW4wqU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "mobile-nixos",
|
"repo": "mobile-nixos",
|
||||||
"rev": "56fc9f9619f305f0865354975a98d22410eed127",
|
"rev": "7cee346c3f8e73b25b1cfbf7a086a7652c11e0f3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -34,46 +34,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nix-serve": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1687251388,
|
|
||||||
"narHash": "sha256-E9cVlgeCvzPbA/G3mCDCzz8TdRwXyGYzIjmwcvIfghg=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "nix-serve",
|
|
||||||
"rev": "d6df5bd8584f37e22cff627db2fc4058a4aab5ee",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "nix-serve",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1606086654,
|
|
||||||
"narHash": "sha256-VFl+3eGIMqNp7cyOMJ6TjM/+UcsLKtodKoYexrlTJMI=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "19db3e5ea2777daa874563b5986288151f502e27",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"id": "nixpkgs",
|
|
||||||
"ref": "nixos-20.09",
|
|
||||||
"type": "indirect"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690066826,
|
"lastModified": 1696123266,
|
||||||
"narHash": "sha256-6L2qb+Zc0BFkh72OS9uuX637gniOjzU6qCDBpjB2LGY=",
|
"narHash": "sha256-S6MZEneQeE4M/E/C8SMnr7B7oBnjH/hbm96Kak5hAAI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ce45b591975d070044ca24e3003c830d26fea1c8",
|
"rev": "dbe90e63a36762f1fbde546e26a84af774a32455",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -85,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unpatched": {
|
"nixpkgs-unpatched": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691006197,
|
"lastModified": 1696375444,
|
||||||
"narHash": "sha256-DbtxVWPt+ZP5W0Usg7jAyTomIM//c3Jtfa59Ht7AV8s=",
|
"narHash": "sha256-Sv0ICt/pXfpnFhTGYTsX6lUr1SljnuXWejYTI2ZqHa4=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "66aedfd010204949cb225cf749be08cb13ce1813",
|
"rev": "81e8f48ebdecf07aab321182011b067aafc78896",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -102,7 +69,6 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"mobile-nixos": "mobile-nixos",
|
"mobile-nixos": "mobile-nixos",
|
||||||
"nix-serve": "nix-serve",
|
|
||||||
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
"nixpkgs-unpatched": "nixpkgs-unpatched",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"uninsane-dot-org": "uninsane-dot-org"
|
"uninsane-dot-org": "uninsane-dot-org"
|
||||||
@@ -116,11 +82,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1690199016,
|
"lastModified": 1696320910,
|
||||||
"narHash": "sha256-yTLL72q6aqGmzHq+C3rDp3rIjno7EJZkFLof6Ika7cE=",
|
"narHash": "sha256-fbuEc6wylH+0VxG48lhPBK+SQJHfo2lusUwWHZNipIM=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "c36df4fe4bf4bb87759b1891cab21e7a05219500",
|
"rev": "746c7fa1a64c1671a4bf287737c27fdc7101c4c2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -152,11 +118,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691106178,
|
"lastModified": 1696306988,
|
||||||
"narHash": "sha256-3mZ9gTvMpbZA9ea9ovoQpn2wKuQY0QZ7MDdEjArYdAQ=",
|
"narHash": "sha256-I/OyJxIxu0n5h1eFqwVw0C6wTN3ewBXp2lGAdo1ur70=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "f4d91aa201b6e49af690f250d4786bd1d8b4dcfd",
|
"rev": "1f588493031168d92a1e60705f26aaf4b2cdc07e",
|
||||||
"revCount": 205,
|
"revCount": 208,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.uninsane.org/colin/uninsane"
|
"url": "https://git.uninsane.org/colin/uninsane"
|
||||||
},
|
},
|
||||||
|
|||||||
170
flake.nix
170
flake.nix
@@ -4,6 +4,8 @@
|
|||||||
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
|
# - this is marginally the case with schemes like `github:nixos/nixpkgs`.
|
||||||
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
|
# - given the *existing* `git+https://` scheme, i propose expressing github URLs similarly:
|
||||||
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
|
# - `github+https://github.com/nixos/nixpkgs/tree/nixos-22.11`
|
||||||
|
# - this would allow for the same optimizations as today's `github:nixos/nixpkgs`, but without obscuring the source.
|
||||||
|
# a code reader could view the source being referenced simply by clicking the https:// portion of that URI.
|
||||||
# - need some way to apply local patches to inputs.
|
# - need some way to apply local patches to inputs.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -46,24 +48,23 @@
|
|||||||
|
|
||||||
mobile-nixos = {
|
mobile-nixos = {
|
||||||
# <https://github.com/nixos/mobile-nixos>
|
# <https://github.com/nixos/mobile-nixos>
|
||||||
|
# only used for building disk images, not relevant after deployment
|
||||||
url = "github:nixos/mobile-nixos";
|
url = "github:nixos/mobile-nixos";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
sops-nix = {
|
sops-nix = {
|
||||||
# <https://github.com/Mic92/sops-nix>
|
# <https://github.com/Mic92/sops-nix>
|
||||||
|
# used to distribute secrets to my hosts
|
||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
# inputs.nixpkgs.follows = "nixpkgs";
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
};
|
};
|
||||||
uninsane-dot-org = {
|
uninsane-dot-org = {
|
||||||
|
# provides the package to deploy <https://uninsane.org>, used only when building the servo host
|
||||||
url = "git+https://git.uninsane.org/colin/uninsane";
|
url = "git+https://git.uninsane.org/colin/uninsane";
|
||||||
# inputs.nixpkgs.follows = "nixpkgs";
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
inputs.nixpkgs.follows = "nixpkgs-unpatched";
|
||||||
};
|
};
|
||||||
nix-serve = {
|
|
||||||
# <https://github.com/edolstra/nix-serve>
|
|
||||||
url = "github:edolstra/nix-serve";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -72,21 +73,37 @@
|
|||||||
mobile-nixos,
|
mobile-nixos,
|
||||||
sops-nix,
|
sops-nix,
|
||||||
uninsane-dot-org,
|
uninsane-dot-org,
|
||||||
nix-serve,
|
|
||||||
...
|
...
|
||||||
}@inputs:
|
}@inputs:
|
||||||
let
|
let
|
||||||
inherit (builtins) attrNames elem listToAttrs map mapAttrs;
|
inherit (builtins) attrNames elem listToAttrs map mapAttrs;
|
||||||
|
# redefine some nixpkgs `lib` functions to avoid the infinite recursion
|
||||||
|
# of if we tried to use patched `nixpkgs.lib` as part of the patching process.
|
||||||
mapAttrs' = f: set:
|
mapAttrs' = f: set:
|
||||||
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
|
||||||
|
optionalAttrs = cond: attrs: if cond then attrs else {};
|
||||||
# mapAttrs but without the `name` argument
|
# mapAttrs but without the `name` argument
|
||||||
mapAttrValues = f: mapAttrs (_: f);
|
mapAttrValues = f: mapAttrs (_: f);
|
||||||
|
|
||||||
# rather than apply our nixpkgs patches as a flake input, do that here instead.
|
# rather than apply our nixpkgs patches as a flake input, do that here instead.
|
||||||
# this (temporarily?) resolves the bad UX wherein a subflake residing in the same git
|
# this (temporarily?) resolves the bad UX wherein a subflake residing in the same git
|
||||||
# repo as the main flake causes the main flake to have an unstable hash.
|
# repo as the main flake causes the main flake to have an unstable hash.
|
||||||
nixpkgs = (import ./nixpatches/flake.nix).outputs {
|
nixpkgs = (import ./nixpatches/flake.nix).outputs {
|
||||||
self = nixpkgs;
|
self = nixpkgs;
|
||||||
nixpkgs = nixpkgs-unpatched;
|
nixpkgs = nixpkgs-unpatched;
|
||||||
|
} // {
|
||||||
|
# provide values that nixpkgs ordinarily sources from the flake.lock file,
|
||||||
|
# inaccessible to it here because of the import-from-derivation.
|
||||||
|
# rev and shortRev seem to not always exist (e.g. if the working tree is dirty),
|
||||||
|
# so those are made conditional.
|
||||||
|
#
|
||||||
|
# these values impact the name of a produced nixos system. having date/rev in the
|
||||||
|
# `readlink /run/current-system` store path helps debuggability.
|
||||||
|
inherit (self) lastModifiedDate lastModified;
|
||||||
|
} // optionalAttrs (self ? rev) {
|
||||||
|
inherit (self) rev;
|
||||||
|
} // optionalAttrs (self ? shortRev) {
|
||||||
|
inherit (self) shortRev;
|
||||||
};
|
};
|
||||||
|
|
||||||
nixpkgsCompiledBy = system: nixpkgs.legacyPackages."${system}";
|
nixpkgsCompiledBy = system: nixpkgs.legacyPackages."${system}";
|
||||||
@@ -184,17 +201,9 @@
|
|||||||
let
|
let
|
||||||
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
mobile = (import "${mobile-nixos}/overlay/overlay.nix");
|
||||||
uninsane = uninsane-dot-org.overlay;
|
uninsane = uninsane-dot-org.overlay;
|
||||||
# nix-serve' = nix-serve.overlay;
|
|
||||||
nix-serve' = next: prev: {
|
|
||||||
# XXX(2023/03/02): upstream isn't compatible with modern `nix`. probably the perl bindings.
|
|
||||||
# - we use the package built against `nixpkgs` specified in its flake rather than use its overlay,
|
|
||||||
# to get around this.
|
|
||||||
inherit (nix-serve.packages."${next.system}") nix-serve;
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
(mobile final prev)
|
(mobile final prev)
|
||||||
// (uninsane final prev)
|
// (uninsane final prev)
|
||||||
// (nix-serve' final prev)
|
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,6 +253,7 @@
|
|||||||
apps."x86_64-linux" =
|
apps."x86_64-linux" =
|
||||||
let
|
let
|
||||||
pkgs = self.legacyPackages."x86_64-linux";
|
pkgs = self.legacyPackages."x86_64-linux";
|
||||||
|
sanePkgs = import ./pkgs { inherit pkgs; };
|
||||||
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
deployScript = host: addr: action: pkgs.writeShellScript "deploy-${host}" ''
|
||||||
nix build '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} $@
|
nix build '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} $@
|
||||||
sudo nix sign-paths -r -k /run/secrets/nix_serve_privkey $(readlink ./result-${host})
|
sudo nix sign-paths -r -k /run/secrets/nix_serve_privkey $(readlink ./result-${host})
|
||||||
@@ -253,6 +263,59 @@
|
|||||||
# let the user handle that edge case by re-running this whole command
|
# let the user handle that edge case by re-running this whole command
|
||||||
nixos-rebuild --flake '.#${host}' ${action} --target-host colin@${addr} --use-remote-sudo $@
|
nixos-rebuild --flake '.#${host}' ${action} --target-host colin@${addr} --use-remote-sudo $@
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# pkg updating.
|
||||||
|
# a cleaner alternative lives here: <https://discourse.nixos.org/t/how-can-i-run-the-updatescript-of-personal-packages/25274/2>
|
||||||
|
mkUpdater = attrPath: {
|
||||||
|
type = "app";
|
||||||
|
program = let
|
||||||
|
pkg = pkgs.lib.getAttrFromPath attrPath sanePkgs;
|
||||||
|
strAttrPath = pkgs.lib.concatStringsSep "." attrPath;
|
||||||
|
commandArgv = pkg.updateScript.command or pkg.updateScript;
|
||||||
|
command = pkgs.lib.escapeShellArgs commandArgv;
|
||||||
|
in builtins.toString (pkgs.writeShellScript "update-${strAttrPath}" ''
|
||||||
|
export UPDATE_NIX_NAME=${pkg.name}
|
||||||
|
export UPDATE_NIX_PNAME=${pkg.pname}
|
||||||
|
export UPDATE_NIX_OLD_VERSION=${pkg.version}
|
||||||
|
export UPDATE_NIX_ATTR_PATH=${strAttrPath}
|
||||||
|
${command}
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
mkUpdatersNoAliases = opts: basePath: pkgs.lib.concatMapAttrs
|
||||||
|
(name: pkg:
|
||||||
|
if pkg.recurseForDerivations or false then {
|
||||||
|
"${name}" = mkUpdaters opts (basePath ++ [ name ]);
|
||||||
|
} else if pkg.updateScript or null != null then {
|
||||||
|
"${name}" = mkUpdater (basePath ++ [ name ]);
|
||||||
|
} else {}
|
||||||
|
)
|
||||||
|
(pkgs.lib.getAttrFromPath basePath sanePkgs);
|
||||||
|
mkUpdaters = { ignore ? [] }@opts: basePath:
|
||||||
|
let
|
||||||
|
updaters = mkUpdatersNoAliases opts basePath;
|
||||||
|
invokeUpdater = name: pkg:
|
||||||
|
let
|
||||||
|
fullPath = basePath ++ [ name ];
|
||||||
|
doUpdateByDefault = !builtins.elem fullPath ignore;
|
||||||
|
|
||||||
|
# in case `name` has a `.` in it, we have to quote it
|
||||||
|
escapedPath = builtins.map (p: ''"${p}"'') fullPath;
|
||||||
|
updatePath = builtins.concatStringsSep "." ([ "update" "pkgs" ] ++ escapedPath);
|
||||||
|
in pkgs.lib.optionalString doUpdateByDefault (
|
||||||
|
pkgs.lib.escapeShellArgs [
|
||||||
|
"nix" "run" ".#${updatePath}"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
in {
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript
|
||||||
|
(builtins.concatStringsSep "-" (["update"] ++ basePath))
|
||||||
|
(builtins.concatStringsSep
|
||||||
|
"\n"
|
||||||
|
(pkgs.lib.mapAttrsToList invokeUpdater updaters)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} // updaters;
|
||||||
in {
|
in {
|
||||||
help = {
|
help = {
|
||||||
type = "app";
|
type = "app";
|
||||||
@@ -261,24 +324,28 @@
|
|||||||
commands:
|
commands:
|
||||||
- `nix run '.#help'`
|
- `nix run '.#help'`
|
||||||
- show this message
|
- show this message
|
||||||
- `nix run '.#update-feeds'`
|
- `nix run '.#update.pkgs'`
|
||||||
|
- updates every package
|
||||||
|
- `nix run '.#update.feeds'`
|
||||||
- updates metadata for all feeds
|
- updates metadata for all feeds
|
||||||
- `nix run '.#init-feed' <url>`
|
- `nix run '.#init-feed' <url>`
|
||||||
- `nix run '.#deploy-{lappy,moby,moby-test,servo}' [nixos-rebuild args ...]`
|
- `nix run '.#deploy-{lappy,moby,moby-test,servo}' [nixos-rebuild args ...]`
|
||||||
- `nix run '.#check-nur'`
|
- `nix run '.#check'`
|
||||||
|
- make sure all systems build; NUR evaluates
|
||||||
'';
|
'';
|
||||||
in builtins.toString (pkgs.writeShellScript "nixos-config-help" ''
|
in builtins.toString (pkgs.writeShellScript "nixos-config-help" ''
|
||||||
cat ${helpMsg}
|
cat ${helpMsg}
|
||||||
|
echo ""
|
||||||
|
echo "complete flake structure:"
|
||||||
|
nix flake show --option allow-import-from-derivation true
|
||||||
'');
|
'');
|
||||||
};
|
};
|
||||||
update-feeds = {
|
update.pkgs = mkUpdaters { ignore = [ ["feeds"] ]; } [];
|
||||||
type = "app";
|
update.feeds = mkUpdaters {} [ "feeds" ];
|
||||||
program = "${pkgs.feeds.updateScript}";
|
|
||||||
};
|
|
||||||
|
|
||||||
init-feed = {
|
init-feed = {
|
||||||
type = "app";
|
type = "app";
|
||||||
program = "${pkgs.feeds.initFeedScript}";
|
program = "${pkgs.feeds.init-feed}";
|
||||||
};
|
};
|
||||||
|
|
||||||
deploy-lappy = {
|
deploy-lappy = {
|
||||||
@@ -298,7 +365,40 @@
|
|||||||
program = ''${deployScript "servo" "servo" "switch"}'';
|
program = ''${deployScript "servo" "servo" "switch"}'';
|
||||||
};
|
};
|
||||||
|
|
||||||
check-nur = {
|
sync-moby = {
|
||||||
|
# copy music from the current device to moby
|
||||||
|
# TODO: should i actually sync from /mnt/servo-media/Music instead of the local drive?
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "sync-to-moby" ''
|
||||||
|
sudo mount /mnt/moby-home
|
||||||
|
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music ~/Music /mnt/moby-home/Music
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
sync-lappy = {
|
||||||
|
# copy music from servo to lappy
|
||||||
|
# can run this from any device that has ssh access to lappy
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "sync-to-lappy" ''
|
||||||
|
sudo mount /mnt/lappy-home
|
||||||
|
${pkgs.sane-scripts.sync-music}/bin/sane-sync-music /mnt/servo-media/Music /mnt/lappy-home/Music
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
check = {
|
||||||
|
type = "app";
|
||||||
|
program = builtins.toString (pkgs.writeShellScript "check-all" ''
|
||||||
|
nix run '.#check.nur'
|
||||||
|
RC0=$?
|
||||||
|
nix run '.#check.host-configs'
|
||||||
|
RC1=$?
|
||||||
|
echo "nur: $RC0"
|
||||||
|
echo "host-configs: $RC1"
|
||||||
|
exit $(($RC0 | $RC1))
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
|
||||||
|
check.nur = {
|
||||||
# `nix run '.#check-nur'`
|
# `nix run '.#check-nur'`
|
||||||
# validates that my repo can be included in the Nix User Repository
|
# validates that my repo can be included in the Nix User Repository
|
||||||
type = "app";
|
type = "app";
|
||||||
@@ -310,9 +410,35 @@
|
|||||||
--option allow-import-from-derivation true \
|
--option allow-import-from-derivation true \
|
||||||
--drv-path --show-trace \
|
--drv-path --show-trace \
|
||||||
-I nixpkgs=$(nix-instantiate --find-file nixpkgs) \
|
-I nixpkgs=$(nix-instantiate --find-file nixpkgs) \
|
||||||
-I ../../
|
-I ../../ \
|
||||||
|
| tee # tee to prevent interactive mode
|
||||||
'');
|
'');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check.host-configs = {
|
||||||
|
type = "app";
|
||||||
|
program = let
|
||||||
|
checkHost = host: ''
|
||||||
|
nix build '.#nixosConfigurations.${host}.config.system.build.toplevel' --out-link ./result-${host} -j2 $@
|
||||||
|
RC_${host}=$?
|
||||||
|
'';
|
||||||
|
in builtins.toString (pkgs.writeShellScript
|
||||||
|
"check-host-configs"
|
||||||
|
''
|
||||||
|
${checkHost "desko"}
|
||||||
|
${checkHost "lappy"}
|
||||||
|
${checkHost "servo"}
|
||||||
|
${checkHost "moby"}
|
||||||
|
${checkHost "rescue"}
|
||||||
|
echo "desko: $RC_desko"
|
||||||
|
echo "lappy: $RC_lappy"
|
||||||
|
echo "servo: $RC_servo"
|
||||||
|
echo "moby: $RC_moby"
|
||||||
|
echo "rescue: $RC_rescue"
|
||||||
|
exit $(($RC_desko | $RC_lappy | $RC_servo | $RC_moby | $RC_rescue))
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
templates = {
|
templates = {
|
||||||
|
|||||||
@@ -3,13 +3,15 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.gui.sxmo = {
|
sane.gui.sxmo = {
|
||||||
greeter = "sway";
|
greeter = "greetd-sway-gtkgreet";
|
||||||
|
noidle = true; #< power button requires 1s hold, which makes it impractical to be dealing with.
|
||||||
settings = {
|
settings = {
|
||||||
# XXX: make sure the user is part of the `input` group!
|
# XXX: make sure the user is part of the `input` group!
|
||||||
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-id/usb-Wacom_Co._Ltd._Pen_and_multitouch_sensor-event-if00";
|
SXMO_LISGD_INPUT_DEVICE = "/dev/input/by-id/usb-Wacom_Co._Ltd._Pen_and_multitouch_sensor-event-if00";
|
||||||
# these identifiers are from `swaymsg -t get_inputs`
|
# these identifiers are from `swaymsg -t get_inputs`
|
||||||
SXMO_VOLUME_BUTTON = "1:1:AT_Translated_Set_2_keyboard";
|
SXMO_VOLUME_BUTTON = "1:1:AT_Translated_Set_2_keyboard";
|
||||||
# SXMO_VOLUME_BUTTON = "none";
|
# SXMO_VOLUME_BUTTON = "none";
|
||||||
|
# N.B.: thinkpad's power button requires a full second press to do anything
|
||||||
SXMO_POWER_BUTTON = "0:1:Power_Button";
|
SXMO_POWER_BUTTON = "0:1:Power_Button";
|
||||||
# SXMO_POWER_BUTTON = "none";
|
# SXMO_POWER_BUTTON = "none";
|
||||||
SXMO_DISABLE_LEDS = "1";
|
SXMO_DISABLE_LEDS = "1";
|
||||||
@@ -27,8 +29,11 @@
|
|||||||
# - SXMO_SWAY_SCALE
|
# - SXMO_SWAY_SCALE
|
||||||
# see <repo:mil/sxmo-utils:scripts/deviceprofiles>
|
# see <repo:mil/sxmo-utils:scripts/deviceprofiles>
|
||||||
# SXMO_DEVICE_NAME = "pine64,pinephone-1.2";
|
# SXMO_DEVICE_NAME = "pine64,pinephone-1.2";
|
||||||
|
# if sxmo doesn't know the device, it can't decide whether to use one_button or three_button mode
|
||||||
|
# and so it just wouldn't handle any button inputs (sxmo_hook_inputhandler.sh not on path)
|
||||||
|
SXMO_DEVICE_NAME = "three_button_touchscreen";
|
||||||
};
|
};
|
||||||
package = pkgs.sxmo-utils.overrideAttrs (base: {
|
package = (pkgs.sxmo-utils-latest.override { preferSystemd = true; }).overrideAttrs (base: {
|
||||||
postPatch = (base.postPatch or "") + ''
|
postPatch = (base.postPatch or "") + ''
|
||||||
# after volume-button navigation mode, restore full keyboard functionality
|
# after volume-button navigation mode, restore full keyboard functionality
|
||||||
cp ${./xkb_mobile_normal_buttons} ./configs/xkb/xkb_mobile_normal_buttons
|
cp ${./xkb_mobile_normal_buttons} ./configs/xkb/xkb_mobile_normal_buttons
|
||||||
|
|||||||
@@ -23,6 +23,12 @@
|
|||||||
sane.zsh.showDeadlines = false; # unlikely to act on them when in shell
|
sane.zsh.showDeadlines = false; # unlikely to act on them when in shell
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
||||||
|
sane.wowlan.enable = true;
|
||||||
|
sane.wowlan.patterns = [
|
||||||
|
{ ipv4.destPort = 22; } # wake on SSH
|
||||||
|
{ ipv4.srcPort = 2587; } # wake on `ntfy-sh` push from servo
|
||||||
|
{ arp.queryIp = [ 10 78 79 54 ]; } # wake when somebody is doing an ARP query against us
|
||||||
|
];
|
||||||
|
|
||||||
# XXX colin: phosh doesn't work well with passwordless login,
|
# XXX colin: phosh doesn't work well with passwordless login,
|
||||||
# so set this more reliable default password should anything go wrong
|
# so set this more reliable default password should anything go wrong
|
||||||
@@ -31,25 +37,53 @@
|
|||||||
|
|
||||||
sops.secrets.colin-passwd.neededForUsers = true;
|
sops.secrets.colin-passwd.neededForUsers = true;
|
||||||
|
|
||||||
sane.user.persist.plaintext = [
|
|
||||||
# TODO: make this just generally conditional upon pulse being enabled?
|
|
||||||
".config/pulse" # persist pulseaudio volume
|
|
||||||
];
|
|
||||||
|
|
||||||
sane.gui.sxmo.enable = true;
|
sane.gui.sxmo.enable = true;
|
||||||
sane.programs.guiApps.suggestedPrograms = [ "handheldGuiApps" ];
|
sane.programs.guiApps.suggestedPrograms = [ "handheldGuiApps" ];
|
||||||
# sane.programs.consoleUtils.enableFor.user.colin = false;
|
# sane.programs.consoleUtils.enableFor.user.colin = false;
|
||||||
# sane.programs.guiApps.enableFor.user.colin = false;
|
# sane.programs.guiApps.enableFor.user.colin = false;
|
||||||
|
sane.programs.blueberry.enableFor.user.colin = false; # bluetooth manager: doesn't cross compile!
|
||||||
sane.programs.sequoia.enableFor.user.colin = false;
|
sane.programs.sequoia.enableFor.user.colin = false;
|
||||||
sane.programs.tuiApps.enableFor.user.colin = false; # visidata, others, don't compile well
|
sane.programs.tuiApps.enableFor.user.colin = false; # visidata, others, don't compile well
|
||||||
# disabled for faster deploys
|
# disabled for faster deploys
|
||||||
sane.programs.soundconverter.enableFor.user.colin = false;
|
sane.programs.soundconverter.enableFor.user.colin = false;
|
||||||
|
sane.programs.eg25-control.enableFor.user.colin = true;
|
||||||
|
|
||||||
# sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
sane.programs.ntfy-sh.config.autostart = true;
|
||||||
|
sane.programs.dino.config.autostart = true;
|
||||||
|
# sane.programs.calls.config.autostart = true;
|
||||||
|
|
||||||
|
sane.programs.firefox.mime.priority = 300; # prefer other browsers when possible
|
||||||
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
# HACK/TODO: make `programs.P.env.VAR` behave according to `mime.priority`
|
||||||
# sane.programs.firefox.env = lib.mkForce {};
|
sane.programs.firefox.env = lib.mkForce {};
|
||||||
# sane.programs.epiphany.env.BROWSER = "epiphany";
|
sane.programs.epiphany.env.BROWSER = "epiphany";
|
||||||
# sane.programs.firefox.enableFor.user.colin = false; # use epiphany instead
|
sane.programs.firefox.enableFor.user.colin = false; # use epiphany instead
|
||||||
|
|
||||||
|
# note the .conf.d approach: using ~/.config/pipewire/pipewire.conf directly breaks all audio,
|
||||||
|
# presumably because that deletes the defaults entirely whereas the .conf.d approach selectively overrides defaults
|
||||||
|
sane.user.fs.".config/pipewire/pipewire.conf.d/10-fix-dino-mic-cutout.conf".symlink.text = ''
|
||||||
|
# config docs: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire#properties>
|
||||||
|
# useful to run `pw-top` to see that these settings are actually having effect,
|
||||||
|
# and `pw-metadata` to see if any settings conflict (e.g. max-quantum < min-quantum)
|
||||||
|
#
|
||||||
|
# restart pipewire after editing these files:
|
||||||
|
# - `systemctl --user restart pipewire`
|
||||||
|
# - pipewire users will likely stop outputting audio until they are also restarted
|
||||||
|
#
|
||||||
|
# there's seemingly two buffers for the mic (see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>)
|
||||||
|
# 1. Pipewire buffering out of the driver and into its own member.
|
||||||
|
# 2. Pipewire buffering into Dino.
|
||||||
|
# the latter is fixed at 10ms by Dino, difficult to override via runtime config.
|
||||||
|
# the former defaults low (e.g. 512 samples)
|
||||||
|
# this default configuration causes the mic to regularly drop out entirely for a couple seconds at a time during a call,
|
||||||
|
# presumably because the system can't keep up (pw-top shows incrementing counter in ERR column).
|
||||||
|
# `pw-metadata -n settings 0 clock.force-quantum 1024` reduces to about 1 error per second.
|
||||||
|
# `pw-metadata -n settings 0 clock.force-quantum 2048` reduces to 1 error every < 10s.
|
||||||
|
# pipewire default config includes `clock.power-of-two-quantum = true`
|
||||||
|
context.properties = {
|
||||||
|
default.clock.min-quantum = 2048
|
||||||
|
default.clock.max-quantum = 8192
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
# sane.programs.mpv.enableFor.user.colin = true;
|
# sane.programs.mpv.enableFor.user.colin = true;
|
||||||
|
|
||||||
@@ -72,12 +106,16 @@
|
|||||||
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
# `cat /proc/meminfo` to see CmaTotal/CmaFree if interested in tuning this.
|
||||||
boot.kernelParams = [ "cma=512M" ];
|
boot.kernelParams = [ "cma=512M" ];
|
||||||
|
|
||||||
|
# hardware.firmware makes the referenced files visible to the kernel, for whenever a driver explicitly asks for them.
|
||||||
|
# these files are visible from userspace by following `/sys/module/firmware_class/parameters/path`
|
||||||
|
#
|
||||||
# mobile-nixos' /lib/firmware includes:
|
# mobile-nixos' /lib/firmware includes:
|
||||||
# rtl_bt (bluetooth)
|
# rtl_bt (bluetooth)
|
||||||
# anx7688-fw.bin (USB-C -> HDMI bridge)
|
# anx7688-fw.bin (USB-C chip: power negotiation, HDMI/dock)
|
||||||
# ov5640_af.bin (camera module)
|
# ov5640_af.bin (camera module)
|
||||||
# hardware.firmware = [ config.mobile.device.firmware ];
|
# hardware.firmware = [ config.mobile.device.firmware ];
|
||||||
hardware.firmware = [ pkgs.rtl8723cs-firmware ];
|
# hardware.firmware = [ pkgs.rtl8723cs-firmware ];
|
||||||
|
hardware.firmware = [ pkgs.linux-firmware-megous ];
|
||||||
|
|
||||||
system.stateVersion = "21.11";
|
system.stateVersion = "21.11";
|
||||||
|
|
||||||
@@ -96,15 +134,11 @@
|
|||||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
||||||
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
|
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
|
||||||
systemd =
|
systemd = let
|
||||||
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
||||||
in {
|
in {
|
||||||
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||||
|
|
||||||
# pulseaudio
|
|
||||||
user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
|
||||||
services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
|
||||||
|
|
||||||
# pipewire
|
# pipewire
|
||||||
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
@@ -112,6 +146,19 @@
|
|||||||
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
|
|
||||||
|
# pulseaudio
|
||||||
|
# user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
|
# services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: move elsewhere...
|
||||||
|
services.ModemManager.serviceConfig = {
|
||||||
|
# N.B.: the extra "" in ExecStart serves to force upstream ExecStart to be ignored
|
||||||
|
ExecStart = [ "" "${pkgs.modemmanager}/bin/ModemManager --debug" ];
|
||||||
|
# --debug sets DEBUG level logging: so reset
|
||||||
|
ExecStartPost = [ "${pkgs.modemmanager}/bin/mmcli --set-logging=INFO" ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.udev.extraRules = let
|
services.udev.extraRules = let
|
||||||
@@ -125,39 +172,4 @@
|
|||||||
# make Pinephone front LEDs writable by user.
|
# make Pinephone front LEDs writable by user.
|
||||||
SUBSYSTEM=="leds", DEVPATH=="*/*:indicator", RUN+="${chmod} g+w /sys%p/brightness", RUN+="${chown} :video /sys%p/brightness"
|
SUBSYSTEM=="leds", DEVPATH=="*/*:indicator", RUN+="${chmod} g+w /sys%p/brightness", RUN+="${chown} :video /sys%p/brightness"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
hardware.opengl.driSupport = true;
|
|
||||||
|
|
||||||
services.xserver.displayManager.job.preStart = let
|
|
||||||
dmesg = "${pkgs.util-linux}/bin/dmesg";
|
|
||||||
grep = "${pkgs.gnugrep}/bin/grep";
|
|
||||||
modprobe = "${pkgs.kmod}/bin/modprobe";
|
|
||||||
in ''
|
|
||||||
# common boot failure:
|
|
||||||
# blank screen (no backlight even), with the following log:
|
|
||||||
# ```syslog
|
|
||||||
# sun8i-dw-hdmi 1ee0000.hdmi: Couldn't get the HDMI PHY
|
|
||||||
# ...
|
|
||||||
# sun4i-drm display-engine: Couldn't bind all pipelines components
|
|
||||||
# ...
|
|
||||||
# sun8i-dw-hdmi: probe of 1ee0000.hdmi failed with error -17
|
|
||||||
# ```
|
|
||||||
#
|
|
||||||
# in particular, that `probe ... failed` occurs *only* on failed boots
|
|
||||||
# (the other messages might sometimes occur even on successful runs?)
|
|
||||||
#
|
|
||||||
# reloading the sun8i hdmi driver usually gets the screen on, showing boot text.
|
|
||||||
# then restarting display-manager.service gets us to the login.
|
|
||||||
#
|
|
||||||
# NB: the above log is default level. though less specific, there's a `err` level message that also signals this:
|
|
||||||
# sun4i-drm display-engine: failed to bind 1ee0000.hdmi (ops sun8i_dw_hdmi_ops [sun8i_drm_hdmi]): -17
|
|
||||||
|
|
||||||
if (${dmesg} --kernel --level err --color=never --notime | ${grep} -q 'sun4i-drm display-engine: failed to bind 1ee0000.hdmi')
|
|
||||||
then
|
|
||||||
echo "reprobing sun8i_drm_hdmi"
|
|
||||||
# if a command here fails it errors the whole service, so prefer to log instead
|
|
||||||
${modprobe} -r sun8i_drm_hdmi || echo "failed to unload sun8i_drm_hdmi"
|
|
||||||
${modprobe} sun8i_drm_hdmi || echo "failed to load sub8i_drm_hdmi"
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,63 @@
|
|||||||
# - `screen /dev/ttyUSB2 115200`
|
# - `screen /dev/ttyUSB2 115200`
|
||||||
# - `AT+QGPSCFG="nmeasrc",1`
|
# - `AT+QGPSCFG="nmeasrc",1`
|
||||||
# - `AT+QGPS=1`
|
# - `AT+QGPS=1`
|
||||||
|
# this process is automated by my `eg25-control` program and services (`eg25-control-powered`, `eg25-control-gps`)
|
||||||
|
# - see the `modules/` directory further up this repository.
|
||||||
#
|
#
|
||||||
# now, something like `gpsd` can directly read from /dev/ttyUSB1.
|
# now, something like `gpsd` can directly read from /dev/ttyUSB1,
|
||||||
|
# or geoclue can query the GPS directly through modem-manager
|
||||||
#
|
#
|
||||||
# initial GPS fix can take 15+ minutes.
|
# initial GPS fix can take 15+ minutes.
|
||||||
# meanwhile, services like eg25-manager can speed this up by uploading assisted GPS data to the modem.
|
# meanwhile, services like eg25-manager or eg25-control-freshen-agps can speed this up by uploading assisted GPS data to the modem.
|
||||||
#
|
#
|
||||||
# geoclue somehow fits in here as a geospatial provider that leverages GPS and also other sources like radio towers
|
# support/help:
|
||||||
|
# - geoclue, gnome-maps
|
||||||
|
# - irc: #gnome-maps on irc.gimp.org
|
||||||
|
# - Matrix: #gnome-maps:gnome.org (unclear if bridged to IRC)
|
||||||
|
#
|
||||||
|
# programs to pair this with:
|
||||||
|
# - `satellite-gtk`: <https://codeberg.org/tpikonen/satellite>
|
||||||
|
# - shows/tracks which satellites the GPS is connected to; useful to understand fix characteristics
|
||||||
|
# - `gnome-maps`: uses geoclue, has route planning
|
||||||
|
# - `mepo`: uses gpsd, minimalist, flaky, and buttons are kinda hard to activate on mobile
|
||||||
|
# - puremaps?
|
||||||
|
# - osmin?
|
||||||
|
#
|
||||||
|
# known/outstanding bugs:
|
||||||
|
# - `systemctl start eg25-control-gps` can the hang the whole system (2023/10/06)
|
||||||
|
# - i think it's actually `eg25-control-powered` which does this (started by the gps)
|
||||||
|
# - best guess is modem draws so much power at launch that other parts of the system see undervoltage
|
||||||
|
# - workaround is to hard power-cycle the system. the modem may not bring up after reboot: leave unpowered for 60s and boot again.
|
||||||
|
#
|
||||||
|
# future work:
|
||||||
|
# - integrate with [wigle](https://www.wigle.net/) for offline equivalent to Mozilla Location Services
|
||||||
|
|
||||||
{ ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
|
# test gpsd with `gpspipe -w -n 10 2> /dev/null | grep -m 1 TPV | jq '.lat, .lon' | tr '\n' ' '`
|
||||||
|
# ^ should return <lat> <long>
|
||||||
services.gpsd.enable = true;
|
services.gpsd.enable = true;
|
||||||
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
services.gpsd.devices = [ "/dev/ttyUSB1" ];
|
||||||
|
|
||||||
# TODO: enable eg25-manager, and bring online both the modem and GPS on boot
|
# test geoclue2 by building `geoclue2-with-demo-agent`
|
||||||
|
# and running "${geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/where-am-i"
|
||||||
|
# note that geoclue is dbus-activated, and auto-stops after 60s with no caller
|
||||||
|
services.geoclue2.enable = true;
|
||||||
|
services.geoclue2.appConfig.where-am-i = {
|
||||||
|
# this is the default "agent", shipped by geoclue package: allow it to use location
|
||||||
|
isAllowed = true;
|
||||||
|
isSystem = false;
|
||||||
|
# XXX: setting users != [] might be causing `where-am-i` to time out
|
||||||
|
users = [
|
||||||
|
# restrict to only one set of users. empty array (default) means "allow any user to access geolocation".
|
||||||
|
(builtins.toString config.users.users.colin.uid)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
systemd.services.geoclue.after = lib.mkForce []; #< defaults to network-online, but not all my sources require network
|
||||||
|
users.users.geoclue.extraGroups = [
|
||||||
|
"dialout" # TODO: figure out if dialout is required. that's for /dev/ttyUSB1, but geoclue probably doesn't read that?
|
||||||
|
];
|
||||||
|
|
||||||
|
sane.services.eg25-control.enable = true;
|
||||||
|
sane.programs.where-am-i.enableFor.user.colin = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,56 @@
|
|||||||
{ lib, pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
let
|
let
|
||||||
# use the last commit on the 5.18 branch (5.18.14)
|
dmesg = "${pkgs.util-linux}/bin/dmesg";
|
||||||
# manjaro's changes between kernel patch versions tend to be minimal if any.
|
grep = "${pkgs.gnugrep}/bin/grep";
|
||||||
manjaroBase = "https://gitlab.manjaro.org/manjaro-arm/packages/core/linux/-/raw/25bd828cd47b1c6e09fcbcf394a649b89d2876dd";
|
modprobe = "${pkgs.kmod}/bin/modprobe";
|
||||||
manjaroPatch = name: sha256: {
|
ensureHWReady = ''
|
||||||
inherit name;
|
# common boot failure:
|
||||||
patch = pkgs.fetchpatch {
|
# blank screen (no backlight even), with the following log:
|
||||||
inherit name;
|
# ```syslog
|
||||||
url = "${manjaroBase}/${name}?inline=false";
|
# sun8i-dw-hdmi 1ee0000.hdmi: Couldn't get the HDMI PHY
|
||||||
inherit sha256;
|
# ...
|
||||||
};
|
# sun4i-drm display-engine: Couldn't bind all pipelines components
|
||||||
};
|
# ...
|
||||||
|
# sun8i-dw-hdmi: probe of 1ee0000.hdmi failed with error -17
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# in particular, that `probe ... failed` occurs *only* on failed boots
|
||||||
|
# (the other messages might sometimes occur even on successful runs?)
|
||||||
|
#
|
||||||
|
# reloading the sun8i hdmi driver usually gets the screen on, showing boot text.
|
||||||
|
# then restarting display-manager.service gets us to the login.
|
||||||
|
#
|
||||||
|
# NB: the above log is default level. though less specific, there's a `err` level message that also signals this:
|
||||||
|
# sun4i-drm display-engine: failed to bind 1ee0000.hdmi (ops sun8i_dw_hdmi_ops [sun8i_drm_hdmi]): -17
|
||||||
|
# NB: this is the most common, but not the only, failure mode for `display-manager`.
|
||||||
|
# another error seems characterized by these dmesg logs, in which reprobing sun8i_drm_hdmi does not fix:
|
||||||
|
# ```syslog
|
||||||
|
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't get the MIPI D-PHY
|
||||||
|
# sun4i-drm display-engine: Couldn't bind all pipelines components
|
||||||
|
# sun6i-mipi-dsi 1ca0000.dsi: Couldn't register our component
|
||||||
|
# ```
|
||||||
|
|
||||||
# the idea for patching off Manjaro's kernel comes from jakewaksbaum:
|
if (${dmesg} --kernel --level err --color=never --notime | ${grep} -q 'sun4i-drm display-engine: failed to bind 1ee0000.hdmi')
|
||||||
# - https://git.sr.ht/~jakewaksbaum/pi/tree/af20aae5653545d6e67a459b59ee3e1ca8a680b0/item/kernel/default.nix
|
then
|
||||||
# - he later abandoned this, i think because he's using the Pinephone Pro which received mainline support.
|
echo "reprobing sun8i_drm_hdmi"
|
||||||
manjaroPatches = [
|
# if a command here fails it errors the whole service, so prefer to log instead
|
||||||
(manjaroPatch
|
${modprobe} -r sun8i_drm_hdmi || echo "failed to unload sun8i_drm_hdmi"
|
||||||
"1001-arm64-dts-allwinner-add-hdmi-sound-to-pine-devices.patch"
|
${modprobe} sun8i_drm_hdmi || echo "failed to load sub8i_drm_hdmi"
|
||||||
"sha256-DApd791A+AxB28Ven/MVAyuyVphdo8KQDx8O7oxVPnc="
|
fi
|
||||||
)
|
'';
|
||||||
# these patches below are critical to enable wifi (RTL8723CS)
|
|
||||||
# - the alternative is a wholly forked kernel by megi/megous:
|
|
||||||
# - https://xnux.eu/howtos/build-pinephone-kernel.html#toc-how-to-build-megi-s-pinehpone-kernel
|
|
||||||
# - i don't know if these patches are based on megi's or original
|
|
||||||
(manjaroPatch
|
|
||||||
"2001-Bluetooth-Add-new-quirk-for-broken-local-ext-features.patch"
|
|
||||||
"sha256-CExhJuUWivegxPdnzKINEsKrMFx/m/1kOZFmlZ2SEOc="
|
|
||||||
)
|
|
||||||
(manjaroPatch
|
|
||||||
"2002-Bluetooth-btrtl-add-support-for-the-RTL8723CS.patch"
|
|
||||||
"sha256-dDdvOphTcP/Aog93HyH+L9m55laTgtjndPSE4/rnzUA="
|
|
||||||
)
|
|
||||||
(manjaroPatch
|
|
||||||
"2004-arm64-dts-allwinner-enable-bluetooth-pinetab-pinepho.patch"
|
|
||||||
"sha256-o43P3WzXyHK1PF+Kdter4asuyGAEKO6wf5ixcco2kCQ="
|
|
||||||
)
|
|
||||||
# XXX: this one has a Makefile, which hardcodes /sbin/depmod:
|
|
||||||
# - drivers/staging/rtl8723cs/Makefile
|
|
||||||
# - not sure if this is problematic?
|
|
||||||
(manjaroPatch
|
|
||||||
"2005-staging-add-rtl8723cs-driver.patch"
|
|
||||||
"sha256-6ywm3dQQ5JYl60CLKarxlSUukwi4QzqctCj3tVgzFbo="
|
|
||||||
)
|
|
||||||
];
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# use Megi's kernel:
|
|
||||||
# even with the Manjaro patches, stock 5.18 has a few issues on Pinephone:
|
|
||||||
# - no battery charging
|
|
||||||
# - phone rotation sensor is off by 90 degrees
|
|
||||||
# - ambient light sensor causes screen brightness to be shakey
|
|
||||||
# - phosh greeter may not appear after wake from sleep
|
|
||||||
boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-megous;
|
boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-megous;
|
||||||
|
# boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux-manjaro;
|
||||||
|
# boot.kernelPackages = pkgs.linuxPackagesFor pkgs.linux_latest;
|
||||||
|
|
||||||
# alternatively, use nixos' kernel and add the stuff we want:
|
# alternatively, apply patches directly to stock nixos kernel:
|
||||||
# # cross-compilation optimization:
|
|
||||||
# boot.kernelPackages =
|
|
||||||
# let p = (import nixpkgs { localSystem = "x86_64-linux"; });
|
|
||||||
# in p.pkgsCross.aarch64-multiplatform.linuxPackages_5_18;
|
|
||||||
# # non-cross:
|
|
||||||
# # boot.kernelPackages = pkgs.linuxPackages_5_18;
|
|
||||||
|
|
||||||
# boot.kernelPatches = manjaroPatches ++ [
|
# boot.kernelPatches = manjaroPatches ++ [
|
||||||
# (patchDefconfig kernelConfig)
|
# (patchDefconfig kernelConfig)
|
||||||
# ];
|
# ];
|
||||||
|
|
||||||
|
# configure nixos to build a compressed kernel image, since it doesn't usually do that for aarch64 target.
|
||||||
|
# without this i run out of /boot space in < 10 generations
|
||||||
nixpkgs.hostPlatform.linux-kernel = {
|
nixpkgs.hostPlatform.linux-kernel = {
|
||||||
# defaults:
|
# defaults:
|
||||||
name = "aarch64-multiplatform";
|
name = "aarch64-multiplatform";
|
||||||
@@ -80,4 +65,7 @@ in
|
|||||||
target = "Image.gz"; # <-- compress the kernel image
|
target = "Image.gz"; # <-- compress the kernel image
|
||||||
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
# target = "zImage"; # <-- confuses other parts of nixos :-(
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.xserver.displayManager.job.preStart = ensureHWReady;
|
||||||
|
systemd.services.greetd.preStart = ensureHWReady;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,6 @@
|
|||||||
# - <https://itsfoss.com/content/images/2023/04/nixos-tutorials.png>
|
# - <https://itsfoss.com/content/images/2023/04/nixos-tutorials.png>
|
||||||
|
|
||||||
{ lib, pkgs, sane-lib, ... }:
|
{ lib, pkgs, sane-lib, ... }:
|
||||||
let
|
|
||||||
# TODO: generate this from the .svg
|
|
||||||
# bg = ./nixos-bg-02.png;
|
|
||||||
bg = pkgs.runCommand "nixos-bg.png" { nativeBuildInputs = [ pkgs.inkscape ]; } ''
|
|
||||||
inkscape ${./nixos-bg-02.svg} -o $out
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
sane.programs.firefox.config = {
|
sane.programs.firefox.config = {
|
||||||
# compromise impermanence for the sake of usability
|
# compromise impermanence for the sake of usability
|
||||||
@@ -27,6 +20,9 @@ in
|
|||||||
# sidebery UX doesn't make sense on small screen
|
# sidebery UX doesn't make sense on small screen
|
||||||
addons.sidebery.enable = false;
|
addons.sidebery.enable = false;
|
||||||
};
|
};
|
||||||
|
sane.programs.swaynotificationcenter.config = {
|
||||||
|
backlight = "backlight"; # /sys/class/backlight/*backlight*/brightness
|
||||||
|
};
|
||||||
|
|
||||||
sane.gui.sxmo = {
|
sane.gui.sxmo = {
|
||||||
nogesture = true;
|
nogesture = true;
|
||||||
@@ -111,7 +107,7 @@ in
|
|||||||
"--center --margin 45"
|
"--center --margin 45"
|
||||||
"--no-spacing"
|
"--no-spacing"
|
||||||
# XXX: font size doesn't seem to take effect (would prefer larger)
|
# XXX: font size doesn't seem to take effect (would prefer larger)
|
||||||
"--fn 'Sxmo 14' --line-height 22 --border 3"
|
"--fn 'monospace 14' --line-height 22 --border 3"
|
||||||
"--bdr '${accent0}'" # border
|
"--bdr '${accent0}'" # border
|
||||||
"--scf '${accent2}' --scb '${accent0}'" # scrollbar
|
"--scf '${accent2}' --scb '${accent0}'" # scrollbar
|
||||||
"--tb '${accent0}' --tf '${fg0}'" # title
|
"--tb '${accent0}' --tf '${fg0}'" # title
|
||||||
@@ -123,6 +119,8 @@ in
|
|||||||
];
|
];
|
||||||
DEFAULT_COUNTRY = "US";
|
DEFAULT_COUNTRY = "US";
|
||||||
|
|
||||||
|
SXMO_AUTOROTATE = "1"; # enable auto-rotation at launch. has no meaning in stock/upstream sxmo-utils
|
||||||
|
|
||||||
# BEMENU lines (wayland DMENU):
|
# BEMENU lines (wayland DMENU):
|
||||||
# - camera is 9th entry
|
# - camera is 9th entry
|
||||||
# - flashlight is 10th entry
|
# - flashlight is 10th entry
|
||||||
@@ -134,7 +132,6 @@ in
|
|||||||
# - close is 16th entry
|
# - close is 16th entry
|
||||||
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
|
SXMO_BEMENU_LANDSCAPE_LINES = "11"; # default 8
|
||||||
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
|
SXMO_BEMENU_PORTRAIT_LINES = "16"; # default 16
|
||||||
SXMO_BG_IMG = "${bg}";
|
|
||||||
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
|
SXMO_LOCK_IDLE_TIME = "15"; # how long between screenoff -> lock -> back to screenoff (default: 8)
|
||||||
# gravity: how far to tilt the device before the screen rotates
|
# gravity: how far to tilt the device before the screen rotates
|
||||||
# for a given setting, normal <-> invert requires more movement then left <-> right
|
# for a given setting, normal <-> invert requires more movement then left <-> right
|
||||||
@@ -167,13 +164,5 @@ in
|
|||||||
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
|
WVKBD_LANDSCAPE_LAYERS = "landscape,special,emoji";
|
||||||
WVKBD_LAYERS = "full,special,emoji";
|
WVKBD_LAYERS = "full,special,emoji";
|
||||||
};
|
};
|
||||||
package = pkgs.sxmo-utils.overrideAttrs (base: {
|
|
||||||
postPatch = (base.postPatch or "") + ''
|
|
||||||
cat <<EOF >> ./configs/default_hooks/sxmo_hook_start.sh
|
|
||||||
# rotate UI based on physical display angle by default
|
|
||||||
sxmo_daemons.sh start autorotate sxmo_autorotate.sh
|
|
||||||
EOF
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
];
|
];
|
||||||
sane.services.dyn-dns.enable = true;
|
sane.services.dyn-dns.enable = true;
|
||||||
sane.services.wg-home.enable = true;
|
sane.services.wg-home.enable = true;
|
||||||
sane.services.wg-home.enableWan = true;
|
sane.services.wg-home.visibleToWan = true;
|
||||||
|
sane.services.wg-home.forwardToWan = true;
|
||||||
|
sane.services.wg-home.routeThroughServo = false;
|
||||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||||
sane.nixcache.substituters.servo = false;
|
sane.nixcache.substituters.servo = false;
|
||||||
sane.nixcache.substituters.desko = false;
|
sane.nixcache.substituters.desko = false;
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.root-on-tmpfs = true;
|
sane.persist.root-on-tmpfs = true;
|
||||||
|
# increase /tmp space (defaults to 50% of RAM) for building large nix things.
|
||||||
|
# even the stock `nixpkgs.linux` consumes > 16 GB of tmp
|
||||||
|
fileSystems."/tmp".options = [ "size=32G" ];
|
||||||
|
|
||||||
fileSystems."/nix" = {
|
fileSystems."/nix" = {
|
||||||
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
|
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
|
||||||
@@ -37,6 +40,34 @@
|
|||||||
# TODO: this is overly broad; only need media and share directories to be persisted
|
# TODO: this is overly broad; only need media and share directories to be persisted
|
||||||
{ user = "colin"; group = "users"; path = "/var/lib/uninsane"; }
|
{ user = "colin"; group = "users"; path = "/var/lib/uninsane"; }
|
||||||
];
|
];
|
||||||
|
# force some problematic directories to always get correct permissions:
|
||||||
|
sane.fs."/var/lib/uninsane/media".dir.acl = {
|
||||||
|
user = "colin"; group = "media"; mode = "0775";
|
||||||
|
};
|
||||||
|
sane.fs."/var/lib/uninsane/media/archive".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/archive/README.md".file.text = ''
|
||||||
|
this directory is for media i wish to remove from my library,
|
||||||
|
but keep for a short time in case i reverse my decision.
|
||||||
|
treat it like a system trash can.
|
||||||
|
'';
|
||||||
|
sane.fs."/var/lib/uninsane/media/Books".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Books/Audiobooks".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Books/Books".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Books/Visual".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/collections".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/datasets".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/freeleech".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Music".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Pictures".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Videos".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Videos/Film".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Videos/Shows".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/media/Videos/Talks".dir = {};
|
||||||
|
sane.fs."/var/lib/uninsane/datasets/README.md".file.text = ''
|
||||||
|
this directory may seem redundant with ../media/datasets. it isn't.
|
||||||
|
this directory exists on SSD, allowing for speedy access to specific datasets when necessary.
|
||||||
|
the contents should be a subset of what's in ../media/datasets.
|
||||||
|
'';
|
||||||
# make sure large media is stored to the HDD
|
# make sure large media is stored to the HDD
|
||||||
sane.persist.sys.ext = [
|
sane.persist.sys.ext = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
./email
|
./email
|
||||||
./ejabberd.nix
|
./ejabberd.nix
|
||||||
./freshrss.nix
|
./freshrss.nix
|
||||||
./ftp
|
./export
|
||||||
./gitea.nix
|
./gitea.nix
|
||||||
./goaccess.nix
|
./goaccess.nix
|
||||||
./ipfs.nix
|
./ipfs.nix
|
||||||
@@ -18,9 +18,9 @@
|
|||||||
./lemmy.nix
|
./lemmy.nix
|
||||||
./matrix
|
./matrix
|
||||||
./navidrome.nix
|
./navidrome.nix
|
||||||
./nfs.nix
|
|
||||||
./nixserve.nix
|
|
||||||
./nginx.nix
|
./nginx.nix
|
||||||
|
./nixserve.nix
|
||||||
|
./ntfy.nix
|
||||||
./pict-rs.nix
|
./pict-rs.nix
|
||||||
./pleroma.nix
|
./pleroma.nix
|
||||||
./postgres.nix
|
./postgres.nix
|
||||||
|
|||||||
@@ -14,76 +14,105 @@
|
|||||||
#
|
#
|
||||||
# compliance tests:
|
# compliance tests:
|
||||||
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
|
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
|
||||||
|
#
|
||||||
|
# administration:
|
||||||
|
# - `sudo -u ejabberd ejabberdctl help`
|
||||||
|
#
|
||||||
|
# federation/support matrix:
|
||||||
|
# - avatars
|
||||||
|
# - nixnet.services + dino: works in MUCs but not DMs (as of 2023 H1)
|
||||||
|
# - movim.eu + dino: works in DMs, MUCs untested (as of 2023/08/29)
|
||||||
|
# - calls
|
||||||
|
# - local + dino: audio, video, works in DMs (as of 2023/08/29)
|
||||||
|
# - movim.eu + dino: audio, video, works in DMs, no matter which side initiates (as of 2023/08/30)
|
||||||
|
# - +native-cell-number@cheogram.com + dino: audio works in DMs, no matter which side initiates (as of 2023/09/01)
|
||||||
|
# - can receive calls even if sender isn't in my roster
|
||||||
|
# - this is presumably using JMP.chat's SIP servers, which then convert it to XMPP call
|
||||||
|
#
|
||||||
|
# bugs:
|
||||||
|
# - 2023/09/01: will randomly stop federating. `systemctl restart ejabberd` fixes, but takes 10 minutes.
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
# XXX: avatar support works in MUCs but not DMs
|
let
|
||||||
# lib.mkIf false
|
# TODO: this range could be larger, but right now that's costly because each element is its own UPnP forward
|
||||||
|
# TURN port range (inclusive)
|
||||||
|
turnPortLow = 49152;
|
||||||
|
turnPortHigh = 49167;
|
||||||
|
turnPortRange = lib.range turnPortLow turnPortHigh;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.plaintext = [
|
||||||
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; }
|
{ user = "ejabberd"; group = "ejabberd"; path = "/var/lib/ejabberd"; }
|
||||||
];
|
];
|
||||||
sane.ports.ports."3478" = {
|
sane.ports.ports = lib.mkMerge ([
|
||||||
protocol = [ "tcp" "udp" ];
|
{
|
||||||
visibleTo.lan = true;
|
"3478" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" "udp" ];
|
||||||
description = "colin-xmpp-stun-turn";
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5222" = {
|
description = "colin-xmpp-stun-turn";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5222" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpp-client-to-server";
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5223" = {
|
description = "colin-xmpp-client-to-server";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5223" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5269" = {
|
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.wan = true;
|
"5269" = {
|
||||||
description = "colin-xmpp-server-to-server";
|
protocol = [ "tcp" ];
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5270" = {
|
description = "colin-xmpp-server-to-server";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.wan = true;
|
"5270" = {
|
||||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
protocol = [ "tcp" ];
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5280" = {
|
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5280" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpp-bosh";
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5281" = {
|
description = "colin-xmpp-bosh";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5281" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpp-bosh-https";
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5349" = {
|
description = "colin-xmpp-bosh-https";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5349" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpp-stun-turn-over-tls";
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
sane.ports.ports."5443" = {
|
description = "colin-xmpp-stun-turn-over-tls";
|
||||||
protocol = [ "tcp" ];
|
};
|
||||||
visibleTo.lan = true;
|
"5443" = {
|
||||||
visibleTo.wan = true;
|
protocol = [ "tcp" ];
|
||||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
visibleTo.lan = true;
|
||||||
};
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||||
# TODO: forward these TURN ports!
|
};
|
||||||
networking.firewall.allowedTCPPortRanges = [{
|
}
|
||||||
from = 49152; # TURN
|
] ++ (builtins.map
|
||||||
to = 49408;
|
(port: {
|
||||||
}];
|
"${builtins.toString port}" = let
|
||||||
networking.firewall.allowedUDPPortRanges = [{
|
count = port - turnPortLow + 1;
|
||||||
from = 49152; # TURN
|
numPorts = turnPortHigh - turnPortLow + 1;
|
||||||
to = 49408;
|
in {
|
||||||
}];
|
protocol = [ "tcp" "udp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-xmpp-turn-${builtins.toString count}-of-${builtins.toString numPorts}";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
turnPortRange
|
||||||
|
));
|
||||||
|
|
||||||
# provide access to certs
|
# provide access to certs
|
||||||
# TODO: this should just be `acme`. then we also add nginx to the `acme` group.
|
# TODO: this should just be `acme`. then we also add nginx to the `acme` group.
|
||||||
@@ -150,284 +179,285 @@
|
|||||||
services.ejabberd.enable = true;
|
services.ejabberd.enable = true;
|
||||||
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
|
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
|
||||||
systemd.services.ejabberd.preStart = let
|
systemd.services.ejabberd.preStart = let
|
||||||
config-in = pkgs.writeTextFile {
|
config-in = pkgs.writeText "ejabberd.yaml.in" (lib.generators.toYAML {} {
|
||||||
name = "ejabberd.yaml.in";
|
hosts = [ "uninsane.org" ];
|
||||||
text = ''
|
# none | emergency | alert | critical | error | warning | notice | info | debug
|
||||||
hosts:
|
loglevel = "debug";
|
||||||
- uninsane.org
|
acme.auto = false;
|
||||||
|
certfiles = [ "/var/lib/acme/uninsane.org/full.pem" ];
|
||||||
|
# ca_file = "${pkgs.cacert.unbundled}/etc/ssl/certs/";
|
||||||
|
# ca_file = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
|
|
||||||
# none | emergency | alert | critical | error | warning | notice | info | debug
|
pam_userinfotype = "jid";
|
||||||
loglevel: debug
|
acl = {
|
||||||
# loglevel: info
|
admin.user = [ "colin@uninsane.org" ];
|
||||||
# loglevel: notice
|
local.user_regexp = "";
|
||||||
|
loopback.ip = [ "127.0.0.0/8" "::1/128" ];
|
||||||
|
};
|
||||||
|
|
||||||
acme:
|
access_rules = {
|
||||||
auto: false
|
local.allow = "local";
|
||||||
certfiles:
|
c2s_access.allow = "all";
|
||||||
- /var/lib/acme/uninsane.org/full.pem
|
announce.allow = "admin";
|
||||||
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
|
configure.allow = "admin";
|
||||||
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
|
muc_create.allow = "local";
|
||||||
|
pubsub_createnode_access.allow = "all";
|
||||||
|
trusted_network.allow = "loopback";
|
||||||
|
};
|
||||||
|
|
||||||
pam_userinfotype: jid
|
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
|
||||||
|
shaper_rules = {
|
||||||
|
# setting this to above 1 may break outgoing messages
|
||||||
|
# - maybe some servers rate limit? or just don't understand simultaneous connections?
|
||||||
|
max_s2s_connections = 1;
|
||||||
|
max_user_sessions = 10;
|
||||||
|
max_user_offline_messages = 5000;
|
||||||
|
c2s_shaper.fast = "all";
|
||||||
|
s2s_shaper.med = "all";
|
||||||
|
};
|
||||||
|
|
||||||
acl:
|
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
||||||
admin:
|
# this limits the bytes/sec.
|
||||||
user:
|
# for example, burst: 3_000_000 and rate: 100_000 means:
|
||||||
- "colin@uninsane.org"
|
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
||||||
local:
|
shaper.fast = 1000000;
|
||||||
user_regexp: ""
|
shaper.med = 500000;
|
||||||
loopback:
|
# shaper.fast.rate = 1000000;
|
||||||
ip:
|
# shaper.fast.burst_size = 10000000;
|
||||||
- 127.0.0.0/8
|
# shaper.med.rate = 500000;
|
||||||
- ::1/128
|
# shaper.med.burst_size = 5000000;
|
||||||
|
|
||||||
access_rules:
|
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
||||||
local:
|
# s2s_use_starttls = true;
|
||||||
allow: local
|
s2s_use_starttls = "optional";
|
||||||
c2s_access:
|
# lessens 504: remote-server-timeout errors
|
||||||
allow: all
|
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
|
||||||
announce:
|
negotiation_timeout = 60;
|
||||||
allow: admin
|
|
||||||
configure:
|
|
||||||
allow: admin
|
|
||||||
muc_create:
|
|
||||||
allow: local
|
|
||||||
pubsub_createnode_access:
|
|
||||||
allow: all
|
|
||||||
trusted_network:
|
|
||||||
allow: loopback
|
|
||||||
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
|
listen = [
|
||||||
shaper_rules:
|
{
|
||||||
# setting this to above 1 may break outgoing messages
|
port = 5222;
|
||||||
# - maybe some servers rate limit? or just don't understand simultaneous connections?
|
module = "ejabberd_c2s";
|
||||||
max_s2s_connections: 1
|
shaper = "c2s_shaper";
|
||||||
max_user_sessions: 10
|
starttls = true;
|
||||||
max_user_offline_messages: 5000
|
access = "c2s_access";
|
||||||
c2s_shaper:
|
}
|
||||||
fast: all
|
{
|
||||||
s2s_shaper:
|
port = 5223;
|
||||||
med: all
|
module = "ejabberd_c2s";
|
||||||
|
shaper = "c2s_shaper";
|
||||||
|
tls = true;
|
||||||
|
access = "c2s_access";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 5269;
|
||||||
|
module = "ejabberd_s2s_in";
|
||||||
|
shaper = "s2s_shaper";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 5270;
|
||||||
|
module = "ejabberd_s2s_in";
|
||||||
|
shaper = "s2s_shaper";
|
||||||
|
tls = true;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
port = 5443;
|
||||||
|
module = "ejabberd_http";
|
||||||
|
tls = true;
|
||||||
|
request_handlers = {
|
||||||
|
"/admin" = "ejabberd_web_admin"; # TODO: ensure this actually works
|
||||||
|
"/api" = "mod_http_api"; # ejabberd API endpoint (to control server)
|
||||||
|
"/bosh" = "mod_bosh";
|
||||||
|
"/upload" = "mod_http_upload";
|
||||||
|
"/ws" = "ejabberd_http_ws";
|
||||||
|
# "/.well-known/host-meta" = "mod_host_meta";
|
||||||
|
# "/.well-known/host-meta.json" = "mod_host_meta";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# STUN+TURN TCP
|
||||||
|
# note that the full port range should be forwarded ("not NAT'd")
|
||||||
|
# `use_turn=true` enables both TURN *and* STUN
|
||||||
|
port = 3478;
|
||||||
|
module = "ejabberd_stun";
|
||||||
|
transport = "tcp";
|
||||||
|
use_turn = true;
|
||||||
|
turn_min_port = turnPortLow;
|
||||||
|
turn_max_port = turnPortHigh;
|
||||||
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# STUN+TURN UDP
|
||||||
|
port = 3478;
|
||||||
|
module = "ejabberd_stun";
|
||||||
|
transport = "udp";
|
||||||
|
use_turn = true;
|
||||||
|
turn_min_port = turnPortLow;
|
||||||
|
turn_max_port = turnPortHigh;
|
||||||
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# STUN+TURN TLS over TCP
|
||||||
|
port = 5349;
|
||||||
|
module = "ejabberd_stun";
|
||||||
|
transport = "tcp";
|
||||||
|
tls = true;
|
||||||
|
certfile = "/var/lib/acme/uninsane.org/full.pem";
|
||||||
|
use_turn = true;
|
||||||
|
turn_min_port = turnPortLow;
|
||||||
|
turn_max_port = turnPortHigh;
|
||||||
|
turn_ipv4_address = "%ANATIVE%";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
|
# TODO: enable mod_fail2ban
|
||||||
# this limits the bytes/sec.
|
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||||
# for example, burst: 3_000_000 and rate: 100_000 means:
|
modules = {
|
||||||
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
|
# mod_adhoc = {};
|
||||||
shaper:
|
# mod_announce = {
|
||||||
fast: 1000000
|
# access = "admin";
|
||||||
med: 500000
|
# };
|
||||||
# fast:
|
# allows users to set avatars in vCard
|
||||||
# - rate: 1000000
|
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
|
||||||
# - burst_size: 10000000
|
mod_avatar = {};
|
||||||
# med:
|
mod_caps = {}; # for mod_pubsub
|
||||||
# - rate: 500000
|
mod_carboncopy = {}; # allows multiple clients to receive a user's message
|
||||||
# - burst_size: 5000000
|
# queues messages when recipient is offline, including PEP and presence messages.
|
||||||
|
# compliance test suggests this be enabled
|
||||||
|
mod_client_state = {};
|
||||||
|
|
||||||
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
|
# mod_conversejs: TODO: enable once on 21.12
|
||||||
# s2s_use_starttls: true
|
# allows clients like Dino to discover where to upload files
|
||||||
s2s_use_starttls: optional
|
mod_disco.server_info = [
|
||||||
# lessens 504: remote-server-timeout errors
|
{
|
||||||
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
|
modules = "all";
|
||||||
negotiation_timeout: 60
|
name = "abuse-addresses";
|
||||||
|
urls = [
|
||||||
listen:
|
"mailto:admin.xmpp@uninsane.org"
|
||||||
-
|
"xmpp:colin@uninsane.org"
|
||||||
port: 5222
|
];
|
||||||
module: ejabberd_c2s
|
}
|
||||||
shaper: c2s_shaper
|
{
|
||||||
starttls: true
|
modules = "all";
|
||||||
access: c2s_access
|
name = "admin-addresses";
|
||||||
-
|
urls = [
|
||||||
port: 5223
|
"mailto:admin.xmpp@uninsane.org"
|
||||||
module: ejabberd_c2s
|
"xmpp:colin@uninsane.org"
|
||||||
shaper: c2s_shaper
|
];
|
||||||
tls: true
|
}
|
||||||
access: c2s_access
|
];
|
||||||
-
|
mod_http_upload = {
|
||||||
port: 5269
|
host = "upload.xmpp.uninsane.org";
|
||||||
module: ejabberd_s2s_in
|
hosts = [ "upload.xmpp.uninsane.org" ];
|
||||||
shaper: s2s_shaper
|
put_url = "https://@HOST@:5443/upload";
|
||||||
-
|
dir_mode = "0750";
|
||||||
port: 5270
|
file_mode = "0750";
|
||||||
module: ejabberd_s2s_in
|
rm_on_unregister = false;
|
||||||
shaper: s2s_shaper
|
};
|
||||||
tls: true
|
# allow discoverability of BOSH and websocket endpoints
|
||||||
-
|
# TODO: enable once on ejabberd 22.05 (presently 21.04)
|
||||||
port: 5443
|
# mod_host_meta = {};
|
||||||
module: ejabberd_http
|
mod_jidprep = {}; # probably not needed: lets clients normalize jids
|
||||||
tls: true
|
mod_last = {}; # allow other users to know when i was last online
|
||||||
request_handlers:
|
mod_mam = {
|
||||||
/admin: ejabberd_web_admin # TODO: ensure this actually works
|
# Mnesia is limited to 2GB, better to use an SQL backend
|
||||||
/api: mod_http_api # ejabberd API endpoint (to control server)
|
# For small servers SQLite is a good fit and is very easy
|
||||||
/bosh: mod_bosh
|
# to configure. Uncomment this when you have SQL configured:
|
||||||
/upload: mod_http_upload
|
# db_type: sql
|
||||||
/ws: ejabberd_http_ws
|
assume_mam_usage = true;
|
||||||
# /.well-known/host-meta: mod_host_meta
|
default = "always";
|
||||||
# /.well-known/host-meta.json: mod_host_meta
|
};
|
||||||
-
|
mod_muc = {
|
||||||
# STUN+TURN TCP
|
access = [ "allow" ];
|
||||||
# note that the full port range should be forwarded ("not NAT'd")
|
access_admin = { allow = "admin"; };
|
||||||
# `use_turn=true` enables both TURN *and* STUN
|
access_create = "muc_create";
|
||||||
port: 3478
|
access_persistent = "muc_create";
|
||||||
module: ejabberd_stun
|
access_mam = [ "allow" ];
|
||||||
transport: tcp
|
history_size = 100; # messages to show new participants
|
||||||
use_turn: true
|
host = "muc.xmpp.uninsane.org";
|
||||||
turn_min_port: 49152
|
hosts = [ "muc.xmpp.uninsane.org" ];
|
||||||
turn_max_port: 65535
|
default_room_options = {
|
||||||
turn_ipv4_address: %ANATIVE%
|
anonymous = false;
|
||||||
-
|
lang = "en";
|
||||||
# STUN+TURN UDP
|
persistent = true;
|
||||||
port: 3478
|
mam = true;
|
||||||
module: ejabberd_stun
|
};
|
||||||
transport: udp
|
};
|
||||||
use_turn: true
|
mod_muc_admin = {};
|
||||||
turn_min_port: 49152
|
mod_offline = {
|
||||||
turn_max_port: 65535
|
# store messages for a user when they're offline (TODO: understand multi-client workflow?)
|
||||||
turn_ipv4_address: %ANATIVE%
|
access_max_user_messages = "max_user_offline_messages";
|
||||||
-
|
store_groupchat = true;
|
||||||
# STUN+TURN TLS over TCP
|
};
|
||||||
port: 5349
|
mod_ping = {};
|
||||||
module: ejabberd_stun
|
mod_privacy = {}; # deprecated, but required for `ejabberctl export_piefxis`
|
||||||
transport: tcp
|
mod_private = {}; # allow local clients to persist arbitrary data on my server
|
||||||
tls: true
|
# push notifications to services integrated with e.g. Apple/Android.
|
||||||
certfile: /var/lib/acme/uninsane.org/full.pem
|
# default is for a maximum amount of PII to be withheld, since these push notifs
|
||||||
use_turn: true
|
# generally traverse 3rd party services. can opt to include message body, etc, though.
|
||||||
turn_min_port: 49152
|
mod_push = {};
|
||||||
turn_max_port: 65535
|
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
|
||||||
turn_ipv4_address: %ANATIVE%
|
mod_push_keepalive = {};
|
||||||
|
mod_roster = {
|
||||||
# TODO: enable mod_fail2ban
|
versioning = true;
|
||||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
};
|
||||||
modules:
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
|
||||||
# mod_adhoc: {}
|
# s2s dialback to verify inbound messages
|
||||||
# mod_announce:
|
# unclear to what degree the XMPP network requires this
|
||||||
# access: admin
|
mod_s2s_dialback = {};
|
||||||
# allows users to set avatars in vCard
|
mod_shared_roster = {}; # creates groups for @all, @online, and anything manually administered?
|
||||||
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
|
mod_stream_mgmt = {
|
||||||
mod_avatar: {}
|
# resend undelivered messages if the origin client is offline
|
||||||
mod_caps: {} # for mod_pubsub
|
resend_on_timeout = "if_offline";
|
||||||
mod_carboncopy: {} # allows multiple clients to receive a user's message
|
};
|
||||||
# queues messages when recipient is offline, including PEP and presence messages.
|
# fallback for when DNS-based STUN discovery is unsupported.
|
||||||
# compliance test suggests this be enabled
|
# - see: <https://xmpp.org/extensions/xep-0215.html>
|
||||||
mod_client_state: {}
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
|
||||||
# mod_conversejs: TODO: enable once on 21.12
|
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
|
||||||
# allows clients like Dino to discover where to upload files
|
mod_stun_disco = {};
|
||||||
mod_disco:
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
|
||||||
server_info:
|
mod_vcard = {
|
||||||
-
|
allow_return_all = true; # all users are discoverable (?)
|
||||||
modules: all
|
host = "vjid.xmpp.uninsane.org";
|
||||||
name: abuse-addresses
|
hosts = [ "vjid.xmpp.uninsane.org" ];
|
||||||
urls:
|
search = true;
|
||||||
- "mailto:admin.xmpp@uninsane.org"
|
};
|
||||||
- "xmpp:colin@uninsane.org"
|
mod_vcard_xupdate = {}; # needed for avatars
|
||||||
-
|
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
|
||||||
modules: all
|
mod_pubsub = {
|
||||||
name: admin-addresses
|
#^ needed for avatars
|
||||||
urls:
|
access_createnode = "pubsub_createnode_access";
|
||||||
- "mailto:admin.xmpp@uninsane.org"
|
host = "pubsub.xmpp.uninsane.org";
|
||||||
- "xmpp:colin@uninsane.org"
|
hosts = [ "pubsub.xmpp.uninsane.org" ];
|
||||||
mod_http_upload:
|
ignore_pep_from_offline = false;
|
||||||
host: upload.xmpp.uninsane.org
|
last_item_cache = true;
|
||||||
hosts:
|
plugins = [
|
||||||
- upload.xmpp.uninsane.org
|
"pep"
|
||||||
put_url: "https://@HOST@:5443/upload"
|
"flat"
|
||||||
dir_mode: "0750"
|
];
|
||||||
file_mode: "0750"
|
force_node_config = {
|
||||||
rm_on_unregister: false
|
# ensure client bookmarks are private
|
||||||
# allow discoverability of BOSH and websocket endpoints
|
"storage:bookmarks:" = {
|
||||||
# TODO: enable once on ejabberd 22.05 (presently 21.04)
|
"access_model" = "whitelist";
|
||||||
# mod_host_meta: {}
|
};
|
||||||
mod_jidprep: {} # probably not needed: lets clients normalize jids
|
"urn:xmpp:avatar:data" = {
|
||||||
mod_last: {} # allow other users to know when i was last online
|
"access_model" = "open";
|
||||||
mod_mam:
|
};
|
||||||
# Mnesia is limited to 2GB, better to use an SQL backend
|
"urn:xmpp:avatar:metadata" = {
|
||||||
# For small servers SQLite is a good fit and is very easy
|
"access_model" = "open";
|
||||||
# to configure. Uncomment this when you have SQL configured:
|
};
|
||||||
# db_type: sql
|
};
|
||||||
assume_mam_usage: true
|
};
|
||||||
default: always
|
mod_version = {};
|
||||||
mod_muc:
|
};
|
||||||
access:
|
});
|
||||||
- allow
|
|
||||||
access_admin:
|
|
||||||
- allow: admin
|
|
||||||
access_create: muc_create
|
|
||||||
access_persistent: muc_create
|
|
||||||
access_mam:
|
|
||||||
- allow
|
|
||||||
history_size: 100 # messages to show new participants
|
|
||||||
host: muc.xmpp.uninsane.org
|
|
||||||
hosts:
|
|
||||||
- muc.xmpp.uninsane.org
|
|
||||||
default_room_options:
|
|
||||||
anonymous: false
|
|
||||||
lang: en
|
|
||||||
persistent: true
|
|
||||||
mam: true
|
|
||||||
mod_muc_admin: {}
|
|
||||||
mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?)
|
|
||||||
access_max_user_messages: max_user_offline_messages
|
|
||||||
store_groupchat: true
|
|
||||||
mod_ping: {}
|
|
||||||
mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis`
|
|
||||||
mod_private: {} # allow local clients to persist arbitrary data on my server
|
|
||||||
# push notifications to services integrated with e.g. Apple/Android.
|
|
||||||
# default is for a maximum amount of PII to be withheld, since these push notifs
|
|
||||||
# generally traverse 3rd party services. can opt to include message body, etc, though.
|
|
||||||
mod_push: {}
|
|
||||||
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
|
|
||||||
mod_push_keepalive: {}
|
|
||||||
mod_roster:
|
|
||||||
versioning: true
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
|
|
||||||
# s2s dialback to verify inbound messages
|
|
||||||
# unclear to what degree the XMPP network requires this
|
|
||||||
mod_s2s_dialback: {}
|
|
||||||
mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered?
|
|
||||||
mod_stream_mgmt:
|
|
||||||
resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline
|
|
||||||
# fallback for when DNS-based STUN discovery is unsupported.
|
|
||||||
# - see: <https://xmpp.org/extensions/xep-0215.html>
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
|
|
||||||
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
|
|
||||||
mod_stun_disco: {}
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
|
|
||||||
mod_vcard:
|
|
||||||
allow_return_all: true # all users are discoverable (?)
|
|
||||||
host: vjid.xmpp.uninsane.org
|
|
||||||
hosts:
|
|
||||||
- vjid.xmpp.uninsane.org
|
|
||||||
search: true
|
|
||||||
mod_vcard_xupdate: {} # needed for avatars
|
|
||||||
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
|
|
||||||
mod_pubsub: # needed for avatars
|
|
||||||
access_createnode: pubsub_createnode_access
|
|
||||||
host: pubsub.xmpp.uninsane.org
|
|
||||||
hosts:
|
|
||||||
- pubsub.xmpp.uninsane.org
|
|
||||||
ignore_pep_from_offline: false
|
|
||||||
last_item_cache: true
|
|
||||||
plugins:
|
|
||||||
- pep
|
|
||||||
- flat
|
|
||||||
force_node_config:
|
|
||||||
# ensure client bookmarks are private
|
|
||||||
storage:bookmarks:
|
|
||||||
access_model: whitelist
|
|
||||||
urn:xmpp:avatar:data:
|
|
||||||
access_model: open
|
|
||||||
urn:xmpp:avatar:metadata:
|
|
||||||
access_model: open
|
|
||||||
mod_version: {}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
sed = "${pkgs.gnused}/bin/sed";
|
sed = "${pkgs.gnused}/bin/sed";
|
||||||
in ''
|
in ''
|
||||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||||
# config is 444 (not 644), so we want to write out-of-place and then atomically move
|
# config is 444 (not 644), so we want to write out-of-place and then atomically move
|
||||||
# TODO: factor this out into `sane-woop` helper?
|
# TODO: factor this out into `sane-woop` helper?
|
||||||
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
||||||
${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
${sed} "s/%ANATIVE%/$ip/g" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||||
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
53
hosts/by-name/servo/services/export/default.nix
Normal file
53
hosts/by-name/servo/services/export/default.nix
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./nfs.nix
|
||||||
|
./sftpgo.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
users.groups.export = {};
|
||||||
|
|
||||||
|
fileSystems."/var/export/media" = {
|
||||||
|
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
||||||
|
device = "/var/lib/uninsane/media";
|
||||||
|
options = [ "rbind" ];
|
||||||
|
};
|
||||||
|
# fileSystems."/var/export/playground" = {
|
||||||
|
# device = config.fileSystems."/mnt/persist/ext".device;
|
||||||
|
# fsType = "btrfs";
|
||||||
|
# options = [
|
||||||
|
# "subvol=export-playground"
|
||||||
|
# "compress=zstd"
|
||||||
|
# "defaults"
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
# N.B.: the backing directory should be manually created here **as a btrfs subvolume** and with a quota.
|
||||||
|
# - `sudo btrfs subvolume create /mnt/persist/ext/persist/var/export/playground`
|
||||||
|
# - `sudo btrfs quota enable /mnt/persist/ext/persist/var/export/playground`
|
||||||
|
# - `sudo btrfs quota rescan -sw /mnt/persist/ext/persist/var/export/playground`
|
||||||
|
# to adjust the limits (which apply at the block layer, i.e. post-compression):
|
||||||
|
# - `sudo btrfs qgroup limit 20G /mnt/persist/ext/persist/var/export/playground`
|
||||||
|
# to query the quota/status:
|
||||||
|
# - `sudo btrfs qgroup show -re /var/export/playground`
|
||||||
|
sane.persist.sys.ext = [
|
||||||
|
{ user = "root"; group = "export"; mode = "0775"; path = "/var/export/playground"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
sane.fs."/var/export/README.md" = {
|
||||||
|
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||||
|
file.text = ''
|
||||||
|
- media/ read-only: Videos, Music, Books, etc
|
||||||
|
- playground/ read-write: use it to share files with other users of this server
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.fs."/var/export/playground/README.md" = {
|
||||||
|
wantedBy = [ "nfs.service" "sftpgo.service" ];
|
||||||
|
file.text = ''
|
||||||
|
this directory is intentionally read+write by anyone with access (i.e. on the LAN).
|
||||||
|
- share files
|
||||||
|
- write poetry
|
||||||
|
- be a friendly troll
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
110
hosts/by-name/servo/services/export/nfs.nix
Normal file
110
hosts/by-name/servo/services/export/nfs.nix
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# docs:
|
||||||
|
# - <https://nixos.wiki/wiki/NFS>
|
||||||
|
# - <https://wiki.gentoo.org/wiki/Nfs-utils>
|
||||||
|
# system files:
|
||||||
|
# - /etc/exports
|
||||||
|
# system services:
|
||||||
|
# - nfs-server.service
|
||||||
|
# - nfs-idmapd.service
|
||||||
|
# - nfs-mountd.service
|
||||||
|
# - nfsdcld.service
|
||||||
|
# - rpc-statd.service
|
||||||
|
# - rpcbind.service
|
||||||
|
#
|
||||||
|
# TODO: force files to be 755, or 750.
|
||||||
|
# - could maybe be done with some mount option?
|
||||||
|
|
||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
services.nfs.server.enable = true;
|
||||||
|
|
||||||
|
# see which ports NFS uses with:
|
||||||
|
# - `rpcinfo -p`
|
||||||
|
sane.ports.ports."111" = {
|
||||||
|
protocol = [ "tcp" "udp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "NFS server portmapper";
|
||||||
|
};
|
||||||
|
sane.ports.ports."2049" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "NFS server";
|
||||||
|
};
|
||||||
|
sane.ports.ports."4000" = {
|
||||||
|
protocol = [ "udp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "NFS server status daemon";
|
||||||
|
};
|
||||||
|
sane.ports.ports."4001" = {
|
||||||
|
protocol = [ "tcp" "udp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "NFS server lock daemon";
|
||||||
|
};
|
||||||
|
sane.ports.ports."4002" = {
|
||||||
|
protocol = [ "tcp" "udp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "NFS server mount daemon";
|
||||||
|
};
|
||||||
|
|
||||||
|
# NFS4 allows these to float, but NFS3 mandates specific ports, so fix them for backwards compat.
|
||||||
|
services.nfs.server.lockdPort = 4001;
|
||||||
|
services.nfs.server.mountdPort = 4002;
|
||||||
|
services.nfs.server.statdPort = 4000;
|
||||||
|
|
||||||
|
# format:
|
||||||
|
# fspoint visibility(options)
|
||||||
|
# options:
|
||||||
|
# - see: <https://wiki.gentoo.org/wiki/Nfs-utils#Exports>
|
||||||
|
# - see [man 5 exports](https://linux.die.net/man/5/exports)
|
||||||
|
# - insecure: require clients use src port > 1024
|
||||||
|
# - rw, ro (default)
|
||||||
|
# - async, sync (default)
|
||||||
|
# - no_subtree_check (default), subtree_check: verify not just that files requested by the client live
|
||||||
|
# in the expected fs, but also that they live under whatever subdirectory of that fs is exported.
|
||||||
|
# - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`.
|
||||||
|
# - crossmnt: reveal filesystems that are mounted under this endpoint
|
||||||
|
# - fsid: must be zero for the root export
|
||||||
|
# - fsid=root is alias for fsid=0
|
||||||
|
# - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts.
|
||||||
|
# - all_squash: rewrite all client requests such that they come from anonuid/anongid
|
||||||
|
# - any files a user creates are owned by local anonuid/anongid.
|
||||||
|
# - users can read any local file which anonuid/anongid would be able to read.
|
||||||
|
# - users can't chown to/away from anonuid/anongid.
|
||||||
|
# - users can chmod files they own, to anything (making them unreadable to non-`nfsuser` export users, like FTP).
|
||||||
|
# - `stat` remains unchanged, returning the real UIDs/GIDs to the client.
|
||||||
|
# - thus programs which check `uid` or `gid` before trying an operation may incorrectly conclude they can't perform some op.
|
||||||
|
#
|
||||||
|
# 10.0.0.0/8 to export both to LAN (readonly, unencrypted) and wg vpn (read-write, encrypted)
|
||||||
|
services.nfs.server.exports =
|
||||||
|
let
|
||||||
|
fmtExport = { export, baseOpts, extraLanOpts ? [], extraVpnOpts ? [] }:
|
||||||
|
let
|
||||||
|
always = [ "subtree_check" ];
|
||||||
|
lanOpts = always ++ baseOpts ++ extraLanOpts;
|
||||||
|
vpnOpts = always ++ baseOpts ++ extraVpnOpts;
|
||||||
|
in "${export} 10.78.79.0/22(${lib.concatStringsSep "," lanOpts}) 10.0.10.0/24(${lib.concatStringsSep "," vpnOpts})";
|
||||||
|
in lib.concatStringsSep "\n" [
|
||||||
|
(fmtExport {
|
||||||
|
export = "/var/export";
|
||||||
|
baseOpts = [ "crossmnt" "fsid=root" ];
|
||||||
|
extraLanOpts = [ "ro" ];
|
||||||
|
extraVpnOpts = [ "rw" "no_root_squash" ];
|
||||||
|
})
|
||||||
|
(fmtExport {
|
||||||
|
export = "/var/export/playground";
|
||||||
|
baseOpts = [
|
||||||
|
"mountpoint"
|
||||||
|
"all_squash"
|
||||||
|
"rw"
|
||||||
|
"anonuid=${builtins.toString config.users.users.nfsuser.uid}"
|
||||||
|
"anongid=${builtins.toString config.users.groups.export.gid}"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.nfsuser = {
|
||||||
|
description = "virtual user for anonymous NFS operations";
|
||||||
|
group = "export";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
179
hosts/by-name/servo/services/export/sftpgo.nix
Normal file
179
hosts/by-name/servo/services/export/sftpgo.nix
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
# docs:
|
||||||
|
# - <https://github.com/drakkan/sftpgo>
|
||||||
|
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
|
||||||
|
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
|
||||||
|
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
|
||||||
|
# - nixos example: <repo:nixos/nixpkgs:nixos/tests/sftpgo.nix>
|
||||||
|
#
|
||||||
|
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
||||||
|
#
|
||||||
|
# TODO: change umask so sftpgo-created files default to 644.
|
||||||
|
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
||||||
|
|
||||||
|
|
||||||
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
|
let
|
||||||
|
# user permissions:
|
||||||
|
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
||||||
|
# - "*" = grant all permissions
|
||||||
|
# - read-only perms:
|
||||||
|
# - "list" = list files and directories
|
||||||
|
# - "download"
|
||||||
|
# - rw perms:
|
||||||
|
# - "upload"
|
||||||
|
# - "overwrite" = allow uploads to replace existing files
|
||||||
|
# - "delete" = delete files and directories
|
||||||
|
# - "delete_files"
|
||||||
|
# - "delete_dirs"
|
||||||
|
# - "rename" = rename files and directories
|
||||||
|
# - "rename_files"
|
||||||
|
# - "rename_dirs"
|
||||||
|
# - "create_dirs"
|
||||||
|
# - "create_symlinks"
|
||||||
|
# - "chmod"
|
||||||
|
# - "chown"
|
||||||
|
# - "chtimes" = change atime/mtime (access and modification times)
|
||||||
|
#
|
||||||
|
# home_dir:
|
||||||
|
# - it seems (empirically) that a user can't cd above their home directory.
|
||||||
|
# though i don't have a reference for that in the docs.
|
||||||
|
authResponseSuccess = {
|
||||||
|
status = 1;
|
||||||
|
username = "anonymous";
|
||||||
|
expiration_date = 0;
|
||||||
|
home_dir = "/var/export";
|
||||||
|
# uid/gid 0 means to inherit sftpgo uid.
|
||||||
|
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
||||||
|
# - uploaded files belong to Linux user `sftpgo`
|
||||||
|
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
||||||
|
uid = 0;
|
||||||
|
gid = 0;
|
||||||
|
# uid = 65534;
|
||||||
|
# gid = 65534;
|
||||||
|
max_sessions = 0;
|
||||||
|
# quota_*: 0 means to not use SFTP's quota system
|
||||||
|
quota_size = 0;
|
||||||
|
quota_files = 0;
|
||||||
|
permissions = {
|
||||||
|
"/" = [ "list" "download" ];
|
||||||
|
"/playground" = [
|
||||||
|
# read-only:
|
||||||
|
"list"
|
||||||
|
"download"
|
||||||
|
# write:
|
||||||
|
"upload"
|
||||||
|
"overwrite"
|
||||||
|
"delete"
|
||||||
|
"rename"
|
||||||
|
"create_dirs"
|
||||||
|
"create_symlinks"
|
||||||
|
# intentionally omitted:
|
||||||
|
# "chmod"
|
||||||
|
# "chown"
|
||||||
|
# "chtimes"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
upload_bandwidth = 0;
|
||||||
|
download_bandwidth = 0;
|
||||||
|
filters = {
|
||||||
|
allowed_ip = [];
|
||||||
|
denied_ip = [];
|
||||||
|
};
|
||||||
|
public_keys = [];
|
||||||
|
# other fields:
|
||||||
|
# ? groups
|
||||||
|
# ? virtual_folders
|
||||||
|
};
|
||||||
|
authResponseFail = {
|
||||||
|
username = "";
|
||||||
|
};
|
||||||
|
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
|
||||||
|
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
|
||||||
|
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sftpgo_external_auth_hook";
|
||||||
|
src = ./.;
|
||||||
|
pkgs = [ "coreutils" ];
|
||||||
|
};
|
||||||
|
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
|
||||||
|
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Client initiates a FTP "control connection" on port 21.
|
||||||
|
# - this handles the client -> server commands, and the server -> client status, but not the actual data
|
||||||
|
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
||||||
|
# - 50000-50100 is a common port range for this.
|
||||||
|
sane.ports.ports = {
|
||||||
|
"21" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "colin-FTP server";
|
||||||
|
};
|
||||||
|
} // (sane-lib.mapToAttrs
|
||||||
|
(port: {
|
||||||
|
name = builtins.toString port;
|
||||||
|
value = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
description = "colin-FTP server data port range";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.range 50000 50100)
|
||||||
|
);
|
||||||
|
|
||||||
|
services.sftpgo = {
|
||||||
|
enable = true;
|
||||||
|
group = "export";
|
||||||
|
settings = {
|
||||||
|
ftpd = {
|
||||||
|
bindings = [
|
||||||
|
{
|
||||||
|
# binding this means any wireguard client can connect
|
||||||
|
address = "10.0.10.5";
|
||||||
|
port = 21;
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# binding this means any LAN client can connect
|
||||||
|
address = "10.78.79.51";
|
||||||
|
port = 21;
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
|
||||||
|
disable_active_mode = true;
|
||||||
|
hash_support = true;
|
||||||
|
passive_port_range = {
|
||||||
|
start = 50000;
|
||||||
|
end = 50100;
|
||||||
|
};
|
||||||
|
|
||||||
|
banner = ''
|
||||||
|
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
|
||||||
|
Username: "anonymous"
|
||||||
|
Password: "anonymous"
|
||||||
|
CONFIGURE YOUR CLIENT FOR "PASSIVE" mode, e.g. `ftp --passive uninsane.org`
|
||||||
|
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
data_provider = {
|
||||||
|
driver = "memory";
|
||||||
|
external_auth_hook = "${authProgram}";
|
||||||
|
# track_quota:
|
||||||
|
# - 0: disable quota tracking
|
||||||
|
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
||||||
|
# - 2: quota is updated on every upload/delete, but only if user/folder has a quota restriction (default, i think)
|
||||||
|
# track_quota = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.sftpgo.extraGroups = [ "export" ];
|
||||||
|
|
||||||
|
systemd.services.sftpgo.serviceConfig = {
|
||||||
|
ReadOnlyPaths = [ "/var/export" ];
|
||||||
|
ReadWritePaths = [ "/var/export/playground" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
23
hosts/by-name/servo/services/export/sftpgo_external_auth_hook
Executable file
23
hosts/by-name/servo/services/export/sftpgo_external_auth_hook
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p coreutils
|
||||||
|
# vim: set filetype=bash :
|
||||||
|
#
|
||||||
|
# available environment variables:
|
||||||
|
# - SFTPGO_AUTHD_USERNAME
|
||||||
|
# - SFTPGO_AUTHD_USER
|
||||||
|
# - SFTPGO_AUTHD_IP
|
||||||
|
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
|
||||||
|
# - SFTPGO_AUTHD_PASSWORD
|
||||||
|
# - SFTPGO_AUTHD_PUBLIC_KEY
|
||||||
|
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
||||||
|
# - SFTPGO_AUTHD_TLS_CERT
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
|
||||||
|
|
||||||
|
|
||||||
|
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
|
||||||
|
cat "$2"
|
||||||
|
else
|
||||||
|
cat "$1"
|
||||||
|
fi
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
# docs:
|
|
||||||
# - <https://github.com/drakkan/sftpgo>
|
|
||||||
# - config options: <https://github.com/drakkan/sftpgo/blob/main/docs/full-configuration.md>
|
|
||||||
# - config defaults: <https://github.com/drakkan/sftpgo/blob/main/sftpgo.json>
|
|
||||||
# - nixos options: <repo:nixos/nixpkgs:nixos/modules/services/web-apps/sftpgo.nix>
|
|
||||||
#
|
|
||||||
# sftpgo is a FTP server that also supports WebDAV, SFTP, and web clients.
|
|
||||||
|
|
||||||
|
|
||||||
{ lib, pkgs, sane-lib, ... }:
|
|
||||||
let
|
|
||||||
authProgram = pkgs.static-nix-shell.mkBash {
|
|
||||||
pname = "sftpgo_external_auth_hook";
|
|
||||||
src = ./.;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
# Client initiates a FTP "control connection" on port 21.
|
|
||||||
# - this handles the client -> server commands, and the server -> client status, but not the actual data
|
|
||||||
# - file data, directory listings, etc need to be transferred on an ephemeral "data port".
|
|
||||||
# - 50000-50100 is a common port range for this.
|
|
||||||
sane.ports.ports = {
|
|
||||||
"21" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "colin-FTP server";
|
|
||||||
};
|
|
||||||
} // (sane-lib.mapToAttrs
|
|
||||||
(port: {
|
|
||||||
name = builtins.toString port;
|
|
||||||
value = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "colin-FTP server data port range";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
(lib.range 50000 50100)
|
|
||||||
);
|
|
||||||
|
|
||||||
services.sftpgo = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
ftpd = {
|
|
||||||
bindings = [{
|
|
||||||
address = "10.0.10.5";
|
|
||||||
port = 21;
|
|
||||||
debug = true;
|
|
||||||
}];
|
|
||||||
|
|
||||||
# active mode is susceptible to "bounce attacks", without much benefit over passive mode
|
|
||||||
disable_active_mode = true;
|
|
||||||
hash_support = true;
|
|
||||||
passive_port_range = {
|
|
||||||
start = 50000;
|
|
||||||
end = 50100;
|
|
||||||
};
|
|
||||||
|
|
||||||
banner = ''
|
|
||||||
Welcome, friends, to Colin's read-only FTP server! Also available via NFS on the same host.
|
|
||||||
Please let me know if anything's broken or not as it should be. Otherwise, browse and DL freely :)
|
|
||||||
'';
|
|
||||||
|
|
||||||
};
|
|
||||||
data_provider = {
|
|
||||||
driver = "memory";
|
|
||||||
external_auth_hook = "${authProgram}/bin/sftpgo_external_auth_hook";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#!/usr/bin/env nix-shell
|
|
||||||
#!nix-shell -i bash
|
|
||||||
# vim: set filetype=bash :
|
|
||||||
#
|
|
||||||
# available environment variables:
|
|
||||||
# - SFTPGO_AUTHD_USERNAME
|
|
||||||
# - SFTPGO_AUTHD_USER
|
|
||||||
# - SFTPGO_AUTHD_IP
|
|
||||||
# - SFTPGO_AUTHD_PROTOCOL = { "DAV", "FTP", "HTTP", "SSH" }
|
|
||||||
# - SFTPGO_AUTHD_PASSWORD
|
|
||||||
# - SFTPGO_AUTHD_PUBLIC_KEY
|
|
||||||
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
|
||||||
# - SFTPGO_AUTHD_TLS_CERT
|
|
||||||
#
|
|
||||||
# user permissions:
|
|
||||||
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
|
||||||
# - "*" = grant all permissions
|
|
||||||
# - read-only perms:
|
|
||||||
# - "list" = list files and directories
|
|
||||||
# - "download"
|
|
||||||
# - rw perms:
|
|
||||||
# - "upload"
|
|
||||||
# - "overwrite" = allow uploads to replace existing files
|
|
||||||
# - "delete" = delete files and directories
|
|
||||||
# - "delete_files"
|
|
||||||
# - "delete_dirs"
|
|
||||||
# - "rename" = rename files and directories
|
|
||||||
# - "rename_files"
|
|
||||||
# - "rename_dirs"
|
|
||||||
# - "create_dirs"
|
|
||||||
# - "create_symlinks"
|
|
||||||
# - "chmod"
|
|
||||||
# - "chown"
|
|
||||||
# - "chtimes" = change atime/mtime (access and modification times)
|
|
||||||
#
|
|
||||||
# home_dir:
|
|
||||||
# - it seems (empirically) that a user can't cd above their home directory.
|
|
||||||
# though i don't have a reference for that in the docs.
|
|
||||||
# TODO: don't reuse /var/nfs/export here. formalize this some other way.
|
|
||||||
|
|
||||||
|
|
||||||
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
|
|
||||||
echo '{'
|
|
||||||
echo ' "status":1,'
|
|
||||||
echo ' "username":"anonymous","expiration_date":0,'
|
|
||||||
echo ' "home_dir":"/var/nfs/export","uid":65534,"gid":65534,"max_sessions":0,"quota_size":0,"quota_files":100000,'
|
|
||||||
echo ' "permissions":{'
|
|
||||||
echo ' "/":["list", "download"]'
|
|
||||||
echo ' },'
|
|
||||||
echo ' "upload_bandwidth":0,"download_bandwidth":0,'
|
|
||||||
echo ' "filters":{"allowed_ip":[],"denied_ip":[]},"public_keys":[]'
|
|
||||||
echo '}'
|
|
||||||
else
|
|
||||||
echo '{"username":""}'
|
|
||||||
fi
|
|
||||||
@@ -3,13 +3,23 @@
|
|||||||
# - <repo:LemmyNet/lemmy:docker/nginx.conf>
|
# - <repo:LemmyNet/lemmy:docker/nginx.conf>
|
||||||
# - <repo:LemmyNet/lemmy-ansible:templates/nginx.conf>
|
# - <repo:LemmyNet/lemmy-ansible:templates/nginx.conf>
|
||||||
|
|
||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
inherit (builtins) toString;
|
inherit (builtins) toString;
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
uiPort = 1234; # default ui port is 1234
|
uiPort = 1234; # default ui port is 1234
|
||||||
backendPort = 8536; # default backend port is 8536
|
backendPort = 8536; # default backend port is 8536
|
||||||
# - i guess the "backend" port is used for federation?
|
#^ i guess the "backend" port is used for federation?
|
||||||
|
pict-rs = pkgs.pict-rs.overrideAttrs (upstream: {
|
||||||
|
# as of v 0.4.2, all non-GIF video is forcibly transcoded.
|
||||||
|
# that breaks lemmy, because of the request latency.
|
||||||
|
# and it eats up hella CPU.
|
||||||
|
# pict-rs is iffy around video altogether: mp4 seems the best supported.
|
||||||
|
postPatch = (upstream.postPatch or "") + ''
|
||||||
|
substituteInPlace src/validate.rs \
|
||||||
|
--replace 'if transcode_options.needs_reencode() {' 'if false {'
|
||||||
|
'';
|
||||||
|
});
|
||||||
in {
|
in {
|
||||||
services.lemmy = {
|
services.lemmy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -56,4 +66,20 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
sane.dns.zones."uninsane.org".inet.CNAME."lemmy" = "native";
|
||||||
|
|
||||||
|
#v DO NOT REMOVE: defaults to 0.3, instead of latest, so always need to explicitly set this.
|
||||||
|
services.pict-rs.package = pict-rs;
|
||||||
|
|
||||||
|
# pict-rs configuration is applied in this order:
|
||||||
|
# - via toml
|
||||||
|
# - via env vars (overrides everything above)
|
||||||
|
# - via CLI flags (overrides everything above)
|
||||||
|
# some of the CLI flags have defaults, making it the only actual way to configure certain things even when docs claim otherwise.
|
||||||
|
# CLI args: <https://git.asonix.dog/asonix/pict-rs#user-content-running>
|
||||||
|
systemd.services.pict-rs.serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " " [
|
||||||
|
"${lib.getBin pict-rs}/bin/pict-rs run"
|
||||||
|
"--media-max-frame-count" (builtins.toString (30*60*60))
|
||||||
|
"--media-process-timeout 120"
|
||||||
|
"--media-enable-full-video true" # allow audio
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,39 +14,47 @@
|
|||||||
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/matrix-synapse"; }
|
{ user = "matrix-synapse"; group = "matrix-synapse"; path = "/var/lib/matrix-synapse"; }
|
||||||
];
|
];
|
||||||
services.matrix-synapse.enable = true;
|
services.matrix-synapse.enable = true;
|
||||||
# this changes the default log level from INFO to WARN.
|
services.matrix-synapse.settings = {
|
||||||
# maybe there's an easier way?
|
# this changes the default log level from INFO to WARN.
|
||||||
services.matrix-synapse.settings.log_config = ./synapse-log_level.yaml;
|
# maybe there's an easier way?
|
||||||
services.matrix-synapse.settings.server_name = "uninsane.org";
|
log_config = ./synapse-log_level.yaml;
|
||||||
|
server_name = "uninsane.org";
|
||||||
|
|
||||||
# services.matrix-synapse.enable_registration_captcha = true;
|
# services.matrix-synapse.enable_registration_captcha = true;
|
||||||
# services.matrix-synapse.enable_registration_without_verification = true;
|
# services.matrix-synapse.enable_registration_without_verification = true;
|
||||||
services.matrix-synapse.settings.enable_registration = true;
|
enable_registration = true;
|
||||||
# services.matrix-synapse.registration_shared_secret = "<shared key goes here>";
|
# services.matrix-synapse.registration_shared_secret = "<shared key goes here>";
|
||||||
|
|
||||||
# default for listeners is port = 8448, tls = true, x_forwarded = false.
|
# default for listeners is port = 8448, tls = true, x_forwarded = false.
|
||||||
# we change this because the server is situated behind nginx.
|
# we change this because the server is situated behind nginx.
|
||||||
services.matrix-synapse.settings.listeners = [
|
listeners = [
|
||||||
{
|
{
|
||||||
port = 8008;
|
port = 8008;
|
||||||
bind_addresses = [ "127.0.0.1" ];
|
bind_addresses = [ "127.0.0.1" ];
|
||||||
type = "http";
|
type = "http";
|
||||||
tls = false;
|
tls = false;
|
||||||
x_forwarded = true;
|
x_forwarded = true;
|
||||||
resources = [
|
resources = [
|
||||||
{
|
{
|
||||||
names = [ "client" "federation" ];
|
names = [ "client" "federation" ];
|
||||||
compress = false;
|
compress = false;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
services.matrix-synapse.settings.x_forwarded = true; # because we proxy matrix behind nginx
|
ip_range_whitelist = [
|
||||||
services.matrix-synapse.settings.max_upload_size = "100M"; # default is "50M"
|
# to communicate with ntfy.uninsane.org push notifs.
|
||||||
|
# TODO: move this to some non-shared loopback device: we don't want Matrix spouting http requests to *anything* on this machine
|
||||||
|
"10.78.79.51"
|
||||||
|
];
|
||||||
|
|
||||||
services.matrix-synapse.settings.admin_contact = "admin.matrix@uninsane.org";
|
x_forwarded = true; # because we proxy matrix behind nginx
|
||||||
services.matrix-synapse.settings.registrations_require_3pid = [ "email" ];
|
max_upload_size = "100M"; # default is "50M"
|
||||||
|
|
||||||
|
admin_contact = "admin.matrix@uninsane.org";
|
||||||
|
registrations_require_3pid = [ "email" ];
|
||||||
|
};
|
||||||
|
|
||||||
services.matrix-synapse.extraConfigFiles = [
|
services.matrix-synapse.extraConfigFiles = [
|
||||||
config.sops.secrets."matrix_synapse_secrets.yaml".path
|
config.sops.secrets."matrix_synapse_secrets.yaml".path
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ in
|
|||||||
sasl = false;
|
sasl = false;
|
||||||
# notable channels:
|
# notable channels:
|
||||||
# - #hare
|
# - #hare
|
||||||
|
# - #mnt-reform
|
||||||
};
|
};
|
||||||
"irc.myanonamouse.net" = ircServer {
|
"irc.myanonamouse.net" = ircServer {
|
||||||
name = "MyAnonamouse";
|
name = "MyAnonamouse";
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
# docs:
|
|
||||||
# - <https://nixos.wiki/wiki/NFS>
|
|
||||||
# - <https://wiki.gentoo.org/wiki/Nfs-utils>
|
|
||||||
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
services.nfs.server.enable = true;
|
|
||||||
|
|
||||||
# see which ports NFS uses with:
|
|
||||||
# - `rpcinfo -p`
|
|
||||||
sane.ports.ports."111" = {
|
|
||||||
protocol = [ "tcp" "udp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "NFS server portmapper";
|
|
||||||
};
|
|
||||||
sane.ports.ports."2049" = {
|
|
||||||
protocol = [ "tcp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "NFS server";
|
|
||||||
};
|
|
||||||
sane.ports.ports."4000" = {
|
|
||||||
protocol = [ "udp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "NFS server status daemon";
|
|
||||||
};
|
|
||||||
sane.ports.ports."4001" = {
|
|
||||||
protocol = [ "tcp" "udp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "NFS server lock daemon";
|
|
||||||
};
|
|
||||||
sane.ports.ports."4002" = {
|
|
||||||
protocol = [ "tcp" "udp" ];
|
|
||||||
visibleTo.lan = true;
|
|
||||||
description = "NFS server mount daemon";
|
|
||||||
};
|
|
||||||
|
|
||||||
# NFS4 allows these to float, but NFS3 mandates specific ports, so fix them for backwards compat.
|
|
||||||
services.nfs.server.lockdPort = 4001;
|
|
||||||
services.nfs.server.mountdPort = 4002;
|
|
||||||
services.nfs.server.statdPort = 4000;
|
|
||||||
|
|
||||||
# format:
|
|
||||||
# fspoint visibility(options)
|
|
||||||
# options:
|
|
||||||
# - see: <https://wiki.gentoo.org/wiki/Nfs-utils#Exports>
|
|
||||||
# - see [man 5 exports](https://linux.die.net/man/5/exports)
|
|
||||||
# - insecure: require clients use src port > 1024
|
|
||||||
# - rw, ro (default)
|
|
||||||
# - async, sync (default)
|
|
||||||
# - no_subtree_check (default), subtree_check: verify not just that files requested by the client live
|
|
||||||
# in the expected fs, but also that they live under whatever subdirectory of that fs is exported.
|
|
||||||
# - no_root_squash, root_squash (default): map requests from uid 0 to user `nobody`.
|
|
||||||
# - crossmnt: reveal filesystems that are mounted under this endpoint
|
|
||||||
# - fsid: must be zero for the root export
|
|
||||||
# - mountpoint[=/path]: only export the directory if it's a mountpoint. used to avoid exporting failed mounts.
|
|
||||||
#
|
|
||||||
# 10.0.0.0/8 to export (readonly) both to LAN (unencrypted) and wg vpn (encrypted)
|
|
||||||
services.nfs.server.exports = ''
|
|
||||||
/var/nfs/export 10.78.79.0/22(ro,crossmnt,fsid=0,subtree_check) 10.0.10.0/24(rw,no_root_squash,crossmnt,fsid=0,subtree_check)
|
|
||||||
'';
|
|
||||||
|
|
||||||
fileSystems."/var/nfs/export/media" = {
|
|
||||||
# everything in here could be considered publicly readable (based on the viewer's legal jurisdiction)
|
|
||||||
device = "/var/lib/uninsane/media";
|
|
||||||
options = [ "rbind" ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
98
hosts/by-name/servo/services/ntfy.nix
Normal file
98
hosts/by-name/servo/services/ntfy.nix
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# ntfy: UnifiedPush notification delivery system
|
||||||
|
# - used to get push notifications out of Matrix and onto a Phone (iOS, Android, or a custom client)
|
||||||
|
#
|
||||||
|
# config options:
|
||||||
|
# - <https://docs.ntfy.sh/config/#config-options>
|
||||||
|
#
|
||||||
|
# usage:
|
||||||
|
# - ntfy sub https://ntfy.uninsane.org/TOPIC
|
||||||
|
# - ntfy pub https://ntfy.uninsane.org/TOPIC "my message"
|
||||||
|
# in production, TOPIC is a shared secret between the publisher (Matrix homeserver) and the subscriber (phone)
|
||||||
|
#
|
||||||
|
# administering:
|
||||||
|
# - sudo -u ntfy-sh ntfy access
|
||||||
|
#
|
||||||
|
# debugging:
|
||||||
|
# - make sure that the keepalives are good:
|
||||||
|
# - on the subscriber machine, run `lsof -i4` to find the port being used
|
||||||
|
# - `sudo tcpdump tcp port <p>`
|
||||||
|
# - shouldn't be too spammy
|
||||||
|
#
|
||||||
|
# matrix integration:
|
||||||
|
# - the user must manually point synapse to the ntfy endpoint:
|
||||||
|
# - `curl --header "Authorization: <your_token>" --data '{ "app_display_name": "sane-nix moby", "app_id": "ntfy.uninsane.org", "data": { "url": "https://ntfy.uninsane.org/_matrix/push/v1/notify", "format": "event_id_only" }, "device_display_name": "sane-nix moby", "kind": "http", "lang": "en-US", "profile_tag": "", "pushkey": "https://ntfy.uninsane.org/TOPIC" }' localhost:8008/_matrix/client/v3/pushers/set`
|
||||||
|
# where the token is grabbed from Element's help&about page when logged in
|
||||||
|
# - to remove, send this `curl` with `"kind": null`
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
# subscribers need a non-443 public port to listen on as a way to easily differentiate this traffic
|
||||||
|
# at the IP layer, to enable e.g. wake-on-lan.
|
||||||
|
altPort = 2587;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.persist.sys.plaintext = [
|
||||||
|
# not 100% necessary to persist this, but ntfy does keep a 12hr (by default) cache
|
||||||
|
# for pushing notifications to users who become offline.
|
||||||
|
# ACLs also live here.
|
||||||
|
{ user = "ntfy-sh"; group ="ntfy-sh"; path = "/var/lib/ntfy-sh"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
services.ntfy-sh.enable = true;
|
||||||
|
services.ntfy-sh.settings = {
|
||||||
|
base-url = "https://ntfy.uninsane.org";
|
||||||
|
behind-proxy = true; # not sure if needed
|
||||||
|
# keepalive interval is a ntfy-specific keepalive thing, where it sends actual data down the wire.
|
||||||
|
# it's not simple TCP keepalive.
|
||||||
|
# defaults to 45s.
|
||||||
|
# note that the client may still do its own TCP-level keepalives, typically every 30s
|
||||||
|
keepalive-interval = "15m";
|
||||||
|
log-level = "trace"; # trace, debug, info (default), warn, error
|
||||||
|
auth-default-access = "deny-all";
|
||||||
|
};
|
||||||
|
systemd.services.ntfy-sh.serviceConfig.DynamicUser = lib.mkForce false;
|
||||||
|
systemd.services.ntfy-sh.preStart = ''
|
||||||
|
# make this specific topic read-write by world
|
||||||
|
# it would be better to use the token system, but that's extra complexity for e.g.
|
||||||
|
# how do i plumb a secret into the Matrix notification pusher
|
||||||
|
#
|
||||||
|
# note that this will fail upon first run, i.e. before ntfy has created its db.
|
||||||
|
# just restart the service.
|
||||||
|
topic=$(cat ${config.sops.secrets.ntfy-sh-topic.path})
|
||||||
|
${pkgs.ntfy-sh}/bin/ntfy access everyone "$topic" read-write
|
||||||
|
'';
|
||||||
|
|
||||||
|
sops.secrets."ntfy-sh-topic" = {
|
||||||
|
mode = "0440";
|
||||||
|
owner = config.users.users.ntfy-sh.name;
|
||||||
|
group = config.users.users.ntfy-sh.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."ntfy.uninsane.org" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
listen = [
|
||||||
|
{ addr = "0.0.0.0"; port = altPort; ssl = true; }
|
||||||
|
{ addr = "0.0.0.0"; port = 443; ssl = true; }
|
||||||
|
{ addr = "0.0.0.0"; port = 80; ssl = false; }
|
||||||
|
];
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:2586";
|
||||||
|
proxyWebsockets = true; #< support websocket upgrades. without that, `ntfy sub` hangs silently
|
||||||
|
recommendedProxySettings = true; #< adds headers so ntfy logs include the real IP
|
||||||
|
extraConfig = ''
|
||||||
|
# absurdly long timeout (86400s=24h) so that we never hang up on clients.
|
||||||
|
# make sure the client is smart enough to detect a broken proxy though!
|
||||||
|
proxy_read_timeout 86400s;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
sane.dns.zones."uninsane.org".inet.CNAME."ntfy" = "native";
|
||||||
|
|
||||||
|
sane.ports.ports."${builtins.toString altPort}" = {
|
||||||
|
protocol = [ "tcp" ];
|
||||||
|
visibleTo.lan = true;
|
||||||
|
visibleTo.wan = true;
|
||||||
|
description = "colin-ntfy.uninsane.org";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ in
|
|||||||
database: "pleroma",
|
database: "pleroma",
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
pool_size: 10,
|
pool_size: 10,
|
||||||
|
prepare: :named,
|
||||||
parameters: [
|
parameters: [
|
||||||
plan_cache_mode: "force_custom_plan"
|
plan_cache_mode: "force_custom_plan"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,12 +1,39 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
GiB = n: MiB 1024*n;
|
||||||
|
MiB = n: KiB 1024*n;
|
||||||
|
KiB = n: 1024*n;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode?
|
# TODO: mode?
|
||||||
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; }
|
{ user = "postgres"; group = "postgres"; path = "/var/lib/postgresql"; }
|
||||||
];
|
];
|
||||||
services.postgresql.enable = true;
|
services.postgresql.enable = true;
|
||||||
# services.postgresql.dataDir = "/opt/postgresql/13";
|
|
||||||
|
# HOW TO UPDATE:
|
||||||
|
# postgres version updates are manual and require intervention.
|
||||||
|
# - `sane-stop-all-servo`
|
||||||
|
# - `systemctl start postgresql`
|
||||||
|
# - as `sudo su postgres`:
|
||||||
|
# - `cd /var/log/postgresql`
|
||||||
|
# - `pg_dumpall > state.sql`
|
||||||
|
# - `echo placeholder > <new_version>` # to prevent state from being created earlier than we want
|
||||||
|
# - then, atomically:
|
||||||
|
# - update the `services.postgresql.package` here
|
||||||
|
# - `dataDir` is atomically updated to match package; don't touch
|
||||||
|
# - `nixos-rebuild --flake . switch ; sane-stop-all-servo`
|
||||||
|
# - `sudo rm -rf /var/lib/postgresql/<new_version>`
|
||||||
|
# - `systemctl start postgresql`
|
||||||
|
# - as `sudo su postgres`:
|
||||||
|
# - `cd /var/lib/postgreql`
|
||||||
|
# - `psql -f state.sql`
|
||||||
|
# - restart dependent services (maybe test one at a time)
|
||||||
|
|
||||||
|
services.postgresql.package = pkgs.postgresql_15;
|
||||||
|
|
||||||
|
|
||||||
# XXX colin: for a proper deploy, we'd want to include something for Pleroma here too.
|
# XXX colin: for a proper deploy, we'd want to include something for Pleroma here too.
|
||||||
# services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
|
# services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
|
||||||
# CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD '<password goes here>';
|
# CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD '<password goes here>';
|
||||||
@@ -17,10 +44,33 @@
|
|||||||
# LC_CTYPE = "C";
|
# LC_CTYPE = "C";
|
||||||
# '';
|
# '';
|
||||||
|
|
||||||
# TODO: perf tuning
|
# perf tuning
|
||||||
# - for recommended values see: <https://pgtune.leopard.in.ua/>
|
# - for recommended values see: <https://pgtune.leopard.in.ua/>
|
||||||
# - for official docs (sparse), see: <https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
|
# - for official docs (sparse), see: <https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
|
||||||
# services.postgresql.settings = { ... }
|
services.postgresql.settings = {
|
||||||
|
# DB Version: 15
|
||||||
|
# OS Type: linux
|
||||||
|
# DB Type: web
|
||||||
|
# Total Memory (RAM): 32 GB
|
||||||
|
# CPUs num: 12
|
||||||
|
# Data Storage: ssd
|
||||||
|
max_connections = 200;
|
||||||
|
shared_buffers = "8GB";
|
||||||
|
effective_cache_size = "24GB";
|
||||||
|
maintenance_work_mem = "2GB";
|
||||||
|
checkpoint_completion_target = 0.9;
|
||||||
|
wal_buffers = "16MB";
|
||||||
|
default_statistics_target = 100;
|
||||||
|
random_page_cost = 1.1;
|
||||||
|
effective_io_concurrency = 200;
|
||||||
|
work_mem = "10485kB";
|
||||||
|
min_wal_size = "1GB";
|
||||||
|
max_wal_size = "4GB";
|
||||||
|
max_worker_processes = 12;
|
||||||
|
max_parallel_workers_per_gather = 4;
|
||||||
|
max_parallel_workers = 12;
|
||||||
|
max_parallel_maintenance_workers = 4;
|
||||||
|
};
|
||||||
|
|
||||||
# daily backups to /var/backup
|
# daily backups to /var/backup
|
||||||
services.postgresqlBackup.enable = true;
|
services.postgresqlBackup.enable = true;
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
{ pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
sane.persist.sys.plaintext = [
|
sane.persist.sys.plaintext = [
|
||||||
# TODO: mode? we need this specifically for the stats tracking in .config/
|
# TODO: mode? we need this specifically for the stats tracking in .config/
|
||||||
{ user = "transmission"; group = "transmission"; path = "/var/lib/transmission"; }
|
{ user = "transmission"; group = config.users.users.transmission.group; path = "/var/lib/transmission"; }
|
||||||
];
|
];
|
||||||
|
users.users.transmission.extraGroups = [ "media" ];
|
||||||
|
|
||||||
services.transmission.enable = true;
|
services.transmission.enable = true;
|
||||||
|
services.transmission.package = pkgs.transmission_4; #< 2023/09/06: nixpkgs `transmission` defaults to old 3.00
|
||||||
|
#v setting `group` this way doesn't tell transmission to `chown` the files it creates
|
||||||
|
# it's a nixpkgs setting which just runs the transmission daemon as this group
|
||||||
|
services.transmission.group = "media";
|
||||||
|
|
||||||
|
# transmission will by default not allow the world to read its files.
|
||||||
|
services.transmission.downloadDirPermissions = "775";
|
||||||
|
services.transmission.extraFlags = [
|
||||||
|
"--log-level=debug"
|
||||||
|
];
|
||||||
|
|
||||||
services.transmission.settings = {
|
services.transmission.settings = {
|
||||||
|
# message-level = 3; #< enable for debug logging. 0-3, default is 2.
|
||||||
|
# 0.0.0.0 => allow rpc from any host: we gate it via firewall and auth requirement
|
||||||
rpc-bind-address = "0.0.0.0";
|
rpc-bind-address = "0.0.0.0";
|
||||||
#rpc-host-whitelist = "bt.uninsane.org";
|
#rpc-host-whitelist = "bt.uninsane.org";
|
||||||
#rpc-whitelist = "*.*.*.*";
|
#rpc-whitelist = "*.*.*.*";
|
||||||
@@ -17,9 +32,8 @@
|
|||||||
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
|
rpc-password = "{503fc8928344f495efb8e1f955111ca5c862ce0656SzQnQ5";
|
||||||
rpc-whitelist-enabled = false;
|
rpc-whitelist-enabled = false;
|
||||||
|
|
||||||
# download-dir = "/opt/uninsane/media/";
|
|
||||||
# hopefully, make the downloads world-readable
|
# hopefully, make the downloads world-readable
|
||||||
umask = 0;
|
# umask = 0; #< default is 2: i.e. deny writes from world
|
||||||
|
|
||||||
# force peer connections to be encrypted
|
# force peer connections to be encrypted
|
||||||
encryption = 2;
|
encryption = 2;
|
||||||
@@ -35,17 +49,18 @@
|
|||||||
|
|
||||||
download-dir = "/var/lib/uninsane/media";
|
download-dir = "/var/lib/uninsane/media";
|
||||||
incomplete-dir = "/var/lib/uninsane/media/incomplete";
|
incomplete-dir = "/var/lib/uninsane/media/incomplete";
|
||||||
|
# transmission regularly fails to move stuff from the incomplete dir to the main one, so disable:
|
||||||
|
# TODO: uncomment this line!
|
||||||
|
incomplete-dir-enabled = false;
|
||||||
};
|
};
|
||||||
# transmission will by default not allow the world to read its files.
|
|
||||||
services.transmission.downloadDirPermissions = "775";
|
|
||||||
|
|
||||||
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.after = [ "wireguard-wg-ovpns.service" ];
|
||||||
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
systemd.services.transmission.partOf = [ "wireguard-wg-ovpns.service" ];
|
||||||
systemd.services.transmission.serviceConfig = {
|
systemd.services.transmission.serviceConfig = {
|
||||||
# run this behind the OVPN static VPN
|
# run this behind the OVPN static VPN
|
||||||
NetworkNamespacePath = "/run/netns/ovpns";
|
NetworkNamespacePath = "/run/netns/ovpns";
|
||||||
LogLevelMax = "warning";
|
Restart = "on-failure";
|
||||||
|
RestartSec = "30s";
|
||||||
};
|
};
|
||||||
|
|
||||||
# service to automatically backup torrents i add to transmission
|
# service to automatically backup torrents i add to transmission
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
|
# TODO: split this file apart into smaller files to make it easier to understand
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
nativeAddrs = lib.mapAttrs (_name: builtins.head) config.sane.dns.zones."uninsane.org".inet.A;
|
||||||
|
bindOvpn = "10.0.1.5";
|
||||||
|
in lib.mkMerge [
|
||||||
{
|
{
|
||||||
services.trust-dns.enable = true;
|
services.trust-dns.enable = true;
|
||||||
|
|
||||||
services.trust-dns.settings.listen_addrs_ipv4 = [
|
|
||||||
# specify each address explicitly, instead of using "*".
|
|
||||||
# this ensures responses are sent from the address at which the request was received.
|
|
||||||
config.sane.hosts.by-name."servo".lan-ip
|
|
||||||
"10.0.1.5"
|
|
||||||
];
|
|
||||||
# don't bind to IPv6 until i explicitly test that stack
|
# don't bind to IPv6 until i explicitly test that stack
|
||||||
services.trust-dns.settings.listen_addrs_ipv6 = [];
|
services.trust-dns.settings.listen_addrs_ipv6 = [];
|
||||||
services.trust-dns.quiet = true;
|
services.trust-dns.quiet = true;
|
||||||
@@ -34,18 +33,19 @@
|
|||||||
sane.dns.zones."uninsane.org".inet = {
|
sane.dns.zones."uninsane.org".inet = {
|
||||||
SOA."@" = ''
|
SOA."@" = ''
|
||||||
ns1.uninsane.org. admin-dns.uninsane.org. (
|
ns1.uninsane.org. admin-dns.uninsane.org. (
|
||||||
2022122101 ; Serial
|
2023092101 ; Serial
|
||||||
4h ; Refresh
|
4h ; Refresh
|
||||||
30m ; Retry
|
30m ; Retry
|
||||||
7d ; Expire
|
7d ; Expire
|
||||||
5m) ; Negative response TTL
|
5m) ; Negative response TTL
|
||||||
'';
|
'';
|
||||||
TXT."rev" = "2023052901";
|
TXT."rev" = "2023092101";
|
||||||
|
|
||||||
CNAME."native" = "%CNAMENATIVE%";
|
CNAME."native" = "%CNAMENATIVE%";
|
||||||
A."@" = "%ANATIVE%";
|
A."@" = "%ANATIVE%";
|
||||||
A."wan" = "%AWAN%";
|
A."servo.wan" = "%AWAN%";
|
||||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||||
|
A."servo.hn" = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||||
|
|
||||||
# XXX NS records must also not be CNAME
|
# XXX NS records must also not be CNAME
|
||||||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
||||||
@@ -63,55 +63,23 @@
|
|||||||
|
|
||||||
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
services.trust-dns.settings.zones = [ "uninsane.org" ];
|
||||||
|
|
||||||
services.trust-dns.package =
|
# TODO: can i transform this into some sort of service group?
|
||||||
let
|
# have `systemctl restart trust-dns.service` restart all the individual services?
|
||||||
sed = "${pkgs.gnused}/bin/sed";
|
|
||||||
zone-dir = "/var/lib/trust-dns";
|
|
||||||
zone-wan = "${zone-dir}/wan/uninsane.org.zone";
|
|
||||||
zone-lan = "${zone-dir}/lan/uninsane.org.zone";
|
|
||||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
|
|
||||||
in pkgs.writeShellScriptBin "named" ''
|
|
||||||
# compute wan/lan values
|
|
||||||
mkdir -p ${zone-dir}/{ovpn,wan,lan}
|
|
||||||
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
|
||||||
lan=${config.sane.hosts.by-name."servo".lan-ip}
|
|
||||||
|
|
||||||
# create specializations that resolve native.uninsane.org to different CNAMEs
|
|
||||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
|
||||||
| ${sed} s/%CNAMENATIVE%/wan/ \
|
|
||||||
| ${sed} s/%ANATIVE%/$wan/ \
|
|
||||||
> ${zone-wan}
|
|
||||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
|
||||||
| ${sed} s/%CNAMENATIVE%/servo.lan/ \
|
|
||||||
| ${sed} s/%ANATIVE%/$lan/ \
|
|
||||||
> ${zone-lan}
|
|
||||||
|
|
||||||
# launch the different interfaces, separately
|
|
||||||
${pkgs.trust-dns}/bin/named --port 53 --zonedir ${zone-dir}/wan/ $@ &
|
|
||||||
WANPID=$!
|
|
||||||
${pkgs.trust-dns}/bin/named --port 1053 --zonedir ${zone-dir}/lan/ $@ &
|
|
||||||
LANPID=$!
|
|
||||||
|
|
||||||
# wait until any of the processes exits, then kill them all and exit error
|
|
||||||
while kill -0 $WANPID $LANPID ; do
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
kill $WANPID $LANPID
|
|
||||||
exit 1
|
|
||||||
'';
|
|
||||||
|
|
||||||
systemd.services.trust-dns.serviceConfig = {
|
systemd.services.trust-dns.serviceConfig = {
|
||||||
DynamicUser = lib.mkForce false;
|
DynamicUser = lib.mkForce false;
|
||||||
User = "trust-dns";
|
User = "trust-dns";
|
||||||
Group = "trust-dns";
|
Group = "trust-dns";
|
||||||
|
wantedBy = lib.mkForce [];
|
||||||
};
|
};
|
||||||
|
systemd.services.trust-dns.enable = false;
|
||||||
|
|
||||||
users.groups.trust-dns = {};
|
users.groups.trust-dns = {};
|
||||||
users.users.trust-dns = {
|
users.users.trust-dns = {
|
||||||
group = "trust-dns";
|
group = "trust-dns";
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
# sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||||
|
|
||||||
networking.nat.enable = true;
|
networking.nat.enable = true;
|
||||||
networking.nat.extraCommands = ''
|
networking.nat.extraCommands = ''
|
||||||
@@ -127,12 +95,112 @@
|
|||||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||||
-j DNAT --to-destination :1053
|
-j DNAT --to-destination :1053
|
||||||
'';
|
'';
|
||||||
|
|
||||||
sane.ports.ports."1053" = {
|
sane.ports.ports."1053" = {
|
||||||
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||||
# TODO: try nixos-nat-post instead?
|
# TODO: try nixos-nat-post instead?
|
||||||
|
# TODO: or, don't NAT from port 53 -> port 1053, but rather nat from LAN addr to a loopback addr.
|
||||||
|
# - this is complicated in that loopback is a different interface than eth0, so rewriting the destination address would cause the packets to just be dropped by the interface
|
||||||
protocol = [ "udp" "tcp" ];
|
protocol = [ "udp" "tcp" ];
|
||||||
visibleTo.lan = true;
|
visibleTo.lan = true;
|
||||||
description = "colin-redirected-dns-for-lan-namespace";
|
description = "colin-redirected-dns-for-lan-namespace";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
systemd.services =
|
||||||
|
let
|
||||||
|
sed = "${pkgs.gnused}/bin/sed";
|
||||||
|
stateDir = "/var/lib/trust-dns";
|
||||||
|
zoneTemplate = pkgs.writeText "uninsane.org.zone.in" config.sane.dns.zones."uninsane.org".rendered;
|
||||||
|
|
||||||
|
zoneDirFor = flavor: "${stateDir}/${flavor}";
|
||||||
|
zoneFor = flavor: "${zoneDirFor flavor}/uninsane.org.zone";
|
||||||
|
mkTrustDnsService = opts: flavor: let
|
||||||
|
flags = let baseCfg = config.services.trust-dns; in
|
||||||
|
(lib.optional baseCfg.debug "--debug") ++ (lib.optional baseCfg.quiet "--quiet");
|
||||||
|
flagsStr = builtins.concatStringsSep " " flags;
|
||||||
|
|
||||||
|
anative = nativeAddrs."servo.${flavor}";
|
||||||
|
|
||||||
|
toml = pkgs.formats.toml { };
|
||||||
|
configTemplate = opts.config or (toml.generate "trust-dns-${flavor}.toml" (
|
||||||
|
(
|
||||||
|
lib.filterAttrsRecursive (_: v: v != null) config.services.trust-dns.settings
|
||||||
|
) // {
|
||||||
|
listen_addrs_ipv4 = opts.listen or [ anative ];
|
||||||
|
}
|
||||||
|
));
|
||||||
|
configFile = "${stateDir}/${flavor}-config.toml";
|
||||||
|
|
||||||
|
port = opts.port or 53;
|
||||||
|
in {
|
||||||
|
description = "trust-dns Domain Name Server (serving ${flavor})";
|
||||||
|
unitConfig.Documentation = "https://trust-dns.org/";
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||||
|
${sed} s/%AWAN%/$wan/ ${configTemplate} > ${configFile}
|
||||||
|
'' + lib.optionalString (!opts ? config) ''
|
||||||
|
mkdir -p ${zoneDirFor flavor}
|
||||||
|
${sed} \
|
||||||
|
-e s/%CNAMENATIVE%/servo.${flavor}/ \
|
||||||
|
-e s/%ANATIVE%/${anative}/ \
|
||||||
|
-e s/%AWAN%/$wan/ \
|
||||||
|
${zoneTemplate} > ${zoneFor flavor}
|
||||||
|
'';
|
||||||
|
serviceConfig = config.systemd.services.trust-dns.serviceConfig // {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.trust-dns}/bin/trust-dns \
|
||||||
|
--port ${builtins.toString port} \
|
||||||
|
--zonedir ${zoneDirFor flavor}/ \
|
||||||
|
--config ${configFile} ${flagsStr}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
trust-dns-wan = mkTrustDnsService { listen = [ nativeAddrs."servo.lan" bindOvpn ]; } "wan";
|
||||||
|
trust-dns-lan = mkTrustDnsService { port = 1053; } "lan";
|
||||||
|
trust-dns-hn = mkTrustDnsService { port = 1053; } "hn";
|
||||||
|
trust-dns-hn-resolver = mkTrustDnsService {
|
||||||
|
config = pkgs.writeText "hn-resolver-config.toml" ''
|
||||||
|
# i host a resolver in the wireguard VPN so that clients can resolve DNS through the VPN.
|
||||||
|
# (that's what this file achieves).
|
||||||
|
#
|
||||||
|
# one would expect this resolver could host the authoritative zone for `uninsane.org`, and then forward everything else to the system resolver...
|
||||||
|
# and while that works for `dig`, it breaks for `nslookup` (and so `ssh`, etc).
|
||||||
|
#
|
||||||
|
# DNS responses include a flag for if the responding server is the authority of the zone queried.
|
||||||
|
# it seems that default Linux stub resolvers either:
|
||||||
|
# - expect DNSSEC when the response includes that bit, or
|
||||||
|
# - expect A records to be in the `answer` section instead of `additional` section.
|
||||||
|
# or perhaps something more nuanced. but for `nslookup` to be reliable, it has to talk to an
|
||||||
|
# instance of trust-dns which is strictly a resolver, with no authority.
|
||||||
|
# hence, this config: a resolver which forwards to the actual authority.
|
||||||
|
|
||||||
|
listen_addrs_ipv4 = ["${nativeAddrs."servo.hn"}"]
|
||||||
|
listen_addrs_ipv6 = []
|
||||||
|
|
||||||
|
[[zones]]
|
||||||
|
zone = "uninsane.org"
|
||||||
|
zone_type = "Forward"
|
||||||
|
stores = { type = "forward", name_servers = [{ socket_addr = "${nativeAddrs."servo.hn"}:1053", protocol = "udp", trust_nx_responses = true }] }
|
||||||
|
|
||||||
|
[[zones]]
|
||||||
|
# forward the root zone to the local DNS resolver
|
||||||
|
zone = "."
|
||||||
|
zone_type = "Forward"
|
||||||
|
stores = { type = "forward", name_servers = [{ socket_addr = "127.0.0.53:53", protocol = "udp", trust_nx_responses = true }] }
|
||||||
|
'';
|
||||||
|
} "hn-resolver";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.services.dyn-dns.restartOnChange = [
|
||||||
|
"trust-dns-wan.service"
|
||||||
|
"trust-dns-lan.service"
|
||||||
|
"trust-dns-hn.service"
|
||||||
|
# "trust-dns-hn-resolver.service" # doesn't need restart because it doesn't know about WAN IP
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./feeds.nix
|
./feeds.nix
|
||||||
./fs.nix
|
./fs.nix
|
||||||
./hardware.nix
|
./hardware
|
||||||
./home
|
./home
|
||||||
|
./hosts.nix
|
||||||
./ids.nix
|
./ids.nix
|
||||||
./machine-id.nix
|
./machine-id.nix
|
||||||
./net.nix
|
./net.nix
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# where to find good stuff?
|
# where to find good stuff?
|
||||||
|
# - podcasts w/ a community: <https://lemmyverse.net/communities?query=podcast>
|
||||||
# - podcast rec thread: <https://lemmy.ml/post/1565858>
|
# - podcast rec thread: <https://lemmy.ml/post/1565858>
|
||||||
#
|
#
|
||||||
# candidates:
|
# candidates:
|
||||||
@@ -67,7 +68,12 @@ let
|
|||||||
(fromDb "craphound.com" // pol)
|
(fromDb "craphound.com" // pol)
|
||||||
## Maggie Killjoy -- referenced by Cory Doctorow
|
## Maggie Killjoy -- referenced by Cory Doctorow
|
||||||
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol)
|
(fromDb "omny.fm/shows/cool-people-who-did-cool-stuff" // pol)
|
||||||
|
## also Maggie Killjoy
|
||||||
|
(fromDb "feeds.megaphone.fm/behindthebastards" // pol)
|
||||||
|
## Jennifer Briney
|
||||||
(fromDb "congressionaldish.libsyn.com" // pol)
|
(fromDb "congressionaldish.libsyn.com" // pol)
|
||||||
|
(fromDb "werenotwrong.fireside.fm" // pol)
|
||||||
|
(fromDb "politicalorphanage.libsyn.com" // pol)
|
||||||
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
# (mkPod "https://podcasts.la.utexas.edu/this-is-democracy/feed/podcast/" // pol // weekly)
|
||||||
## Civboot -- https://anchor.fm/civboot
|
## Civboot -- https://anchor.fm/civboot
|
||||||
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
(fromDb "anchor.fm/s/34c7232c/podcast/rss" // tech)
|
||||||
@@ -110,7 +116,7 @@ let
|
|||||||
(fromDb "cast.postmarketos.org" // tech)
|
(fromDb "cast.postmarketos.org" // tech)
|
||||||
(fromDb "podcast.thelinuxexp.com" // tech)
|
(fromDb "podcast.thelinuxexp.com" // tech)
|
||||||
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
## Michael Malice - Your Welcome -- also available here: <https://origin.podcastone.com/podcast?categoryID2=2232>
|
||||||
(fromDb "rss.art19.com/your-welcome" // pol)
|
# (fromDb "rss.art19.com/your-welcome" // pol)
|
||||||
(fromDb "seattlenice.buzzsprout.com" // pol)
|
(fromDb "seattlenice.buzzsprout.com" // pol)
|
||||||
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
## Sci-Fi? has Peter Watts; author of No Moods, Ads or Cutesy Fucking Icons (rifters.com)
|
||||||
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
|
(fromDb "talesfromthebridge.buzzsprout.com" // tech)
|
||||||
@@ -125,6 +131,10 @@ let
|
|||||||
(fromDb "feeds.simplecast.com/82FI35Px" // pol)
|
(fromDb "feeds.simplecast.com/82FI35Px" // pol)
|
||||||
## Wireshark Podcast o_0
|
## Wireshark Podcast o_0
|
||||||
(fromDb "sharkbytes.transistor.fm" // tech)
|
(fromDb "sharkbytes.transistor.fm" // tech)
|
||||||
|
## 3/4 German; 1/4 eps are English
|
||||||
|
(fromDb "omegataupodcast.net" // tech)
|
||||||
|
## Lateral with Tom Scott
|
||||||
|
(mkPod "https://audioboom.com/channels/5097784.rss" // tech)
|
||||||
];
|
];
|
||||||
|
|
||||||
texts = [
|
texts = [
|
||||||
@@ -140,7 +150,7 @@ let
|
|||||||
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
(mkText "https://linuxphoneapps.org/blog/atom.xml" // tech // infrequent)
|
||||||
(fromDb "tuxphones.com" // tech)
|
(fromDb "tuxphones.com" // tech)
|
||||||
(fromDb "spectrum.ieee.org" // tech)
|
(fromDb "spectrum.ieee.org" // tech)
|
||||||
(fromDb "theregister.com" // tech)
|
# (fromDb "theregister.com" // tech)
|
||||||
(fromDb "thisweek.gnome.org" // tech)
|
(fromDb "thisweek.gnome.org" // tech)
|
||||||
# more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
|
# more nixos stuff here, but unclear how to subscribe: <https://nixos.org/blog/categories.html>
|
||||||
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent)
|
(mkText "https://nixos.org/blog/announcements-rss.xml" // tech // infrequent)
|
||||||
@@ -156,6 +166,8 @@ let
|
|||||||
(fromDb "uninsane.org" // tech)
|
(fromDb "uninsane.org" // tech)
|
||||||
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
(fromDb "ascii.textfiles.com" // tech) # Jason Scott
|
||||||
(fromDb "xn--gckvb8fzb.com" // tech)
|
(fromDb "xn--gckvb8fzb.com" // tech)
|
||||||
|
(fromDb "amosbbatto.wordpress.com" // tech)
|
||||||
|
(fromDb "fasterthanli.me" // tech)
|
||||||
(fromDb "mg.lol" // tech)
|
(fromDb "mg.lol" // tech)
|
||||||
# (fromDb "drewdevault.com" // tech)
|
# (fromDb "drewdevault.com" // tech)
|
||||||
## Ken Shirriff
|
## Ken Shirriff
|
||||||
@@ -241,6 +253,7 @@ let
|
|||||||
images = [
|
images = [
|
||||||
(fromDb "smbc-comics.com" // img // humor)
|
(fromDb "smbc-comics.com" // img // humor)
|
||||||
(fromDb "xkcd.com" // img // humor)
|
(fromDb "xkcd.com" // img // humor)
|
||||||
|
(fromDb "turnoff.us" // img // humor)
|
||||||
(fromDb "pbfcomics.com" // img // humor)
|
(fromDb "pbfcomics.com" // img // humor)
|
||||||
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
# (mkImg "http://dilbert.com/feed" // humor // daily)
|
||||||
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
(fromDb "poorlydrawnlines.com/feed" // img // humor)
|
||||||
|
|||||||
@@ -1,136 +1,143 @@
|
|||||||
# docs
|
# docs
|
||||||
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
# - x-systemd options: <https://www.freedesktop.org/software/systemd/man/systemd.mount.html>
|
||||||
|
|
||||||
{ pkgs, sane-lib, ... }:
|
{ lib, pkgs, sane-lib, ... }:
|
||||||
|
|
||||||
let fsOpts = rec {
|
let
|
||||||
common = [
|
fsOpts = rec {
|
||||||
"_netdev"
|
common = [
|
||||||
"noatime"
|
"_netdev"
|
||||||
"user" # allow any user with access to the device to mount the fs
|
"noatime"
|
||||||
"x-systemd.requires=network-online.target"
|
"user" # allow any user with access to the device to mount the fs
|
||||||
"x-systemd.after=network-online.target"
|
"x-systemd.requires=network-online.target"
|
||||||
"x-systemd.mount-timeout=10s" # how long to wait for mount **and** how long to wait for unmount
|
"x-systemd.after=network-online.target"
|
||||||
];
|
"x-systemd.mount-timeout=10s" # how long to wait for mount **and** how long to wait for unmount
|
||||||
auto = [ "x-systemd.automount" ];
|
];
|
||||||
noauto = [ "noauto" ]; # don't mount as part of remote-fs.target
|
auto = [ "x-systemd.automount" ];
|
||||||
wg = [
|
noauto = [ "noauto" ]; # don't mount as part of remote-fs.target
|
||||||
"x-systemd.requires=wireguard-wg-home.service"
|
wg = [
|
||||||
"x-systemd.after=wireguard-wg-home.service"
|
"x-systemd.requires=wireguard-wg-home.service"
|
||||||
];
|
"x-systemd.after=wireguard-wg-home.service"
|
||||||
|
];
|
||||||
|
|
||||||
ssh = common ++ [
|
ssh = common ++ [
|
||||||
"identityfile=/home/colin/.ssh/id_ed25519"
|
"identityfile=/home/colin/.ssh/id_ed25519"
|
||||||
"allow_other"
|
"allow_other"
|
||||||
"default_permissions"
|
"default_permissions"
|
||||||
];
|
];
|
||||||
sshColin = ssh ++ [
|
sshColin = ssh ++ [
|
||||||
"transform_symlinks"
|
"transform_symlinks"
|
||||||
"idmap=user"
|
"idmap=user"
|
||||||
"uid=1000"
|
"uid=1000"
|
||||||
"gid=100"
|
"gid=100"
|
||||||
];
|
];
|
||||||
sshRoot = ssh ++ [
|
sshRoot = ssh ++ [
|
||||||
# we don't transform_symlinks because that breaks the validity of remote /nix stores
|
# we don't transform_symlinks because that breaks the validity of remote /nix stores
|
||||||
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
|
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
|
||||||
];
|
];
|
||||||
# in the event of hunt NFS mounts, consider:
|
# in the event of hunt NFS mounts, consider:
|
||||||
# - <https://unix.stackexchange.com/questions/31979/stop-broken-nfs-mounts-from-locking-a-directory>
|
# - <https://unix.stackexchange.com/questions/31979/stop-broken-nfs-mounts-from-locking-a-directory>
|
||||||
|
|
||||||
# NFS options: <https://linux.die.net/man/5/nfs>
|
# NFS options: <https://linux.die.net/man/5/nfs>
|
||||||
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
# actimeo=n = how long (in seconds) to cache file/dir attributes (default: 3-60s)
|
||||||
# bg = retry failed mounts in the background
|
# bg = retry failed mounts in the background
|
||||||
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
# retry=n = for how many minutes `mount` will retry NFS mount operation
|
||||||
# soft = on "major timeout", report I/O error to userspace
|
# soft = on "major timeout", report I/O error to userspace
|
||||||
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
# retrans=n = how many times to retry a NFS request before giving userspace a "server not responding" error (default: 3)
|
||||||
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
# timeo=n = number of *deciseconds* to wait for a response before retrying it (default: 600)
|
||||||
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
# note: client uses a linear backup, so the second request will have double this timeout, then triple, etc.
|
||||||
nfs = common ++ [
|
nfs = common ++ [
|
||||||
# "actimeo=10"
|
# "actimeo=10"
|
||||||
"bg"
|
"bg"
|
||||||
"retrans=4"
|
"retrans=4"
|
||||||
"retry=0"
|
"retry=0"
|
||||||
"soft"
|
"soft"
|
||||||
"timeo=15"
|
"timeo=15"
|
||||||
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
"nofail" # don't fail remote-fs.target when this mount fails (not an option for sshfs else would be common)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
remoteHome = host: {
|
||||||
|
fileSystems."/mnt/${host}-home" = {
|
||||||
|
device = "colin@${host}:/home/colin";
|
||||||
|
fsType = "fuse.sshfs";
|
||||||
|
options = fsOpts.sshColin ++ fsOpts.noauto;
|
||||||
|
noCheck = true;
|
||||||
|
};
|
||||||
|
sane.fs."/mnt/${host}-home" = sane-lib.fs.wantedDir;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
lib.mkMerge [
|
||||||
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
|
{
|
||||||
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
# some services which use private directories error if the parent (/var/lib/private) isn't 700.
|
||||||
|
sane.fs."/var/lib/private".dir.acl.mode = "0700";
|
||||||
|
|
||||||
# in-memory compressed RAM
|
# in-memory compressed RAM
|
||||||
# defaults to compressing at most 50% size of RAM
|
# defaults to compressing at most 50% size of RAM
|
||||||
# claimed compression ratio is about 2:1
|
# claimed compression ratio is about 2:1
|
||||||
# - but on moby w/ zstd default i see 4-7:1 (ratio lowers as it fills)
|
# - but on moby w/ zstd default i see 4-7:1 (ratio lowers as it fills)
|
||||||
# note that idle overhead is about 0.05% of capacity (e.g. 2B per 4kB page)
|
# note that idle overhead is about 0.05% of capacity (e.g. 2B per 4kB page)
|
||||||
# docs: <https://www.kernel.org/doc/Documentation/blockdev/zram.txt>
|
# docs: <https://www.kernel.org/doc/Documentation/blockdev/zram.txt>
|
||||||
#
|
#
|
||||||
# to query effectiveness:
|
# to query effectiveness:
|
||||||
# `cat /sys/block/zram0/mm_stat`. whitespace separated fields:
|
# `cat /sys/block/zram0/mm_stat`. whitespace separated fields:
|
||||||
# - *orig_data_size* (bytes)
|
# - *orig_data_size* (bytes)
|
||||||
# - *compr_data_size* (bytes)
|
# - *compr_data_size* (bytes)
|
||||||
# - mem_used_total (bytes)
|
# - mem_used_total (bytes)
|
||||||
# - mem_limit (bytes)
|
# - mem_limit (bytes)
|
||||||
# - mem_used_max (bytes)
|
# - mem_used_max (bytes)
|
||||||
# - *same_pages* (pages which are e.g. all zeros (consumes no additional mem))
|
# - *same_pages* (pages which are e.g. all zeros (consumes no additional mem))
|
||||||
# - *pages_compacted* (pages which have been freed thanks to compression)
|
# - *pages_compacted* (pages which have been freed thanks to compression)
|
||||||
# - huge_pages (incompressible)
|
# - huge_pages (incompressible)
|
||||||
#
|
#
|
||||||
# see also:
|
# see also:
|
||||||
# - `man zramctl`
|
# - `man zramctl`
|
||||||
zramSwap.enable = true;
|
zramSwap.enable = true;
|
||||||
# how much ram can be swapped into the zram device.
|
# how much ram can be swapped into the zram device.
|
||||||
# this shouldn't be higher than the observed compression ratio.
|
# this shouldn't be higher than the observed compression ratio.
|
||||||
# the default is 50% (why?)
|
# the default is 50% (why?)
|
||||||
# 100% should be "guaranteed" safe so long as the data is even *slightly* compressible.
|
# 100% should be "guaranteed" safe so long as the data is even *slightly* compressible.
|
||||||
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
# but it decreases working memory under the heaviest of loads by however much space the compressed memory occupies (e.g. 50% if 2:1; 25% if 4:1)
|
||||||
zramSwap.memoryPercent = 100;
|
zramSwap.memoryPercent = 100;
|
||||||
|
|
||||||
# fileSystems."/mnt/servo-nfs" = {
|
# fileSystems."/mnt/servo-nfs" = {
|
||||||
# device = "servo-hn:/";
|
# device = "servo-hn:/";
|
||||||
# noCheck = true;
|
# noCheck = true;
|
||||||
# fsType = "nfs";
|
# fsType = "nfs";
|
||||||
# options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
# options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
||||||
# };
|
# };
|
||||||
fileSystems."/mnt/servo-nfs/media" = {
|
fileSystems."/mnt/servo-nfs/media" = {
|
||||||
device = "servo-hn:/media";
|
device = "servo-hn:/media";
|
||||||
noCheck = true;
|
noCheck = true;
|
||||||
fsType = "nfs";
|
fsType = "nfs";
|
||||||
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
||||||
};
|
};
|
||||||
# fileSystems."/mnt/servo-media-nfs" = {
|
fileSystems."/mnt/servo-nfs/playground" = {
|
||||||
# device = "servo-hn:/media";
|
device = "servo-hn:/playground";
|
||||||
# noCheck = true;
|
noCheck = true;
|
||||||
# fsType = "nfs";
|
fsType = "nfs";
|
||||||
# options = fsOpts.common ++ fsOpts.auto;
|
options = fsOpts.nfs ++ fsOpts.auto ++ fsOpts.wg;
|
||||||
# };
|
};
|
||||||
sane.fs."/mnt/servo-media" = sane-lib.fs.wantedSymlinkTo "/mnt/servo-nfs/media";
|
# fileSystems."/mnt/servo-media-nfs" = {
|
||||||
|
# device = "servo-hn:/media";
|
||||||
|
# noCheck = true;
|
||||||
|
# fsType = "nfs";
|
||||||
|
# options = fsOpts.common ++ fsOpts.auto;
|
||||||
|
# };
|
||||||
|
sane.fs."/mnt/servo-media" = sane-lib.fs.wantedSymlinkTo "/mnt/servo-nfs/media";
|
||||||
|
|
||||||
fileSystems."/mnt/desko-home" = {
|
environment.pathsToLink = [
|
||||||
device = "colin@desko:/home/colin";
|
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
|
||||||
fsType = "fuse.sshfs";
|
# we can only link whole directories here, even though we're only interested in pkgs.openssh
|
||||||
options = fsOpts.sshColin ++ fsOpts.noauto;
|
"/libexec"
|
||||||
noCheck = true;
|
];
|
||||||
};
|
|
||||||
sane.fs."/mnt/desko-home" = sane-lib.fs.wantedDir;
|
|
||||||
fileSystems."/mnt/desko-root" = {
|
|
||||||
device = "colin@desko:/";
|
|
||||||
fsType = "fuse.sshfs";
|
|
||||||
options = fsOpts.sshRoot ++ fsOpts.noauto;
|
|
||||||
noCheck = true;
|
|
||||||
};
|
|
||||||
sane.fs."/mnt/desko-root" = sane-lib.fs.wantedDir;
|
|
||||||
|
|
||||||
environment.pathsToLink = [
|
environment.systemPackages = [
|
||||||
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
|
pkgs.sshfs-fuse
|
||||||
# we can only link whole directories here, even though we're only interested in pkgs.openssh
|
];
|
||||||
"/libexec"
|
}
|
||||||
];
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
(remoteHome "desko")
|
||||||
pkgs.sshfs-fuse
|
(remoteHome "lappy")
|
||||||
];
|
(remoteHome "moby")
|
||||||
}
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
{ lib, pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
./x86_64.nix
|
||||||
|
];
|
||||||
|
|
||||||
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
boot.initrd.supportedFilesystems = [ "ext4" "btrfs" "ext2" "ext3" "vfat" ];
|
||||||
# useful emergency utils
|
# useful emergency utils
|
||||||
boot.initrd.extraUtilsCommands = ''
|
boot.initrd.extraUtilsCommands = ''
|
||||||
@@ -26,6 +30,20 @@
|
|||||||
|
|
||||||
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
|
# powertop will default to putting USB devices -- including HID -- to sleep after TWO SECONDS
|
||||||
powerManagement.powertop.enable = false;
|
powerManagement.powertop.enable = false;
|
||||||
|
# linux CPU governor: <https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt>
|
||||||
|
# - options:
|
||||||
|
# - "powersave" => force CPU to always run at lowest supported frequency
|
||||||
|
# - "performance" => force CPU to always run at highest frequency
|
||||||
|
# - "ondemand" => adjust frequency based on load
|
||||||
|
# - "conservative" (ondemand but slower to adjust)
|
||||||
|
# - "schedutil"
|
||||||
|
# - "userspace"
|
||||||
|
# - not all options are available for all platforms
|
||||||
|
# - intel (intel_pstate) appears to manage scaling w/o intervention/control from the OS.
|
||||||
|
# - AMD (acpi-cpufreq) appears to manage scaling via the OS *or* HW. but the ondemand defaults never put it to max hardware frequency.
|
||||||
|
# - qualcomm (cpufreq-dt) appears to manage scaling *only* via the OS. ondemand governor exercises the full range.
|
||||||
|
# - query details with `sudo cpupower frequency-info`
|
||||||
|
powerManagement.cpuFreqGovernor = "ondemand";
|
||||||
|
|
||||||
services.logind.extraConfig = ''
|
services.logind.extraConfig = ''
|
||||||
# don’t shutdown when power button is short-pressed
|
# don’t shutdown when power button is short-pressed
|
||||||
@@ -9,12 +9,7 @@
|
|||||||
# efi_pstore evivars
|
# efi_pstore evivars
|
||||||
];
|
];
|
||||||
|
|
||||||
powerManagement.cpuFreqGovernor = "powersave";
|
|
||||||
hardware.cpu.amd.updateMicrocode = true; # desktop
|
hardware.cpu.amd.updateMicrocode = true; # desktop
|
||||||
hardware.cpu.intel.updateMicrocode = true; # laptop
|
hardware.cpu.intel.updateMicrocode = true; # laptop
|
||||||
|
|
||||||
hardware.opengl.driSupport = true;
|
|
||||||
# For 32 bit applications
|
|
||||||
hardware.opengl.driSupport32Bit = true;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
39
hosts/common/hosts.nix
Normal file
39
hosts/common/hosts.nix
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# TODO: this should be populated per-host
|
||||||
|
sane.hosts.by-name."desko" = {
|
||||||
|
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||||
|
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||||
|
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
|
||||||
|
wg-home.ip = "10.0.10.22";
|
||||||
|
lan-ip = "10.78.79.52";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.hosts.by-name."lappy" = {
|
||||||
|
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||||
|
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||||
|
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
|
||||||
|
wg-home.ip = "10.0.10.20";
|
||||||
|
lan-ip = "10.78.79.53";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.hosts.by-name."moby" = {
|
||||||
|
ssh.authorized = lib.mkDefault false; # moby's too easy to hijack: don't let it ssh places
|
||||||
|
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||||
|
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||||
|
wg-home.pubkey = "I7XIR1hm8bIzAtcAvbhWOwIAabGkuEvbWH/3kyIB1yA=";
|
||||||
|
wg-home.ip = "10.0.10.48";
|
||||||
|
lan-ip = "10.78.79.54";
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.hosts.by-name."servo" = {
|
||||||
|
ssh.authorized = lib.mkDefault false; # servo presents too many services to the internet: easy atack vector
|
||||||
|
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||||
|
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||||
|
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
|
||||||
|
wg-home.ip = "10.0.10.5";
|
||||||
|
wg-home.endpoint = "uninsane.org:51820";
|
||||||
|
lan-ip = "10.78.79.51";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -44,6 +44,11 @@
|
|||||||
sane.ids.sftpgo.gid = 2410;
|
sane.ids.sftpgo.gid = 2410;
|
||||||
sane.ids.trust-dns.uid = 2411;
|
sane.ids.trust-dns.uid = 2411;
|
||||||
sane.ids.trust-dns.gid = 2411;
|
sane.ids.trust-dns.gid = 2411;
|
||||||
|
sane.ids.export.gid = 2412;
|
||||||
|
sane.ids.nfsuser.uid = 2413;
|
||||||
|
sane.ids.media.gid = 2414;
|
||||||
|
sane.ids.ntfy-sh.uid = 2415;
|
||||||
|
sane.ids.ntfy-sh.gid = 2415;
|
||||||
|
|
||||||
sane.ids.colin.uid = 1000;
|
sane.ids.colin.uid = 1000;
|
||||||
sane.ids.guest.uid = 1100;
|
sane.ids.guest.uid = 1100;
|
||||||
@@ -81,4 +86,8 @@
|
|||||||
sane.ids.rtkit.gid = 2307;
|
sane.ids.rtkit.gid = 2307;
|
||||||
# phosh
|
# phosh
|
||||||
sane.ids.feedbackd.gid = 2308;
|
sane.ids.feedbackd.gid = 2308;
|
||||||
|
|
||||||
|
# new moby users
|
||||||
|
sane.ids.eg25-control.uid = 2309;
|
||||||
|
sane.ids.eg25-control.gid = 2309;
|
||||||
}
|
}
|
||||||
|
|||||||
24
hosts/common/programs/alacritty.nix
Normal file
24
hosts/common/programs/alacritty.nix
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# alacritty terminal emulator
|
||||||
|
# - config options: <https://github.com/alacritty/alacritty/blob/master/extra/man/alacritty.5.scd>
|
||||||
|
# - `man 5 alacritty`
|
||||||
|
# - defaults: <https://github.com/alacritty/alacritty/releases> -> alacritty.yml
|
||||||
|
# - irc: #alacritty on libera.chat
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.alacritty = {
|
||||||
|
env.TERMINAL = lib.mkDefault "alacritty";
|
||||||
|
# note: alacritty will switch to .toml config in 13.0 release
|
||||||
|
# - run `alacritty migrate` to convert the yaml to toml
|
||||||
|
fs.".config/alacritty/alacritty.yml".symlink.text = ''
|
||||||
|
font:
|
||||||
|
size: 14
|
||||||
|
|
||||||
|
key_bindings:
|
||||||
|
- { key: N, mods: Control, action: CreateNewWindow }
|
||||||
|
- { key: PageUp, mods: Control, action: ScrollPageUp }
|
||||||
|
- { key: PageDown, mods: Control, action: ScrollPageDown }
|
||||||
|
- { key: PageUp, mods: Control|Shift, action: ScrollPageUp }
|
||||||
|
- { key: PageDown, mods: Control|Shift, action: ScrollPageDown }
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
declPackageSet = pkgs: {
|
declPackageSet = pkgs: {
|
||||||
@@ -44,13 +44,13 @@ in
|
|||||||
"sane-scripts.ssl-dump"
|
"sane-scripts.ssl-dump"
|
||||||
"sane-scripts.sudo-redirect"
|
"sane-scripts.sudo-redirect"
|
||||||
"sane-scripts.sync-from-servo"
|
"sane-scripts.sync-from-servo"
|
||||||
"sane-scripts.vpn-down"
|
"sane-scripts.vpn"
|
||||||
"sane-scripts.vpn-up"
|
|
||||||
"sane-scripts.which"
|
"sane-scripts.which"
|
||||||
"sane-scripts.wipe-browser"
|
"sane-scripts.wipe-browser"
|
||||||
];
|
];
|
||||||
"sane-scripts.sys-utils" = declPackageSet [
|
"sane-scripts.sys-utils" = declPackageSet [
|
||||||
"sane-scripts.ip-port-forward"
|
"sane-scripts.ip-port-forward"
|
||||||
|
"sane-scripts.sync-music"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@@ -59,7 +59,9 @@ in
|
|||||||
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
"cacert.unbundled" # some services require unbundled /etc/ssl/certs
|
||||||
"cryptsetup"
|
"cryptsetup"
|
||||||
"dig"
|
"dig"
|
||||||
|
"dtc" # device tree [de]compiler
|
||||||
"efibootmgr"
|
"efibootmgr"
|
||||||
|
"ethtool"
|
||||||
"fatresize"
|
"fatresize"
|
||||||
"fd"
|
"fd"
|
||||||
"file"
|
"file"
|
||||||
@@ -73,6 +75,7 @@ in
|
|||||||
"inetutils" # for telnet
|
"inetutils" # for telnet
|
||||||
"iotop"
|
"iotop"
|
||||||
"iptables"
|
"iptables"
|
||||||
|
"iw"
|
||||||
"jq"
|
"jq"
|
||||||
"killall"
|
"killall"
|
||||||
"lsof"
|
"lsof"
|
||||||
@@ -114,6 +117,7 @@ in
|
|||||||
# - debugging?
|
# - debugging?
|
||||||
consoleUtils = declPackageSet [
|
consoleUtils = declPackageSet [
|
||||||
"alsaUtils" # for aplay, speaker-test
|
"alsaUtils" # for aplay, speaker-test
|
||||||
|
"binutils" # for strings; though this brings 80MB of unrelated baggage too
|
||||||
# "cdrtools"
|
# "cdrtools"
|
||||||
"clinfo"
|
"clinfo"
|
||||||
"dmidecode"
|
"dmidecode"
|
||||||
@@ -126,7 +130,6 @@ in
|
|||||||
# "gopass"
|
# "gopass"
|
||||||
# "gopass-jsonapi"
|
# "gopass-jsonapi"
|
||||||
"helix" # text editor
|
"helix" # text editor
|
||||||
"kitty" # TODO: move to GUI, but `ssh servo` from kitty sets `TERM=xterm-kitty` in the remove and breaks things
|
|
||||||
"libsecret" # for managing user keyrings. TODO: what needs this? lift into the consumer
|
"libsecret" # for managing user keyrings. TODO: what needs this? lift into the consumer
|
||||||
"lm_sensors" # for sensors-detect. TODO: what needs this? lift into the consumer
|
"lm_sensors" # for sensors-detect. TODO: what needs this? lift into the consumer
|
||||||
"lshw"
|
"lshw"
|
||||||
@@ -153,6 +156,7 @@ in
|
|||||||
"sudo"
|
"sudo"
|
||||||
# "tageditor" # music tagging
|
# "tageditor" # music tagging
|
||||||
# "unar"
|
# "unar"
|
||||||
|
"unzip"
|
||||||
"wireguard-tools"
|
"wireguard-tools"
|
||||||
"xdg-terminal-exec"
|
"xdg-terminal-exec"
|
||||||
"xdg-utils" # for xdg-open
|
"xdg-utils" # for xdg-open
|
||||||
@@ -192,15 +196,17 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
devPkgs = declPackageSet [
|
devPkgs = declPackageSet [
|
||||||
|
"cargo"
|
||||||
"clang"
|
"clang"
|
||||||
"nodejs"
|
"nodejs"
|
||||||
|
"rustc"
|
||||||
"tree-sitter"
|
"tree-sitter"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
# INDIVIDUAL PACKAGE DEFINITIONS
|
# INDIVIDUAL PACKAGE DEFINITIONS
|
||||||
|
|
||||||
dino.persist.private = [ ".local/share/dino" ];
|
cargo.persist.plaintext = [ ".cargo" ];
|
||||||
|
|
||||||
# creds, but also 200 MB of node modules, etc
|
# creds, but also 200 MB of node modules, etc
|
||||||
discord.persist.private = [ ".config/discord" ];
|
discord.persist.private = [ ".config/discord" ];
|
||||||
@@ -212,10 +218,18 @@ in
|
|||||||
|
|
||||||
fluffychat-moby.persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
fluffychat-moby.persist.plaintext = [ ".local/share/chat.fluffy.fluffychat" ];
|
||||||
|
|
||||||
|
font-manager.package = pkgs.font-manager.override {
|
||||||
|
# build without the "Google Fonts" integration feature, to save closure / avoid webkitgtk_4_0
|
||||||
|
withWebkit = false;
|
||||||
|
};
|
||||||
|
|
||||||
# MS GitHub stores auth token in .config
|
# MS GitHub stores auth token in .config
|
||||||
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
# TODO: we can populate gh's stuff statically; it even lets us use the same oauth across machines
|
||||||
gh.persist.private = [ ".config/gh" ];
|
gh.persist.private = [ ".config/gh" ];
|
||||||
|
|
||||||
|
"gnome.gnome-maps".persist.plaintext = [ ".cache/shumate" ];
|
||||||
|
"gnome.gnome-maps".persist.private = [ ".local/share/maps-places.json" ];
|
||||||
|
|
||||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||||
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
||||||
@@ -251,4 +265,8 @@ in
|
|||||||
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
|
||||||
zecwallet-lite.persist.private = [ ".zcash" ];
|
zecwallet-lite.persist.private = [ ".zcash" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
programs.feedbackd = lib.mkIf config.sane.programs.feedbackd.enabled {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
61
hosts/common/programs/calls.nix
Normal file
61
hosts/common/programs/calls.nix
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# GNOME calls
|
||||||
|
# - <https://gitlab.gnome.org/GNOME/calls>
|
||||||
|
# - both a dialer and a call handler.
|
||||||
|
# - uses callaudiod dbus package.
|
||||||
|
#
|
||||||
|
# initial JMP.chat configuration:
|
||||||
|
# - message @cheogram.com "reset sip account" (this is not destructive, despite the name)
|
||||||
|
# - the bot will reply with auto-generated username/password plus a SIP server endpoint.
|
||||||
|
# just copy those into gnome-calls' GUI configurator
|
||||||
|
# - now gnome-calls can do outbound calls. inbound calls requires more chatting with the help bot
|
||||||
|
#
|
||||||
|
# my setup here is still very WIP.
|
||||||
|
# open questions:
|
||||||
|
# - can i receive calls even with GUI closed?
|
||||||
|
# - e.g. activated by callaudiod?
|
||||||
|
# - looks like `gnome-calls --daemon` does that?
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.calls;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.calls = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.autostart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
persist.private = [
|
||||||
|
# ".cache/folks" # contact avatars?
|
||||||
|
# ".config/calls"
|
||||||
|
".local/share/calls" # call "records"
|
||||||
|
# .local/share/folks # contacts?
|
||||||
|
];
|
||||||
|
secrets.".config/calls/sip-account.cfg" = ../../../secrets/common/gnome_calls_sip-account.cfg.bin;
|
||||||
|
suggestedPrograms = [
|
||||||
|
"feedbackd" # needs `phone-incoming-call`, in particular
|
||||||
|
];
|
||||||
|
|
||||||
|
services.gnome-calls = {
|
||||||
|
# TODO: prevent gnome-calls from daemonizing when started manually
|
||||||
|
description = "gnome-calls daemon to monitor incoming SIP calls";
|
||||||
|
wantedBy = lib.mkIf cfg.config.autostart [ "default.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
# add --verbose for more debugging
|
||||||
|
ExecStart = "${cfg.package}/bin/gnome-calls --daemon";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "10s";
|
||||||
|
};
|
||||||
|
environment.G_MESSAGES_DEBUG = "all";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
programs.calls = lib.mkIf cfg.enabled {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
36
hosts/common/programs/cantata.nix
Normal file
36
hosts/common/programs/cantata.nix
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# cantata is a mpd frontend.
|
||||||
|
# before launching it, run `mopidy` in some tab
|
||||||
|
# TODO: auto-launch mopidy when cantata launches?
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.cantata = {
|
||||||
|
persist.plaintext = [
|
||||||
|
".cache/cantata" # album art
|
||||||
|
".local/share/cantata/library" # library index (?)
|
||||||
|
];
|
||||||
|
fs.".config/cantata/cantata.conf".symlink.text = ''
|
||||||
|
[General]
|
||||||
|
fetchCovers=true
|
||||||
|
storeCoversInMpdDir=false
|
||||||
|
version=2.5.0
|
||||||
|
|
||||||
|
[Connection]
|
||||||
|
allowLocalStreaming=true
|
||||||
|
applyReplayGain=true
|
||||||
|
autoUpdate=false
|
||||||
|
dir=~/Music
|
||||||
|
host=localhost
|
||||||
|
partition=
|
||||||
|
passwd=
|
||||||
|
port=6600
|
||||||
|
replayGain=off
|
||||||
|
streamUrl=
|
||||||
|
|
||||||
|
[LibraryPage]
|
||||||
|
artist\gridZoom=100
|
||||||
|
artist\searchActive=false
|
||||||
|
artist\viewMode=detailedtree
|
||||||
|
'';
|
||||||
|
suggestedPrograms = [ "mopidy" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ let
|
|||||||
# frees us from webkit_4_1, in turn.
|
# frees us from webkit_4_1, in turn.
|
||||||
enableBackend = false;
|
enableBackend = false;
|
||||||
gvfs = pkgs.gvfs.override {
|
gvfs = pkgs.gvfs.override {
|
||||||
# saves 20 minutes of build time, for unused feature
|
# saves 20 minutes of build time and cross issues, for unused feature
|
||||||
samba = null;
|
samba = null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -38,7 +38,9 @@ in
|
|||||||
suggestedPrograms = [ "gnome-keyring" ];
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
persist.private = [
|
persist.private = [
|
||||||
".local/share/chatty" # matrix avatars and files
|
".local/share/chatty" # matrix avatars and files
|
||||||
# ".purple" # XMPP stuff
|
# not just XMPP; without this Chatty will regenerate its device-id every boot.
|
||||||
|
# .purple/ contains XMPP *and* Matrix auth, logs, avatar cache, and a bit more
|
||||||
|
".purple"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
-- configversion: 737cb1de0389cee32a04785691a446a2
|
|
||||||
|
|
||||||
-- docs: <https://conky.cc/variables>
|
-- docs: <https://conky.cc/variables>
|
||||||
-- color names are X11 colors: <https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart>
|
-- color names are X11 colors: <https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart>
|
||||||
-- - can also use #rrggbb syntax
|
-- - can also use #rrggbb syntax
|
||||||
@@ -29,7 +27,7 @@ conky.config = {
|
|||||||
default_shade_color = '#beebe5',
|
default_shade_color = '#beebe5',
|
||||||
default_outline_color = '#beebe5',
|
default_outline_color = '#beebe5',
|
||||||
|
|
||||||
font = 'Sxmo:size=8',
|
font = 'sans-serif:size=8',
|
||||||
use_xft = true,
|
use_xft = true,
|
||||||
|
|
||||||
default_color = '#ffffff',
|
default_color = '#ffffff',
|
||||||
@@ -37,14 +35,16 @@ conky.config = {
|
|||||||
color2 = '404040',
|
color2 = '404040',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- texeci <interval_sec> <cmd>: run the command periodically, _in a separate thread_ so as not to block rendering
|
||||||
conky.text = [[
|
conky.text = [[
|
||||||
${color1}${shadecolor 707070}${font Sxmo:size=50:style=Bold}${alignc}${exec date +"%H:%M"}${font}
|
${color1}${shadecolor 707070}${font sans-serif:size=50:style=Bold}${alignc}${exec date +"%H:%M"}${font}
|
||||||
${color2}${shadecolor a4d7d0}${font Sxmo:size=20}${alignc}${exec date +"%a %d %b"}${font}
|
${color2}${shadecolor a4d7d0}${font sans-serif:size=20}${alignc}${exec date +"%a %d %b"}${font}
|
||||||
|
|
||||||
|
|
||||||
${color1}${shadecolor}${font Sxmo:size=22:style=Bold}${alignc}${exec @bat@ }${font}
|
${color1}${shadecolor}${font sans-serif:size=22:style=Bold}${alignc}${exec @bat@ }${font}
|
||||||
|
${color1}${shadecolor}${font sans-serif:size=20:style=Bold}${alignc}${texeci 600 @weather@ }${font}
|
||||||
|
|
||||||
|
|
||||||
${color2}${shadecolor a4d7d0}${font Sxmo:size=16}${alignc}⇅ ${downspeedf wlan0}K/s${font}
|
${color2}${shadecolor a4d7d0}${font sans-serif:size=16}${alignc}⇅ ${downspeedf wlan0}K/s${font}
|
||||||
${font Sxmo:size=16}${alignc}☵ $memperc% $cpu%${font}
|
${font sans-serif:size=16}${alignc}☵ $memperc% $cpu%${font}
|
||||||
]]
|
]]
|
||||||
34
hosts/common/programs/conky/default.nix
Normal file
34
hosts/common/programs/conky/default.nix
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.conky = {
|
||||||
|
fs.".config/conky/conky.conf".symlink.target =
|
||||||
|
let
|
||||||
|
battery_estimate = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "battery_estimate";
|
||||||
|
src = ./.;
|
||||||
|
};
|
||||||
|
in pkgs.substituteAll {
|
||||||
|
src = ./conky.conf;
|
||||||
|
bat = "${battery_estimate}/bin/battery_estimate";
|
||||||
|
weather = "timeout 20 ${pkgs.sane-weather}/bin/sane-weather";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.conky = {
|
||||||
|
description = "conky dynamic desktop background";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
# XXX: should be part of graphical-session.target, but whatever mix of greetd/sway
|
||||||
|
# i'm using means that target's never reached...
|
||||||
|
# wantedBy = [ "graphical-session.target" ];
|
||||||
|
# partOf = [ "graphical-session.target" ];
|
||||||
|
|
||||||
|
serviceConfig.ExecStart = "${config.sane.programs.conky.package}/bin/conky";
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
serviceConfig.Restart = "on-failure";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
|
# serviceConfig.Slice = "session.slice";
|
||||||
|
|
||||||
|
# don't start conky until after sway
|
||||||
|
preStart = ''test -n "$SWAYSOCK"'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,44 +3,59 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./aerc.nix
|
./aerc.nix
|
||||||
|
./alacritty.nix
|
||||||
./assorted.nix
|
./assorted.nix
|
||||||
|
./calls.nix
|
||||||
|
./cantata.nix
|
||||||
./chatty.nix
|
./chatty.nix
|
||||||
|
./conky
|
||||||
./cozy.nix
|
./cozy.nix
|
||||||
|
./dino.nix
|
||||||
./element-desktop.nix
|
./element-desktop.nix
|
||||||
./epiphany.nix
|
./epiphany.nix
|
||||||
./evince.nix
|
./evince.nix
|
||||||
|
./feedbackd.nix
|
||||||
./firefox.nix
|
./firefox.nix
|
||||||
./fontconfig.nix
|
./fontconfig.nix
|
||||||
./fractal.nix
|
./fractal.nix
|
||||||
./fwupd.nix
|
./fwupd.nix
|
||||||
|
./g4music.nix
|
||||||
|
./gajim.nix
|
||||||
./git.nix
|
./git.nix
|
||||||
./gnome-feeds.nix
|
./gnome-feeds.nix
|
||||||
./gnome-keyring.nix
|
./gnome-keyring.nix
|
||||||
|
./gnome-weather.nix
|
||||||
./gpodder.nix
|
./gpodder.nix
|
||||||
./gthumb.nix
|
./gthumb.nix
|
||||||
./helix.nix
|
./helix.nix
|
||||||
./imagemagick.nix
|
./imagemagick.nix
|
||||||
./jellyfin-media-player.nix
|
./jellyfin-media-player.nix
|
||||||
./kitty
|
|
||||||
./komikku.nix
|
./komikku.nix
|
||||||
./koreader
|
./koreader
|
||||||
./libreoffice.nix
|
./libreoffice.nix
|
||||||
./lemoa.nix
|
./lemoa.nix
|
||||||
|
./mako.nix
|
||||||
./megapixels.nix
|
./megapixels.nix
|
||||||
./mepo.nix
|
./mepo.nix
|
||||||
|
./mopidy.nix
|
||||||
./mpv.nix
|
./mpv.nix
|
||||||
./msmtp.nix
|
./msmtp.nix
|
||||||
./neovim.nix
|
./neovim.nix
|
||||||
./newsflash.nix
|
./newsflash.nix
|
||||||
./nheko.nix
|
./nheko.nix
|
||||||
./nix-index.nix
|
./nix-index.nix
|
||||||
|
./ntfy-sh.nix
|
||||||
./obsidian.nix
|
./obsidian.nix
|
||||||
./offlineimap.nix
|
./offlineimap.nix
|
||||||
|
./playerctl.nix
|
||||||
|
./rhythmbox.nix
|
||||||
./ripgrep.nix
|
./ripgrep.nix
|
||||||
./sfeed.nix
|
./sfeed.nix
|
||||||
./splatmoji.nix
|
./splatmoji.nix
|
||||||
./steam.nix
|
./steam.nix
|
||||||
|
./stepmania.nix
|
||||||
./sublime-music.nix
|
./sublime-music.nix
|
||||||
|
./swaynotificationcenter.nix
|
||||||
./tangram.nix
|
./tangram.nix
|
||||||
./tuba.nix
|
./tuba.nix
|
||||||
./vlc.nix
|
./vlc.nix
|
||||||
|
|||||||
69
hosts/common/programs/dino.nix
Normal file
69
hosts/common/programs/dino.nix
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# usage:
|
||||||
|
# - start a DM with a rando via
|
||||||
|
# - '+' -> 'start conversation'
|
||||||
|
# - add a user to your roster via
|
||||||
|
# - '+' -> 'start conversation' -> '+' (opens the "add contact" dialog)
|
||||||
|
# - this triggers a popup on the remote side asking them for confirmation
|
||||||
|
# - after the remote's confirmation there will be a local popup for you to allow them to add you to their roster
|
||||||
|
# - to make a call:
|
||||||
|
# - ensure the other party is in your roster
|
||||||
|
# - open a DM with the party
|
||||||
|
# - click the phone icon at top (only visible if other party is in your roster)
|
||||||
|
#
|
||||||
|
# dino can be autostarted on login -- useful to ensure that i always receive calls and notifications --
|
||||||
|
# but at present it has no "start in tray" type of option: it must render a window.
|
||||||
|
#
|
||||||
|
# outstanding bugs:
|
||||||
|
# - mic is sometimes disabled at call start despite presenting as enabled
|
||||||
|
# - fix is to toggle it off -> on in the Dino UI
|
||||||
|
# - default mic gain is WAY TOO MUCH (heavily distorted)
|
||||||
|
# - TODO: dino should have more optimal niceness/priority to ensure it can process its buffers
|
||||||
|
#
|
||||||
|
# probably fixed:
|
||||||
|
# - once per 1-2 minutes dino will temporarily drop mic input:
|
||||||
|
# - `rtp-WRNING: plugin.vala:148: Warning in pipeline: Can't record audio fast enough
|
||||||
|
# - this was *partially* fixed by bumping the pipewire mic buffer to 2048 samples (from ~512)
|
||||||
|
# - this was further fixed by setting PULSE_LATENCY_MSEC=20.
|
||||||
|
# - possibly Dino should be updated internally: `info.rate / 100` -> `info.rate / 50`.
|
||||||
|
# - i think that affects the batching for echo cancellation, adaptive gain control, etc.
|
||||||
|
#
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.dino;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.dino = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.autostart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
persist.private = [ ".local/share/dino" ];
|
||||||
|
|
||||||
|
services.dino = {
|
||||||
|
description = "auto-start and maintain dino XMPP connection";
|
||||||
|
wantedBy = lib.mkIf cfg.config.autostart [ "default.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/bin/dino";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
|
|
||||||
|
# audio buffering; see: <https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/FAQ#pipewire-buffering-explained>
|
||||||
|
# dino defaults to 10ms mic buffer, which causes underruns, which Dino handles *very* poorly
|
||||||
|
# as in, the other end of the call will just not receive sound from us for a couple seconds.
|
||||||
|
# pipewire uses power-of-two buffering for the mic itself. that would put us at 21.33 ms, but this env var supports only whole numbers (21ms ends up not power-of-two).
|
||||||
|
# also, Dino's likely still doing things in 10ms batches internally anyway.
|
||||||
|
environment.PULSE_LATENCY_MSEC = "20";
|
||||||
|
|
||||||
|
# note that debug logging during calls produces so much journal spam that it pegs the CPU and causes dropped audio
|
||||||
|
# environment.G_MESSAGES_DEBUG = "all";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# debugging tips:
|
||||||
|
# - if element opens but does not render:
|
||||||
|
# - `element-desktop --disable-gpu --in-process-gpu`
|
||||||
|
# - <https://github.com/vector-im/element-desktop/issues/1029#issuecomment-1632688224>
|
||||||
|
# - `rm -rf ~/.config/Element/GPUCache`
|
||||||
|
# - <https://github.com/NixOS/nixpkgs/issues/244486>
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
sane.programs.element-desktop = {
|
sane.programs.element-desktop = {
|
||||||
|
|||||||
114
hosts/common/programs/feedbackd.nix
Normal file
114
hosts/common/programs/feedbackd.nix
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.feedbackd;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.feedbackd = {
|
||||||
|
package = pkgs.rmDbusServices pkgs.feedbackd;
|
||||||
|
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options.proxied = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
whether to use a sound theme in which common application events are muted
|
||||||
|
with the intent that a proxy (notification daemon) with knowledge of this
|
||||||
|
modification will "speak" on behalf of all applications.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
# N.B.: feedbackd will load ~/.config/feedbackd/themes/default.json by default
|
||||||
|
# - but using that would forbid `parent-theme = "default"`
|
||||||
|
# the default theme ships support for these events:
|
||||||
|
# - alarm-clock-elapsed
|
||||||
|
# - battery-caution
|
||||||
|
# - bell-terminal
|
||||||
|
# - button-pressed
|
||||||
|
# - button-released
|
||||||
|
# - camera-focus
|
||||||
|
# - camera-shutter
|
||||||
|
# - message-missed-email
|
||||||
|
# - message-missed-instant
|
||||||
|
# - message-missed-notification
|
||||||
|
# - message-missed-sms
|
||||||
|
# - message-new-email
|
||||||
|
# - message-new-instant
|
||||||
|
# - message-new-sms
|
||||||
|
# - message-sent-instant
|
||||||
|
# - phone-failure
|
||||||
|
# - phone-hangup
|
||||||
|
# - phone-incoming-call
|
||||||
|
# - phone-missed-call
|
||||||
|
# - phone-outgoing-busy
|
||||||
|
# - screen-capture
|
||||||
|
# - theme-demo
|
||||||
|
# - timeout-completed
|
||||||
|
# - window-close
|
||||||
|
fs.".config/feedbackd/themes/proxied.json".symlink.text = builtins.toJSON {
|
||||||
|
name = "proxied";
|
||||||
|
parent-theme = "default";
|
||||||
|
profiles = [
|
||||||
|
{
|
||||||
|
name = "full";
|
||||||
|
feedbacks = [
|
||||||
|
# forcibly disable normal events which we'd prefer for the notification daemon (e.g. swaync) to handle
|
||||||
|
{
|
||||||
|
event-name = "message-new-instant";
|
||||||
|
type = "Dummy";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
event-name = "proxied-message-new-instant";
|
||||||
|
type = "Sound";
|
||||||
|
effect = "message-new-instant";
|
||||||
|
}
|
||||||
|
# re-define sounds from the default theme which we'd like to pass through w/o proxying.
|
||||||
|
# i guess this means i'm not inheriting the default theme :|
|
||||||
|
{
|
||||||
|
event-name = "phone-incoming-call";
|
||||||
|
type = "Sound";
|
||||||
|
effect = "phone-incoming-call";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
event-name = "alarm-clock-elapsed";
|
||||||
|
type = "Sound";
|
||||||
|
effect = "alarm-clock-elapsed";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
event-name = "timeout-completed";
|
||||||
|
type = "Sound";
|
||||||
|
effect = "complete";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.feedbackd = {
|
||||||
|
description = "feedbackd audio/vibration/led controller";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/libexec/feedbackd";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
G_MESSAGES_DEBUG = "all";
|
||||||
|
} // (lib.optionalAttrs cfg.config.proxied {
|
||||||
|
FEEDBACK_THEME = "/home/colin/.config/feedbackd/themes/proxied.json";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.udev.packages = lib.mkIf cfg.enabled [
|
||||||
|
# ships udev rules for `feedbackd` group to be able to control vibrator and LEDs
|
||||||
|
cfg.package
|
||||||
|
];
|
||||||
|
users.groups = lib.mkIf cfg.enabled {
|
||||||
|
feedbackd = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,15 +1,68 @@
|
|||||||
|
# to preview fonts:
|
||||||
|
# - `font-manager` (gui)
|
||||||
|
# - useful to determine official name; codepoint support
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
# nerdfonts takes popular open fonts and patches them to support a wider range of glyphs, notably emoji.
|
||||||
|
# any nerdfonts font includes icons such as these:
|
||||||
|
# - (battery charging)
|
||||||
|
# - (brightness)
|
||||||
|
# - (gps / crosshairs)
|
||||||
|
# - (music note)
|
||||||
|
# - (message bubble)
|
||||||
|
# - (phone)
|
||||||
|
# - (weather/sun-behind-clouds)
|
||||||
|
# used particularly by sxmo utilities, but also a few of my own (e.g. conky)
|
||||||
|
#
|
||||||
|
# nerdfonts is very heavy. each font is 20-900 MiB (2 MiB per "variation")
|
||||||
|
# lots of redundant data inside there, but no deduplication except whatever nix or the fs does implicitly.
|
||||||
|
wantedNerdfonts = [
|
||||||
|
# used explicitly by SXMO
|
||||||
|
# "DejaVuSansMono" # 25 MiB
|
||||||
|
# good terminal/coding font. grab via nerdfonts for more emoji/unicode support
|
||||||
|
"Hack" # 26 MiB
|
||||||
|
"Noto" # 861 MiB
|
||||||
|
];
|
||||||
|
nerdfontPkgs = builtins.map
|
||||||
|
(f: pkgs.nerdfonts.override { fonts = [ f ]; })
|
||||||
|
wantedNerdfonts;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
|
fonts = lib.mkIf config.sane.programs.fontconfig.enabled {
|
||||||
fontconfig.enable = true;
|
fontconfig.enable = true;
|
||||||
fontconfig.defaultFonts = {
|
fontconfig.defaultFonts = {
|
||||||
emoji = [ "Font Awesome 6 Free" "Noto Color Emoji" ];
|
emoji = [
|
||||||
monospace = [ "Hack" ];
|
"Noto Color Emoji"
|
||||||
serif = [ "DejaVu Serif" ];
|
"Font Awesome 6 Free"
|
||||||
sansSerif = [ "DejaVu Sans" ];
|
"Font Awesome 6 Brands"
|
||||||
|
];
|
||||||
|
monospace = [
|
||||||
|
"Hack Nerd Font Propo"
|
||||||
|
# "DejaVuSansM Nerd Font Propo"
|
||||||
|
"NotoMono Nerd Font Propo"
|
||||||
|
];
|
||||||
|
serif = [
|
||||||
|
"NotoSerif Nerd Font"
|
||||||
|
"DejaVu Serif"
|
||||||
|
];
|
||||||
|
sansSerif = [
|
||||||
|
"NotoSans Nerd Font"
|
||||||
|
"DejaVu Sans"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
#vvv enables dejavu_fonts, freefont_ttf, gyre-fonts, liberation_ttf, unifont, noto-fonts-emoji
|
#vvv enables dejavu_fonts, freefont_ttf, gyre-fonts, liberation_ttf, unifont, noto-fonts-emoji
|
||||||
enableDefaultPackages = true;
|
enableDefaultPackages = false;
|
||||||
packages = with pkgs; [ font-awesome noto-fonts-emoji hack-font ];
|
packages = with pkgs; [
|
||||||
|
# TODO: reduce this font set.
|
||||||
|
# - probably need only one of dejavu/freefont/liberation
|
||||||
|
dejavu_fonts # 10 MiB; DejaVu {Sans,Serif,Sans Mono,Math TeX Gyre}; also available as a NerdFonts (Sans Mono only)
|
||||||
|
font-awesome # 2 MiB; Font Awesome 6 {Free,Brands}
|
||||||
|
freefont_ttf # 11 MiB; Free{Mono,Sans,Serif}
|
||||||
|
gyre-fonts # 4 MiB; Tex Gyre *; ttf substitutes for standard PostScript fonts
|
||||||
|
# hack-font # 1 MiB; Hack; also available as a NerdFonts
|
||||||
|
liberation_ttf # 4 MiB; Liberation {Mono,Sans,Serif}; also available as a NerdFonts
|
||||||
|
noto-fonts-color-emoji # 10 Mib; Noto Color Emoji
|
||||||
|
unifont # 16 MiB; Unifont; provides LOTS of unicode coverage
|
||||||
|
] ++ nerdfontPkgs;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,41 @@
|
|||||||
{ pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.fractal;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
sane.programs.fractal = {
|
sane.programs.fractal = {
|
||||||
|
package = pkgs.fractal-nixified;
|
||||||
# package = pkgs.fractal-latest;
|
# package = pkgs.fractal-latest;
|
||||||
package = pkgs.fractal-next;
|
# package = pkgs.fractal-next;
|
||||||
|
|
||||||
# XXX by default fractal stores its state in ~/.local/share/stable/<UUID>.
|
configOption = with lib; mkOption {
|
||||||
persist.private = [ ".local/share/stable" ];
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.autostart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
persist.private = [
|
||||||
|
# XXX by default fractal stores its state in ~/.local/share/<build-profile>/<UUID>.
|
||||||
|
".local/share/hack" # for debug-like builds
|
||||||
|
".local/share/stable" # for normal releases
|
||||||
|
];
|
||||||
|
|
||||||
suggestedPrograms = [ "gnome-keyring" ];
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
|
|
||||||
|
services.fractal = {
|
||||||
|
description = "auto-start and maintain fractal Matrix connection";
|
||||||
|
wantedBy = lib.mkIf cfg.config.autostart [ "default.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${cfg.package}/bin/fractal";
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
|
# environment.G_MESSAGES_DEBUG = "all";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
16
hosts/common/programs/g4music.nix
Normal file
16
hosts/common/programs/g4music.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# N.B.: requires first-run setup on moby:
|
||||||
|
# - UI will render transparent
|
||||||
|
# - click the hamburger (top-right: immediately left from close button)
|
||||||
|
# > Preferences
|
||||||
|
# > Background-blur mode: change from "Always" to "Never"
|
||||||
|
#
|
||||||
|
# the background blur is probably some dconf setting somewhere.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.g4music = {
|
||||||
|
persist.plaintext = [
|
||||||
|
# index?
|
||||||
|
".cache/com.github.neithern.g4music"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
13
hosts/common/programs/gajim.nix
Normal file
13
hosts/common/programs/gajim.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.gajim = {
|
||||||
|
persist.private = [
|
||||||
|
# avatars, thumbnails...
|
||||||
|
".cache/gajim"
|
||||||
|
# sqlite database labeled "settings". definitely includes UI theming
|
||||||
|
".config/gajim"
|
||||||
|
# omemo keys, downloads, logs
|
||||||
|
".local/share/gajim"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
10
hosts/common/programs/gnome-weather.nix
Normal file
10
hosts/common/programs/gnome-weather.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# preferences are saved via dconf; see `dconf dump /`
|
||||||
|
# cache dir is just for weather data (or maybe a http cache)
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.gnome-weather = {
|
||||||
|
persist.plaintext = [
|
||||||
|
".cache/libgweather"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,10 +4,12 @@
|
|||||||
# compile without webservices to avoid the expensive webkitgtk dependency
|
# compile without webservices to avoid the expensive webkitgtk dependency
|
||||||
package = pkgs.gthumb.override { withWebservices = false; };
|
package = pkgs.gthumb.override { withWebservices = false; };
|
||||||
mime.associations = {
|
mime.associations = {
|
||||||
|
"image/gif" = "org.gnome.gThumb.desktop";
|
||||||
"image/heif" = "org.gnome.gThumb.desktop"; # apple codec
|
"image/heif" = "org.gnome.gThumb.desktop"; # apple codec
|
||||||
"image/png" = "org.gnome.gThumb.desktop";
|
"image/png" = "org.gnome.gThumb.desktop";
|
||||||
"image/jpeg" = "org.gnome.gThumb.desktop";
|
"image/jpeg" = "org.gnome.gThumb.desktop";
|
||||||
"image/svg+xml" = "org.gnome.gThumb.desktop";
|
"image/svg+xml" = "org.gnome.gThumb.desktop";
|
||||||
|
"image/webp" = "org.gnome.gThumb.desktop";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# vim:ft=kitty
|
|
||||||
|
|
||||||
## name: PaperColor Dark
|
|
||||||
## author: Nikyle Nguyen
|
|
||||||
## license: MIT
|
|
||||||
## blurb: Dark color scheme inspired by Google's Material Design
|
|
||||||
|
|
||||||
# special
|
|
||||||
foreground #d0d0d0
|
|
||||||
background #1c1c1c
|
|
||||||
cursor #d0d0d0
|
|
||||||
cursor_text_color background
|
|
||||||
|
|
||||||
# black
|
|
||||||
color0 #1c1c1c
|
|
||||||
color8 #585858
|
|
||||||
|
|
||||||
# red
|
|
||||||
color1 #af005f
|
|
||||||
color9 #5faf5f
|
|
||||||
|
|
||||||
# green
|
|
||||||
# "color2" is the green color used by ls to indicate executability
|
|
||||||
# both as text color
|
|
||||||
# or as bg color when the text is blue (color4)
|
|
||||||
color2 #246a28
|
|
||||||
color10 #2df200
|
|
||||||
|
|
||||||
# yellow
|
|
||||||
color3 #d7af5f
|
|
||||||
color11 #af87d7
|
|
||||||
|
|
||||||
# blue
|
|
||||||
color4 #78c6ef
|
|
||||||
color12 #ffaf00
|
|
||||||
|
|
||||||
# magenta
|
|
||||||
color5 #808080
|
|
||||||
color13 #ff5faf
|
|
||||||
|
|
||||||
# cyan
|
|
||||||
color6 #d7875f
|
|
||||||
color14 #00afaf
|
|
||||||
|
|
||||||
# white
|
|
||||||
color7 #d0d0d0
|
|
||||||
color15 #5f8787
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
{ lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
sane.programs.kitty = {
|
|
||||||
fs.".config/kitty/kitty.conf".symlink.text = ''
|
|
||||||
# docs: https://sw.kovidgoyal.net/kitty/conf/
|
|
||||||
# disable terminal bell (when e.g. you backspace too many times)
|
|
||||||
enable_audio_bell no
|
|
||||||
|
|
||||||
map ctrl+n new_os_window_with_cwd
|
|
||||||
include ${./PaperColor_dark.conf}
|
|
||||||
'';
|
|
||||||
env.TERMINAL = lib.mkDefault "kitty";
|
|
||||||
};
|
|
||||||
|
|
||||||
# include ${pkgs.kitty-themes}/themes/PaperColor_dark.conf
|
|
||||||
|
|
||||||
# THEME CHOICES:
|
|
||||||
# docs: https://github.com/kovidgoyal/kitty-themes
|
|
||||||
# theme = "1984 Light"; # dislike: awful, harsh blues/teals
|
|
||||||
# theme = "Adventure Time"; # dislike: harsh (dark)
|
|
||||||
# theme = "Atom One Light"; # GOOD: light theme. all color combos readable. not a huge fan of the blue.
|
|
||||||
# theme = "Belafonte Day"; # dislike: too low contrast for text colors
|
|
||||||
# theme = "Belafonte Night"; # better: dark theme that's easy on the eyes. all combos readable. low contrast.
|
|
||||||
# theme = "Catppuccin"; # dislike: a bit pale/low-contrast (dark)
|
|
||||||
# theme = "Desert"; # mediocre: colors are harsh
|
|
||||||
# theme = "Earthsong"; # BEST: dark theme. readable, good contrast. unique, but decent colors.
|
|
||||||
# theme = "Espresso Libre"; # better: dark theme. readable, but meh colors
|
|
||||||
# theme = "Forest Night"; # decent: very pastel. it's workable, but unconventional and muted/flat.
|
|
||||||
# theme = "Gruvbox Material Light Hard"; # mediocre light theme.
|
|
||||||
# theme = "kanagawabones"; # better: dark theme. colors are too background-y
|
|
||||||
# theme = "Kaolin Dark"; # dislike: too dark
|
|
||||||
# theme = "Kaolin Breeze"; # mediocre: not-too-harsh light theme, but some parts are poor contrast
|
|
||||||
# theme = "Later This Evening"; # mediocre: not-too-harsh dark theme, but cursor is poor contrast
|
|
||||||
# theme = "Material"; # decent: light theme, few colors.
|
|
||||||
# theme = "Mayukai"; # decent: not-too-harsh dark theme. the teal is a bit straining
|
|
||||||
# theme = "Nord"; # mediocre: pale background, low contrast
|
|
||||||
# theme = "One Half Light"; # better: not-too-harsh light theme. contrast could be better
|
|
||||||
# theme = "PaperColor Dark"; # BEST: dark theme, very readable still the colors are background-y
|
|
||||||
# theme = "Parasio Dark"; # dislike: too low contrast
|
|
||||||
# theme = "Pencil Light"; # better: not-too-harsh light theme. decent contrast.
|
|
||||||
# theme = "Pnevma"; # dislike: too low contrast
|
|
||||||
# theme = "Piatto Light"; # better: readable light theme. pleasing colors. powerline prompt is hard to read.
|
|
||||||
# theme = "Rosé Pine Dawn"; # GOOD: light theme. all color combinations are readable. it is very mild -- may need to manually tweak contrast. tasteful colors
|
|
||||||
# theme = "Rosé Pine Moon"; # GOOD: dark theme. tasteful colors. but background is a bit intense
|
|
||||||
# theme = "Sea Shells"; # mediocre. not all color combos are readable
|
|
||||||
# theme = "Solarized Light"; # mediocre: not-too-harsh light theme; GREAT background; but some colors are low contrast
|
|
||||||
# theme = "Solarized Dark Higher Contrast"; # better: dark theme, decent colors
|
|
||||||
# theme = "Sourcerer"; # mediocre: ugly colors
|
|
||||||
# theme = "Space Gray"; # mediocre: too muted
|
|
||||||
# theme = "Space Gray Eighties"; # better: all readable, decent colors
|
|
||||||
# theme = "Spacemacs"; # mediocre: too muted
|
|
||||||
# theme = "Spring"; # mediocre: readable light theme, but the teal is ugly.
|
|
||||||
# theme = "Srcery"; # better: highly readable. colors are ehhh
|
|
||||||
# theme = "Substrata"; # decent: nice colors, but a bit flat.
|
|
||||||
# theme = "Sundried"; # mediocre: the solar text makes me squint
|
|
||||||
# theme = "Symfonic"; # mediocre: the dark purple has low contrast to the black bg.
|
|
||||||
# theme = "Tango Light"; # dislike: teal is too grating
|
|
||||||
# theme = "Tokyo Night Day"; # medicore: too muted
|
|
||||||
# theme = "Tokyo Night"; # better: tasteful. a bit flat
|
|
||||||
# theme = "Tomorrow"; # GOOD: all color combinations are readable. contrast is slightly better than Rose. on the blander side
|
|
||||||
# theme = "Treehouse"; # dislike: the orange is harsh on my eyes.
|
|
||||||
# theme = "Urple"; # dislike: weird palette
|
|
||||||
# theme = "Warm Neon"; # decent: not-too-harsh dark theme. the green is a bit unattractive
|
|
||||||
# theme = "Wild Cherry"; # GOOD: dark theme: nice colors. a bit flat
|
|
||||||
# theme = "Xcodedark"; # dislike: bad palette
|
|
||||||
# theme = "citylights"; # decent: dark theme. some parts have just a bit low contrast
|
|
||||||
# theme = "neobones_light"; # better light theme. the background is maybe too muted
|
|
||||||
# theme = "vimbones";
|
|
||||||
# theme = "zenbones_dark"; # mediocre: readable, but meh colors
|
|
||||||
# theme = "zenbones_light"; # decent: light theme. all colors are readable. contrast is passable but not excellent. highlight color is BAD
|
|
||||||
# theme = "zenwritten_dark"; # mediocre: looks same as zenbones_dark
|
|
||||||
}
|
|
||||||
67
hosts/common/programs/mako.nix
Normal file
67
hosts/common/programs/mako.nix
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# config docs:
|
||||||
|
# - `man 5 mako`
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.mako = {
|
||||||
|
# we control mako as a systemd service, so have dbus not automatically activate it.
|
||||||
|
package = pkgs.rmDbusServices pkgs.mako;
|
||||||
|
fs.".config/mako/config".symlink.text = ''
|
||||||
|
# notification interaction mapping
|
||||||
|
# "on-touch" defaults to "dismiss", which isn't nice for touchscreens.
|
||||||
|
on-button-left=invoke-default-action
|
||||||
|
on-touch=invoke-default-action
|
||||||
|
on-button-middle=dismiss-group
|
||||||
|
|
||||||
|
max-visible=3
|
||||||
|
# layer:
|
||||||
|
# - overlay: shows notifs above all else, even full-screen windows
|
||||||
|
# - top: shows notifs above windows, but not if they're full-screen
|
||||||
|
# - bottom; background
|
||||||
|
layer=overlay
|
||||||
|
# notifications can be grouped by:
|
||||||
|
# - app-name
|
||||||
|
# - app-icon
|
||||||
|
# - summary
|
||||||
|
# - body
|
||||||
|
# possibly more: urgency, category, desktop-entry, ...
|
||||||
|
# to group by multiple fields, join with `,`
|
||||||
|
group-by=app-name
|
||||||
|
|
||||||
|
# BELOW IS SXMO DEFAULTS, modified very slightly.
|
||||||
|
# TODO: apply theme colors!
|
||||||
|
|
||||||
|
# default-timeout=15000
|
||||||
|
background-color=#ffffff
|
||||||
|
text-color=#000000
|
||||||
|
border-color=#000000
|
||||||
|
# group-by=app-name
|
||||||
|
|
||||||
|
[urgency=low]
|
||||||
|
# default-timeout=10000
|
||||||
|
background-color=#222222
|
||||||
|
text-color=#888888
|
||||||
|
|
||||||
|
[urgency=high]
|
||||||
|
default-timeout=0
|
||||||
|
background-color=#900000
|
||||||
|
text-color=#ffffff
|
||||||
|
background-color=#ff0000
|
||||||
|
'';
|
||||||
|
|
||||||
|
# mako supports activation via dbus (i.e. the daemon will be started on-demand when a
|
||||||
|
# dbus client tries to talk to it): that works out-of-the-box just by putting mako
|
||||||
|
# on environment.packages, but then logs are blackholed.
|
||||||
|
services.mako = {
|
||||||
|
description = "mako desktop notification daemon";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
# XXX: should be part of graphical-session.target, but whatever mix of greetd/sway
|
||||||
|
# i'm using means that target's never reached...
|
||||||
|
|
||||||
|
serviceConfig.ExecStart = "${config.sane.programs.mako.package}/bin/mako";
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
# mako will predictably fail if launched before the wayland server is fully initialized
|
||||||
|
serviceConfig.Restart = "on-failure";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
56
hosts/common/programs/mopidy.nix
Normal file
56
hosts/common/programs/mopidy.nix
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# chat: <https://mopidy.zulipchat.com/>
|
||||||
|
# config docs: <https://docs.mopidy.com/en/latest/config/>
|
||||||
|
# web client: <http://localhost:6680>
|
||||||
|
# mpd: hosted on `localhost:6600`, no password`
|
||||||
|
#
|
||||||
|
# dump config:
|
||||||
|
# - `mopidy config`
|
||||||
|
# update local file index with
|
||||||
|
# - `mopidy local scan`
|
||||||
|
#
|
||||||
|
# if running as service, those commands are `mopidy --config ... <command>`
|
||||||
|
# and config path is found by `systemctl cat mopidy`
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# TODO: upstream this as `mopidy.withExtensions`
|
||||||
|
# this is borrowed from the nixos mopidy service
|
||||||
|
mopidyWithExtensions = extensions: with pkgs; buildEnv {
|
||||||
|
name = "mopidy-with-extensions-${mopidy.version}";
|
||||||
|
|
||||||
|
paths = lib.closePropagation extensions;
|
||||||
|
pathsToLink = [ "/${mopidyPackages.python.sitePackages}" ];
|
||||||
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
postBuild = ''
|
||||||
|
makeWrapper ${mopidy}/bin/mopidy $out/bin/mopidy \
|
||||||
|
--prefix PYTHONPATH : $out/${mopidyPackages.python.sitePackages}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.mopidy = {
|
||||||
|
package = mopidyWithExtensions (with pkgs; [
|
||||||
|
mopidy-iris # web client: <https://github.com/jaedb/Iris>
|
||||||
|
mopidy-jellyfin
|
||||||
|
mopidy-local
|
||||||
|
mopidy-mpd
|
||||||
|
mopidy-mpris
|
||||||
|
mopidy-spotify
|
||||||
|
# TODO: mopidy-podcast, mopidy-youtube
|
||||||
|
|
||||||
|
# alternate web clients:
|
||||||
|
# mopidy-moped: <https://github.com/martijnboland/moped>
|
||||||
|
# mopidy-muse: <https://github.com/cristianpb/muse>
|
||||||
|
]);
|
||||||
|
persist.plaintext = [
|
||||||
|
".local/share/mopidy/local" # thumbs, library db
|
||||||
|
];
|
||||||
|
persist.private = [
|
||||||
|
".local/share/mopidy/http" # cookie
|
||||||
|
];
|
||||||
|
secrets.".config/mopidy/mopidy.conf" = ../../../secrets/common/mopidy.conf.bin;
|
||||||
|
# other folders:
|
||||||
|
# - .cache/mopidy
|
||||||
|
# - .config/mopidy
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,17 +8,41 @@
|
|||||||
{
|
{
|
||||||
sane.programs.mpv = {
|
sane.programs.mpv = {
|
||||||
package = pkgs.wrapMpv pkgs.mpv-unwrapped {
|
package = pkgs.wrapMpv pkgs.mpv-unwrapped {
|
||||||
youtubeSupport = false; #< XXX(2023/08/03): doesn't cross compile until next staging -> master merge
|
|
||||||
scripts = with pkgs.mpvScripts; [
|
scripts = with pkgs.mpvScripts; [
|
||||||
mpris
|
mpris
|
||||||
# uosc
|
# uosc
|
||||||
pkgs.mpv-uosc-latest
|
pkgs.mpv-uosc-latest
|
||||||
];
|
];
|
||||||
|
extraMakeWrapperArgs = [
|
||||||
|
# 2023/08/29: fixes an error where mpv on moby launches with the message
|
||||||
|
# "DRM_IOCTL_MODE_CREATE_DUMB failed: Cannot allocate memory"
|
||||||
|
# audio still works, and controls, screenshotting, etc -- just not the actual rendering
|
||||||
|
# this is likely a regression for mpv 0.36.0.
|
||||||
|
# the actual error message *appears* to come from the mesa library, but it's tough to trace.
|
||||||
|
# run with `--vo=help` to see a list of all output options.
|
||||||
|
# non-exhaustive (F=fails, W=works)
|
||||||
|
# ? libmpv render API for libmpv
|
||||||
|
# ? gpu Shader-based GPU Renderer
|
||||||
|
# ? gpu-next Video output based on libplacebo
|
||||||
|
# ? vdpau VDPAU with X11
|
||||||
|
# ? wlshm Wayland SHM video output (software scaling)
|
||||||
|
# ? xv X11/Xv
|
||||||
|
# W sdl SDL 2.0 Renderer
|
||||||
|
# F dmabuf-wayland Wayland dmabuf video output
|
||||||
|
# ? vaapi VA API with X11
|
||||||
|
# ? x11 X11 (software scaling)
|
||||||
|
# ? null Null video output
|
||||||
|
# ? caca libcaca
|
||||||
|
# F drm Direct Rendering Manager (software scaling)
|
||||||
|
"--add-flags" "--vo=sdl"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
persist.plaintext = [ ".config/mpv/watch_later" ];
|
persist.plaintext = [ ".local/state/mpv/watch_later" ];
|
||||||
fs.".config/mpv/input.conf".symlink.text = ''
|
fs.".config/mpv/input.conf".symlink.text = ''
|
||||||
# let volume keys be interpreted by the system.
|
# let volume/power keys be interpreted by the system.
|
||||||
# this is important for sxmo.
|
# this is important for sxmo.
|
||||||
|
# mpv defaults is POWER = close, VOLUME_{UP,DOWN} = adjust application-level volume
|
||||||
|
POWER ignore
|
||||||
VOLUME_UP ignore
|
VOLUME_UP ignore
|
||||||
VOLUME_DOWN ignore
|
VOLUME_DOWN ignore
|
||||||
'';
|
'';
|
||||||
@@ -26,6 +50,10 @@
|
|||||||
save-position-on-quit=yes
|
save-position-on-quit=yes
|
||||||
keep-open=yes
|
keep-open=yes
|
||||||
|
|
||||||
|
# force GUI, even for tracks w/o album art
|
||||||
|
# see: <https://www.reddit.com/r/mpv/comments/rvrrpt/oscosdgui_and_arch_linux/>
|
||||||
|
player-operation-mode=pseudo-gui
|
||||||
|
|
||||||
# use uosc instead (for On Screen Controls)
|
# use uosc instead (for On Screen Controls)
|
||||||
osc=no
|
osc=no
|
||||||
# uosc provides its own seeking/volume indicators, so you also don't need this
|
# uosc provides its own seeking/volume indicators, so you also don't need this
|
||||||
@@ -69,7 +97,8 @@
|
|||||||
ui_scale=1.0
|
ui_scale=1.0
|
||||||
'';
|
'';
|
||||||
|
|
||||||
mime.priority = 200; # default = 100; 200 means to yield to other apps
|
# mime.priority = 200; # default = 100; 200 means to yield to other apps
|
||||||
|
mime.priority = 50; # default = 100; 50 in order to take precedence over vlc.
|
||||||
mime.associations."audio/flac" = "mpv.desktop";
|
mime.associations."audio/flac" = "mpv.desktop";
|
||||||
mime.associations."audio/mpeg" = "mpv.desktop";
|
mime.associations."audio/mpeg" = "mpv.desktop";
|
||||||
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
mime.associations."audio/x-vorbis+ogg" = "mpv.desktop";
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ in
|
|||||||
env.EDITOR = "vim";
|
env.EDITOR = "vim";
|
||||||
# git claims it should use EDITOR, but it doesn't!
|
# git claims it should use EDITOR, but it doesn't!
|
||||||
env.GIT_EDITOR = "vim";
|
env.GIT_EDITOR = "vim";
|
||||||
|
mime.priority = 200; # default=100 => yield to other, more specialized applications
|
||||||
|
mime.associations."application/schema+json" = "nvim.desktop";
|
||||||
|
mime.associations."plain/text" = "nvim.desktop";
|
||||||
|
mime.associations."text/markdown" = "nvim.desktop";
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.neovim = mkIf config.sane.programs.neovim.enabled {
|
programs.neovim = mkIf config.sane.programs.neovim.enabled {
|
||||||
|
|||||||
40
hosts/common/programs/ntfy-sh.nix
Normal file
40
hosts/common/programs/ntfy-sh.nix
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# notification system, used especially to remotely wake moby
|
||||||
|
# source: <https://github.com/binwiederhier/ntfy>
|
||||||
|
# docs: <https://docs.ntfy.sh/>
|
||||||
|
#
|
||||||
|
# send a test notification with:
|
||||||
|
# - `ntfy pub "https://ntfy.uninsane.org/$(cat ~/.config/ntfy-sh/topic)" test`
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.ntfy-sh;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.ntfy-sh = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options.autostart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets.".config/ntfy-sh/topic" = ../../../secrets/common/ntfy-sh-topic.bin;
|
||||||
|
|
||||||
|
services.ntfy-sub = {
|
||||||
|
description = "listen for push-notifications";
|
||||||
|
wantedBy = lib.mkIf cfg.config.autostart [ "default.target" ];
|
||||||
|
path = [ cfg.package ];
|
||||||
|
script = ''
|
||||||
|
topic=$(cat ~/.config/ntfy-sh/topic)
|
||||||
|
ntfy sub "https://ntfy.uninsane.org:2587/$topic"
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = "20s";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
14
hosts/common/programs/playerctl.nix
Normal file
14
hosts/common/programs/playerctl.nix
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
sane.programs.playerctl.services.playerctld = {
|
||||||
|
description = "playerctl daemon to keep track of which MPRIS players were recently active";
|
||||||
|
documentation = [ "https://github.com/altdesktop/playerctl/issues/161" ];
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
serviceConfig.ExecStart = "${config.sane.programs.playerctl.package}/bin/playerctld";
|
||||||
|
# serviceConfig.Type = "dbus";
|
||||||
|
# serviceConfig.BusName = "org.mpris.MediaPlayer2.Player";
|
||||||
|
serviceConfig.Type = "simple"; # playerctl also supports a --daemon option, idk if that's better
|
||||||
|
serviceConfig.Restart = "on-failure";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
|
};
|
||||||
|
}
|
||||||
11
hosts/common/programs/rhythmbox.nix
Normal file
11
hosts/common/programs/rhythmbox.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.rhythmbox = {
|
||||||
|
persist.plaintext = [
|
||||||
|
# playlists; index
|
||||||
|
".local/share/rhythmbox"
|
||||||
|
# album art
|
||||||
|
".cache/rhythmbox"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
27
hosts/common/programs/stepmania.nix
Normal file
27
hosts/common/programs/stepmania.nix
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# configuration:
|
||||||
|
# - things like calibration data live in ~/.stepmania-5.1/Save/Preferences.ini
|
||||||
|
# - GlobalOffsetSeconds = difference between audio and video delay.
|
||||||
|
# Hit F6 twice in-game to being auto calibration
|
||||||
|
# Usually the result will be negative (i.e. the higher the latency of the pad, the more negative the offset)
|
||||||
|
# - SoundDevice: use pacmd list-sources and select alsa_output.pci-xxxxx
|
||||||
|
# - VisualOffset: if video is coming LATE, then use a negative number
|
||||||
|
#
|
||||||
|
# songs/packs:
|
||||||
|
# - find pad packs:
|
||||||
|
# - <https://docs.google.com/spreadsheets/d/1F1IURV1UAYiICTLhAOKIJfwUN1iG12ZOufHZuDKiP48/edit#gid=27038621>
|
||||||
|
# - https://www.reddit.com/r/Stepmania/comments/aku3lb/best_pad_packs_on_stepmaniaonlinenet_or_elsewhere/
|
||||||
|
# - https://fitupyourstyle.com/
|
||||||
|
# allows search by difficulty
|
||||||
|
# - dl packs from <https://stepmaniaonline.net>
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
sane.programs.stepmania = {
|
||||||
|
persist.plaintext = [
|
||||||
|
".stepmania-5.1/Cache" #< otherwise gotta index all the songs every launch
|
||||||
|
".stepmania-5.1/Save"
|
||||||
|
];
|
||||||
|
fs.".stepmania-5.1/Courses".symlink.target = "/mnt/servo-media/games/stepmania/Courses";
|
||||||
|
fs.".stepmania-5.1/Songs".symlink.target = "/mnt/servo-media/games/stepmania/Songs";
|
||||||
|
# TODO: setup ~/.stepmania-5.1/Themes
|
||||||
|
};
|
||||||
|
}
|
||||||
380
hosts/common/programs/swaynotificationcenter.nix
Normal file
380
hosts/common/programs/swaynotificationcenter.nix
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
# <https://github.com/ErikReider/SwayNotificationCenter>
|
||||||
|
# sway notification daemon
|
||||||
|
# alternative to mako, dunst, etc
|
||||||
|
#
|
||||||
|
# debugging:
|
||||||
|
# - `journalctl --user -u swaync`
|
||||||
|
# - `G_MESSAGES_DEBUG=all swaync`
|
||||||
|
# - reveal notification center: `swaync-client -t -sw`
|
||||||
|
#
|
||||||
|
# configuration:
|
||||||
|
# - defaults: /run/current-system/etc/profiles/per-user/colin/etc/xdg/swaync/
|
||||||
|
# - `man 5 swaync`
|
||||||
|
# - view document tree: `GTK_DEBUG=interactive swaync` (`systemctl stop --user swaync` first)
|
||||||
|
# - examples:
|
||||||
|
# - thread: <https://github.com/ErikReider/SwayNotificationCenter/discussions/183>
|
||||||
|
# - buttons-grid and menubar: <https://gist.github.com/JannisPetschenka/fb00eec3efea9c7fff8c38a01ce5d507>
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.programs.swaynotificationcenter;
|
||||||
|
fbcli-wrapper = pkgs.writeShellApplication {
|
||||||
|
name = "swaync-fbcli";
|
||||||
|
runtimeInputs = [
|
||||||
|
config.sane.programs.feedbackd.package
|
||||||
|
pkgs.procps # for pkill
|
||||||
|
cfg.package
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
# if in Do Not Disturb, don't do any feedback
|
||||||
|
# TODO: better solution is to actually make use of feedbackd profiles.
|
||||||
|
# i.e. set profile to `quiet` when in DnD mode
|
||||||
|
if [ "$SWAYNC_URGENCY" != "Critical" ] && [ "$(swaync-client --get-dnd)" = "true" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# kill children if killed, to allow that killing this parent process will end the real fbcli call
|
||||||
|
cleanup() {
|
||||||
|
echo "aborting fbcli notification (PID $child)"
|
||||||
|
pkill -P "$child"
|
||||||
|
exit 0 # exit cleanly to avoid swaync alerting a script failure
|
||||||
|
}
|
||||||
|
trap cleanup SIGINT SIGQUIT SIGTERM
|
||||||
|
|
||||||
|
# feedbackd stops playback when the caller exits
|
||||||
|
# and fbcli will exit immediately if it has no stdin.
|
||||||
|
# so spoof a stdin:
|
||||||
|
/bin/sh -c "true | fbcli $*" &
|
||||||
|
child=$!
|
||||||
|
wait
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
fbcli = "${fbcli-wrapper}/bin/swaync-fbcli";
|
||||||
|
|
||||||
|
# we do this because swaync's exec naively splits the command on space to produce its argv, rather than parsing the shell.
|
||||||
|
# [ "pkill" "-f" "fbcli" "--event" ... ] -> breaks pkill
|
||||||
|
# [ "pkill" "-f" "fbcli --event ..." ] -> is what we want
|
||||||
|
fbcli-stop-wrapper = pkgs.writeShellApplication {
|
||||||
|
name = "fbcli-stop";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.procps # for pkill
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
pkill -e -f "${fbcli} $*"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
fbcli-stop = "${fbcli-stop-wrapper}/bin/fbcli-stop";
|
||||||
|
|
||||||
|
kill-singleton_ = pkgs.writeShellApplication {
|
||||||
|
name = "kill-singleton";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.procps # for pgrep
|
||||||
|
pkgs.gnugrep
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
pids=$(pgrep --full "$*" | tr '\n' ' ') || true
|
||||||
|
# only act if there's exactly one pid
|
||||||
|
if echo "$pids" | grep -Eq '^[0-9]+ ?$'; then
|
||||||
|
kill "$pids"
|
||||||
|
else
|
||||||
|
echo "kill-singleton: skipping because multiple pids match: $pids"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
kill-singleton = "${kill-singleton_}/bin/kill-singleton";
|
||||||
|
|
||||||
|
systemctl-toggle = pkgs.writeShellApplication {
|
||||||
|
name = "systemctl-toggle";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.systemd
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
if systemctl is-active "$@"; then
|
||||||
|
systemctl stop "$@"
|
||||||
|
else
|
||||||
|
systemctl start "$@"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
sane.programs.swaynotificationcenter = {
|
||||||
|
configOption = with lib; mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
backlight = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "intel_backlight";
|
||||||
|
description = ''
|
||||||
|
name of entry in /sys/class/backlight which indicates the primary backlight.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
# prevent dbus from automatically activating swaync so i can manage it as a systemd service instead
|
||||||
|
package = pkgs.rmDbusServices (pkgs.swaynotificationcenter.overrideAttrs (upstream: {
|
||||||
|
# allow toggle buttons:
|
||||||
|
patches = (upstream.patches or []) ++ [
|
||||||
|
(pkgs.fetchpatch {
|
||||||
|
url = "https://github.com/ErikReider/SwayNotificationCenter/pull/304.patch";
|
||||||
|
name = "Add toggle button";
|
||||||
|
hash = "sha256-bove2EXc5FZ5nN1X1FYOn3czCgHG03ibIAupJNoctiM=";
|
||||||
|
})
|
||||||
|
(pkgs.fetchpatch {
|
||||||
|
url = "https://git.uninsane.org/colin/SwayNotificationCenter/commit/f5d9405e040fc42ea98dc4d37202c85728d0d4fd.patch";
|
||||||
|
name = "toggleButton: change active field to be a command";
|
||||||
|
hash = "sha256-Y8fiZbAP9yGOVU3rOkZKO8TnPPlrGpINWYGaqeeNzF0=";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
suggestedPrograms = [ "feedbackd" ];
|
||||||
|
fs.".config/swaync/style.css".symlink.text = ''
|
||||||
|
/* avoid black-on-black text that the default style ships */
|
||||||
|
window {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
}
|
||||||
|
button.active {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
background-color: rgb(0, 110, 190);
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
fs.".config/swaync/config.json".symlink.text = builtins.toJSON {
|
||||||
|
"$schema" = "/etc/xdg/swaync/configSchema.json";
|
||||||
|
positionX = "right";
|
||||||
|
positionY = "top";
|
||||||
|
layer = "overlay";
|
||||||
|
control-center-layer = "top";
|
||||||
|
layer-shell = true;
|
||||||
|
cssPriority = "user"; # "application"|"user". "user" in order to override the system gtk theme.
|
||||||
|
control-center-margin-top = 0;
|
||||||
|
control-center-margin-bottom = 0;
|
||||||
|
control-center-margin-right = 0;
|
||||||
|
control-center-margin-left = 0;
|
||||||
|
notification-2fa-action = true;
|
||||||
|
notification-inline-replies = false;
|
||||||
|
notification-icon-size = 64;
|
||||||
|
notification-body-image-height = 100;
|
||||||
|
notification-body-image-width = 200;
|
||||||
|
timeout = 30;
|
||||||
|
timeout-low = 5;
|
||||||
|
timeout-critical = 0;
|
||||||
|
fit-to-screen = true; #< have notification center take full vertical screen space
|
||||||
|
control-center-width = 400;
|
||||||
|
control-center-height = 600;
|
||||||
|
notification-window-width = 400;
|
||||||
|
keyboard-shortcuts = true;
|
||||||
|
image-visibility = "when-available";
|
||||||
|
transition-time = 100;
|
||||||
|
hide-on-clear = true; #< hide control center when clicking "clear all"
|
||||||
|
hide-on-action = true;
|
||||||
|
script-fail-notify = true;
|
||||||
|
scripts = {
|
||||||
|
# a script can match regex on these fields. only fired if all listed fields match:
|
||||||
|
# - app-name
|
||||||
|
# - desktop-entry
|
||||||
|
# - summary
|
||||||
|
# - body
|
||||||
|
# - urgency (Low/Normal/Critical)
|
||||||
|
# - category
|
||||||
|
# additionally, the script can be run either on receipt or action:
|
||||||
|
# - run-on = "receive" or "action"
|
||||||
|
# when script is run, these env vars are available:
|
||||||
|
# - SWAYNC_BODY
|
||||||
|
# - SWAYNC_DESKTOP_ENTRY
|
||||||
|
# - SWAYNC_URGENCY
|
||||||
|
# - SWAYNC_TIME
|
||||||
|
# - SWAYNC_APP_NAME
|
||||||
|
# - SWAYNC_CATEGORY
|
||||||
|
# - SWAYNC_REPLACES_ID
|
||||||
|
# - SWAYNC_ID
|
||||||
|
# - SWAYNC_SUMMARY
|
||||||
|
incoming-im = {
|
||||||
|
# trigger notification sound on behalf of these IM clients.
|
||||||
|
app-name = "(Chats|Dino|discord|Element|Fractal)";
|
||||||
|
body = "^(?!Incoming call).*$"; #< don't match Dino Incoming calls
|
||||||
|
exec = "${fbcli} --event proxied-message-new-instant";
|
||||||
|
};
|
||||||
|
incoming-call = {
|
||||||
|
app-name = "Dino";
|
||||||
|
body = "^Incoming call$";
|
||||||
|
exec = "${fbcli} --event phone-incoming-call -t 20";
|
||||||
|
};
|
||||||
|
incoming-call-acted-on = {
|
||||||
|
# when the notification is clicked, stop sounding the ringer
|
||||||
|
app-name = "Dino";
|
||||||
|
body = "^Incoming call$";
|
||||||
|
run-on = "action";
|
||||||
|
exec = "${fbcli-stop} --event phone-incoming-call -t 20";
|
||||||
|
};
|
||||||
|
timer-done = {
|
||||||
|
# sxmo_timer.sh fires off notifications like "Done with 10m" when a 10minute timer completes.
|
||||||
|
# it sends such a notification every second until dismissed
|
||||||
|
app-name = "notify-send";
|
||||||
|
summary = "^Done with .*$";
|
||||||
|
# XXX: could use alarm-clock-elapsed, but that's got a duration > 1s
|
||||||
|
# which isn't great for sxmo's 1s repeat.
|
||||||
|
# TODO: maybe better to have sxmo only notify once, and handle this like with Dino's incoming call
|
||||||
|
exec = "${fbcli} --event timeout-completed";
|
||||||
|
};
|
||||||
|
timer-done-acted-on = {
|
||||||
|
# when the notification is clicked, kill whichever sxmo process is sending it
|
||||||
|
app-name = "notify-send";
|
||||||
|
summary = "^Done with .*$";
|
||||||
|
run-on = "action";
|
||||||
|
# process tree looks like:
|
||||||
|
# - foot -T <...> /nix/store/.../sh /nix/store/.../.sxmo_timer.sh-wrapped timerrun <duration>
|
||||||
|
# - /nix/store/.../sh /nix/store/.../.sxmo_timer.sh-wrapped timerrun duration
|
||||||
|
# we want to match exactly one of those, reliably.
|
||||||
|
# foot might not be foot, but alacritty, kitty, or any other terminal.
|
||||||
|
exec = "${kill-singleton} ^[^ ]* ?[^ ]*sxmo_timer.sh(-wrapped)? timerrun";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
notification-visibility = {
|
||||||
|
# match incoming notifications and decide if they should be visible.
|
||||||
|
# map of rule-name => { criteria and effect };
|
||||||
|
# keys:
|
||||||
|
# - `state`: "ignored"|"muted"|"transient"|"enabled"
|
||||||
|
# => which visibility to apply to matched notifications
|
||||||
|
# => "ignored" behaves as if the notification was never sent.
|
||||||
|
# => "muted" adds it to the sidebar & sets the notif indicator but doesn't display it on main display
|
||||||
|
# - `override-urgency`: "unset"|"low"|"normal"|"critical"
|
||||||
|
# => which urgency to apply to matched notifs
|
||||||
|
# critera: each key is optional, value is regex; rule applies if *all* specified are matched
|
||||||
|
# - `app-name`: string
|
||||||
|
# - `desktop-entry`: string
|
||||||
|
# - `summary`: string
|
||||||
|
# - `body`: string
|
||||||
|
# - `urgency`: "Low"|"Normal"|"Critical"
|
||||||
|
# - `category`: string
|
||||||
|
#
|
||||||
|
# test rules by using `notify-send` (libnotify)
|
||||||
|
sxmo-extraneous-daemons = {
|
||||||
|
app-name = "notify-send";
|
||||||
|
summary = "(sxmo_hook_lisgd|Autorotate) (Stopped|Started)";
|
||||||
|
state = "ignored";
|
||||||
|
};
|
||||||
|
sxmo-extraneous-warnings = {
|
||||||
|
app-name = "notify-send";
|
||||||
|
# "Modem crashed! 30s recovery.": happens on sxmo_hook_postwake.sh (i.e. unlock)
|
||||||
|
summary = "^Modem crashed.*$";
|
||||||
|
state = "ignored";
|
||||||
|
};
|
||||||
|
sxmo-timer = {
|
||||||
|
# force timer announcements to bypass DND
|
||||||
|
app-name = "notify-send";
|
||||||
|
summary = "^Done with .*$";
|
||||||
|
override-urgency = "critical";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
widgets = [
|
||||||
|
# what to show in the notification center (and in which order).
|
||||||
|
# these are configurable further via `widget-config`.
|
||||||
|
# besides these listed, there are general-purpose UI tools:
|
||||||
|
# - label (show some text)
|
||||||
|
# - buttons-grid (labels which trigger actions when clicked)
|
||||||
|
# - menubar (tree of labels/actions)
|
||||||
|
"title"
|
||||||
|
"dnd"
|
||||||
|
"inhibitors"
|
||||||
|
"buttons-grid"
|
||||||
|
"backlight"
|
||||||
|
"volume"
|
||||||
|
"mpris"
|
||||||
|
"notifications"
|
||||||
|
];
|
||||||
|
widget-config = {
|
||||||
|
backlight = {
|
||||||
|
label = " ";
|
||||||
|
device = cfg.config.backlight;
|
||||||
|
};
|
||||||
|
buttons-grid = {
|
||||||
|
actions =
|
||||||
|
# {
|
||||||
|
# type = "toggle";
|
||||||
|
# label = "feedbackd";
|
||||||
|
# command = "${systemctl-toggle}/bin/systemctl-toggle --user feedbackd";
|
||||||
|
# active = "${pkgs.systemd}/bin/systemctl is-active --user feedbackd.service";
|
||||||
|
# }
|
||||||
|
lib.optionals config.sane.programs.eg25-control.enabled [
|
||||||
|
{
|
||||||
|
type = "toggle";
|
||||||
|
label = "gps";
|
||||||
|
command = "/run/wrappers/bin/sudo ${systemctl-toggle}/bin/systemctl-toggle eg25-control-gps";
|
||||||
|
active = "${pkgs.systemd}/bin/systemctl is-active eg25-control-gps.service";
|
||||||
|
}
|
||||||
|
] ++ [
|
||||||
|
{
|
||||||
|
type = "toggle";
|
||||||
|
label = "vpn::hn";
|
||||||
|
command = "/run/wrappers/bin/sudo ${systemctl-toggle}/bin/systemctl-toggle wg-quick-vpn-servo";
|
||||||
|
active = "${pkgs.systemd}/bin/systemctl is-active wg-quick-vpn-servo.service";
|
||||||
|
}
|
||||||
|
] ++ lib.optionals config.sane.programs.calls.config.autostart [
|
||||||
|
{
|
||||||
|
type = "toggle";
|
||||||
|
label = "SIP";
|
||||||
|
command = "${systemctl-toggle}/bin/systemctl-toggle --user gnome-calls";
|
||||||
|
active = "${pkgs.systemd}/bin/systemctl is-active --user gnome-calls";
|
||||||
|
}
|
||||||
|
] ++ lib.optionals config.sane.programs.dino.enabled [
|
||||||
|
{
|
||||||
|
type = "toggle";
|
||||||
|
label = "XMPP"; # XMPP calls (jingle)
|
||||||
|
command = "${systemctl-toggle}/bin/systemctl-toggle --user dino";
|
||||||
|
active = "${pkgs.systemd}/bin/systemctl is-active --user dino";
|
||||||
|
}
|
||||||
|
] ++ lib.optionals config.sane.programs.fractal.enabled [
|
||||||
|
{
|
||||||
|
type = "toggle";
|
||||||
|
label = "Matrix"; # Matrix messages
|
||||||
|
command = "${systemctl-toggle}/bin/systemctl-toggle --user fractal";
|
||||||
|
active = "${pkgs.systemd}/bin/systemctl is-active --user fractal";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
dnd = {
|
||||||
|
text = "Do Not Disturb";
|
||||||
|
};
|
||||||
|
inhibitors = {
|
||||||
|
text = "Inhibitors";
|
||||||
|
button-text = "Clear All";
|
||||||
|
clear-all-button = true;
|
||||||
|
};
|
||||||
|
mpris = {
|
||||||
|
image-size = 64;
|
||||||
|
image-radius = 8;
|
||||||
|
};
|
||||||
|
title = {
|
||||||
|
text = "Notifications";
|
||||||
|
clear-all-button = true;
|
||||||
|
button-text = "Clear All";
|
||||||
|
};
|
||||||
|
volume = {
|
||||||
|
label = " ";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.swaync = {
|
||||||
|
# swaync ships its own service, but i want to add `environment` variables and flags for easier debugging.
|
||||||
|
# seems that's not possible without defining an entire nix-native service (i.e. this).
|
||||||
|
description = "Swaync desktop notification daemon";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
serviceConfig.ExecStart = "${cfg.package}/bin/swaync";
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
# serviceConfig.BusName = "org.freedesktop.Notifications";
|
||||||
|
serviceConfig.Restart = "on-failure";
|
||||||
|
serviceConfig.RestartSec = "10s";
|
||||||
|
environment.G_MESSAGES_DEBUG = "all";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sane.programs.feedbackd.config = lib.mkIf cfg.enabled {
|
||||||
|
# claim control over feedbackd: we'll proxy the sounds we want on behalf of notifying programs
|
||||||
|
proxied = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,4 +1,26 @@
|
|||||||
{ ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
sane.programs.tuba.suggestedPrograms = [ "gnome-keyring" ];
|
sane.programs.tuba = {
|
||||||
|
package = pkgs.tuba.overrideAttrs (upstream: {
|
||||||
|
postInstall = (upstream.postInstall or "") + ''
|
||||||
|
# ship a `tuba` alias to the actual tuba binary, since i can never remember its name
|
||||||
|
ln -s $out/bin/dev.geopjr.Tuba $out/bin/tuba
|
||||||
|
'';
|
||||||
|
|
||||||
|
preFixup = (upstream.preFixup or "") + ''
|
||||||
|
# 2023/09/24: fix so i can upload media when creating a post.
|
||||||
|
# see: <https://github.com/GeopJr/Tuba/issues/414#issuecomment-1732695845>
|
||||||
|
gappsWrapperArgs+=(
|
||||||
|
--prefix GDK_DEBUG , no-portals
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
# alternative to disabling portals is to remove the filters on FileDialogs.
|
||||||
|
# done like so (but would want to apply to the other dialogs too)
|
||||||
|
# postPatch = (upstream.postPatch or "") + ''
|
||||||
|
# substituteInPlace src/Dialogs/ProfileEdit.vala \
|
||||||
|
# --replace "default_filter = filter" ""
|
||||||
|
# '';
|
||||||
|
});
|
||||||
|
suggestedPrograms = [ "gnome-keyring" ];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
# - $ZDOTDIR/.zprofile
|
# - $ZDOTDIR/.zprofile
|
||||||
# - if interactive:
|
# - if interactive:
|
||||||
# - /etc/zshrc
|
# - /etc/zshrc
|
||||||
|
# -> /etc/zinputrc
|
||||||
# - $ZDOTDIR/.zshrc
|
# - $ZDOTDIR/.zshrc
|
||||||
# - if login (again):
|
# - if login (again):
|
||||||
# - /etc/zlogin
|
# - /etc/zlogin
|
||||||
@@ -23,7 +24,6 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./p10k.nix
|
|
||||||
./starship.nix
|
./starship.nix
|
||||||
];
|
];
|
||||||
options = {
|
options = {
|
||||||
@@ -33,11 +33,6 @@ in
|
|||||||
default = true;
|
default = true;
|
||||||
description = "show upcoming deadlines (from my PKM) upon shell init";
|
description = "show upcoming deadlines (from my PKM) upon shell init";
|
||||||
};
|
};
|
||||||
p10k = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = "enable powerlevel10k prompt and prezto";
|
|
||||||
};
|
|
||||||
starship = mkOption {
|
starship = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
@@ -54,10 +49,6 @@ in
|
|||||||
# but zsh will sometimes backup the history file and symlinking just the file messes things up
|
# but zsh will sometimes backup the history file and symlinking just the file messes things up
|
||||||
".local/share/zsh"
|
".local/share/zsh"
|
||||||
];
|
];
|
||||||
persist.plaintext = [
|
|
||||||
# cache gitstatus otherwise p10k fetches it from the net EVERY BOOT
|
|
||||||
".cache/gitstatus"
|
|
||||||
];
|
|
||||||
|
|
||||||
fs.".config/zsh/.zshrc".symlink.text = ''
|
fs.".config/zsh/.zshrc".symlink.text = ''
|
||||||
# zsh/prezto complains if zshrc doesn't exist or is empty;
|
# zsh/prezto complains if zshrc doesn't exist or is empty;
|
||||||
@@ -79,6 +70,18 @@ in
|
|||||||
hash -d tmp="/home/colin/tmp"
|
hash -d tmp="/home/colin/tmp"
|
||||||
hash -d uninsane="/home/colin/dev/uninsane"
|
hash -d uninsane="/home/colin/dev/uninsane"
|
||||||
hash -d Videos="/home/colin/Videos"
|
hash -d Videos="/home/colin/Videos"
|
||||||
|
|
||||||
|
# emulate bash keybindings
|
||||||
|
bindkey -e
|
||||||
|
|
||||||
|
# or manually recreate what i care about...
|
||||||
|
# key[Left]=''${terminfo[kcub1]}
|
||||||
|
# key[Right]=''${terminfo[kcuf1]}
|
||||||
|
# bindkey '^R' history-incremental-search-backward
|
||||||
|
# bindkey '^A' beginning-of-line
|
||||||
|
# bindkey '^E' end-of-line
|
||||||
|
# bindkey "^''${key[Left]}" backward-word
|
||||||
|
# bindkey "^''${key[Right]}" forward-word
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -93,6 +96,9 @@ in
|
|||||||
# common typos
|
# common typos
|
||||||
"cd.." = "cd ..";
|
"cd.." = "cd ..";
|
||||||
"cd../" = "cd ../";
|
"cd../" = "cd ../";
|
||||||
|
# overcome poor defaults
|
||||||
|
"lsof" = "lsof -P"; #< lsof: use port *numbers*, not names
|
||||||
|
"tcpdump" = "tcpdump -n"; #< tcpdump: use port *numbers*, not names
|
||||||
};
|
};
|
||||||
setOptions = [
|
setOptions = [
|
||||||
# docs: `man zshoptions`
|
# docs: `man zshoptions`
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
{ config, lib, pkgs, ...}:
|
|
||||||
|
|
||||||
let
|
|
||||||
# powerlevel10k prompt config
|
|
||||||
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
|
|
||||||
p10k-overrides = ''
|
|
||||||
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
|
|
||||||
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
|
|
||||||
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
|
|
||||||
# i can disable gitstatusd and get slower fallback git queries:
|
|
||||||
# - either universally
|
|
||||||
# - or selectively by path
|
|
||||||
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
|
|
||||||
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
|
|
||||||
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
|
|
||||||
|
|
||||||
# show user@host also when logged into the current machine.
|
|
||||||
# default behavior is to show it only over ssh.
|
|
||||||
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
|
|
||||||
'';
|
|
||||||
|
|
||||||
prezto-init = ''
|
|
||||||
source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh
|
|
||||||
source ${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
|
|
||||||
source ${pkgs.zsh-prezto}/share/zsh-prezto/init.zsh
|
|
||||||
'';
|
|
||||||
in {
|
|
||||||
config = lib.mkIf config.sane.zsh.p10k {
|
|
||||||
sane.programs.zsh = {
|
|
||||||
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
|
|
||||||
# see: https://github.com/sorin-ionescu/prezto
|
|
||||||
# this file is auto-sourced by the prezto init.zsh script.
|
|
||||||
# TODO: i should work to move away from prezto:
|
|
||||||
# - it's FUCKING SLOW to initialize (that might also be powerlevel10k tho)
|
|
||||||
# - it messes with my other `setopt`s
|
|
||||||
fs.".config/zsh/.zpreztorc".symlink.text = ''
|
|
||||||
zstyle ':prezto:*:*' color 'yes'
|
|
||||||
zstyle ':prezto:module:utility' correct 'no' # prezto: don't setopt CORRECT
|
|
||||||
|
|
||||||
# modules (they ship with prezto):
|
|
||||||
# ENVIRONMENT: configures jobs to persist after shell exit; other basic niceties
|
|
||||||
# TERMINAL: auto-titles terminal (e.g. based on cwd)
|
|
||||||
# EDITOR: configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
|
|
||||||
# HISTORY: `history-stat` alias, setopts for good history defaults
|
|
||||||
# DIRECTORY: sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack. also overrides CLOBBER and some other options
|
|
||||||
# SPECTRUM: helpers for term colors and styling. used by prompts? might be unnecessary
|
|
||||||
# UTILITY: configures aliases like `ll`, `la`, disables globbing for things like rsync
|
|
||||||
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
|
|
||||||
# COMPLETION: tab completion. requires `utility` module prior to loading
|
|
||||||
zstyle ':prezto:load' pmodule \
|
|
||||||
'environment' \
|
|
||||||
'terminal' \
|
|
||||||
'editor' \
|
|
||||||
'history' \
|
|
||||||
'spectrum' \
|
|
||||||
'utility' \
|
|
||||||
'completion' \
|
|
||||||
'prompt'
|
|
||||||
|
|
||||||
# default keymap. try also `vicmd` (vim normal mode, AKA "cmd mode") or `vi`.
|
|
||||||
zstyle ':prezto:module:editor' key-bindings 'emacs'
|
|
||||||
|
|
||||||
zstyle ':prezto:module:prompt' theme 'powerlevel10k'
|
|
||||||
|
|
||||||
# disable `mv` confirmation (and `rm`, too, unfortunately)
|
|
||||||
zstyle ':prezto:module:utility' safe-ops 'no'
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.zsh.interactiveShellInit = (builtins.readFile ./p10k.zsh)
|
|
||||||
+ p10k-overrides
|
|
||||||
+ prezto-init
|
|
||||||
;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -28,21 +28,26 @@
|
|||||||
{ config, lib, sane-lib, ... }:
|
{ config, lib, sane-lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (lib.strings) hasSuffix removeSuffix;
|
|
||||||
secretsForHost = host: let
|
secretsForHost = host: let
|
||||||
extraAttrsForPath = path: lib.optionalAttrs (sane-lib.path.isChild "guest" path && builtins.hasAttr "guest" config.users.users) {
|
extraAttrsForPath = path: lib.optionalAttrs (sane-lib.path.isChild "guest" path && builtins.hasAttr "guest" config.users.users) {
|
||||||
owner = "guest";
|
owner = "guest";
|
||||||
};
|
};
|
||||||
|
secretsInSrc = (
|
||||||
|
if builtins.pathExists ../../secrets/${host} then
|
||||||
|
sane-lib.enumerateFilePaths ../../secrets/${host}
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
);
|
||||||
in sane-lib.joinAttrsets (
|
in sane-lib.joinAttrsets (
|
||||||
map
|
map
|
||||||
(path: lib.optionalAttrs (hasSuffix ".bin" path) (sane-lib.nameValueToAttrs {
|
(path: lib.optionalAttrs (lib.hasSuffix ".bin" path) (sane-lib.nameValueToAttrs {
|
||||||
name = removeSuffix ".bin" path;
|
name = lib.removeSuffix ".bin" path;
|
||||||
value = {
|
value = {
|
||||||
sopsFile = ../../secrets/${host}/${path};
|
sopsFile = ../../secrets/${host}/${path};
|
||||||
format = "binary";
|
format = "binary";
|
||||||
} // (extraAttrsForPath path);
|
} // (extraAttrsForPath path);
|
||||||
}))
|
}))
|
||||||
(sane-lib.enumerateFilePaths ../../secrets/${host})
|
secretsInSrc
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
group = "users";
|
group = "users";
|
||||||
extraGroups = [
|
extraGroups = [
|
||||||
"dialout" # required for modem access (moby)
|
"dialout" # required for modem access (moby)
|
||||||
"feedbackd"
|
"export" # to read filesystem exports (servo)
|
||||||
|
"feedbackd" # moby, so `fbcli` can control vibrator and LEDs
|
||||||
"input" # for /dev/input/<xyz>: sxmo
|
"input" # for /dev/input/<xyz>: sxmo
|
||||||
|
"media" # servo, for /var/lib/uninsane/media
|
||||||
"networkmanager"
|
"networkmanager"
|
||||||
"nixbuild"
|
"nixbuild"
|
||||||
|
"systemd-journal" # allows to view other user's journals (esp system users)
|
||||||
"transmission" # servo, to admin /var/lib/uninsane/media
|
"transmission" # servo, to admin /var/lib/uninsane/media
|
||||||
"video" # mobile; for LEDs & maybe for camera?
|
"video" # mobile; for LEDs & maybe for camera?
|
||||||
"wheel"
|
"wheel"
|
||||||
@@ -29,7 +32,7 @@
|
|||||||
# initial password is empty, in case anything goes wrong.
|
# initial password is empty, in case anything goes wrong.
|
||||||
# if `colin-passwd` (a password hash) is successfully found/decrypted, that becomes the password at boot.
|
# if `colin-passwd` (a password hash) is successfully found/decrypted, that becomes the password at boot.
|
||||||
initialPassword = lib.mkDefault "";
|
initialPassword = lib.mkDefault "";
|
||||||
passwordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
|
hashedPasswordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
|
||||||
|
|
||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
|
|
||||||
@@ -77,16 +80,18 @@
|
|||||||
# ".rustup"
|
# ".rustup"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# fs.".cargo".symlink.target = "/tmp/colin-cargo";
|
||||||
|
|
||||||
# convenience
|
# convenience
|
||||||
fs."knowledge".symlink.target = "private/knowledge";
|
fs."knowledge".symlink.target = "private/knowledge";
|
||||||
fs."nixos".symlink.target = "dev/nixos";
|
fs."nixos".symlink.target = "dev/nixos";
|
||||||
fs."Books/servo".symlink.target = "/mnt/servo-media/Books";
|
fs."Books/servo".symlink.target = "/mnt/servo-media/Books";
|
||||||
fs."Videos/servo".symlink.target = "/mnt/servo-media/Videos";
|
fs."Videos/servo".symlink.target = "/mnt/servo-media/Videos";
|
||||||
fs."Videos/servo-incomplete".symlink.target = "/mnt/servo-media/incomplete";
|
# fs."Music/servo".symlink.target = "/mnt/servo-media/Music";
|
||||||
fs."Music/servo".symlink.target = "/mnt/servo-media/Music";
|
|
||||||
fs."Pictures/servo-macros".symlink.target = "/mnt/servo-media/Pictures/macros";
|
fs."Pictures/servo-macros".symlink.target = "/mnt/servo-media/Pictures/macros";
|
||||||
|
|
||||||
# used by password managers, e.g. unix `pass`
|
# used by password managers, e.g. unix `pass`
|
||||||
|
# TODO: move this to the specific programs which need it
|
||||||
fs.".password-store".symlink.target = "knowledge/secrets/accounts";
|
fs.".password-store".symlink.target = "knowledge/secrets/accounts";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
./root.nix
|
./root.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
users.groups.media = {};
|
||||||
|
|
||||||
# Users are exactly these specified here;
|
# Users are exactly these specified here;
|
||||||
# old ones will be deleted (from /etc/passwd, etc) upon upgrade.
|
# old ones will be deleted (from /etc/passwd, etc) upon upgrade.
|
||||||
users.mutableUsers = false;
|
users.mutableUsers = false;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ config, lib, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
# to add a new OVPN VPN:
|
# to add a new OVPN VPN:
|
||||||
# - generate a privkey `wg genkey`
|
# - generate a privkey `wg genkey`
|
||||||
@@ -8,14 +8,9 @@
|
|||||||
# - copy the Address, PublicKey, Endpoint from OVPN's config
|
# - copy the Address, PublicKey, Endpoint from OVPN's config
|
||||||
# N.B.: maximum interface name in Linux is 15 characters.
|
# N.B.: maximum interface name in Linux is 15 characters.
|
||||||
let
|
let
|
||||||
def-ovpn = name: { endpoint, publicKey, address }: {
|
def-wg-vpn = name: { endpoint, publicKey, address, dns, privateKeyFile, extraOptions ? {} }: {
|
||||||
networking.wg-quick.interfaces."ovpnd-${name}" = {
|
networking.wg-quick.interfaces."${name}" = {
|
||||||
inherit address;
|
inherit address privateKeyFile dns;
|
||||||
privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path;
|
|
||||||
dns = [
|
|
||||||
"46.227.67.134"
|
|
||||||
"192.165.9.158"
|
|
||||||
];
|
|
||||||
peers = [
|
peers = [
|
||||||
{
|
{
|
||||||
allowedIPs = [
|
allowedIPs = [
|
||||||
@@ -25,11 +20,38 @@ let
|
|||||||
inherit endpoint publicKey;
|
inherit endpoint publicKey;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
# to start: `systemctl start wg-quick-ovpnd-${name}`
|
# to start: `systemctl start wg-quick-${name}`
|
||||||
autostart = false;
|
autostart = false;
|
||||||
|
} // extraOptions;
|
||||||
|
};
|
||||||
|
def-ovpn = name: { endpoint, publicKey, address }: def-wg-vpn "ovpnd-${name}" {
|
||||||
|
inherit endpoint publicKey address;
|
||||||
|
privateKeyFile = config.sops.secrets."wg/ovpnd_${name}_privkey".path;
|
||||||
|
dns = [
|
||||||
|
"46.227.67.134"
|
||||||
|
"192.165.9.158"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: this should live in the same file as hosts/modules/wg-home.nix...
|
||||||
|
def-servo = def-wg-vpn "vpn-servo" {
|
||||||
|
endpoint = config.sane.hosts.by-name."servo".wg-home.endpoint;
|
||||||
|
publicKey = config.sane.hosts.by-name."servo".wg-home.pubkey;
|
||||||
|
address = [ config.sane.services.wg-home.ip ];
|
||||||
|
dns = [
|
||||||
|
config.sane.hosts.by-name."servo".wg-home.ip
|
||||||
|
];
|
||||||
|
privateKeyFile = config.networking.wireguard.interfaces.wg-home.privateKeyFile;
|
||||||
|
extraOptions = {
|
||||||
|
# wg-home and vpn-servo interfaces interfere with the result that when connected to both,
|
||||||
|
# other wg-home users (lappy-hn, ...) aren't visible. disabling wg-home while the full
|
||||||
|
# vpn-servo is active allows wg-home users to be reachable again
|
||||||
|
preUp = "${pkgs.iproute2}/bin/ip link set wg-home down";
|
||||||
|
postDown = "${pkgs.iproute2}/bin/ip link set wg-home up";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in lib.mkMerge [
|
in lib.mkMerge [
|
||||||
|
(def-servo)
|
||||||
(def-ovpn "us" {
|
(def-ovpn "us" {
|
||||||
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
|
||||||
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./derived-secrets
|
./derived-secrets
|
||||||
./gui
|
./gui
|
||||||
./hardware
|
|
||||||
./hostnames.nix
|
./hostnames.nix
|
||||||
./hosts.nix
|
./hosts.nix
|
||||||
./nixcache.nix
|
./nixcache.nix
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./gnome.nix
|
./gnome.nix
|
||||||
|
./greetd.nix
|
||||||
./gtk.nix
|
./gtk.nix
|
||||||
./phosh.nix
|
./phosh.nix
|
||||||
./sway
|
./sway
|
||||||
./sxmo
|
./sxmo
|
||||||
|
./theme
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.programs.guiApps = {
|
sane.programs.guiApps = {
|
||||||
@@ -16,33 +18,38 @@
|
|||||||
# package sets
|
# package sets
|
||||||
"tuiApps"
|
"tuiApps"
|
||||||
] ++ [
|
] ++ [
|
||||||
|
"alacritty" # terminal emulator
|
||||||
|
"calls" # gnome calls (dialer/handler)
|
||||||
# "celluloid" # mpv frontend
|
# "celluloid" # mpv frontend
|
||||||
"chatty" # matrix/xmpp/irc client
|
"chatty" # matrix/xmpp/irc client
|
||||||
"cozy" # audiobook player
|
"cozy" # audiobook player
|
||||||
|
"dino" # XMPP client
|
||||||
# "emote"
|
# "emote"
|
||||||
"epiphany" # gnome's web browser
|
"epiphany" # gnome's web browser
|
||||||
"evince" # works on phosh
|
"evince" # works on phosh
|
||||||
"firefox"
|
"firefox"
|
||||||
# "foliate" # e-book reader
|
# "foliate" # e-book reader
|
||||||
# "fractal" # matrix client
|
"fractal" # matrix client
|
||||||
|
"g4music" # local music player
|
||||||
# "gnome.cheese"
|
# "gnome.cheese"
|
||||||
# "gnome-feeds" # RSS reader (with claimed mobile support)
|
# "gnome-feeds" # RSS reader (with claimed mobile support)
|
||||||
# "gnome.file-roller"
|
# "gnome.file-roller"
|
||||||
# "gnome.gnome-maps" # works on phosh
|
"gnome.gnome-maps"
|
||||||
# "gnome-podcasts"
|
# "gnome-podcasts"
|
||||||
# "gnome.gnome-system-monitor"
|
# "gnome.gnome-system-monitor"
|
||||||
# "gnome.gnome-terminal" # works on phosh
|
# "gnome.gnome-terminal" # works on phosh
|
||||||
# "gnome.gnome-weather"
|
"gnome.gnome-weather"
|
||||||
"gpodder"
|
"gpodder"
|
||||||
"gthumb"
|
"gthumb"
|
||||||
"komikku"
|
"komikku"
|
||||||
"koreader"
|
"koreader"
|
||||||
"lemoa" # lemmy app
|
"lemoa" # lemmy app
|
||||||
# "lollypop"
|
# "lollypop"
|
||||||
|
"mate.engrampa" # archive manager
|
||||||
"mepo" # maps viewer
|
"mepo" # maps viewer
|
||||||
"mpv"
|
"mpv"
|
||||||
"nheko"
|
"networkmanagerapplet" # for nm-connection-editor: it's better than not having any gui!
|
||||||
# "networkmanagerapplet"
|
"ntfy-sh" # notification service
|
||||||
# "newsflash"
|
# "newsflash"
|
||||||
"pavucontrol"
|
"pavucontrol"
|
||||||
# "picard" # music tagging
|
# "picard" # music tagging
|
||||||
@@ -64,28 +71,30 @@
|
|||||||
"audacity"
|
"audacity"
|
||||||
"blanket" # ambient noise generator
|
"blanket" # ambient noise generator
|
||||||
"brave" # for the integrated wallet -- as a backup
|
"brave" # for the integrated wallet -- as a backup
|
||||||
|
# "cantata" # music player (mpd frontend)
|
||||||
# "chromium" # chromium takes hours to build. brave is chromium-based, distributed in binary form, so prefer it.
|
# "chromium" # chromium takes hours to build. brave is chromium-based, distributed in binary form, so prefer it.
|
||||||
# "dino" # XMPP client
|
|
||||||
"electrum"
|
"electrum"
|
||||||
"element-desktop"
|
"element-desktop"
|
||||||
# "font-manager" #< depends on webkitgtk4_0 (expensive to build)
|
"font-manager"
|
||||||
# "gajim" # XMPP client
|
# "gajim" # XMPP client. cross build tries to import host gobject-introspection types (2023/09/01)
|
||||||
"gimp" # broken on phosh
|
"gimp" # broken on phosh
|
||||||
"gnome.dconf-editor"
|
# "gnome.dconf-editor"
|
||||||
"gnome.file-roller"
|
# "gnome.file-roller"
|
||||||
"gnome.gnome-disk-utility"
|
"gnome.gnome-disk-utility"
|
||||||
"gnome.nautilus" # file browser
|
"gnome.nautilus" # file browser
|
||||||
# "gnome.totem" # video player, supposedly supports UPnP
|
# "gnome.totem" # video player, supposedly supports UPnP
|
||||||
"handbrake"
|
"handbrake"
|
||||||
"hase"
|
"hase"
|
||||||
"inkscape"
|
"inkscape"
|
||||||
"jellyfin-media-player"
|
# "jellyfin-media-player"
|
||||||
"kdenlive"
|
"kdenlive"
|
||||||
"kid3" # audio tagging
|
"kid3" # audio tagging
|
||||||
"krita"
|
"krita"
|
||||||
"libreoffice" # TODO: replace with an office suite that uses saner packaging?
|
"libreoffice" # TODO: replace with an office suite that uses saner packaging?
|
||||||
"mumble"
|
"mumble"
|
||||||
"obsidian"
|
"nheko"
|
||||||
|
# "obsidian"
|
||||||
|
# "rhythmbox" # local music player
|
||||||
"slic3r"
|
"slic3r"
|
||||||
"steam"
|
"steam"
|
||||||
"vlc"
|
"vlc"
|
||||||
@@ -125,4 +134,13 @@
|
|||||||
"/var/lib/colord" # preserve color calibrations (?)
|
"/var/lib/colord" # preserve color calibrations (?)
|
||||||
"/var/lib/systemd/backlight" # backlight brightness
|
"/var/lib/systemd/backlight" # backlight brightness
|
||||||
];
|
];
|
||||||
|
|
||||||
|
hardware.opengl = lib.mkIf config.sane.programs.guiApps.enabled ({
|
||||||
|
enable = true;
|
||||||
|
driSupport = lib.mkDefault true;
|
||||||
|
} // (lib.optionalAttrs pkgs.stdenv.isx86_64 {
|
||||||
|
# for 32 bit applications
|
||||||
|
# upstream nixpkgs forbids setting driSupport32Bit unless specifically x86_64 (so aarch64 isn't allowed)
|
||||||
|
driSupport32Bit = lib.mkDefault true;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ in
|
|||||||
# favorite-apps = [
|
# favorite-apps = [
|
||||||
# "org.gnome.Nautilus.desktop"
|
# "org.gnome.Nautilus.desktop"
|
||||||
# "firefox.desktop"
|
# "firefox.desktop"
|
||||||
# "kitty.desktop"
|
|
||||||
# # "org.gnome.Terminal.desktop"
|
# # "org.gnome.Terminal.desktop"
|
||||||
# ];
|
# ];
|
||||||
# };
|
# };
|
||||||
|
|||||||
128
hosts/modules/gui/greetd.nix
Normal file
128
hosts/modules/gui/greetd.nix
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# greetd source/docs:
|
||||||
|
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
systemd-cat = "${pkgs.systemd}/bin/systemd-cat";
|
||||||
|
runWithLogger = identifier: cmd: pkgs.writeShellScriptBin identifier ''
|
||||||
|
echo "launching ${identifier}..." | ${systemd-cat} --identifier=${identifier}
|
||||||
|
${cmd} 2>&1 | ${systemd-cat} --identifier=${identifier}
|
||||||
|
'';
|
||||||
|
cfg = config.sane.gui.greetd;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = with lib; {
|
||||||
|
sane.gui.greetd.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
sane.gui.greetd.session.command = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
name to use for the default session in syslog.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.session.name = mkOption {
|
||||||
|
default = "greetd-session";
|
||||||
|
type = types.str;
|
||||||
|
description = "name of session to use in logger";
|
||||||
|
};
|
||||||
|
sane.gui.greetd.session.user = mkOption {
|
||||||
|
default = null;
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
};
|
||||||
|
|
||||||
|
# helpers for common things to layer on top of greetd
|
||||||
|
sane.gui.greetd.sway.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
use sway as a wayland compositor in which to host a graphical greeter like gtkgreet, phog, etc.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.sway.greeterCmd = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
command for sway to `exec` that provides the actual graphical greeter.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.sway.gtkgreet.enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
have sway launch gtkgreet instead of directly presenting a desktop.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.sway.gtkgreet.session.command = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
command for gtkgreet to execute on successful authentication.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.sway.gtkgreet.session.name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
name to use for the default session in syslog and in the gtkgreet menu.
|
||||||
|
note that this `sessionName` will become a binary on the user's PATH.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.greetd.sway.gtkgreet.session.user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "colin";
|
||||||
|
description = ''
|
||||||
|
name of user which one expects to login as.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||||
|
(lib.mkIf cfg.sway.enable {
|
||||||
|
sane.gui.greetd.session = if cfg.sway.greeterCmd != null then {
|
||||||
|
name = "sway-as-greeter";
|
||||||
|
command = let
|
||||||
|
swayAsGreeterConfig = pkgs.writeText "sway-as-greeter-config" ''
|
||||||
|
exec ${cfg.sway.greeterCmd}
|
||||||
|
'';
|
||||||
|
in "${pkgs.sway}/bin/sway --debug --config ${swayAsGreeterConfig}";
|
||||||
|
} else {
|
||||||
|
name = "sway";
|
||||||
|
user = lib.mkDefault "colin";
|
||||||
|
command = "${pkgs.sway}/bin/sway --debug";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf cfg.sway.gtkgreet.enable (
|
||||||
|
let
|
||||||
|
inherit (cfg.sway.gtkgreet) session;
|
||||||
|
sessionProvider = runWithLogger session.name session.command;
|
||||||
|
in {
|
||||||
|
# gtkgreet shows the --command argument in the UI
|
||||||
|
# - so we want it to look nice (not a /nix/store/... path)
|
||||||
|
# - to do that we put it in the user's PATH.
|
||||||
|
sane.gui.greetd.sway.greeterCmd = "${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command ${session.name}";
|
||||||
|
users.users.${session.user}.packages = [ sessionProvider ];
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
{
|
||||||
|
services.greetd = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# i could have gtkgreet launch the session directly: but stdout/stderr gets dropped
|
||||||
|
# settings.default_session.command = cfg.session.command;
|
||||||
|
|
||||||
|
# wrapper to launch with stdout/stderr redirected to system journal.
|
||||||
|
settings.default_session.command = let
|
||||||
|
launchWithLogger = runWithLogger cfg.session.name cfg.session.command;
|
||||||
|
in "${launchWithLogger}/bin/${cfg.session.name}";
|
||||||
|
};
|
||||||
|
|
||||||
|
# persisting fontconfig & mesa_shader_cache improves start time by ~5x
|
||||||
|
users.users.greeter.home = "/var/lib/greeter";
|
||||||
|
sane.persist.sys.plaintext = [
|
||||||
|
{ user = "greeter"; group = "greeter"; path = "/var/lib/greeter/.cache/fontconfig"; }
|
||||||
|
{ user = "greeter"; group = "greeter"; path = "/var/lib/greeter/.cache/mesa_shader_cache"; }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -180,8 +180,100 @@ let
|
|||||||
# other Tokyonight-* omitted
|
# other Tokyonight-* omitted
|
||||||
};
|
};
|
||||||
icon-theme = {
|
icon-theme = {
|
||||||
|
# find icon themes via `nix-locate share/icons/Adwaita`
|
||||||
|
# then determine the name here by building and `ls result/share/icons`
|
||||||
|
# this misses quite a few icon themes that aren't Adwaita-based.
|
||||||
|
# for those, try `nix-locate share/icons`?
|
||||||
|
#
|
||||||
|
# note that adwaita apps expect exactly the icon set provided by adwaita-icon-theme:
|
||||||
|
# - most icon themes are supplementary to adwaita, rather than a full replacement.
|
||||||
|
# - i.e. most themes, unless adwaita is also installed, will cause some missing icons inside apps.
|
||||||
|
# - that's probably why so many themes here also symlink Adwaita
|
||||||
|
# my accounting of "adwaita coverage" seems to be overoptimistic somehow
|
||||||
|
# maybe some apps bundle adwaita themselves
|
||||||
Adwaita = gnome.adwaita-icon-theme;
|
Adwaita = gnome.adwaita-icon-theme;
|
||||||
HighContrast = gnome.gnome-themes-extra; # gtk-3.0
|
Arc = arc-icon-theme; # 4.5/5, meh icon for "vertical ellipsis". 3/5 adwaita coverage
|
||||||
|
elementary-xfce = elementary-xfce-icon-theme; # does not cross compile (2023/10/03)
|
||||||
|
elementary-xfce-dark = elementary-xfce-icon-theme;
|
||||||
|
elementary-xfce-darker = elementary-xfce-icon-theme;
|
||||||
|
elementary-xfce-darkest = elementary-xfce-icon-theme;
|
||||||
|
HighContrast = gnome.gnome-themes-extra; # 5/5. 5/5 adwaita coverage (4/5 cross)
|
||||||
|
Humanity = humanity-icon-theme; # 5/5. 5/5 adwaita coverage (3.5/5 cross, unique in which icons work)
|
||||||
|
Humanity-Dark = humanity-icon-theme;
|
||||||
|
kora = kora-icon-theme;
|
||||||
|
kora-light = kora-icon-theme;
|
||||||
|
kora-light-panel = kora-icon-theme;
|
||||||
|
kora-pgrey = kora-icon-theme;
|
||||||
|
Numix = numix-icon-theme; # 4/5, meh icon for "back".
|
||||||
|
Numix-Light = numix-icon-theme;
|
||||||
|
Paper = paper-icon-theme; # 4/5, weird icon for "info". 5/5 adwaita coverage (3.5 cross, highly unique in which icons work)
|
||||||
|
Paper-Mono-Dark = paper-icon-theme;
|
||||||
|
Pop = pop-icon-theme; # 5/5. 2/5 adwaita coverage
|
||||||
|
Tela-circle = tela-circle-icon-theme;
|
||||||
|
Tela-circle-dark = tela-circle-icon-theme;
|
||||||
|
Tela-circle-light = tela-circle-icon-theme;
|
||||||
|
|
||||||
|
# themes which don't symlink Adwaita
|
||||||
|
BeautyLine = beauty-line-icon-theme; # 3.5/5. 4/5 adwaita coverage
|
||||||
|
breeze = breeze-icons;
|
||||||
|
breeze-dark = breeze-icons;
|
||||||
|
Mint-X = cinnamon.mint-x-icons;
|
||||||
|
# 10-ish other Mint-X variants omitted
|
||||||
|
# cinnamon.mint-l-icons;
|
||||||
|
# cinnamon.mint-y-icons;
|
||||||
|
Colloid = colloid-icon-theme; # 4.5/5, thin. 5/5 adwaita coverage (3/5 cross)
|
||||||
|
Colloid-dark = colloid-icon-theme;
|
||||||
|
Colloid-light = colloid-icon-theme;
|
||||||
|
bloom = deepin.deepin-icon-theme;
|
||||||
|
# 4 other deepin editions omitted
|
||||||
|
Dracula = dracula-icon-theme; # 4.5/5, a little thin. 4.5/5 adwaita coverage
|
||||||
|
Faba = faba-icon-theme; # 4/5. 4/5 adwaita coverage
|
||||||
|
Faba-Mono = faba-mono-icons;
|
||||||
|
Faba-Mono-Dark = faba-mono-icons;
|
||||||
|
Flat-Remix-Grey-Light = flat-remix-icon-theme; # 5/5. 5/5 adwaita coverage. builds on breeze, elementary
|
||||||
|
# 20-ish other flat-remix editions omitted
|
||||||
|
Fluent = fluent-icon-theme; # 5/5, though thin. 5/5 adwaita coverage (3/5 cross)
|
||||||
|
Fluent-dark = fluent-icon-theme;
|
||||||
|
gnome = gnome-icon-theme; # 3/5, icons are colored. 3/5 adwaita coverage
|
||||||
|
hicolor = hicolor-icon-theme; # 2/5 adwaita coverage; using this forces application builtin icons
|
||||||
|
la-capitaine-icon-theme = la-capitaine-icon-theme; # 4.5/5. 4.5/5 adwaita coverage. builds upon elementary
|
||||||
|
Luna = luna-icons;
|
||||||
|
# 5 other Luna variants omitted
|
||||||
|
maia = maia-icon-theme; # 3/5, icons are colored. 2/5 adwaita coverage
|
||||||
|
maia-dark = maia-icon-theme;
|
||||||
|
# mate.mate-icon-theme-faenza
|
||||||
|
mate = mate.mate-icon-theme; # 4.5/5. 4/5 adwaita coverage
|
||||||
|
menta = mate.mate-icon-theme;
|
||||||
|
Moka = moka-icon-theme; # 3/5, icons are colored. 3/5 adwaita coverage
|
||||||
|
# nixos-icons;
|
||||||
|
Nordzy = nordzy-icon-theme; # 5/5, thin. 5/5 adwaita coverage (3/5 cross)
|
||||||
|
# 10-ish Nordzy editions omitted
|
||||||
|
# numix-icon-theme-circle
|
||||||
|
# numix-icon-theme-square
|
||||||
|
oomox-gruvbox-dark = gruvbox-dark-icons-gtk;
|
||||||
|
Oranchelo = oranchelo-icon-theme;
|
||||||
|
# 3 other oranchelo editions omitted
|
||||||
|
elementary = pantheon.elementary-icon-theme; # 4.5/5. 4.5/5 adwaita coverage
|
||||||
|
Papirus = papirus-icon-theme; # 5/5. 5/5 adwaita coverage
|
||||||
|
# 4 other Papirus editions omitted
|
||||||
|
# papirus-maia-icon-theme
|
||||||
|
Qogir = qogir-icon-theme; # 5/5, thin. 5/5 adwaita coverage (2.5/5 cross)
|
||||||
|
# 5 other Qogir variants omitted
|
||||||
|
rose-pine = rose-pine-icon-theme;
|
||||||
|
rose-pine-dawn = rose-pine-icon-theme; # 5/5. 5/5 adwaita coverage (2.5 cross). looks a lot like Flat Remix...
|
||||||
|
rose-pine-moon = rose-pine-icon-theme;
|
||||||
|
SuperTinyIcons = super-tiny-icons; # 4/5. 2/5 adwaita coverage
|
||||||
|
Tango = tango-icon-theme; # 2/5. 3/5 adwaita coverage -- mostly just forwards to gnome-icon-theme
|
||||||
|
Tela = tela-icon-theme; # 5/5. 5/5 adwaita coverage
|
||||||
|
# 30-ish other Tela editions omitted
|
||||||
|
Vimix = vimix-icon-theme;
|
||||||
|
# 15-ish other Vimix editions omitted
|
||||||
|
WhiteSur = whitesur-icon-theme; # 4.5/5, thin & like iOS. 5/5 adwaita coverage (3.5/5 cross)
|
||||||
|
WhiteSur-dark = whitesur-icon-theme;
|
||||||
|
Rodent = xfce.xfce4-icon-theme;
|
||||||
|
Zafiro-icons-Dark = zafiro-icons;
|
||||||
|
Zafiro-icons-Light = zafiro-icons; # 5/5. 5/5 adwaita coverage
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ https://nixos.org/manual/nix/stable/language/builtins.html
|
|||||||
https://github.com/nixos/nixpkgs/pulls?q=
|
https://github.com/nixos/nixpkgs/pulls?q=
|
||||||
https://nur.nix-community.org/
|
https://nur.nix-community.org/
|
||||||
https://nix-community.github.io/home-manager/options.html
|
https://nix-community.github.io/home-manager/options.html
|
||||||
|
https://lists.sr.ht/~mil/sxmo-devel
|
||||||
https://w.uninsane.org/viewer#search?books.name=wikipedia_en_all_maxi_2022-05&pattern=
|
https://w.uninsane.org/viewer#search?books.name=wikipedia_en_all_maxi_2022-05&pattern=
|
||||||
https://jackett.uninsane.org/UI/Dashboard#search=
|
https://jackett.uninsane.org/UI/Dashboard#search=
|
||||||
|
https://lemmy.uninsane.org
|
||||||
https://fed.uninsane.org
|
https://fed.uninsane.org
|
||||||
|
https://jelly.uninsane.org
|
||||||
https://bt.uninsane.org
|
https://bt.uninsane.org
|
||||||
https://sci-hub.se
|
https://sci-hub.se
|
||||||
https://archive.is
|
https://archive.is
|
||||||
https://news.ycombinator.com
|
https://news.ycombinator.com
|
||||||
http://10.78.79.1 # Router/Firewall
|
http://10.78.79.1 # router/firewall
|
||||||
|
https://jochen-hoenicke.de/queue # johoe's mempool (bitcoin/ethereum)
|
||||||
|
|||||||
@@ -1,48 +1,55 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
# docs: https://nixos.wiki/wiki/Sway
|
# docs: https://nixos.wiki/wiki/Sway
|
||||||
with lib;
|
# sway-config docs: `man 5 sway`
|
||||||
let
|
let
|
||||||
cfg = config.sane.gui.sway;
|
cfg = config.sane.gui.sway;
|
||||||
|
defaultPackage = let
|
||||||
# bare sway launcher
|
# `defaultPackage` exists to create a `sway.desktop` file
|
||||||
sway-launcher = pkgs.writeShellScriptBin "sway-launcher" ''
|
# which will launch sway with our desired debugging facilities.
|
||||||
${pkgs.sway}/bin/sway --debug > /var/log/sway/sway.log 2>&1
|
# i.e. redirect output to syslog.
|
||||||
'';
|
scfg = config.programs.sway;
|
||||||
# start sway and have it construct the gtkgreeter
|
systemd-cat = "${pkgs.systemd}/bin/systemd-cat";
|
||||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
swayWithLogger = pkgs.writeShellScriptBin "sway-session" ''
|
||||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /var/log/sway/sway-as-greeter.log 2>&1
|
echo "launching sway-session (sway.desktop)..." | ${systemd-cat} --identifier=sway-session
|
||||||
'';
|
sway 2>&1 | ${systemd-cat} --identifier=sway-session
|
||||||
# (config file for the above)
|
'';
|
||||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
origSway = (pkgs.sway.override {
|
||||||
exec "${gtkgreet-launcher}"
|
# this override is what `programs.nixos` would do internally if we left `package` unset.
|
||||||
'';
|
extraSessionCommands = scfg.extraSessionCommands;
|
||||||
# gtkgreet which launches a layered sway instance
|
extraOptions = scfg.extraOptions;
|
||||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
withBaseWrapper = scfg.wrapperFeatures.base;
|
||||||
# NB: the "command" field here is run in the user's shell.
|
withGtkWrapper = scfg.wrapperFeatures.gtk;
|
||||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
isNixOS = true;
|
||||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sway-launcher
|
# TODO: `enableXWayland = ...`?
|
||||||
'';
|
});
|
||||||
greeter-session = {
|
desktop-file = pkgs.runCommand "sway-desktop-wrapper" {} ''
|
||||||
# greeter session config
|
mkdir -p $out/share/wayland-sessions
|
||||||
command = "${sway-as-greeter}/bin/sway-as-greeter";
|
substitute ${origSway}/share/wayland-sessions/sway.desktop $out/share/wayland-sessions/sway.desktop \
|
||||||
# alternatives:
|
--replace 'Exec=sway' 'Exec=${swayWithLogger}/bin/sway-session'
|
||||||
# - TTY: `command = "${pkgs.greetd.greetd}/bin/agreety --cmd ${pkgs.sway}/bin/sway";`
|
# XXX(2023/09/24) phog greeter (mobile greeter) will crash if DesktopNames is not set
|
||||||
# - autologin: `command = "${pkgs.sway}/bin/sway"; user = "colin";`
|
echo "DesktopNames=Sway" >> $out/share/wayland-sessions/sway.desktop
|
||||||
# - Dumb Login (doesn't work)": `command = "${pkgs.greetd.dlm}/bin/dlm";`
|
'';
|
||||||
};
|
in pkgs.symlinkJoin {
|
||||||
greeterless-session = {
|
inherit (origSway) name meta;
|
||||||
# no greeter
|
# the order of these `paths` is suchs that the desktop-file should claim share/wayland-sessions/sway.deskop,
|
||||||
command = "${sway-launcher}/bin/sway-launcher";
|
# overriding whatever the origSway provides
|
||||||
user = "colin";
|
paths = [ desktop-file origSway ];
|
||||||
|
passthru = {
|
||||||
|
inherit (origSway.passthru) providedSessions;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = with lib; {
|
||||||
sane.gui.sway.enable = mkOption {
|
sane.gui.sway.enable = mkOption {
|
||||||
default = false;
|
default = false;
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
|
sane.gui.sway.package = mkOption {
|
||||||
|
default = defaultPackage;
|
||||||
|
type = types.package;
|
||||||
|
};
|
||||||
sane.gui.sway.useGreeter = mkOption {
|
sane.gui.sway.useGreeter = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
launch sway via a greeter (like greetd's gtkgreet).
|
launch sway via a greeter (like greetd's gtkgreet).
|
||||||
@@ -51,19 +58,124 @@ in
|
|||||||
default = true;
|
default = true;
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
|
sane.gui.sway.config = {
|
||||||
|
extra_lines = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
description = ''
|
||||||
|
extra lines to append to the sway config
|
||||||
|
'';
|
||||||
|
default = ''
|
||||||
|
# XXX: sway needs exclusive control of XF86Audio{Raise,Lower}Volume, so assign this from a block that it can override.
|
||||||
|
# TODO: factor the bindings out into proper options and be less hacky?
|
||||||
|
bindsym --locked XF86AudioRaiseVolume exec $volume_up
|
||||||
|
bindsym --locked XF86AudioLowerVolume exec $volume_down
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
background = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
};
|
||||||
|
font = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "pango:monospace 11";
|
||||||
|
description = ''
|
||||||
|
default font (for e.g. window titles)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
mod = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "Mod4";
|
||||||
|
description = ''
|
||||||
|
Super key (for non-application shortcuts).
|
||||||
|
- "Mod1" for Alt
|
||||||
|
- "Mod4" for logo key
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
workspace_layout = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "default";
|
||||||
|
description = ''
|
||||||
|
how to arrange windows within new workspaces, by default:
|
||||||
|
- "default" (split)
|
||||||
|
- "tabbed"
|
||||||
|
- etc
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
xwayland = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
whether or not to enable xwayland (allows running X11 apps on sway).
|
||||||
|
some electron apps (e.g. element-desktop) require xwayland.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: split these into their own option scope
|
||||||
|
brightness_down_cmd = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "${pkgs.brightnessctl}/bin/brightnessctl set -2%";
|
||||||
|
description = "command to run when use wants to decrease screen brightness";
|
||||||
|
};
|
||||||
|
brightness_up_cmd = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
||||||
|
description = "command to run when use wants to increase screen brightness";
|
||||||
|
};
|
||||||
|
screenshot_cmd = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
||||||
|
description = "command to run when user wants to take a screenshot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
sane.gui.sway.waybar.extra_style = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = ''
|
||||||
|
/* default font-size is about 14px, which is good for moby, but not quite for larger displays */
|
||||||
|
window#waybar {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
extra CSS rules to append to ~/.config/waybar/style.css
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sane.gui.sway.waybar.top = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
# `attrsOf types.anything` (v.s. plain `attrs`) causes merging of the toplevel items.
|
||||||
|
# this allows for `waybar.top.x = lib.mkDefault a;` with `waybar.top.x = b;` to resolve to `b`.
|
||||||
|
# but note that `waybar.top.x.y = <multiple assignment>` won't be handled as desired.
|
||||||
|
freeformType = types.attrsOf types.anything;
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Waybar configuration for the bar at the top of the display.
|
||||||
|
see: <https://github.com/Alexays/Waybar/wiki/Configuration>
|
||||||
|
example:
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
height = 40;
|
||||||
|
modules-left = [ "sway/workspaces" "sway/mode" ];
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
config = mkMerge [
|
|
||||||
|
config = lib.mkMerge [
|
||||||
{
|
{
|
||||||
sane.programs.swayApps = {
|
sane.programs.swayApps = {
|
||||||
package = null;
|
package = null;
|
||||||
suggestedPrograms = [
|
suggestedPrograms = [
|
||||||
"guiApps"
|
"guiApps"
|
||||||
|
"conky" # for a nice background
|
||||||
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
|
"splatmoji" # used by us, but 'enabling' it gets us persistence & cfg
|
||||||
"swaylock"
|
"swaylock"
|
||||||
"swayidle"
|
"swayidle"
|
||||||
"wl-clipboard"
|
"wl-clipboard"
|
||||||
"blueberry" # GUI bluetooth manager
|
"blueberry" # GUI bluetooth manager
|
||||||
"mako" # notification daemon
|
"playerctl" # for waybar & particularly to have playerctld running
|
||||||
|
# "mako" # notification daemon
|
||||||
|
"swaynotificationcenter" # notification daemon
|
||||||
# # "pavucontrol"
|
# # "pavucontrol"
|
||||||
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
|
# "gnome.gnome-bluetooth" # XXX(2023/05/14): broken
|
||||||
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
|
# "gnome.gnome-control-center" # XXX(2023/06/28): depends on webkitgtk4_1
|
||||||
@@ -73,27 +185,39 @@ in
|
|||||||
|
|
||||||
secrets.".config/sane-sway/snippets.txt" = ../../../../secrets/common/snippets.txt.bin;
|
secrets.".config/sane-sway/snippets.txt" = ../../../../secrets/common/snippets.txt.bin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# default waybar
|
||||||
|
sane.gui.sway.waybar.top = import ./waybar-top.nix { inherit lib pkgs; };
|
||||||
}
|
}
|
||||||
|
|
||||||
(mkIf cfg.enable {
|
(lib.mkIf cfg.enable {
|
||||||
sane.programs.fontconfig.enableFor.system = true;
|
sane.programs.fontconfig.enableFor.system = true;
|
||||||
sane.programs.swayApps.enableFor.user.colin = true;
|
sane.programs.swayApps.enableFor.user.colin = true;
|
||||||
# we need the greeter's command to be on our PATH
|
|
||||||
users.users.colin.packages = [ sway-launcher ];
|
|
||||||
|
|
||||||
sane.gui.gtk.enable = lib.mkDefault true;
|
sane.gui.gtk.enable = lib.mkDefault true;
|
||||||
# sane.gui.gtk.gtk-theme = lib.mkDefault "Fluent-Light-compact";
|
# sane.gui.gtk.gtk-theme = lib.mkDefault "Fluent-Light-compact";
|
||||||
sane.gui.gtk.gtk-theme = lib.mkDefault "Tokyonight-Light-B";
|
sane.gui.gtk.gtk-theme = lib.mkDefault "Tokyonight-Light-B";
|
||||||
|
sane.gui.gtk.icon-theme = lib.mkDefault "HighContrast"; # 4/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "WhiteSur"; # 3.5/5 coverage on moby, but it provides a bunch for Fractal/Dino
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Humanity"; # 3.5/5 coverage on moby, but it provides the bookmark icon
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Paper"; # 3.5/5 coverage on moby, but it provides the bookmark icon
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Nordzy"; # 3/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Fluent"; # 3/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Colloid"; # 3/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Qogir"; # 2.5/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "rose-pine-dawn"; # 2.5/5 coverage on moby
|
||||||
|
# sane.gui.gtk.icon-theme = lib.mkDefault "Flat-Remix-Grey-Light"; # requires qtbase
|
||||||
|
|
||||||
# swap in these lines to use SDDM instead of `services.greetd`.
|
# swap in these lines to use SDDM instead of `services.greetd`.
|
||||||
# services.xserver.displayManager.sddm.enable = true;
|
# services.xserver.displayManager.sddm.enable = true;
|
||||||
# services.xserver.enable = true;
|
# services.xserver.enable = true;
|
||||||
services.greetd = {
|
sane.gui.greetd = lib.mkIf cfg.useGreeter {
|
||||||
# greetd source/docs:
|
|
||||||
# - <https://git.sr.ht/~kennylevinsen/greetd>
|
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
sway.enable = true; # have greetd launch a sway compositor in which we host a greeter
|
||||||
default_session = if cfg.useGreeter then greeter-session else greeterless-session;
|
sway.gtkgreet = {
|
||||||
|
enable = true;
|
||||||
|
session.name = "sway-on-gtkgreet";
|
||||||
|
session.command = "${cfg.package}/bin/sway";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,8 +227,27 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
alsa.support32Bit = true; # ??
|
alsa.support32Bit = true; # ??
|
||||||
|
# emulate pulseaudio for legacy apps (e.g. sxmo-utils)
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
};
|
};
|
||||||
|
# persist per-device volume levels
|
||||||
|
sane.user.persist.plaintext = [ ".local/state/wireplumber" ];
|
||||||
|
|
||||||
|
# persist per-device volume settings across power cycles.
|
||||||
|
# pipewire sits atop the kernel ALSA API, so alsa-utils knows about device volumes.
|
||||||
|
# but wireplumber also tries to do some of this
|
||||||
|
# systemd.services.alsa-store = {
|
||||||
|
# # based on <repo:nixos/nixpkgs:nixos/modules/services/audio/alsa.nix>
|
||||||
|
# description = "Store Sound Card State";
|
||||||
|
# wantedBy = [ "multi-user.target" ];
|
||||||
|
# serviceConfig = {
|
||||||
|
# Type = "oneshot";
|
||||||
|
# RemainAfterExit = true;
|
||||||
|
# ExecStart = "${pkgs.alsa-utils}/sbin/alsactl restore";
|
||||||
|
# ExecStop = "${pkgs.alsa-utils}/sbin/alsactl store --ignore";
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
# sane.persist.sys.plaintext = [ "/var/lib/alsa" ];
|
||||||
|
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
networking.networkmanager.enable = true;
|
networking.networkmanager.enable = true;
|
||||||
@@ -124,20 +267,18 @@ in
|
|||||||
# a system service can't depend on a user service, so just launch it at graphical-session
|
# a system service can't depend on a user service, so just launch it at graphical-session
|
||||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
||||||
|
|
||||||
sane.fs."/var/log/sway" = {
|
|
||||||
dir.acl.mode = "0777";
|
|
||||||
wantedBeforeBy = [ "greetd.service" "display-manager.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.sway = {
|
programs.sway = {
|
||||||
# provides xdg-desktop-portal-wlr, which exposes on dbus:
|
# provides xdg-desktop-portal-wlr, which exposes on dbus:
|
||||||
# - org.freedesktop.impl.portal.ScreenCast
|
# - org.freedesktop.impl.portal.ScreenCast
|
||||||
# - org.freedesktop.impl.portal.Screenshot
|
# - org.freedesktop.impl.portal.Screenshot
|
||||||
enable = true;
|
enable = true;
|
||||||
extraPackages = []; # nixos adds swaylock, swayidle, foot, dmenu by default
|
extraPackages = []; # nixos adds swaylock, swayidle, foot, dmenu by default
|
||||||
|
# extraOptions = [ "--debug" ];
|
||||||
# "wrapGAppsHook wrapper to execute sway with required environment variables for GTK applications."
|
# "wrapGAppsHook wrapper to execute sway with required environment variables for GTK applications."
|
||||||
wrapperFeatures.gtk = true;
|
wrapperFeatures.gtk = true;
|
||||||
|
package = cfg.package;
|
||||||
};
|
};
|
||||||
|
programs.xwayland.enable = cfg.config.xwayland;
|
||||||
# provide portals for:
|
# provide portals for:
|
||||||
# - org.freedesktop.impl.portal.Access
|
# - org.freedesktop.impl.portal.Access
|
||||||
# - org.freedesktop.impl.portal.Account
|
# - org.freedesktop.impl.portal.Account
|
||||||
@@ -162,17 +303,20 @@ in
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
sane.user.fs.".config/sway/config".symlink.text =
|
sane.user.fs = {
|
||||||
import ./sway-config.nix { inherit pkgs; };
|
".config/waybar/config".symlink.target =
|
||||||
|
(pkgs.formats.json {}).generate "waybar-config.json" [
|
||||||
|
({ layer = "top"; } // cfg.waybar.top)
|
||||||
|
];
|
||||||
|
|
||||||
sane.user.fs.".config/waybar/config".symlink.target =
|
".config/waybar/style.css".symlink.text =
|
||||||
let
|
(builtins.readFile ./waybar-style.css) + cfg.waybar.extra_style;
|
||||||
waybar-config = import ./waybar-config.nix { inherit pkgs; };
|
|
||||||
in
|
|
||||||
(pkgs.formats.json {}).generate "waybar-config.json" waybar-config;
|
|
||||||
|
|
||||||
sane.user.fs.".config/waybar/style.css".symlink.text =
|
".config/sway/config".symlink.target = import ./sway-config.nix {
|
||||||
builtins.readFile ./waybar-style.css;
|
inherit pkgs;
|
||||||
|
inherit (cfg) config;
|
||||||
|
};
|
||||||
|
};
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
182
hosts/modules/gui/sway/sway-config
Normal file
182
hosts/modules/gui/sway/sway-config
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# xwayland enable|disable|force
|
||||||
|
# - enable: lazily launch xwayland on first client connection
|
||||||
|
# - disable: never launch xwayland
|
||||||
|
# - force: launch xwayland immediately on boot
|
||||||
|
# XWayland exposes a X11 server that translates the protocol to a wayland backend, allowing legacy x11-only GUI apps.
|
||||||
|
# XWayland uses about 35MB RSS even when idle
|
||||||
|
xwayland @xwayland@
|
||||||
|
|
||||||
|
set $mod @mod@
|
||||||
|
set $term @terminal_cmd@
|
||||||
|
set $menu @launcher_cmd@
|
||||||
|
set $emoji_picker @emoji_cmd@
|
||||||
|
set $locker @lock_cmd@
|
||||||
|
set $snippets_picker @snip_cmd@
|
||||||
|
set $screenshot @screenshot_cmd@
|
||||||
|
set $brightness_up @brightness_up_cmd@
|
||||||
|
set $brightness_down @brightness_down_cmd@
|
||||||
|
set $volume_up @vol_up_cmd@
|
||||||
|
set $volume_down @vol_down_cmd@
|
||||||
|
set $mute @mute_cmd@
|
||||||
|
set $default_workspace_layout @workspace_layout@
|
||||||
|
|
||||||
|
### default font (for e.g. window titles)
|
||||||
|
font @font@
|
||||||
|
|
||||||
|
### tab colors (#border #background #text [#indicator #childBorder])
|
||||||
|
# focused & unfocused are the main interest
|
||||||
|
# urgent is used when an inactive window wants attention (e.g. terminal rings a bell)
|
||||||
|
# colors are synchronized with waybar and mpv
|
||||||
|
client.focused #1f5e54 #418379 #ffffff
|
||||||
|
client.focused_inactive #1f5e54 #5f676a #ffffff
|
||||||
|
client.unfocused #1f5e54 #1f554c #b4b4b4
|
||||||
|
client.urgent #ff8080 #ff8080 #ffffff
|
||||||
|
|
||||||
|
output '*' bg "@background@" fill
|
||||||
|
|
||||||
|
### pixel boundary between windows
|
||||||
|
# hide_edge_borders --i3 means that single-window workspaces never show window bar
|
||||||
|
hide_edge_borders --i3 smart
|
||||||
|
default_border pixel 1
|
||||||
|
titlebar_border_thickness 1
|
||||||
|
# XX YY distance from edge of window title to edge of text
|
||||||
|
# the YY distance here determines the heigh of the overall title
|
||||||
|
titlebar_padding 12 1
|
||||||
|
title_align center
|
||||||
|
|
||||||
|
### focus_wrapping: behavior when trying to focus past the edge of a container
|
||||||
|
#### no => preserve last focus. helpful mostly when `focus_follows_mouse yes`
|
||||||
|
focus_wrapping no
|
||||||
|
focus_follows_mouse yes
|
||||||
|
workspace_layout $default_workspace_layout
|
||||||
|
|
||||||
|
### key bindings
|
||||||
|
floating_modifier $mod
|
||||||
|
#### media keys
|
||||||
|
bindsym $mod+Page_Up exec $volume_up
|
||||||
|
bindsym $mod+Page_Down exec $volume_down
|
||||||
|
# --locked means to keep the binding active even when display is locked/off
|
||||||
|
bindsym --locked XF86AudioMute exec $mute
|
||||||
|
bindsym --locked XF86AudioPlay exec @playerctl@ play-pause
|
||||||
|
bindsym --locked XF86AudioStop exec @playerctl@ stop
|
||||||
|
bindsym --locked XF86AudioNext exec @playerctl@ next
|
||||||
|
bindsym --locked XF86AudioPrev exec @playerctl@ previous
|
||||||
|
bindsym --locked XF86MonBrightnessUp exec $brightness_up
|
||||||
|
bindsym --locked XF86MonBrightnessDown exec $brightness_down
|
||||||
|
|
||||||
|
#### special functions
|
||||||
|
bindsym $mod+Print exec $screenshot
|
||||||
|
bindsym $mod+l exec $locker
|
||||||
|
bindsym $mod+s exec $snippets_picker
|
||||||
|
bindsym $mod+slash exec $emoji_picker
|
||||||
|
bindsym $mod+d exec $menu
|
||||||
|
bindsym $mod+Return exec $term
|
||||||
|
bindsym $mod+Shift+q kill
|
||||||
|
bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
||||||
|
bindsym $mod+Shift+c reload
|
||||||
|
|
||||||
|
#### layout
|
||||||
|
bindsym $mod+b splith
|
||||||
|
bindsym $mod+v splitv
|
||||||
|
bindsym $mod+f fullscreen toggle
|
||||||
|
bindsym $mod+a focus parent
|
||||||
|
bindsym $mod+w layout tabbed
|
||||||
|
bindsym $mod+e layout toggle split
|
||||||
|
bindsym $mod+Shift+space floating toggle
|
||||||
|
bindsym $mod+space focus mode_toggle
|
||||||
|
bindsym $mod+r mode resize
|
||||||
|
|
||||||
|
#### movement
|
||||||
|
bindsym $mod+Up focus up
|
||||||
|
bindsym $mod+Down focus down
|
||||||
|
bindsym $mod+Left focus left
|
||||||
|
bindsym $mod+Right focus right
|
||||||
|
bindsym $mod+Shift+Up move up
|
||||||
|
bindsym $mod+Shift+Down move down
|
||||||
|
bindsym $mod+Shift+Left move left
|
||||||
|
bindsym $mod+Shift+Right move right
|
||||||
|
|
||||||
|
#### workspaces
|
||||||
|
bindsym $mod+1 workspace number 1
|
||||||
|
bindsym $mod+2 workspace number 2
|
||||||
|
bindsym $mod+3 workspace number 3
|
||||||
|
bindsym $mod+4 workspace number 4
|
||||||
|
bindsym $mod+5 workspace number 5
|
||||||
|
bindsym $mod+6 workspace number 6
|
||||||
|
bindsym $mod+7 workspace number 7
|
||||||
|
bindsym $mod+8 workspace number 8
|
||||||
|
bindsym $mod+9 workspace number 9
|
||||||
|
bindsym $mod+Shift+1 move container to workspace number 1
|
||||||
|
bindsym $mod+Shift+2 move container to workspace number 2
|
||||||
|
bindsym $mod+Shift+3 move container to workspace number 3
|
||||||
|
bindsym $mod+Shift+4 move container to workspace number 4
|
||||||
|
bindsym $mod+Shift+5 move container to workspace number 5
|
||||||
|
bindsym $mod+Shift+6 move container to workspace number 6
|
||||||
|
bindsym $mod+Shift+7 move container to workspace number 7
|
||||||
|
bindsym $mod+Shift+8 move container to workspace number 8
|
||||||
|
bindsym $mod+Shift+9 move container to workspace number 9
|
||||||
|
|
||||||
|
#### "scratchpad" = ??
|
||||||
|
bindsym $mod+Shift+minus move scratchpad
|
||||||
|
bindsym $mod+minus scratchpad show
|
||||||
|
|
||||||
|
mode "resize" {
|
||||||
|
bindsym Down resize grow height 30 px
|
||||||
|
bindsym Escape mode default
|
||||||
|
bindsym Left resize shrink width 30 px
|
||||||
|
bindsym Return mode default
|
||||||
|
bindsym Right resize grow width 30 px
|
||||||
|
bindsym Up resize shrink height 30 px
|
||||||
|
bindsym h resize shrink width 30 px
|
||||||
|
bindsym j resize grow height 30 px
|
||||||
|
bindsym k resize shrink height 30 px
|
||||||
|
bindsym l resize grow width 30 px
|
||||||
|
}
|
||||||
|
|
||||||
|
bar {
|
||||||
|
swaybar_command @waybar@
|
||||||
|
}
|
||||||
|
|
||||||
|
### application-specific settings
|
||||||
|
#### to obtain app-id's run `swaymsg -t get_tree`
|
||||||
|
for_window [app_id="pinentry-.*"] floating true
|
||||||
|
for_window [app_id="foot" title=".*sxmo/modem/.*/draft.txt.*"] resize set height 25
|
||||||
|
for_window [title="megapixels"] inhibit_idle open
|
||||||
|
# Dino (XMPP) is ordinarily started for the purpose of daemonizing (but still visible). keep it on a predictable, out-of-the-way workspace
|
||||||
|
# TODO: could be neat to somehow do this in a way that it never steals focus from anything...
|
||||||
|
for_window [app_id="im.dino.Dino"] move container to workspace number 1
|
||||||
|
for_window [app_id="org.gnome.Fractal"] move container to workspace number 1
|
||||||
|
|
||||||
|
### displays
|
||||||
|
## DESKTOP
|
||||||
|
output "Samsung Electric Company S22C300 0x00007F35" {
|
||||||
|
pos 0,0
|
||||||
|
res 1920x1080
|
||||||
|
}
|
||||||
|
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
|
||||||
|
pos 1920,0
|
||||||
|
res 3440x1440
|
||||||
|
}
|
||||||
|
|
||||||
|
## LAPTOP
|
||||||
|
# sh/en TV
|
||||||
|
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
|
||||||
|
pos 0,0
|
||||||
|
res 1920x1080
|
||||||
|
}
|
||||||
|
# internal display
|
||||||
|
output "Unknown 0x0637 0x00000000" {
|
||||||
|
pos 1920,0
|
||||||
|
res 1920x1080
|
||||||
|
}
|
||||||
|
|
||||||
|
# XXX: needed for xdg-desktop-portal-* to work.
|
||||||
|
# this is how we expose these env vars to user dbus services:
|
||||||
|
# - DISPLAY
|
||||||
|
# - WAYLAND_DISPLAY
|
||||||
|
# - SWAYSOCK
|
||||||
|
# - XDG_CURRENT_DESKTOP
|
||||||
|
# for more, see: <repo:nixos/nixpkgs:nixos/modules/programs/wayland/sway.nix>
|
||||||
|
include /etc/sway/config.d/*
|
||||||
|
|
||||||
|
@extra_lines@
|
||||||
@@ -1,182 +1,48 @@
|
|||||||
{ pkgs }:
|
{ config, pkgs }:
|
||||||
let
|
let
|
||||||
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
|
||||||
sed = "${pkgs.gnused}/bin/sed";
|
sed = "${pkgs.gnused}/bin/sed";
|
||||||
wtype = "${pkgs.wtype}/bin/wtype";
|
wtype = "${pkgs.wtype}/bin/wtype";
|
||||||
kitty = "${pkgs.kitty}/bin/kitty";
|
launcher_cmd = fuzzel;
|
||||||
launcher-cmd = fuzzel;
|
terminal_cmd = "${pkgs.xdg-terminal-exec}/bin/xdg-terminal-exec";
|
||||||
terminal-cmd = kitty;
|
lock_cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
||||||
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
|
# TODO: use pipewire controls?
|
||||||
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
vol_up_cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
|
||||||
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
vol_down_cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
|
||||||
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
mute_cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
|
||||||
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
|
|
||||||
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
|
|
||||||
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
|
|
||||||
# "bookmarking"/snippets inspired by Luke Smith:
|
# "bookmarking"/snippets inspired by Luke Smith:
|
||||||
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
|
||||||
snip-file = ../snippets.txt;
|
snip_cmd = pkgs.writeShellScript "type_snippet.sh" ''
|
||||||
list-snips = "cat ${snip-file} ~/.config/sane-sway/snippets.txt";
|
snippet=$(cat ${../snippets.txt} ~/.config/sane-sway/snippets.txt | \
|
||||||
strip-comments = "${sed} 's/ #.*$//'";
|
${fuzzel} -d -i -w 60 | \
|
||||||
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
|
${sed} 's/ #.*$//')
|
||||||
|
${wtype} "$snippet"
|
||||||
|
'';
|
||||||
# TODO: splatmoji release > 1.2.0 should allow `-s none` to disable skin tones
|
# TODO: splatmoji release > 1.2.0 should allow `-s none` to disable skin tones
|
||||||
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
emoji_cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
|
||||||
|
in pkgs.substituteAll {
|
||||||
# mod = "Mod1"; # Alt
|
src = ./sway-config;
|
||||||
mod = "Mod4"; # Super
|
inherit
|
||||||
in ''
|
emoji_cmd
|
||||||
### default font
|
launcher_cmd
|
||||||
font pango:monospace 8
|
lock_cmd
|
||||||
|
mute_cmd
|
||||||
### pixel boundary between windows
|
snip_cmd
|
||||||
default_border pixel 3
|
terminal_cmd
|
||||||
default_floating_border pixel 2
|
vol_down_cmd
|
||||||
hide_edge_borders smart
|
vol_up_cmd
|
||||||
|
;
|
||||||
### defaults
|
inherit (config)
|
||||||
focus_wrapping no
|
background
|
||||||
focus_follows_mouse yes
|
brightness_down_cmd
|
||||||
focus_on_window_activation smart
|
brightness_up_cmd
|
||||||
mouse_warping output
|
extra_lines
|
||||||
workspace_layout default
|
screenshot_cmd
|
||||||
workspace_auto_back_and_forth no
|
font
|
||||||
|
mod
|
||||||
### default colors (#border #background #text #indicator #childBorder)
|
workspace_layout
|
||||||
client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
|
;
|
||||||
client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
|
xwayland = if config.xwayland then "enable" else "disable";
|
||||||
client.unfocused #333333 #222222 #888888 #292d2e #222222
|
playerctl = "${pkgs.playerctl}/bin/playerctl";
|
||||||
client.urgent #2f343a #900000 #ffffff #900000 #900000
|
waybar = "${pkgs.waybar}/bin/waybar";
|
||||||
client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
|
}
|
||||||
client.background #ffffff
|
|
||||||
|
|
||||||
### key bindings
|
|
||||||
floating_modifier ${mod}
|
|
||||||
## media keys
|
|
||||||
bindsym XF86AudioRaiseVolume exec ${vol-up-cmd}
|
|
||||||
bindsym XF86AudioLowerVolume exec ${vol-down-cmd}
|
|
||||||
bindsym ${mod}+Page_Up exec ${vol-up-cmd}
|
|
||||||
bindsym ${mod}+Page_Down exec ${vol-down-cmd}
|
|
||||||
bindsym XF86AudioMute exec ${mute-cmd}
|
|
||||||
bindsym XF86MonBrightnessUp exec ${brightness-up-cmd}
|
|
||||||
bindsym XF86MonBrightnessDown exec ${brightness-down-cmd}
|
|
||||||
## special functions
|
|
||||||
bindsym ${mod}+Print exec ${screenshot-cmd}
|
|
||||||
bindsym ${mod}+l exec ${lock-cmd}
|
|
||||||
bindsym ${mod}+s exec ${snip-cmd}
|
|
||||||
bindsym ${mod}+slash exec ${emoji-cmd}
|
|
||||||
bindsym ${mod}+d exec ${launcher-cmd}
|
|
||||||
bindsym ${mod}+Return exec ${terminal-cmd}
|
|
||||||
bindsym ${mod}+Shift+q kill
|
|
||||||
bindsym ${mod}+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
|
||||||
bindsym ${mod}+Shift+c reload
|
|
||||||
## layout
|
|
||||||
bindsym ${mod}+b splith
|
|
||||||
bindsym ${mod}+v splitv
|
|
||||||
bindsym ${mod}+f fullscreen toggle
|
|
||||||
bindsym ${mod}+a focus parent
|
|
||||||
bindsym ${mod}+w layout tabbed
|
|
||||||
bindsym ${mod}+e layout toggle split
|
|
||||||
bindsym ${mod}+Shift+space floating toggle
|
|
||||||
bindsym ${mod}+space focus mode_toggle
|
|
||||||
bindsym ${mod}+r mode resize
|
|
||||||
## movement
|
|
||||||
bindsym ${mod}+Up focus up
|
|
||||||
bindsym ${mod}+Down focus down
|
|
||||||
bindsym ${mod}+Left focus left
|
|
||||||
bindsym ${mod}+Right focus right
|
|
||||||
bindsym ${mod}+Shift+Up move up
|
|
||||||
bindsym ${mod}+Shift+Down move down
|
|
||||||
bindsym ${mod}+Shift+Left move left
|
|
||||||
bindsym ${mod}+Shift+Right move right
|
|
||||||
## workspaces
|
|
||||||
bindsym ${mod}+1 workspace number 1
|
|
||||||
bindsym ${mod}+2 workspace number 2
|
|
||||||
bindsym ${mod}+3 workspace number 3
|
|
||||||
bindsym ${mod}+4 workspace number 4
|
|
||||||
bindsym ${mod}+5 workspace number 5
|
|
||||||
bindsym ${mod}+6 workspace number 6
|
|
||||||
bindsym ${mod}+7 workspace number 7
|
|
||||||
bindsym ${mod}+8 workspace number 8
|
|
||||||
bindsym ${mod}+9 workspace number 9
|
|
||||||
bindsym ${mod}+Shift+1 move container to workspace number 1
|
|
||||||
bindsym ${mod}+Shift+2 move container to workspace number 2
|
|
||||||
bindsym ${mod}+Shift+3 move container to workspace number 3
|
|
||||||
bindsym ${mod}+Shift+4 move container to workspace number 4
|
|
||||||
bindsym ${mod}+Shift+5 move container to workspace number 5
|
|
||||||
bindsym ${mod}+Shift+6 move container to workspace number 6
|
|
||||||
bindsym ${mod}+Shift+7 move container to workspace number 7
|
|
||||||
bindsym ${mod}+Shift+8 move container to workspace number 8
|
|
||||||
bindsym ${mod}+Shift+9 move container to workspace number 9
|
|
||||||
## "scratchpad" = ??
|
|
||||||
bindsym ${mod}+Shift+minus move scratchpad
|
|
||||||
bindsym ${mod}+minus scratchpad show
|
|
||||||
|
|
||||||
### defaults
|
|
||||||
mode "resize" {
|
|
||||||
bindsym Down resize grow height 10 px
|
|
||||||
bindsym Escape mode default
|
|
||||||
bindsym Left resize shrink width 10 px
|
|
||||||
bindsym Return mode default
|
|
||||||
bindsym Right resize grow width 10 px
|
|
||||||
bindsym Up resize shrink height 10 px
|
|
||||||
bindsym h resize shrink width 10 px
|
|
||||||
bindsym j resize grow height 10 px
|
|
||||||
bindsym k resize shrink height 10 px
|
|
||||||
bindsym l resize grow width 10 px
|
|
||||||
}
|
|
||||||
|
|
||||||
### lightly modified bars
|
|
||||||
bar {
|
|
||||||
mode dock
|
|
||||||
hidden_state hide
|
|
||||||
position top
|
|
||||||
status_command ${pkgs.i3status}/bin/i3status
|
|
||||||
swaybar_command ${pkgs.waybar}/bin/waybar
|
|
||||||
workspace_buttons yes
|
|
||||||
strip_workspace_numbers no
|
|
||||||
tray_output primary
|
|
||||||
colors {
|
|
||||||
background #000000
|
|
||||||
statusline #ffffff
|
|
||||||
separator #666666
|
|
||||||
# #border #background #text
|
|
||||||
focused_workspace #4c7899 #285577 #ffffff
|
|
||||||
active_workspace #333333 #5f676a #ffffff
|
|
||||||
inactive_workspace #333333 #222222 #888888
|
|
||||||
urgent_workspace #2f343a #900000 #ffffff
|
|
||||||
binding_mode #2f343a #900000 #ffffff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
### displays
|
|
||||||
## DESKTOP
|
|
||||||
output "Samsung Electric Company S22C300 0x00007F35" {
|
|
||||||
pos 0,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
output "Goldstar Company Ltd LG ULTRAWIDE 0x00004E94" {
|
|
||||||
pos 1920,0
|
|
||||||
res 3440x1440
|
|
||||||
}
|
|
||||||
|
|
||||||
## LAPTOP
|
|
||||||
# sh/en TV
|
|
||||||
output "Pioneer Electronic Corporation VSX-524 0x00000101" {
|
|
||||||
pos 0,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
# internal display
|
|
||||||
output "Unknown 0x0637 0x00000000" {
|
|
||||||
pos 1920,0
|
|
||||||
res 1920x1080
|
|
||||||
}
|
|
||||||
|
|
||||||
# XXX: needed for xdg-desktop-portal-* to work.
|
|
||||||
# this is how we expose these env vars to user dbus services:
|
|
||||||
# - DISPLAY
|
|
||||||
# - WAYLAND_DISPLAY
|
|
||||||
# - SWAYSOCK
|
|
||||||
# - XDG_CURRENT_DESKTOP
|
|
||||||
# for more, see: <repo:nixos/nixpkgs:nixos/modules/programs/wayland/sway.nix>
|
|
||||||
include /etc/sway/config.d/*
|
|
||||||
''
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
|
||||||
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
|
||||||
{ pkgs }:
|
|
||||||
[
|
|
||||||
{ # TOP BAR
|
|
||||||
layer = "top";
|
|
||||||
height = 40;
|
|
||||||
modules-left = ["sway/workspaces" "sway/mode"];
|
|
||||||
modules-center = ["sway/window"];
|
|
||||||
modules-right = ["custom/mediaplayer" "clock" "battery" "cpu" "network"];
|
|
||||||
"sway/window" = {
|
|
||||||
max-length = 50;
|
|
||||||
};
|
|
||||||
# include song artist/title. source: https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/
|
|
||||||
"custom/mediaplayer" = {
|
|
||||||
exec = pkgs.writeShellScript "waybar-mediaplayer" ''
|
|
||||||
player_status=$(${pkgs.playerctl}/bin/playerctl status 2> /dev/null)
|
|
||||||
if [ "$player_status" = "Playing" ]; then
|
|
||||||
echo "$(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
|
||||||
elif [ "$player_status" = "Paused" ]; then
|
|
||||||
echo " $(${pkgs.playerctl}/bin/playerctl metadata artist) - $(${pkgs.playerctl}/bin/playerctl metadata title)"
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
interval = 2;
|
|
||||||
format = "{} ";
|
|
||||||
# return-type = "json";
|
|
||||||
on-click = "${pkgs.playerctl}/bin/playerctl play-pause";
|
|
||||||
on-scroll-up = "${pkgs.playerctl}/bin/playerctl next";
|
|
||||||
on-scroll-down = "${pkgs.playerctl}/bin/playerctl previous";
|
|
||||||
};
|
|
||||||
network = {
|
|
||||||
# docs: https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd
|
|
||||||
interval = 2;
|
|
||||||
max-length = 40;
|
|
||||||
# custom :> format specifier explained here: https://github.com/Alexays/Waybar/pull/472
|
|
||||||
format-ethernet = " {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
|
|
||||||
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
|
||||||
|
|
||||||
format-disconnected = "";
|
|
||||||
};
|
|
||||||
cpu = {
|
|
||||||
format = " {usage:2}%";
|
|
||||||
tooltip = false;
|
|
||||||
};
|
|
||||||
battery = {
|
|
||||||
states = {
|
|
||||||
good = 95;
|
|
||||||
warning = 30;
|
|
||||||
critical = 10;
|
|
||||||
};
|
|
||||||
format = "{icon} {capacity}%";
|
|
||||||
format-icons = [
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
""
|
|
||||||
];
|
|
||||||
};
|
|
||||||
clock = {
|
|
||||||
format-alt = "{:%a, %d. %b %H:%M}";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]
|
|
||||||
17
hosts/modules/gui/sway/waybar-media
Executable file
17
hosts/modules/gui/sway/waybar-media
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p jq -p playerctl
|
||||||
|
status=$(playerctl status 2> /dev/null | tr 'A-Z' 'a-z')
|
||||||
|
if [ -z "$status" ]; then
|
||||||
|
status="inactive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
artist=$(playerctl metadata artist 2> /dev/null)
|
||||||
|
title=$(playerctl metadata title 2> /dev/null)
|
||||||
|
|
||||||
|
text=
|
||||||
|
if [ -n "$title" ]; then
|
||||||
|
text="$artist - $title"
|
||||||
|
fi
|
||||||
|
# waybar requires output to be on a single line.
|
||||||
|
# `alt` key determines the icon
|
||||||
|
jq --null-input --compact-output --arg status "$status" --arg text "$text" '{ "text": $text, "alt": $status }'
|
||||||
@@ -1,117 +1,5 @@
|
|||||||
/* style docs: https://github.com/Alexays/Waybar/wiki/Styling */
|
/* style docs: https://github.com/Alexays/Waybar/wiki/Styling */
|
||||||
|
/* defaults: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
||||||
* {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* defaults below: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
|
||||||
window#waybar {
|
|
||||||
background-color: rgba(43, 48, 59, 0.5);
|
|
||||||
border-bottom: 3px solid rgba(100, 114, 125, 0.5);
|
|
||||||
color: #ffffff;
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: .5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.hidden {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
window#waybar.empty {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
window#waybar.solo {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
window#waybar.termite {
|
|
||||||
background-color: #3F3F3F;
|
|
||||||
}
|
|
||||||
|
|
||||||
window#waybar.chromium {
|
|
||||||
background-color: #000000;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button {
|
|
||||||
padding: 0 5px;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #ffffff;
|
|
||||||
/* Use box-shadow instead of border so the text isn't offset */
|
|
||||||
box-shadow: inset 0 -3px transparent;
|
|
||||||
/* Avoid rounded borders under each workspace name */
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
|
||||||
#workspaces button:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.focused {
|
|
||||||
background-color: #64727D;
|
|
||||||
box-shadow: inset 0 -3px #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.urgent {
|
|
||||||
background-color: #eb4d4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mode {
|
|
||||||
background-color: #64727D;
|
|
||||||
border-bottom: 3px solid #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clock,
|
|
||||||
#battery,
|
|
||||||
#cpu,
|
|
||||||
#memory,
|
|
||||||
#disk,
|
|
||||||
#temperature,
|
|
||||||
#backlight,
|
|
||||||
#network,
|
|
||||||
#pulseaudio,
|
|
||||||
#custom-media,
|
|
||||||
#tray,
|
|
||||||
#mode,
|
|
||||||
#idle_inhibitor,
|
|
||||||
#mpd {
|
|
||||||
padding: 0 10px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#window,
|
|
||||||
#workspaces {
|
|
||||||
margin: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If workspaces is the leftmost module, omit left margin */
|
|
||||||
.modules-left > widget:first-child > #workspaces {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If workspaces is the rightmost module, omit right margin */
|
|
||||||
.modules-right > widget:last-child > #workspaces {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clock {
|
|
||||||
background-color: #64727D;
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#battery.charging, #battery.plugged {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #26A65B;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
to {
|
to {
|
||||||
@@ -120,8 +8,75 @@ window#waybar.chromium {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window#waybar {
|
||||||
|
background-color: #418379;
|
||||||
|
border-bottom: 0px solid #1f5e54;
|
||||||
|
color: #ffffff;
|
||||||
|
transition-property: background-color;
|
||||||
|
transition-duration: .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modules-right {
|
||||||
|
/* workspace buttons (LHS) get padding between it and the screen edge */
|
||||||
|
/* replicate that same padding for whatever's on the RHS (i.e. the clock) */
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
#workspaces button {
|
||||||
|
padding: 0 5px;
|
||||||
|
background-color: #418379;
|
||||||
|
color: #ffffff;
|
||||||
|
/* Use box-shadow instead of border so the text isn't offset */
|
||||||
|
box-shadow: inset 0 0px #1f5e54;
|
||||||
|
/* Avoid rounded borders under each workspace name */
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
/* keep min-width set here even if it has no apparent effect */
|
||||||
|
/* many gtk themes override it to something large, which doesn't seem appropriate for this context */
|
||||||
|
/* the default adwaita theme uses min-width: 16px */
|
||||||
|
min-width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button:hover {
|
||||||
|
/* i don't want hover effects, so reset this styling to be the same as default button */
|
||||||
|
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
||||||
|
background: inherit;
|
||||||
|
box-shadow: inherit;
|
||||||
|
text-shadow: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.focused {
|
||||||
|
background-color: #63a89c;
|
||||||
|
box-shadow: inset 0 0px #2c8274;
|
||||||
|
}
|
||||||
|
|
||||||
|
#workspaces button.urgent {
|
||||||
|
background-color: #e64291;
|
||||||
|
}
|
||||||
|
|
||||||
|
#backlight,
|
||||||
|
#battery,
|
||||||
|
#clock,
|
||||||
|
#cpu,
|
||||||
|
#custom-media,
|
||||||
|
#custom-swaync,
|
||||||
|
#disk,
|
||||||
|
#idle_inhibitor,
|
||||||
|
#memory,
|
||||||
|
#mode,
|
||||||
|
#network,
|
||||||
|
#pulseaudio,
|
||||||
|
#temperature,
|
||||||
|
#tray,
|
||||||
|
#mpd {
|
||||||
|
/* without padding the text/icons are unreadable */
|
||||||
|
padding: 0 10px;
|
||||||
|
/* need fixed-width for cpu/net/mem meaurements which change frequently */
|
||||||
|
font-family: monospace;
|
||||||
|
border-left: 1px solid #1f5e54;
|
||||||
|
}
|
||||||
|
|
||||||
#battery.critical:not(.charging) {
|
#battery.critical:not(.charging) {
|
||||||
background-color: #f53c3c;
|
background-color: #e64291;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
animation-name: blink;
|
animation-name: blink;
|
||||||
animation-duration: 0.5s;
|
animation-duration: 0.5s;
|
||||||
@@ -130,69 +85,8 @@ window#waybar.chromium {
|
|||||||
animation-direction: alternate;
|
animation-direction: alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
label:focus {
|
|
||||||
background-color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cpu {
|
|
||||||
background-color: #2ecc71;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#memory {
|
|
||||||
background-color: #9b59b6;
|
|
||||||
}
|
|
||||||
|
|
||||||
#disk {
|
|
||||||
background-color: #964B00;
|
|
||||||
}
|
|
||||||
|
|
||||||
#backlight {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#network {
|
|
||||||
background-color: #2980b9;
|
|
||||||
}
|
|
||||||
|
|
||||||
#network.disconnected {
|
|
||||||
background-color: #f53c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pulseaudio {
|
|
||||||
background-color: #f1c40f;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pulseaudio.muted {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
color: #2a5c45;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media {
|
|
||||||
background-color: #66cc99;
|
|
||||||
color: #2a5c45;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media.custom-spotify {
|
|
||||||
background-color: #66cc99;
|
|
||||||
}
|
|
||||||
|
|
||||||
#custom-media.custom-vlc {
|
|
||||||
background-color: #ffa000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature {
|
|
||||||
background-color: #f0932b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature.critical {
|
|
||||||
background-color: #eb4d4b;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray {
|
#tray {
|
||||||
background-color: #2980b9;
|
background-color: #418379;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tray > .passive {
|
#tray > .passive {
|
||||||
@@ -201,56 +95,5 @@ label:focus {
|
|||||||
|
|
||||||
#tray > .needs-attention {
|
#tray > .needs-attention {
|
||||||
-gtk-icon-effect: highlight;
|
-gtk-icon-effect: highlight;
|
||||||
background-color: #eb4d4b;
|
background-color: #e64291;
|
||||||
}
|
}
|
||||||
|
|
||||||
#idle_inhibitor {
|
|
||||||
background-color: #2d3436;
|
|
||||||
}
|
|
||||||
|
|
||||||
#idle_inhibitor.activated {
|
|
||||||
background-color: #ecf0f1;
|
|
||||||
color: #2d3436;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd {
|
|
||||||
background-color: #66cc99;
|
|
||||||
color: #2a5c45;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.disconnected {
|
|
||||||
background-color: #f53c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.stopped {
|
|
||||||
background-color: #90b1b1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mpd.paused {
|
|
||||||
background-color: #51a37a;
|
|
||||||
}
|
|
||||||
|
|
||||||
#language {
|
|
||||||
background: #00b093;
|
|
||||||
color: #740864;
|
|
||||||
padding: 0 5px;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state {
|
|
||||||
background: #97e1ad;
|
|
||||||
color: #000000;
|
|
||||||
padding: 0 0px;
|
|
||||||
margin: 0 5px;
|
|
||||||
min-width: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label {
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#keyboard-state > label.locked {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
114
hosts/modules/gui/sway/waybar-top.nix
Normal file
114
hosts/modules/gui/sway/waybar-top.nix
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# docs: <https://github.com/Alexays/Waybar/wiki/Configuration>
|
||||||
|
# - custom modules: <https://github.com/Alexays/Waybar/wiki/Module:-Custom>
|
||||||
|
# - format specifiers: <https://fmt.dev/latest/syntax.html#syntax>
|
||||||
|
{ lib, pkgs }:
|
||||||
|
let
|
||||||
|
waybar-media = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "waybar-media";
|
||||||
|
src = ./.;
|
||||||
|
pkgs = [ "jq" "playerctl" ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
height = lib.mkDefault 40;
|
||||||
|
modules-left = lib.mkDefault [ "sway/workspaces" ];
|
||||||
|
modules-center = lib.mkDefault [ "sway/window" ];
|
||||||
|
modules-right = lib.mkDefault [
|
||||||
|
"custom/media"
|
||||||
|
"custom/swaync"
|
||||||
|
"clock"
|
||||||
|
"battery"
|
||||||
|
"memory"
|
||||||
|
"cpu"
|
||||||
|
"network"
|
||||||
|
];
|
||||||
|
|
||||||
|
"sway/window" = {
|
||||||
|
max-length = 50;
|
||||||
|
};
|
||||||
|
|
||||||
|
"custom/media" = {
|
||||||
|
# this module shows the actively playing song
|
||||||
|
# - source: <https://www.reddit.com/r/swaywm/comments/ni0vso/waybar_spotify_tracktitle/>
|
||||||
|
# - alternative: <https://github.com/Alexays/Waybar/wiki/Module:-MPRIS>
|
||||||
|
# - alternative: <https://github.com/Alexays/Waybar/wiki/Module:-Custom#mpris-controller>
|
||||||
|
#
|
||||||
|
# N.B.: for this to behave well with multiple MPRIS clients,
|
||||||
|
# `playerctld` must be enabled. see: <https://github.com/altdesktop/playerctl/issues/161>
|
||||||
|
exec = "${waybar-media}/bin/waybar-media";
|
||||||
|
return-type = "json";
|
||||||
|
interval = 2;
|
||||||
|
format = "{icon}{}";
|
||||||
|
max-length = 50;
|
||||||
|
format-icons = {
|
||||||
|
playing = " ";
|
||||||
|
paused = " ";
|
||||||
|
inactive = "";
|
||||||
|
};
|
||||||
|
tooltip = false;
|
||||||
|
on-click = "playerctl play-pause";
|
||||||
|
on-scroll-up = "playerctl next";
|
||||||
|
on-scroll-down = "playerctl previous";
|
||||||
|
};
|
||||||
|
"custom/swaync" = {
|
||||||
|
# source: <https://github.com/ErikReider/SwayNotificationCenter#waybar-example>
|
||||||
|
exec-if = "which swaync-client";
|
||||||
|
exec = "swaync-client -swb";
|
||||||
|
return-type = "json";
|
||||||
|
escape = true;
|
||||||
|
format = "{icon}"; # or "{icon} {}" to include notif count
|
||||||
|
format-icons = {
|
||||||
|
notification = "<span foreground='red'><sup></sup></span>";
|
||||||
|
none = "";
|
||||||
|
dnd-notification = "<span foreground='red'><sup></sup></span>";
|
||||||
|
dnd-none = "";
|
||||||
|
inhibited-notification = "<span foreground='red'><sup></sup></span>";
|
||||||
|
inhibited-none = "";
|
||||||
|
dnd-inhibited-notification = "<span foreground='red'><sup></sup></span>";
|
||||||
|
dnd-inhibited-none = "";
|
||||||
|
};
|
||||||
|
tooltip = false;
|
||||||
|
on-click = "swaync-client -t -sw";
|
||||||
|
on-click-right = "swaync-client -d -sw";
|
||||||
|
};
|
||||||
|
network = {
|
||||||
|
# docs: <https://github.com/Alexays/Waybar/blob/master/man/waybar-network.5.scd>
|
||||||
|
interval = 2;
|
||||||
|
max-length = 40;
|
||||||
|
# custom :> format specifier explained here:
|
||||||
|
# - <https://github.com/Alexays/Waybar/pull/472>
|
||||||
|
format-ethernet = " {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||||
|
tooltip-format-ethernet = "{ifname} {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||||
|
|
||||||
|
format-wifi = "{ifname} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||||
|
tooltip-format-wifi = "{essid} ({signalStrength}%) {bandwidthUpBits:>}▲ {bandwidthDownBits:>}▼";
|
||||||
|
|
||||||
|
format-disconnected = "";
|
||||||
|
};
|
||||||
|
cpu = {
|
||||||
|
format = " {usage:2}%";
|
||||||
|
tooltip = false;
|
||||||
|
};
|
||||||
|
memory = {
|
||||||
|
format = "☵ {percentage:2}%";
|
||||||
|
tooltip = false;
|
||||||
|
};
|
||||||
|
battery = {
|
||||||
|
states = {
|
||||||
|
good = 95;
|
||||||
|
warning = 30;
|
||||||
|
critical = 10;
|
||||||
|
};
|
||||||
|
format = "{icon} {capacity}%";
|
||||||
|
format-icons = [
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
""
|
||||||
|
];
|
||||||
|
};
|
||||||
|
clock = {
|
||||||
|
format-alt = "{:%a, %d. %b %H:%M}";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
#
|
#
|
||||||
# sxmo technical overview:
|
# sxmo technical overview:
|
||||||
# - inputs
|
# - inputs
|
||||||
# - dwm: handles vol/power buttons; hardcoded in config.h
|
# - bonsaid: handles vol/power buttons
|
||||||
|
# - it receives those buttons from dwm (if x11) harcoded in config.h or sway (if wayland)
|
||||||
# - lisgd: handles gestures
|
# - lisgd: handles gestures
|
||||||
# - startup
|
# - startup
|
||||||
# - daemon based (lisgsd, idle_locker, statusbar_periodics)
|
# - daemon based (lisgsd, idle_locker, statusbar_periodics)
|
||||||
@@ -37,18 +38,18 @@
|
|||||||
# - live in ~/.local/state/sxmo.log
|
# - live in ~/.local/state/sxmo.log
|
||||||
# - ~/.local/state/superd.log
|
# - ~/.local/state/superd.log
|
||||||
# - ~/.local/state/superd/logs/<daemon>.log
|
# - ~/.local/state/superd/logs/<daemon>.log
|
||||||
# - `journalctl --user --boot` (lightm redirects the sxmo session stdout => systemd)
|
# - `journalctl --user --boot` (lightdm redirects the sxmo session stdout => systemd)
|
||||||
#
|
#
|
||||||
# - default components:
|
# - default components:
|
||||||
# - DE: sway (if wayland), dwm (if X)
|
# - DE: sway (if wayland), dwm (if X)
|
||||||
# - menus: bemenu (if wayland), dmenu (if X)
|
# - menus: bemenu (if wayland), dmenu (if X)
|
||||||
# - gestures: lisgd
|
# - gestures: lisgd
|
||||||
# - on-screen keyboard: wvkbd (if wayland), svkbd (if X)
|
# - on-screen keyboard: wvkbd (if wayland), svkbd (if X)
|
||||||
#
|
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.sane.gui.sxmo;
|
cfg = config.sane.gui.sxmo;
|
||||||
|
package = cfg.package;
|
||||||
knownKeyboards = {
|
knownKeyboards = {
|
||||||
# map keyboard package name -> name of binary to invoke
|
# map keyboard package name -> name of binary to invoke
|
||||||
wvkbd = "wvkbd-mobintl";
|
wvkbd = "wvkbd-mobintl";
|
||||||
@@ -57,6 +58,39 @@ let
|
|||||||
knownTerminals = {
|
knownTerminals = {
|
||||||
vte = "vte-2.91";
|
vte = "vte-2.91";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd-cat = "${pkgs.systemd}/bin/systemd-cat";
|
||||||
|
runWithLogger = identifier: cmd: pkgs.writeShellScript identifier ''
|
||||||
|
echo "launching ${identifier}..." | ${systemd-cat} --identifier=${identifier}
|
||||||
|
${cmd} 2>&1 | ${systemd-cat} --identifier=${identifier}
|
||||||
|
'';
|
||||||
|
|
||||||
|
hookPkgs = {
|
||||||
|
inputhandler = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sxmo_hook_inputhandler.sh";
|
||||||
|
src = ./hooks;
|
||||||
|
pkgs = [ "coreutils" ];
|
||||||
|
};
|
||||||
|
postwake = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sxmo_hook_postwake.sh";
|
||||||
|
src = ./hooks;
|
||||||
|
};
|
||||||
|
rotate = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sxmo_hook_rotate.sh";
|
||||||
|
src = ./hooks;
|
||||||
|
pkgs = [ "sway" ];
|
||||||
|
};
|
||||||
|
start = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sxmo_hook_start.sh";
|
||||||
|
src = ./hooks;
|
||||||
|
pkgs = [ "systemd" "xdg-user-dirs" ];
|
||||||
|
};
|
||||||
|
suspend = pkgs.static-nix-shell.mkBash {
|
||||||
|
pname = "sxmo_suspend.sh";
|
||||||
|
src = ./hooks;
|
||||||
|
pkgs = [ "coreutils" "util-linux" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = with lib; {
|
options = with lib; {
|
||||||
@@ -65,23 +99,92 @@ in
|
|||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
sane.gui.sxmo.greeter = mkOption {
|
sane.gui.sxmo.greeter = mkOption {
|
||||||
type = types.enum [ "lightdm-mobile" "sway" ];
|
type = types.enum [
|
||||||
default = "lightdm-mobile";
|
"greetd-phog"
|
||||||
|
"greetd-sway-gtkgreet"
|
||||||
|
"greetd-sway-phog"
|
||||||
|
"greetd-sxmo"
|
||||||
|
"lightdm-mobile"
|
||||||
|
];
|
||||||
|
# default = "lightdm-mobile";
|
||||||
|
default = "greetd-sway-phog";
|
||||||
description = ''
|
description = ''
|
||||||
which greeter to use.
|
which greeter to use.
|
||||||
"lightdm-mobile" => keypad style greeter. can only enter digits 0-9 as password.
|
"greetd-phog" => phosh-based greeter. keypad (0-9) with option to open an on-screen keyboard.
|
||||||
"sway" => layered sway greeter. behaves as if you booted to swaylock.
|
"greetd-sway-phog" => phog, but uses sway as the compositor instead of phoc.
|
||||||
|
requires a patched phog, since sway doesn't provide the Wayland global "zphoc_layer_shell_effects_v1".
|
||||||
|
"greetd-sxmo" => launch sxmo directly from greetd, no auth.
|
||||||
|
this means no keychain unlocked or encrypted home mounted.
|
||||||
|
"lightdm-mobile" => keypad style greeter. can only enter digits 0-9 as password.
|
||||||
|
"greetd-sway-gtkgreet" => layered sway greeter. keyboard-only user/pass input; impractical on mobile.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
sane.gui.sxmo.package = mkOption {
|
sane.gui.sxmo.package = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = pkgs.sxmo-utils;
|
default = pkgs.sxmo-utils-latest.override { preferSystemd = true; };
|
||||||
description = ''
|
description = ''
|
||||||
sxmo base scripts and hooks collection.
|
sxmo base scripts and hooks collection.
|
||||||
consider overriding the outputs under /share/sxmo/default_hooks
|
consider overriding the outputs under /share/sxmo/default_hooks
|
||||||
to insert your own user scripts.
|
to insert your own user scripts.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
sane.gui.sxmo.hooks = mkOption {
|
||||||
|
type = types.attrsOf types.path;
|
||||||
|
default = {
|
||||||
|
# default upstream hooks
|
||||||
|
# additional hooks are in subdirectories like three_button_touchscreen/
|
||||||
|
# - sxmo_hook_inputhandler.sh
|
||||||
|
# - sxmo_hook_lock.sh
|
||||||
|
# - sxmo_hook_postwake.sh
|
||||||
|
# - sxmo_hook_screenoff.sh
|
||||||
|
# - sxmo_hook_unlock.sh
|
||||||
|
# by including hooks here, updating the sxmo package also updates the hooks
|
||||||
|
# without requiring any reboot
|
||||||
|
"sxmo_hook_apps.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_apps.sh";
|
||||||
|
"sxmo_hook_block_suspend.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_block_suspend.sh";
|
||||||
|
"sxmo_hook_call_audio.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_call_audio.sh";
|
||||||
|
"sxmo_hook_contextmenu_fallback.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_contextmenu_fallback.sh";
|
||||||
|
"sxmo_hook_contextmenu.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_contextmenu.sh";
|
||||||
|
"sxmo_hook_desktop_widget.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_desktop_widget.sh";
|
||||||
|
"sxmo_hook_discard.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_discard.sh";
|
||||||
|
"sxmo_hook_hangup.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_hangup.sh";
|
||||||
|
"sxmo_hook_icons.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_icons.sh";
|
||||||
|
"sxmo_hook_lisgdstart.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_lisgdstart.sh";
|
||||||
|
"sxmo_hook_logout.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_logout.sh";
|
||||||
|
"sxmo_hook_missed_call.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_missed_call.sh";
|
||||||
|
"sxmo_hook_mnc.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_mnc.sh";
|
||||||
|
"sxmo_hook_modem.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_modem.sh";
|
||||||
|
"sxmo_hook_mute_ring.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_mute_ring.sh";
|
||||||
|
"sxmo_hook_network_down.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_network_down.sh";
|
||||||
|
"sxmo_hook_network_pre_down.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_network_pre_down.sh";
|
||||||
|
"sxmo_hook_network_pre_up.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_network_pre_up.sh";
|
||||||
|
"sxmo_hook_network_up.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_network_up.sh";
|
||||||
|
"sxmo_hook_notification.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_notification.sh";
|
||||||
|
"sxmo_hook_notifications.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_notifications.sh";
|
||||||
|
"sxmo_hook_pickup.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_pickup.sh";
|
||||||
|
"sxmo_hook_restart_modem_daemons.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_restart_modem_daemons.sh";
|
||||||
|
"sxmo_hook_ring.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_ring.sh";
|
||||||
|
"sxmo_hook_rotate.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_rotate.sh";
|
||||||
|
"sxmo_hook_scripts.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_scripts.sh";
|
||||||
|
"sxmo_hook_sendsms.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_sendsms.sh";
|
||||||
|
"sxmo_hook_smslog.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_smslog.sh";
|
||||||
|
"sxmo_hook_sms.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_sms.sh";
|
||||||
|
"sxmo_hook_start.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_start.sh";
|
||||||
|
"sxmo_hook_statusbar.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_statusbar.sh";
|
||||||
|
"sxmo_hook_stop.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_stop.sh";
|
||||||
|
"sxmo_hook_tailtextlog.sh" = "${package}/share/sxmo/default_hooks/sxmo_hook_tailtextlog.sh";
|
||||||
|
} // {
|
||||||
|
# default hooks for this nix module, not upstreamable
|
||||||
|
"sxmo_hook_inputhandler.sh" = "${hookPkgs.inputhandler}/bin/sxmo_hook_inputhandler.sh";
|
||||||
|
"sxmo_hook_postwake.sh" = "${hookPkgs.postwake}/bin/sxmo_hook_postwake.sh";
|
||||||
|
"sxmo_hook_rotate.sh" = "${hookPkgs.rotate}/bin/sxmo_hook_rotate.sh";
|
||||||
|
"sxmo_hook_start.sh" = "${hookPkgs.start}/bin/sxmo_hook_start.sh";
|
||||||
|
"sxmo_suspend.sh" = "${hookPkgs.suspend}/bin/sxmo_suspend.sh";
|
||||||
|
};
|
||||||
|
description = ''
|
||||||
|
extra hooks to add with higher priority than the builtins
|
||||||
|
'';
|
||||||
|
};
|
||||||
sane.gui.sxmo.terminal = mkOption {
|
sane.gui.sxmo.terminal = mkOption {
|
||||||
# type = types.nullOr (types.enum [ "foot" "st" "vte" ]);
|
# type = types.nullOr (types.enum [ "foot" "st" "vte" ]);
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
@@ -116,7 +219,9 @@ in
|
|||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
SXMO_BAR_SHOW_BAT_PER = mkSettingsOpt "1" "show battery percentage in statusbar";
|
SXMO_BAR_SHOW_BAT_PER = mkSettingsOpt "1" "show battery percentage in statusbar";
|
||||||
|
SXMO_DISABLE_CONFIGVERSION_CHECK = mkSettingsOpt "1" "allow omitting the configversion line from user-provided sxmo dotfiles";
|
||||||
SXMO_UNLOCK_IDLE_TIME = mkSettingsOpt "300" "how many seconds of inactivity before locking the screen"; # lock -> screenoff happens 8s later, not configurable
|
SXMO_UNLOCK_IDLE_TIME = mkSettingsOpt "300" "how many seconds of inactivity before locking the screen"; # lock -> screenoff happens 8s later, not configurable
|
||||||
|
# SXMO_WM = mkSettingsOpt "sway" "sway or dwm. ordinarily initialized by sxmo_{x,w}init.sh";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
default = {};
|
default = {};
|
||||||
@@ -139,8 +244,8 @@ in
|
|||||||
package = null;
|
package = null;
|
||||||
suggestedPrograms = [
|
suggestedPrograms = [
|
||||||
"guiApps"
|
"guiApps"
|
||||||
"sfeed" # want this here so that the user's ~/.sfeed/sfeedrc gets created
|
"sfeed" # want this here so that the user's ~/.sfeed/sfeedrc gets created
|
||||||
"superd" # make superctl (used by sxmo) be on PATH
|
"superd" # make superctl (used by sxmo) be on PATH
|
||||||
];
|
];
|
||||||
|
|
||||||
persist.cryptClearOnBoot = [
|
persist.cryptClearOnBoot = [
|
||||||
@@ -152,7 +257,8 @@ in
|
|||||||
|
|
||||||
{
|
{
|
||||||
# TODO: lift to option declaration
|
# TODO: lift to option declaration
|
||||||
sane.gui.sxmo.settings.TERMCMD = lib.mkIf (cfg.terminal != null)
|
# N.B.: TERMCMD was renamed SXMO_TERMINAL on 2023/08/29
|
||||||
|
sane.gui.sxmo.settings.SXMO_TERMINAL = lib.mkIf (cfg.terminal != null)
|
||||||
(lib.mkDefault (knownTerminals."${cfg.terminal}" or cfg.terminal));
|
(lib.mkDefault (knownTerminals."${cfg.terminal}" or cfg.terminal));
|
||||||
sane.gui.sxmo.settings.KEYBOARD = lib.mkIf (cfg.keyboard != null)
|
sane.gui.sxmo.settings.KEYBOARD = lib.mkIf (cfg.keyboard != null)
|
||||||
(lib.mkDefault (knownKeyboards."${cfg.keyboard}" or cfg.keyboard));
|
(lib.mkDefault (knownKeyboards."${cfg.keyboard}" or cfg.keyboard));
|
||||||
@@ -160,59 +266,145 @@ in
|
|||||||
|
|
||||||
(lib.mkIf cfg.enable (lib.mkMerge [
|
(lib.mkIf cfg.enable (lib.mkMerge [
|
||||||
{
|
{
|
||||||
|
sane.gui.sway = {
|
||||||
|
enable = true;
|
||||||
|
# we manage the greeter ourselves (TODO: merge this into sway config as well)
|
||||||
|
useGreeter = false;
|
||||||
|
waybar.top = import ./waybar-top.nix;
|
||||||
|
# reset extra waybar style
|
||||||
|
waybar.extra_style = "";
|
||||||
|
config = {
|
||||||
|
# N.B. missing from upstream sxmo config here is:
|
||||||
|
# - `bindsym $mod+g exec sxmo_hook_locker.sh`
|
||||||
|
# - `bindsym $mod+t exec sxmo_appmenu.sh power`
|
||||||
|
# - `bindsym $mod+i exec sxmo_wmmenu.sh windowswitcher`
|
||||||
|
# - `bindsym $mod+p exec sxmo_appmenu.sh`
|
||||||
|
# - `bindsym $mod+Shift+p exec sxmo_appmenu.sh sys`
|
||||||
|
# - `input * xkb_options compose:ralt`
|
||||||
|
# these could be added, but i don't see much benefit.
|
||||||
|
font = "pango:monospace 10";
|
||||||
|
mod = "Mod1"; # prefer Alt
|
||||||
|
xwayland = false; # disable to reduce RAM usage.
|
||||||
|
workspace_layout = "tabbed";
|
||||||
|
|
||||||
|
brightness_down_cmd = "sxmo_brightness.sh down";
|
||||||
|
brightness_up_cmd = "sxmo_brightness.sh up";
|
||||||
|
screenshot_cmd = "sxmo_screenshot.sh";
|
||||||
|
extra_lines =
|
||||||
|
let
|
||||||
|
sxmo_init = pkgs.writeShellScript "sxmo_init.sh" ''
|
||||||
|
# perform the same behavior as sxmo_{x,w}init.sh -- but without actually launching wayland/X11
|
||||||
|
# this amounts to:
|
||||||
|
# - setting env vars (e.g. getting the hooks onto PATH)
|
||||||
|
# - placing default configs in ~ for sxmo-launched services (sxmo_migrate.sh)
|
||||||
|
# - binding vol/power buttons (sxmo_swayinitconf.sh)
|
||||||
|
# - launching sxmo_hook_start.sh
|
||||||
|
#
|
||||||
|
# the commands here are similar to upstream sxmo_winit.sh, but not identical and the ordering may be different
|
||||||
|
|
||||||
|
# profile may contain SXMO_DEVICE_NAME which is used by _sxmo_load_environment so load it early
|
||||||
|
source "$XDG_CONFIG_HOME/sxmo/profile"
|
||||||
|
# sourcing upstream sxmo_init.sh triggers _sxmo_load_environment
|
||||||
|
# which ensures SXMO_* environment variables are set
|
||||||
|
source ${package}/etc/profile.d/sxmo_init.sh
|
||||||
|
# _sxmo_prepare_dirs ensures ~/.cache/sxmo & other XDG dirs exist with correct perms & owner
|
||||||
|
_sxmo_prepare_dirs
|
||||||
|
# migrate tells sxmo to provide the following default files:
|
||||||
|
# - ~/.config/sxmo/profile
|
||||||
|
# - ~/.config/fontconfig/conf.d/50-sxmo.conf
|
||||||
|
# - ~/.config/sxmo/sway
|
||||||
|
# - ~/.config/foot/foot.ini
|
||||||
|
# - ~/.config/mako/config
|
||||||
|
# - ~/.config/sxmo/bonsai_tree.json
|
||||||
|
# - ~/.config/wob/wob.ini
|
||||||
|
# - ~/.config/sxmo/conky.conf
|
||||||
|
sxmo_migrate.sh sync
|
||||||
|
# various things may have happened above that require me to re-load the profile here:
|
||||||
|
# - _sxmo_load_environment sources a deviceprofile.sh file, which may override my profile settings.
|
||||||
|
# very obvious if you set a non-default SXMO_SWAY_SCALE.
|
||||||
|
# - sxmo_migrate.sh may have provided a default profile, if i failed to
|
||||||
|
source "$XDG_CONFIG_HOME/sxmo/profile"
|
||||||
|
# place my non-specialized hooks at higher precedence than the default device-hooks
|
||||||
|
# alternative would be to move my hooks to ~/.config/sxmo/hooks/<device-name>.
|
||||||
|
export PATH="$XDG_CONFIG_HOME/sxmo/hooks:$PATH"
|
||||||
|
|
||||||
|
# kill anything leftover from the previous sxmo run. this way we can (try to) be reentrant
|
||||||
|
echo "sxmo_init: killing stale daemons (if active)"
|
||||||
|
sxmo_daemons.sh stop all
|
||||||
|
pkill bemenu
|
||||||
|
pkill wvkbd
|
||||||
|
pkill superd
|
||||||
|
|
||||||
|
# configure vol/power-button input mapping (upstream SXMO has this in sway config)
|
||||||
|
echo "sxmo_init: configuring sway bindings/displays with:"
|
||||||
|
echo "SXMO_POWER_BUTTON: $SXMO_POWER_BUTTON"
|
||||||
|
echo "SXMO_VOLUME_BUTTON: $SXMO_VOLUME_BUTTON"
|
||||||
|
echo "SXMO_SWAY_SCALE: $SXMO_SWAY_SCALE"
|
||||||
|
sxmo_swayinitconf.sh
|
||||||
|
|
||||||
|
echo "sxmo_init: invoking sxmo_hook_start.sh with:"
|
||||||
|
echo "PATH: $PATH"
|
||||||
|
sxmo_hook_start.sh
|
||||||
|
'';
|
||||||
|
in ''
|
||||||
|
# TODO: some of this is probably unnecessary
|
||||||
|
mode "menu" {
|
||||||
|
# just a placeholder for "menu" mode
|
||||||
|
bindsym --input-device=1:1:1c21800.lradc XF86AudioMute exec nothing
|
||||||
|
}
|
||||||
|
bindsym button2 kill
|
||||||
|
bindswitch lid:on exec sxmo_wm.sh dpms on
|
||||||
|
bindswitch lid:off exec sxmo_wm.sh dpms off
|
||||||
|
exec 'printf %s "$SWAYSOCK" > "$XDG_RUNTIME_DIR"/sxmo.swaysock'
|
||||||
|
exec_always ${sxmo_init}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
sane.programs.sxmoApps.enableFor.user.colin = true;
|
sane.programs.sxmoApps.enableFor.user.colin = true;
|
||||||
sane.gui.gtk.enable = lib.mkDefault true;
|
|
||||||
|
|
||||||
# sxmo internally uses doas instead of sudo
|
# sxmo internally uses doas instead of sudo
|
||||||
security.doas.enable = true;
|
security.doas.enable = true;
|
||||||
security.doas.wheelNeedsPassword = false;
|
security.doas.wheelNeedsPassword = false;
|
||||||
|
|
||||||
# TODO: move this further to the host-specific config?
|
|
||||||
networking.useDHCP = false;
|
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
networking.wireless.enable = lib.mkForce false;
|
|
||||||
|
|
||||||
hardware.bluetooth.enable = true;
|
|
||||||
services.blueman.enable = true;
|
|
||||||
|
|
||||||
# TODO: nerdfonts is 4GB. it accepts an option to ship only some fonts: probably want to use that.
|
|
||||||
fonts.packages = [ pkgs.nerdfonts ];
|
|
||||||
|
|
||||||
# lightdm-mobile-greeter: "The name org.a11y.Bus was not provided by any .service files"
|
# lightdm-mobile-greeter: "The name org.a11y.Bus was not provided by any .service files"
|
||||||
services.gnome.at-spi2-core.enable = true;
|
services.gnome.at-spi2-core.enable = true;
|
||||||
|
|
||||||
# sxmo has first-class support only for pulseaudio and alsa -- not pipewire.
|
|
||||||
# however, pipewire can emulate pulseaudio support via `services.pipewire.pulse.enable = true`
|
|
||||||
# after which the stock pulseaudio binaries magically work
|
|
||||||
# administer with pw-cli, pw-mon, pw-top commands
|
|
||||||
services.pipewire = {
|
|
||||||
enable = true;
|
|
||||||
alsa.enable = true;
|
|
||||||
alsa.support32Bit = true; # ??
|
|
||||||
pulse.enable = true;
|
|
||||||
};
|
|
||||||
systemd.user.services."pipewire".wantedBy = [ "graphical-session.target" ];
|
|
||||||
|
|
||||||
# TODO: could use `displayManager.sessionPackages`?
|
# TODO: could use `displayManager.sessionPackages`?
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
cfg.package
|
package
|
||||||
|
pkgs.bonsai # sway (not sxmo) needs to exec `bonsaictl` by name (sxmo_swayinitconf.sh)
|
||||||
] ++ lib.optionals (cfg.terminal != null) [ pkgs."${cfg.terminal}" ]
|
] ++ lib.optionals (cfg.terminal != null) [ pkgs."${cfg.terminal}" ]
|
||||||
++ lib.optionals (cfg.keyboard != null) [ pkgs."${cfg.keyboard}" ];
|
++ lib.optionals (cfg.keyboard != null) [ pkgs."${cfg.keyboard}" ];
|
||||||
|
|
||||||
environment.sessionVariables = {
|
environment.sessionVariables = {
|
||||||
XDG_DATA_DIRS = [
|
XDG_DATA_DIRS = [
|
||||||
# TODO: only need the share/sxmo directly linked
|
# TODO: only need the share/sxmo directly linked
|
||||||
"${cfg.package}/share"
|
"${package}/share"
|
||||||
];
|
];
|
||||||
};
|
} // (lib.filterAttrs (k: v:
|
||||||
|
k == "SXMO_DISABLE_CONFIGVERSION_CHECK" # read before `profile` is sourced
|
||||||
|
|| k == "SXMO_TERMINAL" # for apps launched via `swaymsg exec -- sxmo_terminal.sh ...`
|
||||||
|
)
|
||||||
|
cfg.settings
|
||||||
|
);
|
||||||
|
|
||||||
|
# sxmo puts in /share/sxmo:
|
||||||
|
# - profile.d/sxmo_init.sh
|
||||||
|
# - appcfg/
|
||||||
|
# - default_hooks/
|
||||||
|
# - and more
|
||||||
|
# environment.pathsToLink = [ "/share/sxmo" ];
|
||||||
|
|
||||||
systemd.services."sxmo-set-permissions" = {
|
systemd.services."sxmo-set-permissions" = {
|
||||||
|
# TODO: some of these could be modified to be udev rules
|
||||||
description = "configure specific /sys and /dev nodes to be writable by sxmo scripts";
|
description = "configure specific /sys and /dev nodes to be writable by sxmo scripts";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = "${cfg.package}/bin/sxmo_setpermissions.sh";
|
ExecStart = "${package}/bin/sxmo_setpermissions.sh";
|
||||||
};
|
};
|
||||||
wantedBy = [ "display-manager.service" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# if superd fails to start a service within 100ms, it'll try to start again
|
# if superd fails to start a service within 100ms, it'll try to start again
|
||||||
@@ -240,45 +432,92 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
sane.user.fs.".cache/sxmo/sxmo.noidle" = lib.mkIf cfg.noidle {
|
sane.user.fs = lib.mkMerge [
|
||||||
symlink.text = "";
|
{
|
||||||
};
|
# link the superd services into a place where systemd can find them.
|
||||||
sane.user.fs.".cache/sxmo/sxmo.nogesture" = lib.mkIf cfg.nogesture {
|
# the unit files should be compatible, except maybe for PATH handling
|
||||||
symlink.text = "";
|
# ".config/systemd/user/autocutsel-primary.service".symlink.target = "${package}/share/superd/services/autocutsel-primary.service";
|
||||||
};
|
# ".config/systemd/user/autocutsel.service".symlink.target = "${package}/share/superd/services/autocutsel.service";
|
||||||
sane.user.fs.".config/sxmo/profile".symlink.text = let
|
# ".config/systemd/user/bonsaid.service".symlink.target = "${package}/share/superd/services/bonsaid.service";
|
||||||
mkKeyValue = key: value: ''export ${key}="${value}"'';
|
# # ".config/systemd/user/dunst.service".symlink.target = "${package}/share/superd/services/dunst.service";
|
||||||
userConfig = lib.generators.toKeyValue { inherit mkKeyValue; } cfg.settings;
|
# # ".config/systemd/user/mako.service".symlink.target = "${package}/share/superd/services/mako.service";
|
||||||
in ''
|
# ".config/systemd/user/mmsd-tng.service".symlink.target = "${package}/share/superd/services/mmsd-tng.service";
|
||||||
# configversion: 4284f96d91e9550ff8f3b25823e402ad
|
# ".config/systemd/user/sxmo_autosuspend.service".symlink.target = "${package}/share/superd/services/sxmo_autosuspend.service";
|
||||||
# ^ upstream adds new options every now and then, expects user config file
|
# ".config/systemd/user/sxmo_battery_monitor.service".symlink.target = "${package}/share/superd/services/sxmo_battery_monitor.service";
|
||||||
# to include the md5sum of the template it's based on.
|
# ".config/systemd/user/sxmo_conky.service".symlink.target = "${package}/share/superd/services/sxmo_conky.service";
|
||||||
# see `setup_config_version.sh`
|
# ".config/systemd/user/sxmo_desktop_widget.service".symlink.target = "${package}/share/superd/services/sxmo_desktop_widget.service";
|
||||||
${userConfig}
|
# ".config/systemd/user/sxmo_hook_lisgd.service".symlink.target = "${package}/share/superd/services/sxmo_hook_lisgd.service";
|
||||||
'';
|
# ".config/systemd/user/sxmo_menumode_toggler.service".symlink.target = "${package}/share/superd/services/sxmo_menumode_toggler.service";
|
||||||
|
# ".config/systemd/user/sxmo_modemmonitor.service".symlink.target = "${package}/share/superd/services/sxmo_modemmonitor.service";
|
||||||
|
# ".config/systemd/user/sxmo_networkmonitor.service".symlink.target = "${package}/share/superd/services/sxmo_networkmonitor.service";
|
||||||
|
# ".config/systemd/user/sxmo_notificationmonitor.service".symlink.target = "${package}/share/superd/services/sxmo_notificationmonitor.service";
|
||||||
|
# ".config/systemd/user/sxmo_soundmonitor.service".symlink.target = "${package}/share/superd/services/sxmo_soundmonitor.service";
|
||||||
|
# ".config/systemd/user/sxmo_wob.service".symlink.target = "${package}/share/superd/services/sxmo_wob.service";
|
||||||
|
# ".config/systemd/user/sxmo-x11-status.service".symlink.target = "${package}/share/superd/services/sxmo-x11-status.service";
|
||||||
|
# ".config/systemd/user/unclutter.service".symlink.target = "${package}/share/superd/services/unclutter.service";
|
||||||
|
# ".config/systemd/user/unclutter-xfixes.service".symlink.target = "${package}/share/superd/services/unclutter-xfixes.service";
|
||||||
|
# ".config/systemd/user/vvmd.service".symlink.target = "${package}/share/superd/services/vvmd.service";
|
||||||
|
|
||||||
sane.user.fs.".config/sxmo/sway".symlink.target = pkgs.substituteAll {
|
# service code further below tells systemd to put ~/.config/sxmo/hooks on PATH, but it puts hooks/bin on PATH instead, so symlink that
|
||||||
src = ./sway-config;
|
".config/sxmo/hooks/bin".symlink.target = ".";
|
||||||
waybar = "${pkgs.waybar}/bin/waybar";
|
|
||||||
};
|
|
||||||
|
|
||||||
sane.user.fs.".config/waybar/config".symlink.target =
|
".cache/sxmo/sxmo.noidle" = lib.mkIf cfg.noidle {
|
||||||
let
|
symlink.text = "";
|
||||||
waybar-config = import ./waybar-config.nix { inherit pkgs; };
|
};
|
||||||
in
|
".cache/sxmo/sxmo.nogesture" = lib.mkIf cfg.nogesture {
|
||||||
(pkgs.formats.json {}).generate "waybar-config.json" waybar-config;
|
symlink.text = "";
|
||||||
|
};
|
||||||
|
".config/sxmo/profile".symlink.text = let
|
||||||
|
mkKeyValue = key: value: ''export ${key}="${value}"'';
|
||||||
|
in
|
||||||
|
lib.generators.toKeyValue { inherit mkKeyValue; } cfg.settings;
|
||||||
|
}
|
||||||
|
(lib.mapAttrs' (name: value: {
|
||||||
|
# sxmo's `_sxmo_load_environments` adds to PATH:
|
||||||
|
# - ~/.config/sxmo/hooks/$SXMO_DEVICE_NAME
|
||||||
|
# - ~/.config/sxmo/hooks
|
||||||
|
name = ".config/sxmo/hooks/${name}";
|
||||||
|
value.symlink.target = value;
|
||||||
|
}) cfg.hooks)
|
||||||
|
];
|
||||||
|
|
||||||
sane.user.fs.".config/waybar/style.css".symlink.text =
|
sane.user.services = let
|
||||||
builtins.readFile ./waybar-style.css;
|
sxmoPath = [ "/home/colin/.config/sxmo/hooks" package ] ++ package.runtimeDeps ++ [
|
||||||
|
"/run/current-system/sw" # for flock wrapper
|
||||||
sane.user.fs.".config/sxmo/conky.conf".symlink.target = let
|
"/run/wrappers" # for doas
|
||||||
battery_estimate = pkgs.static-nix-shell.mkBash {
|
];
|
||||||
pname = "battery_estimate";
|
sxmoService = name: {
|
||||||
src = ./.;
|
description = "sxmo ${name}";
|
||||||
|
path = sxmoPath;
|
||||||
|
serviceConfig.ExecStart = "${pkgs.coreutils}/bin/env sxmo_${name}.sh";
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
serviceConfig.Restart = "always";
|
||||||
|
serviceConfig.RestartSec = "20s";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
sxmo_autosuspend = sxmoService "autosuspend";
|
||||||
|
sxmo_battery_monitor = sxmoService "battery_monitor";
|
||||||
|
sxmo_desktop_widget = sxmoService "hook_desktop_widget";
|
||||||
|
sxmo_hook_lisgd = sxmoService "hook_lisgdstart";
|
||||||
|
sxmo_menumode_toggler = sxmoService "menumode_toggler";
|
||||||
|
sxmo_modemmonitor = sxmoService "modemmonitor";
|
||||||
|
sxmo_networkmonitor = sxmoService "networkmonitor";
|
||||||
|
sxmo_notificationmonitor = sxmoService "notificationmonitor";
|
||||||
|
sxmo_soundmonitor = sxmoService "soundmonitor";
|
||||||
|
sxmo_wob = sxmoService "wob";
|
||||||
|
sxmo-x11-status = sxmoService "status_xsetroot";
|
||||||
|
|
||||||
|
bonsaid = {
|
||||||
|
description = "programmable input dispatcher";
|
||||||
|
path = sxmoPath;
|
||||||
|
# systemd expands %E to $XDG_CONFIG_HOME
|
||||||
|
serviceConfig.ExecStart = "${pkgs.bonsai}/bin/bonsaid -t %E/sxmo/bonsai_tree.json";
|
||||||
|
serviceConfig.ExecStartPre = "${pkgs.coreutils}/bin/rm -f %t/bonsai"; # systemd expands %t to $XDG_RUNTIME_DIR
|
||||||
|
serviceConfig.Type = "simple";
|
||||||
|
serviceConfig.Restart = "always";
|
||||||
|
serviceConfig.RestartSec = "5s";
|
||||||
|
environment.SXMO_STATE = "/run/user/1000/sxmo.state"; #< TODO: this is $XDG_RUNTIME_DIR/sxmo.state
|
||||||
};
|
};
|
||||||
in pkgs.substituteAll {
|
|
||||||
src = ./conky-config;
|
|
||||||
bat = "${battery_estimate}/bin/battery_estimate";
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +538,7 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
displayManager.sessionPackages = with pkgs; [
|
displayManager.sessionPackages = with pkgs; [
|
||||||
cfg.package # this gets share/wayland-sessions/swmo.desktop linked
|
package # this gets share/wayland-sessions/swmo.desktop linked
|
||||||
];
|
];
|
||||||
|
|
||||||
# taken from gui/phosh:
|
# taken from gui/phosh:
|
||||||
@@ -313,32 +552,43 @@ in
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
(lib.mkIf (cfg.greeter == "sway") {
|
(lib.mkIf (cfg.greeter == "greetd-sway-gtkgreet") {
|
||||||
services.greetd = {
|
sane.gui.greetd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
# borrowed from gui/sway
|
sway.enable = true;
|
||||||
settings.default_session.command =
|
sway.gtkgreet.enable = true;
|
||||||
let
|
sway.gtkgreet.session.name = "sxmo-on-gtkgreet";
|
||||||
# start sway and have it construct the gtkgreeter
|
sway.gtkgreet.session.command = "${pkgs.sway}/bin/sway --debug";
|
||||||
sway-as-greeter = pkgs.writeShellScriptBin "sway-as-greeter" ''
|
|
||||||
${pkgs.sway}/bin/sway --debug --config ${sway-config-into-gtkgreet} > /var/log/sway/sway-as-greeter.log 2>&1
|
|
||||||
'';
|
|
||||||
# (config file for the above)
|
|
||||||
sway-config-into-gtkgreet = pkgs.writeText "greetd-sway-config" ''
|
|
||||||
exec "${gtkgreet-launcher}"
|
|
||||||
'';
|
|
||||||
# gtkgreet which launches a layered sway instance
|
|
||||||
gtkgreet-launcher = pkgs.writeShellScript "gtkgreet-launcher" ''
|
|
||||||
# NB: the "command" field here is run in the user's shell.
|
|
||||||
# so that command must exist on the specific user's path who is logging in. it doesn't need to exist system-wide.
|
|
||||||
${pkgs.greetd.gtkgreet}/bin/gtkgreet --layer-shell --command sxmo_winit.sh
|
|
||||||
'';
|
|
||||||
in "${sway-as-greeter}/bin/sway-as-greeter";
|
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
|
||||||
sane.fs."/var/log/sway" = {
|
(lib.mkIf (cfg.greeter == "greetd-sway-phog") {
|
||||||
dir.acl.mode = "0777";
|
sane.gui.greetd = {
|
||||||
wantedBeforeBy = [ "greetd.service" "display-manager.service" ];
|
enable = true;
|
||||||
|
sway.enable = true;
|
||||||
|
sway.greeterCmd = "${pkgs.phog}/libexec/phog";
|
||||||
|
};
|
||||||
|
# phog locates sxmo_winit.sh (or sway.desktop) via <env>/share/wayland-sessions
|
||||||
|
environment.pathsToLink = [ "/share/wayland-sessions" ];
|
||||||
|
})
|
||||||
|
|
||||||
|
(lib.mkIf (cfg.greeter == "greetd-phog") {
|
||||||
|
sane.gui.greetd = {
|
||||||
|
enable = true;
|
||||||
|
session.name = "phog";
|
||||||
|
session.command = "${pkgs.phog}/bin/phog";
|
||||||
|
};
|
||||||
|
# phog locates sxmo_winit.sh (or sway.desktop) via <env>/share/wayland-sessions
|
||||||
|
environment.pathsToLink = [ "/share/wayland-sessions" ];
|
||||||
|
})
|
||||||
|
|
||||||
|
(lib.mkIf (cfg.greeter == "greetd-sxmo") {
|
||||||
|
sane.gui.greetd = {
|
||||||
|
enable = true;
|
||||||
|
session.name = "sxmo";
|
||||||
|
# session.command = "${package}/bin/sxmo_winit.sh";
|
||||||
|
session.command = "${pkgs.sway}/bin/sway --debug";
|
||||||
|
session.user = "colin";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -347,21 +597,11 @@ in
|
|||||||
# name = "sxmo";
|
# name = "sxmo";
|
||||||
# desktopNames = [ "sxmo" ];
|
# desktopNames = [ "sxmo" ];
|
||||||
# start = ''
|
# start = ''
|
||||||
# ${cfg.package}/bin/sxmo_xinit.sh &
|
# ${package}/bin/sxmo_xinit.sh &
|
||||||
# waitPID=$!
|
# waitPID=$!
|
||||||
# '';
|
# '';
|
||||||
# }];
|
# }];
|
||||||
# services.xserver.enable = true;
|
# services.xserver.enable = true;
|
||||||
|
|
||||||
# services.greetd = {
|
|
||||||
# enable = true;
|
|
||||||
# settings = {
|
|
||||||
# default_session = {
|
|
||||||
# command = "${cfg.package}/bin/sxmo_winit.sh";
|
|
||||||
# user = "colin";
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
# };
|
|
||||||
]))
|
]))
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
142
hosts/modules/gui/sxmo/hooks/sxmo_hook_inputhandler.sh
Executable file
142
hosts/modules/gui/sxmo/hooks/sxmo_hook_inputhandler.sh
Executable file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p coreutils
|
||||||
|
|
||||||
|
# input map considerations
|
||||||
|
# - using compound actions causes delays.
|
||||||
|
# e.g. if volup->volup is a distinct action from volup, then single-volup action is forced to wait the maximum button delay.
|
||||||
|
# - actions which are to be responsive should therefore have a dedicated key.
|
||||||
|
# - a dedicated "kill" combo is important for unresponsive fullscreen apps, because appmenu doesn't show in those
|
||||||
|
# - although better may be to force appmenu to show over FS apps
|
||||||
|
# - bonsai mappings are static, so buttons can't benefit from non-compounding unless they're mapped accordingly for all lock states
|
||||||
|
# - this limitation could be removed, but with work
|
||||||
|
#
|
||||||
|
# proposed future design:
|
||||||
|
# - when unlocked:
|
||||||
|
# - volup1 -> app menu
|
||||||
|
# - voldown1 -> toggle keyboard
|
||||||
|
# - pow1 -> volup1 -> volume up
|
||||||
|
# - pow1 -> voldown1 -> volume down
|
||||||
|
# - pow2 -> screen off
|
||||||
|
# - pow3 -> kill app
|
||||||
|
# - when locked:
|
||||||
|
# - volup1 -> volume up
|
||||||
|
# - voldown1 -> volume down
|
||||||
|
# - pow1 -> screen on
|
||||||
|
# - pow2 -> toggle player
|
||||||
|
# benefits
|
||||||
|
# - volup and voldown are able to be far more responsive
|
||||||
|
# - which means faster vkbd, menus, volume adjustment (when locked)
|
||||||
|
# limitations
|
||||||
|
# - terminal is unmapped. that could be mapped to pow1?
|
||||||
|
# - wm menu is unmapped. but i never used that much anyway
|
||||||
|
|
||||||
|
# increments to use for volume adjustment
|
||||||
|
VOL_INCR_1=5
|
||||||
|
VOL_INCR_2=10
|
||||||
|
VOL_INCR_3=15
|
||||||
|
|
||||||
|
# replicating the naming from upstream sxmo_hook_inputhandler.sh...
|
||||||
|
ACTION="$1"
|
||||||
|
STATE=$(cat "$SXMO_STATE")
|
||||||
|
|
||||||
|
|
||||||
|
handle_with() {
|
||||||
|
echo "sxmo_hook_inputhandler.sh: STATE=$STATE ACTION=$ACTION: handle_with: $@"
|
||||||
|
"$@"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# handle_with_state_toggle() {
|
||||||
|
# # - unlock,lock => screenoff
|
||||||
|
# # - screenoff => unlock
|
||||||
|
# #
|
||||||
|
# # probably not handling proximity* correctly here
|
||||||
|
# case "$STATE" in
|
||||||
|
# *lock)
|
||||||
|
# respond_with sxmo_state_switch.sh set screenoff
|
||||||
|
# *)
|
||||||
|
# respond_with sxmo_state_switch.sh set unlock
|
||||||
|
# esac
|
||||||
|
# }
|
||||||
|
|
||||||
|
# state is one of:
|
||||||
|
# - "unlock" => normal operation; display on and touchscreen on
|
||||||
|
# - "screenoff" => display off and touchscreen off
|
||||||
|
# - "lock" => display on but touchscreen disabled
|
||||||
|
# - "proximity{lock,unlock}" => intended for when in a phone call
|
||||||
|
|
||||||
|
if [ "$STATE" = "unlock" ]; then
|
||||||
|
case "$ACTION" in
|
||||||
|
# powerbutton_one: intentional default to no-op
|
||||||
|
# powerbutton_two: intentional default to screenoff
|
||||||
|
"powerbutton_three")
|
||||||
|
# power thrice: kill active window
|
||||||
|
handle_with sxmo_killwindow.sh
|
||||||
|
;;
|
||||||
|
|
||||||
|
"volup_one")
|
||||||
|
# volume up once: app-specific menu w/ fallback to SXMO system menu
|
||||||
|
handle_with sxmo_appmenu.sh
|
||||||
|
;;
|
||||||
|
# volup_two: intentionally defaulted for volume control
|
||||||
|
"volup_three")
|
||||||
|
# volume up thrice: DE menu
|
||||||
|
handle_with sxmo_wmmenu.sh
|
||||||
|
;;
|
||||||
|
|
||||||
|
"voldown_one")
|
||||||
|
# volume down once: toggle keyboard
|
||||||
|
handle_with sxmo_keyboard.sh toggle
|
||||||
|
;;
|
||||||
|
# voldown_two: intentionally defaulted for volume control
|
||||||
|
"voldown_three")
|
||||||
|
# volume down thrice: launch terminal
|
||||||
|
handle_with sxmo_terminal.sh
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$STATE" = "screenoff" ]; then
|
||||||
|
case "$ACTION" in
|
||||||
|
"powerbutton_two")
|
||||||
|
# power twice => toggle media player
|
||||||
|
handle_with playerctl play-pause
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# default actions
|
||||||
|
case "$ACTION" in
|
||||||
|
"powerbutton_one")
|
||||||
|
# power once => unlock
|
||||||
|
handle_with sxmo_state_switch.sh set unlock
|
||||||
|
;;
|
||||||
|
"powerbutton_two")
|
||||||
|
# power twice => screenoff
|
||||||
|
handle_with sxmo_state_switch.sh set screenoff
|
||||||
|
;;
|
||||||
|
# powerbutton_three: intentional no-op because overloading the kill-window handler is risky
|
||||||
|
|
||||||
|
"volup_one")
|
||||||
|
handle_with sxmo_audio.sh vol up "$VOL_INCR_1"
|
||||||
|
;;
|
||||||
|
"volup_two")
|
||||||
|
handle_with sxmo_audio.sh vol up "$VOL_INCR_2"
|
||||||
|
;;
|
||||||
|
"volup_three")
|
||||||
|
handle_with sxmo_audio.sh vol up "$VOL_INCR_3"
|
||||||
|
;;
|
||||||
|
|
||||||
|
"voldown_one")
|
||||||
|
handle_with sxmo_audio.sh vol down "$VOL_INCR_1"
|
||||||
|
;;
|
||||||
|
"voldown_two")
|
||||||
|
handle_with sxmo_audio.sh vol down "$VOL_INCR_2"
|
||||||
|
;;
|
||||||
|
"voldown_three")
|
||||||
|
handle_with sxmo_audio.sh vol down "$VOL_INCR_3"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
handle_with echo "no-op"
|
||||||
7
hosts/modules/gui/sxmo/hooks/sxmo_hook_postwake.sh
Executable file
7
hosts/modules/gui/sxmo/hooks/sxmo_hook_postwake.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash
|
||||||
|
|
||||||
|
# the default sxmo_postwake handler checks if the modem is offline
|
||||||
|
# and if so installs a wakelock to block suspend for 30s.
|
||||||
|
# that's a questionable place to install that logic, and i want to keep stuff
|
||||||
|
# out of the wake-from-sleep critical path, so i'm overriding this hook to disable that.
|
||||||
16
hosts/modules/gui/sxmo/hooks/sxmo_hook_rotate.sh
Executable file
16
hosts/modules/gui/sxmo/hooks/sxmo_hook_rotate.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p sway
|
||||||
|
|
||||||
|
# called whenever sxmo_rotate.sh is invoked.
|
||||||
|
# i.e. whenever the screen is rotated manually, or automatically if autorotate is enabled.
|
||||||
|
# $1 = the new orientation
|
||||||
|
# possible values are "normal", "invert", "left" and "right"
|
||||||
|
|
||||||
|
# exit fullscreen, if any app is in FS.
|
||||||
|
# this benefits UX because:
|
||||||
|
# - most of my FS use is in landscape mode
|
||||||
|
# - when i toggle apps or desktops i always do so in portrait mode
|
||||||
|
# - therefore when i rotate landscape -> portrait mode, i almost never want fullscreen anymore.
|
||||||
|
if [ "$1" = "normal" ] || [ "$1" = "invert" ]; then
|
||||||
|
swaymsg fullscreen disable
|
||||||
|
fi
|
||||||
88
hosts/modules/gui/sxmo/hooks/sxmo_hook_start.sh
Executable file
88
hosts/modules/gui/sxmo/hooks/sxmo_hook_start.sh
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p systemd -p xdg-user-dirs
|
||||||
|
# this is based on upstream sxmo-utils sxmo_hook_start.sh
|
||||||
|
# but modified for nixos integration and specialize a bit to my needs
|
||||||
|
. sxmo_common.sh
|
||||||
|
|
||||||
|
# Create xdg user directories, such as ~/Pictures
|
||||||
|
xdg-user-dirs-update
|
||||||
|
|
||||||
|
sxmo_daemons.sh start daemon_manager
|
||||||
|
|
||||||
|
# Periodically update some status bar components
|
||||||
|
sxmo_hook_statusbar.sh all
|
||||||
|
sxmo_daemons.sh start statusbar_periodics sxmo_run_aligned.sh 60 \
|
||||||
|
sxmo_hook_statusbar.sh periodics
|
||||||
|
|
||||||
|
# TODO: start these externally, via `wantedBy` in nix
|
||||||
|
# don't: mako is managed externally
|
||||||
|
# superctl start mako
|
||||||
|
systemctl --user start sxmo_wob
|
||||||
|
systemctl --user start sxmo_menumode_toggler
|
||||||
|
systemctl --user start bonsaid
|
||||||
|
# don't: sway background is managed externally
|
||||||
|
# swaymsg output '*' bg "$SXMO_BG_IMG" fill
|
||||||
|
|
||||||
|
# To setup initial lock state
|
||||||
|
sxmo_state_switch.sh set unlock
|
||||||
|
|
||||||
|
# Turn on auto-suspend
|
||||||
|
if [ -w "/sys/power/wakeup_count" ] && [ -f "/sys/power/wake_lock" ]; then
|
||||||
|
systemctl --user start sxmo_autosuspend
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Turn on lisgd
|
||||||
|
if [ ! -e "$XDG_CACHE_HOME"/sxmo/sxmo.nogesture ]; then
|
||||||
|
systemctl --user start sxmo_hook_lisgd
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(command -v ModemManager)" ]; then
|
||||||
|
# Turn on the dbus-monitors for modem-related tasks
|
||||||
|
systemctl --user start sxmo_modemmonitor
|
||||||
|
|
||||||
|
# place a wakelock for 120s to allow the modem to fully warm up (eg25 +
|
||||||
|
# elogind/systemd would do this for us, but we don't use those.)
|
||||||
|
sxmo_wakelock.sh lock sxmo_modem_warming_up 120s
|
||||||
|
fi
|
||||||
|
|
||||||
|
# don't: conky is managed externally
|
||||||
|
# superctl start sxmo_conky
|
||||||
|
|
||||||
|
# Monitor the battery
|
||||||
|
systemctl --user start sxmo_battery_monitor
|
||||||
|
|
||||||
|
# It watch network changes and update the status bar icon by example
|
||||||
|
systemctl --user start sxmo_networkmonitor
|
||||||
|
|
||||||
|
# The daemon that display notifications popup messages
|
||||||
|
systemctl --user start sxmo_notificationmonitor
|
||||||
|
|
||||||
|
# monitor for headphone for statusbar
|
||||||
|
systemctl --user start sxmo_soundmonitor
|
||||||
|
|
||||||
|
# rotate UI based on physical display angle by default
|
||||||
|
if [ -n "$SXMO_AUTOROTATE" ]; then
|
||||||
|
# TODO: this could use ~/.cache/sxmo/sxmo.autorotate like for lisgd above
|
||||||
|
sxmo_daemons.sh start autorotate sxmo_autorotate.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Play a funky startup tune if you want (disabled by default)
|
||||||
|
#mpv --quiet --no-video ~/welcome.ogg &
|
||||||
|
|
||||||
|
# mmsd and vvmd
|
||||||
|
if [ -f "${SXMO_MMS_BASE_DIR:-"$HOME"/.mms/modemmanager}/mms" ]; then
|
||||||
|
systemctl --user start mmsd-tng
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${SXMO_VVM_BASE_DIR:-"$HOME"/.vvm/modemmanager}/vvm" ]; then
|
||||||
|
systemctl --user start vvmd
|
||||||
|
fi
|
||||||
|
|
||||||
|
# add some warnings if things are not setup correctly
|
||||||
|
if ! command -v "sxmo_deviceprofile_$SXMO_DEVICE_NAME.sh"; then
|
||||||
|
sxmo_notify_user.sh --urgency=critical \
|
||||||
|
"No deviceprofile found $SXMO_DEVICE_NAME. See: https://sxmo.org/deviceprofile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sxmo_migrate.sh state || sxmo_notify_user.sh --urgency=critical \
|
||||||
|
"Config needs migration" "$? file(s) in your sxmo configuration are out of date and disabled - using defaults until you migrate (run sxmo_migrate.sh)"
|
||||||
21
hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh
Executable file
21
hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p coreutils -p util-linux
|
||||||
|
|
||||||
|
# yeah, this isn't technically a hook, but the hook infrastructure isn't actually
|
||||||
|
# restricted to stuff that starts with sxmo_hook_ ...
|
||||||
|
#
|
||||||
|
# this script is only called by sxmo_autosuspend, which is small, so if i wanted to
|
||||||
|
# be more proper i could instead re-implement autosuspend + integrations.
|
||||||
|
|
||||||
|
. sxmo_common.sh
|
||||||
|
|
||||||
|
suspend_time=300
|
||||||
|
|
||||||
|
sxmo_log "calling suspend for duration: $suspend_time"
|
||||||
|
|
||||||
|
rtcwake -m mem -s "$suspend_time" || exit 1
|
||||||
|
|
||||||
|
sxmo_log "exited suspend: $(cat /proc/net/rtl8723cs/wlan0/wowlan_last_wake_reason)"
|
||||||
|
|
||||||
|
sxmo_hook_postwake.sh
|
||||||
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
# Default config for sway
|
|
||||||
# configversion: 5eff902ecca36b4e75567322335cc81c
|
|
||||||
#
|
|
||||||
# Copy this to ~/.config/sway/config and edit it to your liking.
|
|
||||||
#
|
|
||||||
# Read `man 5 sway` for a complete reference.
|
|
||||||
|
|
||||||
### Variables
|
|
||||||
#
|
|
||||||
# Mod4 = Logo key
|
|
||||||
# Mod1 = Alt.
|
|
||||||
set $mod Mod1
|
|
||||||
# Home row direction keys, like vim
|
|
||||||
set $left h
|
|
||||||
set $down j
|
|
||||||
set $up k
|
|
||||||
set $right l
|
|
||||||
# Your preferred terminal emulator
|
|
||||||
set $term sxmo_terminal.sh
|
|
||||||
# Your preferred application launcher
|
|
||||||
# Note: pass the final command to swaymsg so that the resulting window can be opened
|
|
||||||
# on the original workspace that the command was run on.
|
|
||||||
set $menu bemenu-run
|
|
||||||
|
|
||||||
# xwayland enable|disable|force
|
|
||||||
# - enable: lazily launch xwayland on first client connection
|
|
||||||
# - disable: never launch xwayland
|
|
||||||
# - force: launch xwayland immediately on boot
|
|
||||||
# XWayland uses about 35MB RSS even when idle
|
|
||||||
xwayland disable
|
|
||||||
|
|
||||||
font "Sxmo 10"
|
|
||||||
|
|
||||||
exec_always sxmo_swayinitconf.sh
|
|
||||||
|
|
||||||
exec_always dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
|
|
||||||
|
|
||||||
mode "menu" {
|
|
||||||
bindsym --input-device=1:1:1c21800.lradc XF86AudioMute exec nothing # just a placeholder for "menu" mode
|
|
||||||
}
|
|
||||||
|
|
||||||
hide_edge_borders smart
|
|
||||||
default_border pixel 1
|
|
||||||
titlebar_border_thickness 1
|
|
||||||
# XX YY distance from edge of window title to edge of text
|
|
||||||
# the YY distance here determines the heigh of the overall title
|
|
||||||
titlebar_padding 12 1
|
|
||||||
title_align center
|
|
||||||
|
|
||||||
### tab colors (#border #background #text [#indicator #childBorder])
|
|
||||||
# fucused & unfocused are the main interest
|
|
||||||
client.focused #1f5e54 #418379 #ffffff
|
|
||||||
client.focused_inactive #1f5e54 #5f676a #ffffff
|
|
||||||
client.unfocused #1f5e54 #1f554c #b4b4b4
|
|
||||||
client.urgent #e64291 #e64291 #ffffff
|
|
||||||
|
|
||||||
### Key bindings
|
|
||||||
#
|
|
||||||
# Basics:
|
|
||||||
#
|
|
||||||
# Start a terminal
|
|
||||||
bindsym $mod+Return exec $term
|
|
||||||
|
|
||||||
# Launch appmenu
|
|
||||||
bindsym $mod+p exec sxmo_appmenu.sh
|
|
||||||
|
|
||||||
# Launch scripts menu
|
|
||||||
bindsym $mod+i exec sxmo_appmenu.sh scripts
|
|
||||||
|
|
||||||
# Kill focused window
|
|
||||||
bindsym $mod+Shift+q kill
|
|
||||||
|
|
||||||
# Start your launcher
|
|
||||||
bindsym $mod+d exec $menu
|
|
||||||
|
|
||||||
# Drag floating windows by holding down $mod and left mouse button.
|
|
||||||
# Resize them with right mouse button + $mod.
|
|
||||||
# Despite the name, also works for non-floating windows.
|
|
||||||
# Change normal to inverse to use left mouse button for resizing and right
|
|
||||||
# mouse button for dragging.
|
|
||||||
floating_modifier $mod normal
|
|
||||||
|
|
||||||
# Reload the configuration file
|
|
||||||
bindsym $mod+Shift+c reload
|
|
||||||
|
|
||||||
# Exit sway (logs you out of your Wayland session)
|
|
||||||
bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'
|
|
||||||
#
|
|
||||||
# Moving around:
|
|
||||||
#
|
|
||||||
# Move your focus around
|
|
||||||
bindsym $mod+$left focus left
|
|
||||||
bindsym $mod+$down focus down
|
|
||||||
bindsym $mod+$up focus up
|
|
||||||
bindsym $mod+$right focus right
|
|
||||||
# Or use $mod+[up|down|left|right]
|
|
||||||
bindsym $mod+Left focus left
|
|
||||||
bindsym $mod+Down focus down
|
|
||||||
bindsym $mod+Up focus up
|
|
||||||
bindsym $mod+Right focus right
|
|
||||||
|
|
||||||
# Move the focused window with the same, but add Shift
|
|
||||||
bindsym $mod+Shift+$left move left
|
|
||||||
bindsym $mod+Shift+$down move down
|
|
||||||
bindsym $mod+Shift+$up move up
|
|
||||||
bindsym $mod+Shift+$right move right
|
|
||||||
# Ditto, with arrow keys
|
|
||||||
bindsym $mod+Shift+Left move left
|
|
||||||
bindsym $mod+Shift+Down move down
|
|
||||||
bindsym $mod+Shift+Up move up
|
|
||||||
bindsym $mod+Shift+Right move right
|
|
||||||
|
|
||||||
# Move the focused workspace to output
|
|
||||||
bindsym $mod+Shift+Ctrl+$left move workspace output left
|
|
||||||
bindsym $mod+Shift+Ctrl+$down move workspace output down
|
|
||||||
bindsym $mod+Shift+Ctrl+$up move workspace output up
|
|
||||||
bindsym $mod+Shift+Ctrl+$right move workspace output right
|
|
||||||
#
|
|
||||||
# Workspaces:
|
|
||||||
#
|
|
||||||
# Switch to workspace
|
|
||||||
bindsym $mod+1 workspace number 1
|
|
||||||
bindsym $mod+2 workspace number 2
|
|
||||||
bindsym $mod+3 workspace number 3
|
|
||||||
bindsym $mod+4 workspace number 4
|
|
||||||
bindsym $mod+5 workspace number 5
|
|
||||||
bindsym $mod+6 workspace number 6
|
|
||||||
bindsym $mod+7 workspace number 7
|
|
||||||
bindsym $mod+8 workspace number 8
|
|
||||||
bindsym $mod+9 workspace number 9
|
|
||||||
bindsym $mod+0 workspace number 10
|
|
||||||
# Move focused container to workspace
|
|
||||||
bindsym $mod+Shift+1 move container to workspace number 1
|
|
||||||
bindsym $mod+Shift+2 move container to workspace number 2
|
|
||||||
bindsym $mod+Shift+3 move container to workspace number 3
|
|
||||||
bindsym $mod+Shift+4 move container to workspace number 4
|
|
||||||
bindsym $mod+Shift+5 move container to workspace number 5
|
|
||||||
bindsym $mod+Shift+6 move container to workspace number 6
|
|
||||||
bindsym $mod+Shift+7 move container to workspace number 7
|
|
||||||
bindsym $mod+Shift+8 move container to workspace number 8
|
|
||||||
bindsym $mod+Shift+9 move container to workspace number 9
|
|
||||||
bindsym $mod+Shift+0 move container to workspace number 10
|
|
||||||
# Note: workspaces can have any name you want, not just numbers.
|
|
||||||
# We just use 1-10 as the default.
|
|
||||||
#
|
|
||||||
# Layout stuff:
|
|
||||||
#
|
|
||||||
# You can "split" the current object of your focus with
|
|
||||||
# $mod+b or $mod+v, for horizontal and vertical splits
|
|
||||||
# respectively.
|
|
||||||
bindsym $mod+b splith
|
|
||||||
bindsym $mod+v splitv
|
|
||||||
|
|
||||||
# Switch the current container between different layout styles
|
|
||||||
bindsym $mod+s layout stacking
|
|
||||||
bindsym $mod+w layout tabbed
|
|
||||||
bindsym $mod+e layout toggle split
|
|
||||||
|
|
||||||
# Make the current focus fullscreen
|
|
||||||
# bindsym $mod+f fullscreen
|
|
||||||
|
|
||||||
# Toggle the current focus between tiling and floating mode
|
|
||||||
bindsym $mod+Shift+space floating toggle
|
|
||||||
|
|
||||||
# Swap focus between the tiling area and the floating area
|
|
||||||
bindsym $mod+space focus mode_toggle
|
|
||||||
|
|
||||||
# Move focus to the parent container
|
|
||||||
bindsym $mod+a focus parent
|
|
||||||
#
|
|
||||||
# Scratchpad:
|
|
||||||
#
|
|
||||||
# Sway has a "scratchpad", which is a bag of holding for windows.
|
|
||||||
# You can send windows there and get them back later.
|
|
||||||
|
|
||||||
# Move the currently focused window to the scratchpad
|
|
||||||
bindsym $mod+Shift+minus move scratchpad
|
|
||||||
|
|
||||||
# Show the next scratchpad window or hide the focused scratchpad window.
|
|
||||||
# If there are multiple scratchpad windows, this command cycles through them.
|
|
||||||
bindsym $mod+minus scratchpad show
|
|
||||||
#
|
|
||||||
# Resizing containers:
|
|
||||||
#
|
|
||||||
mode "resize" {
|
|
||||||
# left will shrink the containers width
|
|
||||||
# right will grow the containers width
|
|
||||||
# up will shrink the containers height
|
|
||||||
# down will grow the containers height
|
|
||||||
bindsym $left resize shrink width 10px
|
|
||||||
bindsym $down resize grow height 10px
|
|
||||||
bindsym $up resize shrink height 10px
|
|
||||||
bindsym $right resize grow width 10px
|
|
||||||
|
|
||||||
# Ditto, with arrow keys
|
|
||||||
bindsym Left resize shrink width 10px
|
|
||||||
bindsym Down resize grow height 10px
|
|
||||||
bindsym Up resize shrink height 10px
|
|
||||||
bindsym Right resize grow width 10px
|
|
||||||
|
|
||||||
# Return to default mode
|
|
||||||
bindsym Return mode "default"
|
|
||||||
bindsym Escape mode "default"
|
|
||||||
}
|
|
||||||
bindsym $mod+r mode "resize"
|
|
||||||
|
|
||||||
#
|
|
||||||
# Status Bar:
|
|
||||||
#
|
|
||||||
# Read `man 5 sway-bar` for more information about this section.
|
|
||||||
bar {
|
|
||||||
position top
|
|
||||||
|
|
||||||
# When the status_command prints a new line to stdout, swaybar updates.
|
|
||||||
# The default just shows the current date and time.
|
|
||||||
status_command sxmo_status_watch.sh -o pango
|
|
||||||
swaybar_command @waybar@
|
|
||||||
|
|
||||||
pango_markup enabled
|
|
||||||
|
|
||||||
colors {
|
|
||||||
statusline #ffffff
|
|
||||||
background #323232
|
|
||||||
inactive_workspace #32323200 #32323200 #5c5c5c
|
|
||||||
font "Sxmo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for_window [app_id="foot" title=".*sxmo/modem/.*/draft.txt.*"] resize set height 25
|
|
||||||
for_window [title="megapixels"] inhibit_idle open
|
|
||||||
|
|
||||||
include /etc/sway/config.d/*
|
|
||||||
|
|
||||||
exec 'printf %s "$SWAYSOCK" > "$XDG_RUNTIME_DIR"/sxmo.swaysock'
|
|
||||||
|
|
||||||
exec sxmo_hook_start.sh
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
|
||||||
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
|
||||||
{ pkgs }:
|
|
||||||
[
|
|
||||||
{ # TOP BAR
|
|
||||||
layer = "top";
|
|
||||||
height = 26;
|
|
||||||
|
|
||||||
modules-left = [ "sway/workspaces" ];
|
|
||||||
modules-center = [ ];
|
|
||||||
modules-right = [ "custom/sxmo" ];
|
|
||||||
|
|
||||||
"sway/workspaces" = {
|
|
||||||
all-outputs = true;
|
|
||||||
# force the bar to always show even empty workspaces
|
|
||||||
persistent_workspaces = {
|
|
||||||
"1" = [];
|
|
||||||
"2" = [];
|
|
||||||
"3" = [];
|
|
||||||
"4" = [];
|
|
||||||
"5" = [];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
"custom/sxmo" = {
|
|
||||||
# this gives wifi state, batter, mic/speaker, lockstate, time all as one widget.
|
|
||||||
# a good starting point, but may want to split these apart later to make things configurable.
|
|
||||||
# e.g. distinct vol-up & vol-down buttons next to the speaker?
|
|
||||||
exec = "sxmo_status.sh";
|
|
||||||
interval = 1;
|
|
||||||
format = "{}";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/* style docs: https://github.com/Alexays/Waybar/wiki/Styling */
|
|
||||||
/* defaults: https://github.com/Alexays/Waybar/blob/master/resources/style.css */
|
|
||||||
|
|
||||||
window#waybar {
|
|
||||||
background-color: #418379;
|
|
||||||
border-bottom: 0px solid #1f5e54;
|
|
||||||
color: #ffffff;
|
|
||||||
transition-property: background-color;
|
|
||||||
transition-duration: .2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modules-right {
|
|
||||||
/* workspace buttons (LHS) get padding between it and the screen edge */
|
|
||||||
/* replicate that same padding for whatever's on the RHS (i.e. the clock) */
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button {
|
|
||||||
padding: 0 5px;
|
|
||||||
background-color: #418379;
|
|
||||||
color: #ffffff;
|
|
||||||
/* Use box-shadow instead of border so the text isn't offset */
|
|
||||||
box-shadow: inset 0 0px #1f5e54;
|
|
||||||
/* Avoid rounded borders under each workspace name */
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button:hover {
|
|
||||||
/* i don't want hover effects, so reset this styling to be the same as default button */
|
|
||||||
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
|
|
||||||
background: inherit;
|
|
||||||
box-shadow: inherit;
|
|
||||||
text-shadow: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.focused {
|
|
||||||
background-color: #63a89c;
|
|
||||||
box-shadow: inset 0 0px #2c8274;
|
|
||||||
}
|
|
||||||
|
|
||||||
#workspaces button.urgent {
|
|
||||||
background-color: #e64291;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink {
|
|
||||||
to {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray {
|
|
||||||
background-color: #418379;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > .passive {
|
|
||||||
-gtk-icon-effect: dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tray > .needs-attention {
|
|
||||||
-gtk-icon-effect: highlight;
|
|
||||||
background-color: #e64291;
|
|
||||||
}
|
|
||||||
31
hosts/modules/gui/sxmo/waybar-top.nix
Normal file
31
hosts/modules/gui/sxmo/waybar-top.nix
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# docs: https://github.com/Alexays/Waybar/wiki/Configuration
|
||||||
|
# format specifiers: https://fmt.dev/latest/syntax.html#syntax
|
||||||
|
# this is merged with the sway/waybar-top.nix defaults
|
||||||
|
{
|
||||||
|
height = 26;
|
||||||
|
|
||||||
|
modules-left = [ "sway/workspaces" ];
|
||||||
|
modules-center = [ ];
|
||||||
|
modules-right = [ "custom/swaync" "custom/sxmo" ];
|
||||||
|
|
||||||
|
"sway/workspaces" = {
|
||||||
|
all-outputs = true;
|
||||||
|
# force the bar to always show even empty workspaces
|
||||||
|
persistent_workspaces = {
|
||||||
|
"1" = [];
|
||||||
|
"2" = [];
|
||||||
|
"3" = [];
|
||||||
|
"4" = [];
|
||||||
|
"5" = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"custom/sxmo" = {
|
||||||
|
# this gives wifi state, batter, mic/speaker, lockstate, time all as one widget.
|
||||||
|
# a good starting point, but may want to split these apart later to make things configurable.
|
||||||
|
# e.g. distinct vol-up & vol-down buttons next to the speaker?
|
||||||
|
exec = "sxmo_status.sh";
|
||||||
|
interval = 1;
|
||||||
|
format = "{}";
|
||||||
|
};
|
||||||
|
}
|
||||||
28
hosts/modules/gui/theme/default.nix
Normal file
28
hosts/modules/gui/theme/default.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.sane.gui.theme.background;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = with lib; {
|
||||||
|
sane.gui.theme.background = {
|
||||||
|
svg = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = ./nixos-bg-02.svg;
|
||||||
|
};
|
||||||
|
png = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = pkgs.runCommand
|
||||||
|
"nixos-bg.png"
|
||||||
|
{ nativeBuildInputs = [ pkgs.inkscape ]; }
|
||||||
|
''
|
||||||
|
inkscape ${cfg.svg} -o $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
sane.gui.sway.config.background = lib.mkDefault cfg.png;
|
||||||
|
sane.gui.sxmo.settings.SXMO_BG_IMG = lib.mkDefault (builtins.toString cfg.png);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
@@ -1,7 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./x86_64.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user